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
8// `Modifiers` lives in `cranpose-foundation` because `PointerEvent` there
9// needs it too and this crate sits above foundation in the dependency graph;
10// re-exported here so keyboard call sites keep importing it from
11// `cranpose_ui`/`cranpose_ui::key_event`.
12pub use cranpose_foundation::Modifiers;
13
14/// Type of keyboard event.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum KeyEventType {
17    /// Key was pressed down.
18    KeyDown,
19    /// Key was released.
20    KeyUp,
21}
22
23/// Physical key codes for keyboard input.
24///
25/// These represent physical keys on the keyboard, independent of
26/// the character they produce (which depends on keyboard layout).
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum KeyCode {
29    // Letters
30    A,
31    B,
32    C,
33    D,
34    E,
35    F,
36    G,
37    H,
38    I,
39    J,
40    K,
41    L,
42    M,
43    N,
44    O,
45    P,
46    Q,
47    R,
48    S,
49    T,
50    U,
51    V,
52    W,
53    X,
54    Y,
55    Z,
56
57    // Numbers
58    Digit0,
59    Digit1,
60    Digit2,
61    Digit3,
62    Digit4,
63    Digit5,
64    Digit6,
65    Digit7,
66    Digit8,
67    Digit9,
68
69    // Function keys
70    F1,
71    F2,
72    F3,
73    F4,
74    F5,
75    F6,
76    F7,
77    F8,
78    F9,
79    F10,
80    F11,
81    F12,
82
83    // Navigation
84    ArrowUp,
85    ArrowDown,
86    ArrowLeft,
87    ArrowRight,
88    Home,
89    End,
90    PageUp,
91    PageDown,
92
93    // Editing
94    Backspace,
95    Delete,
96    Enter,
97    Tab,
98    Space,
99    Escape,
100
101    // Modifiers (for completeness, usually detected via Modifiers struct)
102    ShiftLeft,
103    ShiftRight,
104    ControlLeft,
105    ControlRight,
106    AltLeft,
107    AltRight,
108    MetaLeft,
109    MetaRight,
110
111    // Punctuation and symbols
112    Minus,
113    Equal,
114    BracketLeft,
115    BracketRight,
116    Backslash,
117    Semicolon,
118    Quote,
119    Comma,
120    Period,
121    Slash,
122    Backquote,
123
124    /// Key not recognized or not mapped.
125    Unknown,
126}
127
128/// A keyboard input event.
129///
130/// Contains information about which key was pressed/released,
131/// the text it produces (if any), and modifier state.
132#[derive(Debug, Clone, PartialEq, Eq)]
133pub struct KeyEvent {
134    /// The physical key that was pressed.
135    pub key_code: KeyCode,
136    /// The text produced by this key press (may be empty for non-character keys).
137    /// This accounts for keyboard layout and modifiers (e.g., Shift+A = "A").
138    pub text: String,
139    /// Current state of modifier keys.
140    pub modifiers: Modifiers,
141    /// Type of event (down or up).
142    pub event_type: KeyEventType,
143}
144
145impl KeyEvent {
146    /// Creates a new key event.
147    pub fn new(
148        key_code: KeyCode,
149        text: impl Into<String>,
150        modifiers: Modifiers,
151        event_type: KeyEventType,
152    ) -> Self {
153        Self {
154            key_code,
155            text: text.into(),
156            modifiers,
157            event_type,
158        }
159    }
160
161    /// Creates a key down event with the given key code and text.
162    pub fn key_down(key_code: KeyCode, text: impl Into<String>) -> Self {
163        Self::new(key_code, text, Modifiers::NONE, KeyEventType::KeyDown)
164    }
165
166    /// Creates a key down event with modifiers.
167    pub fn key_down_with_modifiers(
168        key_code: KeyCode,
169        text: impl Into<String>,
170        modifiers: Modifiers,
171    ) -> Self {
172        Self::new(key_code, text, modifiers, KeyEventType::KeyDown)
173    }
174
175    /// Returns true if this is a key down event.
176    pub fn is_key_down(&self) -> bool {
177        self.event_type == KeyEventType::KeyDown
178    }
179
180    /// Returns true if this key produces printable text.
181    pub fn has_text(&self) -> bool {
182        !self.text.is_empty()
183    }
184}
185
186impl fmt::Display for KeyEvent {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        write!(
189            f,
190            "KeyEvent({:?}, text=\"{}\", {:?})",
191            self.key_code, self.text, self.event_type
192        )
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn key_event_creation() {
202        let event = KeyEvent::key_down(KeyCode::A, "a");
203        assert_eq!(event.key_code, KeyCode::A);
204        assert_eq!(event.text, "a");
205        assert!(event.is_key_down());
206        assert!(event.has_text());
207    }
208
209    #[test]
210    fn key_event_with_modifiers() {
211        let modifiers = Modifiers {
212            shift: true,
213            ctrl: false,
214            alt: false,
215            meta: false,
216        };
217        let event = KeyEvent::key_down_with_modifiers(KeyCode::A, "A", modifiers);
218        assert_eq!(event.text, "A");
219        assert!(event.modifiers.shift);
220    }
221
222    #[test]
223    fn backspace_has_no_text() {
224        let event = KeyEvent::key_down(KeyCode::Backspace, "");
225        assert!(!event.has_text());
226    }
227}