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