fix: extend left adaptive stick activation to the screen edge

This commit is contained in:
sanogueralorenzo authored and Kurt Himebauch committed 2026-11-05 11:43:46 -05:00
1 parent e1fe6f99cf
commit d8c1bc6059
2 files changed
+67 -3

No files matched your search

@@ -696,7 +696,7 @@ class OnScreenControls(
if (x < safe.left || x >= width - safe.right || y < safe.top || y >= height - safe.bottom) return null
if (controls.any { it.stick < 0 && isVisible(it) && it.contains(x, y, it.radius, 5f) }) return null
return controls.filter {
val dx = x - it.cx
val dx = if (it.stick == 0) max(0f, x - it.cx) else x - it.cx
val dy = y - it.cy
val reach = it.radius * 1.4f * sqrt(1.5f)
it.stick >= 0 && isVisible(it) && it.pressedBy == -1 && dx * dx + dy * dy <= reach * reach
@@ -5,8 +5,11 @@ import kotlin.math.sqrt
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.view.DisplayCutout
import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import org.junit.Assert.*
import org.junit.Before
@@ -191,6 +194,7 @@ class OnScreenControlsTest {
val x = value(left, "cx")
val y = value(left, "cy")
assertNotEquals(0, pixel(x, y))
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (0f to y)))
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (600f to 200f)))
touch(MotionEvent.ACTION_DOWN, 1 to (x + 10f to y))
assertEquals(x, value(left, "ax"), 0f)
@@ -211,7 +215,7 @@ class OnScreenControlsTest {
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (600f to 200f)))
}
@Test fun adaptiveActivationIsLimitedToExpandedSavedCircle() {
@Test fun adaptiveActivationKeepsItsVerticalAndInwardLimits() {
val left = control("ls")
val x = value(left, "cx")
val y = value(left, "cy")
@@ -220,10 +224,53 @@ class OnScreenControlsTest {
assertTrue(touch(MotionEvent.ACTION_DOWN, 1 to (x to y - reach + 1f)))
assertEquals(y - reach + 1f, value(left, "ay"), 0.001f)
touch(MotionEvent.ACTION_UP, 1 to (x to y - reach + 1f))
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (x - reach to y - reach)))
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (x + reach to y - reach)))
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (600f to 200f)))
}
@Test fun leftAdaptiveStickCanStartAtTheLeftEdgeAndReleaseNormally() {
val left = control("ls")
val y = value(left, "cy")
val reach = value(left, "radius") * 1.4f * sqrt(1.5f)
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (0f to y - reach - 1f)))
assertTrue(touch(MotionEvent.ACTION_DOWN, 1 to (0f to y)))
assertEquals(1, field(left, "pressedBy"))
assertEquals(0f, value(left, "ax"), 0f)
assertEquals(0f, value(left, "kx"), 0f)
touch(MotionEvent.ACTION_MOVE, 1 to (30f to y))
assertEquals(30f, value(left, "kx"), 0f)
touch(MotionEvent.ACTION_CANCEL, 1 to (30f to y))
assertEquals(-1, field(left, "pressedBy"))
assertEquals(0f, value(left, "kx"), 0f)
assertEquals(-1, field(control("rs"), "pressedBy"))
}
@Test fun rightAdaptiveStickKeepsItsSavedActivationCircle() {
val right = control("rs")
val x = value(right, "cx")
val y = value(right, "cy")
val reach = value(right, "radius") * 1.4f * sqrt(1.5f)
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (1199f to y)))
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (x + reach + 1f to y)))
assertTrue(touch(MotionEvent.ACTION_DOWN, 1 to (x + reach - 1f to y)))
assertEquals(1, field(right, "pressedBy"))
}
@Test
@Config(sdk = [30])
@Suppress("DEPRECATION")
fun leftActivationStopsAtTheSafeCutoutEdge() {
val insets = WindowInsets.Builder()
.setDisplayCutout(DisplayCutout(Rect(24, 0, 0, 0), listOf(Rect(0, 380, 24, 420))))
.build()
view.dispatchApplyWindowInsets(insets)
val left = control("ls")
val y = value(left, "cy")
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (23f to y)))
assertTrue(touch(MotionEvent.ACTION_DOWN, 1 to (24f to y)))
assertEquals(1, field(left, "pressedBy"))
}
@Test fun touchesOutsideActivationAreasStayWithTrackpad() {
val received = mutableListOf<Int>()
val trackpad = View(context).apply {
@@ -311,6 +358,23 @@ class OnScreenControlsTest {
}
}
@Test fun leftExtensionKeepsButtonPriorityAndClearance() {
val button = control("left")
val x = value(button, "cx")
val y = value(button, "cy")
val edge = y - value(button, "radius") * 1.25f
val left = control("ls")
left.javaClass.getDeclaredField("cx").apply { isAccessible = true }.setFloat(left, x + 200f)
left.javaClass.getDeclaredField("cy").apply { isAccessible = true }.setFloat(left, y)
assertTrue(touch(MotionEvent.ACTION_DOWN, 1 to (x to y)))
assertEquals(1, field(button, "pressedBy"))
assertEquals(-1, field(left, "pressedBy"))
touch(MotionEvent.ACTION_UP, 1 to (x to y))
assertFalse(touch(MotionEvent.ACTION_DOWN, 1 to (x to edge - 4f)))
assertTrue(touch(MotionEvent.ACTION_DOWN, 1 to (x to edge - 6f)))
assertEquals(1, field(left, "pressedBy"))
}
private fun control(id: String): Any = (field(view, "controls") as List<*>).first { field(it!!, "id") == id }!!
private fun editor() {
view = OnScreenControls(context, null, editing = true)