Skip to main content

blinc_core/
events.rs

1//! Event dispatch system
2//!
3//! Unified event handling across all platforms.
4
5use rustc_hash::FxHashMap;
6
7/// Event type identifier
8pub type EventType = u32;
9
10/// Common event types
11pub mod event_types {
12    use super::EventType;
13
14    pub const POINTER_DOWN: EventType = 1;
15    pub const POINTER_UP: EventType = 2;
16    pub const POINTER_MOVE: EventType = 3;
17    pub const POINTER_ENTER: EventType = 4;
18    pub const POINTER_LEAVE: EventType = 5;
19    /// Drag event (mouse down + move)
20    pub const DRAG: EventType = 6;
21    /// Drag ended (mouse up after drag)
22    pub const DRAG_END: EventType = 7;
23    pub const FOCUS: EventType = 10;
24    pub const BLUR: EventType = 11;
25    pub const KEY_DOWN: EventType = 20;
26    pub const KEY_UP: EventType = 21;
27    /// Text input event (for character input, IME composition)
28    pub const TEXT_INPUT: EventType = 22;
29    pub const SCROLL: EventType = 30;
30    /// Scroll gesture ended (for deceleration/momentum)
31    pub const SCROLL_END: EventType = 31;
32    /// Pinch zoom gesture update
33    pub const PINCH: EventType = 32;
34    pub const RESIZE: EventType = 40;
35
36    // Window lifecycle events
37    pub const WINDOW_FOCUS: EventType = 50;
38    pub const WINDOW_BLUR: EventType = 51;
39
40    // Element lifecycle events
41    pub const MOUNT: EventType = 60;
42    pub const UNMOUNT: EventType = 61;
43
44    // Clipboard events
45    pub const CUT: EventType = 70;
46    pub const COPY: EventType = 71;
47    pub const PASTE: EventType = 72;
48
49    // Selection events
50    pub const SELECT_ALL: EventType = 80;
51}
52
53/// A UI event with associated data
54#[derive(Clone, Debug)]
55pub struct Event {
56    pub event_type: EventType,
57    pub target: u64, // Widget ID
58    pub data: EventData,
59    pub timestamp: u64,
60    pub propagation_stopped: bool,
61}
62
63/// Event-specific data
64#[derive(Clone, Debug)]
65pub enum EventData {
66    Pointer {
67        x: f32,
68        y: f32,
69        button: u8,
70        pressure: f32,
71    },
72    Key {
73        /// Virtual key code (platform-specific, use KeyCode constants)
74        key: KeyCode,
75        /// Keyboard modifier flags
76        modifiers: Modifiers,
77        /// Whether this is a repeat event
78        repeat: bool,
79    },
80    /// Text input from keyboard or IME
81    TextInput {
82        /// The input text (may be multiple characters for IME)
83        text: String,
84    },
85    /// Clipboard paste data
86    Clipboard {
87        /// The pasted text content
88        text: String,
89    },
90    Scroll {
91        delta_x: f32,
92        delta_y: f32,
93    },
94    Resize {
95        width: u32,
96        height: u32,
97    },
98    None,
99}
100
101/// Virtual key codes (platform-agnostic)
102#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
103pub struct KeyCode(pub u32);
104
105impl KeyCode {
106    // Alphanumeric keys
107    pub const A: KeyCode = KeyCode(0x41);
108    pub const B: KeyCode = KeyCode(0x42);
109    pub const C: KeyCode = KeyCode(0x43);
110    pub const D: KeyCode = KeyCode(0x44);
111    pub const E: KeyCode = KeyCode(0x45);
112    pub const F: KeyCode = KeyCode(0x46);
113    pub const G: KeyCode = KeyCode(0x47);
114    pub const H: KeyCode = KeyCode(0x48);
115    pub const I: KeyCode = KeyCode(0x49);
116    pub const J: KeyCode = KeyCode(0x4A);
117    pub const K: KeyCode = KeyCode(0x4B);
118    pub const L: KeyCode = KeyCode(0x4C);
119    pub const M: KeyCode = KeyCode(0x4D);
120    pub const N: KeyCode = KeyCode(0x4E);
121    pub const O: KeyCode = KeyCode(0x4F);
122    pub const P: KeyCode = KeyCode(0x50);
123    pub const Q: KeyCode = KeyCode(0x51);
124    pub const R: KeyCode = KeyCode(0x52);
125    pub const S: KeyCode = KeyCode(0x53);
126    pub const T: KeyCode = KeyCode(0x54);
127    pub const U: KeyCode = KeyCode(0x55);
128    pub const V: KeyCode = KeyCode(0x56);
129    pub const W: KeyCode = KeyCode(0x57);
130    pub const X: KeyCode = KeyCode(0x58);
131    pub const Y: KeyCode = KeyCode(0x59);
132    pub const Z: KeyCode = KeyCode(0x5A);
133
134    // Number keys
135    pub const KEY_0: KeyCode = KeyCode(0x30);
136    pub const KEY_1: KeyCode = KeyCode(0x31);
137    pub const KEY_2: KeyCode = KeyCode(0x32);
138    pub const KEY_3: KeyCode = KeyCode(0x33);
139    pub const KEY_4: KeyCode = KeyCode(0x34);
140    pub const KEY_5: KeyCode = KeyCode(0x35);
141    pub const KEY_6: KeyCode = KeyCode(0x36);
142    pub const KEY_7: KeyCode = KeyCode(0x37);
143    pub const KEY_8: KeyCode = KeyCode(0x38);
144    pub const KEY_9: KeyCode = KeyCode(0x39);
145
146    // Special keys
147    pub const BACKSPACE: KeyCode = KeyCode(0x08);
148    pub const TAB: KeyCode = KeyCode(0x09);
149    pub const ENTER: KeyCode = KeyCode(0x0D);
150    pub const ESCAPE: KeyCode = KeyCode(0x1B);
151    pub const SPACE: KeyCode = KeyCode(0x20);
152    pub const DELETE: KeyCode = KeyCode(0x7F);
153
154    // Arrow keys
155    pub const LEFT: KeyCode = KeyCode(0x25);
156    pub const UP: KeyCode = KeyCode(0x26);
157    pub const RIGHT: KeyCode = KeyCode(0x27);
158    pub const DOWN: KeyCode = KeyCode(0x28);
159
160    // Navigation keys
161    pub const HOME: KeyCode = KeyCode(0x24);
162    pub const END: KeyCode = KeyCode(0x23);
163    pub const PAGE_UP: KeyCode = KeyCode(0x21);
164    pub const PAGE_DOWN: KeyCode = KeyCode(0x22);
165
166    // Unknown/unmapped key
167    pub const UNKNOWN: KeyCode = KeyCode(0);
168}
169
170/// Keyboard modifier flags
171#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
172pub struct Modifiers {
173    bits: u8,
174}
175
176impl Modifiers {
177    pub const NONE: Modifiers = Modifiers { bits: 0 };
178    pub const SHIFT: u8 = 0b0001;
179    pub const CTRL: u8 = 0b0010;
180    pub const ALT: u8 = 0b0100;
181    pub const META: u8 = 0b1000; // Cmd on macOS, Win on Windows
182
183    /// Create new modifiers from flags
184    pub const fn new(shift: bool, ctrl: bool, alt: bool, meta: bool) -> Self {
185        let mut bits = 0;
186        if shift {
187            bits |= Self::SHIFT;
188        }
189        if ctrl {
190            bits |= Self::CTRL;
191        }
192        if alt {
193            bits |= Self::ALT;
194        }
195        if meta {
196            bits |= Self::META;
197        }
198        Self { bits }
199    }
200
201    /// Create from raw bits
202    pub const fn from_bits(bits: u8) -> Self {
203        Self { bits }
204    }
205
206    /// Check if shift is pressed
207    pub const fn shift(&self) -> bool {
208        self.bits & Self::SHIFT != 0
209    }
210
211    /// Check if ctrl is pressed
212    pub const fn ctrl(&self) -> bool {
213        self.bits & Self::CTRL != 0
214    }
215
216    /// Check if alt is pressed
217    pub const fn alt(&self) -> bool {
218        self.bits & Self::ALT != 0
219    }
220
221    /// Check if meta (Cmd/Win) is pressed
222    pub const fn meta(&self) -> bool {
223        self.bits & Self::META != 0
224    }
225
226    /// Check if any modifier is pressed
227    pub const fn any(&self) -> bool {
228        self.bits != 0
229    }
230
231    /// Check if command key is pressed (Ctrl on non-macOS, Meta on macOS)
232    #[cfg(target_os = "macos")]
233    pub const fn command(&self) -> bool {
234        self.meta()
235    }
236
237    /// Check if command key is pressed (Ctrl on non-macOS, Meta on macOS)
238    #[cfg(not(target_os = "macos"))]
239    pub const fn command(&self) -> bool {
240        self.ctrl()
241    }
242}
243
244impl Event {
245    pub fn stop_propagation(&mut self) {
246        self.propagation_stopped = true;
247    }
248}
249
250/// Event handler function type
251pub type EventHandler = Box<dyn Fn(&Event) + Send + Sync>;
252
253/// Dispatches events to registered handlers
254pub struct EventDispatcher {
255    handlers: FxHashMap<(u64, EventType), Vec<EventHandler>>,
256}
257
258impl EventDispatcher {
259    pub fn new() -> Self {
260        Self {
261            handlers: FxHashMap::default(),
262        }
263    }
264
265    /// Register an event handler for a widget and event type
266    pub fn register<F>(&mut self, widget_id: u64, event_type: EventType, handler: F)
267    where
268        F: Fn(&Event) + Send + Sync + 'static,
269    {
270        self.handlers
271            .entry((widget_id, event_type))
272            .or_default()
273            .push(Box::new(handler));
274    }
275
276    /// Dispatch an event to all registered handlers
277    pub fn dispatch(&self, event: &mut Event) {
278        if let Some(handlers) = self.handlers.get(&(event.target, event.event_type)) {
279            for handler in handlers {
280                if event.propagation_stopped {
281                    break;
282                }
283                handler(event);
284            }
285        }
286    }
287}
288
289impl Default for EventDispatcher {
290    fn default() -> Self {
291        Self::new()
292    }
293}