1use super::{
2 HoldingRoutedEventArgs, HoldingState, IPointer, InputElement, MouseButton, PointerEventArgs,
3 PointerPressedEventArgs, PointerReleasedEventArgs, PointerType, TappedEventArgs,
4};
5use crate::interactivity::{IRoutedEventArgs, Interactive, RoutingStrategies};
6use crate::reactive::IDisposable;
7use crate::threading::{DispatcherPriority, DispatcherTimer};
8use crate::utilities::HandlerList;
9use crate::{FerroObject, Point, Rect, Ref, Size, Thickness, Visual, WeakRef};
10use std::cell::{Cell, RefCell};
11use std::rc::Rc;
12
13#[derive(Clone, Copy, PartialEq, Eq)]
14enum GestureStateType {
15 Pending,
16 Holding,
17 DoubleTapped,
18}
19
20#[derive(Clone)]
21struct GestureState {
22 type_: GestureStateType,
23 pointer: Rc<dyn IPointer>,
24}
25
26type HoldingHandler = dyn Fn(&Interactive, &HoldingRoutedEventArgs);
27type TappedHandler = dyn Fn(&Interactive, &TappedEventArgs);
28
29struct GesturesData {
30 holding: HandlerList<HoldingHandler>,
34 tapped: HandlerList<TappedHandler>,
35 right_tapped: HandlerList<TappedHandler>,
36 double_tapped: HandlerList<TappedHandler>,
37
38 gesture_state: RefCell<Option<GestureState>>,
39 last_press: RefCell<Option<WeakRef<FerroObject>>>,
40 last_press_point: Cell<Point>,
41 hold_cancellation_token: RefCell<Option<Rc<Cell<bool>>>>,
42 hold_timer: RefCell<Option<Rc<dyn IDisposable>>>,
43}
44
45crate::ferro_thread_local! {
46 static DATA: &'static GesturesData = Box::leak(Box::new(GesturesData {
47 holding: HandlerList::new(),
48 tapped: HandlerList::new(),
49 right_tapped: HandlerList::new(),
50 double_tapped: HandlerList::new(),
51 gesture_state: RefCell::new(None),
52 last_press: RefCell::new(None),
53 last_press_point: Cell::new(Point::new(0.0, 0.0)),
54 hold_cancellation_token: RefCell::new(None),
55 hold_timer: RefCell::new(None),
56 }));
57}
58
59fn data() -> &'static GesturesData {
60 DATA.with(|data| *data)
61}
62
63pub struct Gestures;
66
67crate::ferro_static_type!(Gestures);
68
69impl Gestures {
70 fn static_constructor() {
72 InputElement::pointer_pressed_event().route_finished().subscribe(Self::pointer_pressed);
73 InputElement::pointer_released_event().route_finished().subscribe(Self::pointer_released);
74 InputElement::pointer_moved_event().route_finished().subscribe(Self::pointer_moved);
75 }
76
77 pub(crate) fn add_holding(handler: impl Fn(&Interactive, &HoldingRoutedEventArgs) + 'static) {
78 data().holding.add(Rc::new(handler));
79 }
80
81 pub(crate) fn add_tapped(handler: impl Fn(&Interactive, &TappedEventArgs) + 'static) {
82 data().tapped.add(Rc::new(handler));
83 }
84
85 pub(crate) fn add_right_tapped(handler: impl Fn(&Interactive, &TappedEventArgs) + 'static) {
86 data().right_tapped.add(Rc::new(handler));
87 }
88
89 pub(crate) fn add_double_tapped(handler: impl Fn(&Interactive, &TappedEventArgs) + 'static) {
90 data().double_tapped.add(Rc::new(handler));
91 }
92
93 fn invoke_holding(sender: &Interactive, e: &HoldingRoutedEventArgs) {
94 for (_, handler) in data().holding.snapshot().iter() {
95 handler(sender, e);
96 }
97 }
98
99 fn invoke_tapped(list: &HandlerList<TappedHandler>, sender: &Interactive, e: &TappedEventArgs) {
100 for (_, handler) in list.snapshot().iter() {
101 handler(sender, e);
102 }
103 }
104
105 fn gesture_state() -> Option<GestureState> {
106 data().gesture_state.borrow().clone()
107 }
108
109 fn set_gesture_state(value: Option<GestureState>) {
110 let old = data().gesture_state.replace(value);
111 drop(old);
112 }
113
114 fn last_press() -> Option<Ref<FerroObject>> {
115 data().last_press.borrow().as_ref().and_then(WeakRef::upgrade)
116 }
117
118 fn cancel_hold() {
119 let token = data().hold_cancellation_token.borrow_mut().take();
120 let timer = data().hold_timer.borrow_mut().take();
121 if let Some(token) = token {
122 token.set(true);
123 }
124 if let Some(timer) = timer {
129 timer.dispose();
130 }
131 }
132
133 fn get_captured(args: &dyn IRoutedEventArgs) -> Option<Ref<FerroObject>> {
134 let pointer_event_args = args.downcast_ref::<PointerEventArgs>()?;
135
136 match pointer_event_args.pointer().captured() {
137 Some(captured) => Some(captured.upcast()),
138 None => pointer_event_args.source(),
139 }
140 }
141
142 fn rect_around(point: Point, size: Size) -> Rect {
143 Rect::from_position_size(point, Size::default())
144 .inflate_thickness(Thickness::symmetric(size.width, size.height))
145 }
146
147 fn pointer_pressed(ev: &dyn IRoutedEventArgs) {
148 let Some(source) = Self::get_captured(ev) else { return };
149
150 if ev.route() != RoutingStrategies::BUBBLE {
151 return;
152 }
153
154 let Some(e) = ev.downcast_ref::<PointerPressedEventArgs>() else { return };
155 let Ok(visual) = source.clone().downcast::<Visual>() else { return };
156 let interactive = source.cast::<Interactive>();
157 let data = data();
158
159 if let Some(state) = Self::gesture_state() {
160 if state.type_ == GestureStateType::Holding {
161 if let Some(i) = &interactive {
162 Self::invoke_holding(
163 i,
164 &HoldingRoutedEventArgs::new(
165 HoldingState::Canceled,
166 data.last_press_point.get(),
167 state.pointer.type_(),
168 e,
169 ),
170 );
171 }
172 }
173
174 Self::cancel_hold();
175 Self::set_gesture_state(None);
176 }
177
178 if e.click_count() % 2 == 1 {
179 Self::set_gesture_state(Some(GestureState { type_: GestureStateType::Pending, pointer: e.pointer().clone() }));
180 drop(data.last_press.replace(Some(source.downgrade())));
181 data.last_press_point.set(e.get_position(Some(&visual)));
182
183 let token = Rc::new(Cell::new(false));
184 drop(data.hold_cancellation_token.replace(Some(token.clone())));
185
186 if let Some(settings) = visual.get_platform_settings() {
187 let e = e.clone();
188 let source = source.clone();
189
190 let timer = DispatcherTimer::run_once(
191 move || {
192 if token.get() {
193 return;
194 }
195 Self::cancel_hold();
199 let Some(state) = Self::gesture_state() else { return };
200 let Some(i) = source.cast::<InputElement>() else { return };
201
202 if InputElement::get_is_holding_enabled(&i)
203 && (e.pointer().type_() != PointerType::Mouse
204 || InputElement::get_is_hold_with_mouse_enabled(&i))
205 {
206 let pointer_type = state.pointer.type_();
207 Self::set_gesture_state(Some(GestureState {
208 type_: GestureStateType::Holding,
209 pointer: state.pointer,
210 }));
211 Self::invoke_holding(
212 &i,
213 &HoldingRoutedEventArgs::new(
214 HoldingState::Started,
215 self::data().last_press_point.get(),
216 pointer_type,
217 &e,
218 ),
219 );
220 }
221 },
222 settings.hold_wait_duration(),
223 DispatcherPriority::default(),
224 );
225 let previous = data.hold_timer.replace(Some(timer));
226 if let Some(previous) = previous {
227 previous.dispose();
228 }
229 }
230 } else if e.click_count() % 2 == 0 && e.get_current_point(Some(&visual)).properties.is_left_button_pressed {
231 if let Some(i) = &interactive {
232 if Self::last_press().is_some_and(|target| target == source) {
233 Self::set_gesture_state(Some(GestureState {
234 type_: GestureStateType::DoubleTapped,
235 pointer: e.pointer().clone(),
236 }));
237 Self::invoke_tapped(
238 &data.double_tapped,
239 i,
240 &TappedEventArgs::new(Some(InputElement::double_tapped_event()), e),
241 );
242 }
243 }
244 }
245 }
246
247 fn pointer_released(ev: &dyn IRoutedEventArgs) {
248 if ev.route() != RoutingStrategies::BUBBLE {
249 return;
250 }
251
252 let Some(e) = ev.downcast_ref::<PointerReleasedEventArgs>() else { return };
253 let source = Self::get_captured(ev);
254 let data = data();
255
256 if let (Some(target), Some(source)) = (Self::last_press(), &source) {
257 let button = e.initial_press_mouse_button();
258
259 if target == *source && matches!(button, MouseButton::Left | MouseButton::Right) {
260 if let (Some(i), Some(target_visual)) = (source.cast::<Interactive>(), target.cast::<Visual>()) {
261 let point = e.get_current_point(Some(&target_visual));
262 let tap_size = i
263 .get_platform_settings()
264 .map(|settings| settings.get_tap_size(point.pointer.type_()))
265 .unwrap_or(Size::new(4.0, 4.0));
266 let tap_rect = Self::rect_around(data.last_press_point.get(), tap_size);
267
268 if tap_rect.contains_exclusive(point.position) {
269 let state = Self::gesture_state();
270
271 if let Some(holding) = state.as_ref().filter(|s| s.type_ == GestureStateType::Holding) {
272 Self::invoke_holding(
273 &i,
274 &HoldingRoutedEventArgs::new(
275 HoldingState::Completed,
276 data.last_press_point.get(),
277 holding.pointer.type_(),
278 e,
279 ),
280 );
281 Self::invoke_tapped(
282 &data.right_tapped,
283 &i,
284 &TappedEventArgs::new(Some(InputElement::right_tapped_event()), e),
285 );
286 } else if button == MouseButton::Right {
287 Self::invoke_tapped(
288 &data.right_tapped,
289 &i,
290 &TappedEventArgs::new(Some(InputElement::right_tapped_event()), e),
291 );
292 }
293 else if state.as_ref().map(|s| s.type_) != Some(GestureStateType::DoubleTapped) {
297 Self::invoke_tapped(
298 &data.tapped,
299 &i,
300 &TappedEventArgs::new(Some(InputElement::tapped_event()), e),
301 );
302 }
303 }
304
305 Self::set_gesture_state(None);
306 }
307 }
308 }
309
310 Self::cancel_hold();
311 }
312
313 fn pointer_moved(ev: &dyn IRoutedEventArgs) {
314 if ev.route() != RoutingStrategies::BUBBLE {
315 return;
316 }
317
318 let Some(e) = ev.downcast_ref::<PointerEventArgs>() else { return };
319 let source = Self::get_captured(ev);
320
321 let Some(target) = Self::last_press() else { return };
322 let Some(state) = Self::gesture_state() else { return };
323
324 if !std::ptr::addr_eq(Rc::as_ptr(e.pointer()), Rc::as_ptr(&state.pointer)) {
325 return;
326 }
327
328 let Some(i) = source.and_then(|source| source.downcast::<Interactive>().ok()) else { return };
329 let Some(target_visual) = target.cast::<Visual>() else { return };
330
331 let point = e.get_current_point(Some(&target_visual));
332 let last_press_point = data().last_press_point.get();
333 let hold_rect = Self::rect_around(last_press_point, Size::new(4.0, 4.0));
334
335 if hold_rect.contains_exclusive(point.position) {
336 return;
337 }
338
339 if state.type_ == GestureStateType::Holding {
340 Self::invoke_holding(
341 &i,
342 &HoldingRoutedEventArgs::new(HoldingState::Canceled, last_press_point, state.pointer.type_(), e),
343 );
344 }
345
346 Self::cancel_hold();
347 Self::set_gesture_state(None);
348 }
349}