Touch editor: let the panel get out of the way

Ported from ARMSX2, which built it against a user complaint worth restating:
the panel covers the thing you are trying to edit, and the only remedy on
offer was to drag it away, every time.

ARMSX2's first answer was auto-docking the panel to the opposite half of the
screen from the selected widget. It works, and it did NOT fix the complaint --
selecting a widget under the panel means touching through the panel first, so
a reactive fix cannot help with an obstruction that happens strictly before
there is anything to react to.

What fixed it was a collapse toggle: one tap leaves the grip strip and
uncovers everything beneath it, one tap brings the controls back. It is first
in the grip row so it sits in the same place whether the panel is open or
shut, and it is neither persisted nor carried between sessions -- it is a
momentary 'let me see under this', and an editor that opened to a panel with
no controls on it would look broken.

Column is an inline composable, so the collapsed path returns out of it and
genuinely stops emitting the rest rather than drawing it invisibly.

Auto-dock is not ported. It is the half that demonstrably did not solve the
problem, it needs an anchor flip this panel does not have (top-anchored with a
downward offset, where ARMSX2 switches edges), and the offset sign trap that
comes with it is real. Worth revisiting only if collapse turns out to be
insufficient.
This commit is contained in:
jpolo1224
2026-08-24 16:48:02 -04:00
parent f53a76c0fc
commit 81346ebb8b
2 changed files with 28 additions and 0 deletions
@@ -122,6 +122,21 @@ object TouchControls {
* backdrop to deselect. */
val selectedButton = mutableStateOf<TouchButtonId?>(null)
/**
* Whether the editor panel is collapsed to just its grip strip.
*
* The complaint this answers is not that the panel is ugly, it is that the panel covers the
* thing you are trying to edit -- and the only remedy was to drag it out of the way, every
* time. Auto-docking the panel away from the selected widget does not fix it either, because
* selecting a widget under the panel means touching through the panel first. One tap here
* uncovers everything beneath it and needs no drag.
*
* Deliberately NOT persisted, and reset on leaving edit mode: it is a momentary "let me see
* under this", and a session that opened the editor to a panel with no controls on it would
* just look broken.
*/
val editorPanelCollapsed = mutableStateOf(false)
/** Profile picker / save-as dialog shown over the editor. */
val profileDialogOpen = mutableStateOf(false)
@@ -410,6 +410,10 @@ fun TouchControlsOverlay() {
}
}
// Momentary state: a panel that stayed collapsed into the next editing session would look
// like the controls had gone missing.
LaunchedEffect(edit) { if (!edit) TouchControls.editorPanelCollapsed.value = false }
if (edit) {
// The editor panel is draggable + pinch-resizable (grip handle at its top) so it can be
// moved off the buttons being edited. Offset is applied on the outer Box (real px); resize
@@ -1907,6 +1911,11 @@ private fun EditToolbar(modifier: Modifier = Modifier) {
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
// First in the row on purpose: it must be in the same place whether the panel is
// open or collapsed, or the way back is somewhere the user has to hunt for.
PanelSizeButton(if (TouchControls.editorPanelCollapsed.value) "" else "") {
TouchControls.editorPanelCollapsed.value = !TouchControls.editorPanelCollapsed.value
}
PanelSizeButton("") {
val ls = OverlayDims.last?.let { it.widthPx > it.heightPx } ?: true
TouchControls.editorPanelScale(ls).floatValue =
@@ -1952,6 +1961,10 @@ private fun EditToolbar(modifier: Modifier = Modifier) {
(TouchControls.editorPanelScale(ls).floatValue + 0.1f).coerceIn(0.6f, 1.35f)
}
}
// Everything below is what the collapse toggle hides. Column is an inline composable,
// so this genuinely skips emitting the rest rather than drawing it invisibly.
if (TouchControls.editorPanelCollapsed.value) return@Column
// Scope hint: with no game running the editor edits the GLOBAL Default
// layout (per-game layouts need a running disc).
Text(