windjammer_ui/
events.rs

1//! Cross-platform event system
2
3pub mod dispatcher;
4
5use std::fmt;
6use std::sync::{Arc, Mutex};
7
8pub use dispatcher::ComponentEventDispatcher;
9
10/// Cross-platform event types
11#[derive(Debug, Clone, PartialEq)]
12pub enum Event {
13    /// Mouse click
14    Click { x: f64, y: f64, button: MouseButton },
15    /// Mouse move
16    MouseMove { x: f64, y: f64 },
17    /// Mouse down
18    MouseDown { x: f64, y: f64, button: MouseButton },
19    /// Mouse up
20    MouseUp { x: f64, y: f64, button: MouseButton },
21    /// Mouse enter
22    MouseEnter,
23    /// Mouse leave
24    MouseLeave,
25    /// Key press
26    KeyPress { key: String, modifiers: Modifiers },
27    /// Key down
28    KeyDown { key: String, modifiers: Modifiers },
29    /// Key up
30    KeyUp { key: String, modifiers: Modifiers },
31    /// Input change (for text inputs)
32    Input { value: String },
33    /// Change event
34    Change { value: String },
35    /// Focus
36    Focus,
37    /// Blur
38    Blur,
39    /// Form submit
40    Submit,
41    /// Touch event (mobile)
42    Touch {
43        x: f64,
44        y: f64,
45        touch_type: TouchType,
46    },
47    /// Scroll
48    Scroll { x: f64, y: f64 },
49    /// Resize
50    Resize { width: u32, height: u32 },
51    /// Custom event
52    Custom { name: String, data: String },
53}
54
55/// Mouse button
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum MouseButton {
58    Left,
59    Right,
60    Middle,
61}
62
63/// Touch event type
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum TouchType {
66    Start,
67    Move,
68    End,
69    Cancel,
70}
71
72/// Keyboard modifiers
73#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
74pub struct Modifiers {
75    pub ctrl: bool,
76    pub alt: bool,
77    pub shift: bool,
78    pub meta: bool,
79}
80
81/// Event handler trait
82pub trait EventHandler: Send + Sync {
83    /// Handle an event
84    fn handle(&self, event: Event);
85}
86
87/// Function-based event handler
88pub struct FnHandler<F>
89where
90    F: Fn(Event) + Send + Sync,
91{
92    handler: F,
93}
94
95impl<F> FnHandler<F>
96where
97    F: Fn(Event) + Send + Sync,
98{
99    pub fn new(handler: F) -> Self {
100        Self { handler }
101    }
102}
103
104impl<F> EventHandler for FnHandler<F>
105where
106    F: Fn(Event) + Send + Sync,
107{
108    fn handle(&self, event: Event) {
109        (self.handler)(event);
110    }
111}
112
113impl fmt::Display for Event {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        match self {
116            Event::Click { x, y, button } => write!(f, "Click({}, {}, {:?})", x, y, button),
117            Event::MouseMove { x, y } => write!(f, "MouseMove({}, {})", x, y),
118            Event::MouseDown { x, y, button } => write!(f, "MouseDown({}, {}, {:?})", x, y, button),
119            Event::MouseUp { x, y, button } => write!(f, "MouseUp({}, {}, {:?})", x, y, button),
120            Event::MouseEnter => write!(f, "MouseEnter"),
121            Event::MouseLeave => write!(f, "MouseLeave"),
122            Event::KeyPress { key, .. } => write!(f, "KeyPress({})", key),
123            Event::KeyDown { key, .. } => write!(f, "KeyDown({})", key),
124            Event::KeyUp { key, .. } => write!(f, "KeyUp({})", key),
125            Event::Input { value } => write!(f, "Input({})", value),
126            Event::Change { value } => write!(f, "Change({})", value),
127            Event::Focus => write!(f, "Focus"),
128            Event::Blur => write!(f, "Blur"),
129            Event::Submit => write!(f, "Submit"),
130            Event::Touch { x, y, touch_type } => write!(f, "Touch({}, {}, {:?})", x, y, touch_type),
131            Event::Scroll { x, y } => write!(f, "Scroll({}, {})", x, y),
132            Event::Resize { width, height } => write!(f, "Resize({}, {})", width, height),
133            Event::Custom { name, .. } => write!(f, "Custom({})", name),
134        }
135    }
136}
137
138/// Event context with propagation control
139#[derive(Debug, Clone)]
140pub struct EventContext {
141    /// Target element ID
142    pub target: String,
143    /// Current element ID (for propagation)
144    pub current_target: String,
145    /// Event phase
146    pub phase: EventPhase,
147    /// Whether propagation is stopped
148    stopped: Arc<Mutex<bool>>,
149    /// Whether immediate propagation is stopped
150    immediate_stopped: Arc<Mutex<bool>>,
151    /// Whether default action is prevented
152    default_prevented: Arc<Mutex<bool>>,
153}
154
155/// Event phase (capturing vs bubbling)
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum EventPhase {
158    /// Capturing phase (from root to target)
159    Capturing,
160    /// At target
161    AtTarget,
162    /// Bubbling phase (from target to root)
163    Bubbling,
164}
165
166impl EventContext {
167    /// Create a new event context
168    pub fn new(target: String) -> Self {
169        Self {
170            target: target.clone(),
171            current_target: target,
172            phase: EventPhase::AtTarget,
173            stopped: Arc::new(Mutex::new(false)),
174            immediate_stopped: Arc::new(Mutex::new(false)),
175            default_prevented: Arc::new(Mutex::new(false)),
176        }
177    }
178
179    /// Stop event propagation
180    pub fn stop_propagation(&self) {
181        *self.stopped.lock().unwrap() = true;
182    }
183
184    /// Stop immediate event propagation (prevents other listeners on same element)
185    pub fn stop_immediate_propagation(&self) {
186        *self.immediate_stopped.lock().unwrap() = true;
187    }
188
189    /// Prevent default action
190    pub fn prevent_default(&self) {
191        *self.default_prevented.lock().unwrap() = true;
192    }
193
194    /// Check if propagation is stopped
195    pub fn is_propagation_stopped(&self) -> bool {
196        *self.stopped.lock().unwrap()
197    }
198
199    /// Check if immediate propagation is stopped
200    pub fn is_immediate_propagation_stopped(&self) -> bool {
201        *self.immediate_stopped.lock().unwrap()
202    }
203
204    /// Check if default is prevented
205    pub fn is_default_prevented(&self) -> bool {
206        *self.default_prevented.lock().unwrap()
207    }
208}
209
210/// Event listener with phase
211pub struct EventListener {
212    /// Event type (e.g., "click", "keydown")
213    pub event_type: String,
214    /// Handler function
215    #[allow(clippy::type_complexity)]
216    pub handler: Arc<dyn Fn(&Event, &EventContext) + Send + Sync>,
217    /// Whether to capture
218    pub capture: bool,
219}
220
221impl EventListener {
222    /// Create a new event listener
223    pub fn new<F>(event_type: String, handler: F, capture: bool) -> Self
224    where
225        F: Fn(&Event, &EventContext) + Send + Sync + 'static,
226    {
227        Self {
228            event_type,
229            handler: Arc::new(handler),
230            capture,
231        }
232    }
233
234    /// Invoke the handler
235    pub fn invoke(&self, event: &Event, context: &EventContext) {
236        (self.handler)(event, context);
237    }
238}
239
240/// Event dispatcher for managing event listeners
241pub struct EventDispatcher {
242    /// Listeners by element ID
243    listeners: Arc<Mutex<std::collections::HashMap<String, Vec<EventListener>>>>,
244}
245
246impl EventDispatcher {
247    /// Create a new event dispatcher
248    pub fn new() -> Self {
249        Self {
250            listeners: Arc::new(Mutex::new(std::collections::HashMap::new())),
251        }
252    }
253
254    /// Add an event listener
255    pub fn add_listener(&self, element_id: String, listener: EventListener) {
256        let mut listeners = self.listeners.lock().unwrap();
257        listeners.entry(element_id).or_default().push(listener);
258    }
259
260    /// Remove all listeners for an element
261    pub fn remove_listeners(&self, element_id: &str) {
262        let mut listeners = self.listeners.lock().unwrap();
263        listeners.remove(element_id);
264    }
265
266    /// Dispatch an event with propagation
267    pub fn dispatch(&self, event: &Event, target_id: String, path: Vec<String>) {
268        let context = EventContext::new(target_id.clone());
269
270        // Capturing phase (from root to target)
271        let mut ctx = context.clone();
272        ctx.phase = EventPhase::Capturing;
273        for element_id in path.iter().rev() {
274            if ctx.is_propagation_stopped() {
275                break;
276            }
277            ctx.current_target = element_id.clone();
278            self.invoke_listeners(element_id, event, &ctx, true);
279        }
280
281        // At target
282        if !context.is_propagation_stopped() {
283            let mut ctx = context.clone();
284            ctx.phase = EventPhase::AtTarget;
285            ctx.current_target = target_id.clone();
286            self.invoke_listeners(&target_id, event, &ctx, false);
287        }
288
289        // Bubbling phase (from target to root)
290        if !context.is_propagation_stopped() {
291            let mut ctx = context.clone();
292            ctx.phase = EventPhase::Bubbling;
293            for element_id in path.iter() {
294                if ctx.is_propagation_stopped() {
295                    break;
296                }
297                ctx.current_target = element_id.clone();
298                self.invoke_listeners(element_id, event, &ctx, false);
299            }
300        }
301    }
302
303    fn invoke_listeners(
304        &self,
305        element_id: &str,
306        event: &Event,
307        context: &EventContext,
308        capture_phase: bool,
309    ) {
310        let listeners = self.listeners.lock().unwrap();
311        if let Some(element_listeners) = listeners.get(element_id) {
312            for listener in element_listeners {
313                if context.is_immediate_propagation_stopped() {
314                    break;
315                }
316                if listener.capture == capture_phase {
317                    listener.invoke(event, context);
318                }
319            }
320        }
321    }
322}
323
324impl Default for EventDispatcher {
325    fn default() -> Self {
326        Self::new()
327    }
328}
329
330#[cfg(test)]
331mod tests {
332    use super::*;
333
334    #[test]
335    fn test_event_display() {
336        let event = Event::Click {
337            x: 10.0,
338            y: 20.0,
339            button: MouseButton::Left,
340        };
341        assert!(event.to_string().contains("Click"));
342    }
343
344    #[test]
345    fn test_modifiers_default() {
346        let mods = Modifiers::default();
347        assert!(!mods.ctrl);
348        assert!(!mods.alt);
349        assert!(!mods.shift);
350        assert!(!mods.meta);
351    }
352
353    #[test]
354    fn test_fn_handler() {
355        let called = std::sync::Arc::new(std::sync::Mutex::new(false));
356        let called_clone = called.clone();
357
358        let handler = FnHandler::new(move |_event| {
359            *called_clone.lock().unwrap() = true;
360        });
361
362        handler.handle(Event::Submit);
363        assert!(*called.lock().unwrap());
364    }
365
366    #[test]
367    fn test_event_context_stop_propagation() {
368        let ctx = EventContext::new("button".to_string());
369        assert!(!ctx.is_propagation_stopped());
370
371        ctx.stop_propagation();
372        assert!(ctx.is_propagation_stopped());
373    }
374
375    #[test]
376    fn test_event_context_prevent_default() {
377        let ctx = EventContext::new("link".to_string());
378        assert!(!ctx.is_default_prevented());
379
380        ctx.prevent_default();
381        assert!(ctx.is_default_prevented());
382    }
383
384    #[test]
385    fn test_event_dispatcher_add_listener() {
386        let dispatcher = EventDispatcher::new();
387        let called = std::sync::Arc::new(std::sync::Mutex::new(0));
388        let called_clone = called.clone();
389
390        let listener = EventListener::new(
391            "click".to_string(),
392            move |_event, _ctx| {
393                *called_clone.lock().unwrap() += 1;
394            },
395            false,
396        );
397
398        dispatcher.add_listener("button".to_string(), listener);
399
400        let event = Event::Click {
401            x: 10.0,
402            y: 20.0,
403            button: MouseButton::Left,
404        };
405        dispatcher.dispatch(&event, "button".to_string(), vec!["root".to_string()]);
406
407        assert_eq!(*called.lock().unwrap(), 1);
408    }
409
410    #[test]
411    fn test_event_propagation_bubbling() {
412        let dispatcher = EventDispatcher::new();
413        let events = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
414
415        // Add listener to parent
416        let events_clone = events.clone();
417        let parent_listener = EventListener::new(
418            "click".to_string(),
419            move |_event, ctx| {
420                events_clone
421                    .lock()
422                    .unwrap()
423                    .push(ctx.current_target.clone());
424            },
425            false,
426        );
427        dispatcher.add_listener("parent".to_string(), parent_listener);
428
429        // Add listener to child
430        let events_clone = events.clone();
431        let child_listener = EventListener::new(
432            "click".to_string(),
433            move |_event, ctx| {
434                events_clone
435                    .lock()
436                    .unwrap()
437                    .push(ctx.current_target.clone());
438            },
439            false,
440        );
441        dispatcher.add_listener("child".to_string(), child_listener);
442
443        let event = Event::Click {
444            x: 10.0,
445            y: 20.0,
446            button: MouseButton::Left,
447        };
448
449        // Dispatch from child to parent
450        dispatcher.dispatch(&event, "child".to_string(), vec!["parent".to_string()]);
451
452        let recorded_events = events.lock().unwrap();
453        assert_eq!(recorded_events.len(), 2);
454        assert_eq!(recorded_events[0], "child"); // Target first
455        assert_eq!(recorded_events[1], "parent"); // Then bubbles to parent
456    }
457
458    #[test]
459    fn test_event_stop_propagation() {
460        let dispatcher = EventDispatcher::new();
461        let events = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
462
463        // Add listener to parent
464        let events_clone = events.clone();
465        let parent_listener = EventListener::new(
466            "click".to_string(),
467            move |_event, ctx| {
468                events_clone
469                    .lock()
470                    .unwrap()
471                    .push(ctx.current_target.clone());
472            },
473            false,
474        );
475        dispatcher.add_listener("parent".to_string(), parent_listener);
476
477        // Add listener to child that stops propagation
478        let events_clone = events.clone();
479        let child_listener = EventListener::new(
480            "click".to_string(),
481            move |_event, ctx| {
482                events_clone
483                    .lock()
484                    .unwrap()
485                    .push(ctx.current_target.clone());
486                ctx.stop_propagation();
487            },
488            false,
489        );
490        dispatcher.add_listener("child".to_string(), child_listener);
491
492        let event = Event::Click {
493            x: 10.0,
494            y: 20.0,
495            button: MouseButton::Left,
496        };
497
498        dispatcher.dispatch(&event, "child".to_string(), vec!["parent".to_string()]);
499
500        let recorded_events = events.lock().unwrap();
501        assert_eq!(recorded_events.len(), 1); // Only child, propagation stopped
502        assert_eq!(recorded_events[0], "child");
503    }
504}