Desktop: app-wide zoom (Ctrl+=/-, Ctrl+0 resets) via scaled Density

This commit is contained in:
avi 2026-09-14 08:03:33 -05:00
commit 4944fcd162

View file

@ -4,31 +4,71 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.isCtrlPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState import androidx.compose.ui.window.rememberWindowState
/** Scales every dp/sp in the app uniformly (fonts, paddings, controls). */
private data class ScaledDensity(val base: Density, val zoom: Float) : Density {
override val density: Float get() = base.density * zoom
override val fontScale: Float get() = base.fontScale * zoom
}
fun main() = application { fun main() = application {
val state = remember { DesktopState() } val state = remember { DesktopState() }
val screen by state.screen.collectAsState() val screen by state.screen.collectAsState()
// Ctrl+= / Ctrl+- zoom the whole app; Ctrl+0 resets (after a zoom).
var zoom by remember { mutableStateOf(1f) }
Window( Window(
onCloseRequest = ::exitApplication, onCloseRequest = ::exitApplication,
title = "SHONAR Desktop", title = "SHONAR Desktop",
state = rememberWindowState(width = 1100.dp, height = 800.dp), state = rememberWindowState(width = 1100.dp, height = 800.dp),
) { ) {
val baseDensity = LocalDensity.current
ShonarTheme { ShonarTheme {
Surface(Modifier.fillMaxSize()) { Surface(
Box(Modifier.fillMaxSize().padding(20.dp)) { Modifier.fillMaxSize().onPreviewKeyEvent { e ->
when (screen) { if (e.type != KeyEventType.KeyDown || !e.isCtrlPressed) return@onPreviewKeyEvent false
DesktopState.Screen.ENGINE -> EngineScreen(state) when (e.key) {
DesktopState.Screen.LIBRARY -> LibraryScreen(state) Key.Equals, Key.Plus, Key.NumPadAdd -> {
DesktopState.Screen.DETAIL -> DetailScreen(state) zoom = (zoom * 1.1f).coerceAtMost(3f); true
DesktopState.Screen.SETTINGS -> SettingsScreen(state) }
Key.Minus, Key.NumPadSubtract -> {
zoom = (zoom / 1.1f).coerceAtLeast(0.4f); true
}
Key.Zero, Key.NumPad0 -> {
if (zoom != 1f) { zoom = 1f; true } else false
}
else -> false
}
},
) {
CompositionLocalProvider(
LocalDensity provides ScaledDensity(baseDensity, zoom),
) {
Box(Modifier.fillMaxSize().padding(20.dp)) {
when (screen) {
DesktopState.Screen.ENGINE -> EngineScreen(state)
DesktopState.Screen.LIBRARY -> LibraryScreen(state)
DesktopState.Screen.DETAIL -> DetailScreen(state)
DesktopState.Screen.SETTINGS -> SettingsScreen(state)
}
} }
} }
} }