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