Add global keyevent magagement.

This commit is contained in:
Thomas Andres Gomez 2025-02-05 11:19:22 +01:00
parent ce51a3be0a
commit f715b973ff
4 changed files with 68 additions and 2 deletions

View file

@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.ApplicationScope
import androidx.compose.ui.window.Window import androidx.compose.ui.window.Window
import androidx.compose.ui.window.rememberWindowState 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.MainNavHost
import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetDestination import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetDestination
import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetEditDestination import com.pixelized.desktop.lwa.navigation.screen.destination.CharacterSheetEditDestination
@ -68,11 +71,13 @@ fun ApplicationScope.App() {
val snackHostState = remember { SnackbarHostState() } val snackHostState = remember { SnackbarHostState() }
val errorSnackHostState = remember { SnackbarHostState() } val errorSnackHostState = remember { SnackbarHostState() }
val windowController = remember { WindowController() } val windowController = remember { WindowController() }
val keyEventHandlers = remember { mutableStateListOf<KeyEventHandler>() }
CompositionLocalProvider( CompositionLocalProvider(
LocalSnackHost provides snackHostState, LocalSnackHost provides snackHostState,
LocalErrorSnackHost provides errorSnackHostState, LocalErrorSnackHost provides errorSnackHostState,
LocalWindowController provides windowController, LocalWindowController provides windowController,
LocalKeyEventHandlers provides keyEventHandlers,
) { ) {
Window( Window(
onCloseRequest = ::exitApplication, onCloseRequest = ::exitApplication,
@ -81,6 +86,9 @@ fun ApplicationScope.App() {
height = 900.dp, height = 900.dp,
), ),
title = "LwaCharacterSheet", title = "LwaCharacterSheet",
onKeyEvent = { event ->
keyEventHandlers.reversed().any { it(event) }
},
) { ) {
LwaTheme { LwaTheme {
Surface( Surface(
@ -131,7 +139,7 @@ fun ApplicationScope.App() {
@Composable @Composable
private fun WindowsHandler( private fun WindowsHandler(
windowController: WindowController windowController: WindowController,
) { ) {
WindowsNavHost( WindowsNavHost(
controller = windowController, controller = windowController,
@ -230,7 +238,7 @@ fun Snackbar(
backgroundColor: Color = SnackbarDefaults.backgroundColor, backgroundColor: Color = SnackbarDefaults.backgroundColor,
contentColor: Color = MaterialTheme.colors.surface, contentColor: Color = MaterialTheme.colors.surface,
actionColor: Color = SnackbarDefaults.primaryActionColor, actionColor: Color = SnackbarDefaults.primaryActionColor,
elevation: Dp = 6.dp elevation: Dp = 6.dp,
) { ) {
val actionLabel = snackbarData.actionLabel val actionLabel = snackbarData.actionLabel
val actionComposable: (@Composable () -> Unit)? = if (actionLabel != null) { val actionComposable: (@Composable () -> Unit)? = if (actionLabel != null) {

View file

@ -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<MutableList<KeyEventHandler>> {
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)
}
}
}
}

View file

@ -6,10 +6,14 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.key import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.window.Window import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.rememberWindowState 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 import com.pixelized.desktop.lwa.navigation.window.destination.Window
val LocalWindow = compositionLocalOf<Window> { val LocalWindow = compositionLocalOf<Window> {
@ -45,14 +49,20 @@ fun WindowsNavHost(
controller.windows.value.forEach { (id, window) -> controller.windows.value.forEach { (id, window) ->
key(id) { key(id) {
val state = rememberWindowState(size = window.size) val state = rememberWindowState(size = window.size)
val keyEventHandlers = remember { mutableStateListOf<KeyEventHandler>() }
CompositionLocalProvider( CompositionLocalProvider(
LocalWindow provides window, LocalWindow provides window,
LocalWindowState provides state, LocalWindowState provides state,
LocalKeyEventHandlers provides keyEventHandlers,
) { ) {
Window( Window(
onCloseRequest = { controller.hideWindow(id = window.id) }, onCloseRequest = { controller.hideWindow(id = window.id) },
state = state, state = state,
title = window.title, title = window.title,
onKeyEvent = { event ->
keyEventHandlers.reversed().any { it(event) }
},
) { ) {
content.invoke(window) content.invoke(window)
} }

View file

@ -41,9 +41,14 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.graphicsLayer 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.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp 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.screen.roll.DifficultyUio.Difficulty
import com.pixelized.desktop.lwa.utils.DisableInteractionSource import com.pixelized.desktop.lwa.utils.DisableInteractionSource
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -89,6 +94,15 @@ fun RollPage(
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
KeyHandler {
if (it.type == KeyEventType.KeyUp && it.key == Key.Escape) {
onDismissRequest()
true
} else {
false
}
}
Column( Column(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
.clickable( .clickable(