feat(companion): menu gesture is now a three-finger hold
Two-finger hold collided with two-finger scrolling — any scroll longer than 500ms popped the NESMenu. Three fingers hold for the menu; two fingers scroll, on the trackpad, both controllers and the gamepad. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
265196bc2c
commit
1df075f7b1
@ -38,7 +38,7 @@ import com.archipelago.app.ui.theme.neoRaised
|
|||||||
@Composable
|
@Composable
|
||||||
fun GamepadLayout(
|
fun GamepadLayout(
|
||||||
onKey: (String) -> Unit,
|
onKey: (String) -> Unit,
|
||||||
onTwoFingerHold: () -> Unit,
|
onThreeFingerHold: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val surface = Neo.surface()
|
val surface = Neo.surface()
|
||||||
@ -54,9 +54,9 @@ fun GamepadLayout(
|
|||||||
do {
|
do {
|
||||||
val ev = awaitPointerEvent()
|
val ev = awaitPointerEvent()
|
||||||
val a = ev.changes.filter { !it.changedToUp() }
|
val a = ev.changes.filter { !it.changedToUp() }
|
||||||
if (a.size >= 2 && t == 0L) t = System.currentTimeMillis()
|
if (a.size >= 3 && t == 0L) t = System.currentTimeMillis()
|
||||||
if (a.size >= 2 && !fired && t > 0 && System.currentTimeMillis() - t > 500) { fired = true; onTwoFingerHold() }
|
if (a.size >= 3 && !fired && t > 0 && System.currentTimeMillis() - t > 500) { fired = true; onThreeFingerHold() }
|
||||||
if (a.size < 2) t = 0L
|
if (a.size < 3) t = 0L
|
||||||
} while (ev.changes.any { it.pressed })
|
} while (ev.changes.any { it.pressed })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,7 +116,7 @@ fun NESController(
|
|||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.twoFingerHold(onMenu)
|
.threeFingerHold(onMenu)
|
||||||
.padding(horizontal = 40.dp, vertical = 24.dp),
|
.padding(horizontal = 40.dp, vertical = 24.dp),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
@ -451,17 +451,17 @@ fun PlayerPill(c: NESPalette, playerId: Int, onToggle: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Two-finger hold gesture modifier */
|
/** Three-finger hold gesture modifier (two fingers stay free for scrolling) */
|
||||||
fun Modifier.twoFingerHold(onHold: () -> Unit) = this.pointerInput(Unit) {
|
fun Modifier.threeFingerHold(onHold: () -> Unit) = this.pointerInput(Unit) {
|
||||||
awaitEachGesture {
|
awaitEachGesture {
|
||||||
awaitFirstDown(requireUnconsumed = false)
|
awaitFirstDown(requireUnconsumed = false)
|
||||||
var t = 0L; var fired = false
|
var t = 0L; var fired = false
|
||||||
do {
|
do {
|
||||||
val ev = awaitPointerEvent()
|
val ev = awaitPointerEvent()
|
||||||
val a = ev.changes.filter { !it.changedToUp() }
|
val a = ev.changes.filter { !it.changedToUp() }
|
||||||
if (a.size >= 2 && t == 0L) t = System.currentTimeMillis()
|
if (a.size >= 3 && t == 0L) t = System.currentTimeMillis()
|
||||||
if (a.size >= 2 && !fired && t > 0 && System.currentTimeMillis() - t > 500) { fired = true; onHold() }
|
if (a.size >= 3 && !fired && t > 0 && System.currentTimeMillis() - t > 500) { fired = true; onHold() }
|
||||||
if (a.size < 2) t = 0L
|
if (a.size < 3) t = 0L
|
||||||
} while (ev.changes.any { it.pressed })
|
} while (ev.changes.any { it.pressed })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,7 @@ fun NESPortraitController(
|
|||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.twoFingerHold(onMenu)
|
.threeFingerHold(onMenu)
|
||||||
.padding(horizontal = 40.dp, vertical = 24.dp),
|
.padding(horizontal = 40.dp, vertical = 24.dp),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
@ -87,7 +87,7 @@ fun NESPortraitController(
|
|||||||
onMove = { dx, dy -> onMouseMove(dx, dy) },
|
onMove = { dx, dy -> onMouseMove(dx, dy) },
|
||||||
onClick = { onMouseClick(it) },
|
onClick = { onMouseClick(it) },
|
||||||
onScroll = { dy -> onMouseScroll(dy) },
|
onScroll = { dy -> onMouseScroll(dy) },
|
||||||
onTwoFingerHold = onMenu,
|
onThreeFingerHold = onMenu,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
|
|||||||
@ -32,7 +32,7 @@ fun Trackpad(
|
|||||||
onMove: (dx: Int, dy: Int) -> Unit,
|
onMove: (dx: Int, dy: Int) -> Unit,
|
||||||
onClick: (button: Int) -> Unit,
|
onClick: (button: Int) -> Unit,
|
||||||
onScroll: (dy: Int) -> Unit,
|
onScroll: (dy: Int) -> Unit,
|
||||||
onTwoFingerHold: () -> Unit,
|
onThreeFingerHold: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var fingers by remember { mutableIntStateOf(0) }
|
var fingers by remember { mutableIntStateOf(0) }
|
||||||
@ -53,7 +53,7 @@ fun Trackpad(
|
|||||||
val t0 = System.currentTimeMillis()
|
val t0 = System.currentTimeMillis()
|
||||||
var maxPtrs = 1
|
var maxPtrs = 1
|
||||||
var holdFired = false
|
var holdFired = false
|
||||||
var twoStart = 0L
|
var threeStart = 0L
|
||||||
var scrollAcc = 0f
|
var scrollAcc = 0f
|
||||||
fingers = 1
|
fingers = 1
|
||||||
|
|
||||||
@ -64,19 +64,24 @@ fun Trackpad(
|
|||||||
fingers = active.size
|
fingers = active.size
|
||||||
|
|
||||||
when {
|
when {
|
||||||
active.size >= 2 -> {
|
// Three fingers = hold for menu; two = scroll. Kept
|
||||||
if (twoStart == 0L) twoStart = System.currentTimeMillis()
|
// on separate counts so a long two-finger scroll can
|
||||||
if (!holdFired && System.currentTimeMillis() - twoStart > 500) {
|
// never fire the menu mid-gesture.
|
||||||
|
active.size >= 3 -> {
|
||||||
|
if (threeStart == 0L) threeStart = System.currentTimeMillis()
|
||||||
|
if (!holdFired && System.currentTimeMillis() - threeStart > 500) {
|
||||||
holdFired = true
|
holdFired = true
|
||||||
onTwoFingerHold()
|
onThreeFingerHold()
|
||||||
}
|
}
|
||||||
if (!holdFired) {
|
ev.changes.forEach { it.consume() }
|
||||||
val dy = active.map { it.positionChange().y }.average().toFloat()
|
}
|
||||||
scrollAcc += dy
|
active.size == 2 -> {
|
||||||
if (kotlin.math.abs(scrollAcc) > 12f) {
|
threeStart = 0L
|
||||||
onScroll(if (scrollAcc > 0) 1 else -1)
|
val dy = active.map { it.positionChange().y }.average().toFloat()
|
||||||
scrollAcc = 0f
|
scrollAcc += dy
|
||||||
}
|
if (kotlin.math.abs(scrollAcc) > 12f) {
|
||||||
|
onScroll(if (scrollAcc > 0) 1 else -1)
|
||||||
|
scrollAcc = 0f
|
||||||
}
|
}
|
||||||
ev.changes.forEach { it.consume() }
|
ev.changes.forEach { it.consume() }
|
||||||
}
|
}
|
||||||
@ -99,7 +104,11 @@ fun Trackpad(
|
|||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = if (fingers >= 2) "hold for menu" else "",
|
text = when {
|
||||||
|
fingers >= 3 -> "hold for menu"
|
||||||
|
fingers == 2 -> "scroll"
|
||||||
|
else -> ""
|
||||||
|
},
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = muted.copy(alpha = 0.4f),
|
color = muted.copy(alpha = 0.4f),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -172,7 +172,7 @@ fun RemoteInputScreen(onBack: () -> Unit) {
|
|||||||
onMove = { dx, dy -> ws.sendMouseMove(dx, dy) },
|
onMove = { dx, dy -> ws.sendMouseMove(dx, dy) },
|
||||||
onClick = { ws.sendClick(it) },
|
onClick = { ws.sendClick(it) },
|
||||||
onScroll = { ws.sendScroll(it) },
|
onScroll = { ws.sendScroll(it) },
|
||||||
onTwoFingerHold = { showModal = true },
|
onThreeFingerHold = { showModal = true },
|
||||||
modifier = Modifier.fillMaxWidth().weight(1f)
|
modifier = Modifier.fillMaxWidth().weight(1f)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user