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 last_position: Option<Vec2>,
19 last_move_time: Option<Instant>,
20}
21
22pub struct DragEvent {
23 pub start: Vec2,
24 pub current: Vec2,
25 pub delta: Vec2,
26 pub velocity: Vec2,
27}
28
29pub enum SwipeDirection {
30 Up,
31 Down,
32 Left,
33 Right,
34}
35
36impl Default for GestureDetector {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl GestureDetector {
43 pub fn new() -> Self {
44 Self {
45 on_tap: None,
46 on_double_tap: None,
47 on_long_press: None,
48 on_drag: None,
49 on_swipe: None,
50 last_tap: None,
51 press_start: None,
52 drag_start: None,
53 last_position: None,
54 last_move_time: None,
55 }
56 }
57
58 pub fn handle_pointer(&mut self, event: &PointerEvent) {
59 match event.event {
60 PointerEventKind::Down(_) => {
61 self.press_start = Some((Instant::now(), event.position));
62 self.drag_start = Some(event.position);
63 self.last_position = Some(event.position);
64 self.last_move_time = Some(Instant::now());
65
66 if let Some(last) = self.last_tap
68 && (Instant::now() - last) < Duration::from_millis(300)
69 {
70 if let Some(cb) = &self.on_double_tap {
71 cb(event.position);
72 }
73 self.last_tap = None;
74 }
75 }
76 PointerEventKind::Up(_) => {
77 if let Some((start_time, start_pos)) = self.press_start {
78 let elapsed = Instant::now() - start_time;
79 let distance = ((event.position.x - start_pos.x).powi(2)
80 + (event.position.y - start_pos.y).powi(2))
81 .sqrt();
82
83 if elapsed < Duration::from_millis(200) && distance < 10.0 {
84 if let Some(cb) = &self.on_tap {
86 cb(event.position);
87 }
88 self.last_tap = Some(Instant::now());
89 } else if distance > 50.0 {
90 let dx = event.position.x - start_pos.x;
92 let dy = event.position.y - start_pos.y;
93
94 if let Some(cb) = &self.on_swipe {
95 let dir = if dx.abs() > dy.abs() {
96 if dx > 0.0 {
97 SwipeDirection::Right
98 } else {
99 SwipeDirection::Left
100 }
101 } else if dy > 0.0 {
102 SwipeDirection::Down
103 } else {
104 SwipeDirection::Up
105 };
106 cb(dir);
107 }
108 }
109 }
110 self.press_start = None;
111 self.drag_start = None;
112 self.last_position = None;
113 self.last_move_time = None;
114 }
115 PointerEventKind::Move => {
116 if let Some(start) = self.drag_start
117 && let Some(cb) = &self.on_drag
118 {
119 let delta = if let Some(prev) = self.last_position {
120 Vec2 {
121 x: event.position.x - prev.x,
122 y: event.position.y - prev.y,
123 }
124 } else {
125 Vec2::default()
126 };
127
128 let velocity = if let (Some(prev_time), Some(now)) =
129 (self.last_move_time, Some(Instant::now()))
130 {
131 let dt = (now - prev_time).as_secs_f32().max(1.0 / 240.0);
132 Vec2 {
133 x: delta.x / dt,
134 y: delta.y / dt,
135 }
136 } else {
137 Vec2::default()
138 };
139
140 cb(DragEvent {
141 start,
142 current: event.position,
143 delta,
144 velocity,
145 });
146 }
147
148 self.last_position = Some(event.position);
149 self.last_move_time = Some(Instant::now());
150
151 if let Some((start_time, pos)) = self.press_start
153 && (Instant::now() - start_time) > Duration::from_millis(500)
154 {
155 if let Some(cb) = &self.on_long_press {
156 cb(pos);
157 }
158 self.press_start = None; }
160 }
161 _ => {}
162 }
163 }
164}