mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
Android: walk the pause-menu tabs along the axis they are drawn on
The in-game menu's input controller navigated a layout the screen had stopped drawing: a vertical tab column on the left, content pane to its right. There are now two layouts and it is neither of them. Under 700dp — which is every handheld — the tabs are a horizontally-scrolling row above the content; wider than that they are a rail to the content's right. So the pad walked the strip across its short axis. Up and Down cycled tabs that run left to right, and Right stepped "into" a pane that sits below them. The same constants are wrong the other way round on the wide layout, where entering the content means moving Left, off the rail. Make the axis a property of the layout rather than a constant. The one place that decides `compact` now publishes it, and the mover walks the strip along its own axis and enters the content in the direction the content actually lies. Leaving the pane mirrors that, which frees the other axis to adjust values the way it does on every other registry-driven pane.
This commit is contained in:
@@ -175,6 +175,13 @@ fun EmulationMenuScreen(viewModel: EmulationMenuViewModel = viewModel()) {
|
|||||||
|
|
||||||
BoxWithConstraints(Modifier.fillMaxSize()) {
|
BoxWithConstraints(Modifier.fillMaxSize()) {
|
||||||
val compact = maxWidth < 700.dp
|
val compact = maxWidth < 700.dp
|
||||||
|
// The one place the layout is chosen is the one place that tells the pad which way it
|
||||||
|
// runs — compact puts the tabs in a Row above the content, wide puts them in a rail to
|
||||||
|
// its right, and the D-pad axis follows from here rather than from a constant that can
|
||||||
|
// fall out of step with the UI (which is exactly what it had done).
|
||||||
|
androidx.compose.runtime.SideEffect {
|
||||||
|
EmulationMenuInputController.tabsHorizontal.value = compact
|
||||||
|
}
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = shown,
|
visible = shown,
|
||||||
enter = fadeIn(tween(190, easing = EaseOut)),
|
enter = fadeIn(tween(190, easing = EaseOut)),
|
||||||
|
|||||||
+44
-15
@@ -306,13 +306,27 @@ object EmulationMenuInputController {
|
|||||||
private var owner: EmulationMenuViewModel? = null
|
private var owner: EmulationMenuViewModel? = null
|
||||||
private var pendingTab: EmulationMenuTab? = null
|
private var pendingTab: EmulationMenuTab? = null
|
||||||
|
|
||||||
// Two-zone nav. The pause menu is a vertical TAB column on the left and a
|
// Two-zone nav. `inContent` = false means the D-pad walks the TAB STRIP, where moving
|
||||||
// CONTENT pane on the right. `inContent` = false means the D-pad walks the tab
|
// switches the shown pane outright; stepping off it towards the content enters the
|
||||||
// column (Up/Down between tabs, which switches the shown pane); Right (or A)
|
// CONTENT pane, in which every control is a SettingsControllerNav registry item and the
|
||||||
// steps into the content pane, where every control is a SettingsControllerNav
|
// router drives it (move, adjust, A confirm). B — or stepping back off the near edge —
|
||||||
// registry item and the router drives it (Up/Down move, Left/Right adjust, A
|
// returns to the strip.
|
||||||
// confirm). B (or Left off the first control) returns to the tab column.
|
//
|
||||||
|
// WHICH WAY each of those is depends on the layout, so it is [tabsHorizontal] that says,
|
||||||
|
// never a constant here.
|
||||||
val inContent = androidx.compose.runtime.mutableStateOf(false)
|
val inContent = androidx.compose.runtime.mutableStateOf(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when the tab strip runs left-to-right above the content (the compact layout, which
|
||||||
|
* is every handheld), false when it is the vertical rail to the RIGHT of it.
|
||||||
|
*
|
||||||
|
* Set by the screen from the same `compact` it lays itself out with, because a hardcoded
|
||||||
|
* axis is precisely the bug this replaces: the strip moved from a left-hand column to a
|
||||||
|
* top row and a right-hand rail, and the D-pad kept walking the column that no longer
|
||||||
|
* existed — Up/Down cycling tabs laid out horizontally, and Right stepping "into" content
|
||||||
|
* that was below or to the left.
|
||||||
|
*/
|
||||||
|
val tabsHorizontal = androidx.compose.runtime.mutableStateOf(true)
|
||||||
private val nav get() = com.armsx2.ui.settings.SettingsControllerNav
|
private val nav get() = com.armsx2.ui.settings.SettingsControllerNav
|
||||||
|
|
||||||
// Set by a modal panel drawn OVER the menu (Friends) for as long as it is open; the lambda
|
// Set by a modal panel drawn OVER the menu (Friends) for as long as it is open; the lambda
|
||||||
@@ -363,20 +377,35 @@ object EmulationMenuInputController {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
val viewModel = owner ?: return false
|
val viewModel = owner ?: return false
|
||||||
|
val horizontal = tabsHorizontal.value
|
||||||
if (!inContent.value) {
|
if (!inContent.value) {
|
||||||
// Tab column (vertical): Up/Down switch tabs; Right steps into content.
|
// Walk the strip along its OWN axis, and step into the content in the direction the
|
||||||
|
// content actually lies: below a top row, left of a right-hand rail.
|
||||||
when {
|
when {
|
||||||
dy < 0 -> viewModel.cycleTab(-1)
|
horizontal && dx < 0 -> viewModel.cycleTab(-1)
|
||||||
dy > 0 -> viewModel.cycleTab(1)
|
horizontal && dx > 0 -> viewModel.cycleTab(1)
|
||||||
dx > 0 -> enterContent()
|
horizontal && dy > 0 -> enterContent()
|
||||||
|
!horizontal && dy < 0 -> viewModel.cycleTab(-1)
|
||||||
|
!horizontal && dy > 0 -> viewModel.cycleTab(1)
|
||||||
|
!horizontal && dx < 0 -> enterContent()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Content pane: registry-driven.
|
// Content pane: registry-driven. Leaving it is always "step off the edge nearest the
|
||||||
when {
|
// strip", so which axis carries the exit is the mirror of the one that walks the strip
|
||||||
dy != 0 -> nav.moveSpatial(0, dy)
|
// — and the other axis is free to adjust values, as it is everywhere else.
|
||||||
dx < 0 -> if (!nav.adjust(-1) && !nav.moveSpatial(-1, 0)) exitContent()
|
if (horizontal) {
|
||||||
dx > 0 -> if (!nav.adjust(1)) nav.moveSpatial(1, 0)
|
when {
|
||||||
|
dy < 0 -> if (!nav.moveSpatial(0, -1)) exitContent()
|
||||||
|
dy > 0 -> nav.moveSpatial(0, 1)
|
||||||
|
dx != 0 -> if (!nav.adjust(dx)) nav.moveSpatial(dx, 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when {
|
||||||
|
dy != 0 -> nav.moveSpatial(0, dy)
|
||||||
|
dx > 0 -> if (!nav.adjust(1) && !nav.moveSpatial(1, 0)) exitContent()
|
||||||
|
dx < 0 -> if (!nav.adjust(-1)) nav.moveSpatial(-1, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user