Skip to main content

jag_ui/
event.rs

1//! Standalone event types and the `EventHandler` trait.
2//!
3//! These types are self-contained — no external platform dependencies.
4
5/// Mouse button identifier.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum MouseButton {
8    Left,
9    Right,
10    Middle,
11}
12
13/// Whether a key or button is pressed or released.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum ElementState {
16    Pressed,
17    Released,
18}
19
20/// Active modifier keys at the time of an event.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
22pub struct Modifiers {
23    pub shift: bool,
24    pub ctrl: bool,
25    pub alt: bool,
26    pub meta: bool,
27}
28
29/// Keyboard key codes relevant for widget interaction.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub enum KeyCode {
32    Tab,
33    Enter,
34    Escape,
35    Space,
36    Backspace,
37    Delete,
38    ArrowLeft,
39    ArrowRight,
40    ArrowUp,
41    ArrowDown,
42    Home,
43    End,
44    PageUp,
45    PageDown,
46    KeyA,
47    KeyC,
48    KeyV,
49    KeyX,
50    KeyZ,
51    /// Any key not explicitly listed above.
52    Other(u32),
53}
54
55/// Result of an event handler invocation.
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum EventResult {
58    /// The event was consumed by the handler.
59    Handled,
60    /// The event was not consumed and may propagate further.
61    Ignored,
62}
63
64/// A mouse click (press or release) at a position in logical coordinates.
65#[derive(Debug, Clone)]
66pub struct MouseClickEvent {
67    pub button: MouseButton,
68    pub state: ElementState,
69    pub x: f32,
70    pub y: f32,
71    pub click_count: u32,
72}
73
74/// A keyboard key event with optional text input.
75#[derive(Debug, Clone)]
76pub struct KeyboardEvent {
77    pub key: KeyCode,
78    pub state: ElementState,
79    pub modifiers: Modifiers,
80    pub text: Option<String>,
81}
82
83/// A mouse-move event at a position in logical coordinates.
84#[derive(Debug, Clone)]
85pub struct MouseMoveEvent {
86    pub x: f32,
87    pub y: f32,
88}
89
90/// A scroll event with position and delta.
91#[derive(Debug, Clone)]
92pub struct ScrollEvent {
93    pub x: f32,
94    pub y: f32,
95    pub delta_x: f32,
96    pub delta_y: f32,
97}
98
99/// Trait for elements that can receive and handle input events.
100pub trait EventHandler {
101    /// Handle a mouse click event.  Returns `Handled` if the event was consumed.
102    fn handle_mouse_click(&mut self, event: &MouseClickEvent) -> EventResult {
103        let _ = event;
104        EventResult::Ignored
105    }
106
107    /// Handle a keyboard event.  Returns `Handled` if the event was consumed.
108    fn handle_keyboard(&mut self, event: &KeyboardEvent) -> EventResult {
109        let _ = event;
110        EventResult::Ignored
111    }
112
113    /// Handle a mouse-move event.
114    fn handle_mouse_move(&mut self, event: &MouseMoveEvent) -> EventResult {
115        let _ = event;
116        EventResult::Ignored
117    }
118
119    /// Handle a scroll event.
120    fn handle_scroll(&mut self, event: &ScrollEvent) -> EventResult {
121        let _ = event;
122        EventResult::Ignored
123    }
124
125    /// Whether this element currently has focus.
126    fn is_focused(&self) -> bool;
127
128    /// Set focus state for this element.
129    fn set_focused(&mut self, focused: bool);
130
131    /// Hit-test: does this element contain the given logical point?
132    fn contains_point(&self, x: f32, y: f32) -> bool;
133}
134
135// ---------------------------------------------------------------------------
136// Tests
137// ---------------------------------------------------------------------------
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn mouse_click_event_fields() {
145        let evt = MouseClickEvent {
146            button: MouseButton::Left,
147            state: ElementState::Pressed,
148            x: 10.0,
149            y: 20.0,
150            click_count: 2,
151        };
152        assert_eq!(evt.button, MouseButton::Left);
153        assert_eq!(evt.state, ElementState::Pressed);
154        assert!((evt.x - 10.0).abs() < f32::EPSILON);
155        assert!((evt.y - 20.0).abs() < f32::EPSILON);
156        assert_eq!(evt.click_count, 2);
157    }
158
159    #[test]
160    fn event_result_matching() {
161        let handled = EventResult::Handled;
162        let ignored = EventResult::Ignored;
163        assert_eq!(handled, EventResult::Handled);
164        assert_eq!(ignored, EventResult::Ignored);
165        assert_ne!(handled, ignored);
166    }
167
168    #[test]
169    fn modifiers_default_all_false() {
170        let m = Modifiers::default();
171        assert!(!m.shift);
172        assert!(!m.ctrl);
173        assert!(!m.alt);
174        assert!(!m.meta);
175    }
176
177    #[test]
178    fn key_code_variants() {
179        // Ensure each named variant is distinct.
180        let keys = [
181            KeyCode::Tab,
182            KeyCode::Enter,
183            KeyCode::Escape,
184            KeyCode::Space,
185            KeyCode::Backspace,
186            KeyCode::Delete,
187            KeyCode::ArrowLeft,
188            KeyCode::ArrowRight,
189            KeyCode::ArrowUp,
190            KeyCode::ArrowDown,
191            KeyCode::Home,
192            KeyCode::End,
193            KeyCode::PageUp,
194            KeyCode::PageDown,
195            KeyCode::KeyA,
196            KeyCode::KeyC,
197            KeyCode::KeyV,
198            KeyCode::KeyX,
199            KeyCode::KeyZ,
200            KeyCode::Other(0),
201        ];
202        // All pairs should be distinct.
203        for (i, a) in keys.iter().enumerate() {
204            for (j, b) in keys.iter().enumerate() {
205                if i != j {
206                    assert_ne!(a, b, "KeyCode variants at {i} and {j} should differ");
207                }
208            }
209        }
210    }
211
212    #[test]
213    fn keyboard_event_with_text() {
214        let evt = KeyboardEvent {
215            key: KeyCode::KeyA,
216            state: ElementState::Pressed,
217            modifiers: Modifiers {
218                shift: true,
219                ..Default::default()
220            },
221            text: Some("A".to_string()),
222        };
223        assert_eq!(evt.key, KeyCode::KeyA);
224        assert_eq!(evt.text.as_deref(), Some("A"));
225        assert!(evt.modifiers.shift);
226    }
227
228    #[test]
229    fn scroll_event_fields() {
230        let evt = ScrollEvent {
231            x: 5.0,
232            y: 6.0,
233            delta_x: -1.0,
234            delta_y: 3.5,
235        };
236        assert!((evt.delta_x - (-1.0)).abs() < f32::EPSILON);
237        assert!((evt.delta_y - 3.5).abs() < f32::EPSILON);
238    }
239}