From f715b973ff5446a7372a562aa00acb88a6c97938 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Wed, 5 Feb 2025 11:19:22 +0100 Subject: [PATCH] Add global keyevent magagement. --- .../kotlin/com/pixelized/desktop/lwa/App.kt | 12 +++++-- .../desktop/lwa/composable/key/KeyHandler.kt | 34 +++++++++++++++++++ .../lwa/navigation/window/WindowNavHost.kt | 10 ++++++ .../desktop/lwa/screen/roll/RollPage.kt | 14 ++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/composable/key/KeyHandler.kt diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt index e3f0e95..8bd7241 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -29,6 +30,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.ApplicationScope import androidx.compose.ui.window.Window import androidx.compose.ui.window.rememberWindowState +import com.pixelized.desktop.lwa.composable.key.KeyEventHandler +import com.pixelized.desktop.lwa.composable.key.LocalKeyEventHandlers import com.pixelized.desktop.lwa.navigation.screen.MainNavHost import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetDestination import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetEditDestination @@ -68,11 +71,13 @@ fun ApplicationScope.App() { val snackHostState = remember { SnackbarHostState() } val errorSnackHostState = remember { SnackbarHostState() } val windowController = remember { WindowController() } + val keyEventHandlers = remember { mutableStateListOf() } CompositionLocalProvider( LocalSnackHost provides snackHostState, LocalErrorSnackHost provides errorSnackHostState, LocalWindowController provides windowController, + LocalKeyEventHandlers provides keyEventHandlers, ) { Window( onCloseRequest = ::exitApplication, @@ -81,6 +86,9 @@ fun ApplicationScope.App() { height = 900.dp, ), title = "LwaCharacterSheet", + onKeyEvent = { event -> + keyEventHandlers.reversed().any { it(event) } + }, ) { LwaTheme { Surface( @@ -131,7 +139,7 @@ fun ApplicationScope.App() { @Composable private fun WindowsHandler( - windowController: WindowController + windowController: WindowController, ) { WindowsNavHost( controller = windowController, @@ -230,7 +238,7 @@ fun Snackbar( backgroundColor: Color = SnackbarDefaults.backgroundColor, contentColor: Color = MaterialTheme.colors.surface, actionColor: Color = SnackbarDefaults.primaryActionColor, - elevation: Dp = 6.dp + elevation: Dp = 6.dp, ) { val actionLabel = snackbarData.actionLabel val actionComposable: (@Composable () -> Unit)? = if (actionLabel != null) { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/composable/key/KeyHandler.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/composable/key/KeyHandler.kt new file mode 100644 index 0000000..e154119 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/composable/key/KeyHandler.kt @@ -0,0 +1,34 @@ +package com.pixelized.desktop.lwa.composable.key + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.input.key.KeyEvent + +typealias KeyEventHandler = (KeyEvent) -> Boolean + +val LocalKeyEventHandlers = staticCompositionLocalOf> { + error("LocalKeyEventHandlers is not provided") +} + +@Composable +fun KeyHandler( + key: Any = "KeyHandler", + enabled: Boolean = true, + onKeyPress: (KeyEvent) -> Boolean, +) { + if (enabled) { + val handlerState = rememberUpdatedState(onKeyPress) + val eventHandlers = LocalKeyEventHandlers.current + DisposableEffect(key) { + val localHandler: KeyEventHandler = { + handlerState.value(it) + } + eventHandlers.add(localHandler) + onDispose { + eventHandlers.remove(localHandler) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/window/WindowNavHost.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/window/WindowNavHost.kt index 87d6bf1..3b78df6 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/window/WindowNavHost.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/navigation/window/WindowNavHost.kt @@ -6,10 +6,14 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.State import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.key +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.rememberWindowState +import com.pixelized.desktop.lwa.composable.key.KeyEventHandler +import com.pixelized.desktop.lwa.composable.key.LocalKeyEventHandlers import com.pixelized.desktop.lwa.navigation.window.destination.Window val LocalWindow = compositionLocalOf { @@ -45,14 +49,20 @@ fun WindowsNavHost( controller.windows.value.forEach { (id, window) -> key(id) { val state = rememberWindowState(size = window.size) + val keyEventHandlers = remember { mutableStateListOf() } + CompositionLocalProvider( LocalWindow provides window, LocalWindowState provides state, + LocalKeyEventHandlers provides keyEventHandlers, ) { Window( onCloseRequest = { controller.hideWindow(id = window.id) }, state = state, title = window.title, + onKeyEvent = { event -> + keyEventHandlers.reversed().any { it(event) } + }, ) { content.invoke(window) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt index ce73b0d..b7e9c18 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/screen/roll/RollPage.kt @@ -41,9 +41,14 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.type import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.composable.key.KeyHandler import com.pixelized.desktop.lwa.screen.roll.DifficultyUio.Difficulty import com.pixelized.desktop.lwa.utils.DisableInteractionSource import kotlinx.coroutines.launch @@ -89,6 +94,15 @@ fun RollPage( ) { val scope = rememberCoroutineScope() + KeyHandler { + if (it.type == KeyEventType.KeyUp && it.key == Key.Escape) { + onDismissRequest() + true + } else { + false + } + } + Column( modifier = Modifier.fillMaxSize() .clickable(