Skip to main content

ferroui_base/input/gesture_recognizers/
gesture_recognizer.rs

1use crate::input::{
2    IPointer, InputElement, Pointer, PointerEventArgs, PointerPressedEventArgs, PointerReleasedEventArgs,
3};
4use crate::{ferro_class, ferro_impl_classes, FerroObjectImpl, Ref, StyledElement, StyledElementImpl, WeakRef};
5use std::cell::RefCell;
6use std::rc::Rc;
7
8/// The base class of gesture recognizers.
9///
10/// A recognizer is attached to an input element through the element's
11/// gesture recognizer collection and receives the pointer events of that
12/// element. A recognizer that detects its gesture captures the pointer,
13/// after which pointer input goes to the recognizer only.
14#[repr(C)]
15pub struct GestureRecognizer {
16    base: StyledElement,
17    /// The pointer of the pointer event currently being delivered.
18    current_pointer: RefCell<Option<Rc<dyn IPointer>>>,
19    target: RefCell<Option<WeakRef<InputElement>>>,
20}
21
22ferro_class! {
23    GestureRecognizer: StyledElement, virtuals GestureRecognizerImpl: StyledElementImpl {
24        /// Called when a pointer is pressed on the target.
25        fn pointer_pressed(this, e: &PointerPressedEventArgs);
26        /// Called when a pointer is released on the target.
27        fn pointer_released(this, e: &PointerReleasedEventArgs);
28        /// Called when a pointer moves over the target.
29        fn pointer_moved(this, e: &PointerEventArgs);
30        /// Called when the capture of a pointer is lost.
31        fn pointer_capture_lost(this, pointer: &Rc<dyn IPointer>);
32    }
33}
34
35ferro_impl_classes!(GestureRecognizer: FerroObjectImpl, StyledElementImpl);
36
37/// The base class does nothing: recognizers override these (they are
38/// abstract upstream).
39impl GestureRecognizerImpl for GestureRecognizer {
40    fn pointer_pressed(_this: &Self, _e: &PointerPressedEventArgs) {}
41
42    fn pointer_released(_this: &Self, _e: &PointerReleasedEventArgs) {}
43
44    fn pointer_moved(_this: &Self, _e: &PointerEventArgs) {}
45
46    fn pointer_capture_lost(_this: &Self, _pointer: &Rc<dyn IPointer>) {}
47}
48
49impl GestureRecognizer {
50    /// Creates the class data; see [`crate::FerroObject::construct`].
51    pub fn construct() -> Self {
52        Self { base: StyledElement::construct(), current_pointer: RefCell::new(None), target: RefCell::new(None) }
53    }
54
55    /// The element the recognizer is attached to.
56    pub fn target(&self) -> Option<Ref<InputElement>> {
57        self.target.borrow().as_ref().and_then(WeakRef::upgrade)
58    }
59
60    pub(crate) fn set_target(&self, value: Option<&Ref<InputElement>>) {
61        *self.target.borrow_mut() = value.map(Ref::downgrade);
62    }
63
64    fn set_current_pointer(&self, value: Option<Rc<dyn IPointer>>) {
65        let old = self.current_pointer.replace(value);
66        drop(old);
67    }
68
69    pub(crate) fn pointer_pressed_internal(&self, e: &PointerPressedEventArgs) {
70        self.set_current_pointer(Some(e.pointer().clone()));
71        self.pointer_pressed(e);
72        self.set_current_pointer(None);
73    }
74
75    pub(crate) fn pointer_released_internal(&self, e: &PointerReleasedEventArgs) {
76        self.set_current_pointer(Some(e.pointer().clone()));
77        self.pointer_released(e);
78        self.set_current_pointer(None);
79    }
80
81    pub(crate) fn pointer_moved_internal(&self, e: &PointerEventArgs) {
82        self.set_current_pointer(Some(e.pointer().clone()));
83        self.pointer_moved(e);
84        self.set_current_pointer(None);
85    }
86
87    /// Hands a pointer move to the recognizer as the input devices do for
88    /// the recognizer that captured the pointer. For test helpers that
89    /// raise pointer input themselves.
90    #[cfg(any(test, feature = "testing"))]
91    pub fn pointer_moved_for_testing(&self, e: &PointerEventArgs) {
92        self.pointer_moved_internal(e);
93    }
94
95    /// Hands a pointer release to the recognizer as the input devices do
96    /// for the recognizer that captured the pointer. For test helpers that
97    /// raise pointer input themselves.
98    #[cfg(any(test, feature = "testing"))]
99    pub fn pointer_released_for_testing(&self, e: &PointerReleasedEventArgs) {
100        self.pointer_released_internal(e);
101    }
102
103    pub(crate) fn pointer_capture_lost_internal(&self, pointer: &Rc<dyn IPointer>) {
104        self.pointer_capture_lost(pointer);
105    }
106
107    /// Captures a pointer to the recognizer: further input of the pointer
108    /// goes to the recognizer and other recognizers are skipped.
109    pub fn capture(&self, pointer: &Rc<dyn IPointer>) {
110        if let Some(pointer) = pointer.as_any().downcast_ref::<Pointer>() {
111            pointer.capture_gesture_recognizer(Some(&self.to_ref()));
112        }
113
114        // Prevent gesture recognition for the event being delivered.
115        let current = self.current_pointer.borrow().clone();
116        if let Some(current) = current {
117            if let Some(current) = current.as_any().downcast_ref::<Pointer>() {
118                current.set_is_gesture_recognition_skipped(true);
119            }
120        }
121    }
122}