1use repose_core::Vec2;
2
3use crate::input::*;
4use std::rc::Rc;
5use web_time::{Duration, Instant};
6
7pub struct GestureDetector {
8 on_tap: Option<Rc<dyn Fn(Vec2)>>,
9 on_double_tap: Option<Rc<dyn Fn(Vec2)>>,
10 on_long_press: Option<Rc<dyn Fn(Vec2)>>,
11 on_drag: Option<Rc<dyn Fn(DragEvent)>>,
12 on_swipe: Option<Rc<dyn Fn(SwipeDirection)>>,
13
14 last_tap: Option<Instant>,
16 press_start: Option<(Instant, Vec2)>,
17 drag_start: Option<Vec2>,
18}
19
20pub struct DragEvent {
21 pub start: Vec2,
22 pub current: Vec2,
23 pub delta: Vec2,
24 pub velocity: Vec2,
25}
26
27pub enum SwipeDirection {
28 Up,
29 Down,
30 Left,
31 Right,
32}
33
34impl Default for GestureDetector {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl GestureDetector {
41 pub fn new() -> Self {
42 Self {
43 on_tap: None,
44 on_double_tap: None,
45 on_long_press: None,
46 on_drag: None,
47 on_swipe: None,
48 last_tap: None,
49 press_start: None,
50 drag_start: None,
51 }
52 }
53
54 pub fn handle_pointer(&mut self, event: &PointerEvent) {
55 match event.event {
56 PointerEventKind::Down(_) => {
57 self.press_start = Some((Instant::now(), event.position));
58 self.drag_start = Some(event.position);
59
60 if let Some(last) = self.last_tap
62 && (Instant::now() - last) < Duration::from_millis(300)
63 {
64 if let Some(cb) = &self.on_double_tap {
65 cb(event.position);
66 }
67 self.last_tap = None;
68 }
69 }
70 PointerEventKind::Up(_) => {
71 if let Some((start_time, start_pos)) = self.press_start {
72 let elapsed = Instant::now() - start_time;
73 let distance = ((event.position.x - start_pos.x).powi(2)
74 + (event.position.y - start_pos.y).powi(2))
75 .sqrt();
76
77 if elapsed < Duration::from_millis(200) && distance < 10.0 {
78 if let Some(cb) = &self.on_tap {
80 cb(event.position);
81 }
82 self.last_tap = Some(Instant::now());
83 } else if distance > 50.0 {
84 let dx = event.position.x - start_pos.x;
86 let dy = event.position.y - start_pos.y;
87
88 if let Some(cb) = &self.on_swipe {
89 let dir = if dx.abs() > dy.abs() {
90 if dx > 0.0 {
91 SwipeDirection::Right
92 } else {
93 SwipeDirection::Left
94 }
95 } else if dy > 0.0 {
96 SwipeDirection::Down
97 } else {
98 SwipeDirection::Up
99 };
100 cb(dir);
101 }
102 }
103 }
104 self.press_start = None;
105 self.drag_start = None;
106 }
107 PointerEventKind::Move => {
108 if let Some(start) = self.drag_start
109 && let Some(cb) = &self.on_drag
110 {
111 cb(DragEvent {
112 start,
113 current: event.position,
114 delta: Vec2 {
115 x: event.position.x - start.x,
116 y: event.position.y - start.y,
117 },
118 velocity: Vec2::default(), });
120 }
121
122 if let Some((start_time, pos)) = self.press_start
124 && (Instant::now() - start_time) > Duration::from_millis(500)
125 {
126 if let Some(cb) = &self.on_long_press {
127 cb(pos);
128 }
129 self.press_start = None; }
131 }
132 _ => {}
133 }
134 }
135}