Skip to main content

ferroui_base/input/
pointer.rs

1use super::gesture_recognizers::GestureRecognizer;
2use super::{
3    IPointer, InputElement, PointerCaptureChangingEventArgs, PointerCaptureLostEventArgs, PointerType,
4};
5use crate::reactive::IDisposable;
6use crate::{Ref, Visual, VisualTreeAttachmentEventArgs};
7use std::any::Any;
8use std::cell::{Cell, RefCell};
9use std::rc::{Rc, Weak};
10use std::sync::atomic::{AtomicI32, Ordering};
11
12/// What requested a change of pointer capture.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum CaptureSource {
15    /// The capture was requested by application code.
16    Explicit,
17    /// The capture was taken or released by the input system, e.g. when a
18    /// pointer is pressed on or released from an element.
19    Implicit,
20    /// The capture changed in the platform.
21    Platform,
22}
23
24static NEXT_FREE_POINTER_ID: AtomicI32 = AtomicI32::new(1000);
25
26/// Called to capture the pointer in the platform when the effectively
27/// capturing element changes.
28type PlatformCapture = Box<dyn Fn(Option<&Ref<InputElement>>)>;
29
30/// Identifies a specific input pointer and tracks the element that
31/// captures it.
32pub struct Pointer {
33    this: Weak<Pointer>,
34    id: i32,
35    type_: PointerType,
36    is_primary: bool,
37    disposed: Cell<bool>,
38    captured: RefCell<Option<Ref<InputElement>>>,
39    captured_gesture_recognizer: RefCell<Option<Ref<GestureRecognizer>>>,
40    capture_source: Cell<CaptureSource>,
41    is_gesture_recognition_skipped: Cell<bool>,
42    capture_detached: RefCell<Option<Rc<dyn IDisposable>>>,
43    platform_capture: Option<PlatformCapture>,
44}
45
46impl Pointer {
47    /// Returns a pointer identifier that is not in use.
48    pub fn get_next_free_id() -> i32 {
49        NEXT_FREE_POINTER_ID.fetch_add(1, Ordering::Relaxed)
50    }
51
52    /// Creates a pointer.
53    pub fn new(id: i32, type_: PointerType, is_primary: bool) -> Rc<Pointer> {
54        Self::create(id, type_, is_primary, None)
55    }
56
57    /// Creates a pointer that captures the pointer in the platform through
58    /// `platform_capture` whenever the capturing element changes.
59    pub fn with_platform_capture(
60        id: i32,
61        type_: PointerType,
62        is_primary: bool,
63        platform_capture: impl Fn(Option<&Ref<InputElement>>) + 'static,
64    ) -> Rc<Pointer> {
65        Self::create(id, type_, is_primary, Some(Box::new(platform_capture)))
66    }
67
68    fn create(id: i32, type_: PointerType, is_primary: bool, platform_capture: Option<PlatformCapture>) -> Rc<Pointer> {
69        Rc::new_cyclic(|this| Pointer {
70            this: this.clone(),
71            id,
72            type_,
73            is_primary,
74            disposed: Cell::new(false),
75            captured: RefCell::new(None),
76            captured_gesture_recognizer: RefCell::new(None),
77            capture_source: Cell::new(CaptureSource::Platform),
78            is_gesture_recognition_skipped: Cell::new(false),
79            capture_detached: RefCell::new(None),
80            platform_capture,
81        })
82    }
83
84    /// The lowest input element that is both `control1` or one of its
85    /// ancestors and `control2` or one of its ancestors.
86    fn find_common_parent(
87        control1: Option<&Ref<InputElement>>,
88        control2: Option<&Ref<InputElement>>,
89    ) -> Option<Ref<InputElement>> {
90        let (c1, c2) = (control1?, control2?);
91
92        c2.get_self_and_visual_ancestors()
93            .filter_map(|v| v.downcast::<InputElement>().ok())
94            .find(|candidate| candidate == c1 || candidate.is_visual_ancestor_of(c1))
95    }
96
97    fn platform_capture(&self, element: Option<&Ref<InputElement>>) {
98        if let Some(platform_capture) = &self.platform_capture {
99            platform_capture(element);
100        }
101    }
102
103    /// Notifies the pointer that its capture was lost in the platform.
104    pub fn platform_capture_lost(&self) {
105        self.capture_lost(CaptureSource::Platform);
106    }
107
108    pub(crate) fn capture_lost(&self, source: CaptureSource) {
109        if self.disposed.get() {
110            return;
111        }
112
113        self.capture_lost_core(source);
114    }
115
116    fn capture_lost_core(&self, source: CaptureSource) {
117        self.capture_core(None, None, source);
118        self.is_gesture_recognition_skipped.set(false);
119    }
120
121    /// Captures pointer input to the specified control, on behalf of
122    /// `source`.
123    pub fn capture_with_source(&self, control: Option<&Ref<InputElement>>, source: CaptureSource) {
124        if self.disposed.get() {
125            debug_assert!(control.is_none(), "Capturing a pointer that no longer exists.");
126            return;
127        }
128
129        self.capture_core(control.cloned(), None, source);
130    }
131
132    /// The element pointer input effectively goes to: the target of the
133    /// capturing gesture recognizer, or the capturing element.
134    fn effective_capturer(&self) -> Option<Ref<InputElement>> {
135        self.captured_gesture_recognizer().and_then(|recognizer| recognizer.target()).or_else(|| self.captured())
136    }
137
138    /// The gesture recognizer that captures the pointer, if any.
139    pub fn captured_gesture_recognizer(&self) -> Option<Ref<GestureRecognizer>> {
140        self.captured_gesture_recognizer.borrow().clone()
141    }
142
143    fn capture_core(
144        &self,
145        control: Option<Ref<InputElement>>,
146        gesture_recognizer: Option<Ref<GestureRecognizer>>,
147        source: CaptureSource,
148    ) {
149        let old_capture = self.captured();
150        let old_gesture_recognizer = self.captured_gesture_recognizer();
151        let old_source = self.capture_source.get();
152        let old_effective_capturer = self.effective_capturer();
153
154        // If a handler marks an implicit capture as handled, we still want
155        // them to have another chance if the element is captured explicitly.
156        if old_capture == control && old_gesture_recognizer == gesture_recognizer && old_source == source {
157            return;
158        }
159
160        let this: Rc<dyn IPointer> = match self.this.upgrade() {
161            Some(this) => this,
162            None => return,
163        };
164
165        let mut common_parent = None;
166
167        if old_capture.is_some() || control.is_some() {
168            common_parent = Self::find_common_parent(control.as_ref(), old_capture.as_ref());
169
170            // We want the capture to be cancellable even if there is no
171            // currently captured element.
172            let visual: &Visual = old_capture.as_ref().or(control.as_ref()).expect("one of them is set");
173
174            for notify_target in
175                visual.get_self_and_visual_ancestors().filter_map(|v| v.downcast::<InputElement>().ok())
176            {
177                let args = PointerCaptureChangingEventArgs::new(&notify_target, this.clone(), control.clone(), source);
178                notify_target.raise_event(&args);
179
180                if args.handled() {
181                    return;
182                }
183
184                if Some(&notify_target) == common_parent.as_ref() {
185                    break;
186                }
187            }
188        }
189
190        if old_capture.is_some() {
191            let subscription = self.capture_detached.take();
192            if let Some(subscription) = subscription {
193                subscription.dispose();
194            }
195        }
196
197        let this_pointer = this.clone();
198        if old_gesture_recognizer != gesture_recognizer {
199            if let Some(old) = &old_gesture_recognizer {
200                old.pointer_capture_lost_internal(&this_pointer);
201            }
202        }
203
204        drop(self.captured.replace(control.clone()));
205        drop(self.captured_gesture_recognizer.replace(gesture_recognizer));
206        self.capture_source.set(source);
207
208        // However, we still want to notify the platform only if the captured
209        // element actually changed.
210        let effective_capturer = self.effective_capturer();
211        if old_effective_capturer != effective_capturer && source != CaptureSource::Platform {
212            self.platform_capture(effective_capturer.as_ref());
213        }
214
215        if let Some(old_visual) = &old_capture {
216            for notify_target in
217                old_visual.get_self_and_visual_ancestors().filter_map(|v| v.downcast::<InputElement>().ok())
218            {
219                if Some(&notify_target) == common_parent.as_ref() {
220                    break;
221                }
222
223                notify_target.raise_event(&PointerCaptureLostEventArgs::new(&notify_target, this.clone()));
224            }
225        }
226
227        if let Some(new_visual) = &control {
228            let weak = self.this.clone();
229            let subscription = new_visual.detached_from_visual_tree(move |e| {
230                if let Some(pointer) = weak.upgrade() {
231                    pointer.on_capture_detached(e);
232                }
233            });
234            let old = self.capture_detached.replace(Some(subscription));
235            if let Some(old) = old {
236                old.dispose();
237            }
238        }
239
240        if self.captured.borrow().is_none() && self.captured_gesture_recognizer.borrow().is_none() {
241            self.is_gesture_recognition_skipped.set(false);
242        }
243
244        // Update the pointer-over + cursor immediately following the capture
245        // change.
246        if self.type_ != PointerType::Touch {
247            let old_input_root = old_capture.as_ref().and_then(|v| v.get_input_root());
248            let new_input_root = control.as_ref().and_then(|v| v.get_input_root());
249
250            if let Some(old_input_root) = &old_input_root {
251                old_input_root.pointer_over_invalidated();
252            }
253
254            let same_root = match (&old_input_root, &new_input_root) {
255                (Some(a), Some(b)) => std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b)),
256                (None, None) => true,
257                _ => false,
258            };
259
260            if !same_root {
261                if let Some(new_input_root) = &new_input_root {
262                    new_input_root.pointer_over_invalidated();
263                }
264            }
265        }
266    }
267
268    fn get_next_capture(parent: Option<&Ref<Visual>>) -> Option<Ref<InputElement>> {
269        parent.and_then(|parent| parent.find_ancestor_of_type::<InputElement>(true))
270    }
271
272    fn on_capture_detached(&self, e: &VisualTreeAttachmentEventArgs) {
273        let next = Self::get_next_capture(e.attachment_point());
274        self.capture(next.as_ref());
275    }
276
277    /// Captures the pointer to a gesture recognizer (or releases that
278    /// capture).
279    pub(crate) fn capture_gesture_recognizer(&self, gesture_recognizer: Option<&Ref<GestureRecognizer>>) {
280        if self.disposed.get() {
281            debug_assert!(
282                gesture_recognizer.is_none(),
283                "Capturing a pointer that no longer exists to a gesture recognizer."
284            );
285            return;
286        }
287
288        self.capture_core(None, gesture_recognizer.cloned(), CaptureSource::Explicit);
289    }
290
291    /// What requested the current capture state.
292    pub fn capture_source(&self) -> CaptureSource {
293        self.capture_source.get()
294    }
295
296    /// Whether gesture recognition is skipped for the pointer until it is
297    /// released.
298    pub fn is_gesture_recognition_skipped(&self) -> bool {
299        self.is_gesture_recognition_skipped.get()
300    }
301
302    /// Sets whether gesture recognition is skipped for the pointer.
303    pub fn set_is_gesture_recognition_skipped(&self, value: bool) {
304        self.is_gesture_recognition_skipped.set(value)
305    }
306
307    /// Releases the pointer: its capture is lost and it can no longer be
308    /// captured.
309    pub fn dispose(&self) {
310        if self.disposed.get() {
311            return;
312        }
313
314        // Mark the pointer gone first, so a capture lost handler can't
315        // capture it again. It no longer exists, so the platform is the
316        // only source the release can come from.
317        self.disposed.set(true);
318        self.capture_lost_core(CaptureSource::Platform);
319    }
320}
321
322impl IPointer for Pointer {
323    fn id(&self) -> i32 {
324        self.id
325    }
326
327    fn capture(&self, control: Option<&Ref<InputElement>>) {
328        self.capture_with_source(control, CaptureSource::Explicit);
329    }
330
331    fn captured(&self) -> Option<Ref<InputElement>> {
332        self.captured.borrow().clone()
333    }
334
335    fn type_(&self) -> PointerType {
336        self.type_
337    }
338
339    fn is_primary(&self) -> bool {
340        self.is_primary
341    }
342
343    fn as_any(&self) -> &dyn Any {
344        self
345    }
346}