Keyboard input
Key handling is simple with Doodle; simply include the KeyboardModule
when launching your app, and the underlying framework uses it to produce key events.
You must include the KeyboardModule
(Web, Desktop) in your application in order to use these features.
package keyboard
import io.nacular.doodle.application.Application
import io.nacular.doodle.application.Modules.Companion.KeyboardModule
import io.nacular.doodle.application.application
import io.nacular.doodle.core.Display
import org.kodein.di.instance
import rendering.MyApp
//sampleStart
fun main() {
/** Include [KeyboardModule] when launching your app */
application(modules = listOf(KeyboardModule)) {
MyApp(instance())
}
}
/**
* Key events will fire for this app when launched with [KeyboardModule]
*/
class MyApp(display: Display): Application {
override fun shutdown() {}
}
//sampleEnd
Doodle uses opt-in modules like this to improve bundle size.
A View must gain focus in order to begin receiving key events. This ensures that only a single View can receive key events at any time within the app.
Use the FocusManager
to control focus. It is included in the KeyboardModule
. Just inject it into your app to begin managing the focus.
package focus
import io.nacular.doodle.application.Application
import io.nacular.doodle.application.Modules.Companion.KeyboardModule
import io.nacular.doodle.application.application
import io.nacular.doodle.core.Display
import io.nacular.doodle.core.view
import io.nacular.doodle.focus.FocusManager
import org.kodein.di.instance
//sampleStart
fun main() {
application(modules = listOf(KeyboardModule)) {
// FocusManager is available in the KeyboardModule
MyApp(display = instance(), focusManager = instance())
}
}
class MyApp(display: Display, focusManager: FocusManager): Application {
init {
val view = view {}
display += view
// ...
focusManager.requestFocus(view)
// ...
}
override fun shutdown() {}
}
//sampleEnd
Some controls (i.e. TextField) also manage their focus when styled in the native theme
Key Listeners
Views are able to receive key events once the KeyboardModule
(Web, Desktop) is loaded and they have focus
. You can then attach a KeyListener
to any View and get notified whenever it has focus and a key is Pressed or Released.
You get these notifications by registering with a View's keyChanged
property.
package keyboard
import io.nacular.doodle.core.View
import io.nacular.doodle.event.KeyEvent
import io.nacular.doodle.event.KeyListener
import io.nacular.doodle.event.KeyListener.Companion.on
import io.nacular.doodle.event.KeyListener.Companion.pressed
fun example(view: View) {
//sampleStart
// Listen to pressed/released via interface override
view.keyChanged += object: KeyListener {
override fun pressed(event: KeyEvent) {
// ..
}
override fun released(event: KeyEvent) {
// ..
}
}
// Listener to pressed via DSL
view.keyChanged += pressed { event -> /* .. */ }
// Listen to pressed/released via DSL
view.keyChanged += on(
pressed = { event -> /* .. */ },
released = { event -> /* .. */ },
)
//sampleEnd
}
KeyListener
has no-op defaults for the 2 events, so you only need to implement the ones you need.
Notice that keyChanged
--like other observable properties--supports many observers and enables you to add/remove
an observer any time.
Key events
The event provided to key listeners carries information about the View it originated from (source
), and various attributes about the key that was pressed or released.
Key events are consumable. This means any observer can call consume
on the event and prevent subsequent listeners from receiving it.
package keyboard
import io.nacular.doodle.core.View
import io.nacular.doodle.event.KeyListener.Companion.pressed
fun consume(view: View) {
//sampleStart
view.keyChanged += pressed { event ->
// ... take action based on event
event.consume() // indicate that no other listeners should be notified
}
//sampleEnd
}
Virtual keys and text
KeyEvent
's key
is a layout independent identifier that tells you which "virtual key" was pressed or which text the key can be translated into. Most key handling use-cases should use this property to compare keys.
package keyboard
import io.nacular.doodle.core.View
import io.nacular.doodle.event.KeyListener.Companion.pressed
import io.nacular.doodle.event.KeyText.Companion.Backspace
import io.nacular.doodle.event.KeyText.Companion.Enter
fun virtualKeys(view: View) {
//sampleStart
view.keyChanged += pressed { event ->
when (event.key) {
Enter -> { /* ... */ }
Backspace -> { /* ... */ }
// ...
}
}
view.keyChanged += pressed { event ->
// this will be user-appropriate text when the key pressed is not
// one of the "named" keys (i.e. Tab, Shift, Enter, ...)
event.key.text
}
//sampleEnd
}
Physical keys
Some applications will require the use of "physical" keys instead of virtual ones. This makes sense for games or other apps where the key position on a physical keyboard matters.
This information comes from KeyEvent
's code
.
package keyboard
import io.nacular.doodle.core.View
import io.nacular.doodle.event.KeyCode.Companion.AltLeft
import io.nacular.doodle.event.KeyCode.Companion.AltRight
import io.nacular.doodle.event.KeyCode.Companion.Backspace
import io.nacular.doodle.event.KeyListener.Companion.pressed
fun physicalKeys(view: View) {
//sampleStart
view.keyChanged += pressed { event ->
when (event.code) {
AltLeft -> { /* ... */ }
AltRight -> { /* ... */ }
Backspace -> { /* ... */ }
// ...
}
}
//sampleEnd
}
Physical keys do not take keyboard differences and locale into account; so avoid them if possible