Desktop: make Ctrl+-/= zoom actually work

Two compounding bugs:
1. Nodes skipped in a plain recomposition kept their old captured
   density, so changing the zoom state left the tree unscaled — wrap the
   CompositionLocalProvider in key(zoom) to force full-tree recompose.
2. Compose focus routing missed the shortcut depending on which child
   held focus — add an AWT KeyboardFocusManager dispatcher below Compose
   (also accepts Ctrl+Up/Down, numpad +/-, Ctrl+0 reset).

User-confirmed zooming works on the live app.
This commit is contained in:
avi 2026-09-15 06:52:30 -05:00
commit 5859a082d2

View file

@ -5,7 +5,9 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.key
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -35,6 +37,41 @@ fun main() = application {
val screen by state.screen.collectAsState()
// Ctrl+= / Ctrl+- zoom the whole app; Ctrl+0 resets (after a zoom).
var zoom by remember { mutableStateOf(1f) }
// Belt-and-braces: Compose's focus routing can miss shortcuts depending
// on which child holds focus, so also hook the AWT key pipeline directly.
// Debug print lets us confirm from the dev log whether keys reach the JVM.
DisposableEffect(Unit) {
val mgr = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager()
val disp = java.awt.KeyEventDispatcher { e ->
if (e.id == java.awt.event.KeyEvent.KEY_PRESSED &&
e.modifiers == java.awt.event.KeyEvent.CTRL_MASK
) {
when (e.keyCode) {
java.awt.event.KeyEvent.VK_EQUALS,
java.awt.event.KeyEvent.VK_PLUS,
java.awt.event.KeyEvent.VK_ADD,
java.awt.event.KeyEvent.VK_UP -> {
zoom = (zoom * 1.1f).coerceAtMost(3f)
System.err.println("[zoom] ${zoom}"); true
}
java.awt.event.KeyEvent.VK_MINUS,
java.awt.event.KeyEvent.VK_SUBTRACT,
java.awt.event.KeyEvent.VK_DOWN -> {
zoom = (zoom / 1.1f).coerceAtLeast(0.4f)
System.err.println("[zoom] ${zoom}"); true
}
java.awt.event.KeyEvent.VK_0,
java.awt.event.KeyEvent.VK_NUMPAD0 -> {
if (zoom != 1f) { zoom = 1f; System.err.println("[zoom] reset"); true }
else false
}
else -> false
}
} else false
}
mgr.addKeyEventDispatcher(disp)
onDispose { mgr.removeKeyEventDispatcher(disp) }
}
Window(
onCloseRequest = ::exitApplication,
title = "SHONAR Desktop" + run {
@ -56,10 +93,10 @@ fun main() = application {
Modifier.fillMaxSize().onPreviewKeyEvent { e ->
if (e.type != KeyEventType.KeyDown || !e.isCtrlPressed) return@onPreviewKeyEvent false
when (e.key) {
Key.Equals, Key.Plus, Key.NumPadAdd -> {
Key.Equals, Key.Plus, Key.NumPadAdd, Key.DirectionUp -> {
zoom = (zoom * 1.1f).coerceAtMost(3f); true
}
Key.Minus, Key.NumPadSubtract -> {
Key.Minus, Key.NumPadSubtract, Key.DirectionDown -> {
zoom = (zoom / 1.1f).coerceAtLeast(0.4f); true
}
Key.Zero, Key.NumPad0 -> {
@ -69,6 +106,10 @@ fun main() = application {
}
},
) {
// key(zoom) forces the whole tree to recompose when zoom
// changes: nodes skipped in a plain recomposition keep their
// old captured density, which made Ctrl+-/= a no-op.
key(zoom) {
CompositionLocalProvider(
LocalDensity provides ScaledDensity(baseDensity, zoom),
) {
@ -81,6 +122,7 @@ fun main() = application {
}
}
}
}
}
}
}