Skip to main content

cranpose_ui/
key_event.rs

1//! Keyboard input event types for Cranpose.
2//!
3//! This module provides platform-independent keyboard event types
4//! that are used to route keyboard input to focused components.
5
6use std::fmt;
7
8use cranpose_core::collections::perfect_map::PerfectMap;
9pub use cranpose_foundation::Modifiers;
10
11/// Type of keyboard event.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum KeyEventType {
14    /// Key was pressed down.
15    KeyDown,
16    /// Key was released.
17    KeyUp,
18}
19
20/// A key on a keyboard, remote control or game controller, named as in
21/// Jetpack Compose's `androidx.compose.ui.input.key.Key`.
22///
23/// Letters and digits name the physical key, independent of the character
24/// the keyboard layout gives it.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub enum Key {
27    /// The A key.
28    A,
29    /// The B key.
30    B,
31    /// The C key.
32    C,
33    /// The D key.
34    D,
35    /// The E key.
36    E,
37    /// The F key.
38    F,
39    /// The G key.
40    G,
41    /// The H key.
42    H,
43    /// The I key.
44    I,
45    /// The J key.
46    J,
47    /// The K key.
48    K,
49    /// The L key.
50    L,
51    /// The M key.
52    M,
53    /// The N key.
54    N,
55    /// The O key.
56    O,
57    /// The P key.
58    P,
59    /// The Q key.
60    Q,
61    /// The R key.
62    R,
63    /// The S key.
64    S,
65    /// The T key.
66    T,
67    /// The U key.
68    U,
69    /// The V key.
70    V,
71    /// The W key.
72    W,
73    /// The X key.
74    X,
75    /// The Y key.
76    Y,
77    /// The Z key.
78    Z,
79
80    /// The 0 key on the main number row.
81    Zero,
82    /// The 1 key on the main number row.
83    One,
84    /// The 2 key on the main number row.
85    Two,
86    /// The 3 key on the main number row.
87    Three,
88    /// The 4 key on the main number row.
89    Four,
90    /// The 5 key on the main number row.
91    Five,
92    /// The 6 key on the main number row.
93    Six,
94    /// The 7 key on the main number row.
95    Seven,
96    /// The 8 key on the main number row.
97    Eight,
98    /// The 9 key on the main number row.
99    Nine,
100
101    /// The F1 function key.
102    F1,
103    /// The F2 function key.
104    F2,
105    /// The F3 function key.
106    F3,
107    /// The F4 function key.
108    F4,
109    /// The F5 function key.
110    F5,
111    /// The F6 function key.
112    F6,
113    /// The F7 function key.
114    F7,
115    /// The F8 function key.
116    F8,
117    /// The F9 function key.
118    F9,
119    /// The F10 function key.
120    F10,
121    /// The F11 function key.
122    F11,
123    /// The F12 function key.
124    F12,
125
126    /// The up arrow key, or up on a D-pad.
127    DirectionUp,
128    /// The down arrow key, or down on a D-pad.
129    DirectionDown,
130    /// The left arrow key, or left on a D-pad.
131    DirectionLeft,
132    /// The right arrow key, or right on a D-pad.
133    DirectionRight,
134    /// The center button of a D-pad: OK or Select on a remote.
135    DirectionCenter,
136    /// The Home key, which moves to the start of a line or list.
137    MoveHome,
138    /// The End key, which moves to the end of a line or list.
139    MoveEnd,
140    /// The Page Up key.
141    PageUp,
142    /// The Page Down key.
143    PageDown,
144
145    /// The Backspace key, which deletes backward.
146    Backspace,
147    /// The Delete key, which deletes forward.
148    Delete,
149    /// The main Enter or Return key.
150    Enter,
151    /// The Enter key on the number pad.
152    NumPadEnter,
153    /// The Tab key.
154    Tab,
155    /// The space bar.
156    Spacebar,
157    /// The Escape key.
158    Escape,
159
160    /// The left Shift key.
161    ShiftLeft,
162    /// The right Shift key.
163    ShiftRight,
164    /// The left Control key.
165    CtrlLeft,
166    /// The right Control key.
167    CtrlRight,
168    /// The left Alt key, called Option on macOS.
169    AltLeft,
170    /// The right Alt key, called Option on macOS.
171    AltRight,
172    /// The left Meta key: Command on macOS, the Windows key elsewhere.
173    MetaLeft,
174    /// The right Meta key: Command on macOS, the Windows key elsewhere.
175    MetaRight,
176
177    /// The minus and underscore key on US layouts.
178    Minus,
179    /// The equals and plus key on US layouts.
180    Equals,
181    /// The left square bracket key on US layouts.
182    LeftBracket,
183    /// The right square bracket key on US layouts.
184    RightBracket,
185    /// The backslash key on US layouts.
186    Backslash,
187    /// The semicolon and colon key on US layouts.
188    Semicolon,
189    /// The apostrophe and quotation mark key on US layouts.
190    Apostrophe,
191    /// The comma key on US layouts.
192    Comma,
193    /// The period key on US layouts.
194    Period,
195    /// The forward slash key on US layouts.
196    Slash,
197    /// The grave accent and tilde key on US layouts, left of the 1 key.
198    Grave,
199
200    /// The Back key of a remote or of the Android system navigation.
201    Back,
202    /// The Menu key.
203    Menu,
204    /// The Search key of a remote.
205    Search,
206    /// The Info key of a remote.
207    Info,
208    /// The Guide key of a remote, which opens the program guide.
209    Guide,
210    /// The Settings key of a remote.
211    Settings,
212    /// The Channel Up key of a remote.
213    ChannelUp,
214    /// The Channel Down key of a remote.
215    ChannelDown,
216    /// The red program key of a remote.
217    ProgramRed,
218    /// The green program key of a remote.
219    ProgramGreen,
220    /// The yellow program key of a remote.
221    ProgramYellow,
222    /// The blue program key of a remote.
223    ProgramBlue,
224
225    /// The Play media key.
226    MediaPlay,
227    /// The Pause media key.
228    MediaPause,
229    /// The Play/Pause media key.
230    MediaPlayPause,
231    /// The Stop media key.
232    MediaStop,
233    /// The Next track media key.
234    MediaNext,
235    /// The Previous track media key.
236    MediaPrevious,
237    /// The Rewind media key.
238    MediaRewind,
239    /// The Fast Forward media key.
240    MediaFastForward,
241    /// The Record media key.
242    MediaRecord,
243
244    /// The A button of a game controller.
245    ButtonA,
246    /// The B button of a game controller.
247    ButtonB,
248    /// The X button of a game controller.
249    ButtonX,
250    /// The Y button of a game controller.
251    ButtonY,
252    /// The Select button of a game controller.
253    ButtonSelect,
254    /// The Start button of a game controller.
255    ButtonStart,
256
257    /// Key not recognized or not mapped.
258    Unknown,
259}
260
261// W3C `KeyboardEvent.code` values, found by a hash that gives each its own
262// slot: 4.2 ns a lookup where a scan of the 93 took 27 (M3 Pro, every code
263// and five misses in a shuffled order).
264static DOM_KEY_CODES: PerfectMap<Key, 93> = PerfectMap::new([
265    ("KeyA", Key::A),
266    ("KeyB", Key::B),
267    ("KeyC", Key::C),
268    ("KeyD", Key::D),
269    ("KeyE", Key::E),
270    ("KeyF", Key::F),
271    ("KeyG", Key::G),
272    ("KeyH", Key::H),
273    ("KeyI", Key::I),
274    ("KeyJ", Key::J),
275    ("KeyK", Key::K),
276    ("KeyL", Key::L),
277    ("KeyM", Key::M),
278    ("KeyN", Key::N),
279    ("KeyO", Key::O),
280    ("KeyP", Key::P),
281    ("KeyQ", Key::Q),
282    ("KeyR", Key::R),
283    ("KeyS", Key::S),
284    ("KeyT", Key::T),
285    ("KeyU", Key::U),
286    ("KeyV", Key::V),
287    ("KeyW", Key::W),
288    ("KeyX", Key::X),
289    ("KeyY", Key::Y),
290    ("KeyZ", Key::Z),
291    ("Digit0", Key::Zero),
292    ("Digit1", Key::One),
293    ("Digit2", Key::Two),
294    ("Digit3", Key::Three),
295    ("Digit4", Key::Four),
296    ("Digit5", Key::Five),
297    ("Digit6", Key::Six),
298    ("Digit7", Key::Seven),
299    ("Digit8", Key::Eight),
300    ("Digit9", Key::Nine),
301    ("F1", Key::F1),
302    ("F2", Key::F2),
303    ("F3", Key::F3),
304    ("F4", Key::F4),
305    ("F5", Key::F5),
306    ("F6", Key::F6),
307    ("F7", Key::F7),
308    ("F8", Key::F8),
309    ("F9", Key::F9),
310    ("F10", Key::F10),
311    ("F11", Key::F11),
312    ("F12", Key::F12),
313    ("ArrowUp", Key::DirectionUp),
314    ("ArrowDown", Key::DirectionDown),
315    ("ArrowLeft", Key::DirectionLeft),
316    ("ArrowRight", Key::DirectionRight),
317    ("Home", Key::MoveHome),
318    ("End", Key::MoveEnd),
319    ("PageUp", Key::PageUp),
320    ("PageDown", Key::PageDown),
321    ("Backspace", Key::Backspace),
322    ("Delete", Key::Delete),
323    ("Enter", Key::Enter),
324    ("NumpadEnter", Key::NumPadEnter),
325    ("Tab", Key::Tab),
326    ("Space", Key::Spacebar),
327    ("Escape", Key::Escape),
328    ("ShiftLeft", Key::ShiftLeft),
329    ("ShiftRight", Key::ShiftRight),
330    ("ControlLeft", Key::CtrlLeft),
331    ("ControlRight", Key::CtrlRight),
332    ("AltLeft", Key::AltLeft),
333    ("AltRight", Key::AltRight),
334    ("MetaLeft", Key::MetaLeft),
335    ("MetaRight", Key::MetaRight),
336    ("Minus", Key::Minus),
337    ("Equal", Key::Equals),
338    ("BracketLeft", Key::LeftBracket),
339    ("BracketRight", Key::RightBracket),
340    ("Backslash", Key::Backslash),
341    ("Semicolon", Key::Semicolon),
342    ("Quote", Key::Apostrophe),
343    ("Comma", Key::Comma),
344    ("Period", Key::Period),
345    ("Slash", Key::Slash),
346    ("Backquote", Key::Grave),
347    ("ContextMenu", Key::Menu),
348    ("BrowserSearch", Key::Search),
349    ("MediaPlay", Key::MediaPlay),
350    ("MediaPause", Key::MediaPause),
351    ("MediaPlayPause", Key::MediaPlayPause),
352    ("MediaStop", Key::MediaStop),
353    ("MediaTrackNext", Key::MediaNext),
354    ("MediaTrackPrevious", Key::MediaPrevious),
355    ("MediaRewind", Key::MediaRewind),
356    ("MediaFastForward", Key::MediaFastForward),
357    ("MediaRecord", Key::MediaRecord),
358]);
359
360// W3C `KeyboardEvent.key` values of the keys a TV remote sends, which TV
361// browsers report with no `code`.
362static DOM_KEY_VALUES: PerfectMap<Key, 23> = PerfectMap::new([
363    ("GoBack", Key::Back),
364    ("BrowserBack", Key::Back),
365    ("XF86Back", Key::Back),
366    ("Enter", Key::Enter),
367    ("ContextMenu", Key::Menu),
368    ("BrowserSearch", Key::Search),
369    ("Info", Key::Info),
370    ("Guide", Key::Guide),
371    ("Settings", Key::Settings),
372    ("ChannelUp", Key::ChannelUp),
373    ("ChannelDown", Key::ChannelDown),
374    ("ColorF0Red", Key::ProgramRed),
375    ("ColorF1Green", Key::ProgramGreen),
376    ("ColorF2Yellow", Key::ProgramYellow),
377    ("ColorF3Blue", Key::ProgramBlue),
378    ("MediaPlay", Key::MediaPlay),
379    ("MediaPause", Key::MediaPause),
380    ("MediaPlayPause", Key::MediaPlayPause),
381    ("MediaStop", Key::MediaStop),
382    ("MediaTrackNext", Key::MediaNext),
383    ("MediaTrackPrevious", Key::MediaPrevious),
384    ("MediaRewind", Key::MediaRewind),
385    ("MediaFastForward", Key::MediaFastForward),
386]);
387
388// The key codes Samsung's Tizen and LG's webOS give their remotes' Back key.
389const TV_BACK_KEY_CODES: [u32; 2] = [10009, 461];
390
391impl Key {
392    /// The key a W3C `KeyboardEvent.code` value names, such as `"KeyA"`,
393    /// `"ArrowUp"` or `"MediaPlayPause"`. A code with no counterpart here is
394    /// [`Key::Unknown`].
395    pub fn from_dom_code(code: &str) -> Self {
396        DOM_KEY_CODES.get(code).copied().unwrap_or(Self::Unknown)
397    }
398
399    /// The key a browser event names, as a TV browser reports a remote: by
400    /// its `code` when it has one, else by its `key` value (`"GoBack"`,
401    /// `"MediaPlayPause"`, `"ChannelUp"`), else by the `keyCode` Tizen
402    /// (10009) and webOS (461) give the Back key.
403    pub fn from_dom_event(code: &str, key: &str, key_code: u32) -> Self {
404        match Self::from_dom_code(code) {
405            Self::Unknown if TV_BACK_KEY_CODES.contains(&key_code) => Self::Back,
406            Self::Unknown => DOM_KEY_VALUES.get(key).copied().unwrap_or(Self::Unknown),
407            known => known,
408        }
409    }
410}
411
412/// What the platform reported about a key event beyond the key, as
413/// `KeyEvent.nativeKeyEvent` holds in Compose.
414#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
415pub struct NativeKeyEvent {
416    /// How many times the key repeated while held: 0 for the press itself,
417    /// 1 for the first repeat, and so on on Android. The desktop and the web
418    /// report only whether an event repeats, so every repeat there is 1.
419    pub repeat_count: u32,
420}
421
422/// A keyboard input event.
423///
424/// Holds the key, whether it went down or up, the modifier keys held with it
425/// and the text it typed.
426#[derive(Debug, Clone, PartialEq, Eq)]
427pub struct KeyEvent {
428    key: Key,
429    text: String,
430    modifiers: Modifiers,
431    type_: KeyEventType,
432    native: NativeKeyEvent,
433}
434
435impl KeyEvent {
436    /// Creates a key event. `text` is what the key typed with the keyboard
437    /// layout and modifiers applied (Shift+A types "A"), empty for keys that
438    /// type nothing.
439    pub fn new(
440        key: Key,
441        text: impl Into<String>,
442        modifiers: Modifiers,
443        type_: KeyEventType,
444    ) -> Self {
445        Self {
446            key,
447            text: text.into(),
448            modifiers,
449            type_,
450            native: NativeKeyEvent::default(),
451        }
452    }
453
454    /// Creates a key down event with the given key and text.
455    pub fn key_down(key: Key, text: impl Into<String>) -> Self {
456        Self::new(key, text, Modifiers::NONE, KeyEventType::KeyDown)
457    }
458
459    /// Creates a key up event for `key`.
460    pub fn key_up(key: Key) -> Self {
461        Self::new(key, "", Modifiers::NONE, KeyEventType::KeyUp)
462    }
463
464    /// Creates a key down event with modifiers.
465    pub fn key_down_with_modifiers(
466        key: Key,
467        text: impl Into<String>,
468        modifiers: Modifiers,
469    ) -> Self {
470        Self::new(key, text, modifiers, KeyEventType::KeyDown)
471    }
472
473    /// This event as the `repeat_count`th repeat of a held key.
474    #[must_use]
475    pub fn with_repeat_count(mut self, repeat_count: u32) -> Self {
476        self.native.repeat_count = repeat_count;
477        self
478    }
479
480    /// The key that went down or up.
481    pub fn key(&self) -> Key {
482        self.key
483    }
484
485    /// Whether the key went down or up.
486    pub fn type_(&self) -> KeyEventType {
487        self.type_
488    }
489
490    /// The text this key typed, empty for keys that type nothing.
491    pub fn text(&self) -> &str {
492        &self.text
493    }
494
495    /// The modifier keys held with this key.
496    pub fn modifiers(&self) -> Modifiers {
497        self.modifiers
498    }
499
500    /// Whether a Shift key was held.
501    pub fn is_shift_pressed(&self) -> bool {
502        self.modifiers.shift
503    }
504
505    /// Whether a Control key was held.
506    pub fn is_ctrl_pressed(&self) -> bool {
507        self.modifiers.ctrl
508    }
509
510    /// Whether an Alt key was held.
511    pub fn is_alt_pressed(&self) -> bool {
512        self.modifiers.alt
513    }
514
515    /// Whether a Meta key was held.
516    pub fn is_meta_pressed(&self) -> bool {
517        self.modifiers.meta
518    }
519
520    /// The Unicode code point of the character this key typed, if it typed
521    /// one.
522    pub fn utf16_code_point(&self) -> Option<u32> {
523        self.text.chars().next().map(u32::from)
524    }
525
526    /// What the platform reported beyond the key, such as the repeat count.
527    pub fn native_key_event(&self) -> NativeKeyEvent {
528        self.native
529    }
530
531    /// Returns true if this is a key down event.
532    pub fn is_key_down(&self) -> bool {
533        self.type_ == KeyEventType::KeyDown
534    }
535
536    /// Returns true if this key produces printable text.
537    pub fn has_text(&self) -> bool {
538        !self.text.is_empty()
539    }
540}
541
542impl fmt::Display for KeyEvent {
543    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
544        write!(
545            f,
546            "KeyEvent({:?}, text=\"{}\", {:?})",
547            self.key, self.text, self.type_
548        )
549    }
550}
551
552#[cfg(test)]
553#[path = "tests/key_event_tests.rs"]
554mod tests;