1use std::fmt::Debug;
7
8use cranpose_foundation::PointerId;
9use cranpose_render_common::Renderer;
10
11use crate::{AppShell, PointerSource};
12
13enum Route {
15 Primary,
16 Secondary(PointerId),
17 Untracked,
18}
19
20#[derive(Debug)]
26pub struct TouchRouter<F> {
27 primary: Option<F>,
28 secondaries: Vec<(F, PointerId)>,
31 next_secondary_id: PointerId,
32}
33
34impl<F> Default for TouchRouter<F> {
35 fn default() -> Self {
36 Self {
37 primary: None,
38 secondaries: Vec::new(),
39 next_secondary_id: PointerId::PRIMARY,
40 }
41 }
42}
43
44impl<F: Copy + Eq> TouchRouter<F> {
45 pub fn press<R>(&mut self, shell: &mut AppShell<R>, finger: Option<F>, x: f32, y: f32) -> bool
47 where
48 R: Renderer,
49 R::Error: Debug,
50 {
51 match self.route_press(finger) {
52 Route::Primary => {
53 let event_time = shell.realtime_pointer_event_time(None);
54 shell.set_cursor_at_event_time(x, y, event_time);
55 shell.pointer_pressed_at_event_time(event_time)
56 }
57 Route::Secondary(id) => shell.secondary_pointer_pressed(id, x, y, None),
58 Route::Untracked => false,
59 }
60 }
61
62 pub fn moved<R>(&self, shell: &mut AppShell<R>, finger: Option<F>, x: f32, y: f32) -> bool
65 where
66 R: Renderer,
67 R::Error: Debug,
68 {
69 match self.route(finger) {
70 Route::Primary => shell.set_cursor(x, y),
71 Route::Secondary(id) => shell.secondary_pointer_moved(id, x, y, None),
72 Route::Untracked => false,
73 }
74 }
75
76 pub fn release<R>(&mut self, shell: &mut AppShell<R>, finger: Option<F>, x: f32, y: f32) -> bool
80 where
81 R: Renderer,
82 R::Error: Debug,
83 {
84 let Some(finger) = finger else {
85 return release_primary(shell, x, y);
86 };
87 if self.primary == Some(finger) {
88 let mut changed = false;
89 for (_, id) in self.secondaries.drain(..) {
90 changed |= shell.secondary_pointer_released(id, x, y, None);
91 }
92 self.end_gesture();
93 return release_primary(shell, x, y) | changed;
94 }
95 if let Some(id) = self.take_secondary(finger) {
96 return shell.secondary_pointer_released(id, x, y, None);
97 }
98 self.is_idle() && release_primary(shell, x, y)
101 }
102
103 pub fn cancel<R>(
108 &mut self,
109 shell: &mut AppShell<R>,
110 finger: Option<F>,
111 position: Option<(f32, f32)>,
112 ) -> bool
113 where
114 R: Renderer,
115 R::Error: Debug,
116 {
117 let secondary = finger
118 .filter(|finger| self.primary != Some(*finger))
119 .and_then(|finger| self.take_secondary(finger));
120 if let Some(id) = secondary {
121 shell.set_pointer_source(PointerSource::Touch);
122 let (x, y) = position.unwrap_or((0.0, 0.0));
123 return shell.secondary_pointer_released(id, x, y, None);
124 }
125 let ends_gesture = finger.is_none() || self.primary == finger || self.is_idle();
126 if ends_gesture {
127 self.end_gesture();
128 shell.cancel_gesture();
129 }
130 ends_gesture
131 }
132
133 fn route_press(&mut self, finger: Option<F>) -> Route {
134 let Some(finger) = finger else {
135 return Route::Primary;
136 };
137 match self.primary {
138 None => {
139 self.primary = Some(finger);
140 Route::Primary
141 }
142 Some(primary) if primary == finger => Route::Primary,
143 Some(_) => Route::Secondary(self.secondary_id(finger).unwrap_or_else(|| {
144 self.next_secondary_id = PointerId(self.next_secondary_id.0 + 1);
145 self.secondaries.push((finger, self.next_secondary_id));
146 self.next_secondary_id
147 })),
148 }
149 }
150
151 fn route(&self, finger: Option<F>) -> Route {
152 match finger {
153 None => Route::Primary,
154 Some(finger) if self.primary == Some(finger) => Route::Primary,
155 Some(finger) => self
156 .secondary_id(finger)
157 .map_or(Route::Untracked, Route::Secondary),
158 }
159 }
160
161 fn secondary_id(&self, finger: F) -> Option<PointerId> {
162 self.secondaries
163 .iter()
164 .find(|(tracked, _)| *tracked == finger)
165 .map(|(_, id)| *id)
166 }
167
168 fn take_secondary(&mut self, finger: F) -> Option<PointerId> {
169 let index = self
170 .secondaries
171 .iter()
172 .position(|(tracked, _)| *tracked == finger)?;
173 Some(self.secondaries.remove(index).1)
174 }
175
176 fn is_idle(&self) -> bool {
177 self.primary.is_none() && self.secondaries.is_empty()
178 }
179
180 fn end_gesture(&mut self) {
181 self.primary = None;
182 self.secondaries.clear();
183 self.next_secondary_id = PointerId::PRIMARY;
184 }
185}
186
187fn release_primary<R>(shell: &mut AppShell<R>, x: f32, y: f32) -> bool
188where
189 R: Renderer,
190 R::Error: Debug,
191{
192 let event_time = shell.realtime_pointer_event_time(None);
193 shell.pointer_released_at_position_event_time(x, y, event_time)
194}