1use crate::tree::{NodeId, ShadowTree};
10use crate::layout::LayoutEngine;
11use rustc_hash::FxHashMap;
12use std::time::{Duration, Instant};
13
14#[derive(Debug, Clone)]
20pub enum InputEvent {
21 PointerDown(PointerEvent),
22 PointerMove(PointerEvent),
23 PointerUp(PointerEvent),
24 PointerCancel(PointerEvent),
25 Scroll(ScrollEvent),
26 KeyDown(KeyboardEvent),
27 KeyUp(KeyboardEvent),
28 Tap { x: f32, y: f32, target: Option<NodeId> },
30 DoubleTap { x: f32, y: f32, target: Option<NodeId> },
31 LongPress { x: f32, y: f32, target: Option<NodeId> },
32 Pan { dx: f32, dy: f32, vx: f32, vy: f32, target: Option<NodeId>, ended: bool },
33 Swipe { direction: SwipeDirection, velocity: f32, target: Option<NodeId> },
34}
35
36#[derive(Debug, Clone)]
37pub struct PointerEvent {
38 pub pointer_id: u32,
39 pub pointer_type: PointerType,
40 pub screen_x: f32,
41 pub screen_y: f32,
42 pub pressure: f32,
43 pub buttons: u32,
44 pub modifiers: Modifiers,
45 pub timestamp: Instant,
46 pub target: Option<NodeId>,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum PointerType { Mouse, Touch, Pen }
51
52#[derive(Debug, Clone)]
53pub struct ScrollEvent {
54 pub delta_x: f32,
55 pub delta_y: f32,
56 pub modifiers: Modifiers,
57 pub target: Option<NodeId>,
58}
59
60#[derive(Debug, Clone)]
61pub struct KeyboardEvent {
62 pub code: String,
63 pub key: String,
64 pub modifiers: Modifiers,
65 pub is_repeat: bool,
66 pub target: Option<NodeId>,
67}
68
69#[derive(Debug, Clone, Copy, Default)]
70pub struct Modifiers {
71 pub shift: bool,
72 pub ctrl: bool,
73 pub alt: bool,
74 pub meta: bool,
75}
76
77#[derive(Debug, Clone, Copy)]
78pub enum SwipeDirection { Up, Down, Left, Right }
79
80pub type HandlerFn = Box<dyn Fn(&InputEvent) -> HandlerResponse + Send + Sync>;
85
86#[derive(Debug, Clone, Copy, PartialEq)]
87pub enum HandlerResponse {
88 Continue,
89 StopPropagation,
90 PreventDefault,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
94pub enum EventPhase { Capture, Bubble }
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97struct HandlerKey {
98 node_id: NodeId,
99 event_kind: EventKind,
100 phase: EventPhase,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
104pub enum EventKind {
105 PointerDown, PointerMove, PointerUp, PointerCancel,
106 Scroll, KeyDown, KeyUp,
107 Tap, DoubleTap, LongPress, Pan, Swipe,
108}
109
110impl InputEvent {
111 pub fn kind(&self) -> EventKind {
112 match self {
113 InputEvent::PointerDown(_) => EventKind::PointerDown,
114 InputEvent::PointerMove(_) => EventKind::PointerMove,
115 InputEvent::PointerUp(_) => EventKind::PointerUp,
116 InputEvent::PointerCancel(_) => EventKind::PointerCancel,
117 InputEvent::Scroll(_) => EventKind::Scroll,
118 InputEvent::KeyDown(_) => EventKind::KeyDown,
119 InputEvent::KeyUp(_) => EventKind::KeyUp,
120 InputEvent::Tap { .. } => EventKind::Tap,
121 InputEvent::DoubleTap { .. } => EventKind::DoubleTap,
122 InputEvent::LongPress { .. } => EventKind::LongPress,
123 InputEvent::Pan { .. } => EventKind::Pan,
124 InputEvent::Swipe { .. } => EventKind::Swipe,
125 }
126 }
127
128 pub fn screen_position(&self) -> Option<(f32, f32)> {
130 match self {
131 InputEvent::PointerDown(e) | InputEvent::PointerMove(e) |
132 InputEvent::PointerUp(e) | InputEvent::PointerCancel(e) => {
133 Some((e.screen_x, e.screen_y))
134 },
135 InputEvent::Tap { x, y, .. } | InputEvent::DoubleTap { x, y, .. } |
136 InputEvent::LongPress { x, y, .. } => Some((*x, *y)),
137 _ => None,
138 }
139 }
140}
141
142pub struct EventResult {
147 pub propagation_stopped: bool,
148 pub default_prevented: bool,
149}
150
151pub struct EventDispatcher {
152 handlers: FxHashMap<HandlerKey, Vec<HandlerFn>>,
153 gesture: GestureRecognizer,
154}
155
156impl EventDispatcher {
157 pub fn new() -> Self {
158 Self {
159 handlers: FxHashMap::default(),
160 gesture: GestureRecognizer::new(),
161 }
162 }
163
164 pub fn add_handler(
166 &mut self,
167 node_id: NodeId,
168 kind: EventKind,
169 phase: EventPhase,
170 handler: HandlerFn,
171 ) {
172 let key = HandlerKey { node_id, event_kind: kind, phase };
173 self.handlers.entry(key).or_default().push(handler);
174 }
175
176 pub fn remove_handlers_for(&mut self, node_id: NodeId) {
178 self.handlers.retain(|key, _| key.node_id != node_id);
179 }
180
181 pub fn dispatch(
183 &mut self,
184 mut event: InputEvent,
185 layout: &LayoutEngine,
186 tree: &ShadowTree,
187 ) -> EventResult {
188 let mut result = EventResult {
189 propagation_stopped: false,
190 default_prevented: false,
191 };
192
193 let target = if let Some((x, y)) = event.screen_position() {
195 layout.hit_test(x, y).first().copied()
196 } else {
197 None
198 };
199
200 self.set_target(&mut event, target);
202
203 let target = match target {
204 Some(t) => t,
205 None => return result,
206 };
207
208 let path = tree.ancestors(target);
210 let kind = event.kind();
211
212 for &node_id in &path {
214 if result.propagation_stopped { break; }
215 let key = HandlerKey { node_id, event_kind: kind, phase: EventPhase::Capture };
216 if let Some(handlers) = self.handlers.get(&key) {
217 for handler in handlers {
218 match handler(&event) {
219 HandlerResponse::StopPropagation => result.propagation_stopped = true,
220 HandlerResponse::PreventDefault => result.default_prevented = true,
221 HandlerResponse::Continue => {}
222 }
223 if result.propagation_stopped { break; }
224 }
225 }
226 }
227
228 for &node_id in path.iter().rev() {
230 if result.propagation_stopped { break; }
231 let key = HandlerKey { node_id, event_kind: kind, phase: EventPhase::Bubble };
232 if let Some(handlers) = self.handlers.get(&key) {
233 for handler in handlers {
234 match handler(&event) {
235 HandlerResponse::StopPropagation => result.propagation_stopped = true,
236 HandlerResponse::PreventDefault => result.default_prevented = true,
237 HandlerResponse::Continue => {}
238 }
239 if result.propagation_stopped { break; }
240 }
241 }
242 }
243
244 if let Some(gestures) = self.gesture.process(&event) {
246 for gesture in gestures {
247 self.dispatch(gesture, layout, tree);
248 }
249 }
250
251 result
252 }
253
254 fn set_target(&self, event: &mut InputEvent, target: Option<NodeId>) {
255 match event {
256 InputEvent::PointerDown(e) | InputEvent::PointerMove(e) |
257 InputEvent::PointerUp(e) | InputEvent::PointerCancel(e) => {
258 e.target = target;
259 },
260 InputEvent::Tap { target: t, .. } | InputEvent::DoubleTap { target: t, .. } |
261 InputEvent::LongPress { target: t, .. } | InputEvent::Pan { target: t, .. } |
262 InputEvent::Swipe { target: t, .. } => {
263 *t = target;
264 },
265 _ => {}
266 }
267 }
268}
269
270const TAP_MAX_DURATION: Duration = Duration::from_millis(300);
275const TAP_MAX_DISTANCE: f32 = 10.0;
276const LONG_PRESS_MIN: Duration = Duration::from_millis(500);
277const PAN_THRESHOLD: f32 = 10.0;
278const SWIPE_MIN_VELOCITY: f32 = 300.0;
279
280struct PointerState {
281 start: Instant,
282 start_x: f32,
283 start_y: f32,
284 current_x: f32,
285 current_y: f32,
286 target: Option<NodeId>,
287 panning: bool,
288}
289
290struct GestureRecognizer {
291 pointers: FxHashMap<u32, PointerState>,
292}
293
294impl GestureRecognizer {
295 fn new() -> Self {
296 Self { pointers: FxHashMap::default() }
297 }
298
299 fn process(&mut self, event: &InputEvent) -> Option<Vec<InputEvent>> {
300 match event {
301 InputEvent::PointerDown(e) => {
302 self.pointers.insert(e.pointer_id, PointerState {
303 start: e.timestamp,
304 start_x: e.screen_x,
305 start_y: e.screen_y,
306 current_x: e.screen_x,
307 current_y: e.screen_y,
308 target: e.target,
309 panning: false,
310 });
311 None
312 },
313 InputEvent::PointerMove(e) => {
314 let s = self.pointers.get_mut(&e.pointer_id)?;
315 s.current_x = e.screen_x;
316 s.current_y = e.screen_y;
317
318 let dx = s.current_x - s.start_x;
319 let dy = s.current_y - s.start_y;
320 let dist = (dx * dx + dy * dy).sqrt();
321
322 if !s.panning && dist > PAN_THRESHOLD {
323 s.panning = true;
324 }
325
326 if s.panning {
327 Some(vec![InputEvent::Pan {
328 dx, dy, vx: 0.0, vy: 0.0,
329 target: s.target, ended: false,
330 }])
331 } else {
332 None
333 }
334 },
335 InputEvent::PointerUp(e) => {
336 let s = self.pointers.remove(&e.pointer_id)?;
337 let dur = e.timestamp.duration_since(s.start);
338 let dx = e.screen_x - s.start_x;
339 let dy = e.screen_y - s.start_y;
340 let dist = (dx * dx + dy * dy).sqrt();
341
342 if s.panning {
343 let velocity = dist / dur.as_secs_f32();
344 let mut events = vec![InputEvent::Pan {
345 dx, dy,
346 vx: dx / dur.as_secs_f32(),
347 vy: dy / dur.as_secs_f32(),
348 target: s.target, ended: true,
349 }];
350
351 if velocity > SWIPE_MIN_VELOCITY {
352 let dir = if dx.abs() > dy.abs() {
353 if dx > 0.0 { SwipeDirection::Right } else { SwipeDirection::Left }
354 } else {
355 if dy > 0.0 { SwipeDirection::Down } else { SwipeDirection::Up }
356 };
357 events.push(InputEvent::Swipe {
358 direction: dir, velocity, target: s.target,
359 });
360 }
361 Some(events)
362 } else if dur < TAP_MAX_DURATION && dist < TAP_MAX_DISTANCE {
363 Some(vec![InputEvent::Tap {
364 x: e.screen_x, y: e.screen_y, target: s.target,
365 }])
366 } else if dur >= LONG_PRESS_MIN && dist < TAP_MAX_DISTANCE {
367 Some(vec![InputEvent::LongPress {
368 x: e.screen_x, y: e.screen_y, target: s.target,
369 }])
370 } else {
371 None
372 }
373 },
374 _ => None,
375 }
376 }
377}
378
379#[cfg(test)]
384mod tests {
385 use super::*;
386 use crate::tree::{NodeId, ShadowTree};
387 use crate::layout::{LayoutEngine, LayoutStyle, Dimension};
388 use crate::platform::{ViewType};
389 use crate::platform::mock::MockPlatform;
390 use std::collections::HashMap;
391 use std::sync::{Arc, atomic::{AtomicU32, Ordering}};
392
393 fn setup_simple(platform: &Arc<MockPlatform>) -> (ShadowTree, LayoutEngine, EventDispatcher) {
396 let mut tree = ShadowTree::new();
397 let mut layout = LayoutEngine::new();
398
399 tree.create_node(NodeId(1), ViewType::Container, HashMap::new());
400 layout.create_node(NodeId(1), &LayoutStyle {
401 width: Dimension::Points(400.0),
402 height: Dimension::Points(400.0),
403 ..Default::default()
404 }).unwrap();
405
406 tree.create_node(NodeId(2), ViewType::Container, HashMap::new());
407 layout.create_node(NodeId(2), &LayoutStyle {
408 width: Dimension::Points(200.0),
409 height: Dimension::Points(200.0),
410 ..Default::default()
411 }).unwrap();
412
413 tree.set_root(NodeId(1));
414 layout.set_root(NodeId(1));
415 tree.append_child(NodeId(1), NodeId(2));
416 layout.set_children_from_tree(NodeId(1), &tree).unwrap();
417 layout.compute(&tree, 400.0, 400.0, &**platform).unwrap();
418
419 (tree, layout, EventDispatcher::new())
420 }
421
422 fn make_pointer_down(x: f32, y: f32) -> InputEvent {
423 InputEvent::PointerDown(PointerEvent {
424 pointer_id: 0,
425 pointer_type: PointerType::Mouse,
426 screen_x: x,
427 screen_y: y,
428 pressure: 1.0,
429 buttons: 1,
430 modifiers: Modifiers::default(),
431 timestamp: Instant::now(),
432 target: None,
433 })
434 }
435
436 #[test]
437 fn dispatch_bubble_phase() {
438 let platform = Arc::new(MockPlatform::new());
439 let (tree, layout, mut dispatcher) = setup_simple(&platform);
440
441 let counter = Arc::new(AtomicU32::new(0));
442 let c = counter.clone();
443 dispatcher.add_handler(NodeId(2), EventKind::PointerDown, EventPhase::Bubble,
444 Box::new(move |_| { c.fetch_add(1, Ordering::SeqCst); HandlerResponse::Continue }));
445
446 let event = make_pointer_down(50.0, 50.0);
448 let result = dispatcher.dispatch(event, &layout, &tree);
449
450 assert!(!result.propagation_stopped);
451 assert_eq!(counter.load(Ordering::SeqCst), 1);
452 }
453
454 #[test]
455 fn dispatch_capture_phase() {
456 let platform = Arc::new(MockPlatform::new());
457 let (tree, layout, mut dispatcher) = setup_simple(&platform);
458
459 let order = Arc::new(std::sync::Mutex::new(Vec::new()));
460 let o1 = order.clone();
461 let o2 = order.clone();
462
463 dispatcher.add_handler(NodeId(1), EventKind::PointerDown, EventPhase::Capture,
465 Box::new(move |_| { o1.lock().unwrap().push(1); HandlerResponse::Continue }));
466 dispatcher.add_handler(NodeId(2), EventKind::PointerDown, EventPhase::Capture,
467 Box::new(move |_| { o2.lock().unwrap().push(2); HandlerResponse::Continue }));
468
469 let event = make_pointer_down(50.0, 50.0);
470 dispatcher.dispatch(event, &layout, &tree);
471
472 let fired = order.lock().unwrap().clone();
473 assert_eq!(fired, vec![1, 2], "Capture should fire root first, then target");
474 }
475
476 #[test]
477 fn dispatch_stop_propagation() {
478 let platform = Arc::new(MockPlatform::new());
479 let (tree, layout, mut dispatcher) = setup_simple(&platform);
480
481 let parent_hit = Arc::new(AtomicU32::new(0));
482 let p = parent_hit.clone();
483
484 dispatcher.add_handler(NodeId(2), EventKind::PointerDown, EventPhase::Capture,
486 Box::new(|_| HandlerResponse::StopPropagation));
487
488 dispatcher.add_handler(NodeId(1), EventKind::PointerDown, EventPhase::Bubble,
490 Box::new(move |_| { p.fetch_add(1, Ordering::SeqCst); HandlerResponse::Continue }));
491
492 let event = make_pointer_down(50.0, 50.0);
493 let result = dispatcher.dispatch(event, &layout, &tree);
494
495 assert!(result.propagation_stopped);
496 assert_eq!(parent_hit.load(Ordering::SeqCst), 0, "Bubble should not fire after stop");
497 }
498
499 #[test]
500 fn dispatch_prevent_default() {
501 let platform = Arc::new(MockPlatform::new());
502 let (tree, layout, mut dispatcher) = setup_simple(&platform);
503
504 dispatcher.add_handler(NodeId(2), EventKind::PointerDown, EventPhase::Bubble,
505 Box::new(|_| HandlerResponse::PreventDefault));
506
507 let event = make_pointer_down(50.0, 50.0);
508 let result = dispatcher.dispatch(event, &layout, &tree);
509
510 assert!(result.default_prevented);
511 assert!(!result.propagation_stopped);
512 }
513
514 #[test]
515 fn dispatch_misses_when_outside_bounds() {
516 let platform = Arc::new(MockPlatform::new());
517 let (tree, layout, mut dispatcher) = setup_simple(&platform);
518
519 let counter = Arc::new(AtomicU32::new(0));
520 let c = counter.clone();
521 dispatcher.add_handler(NodeId(2), EventKind::PointerDown, EventPhase::Bubble,
522 Box::new(move |_| { c.fetch_add(1, Ordering::SeqCst); HandlerResponse::Continue }));
523
524 let event = make_pointer_down(500.0, 500.0);
526 let result = dispatcher.dispatch(event, &layout, &tree);
527
528 assert_eq!(counter.load(Ordering::SeqCst), 0, "Handler should not fire for miss");
529 assert!(!result.propagation_stopped);
530 }
531
532 #[test]
533 fn dispatch_full_capture_target_bubble() {
534 let platform = Arc::new(MockPlatform::new());
536 let mut tree = ShadowTree::new();
537 let mut layout = LayoutEngine::new();
538
539 for (id, w, h) in [(1, 400.0, 400.0), (2, 300.0, 300.0), (3, 100.0, 100.0)] {
540 tree.create_node(NodeId(id), ViewType::Container, HashMap::new());
541 layout.create_node(NodeId(id), &LayoutStyle {
542 width: Dimension::Points(w),
543 height: Dimension::Points(h),
544 ..Default::default()
545 }).unwrap();
546 }
547
548 tree.set_root(NodeId(1));
549 layout.set_root(NodeId(1));
550 tree.append_child(NodeId(1), NodeId(2));
551 tree.append_child(NodeId(2), NodeId(3));
552 layout.set_children_from_tree(NodeId(1), &tree).unwrap();
553 layout.set_children_from_tree(NodeId(2), &tree).unwrap();
554 layout.compute(&tree, 400.0, 400.0, &*platform).unwrap();
555
556 let mut dispatcher = EventDispatcher::new();
557 let order = Arc::new(std::sync::Mutex::new(Vec::new()));
558
559 for id in [1u64, 2, 3] {
560 let oc = order.clone();
561 dispatcher.add_handler(NodeId(id), EventKind::PointerDown, EventPhase::Capture,
562 Box::new(move |_| { oc.lock().unwrap().push((id, "cap")); HandlerResponse::Continue }));
563
564 let ob = order.clone();
565 dispatcher.add_handler(NodeId(id), EventKind::PointerDown, EventPhase::Bubble,
566 Box::new(move |_| { ob.lock().unwrap().push((id, "bub")); HandlerResponse::Continue }));
567 }
568
569 let event = make_pointer_down(50.0, 50.0);
570 dispatcher.dispatch(event, &layout, &tree);
571
572 let fired = order.lock().unwrap().clone();
573 assert_eq!(fired, vec![
575 (1, "cap"), (2, "cap"), (3, "cap"),
576 (3, "bub"), (2, "bub"), (1, "bub"),
577 ]);
578 }
579
580 #[test]
581 fn remove_handlers_cleanup() {
582 let platform = Arc::new(MockPlatform::new());
583 let (tree, layout, mut dispatcher) = setup_simple(&platform);
584
585 let counter = Arc::new(AtomicU32::new(0));
586 let c = counter.clone();
587 dispatcher.add_handler(NodeId(2), EventKind::PointerDown, EventPhase::Bubble,
588 Box::new(move |_| { c.fetch_add(1, Ordering::SeqCst); HandlerResponse::Continue }));
589
590 dispatcher.remove_handlers_for(NodeId(2));
591
592 let event = make_pointer_down(50.0, 50.0);
593 dispatcher.dispatch(event, &layout, &tree);
594
595 assert_eq!(counter.load(Ordering::SeqCst), 0, "Handlers should be removed");
596 }
597
598 #[test]
599 fn gesture_tap_recognition() {
600 let platform = Arc::new(MockPlatform::new());
601 let (tree, layout, mut dispatcher) = setup_simple(&platform);
602
603 let tapped = Arc::new(AtomicU32::new(0));
604 let t = tapped.clone();
605 dispatcher.add_handler(NodeId(2), EventKind::Tap, EventPhase::Bubble,
606 Box::new(move |_| { t.fetch_add(1, Ordering::SeqCst); HandlerResponse::Continue }));
607
608 let now = Instant::now();
610 let down = InputEvent::PointerDown(PointerEvent {
611 pointer_id: 1,
612 pointer_type: PointerType::Touch,
613 screen_x: 50.0, screen_y: 50.0,
614 pressure: 1.0, buttons: 1,
615 modifiers: Modifiers::default(),
616 timestamp: now, target: None,
617 });
618 let up = InputEvent::PointerUp(PointerEvent {
619 pointer_id: 1,
620 pointer_type: PointerType::Touch,
621 screen_x: 50.0, screen_y: 50.0,
622 pressure: 0.0, buttons: 0,
623 modifiers: Modifiers::default(),
624 timestamp: now + Duration::from_millis(50), target: None,
625 });
626
627 dispatcher.dispatch(down, &layout, &tree);
628 dispatcher.dispatch(up, &layout, &tree);
629
630 assert_eq!(tapped.load(Ordering::SeqCst), 1, "Tap gesture should fire");
631 }
632
633 #[test]
634 fn event_kind_mapping() {
635 assert_eq!(make_pointer_down(0.0, 0.0).kind(), EventKind::PointerDown);
636
637 let scroll = InputEvent::Scroll(ScrollEvent {
638 delta_x: 1.0, delta_y: 2.0,
639 modifiers: Modifiers::default(),
640 target: None,
641 });
642 assert_eq!(scroll.kind(), EventKind::Scroll);
643
644 let key = InputEvent::KeyDown(KeyboardEvent {
645 code: "KeyA".into(), key: "a".into(),
646 modifiers: Modifiers::default(), is_repeat: false, target: None,
647 });
648 assert_eq!(key.kind(), EventKind::KeyDown);
649 }
650
651 #[test]
652 fn screen_position_extraction() {
653 let pe = make_pointer_down(42.0, 99.0);
654 assert_eq!(pe.screen_position(), Some((42.0, 99.0)));
655
656 let tap = InputEvent::Tap { x: 10.0, y: 20.0, target: None };
657 assert_eq!(tap.screen_position(), Some((10.0, 20.0)));
658
659 let key = InputEvent::KeyDown(KeyboardEvent {
660 code: "Space".into(), key: " ".into(),
661 modifiers: Modifiers::default(), is_repeat: false, target: None,
662 });
663 assert_eq!(key.screen_position(), None);
664 }
665}