Unlike Pointer Events that automagically propagate down your hierarchy (so you can just have a composable react to the click events), Compose for Desktop exposes an onKeyEvent handler on the Window composable. In effect, that means if I have a composable that should react to keyboard shortcuts, I need to keep the state of that composable on Window-level and pass it into the Composable. (see example)
When sharing code between different platforms, this essentially means I need to duplicate state-initialization logic across the different platforms and pass it into the common composable, rather than neatly store the state inside the composable itself.
The reason why Compose for Desktop provides such a separate handler on Window-level, rather than just use the regular keyboard Modifiers, is that when Compose clears the focus, the focus moves to the root.
This becomes a problem when you have a custom “form” that’s only conditionally visible:
If e.g. a text field has the focus, making it disappear sends the focus back to the root, past any parents, so any Modifier relying on being focused won't respond to keyboard input. (reproduced by @igordmn in 1.3)
A potential approach to resolving this might be a "focus root" concept:
FocusRoot(Modifier.onKeyEvent...) {
...
Button(onClick = focusManager.clearFocus() // move the focus to the nearest FocusRoot
}
(Notable exception: If this dialog is AlertDialog from the material module, then it has own focus manager, so when we close the dialog, the focus of the main screen remains intact)
Unlike Pointer Events that automagically propagate down your hierarchy (so you can just have a composable react to the click events), Compose for Desktop exposes an
onKeyEvent
handler on theWindow
composable. In effect, that means if I have a composable that should react to keyboard shortcuts, I need to keep the state of that composable on Window-level and pass it into the Composable. (see example)When sharing code between different platforms, this essentially means I need to duplicate state-initialization logic across the different platforms and pass it into the common composable, rather than neatly store the state inside the composable itself.
The reason why Compose for Desktop provides such a separate handler on Window-level, rather than just use the regular keyboard Modifiers, is that when Compose clears the focus, the focus moves to the root.
This becomes a problem when you have a custom “form” that’s only conditionally visible: If e.g. a text field has the focus, making it disappear sends the focus back to the root, past any parents, so any
Modifier
relying on being focused won't respond to keyboard input. (reproduced by @igordmn in 1.3)A potential approach to resolving this might be a "focus root" concept:
(Notable exception: If this dialog is AlertDialog from the material module, then it has own focus manager, so when we close the dialog, the focus of the main screen remains intact)
(see also internal discussion at https://kotlinlang.slack.com/archives/G010KHY484C/p1676303717412039)