Skip to main content

ferroui_base/input/
pointer_event_args.rs

1use super::raw::RawPointerPoint;
2use super::{
3    CaptureSource, IKeyModifiersEventArgs, IPointer, InputElement, KeyModifiers, Pointer, PointerPoint,
4    PointerPointProperties,
5};
6use crate::interactivity::{RoutedEvent, RoutedEventArgs};
7use crate::rendering::IPresentationSource;
8use crate::{ferro_routed_event_args, FerroObject, Nullable, Point, Ref, Visual};
9use std::any::Any;
10use std::cell::LazyCell;
11use std::rc::Rc;
12
13/// The lazily computed intermediate points of a pointer move: the positions
14/// the platform coalesced into one event.
15pub type IntermediatePoints =
16    Rc<LazyCell<Option<Vec<RawPointerPoint>>, Box<dyn FnOnce() -> Option<Vec<RawPointerPoint>>>>>;
17
18/// Provides data for pointer events.
19#[derive(Clone)]
20pub struct PointerEventArgs {
21    base: RoutedEventArgs,
22    /// The original observer of the event.
23    event_presentation_source: Option<Rc<dyn IPresentationSource>>,
24    presentation_source_position: Point,
25    properties: PointerPointProperties,
26    previous_points: Option<IntermediatePoints>,
27    pointer: Rc<dyn IPointer>,
28    timestamp: u64,
29    platform_input_event_cookie: Option<Rc<dyn Any>>,
30    key_modifiers: KeyModifiers,
31}
32
33ferro_routed_event_args!(PointerEventArgs: RoutedEventArgs);
34
35impl PointerEventArgs {
36    /// Creates pointer event args.
37    ///
38    /// `root_visual` is the root of the tree that observed the event and
39    /// `root_visual_position` the position of the pointer in its
40    /// coordinates. `timestamp` is the platform time stamp of the input.
41    #[allow(clippy::too_many_arguments)]
42    pub fn new<T: ?Sized>(
43        routed_event: Option<&RoutedEvent<T>>,
44        source: impl Into<Nullable<FerroObject>>,
45        pointer: Rc<dyn IPointer>,
46        root_visual: Option<&Visual>,
47        root_visual_position: Point,
48        timestamp: u64,
49        properties: PointerPointProperties,
50        modifiers: KeyModifiers,
51    ) -> Self {
52        let base = RoutedEventArgs::new();
53        base.set_routed_event(routed_event);
54        base.set_source(source);
55
56        Self {
57            base,
58            event_presentation_source: root_visual.and_then(Visual::presentation_source),
59            presentation_source_position: root_visual_position,
60            properties,
61            previous_points: None,
62            pointer,
63            timestamp,
64            platform_input_event_cookie: None,
65            key_modifiers: modifiers,
66        }
67    }
68
69    /// Adds the intermediate points of the event.
70    pub fn with_previous_points(mut self, previous_points: Option<IntermediatePoints>) -> Self {
71        self.previous_points = previous_points;
72        self
73    }
74
75    /// Adds the platform's cookie for the input event.
76    pub fn with_platform_input_event_cookie(mut self, cookie: Option<Rc<dyn Any>>) -> Self {
77        self.platform_input_event_cookie = cookie;
78        self
79    }
80
81    /// The pointer that caused the event.
82    #[inline]
83    pub fn pointer(&self) -> &Rc<dyn IPointer> {
84        &self.pointer
85    }
86
87    /// The time when the pointer event occurred, as reported by the
88    /// platform.
89    #[inline]
90    pub fn timestamp(&self) -> u64 {
91        self.timestamp
92    }
93
94    /// The platform's opaque cookie for the input event that caused this
95    /// event, if any.
96    #[inline]
97    pub fn platform_input_event_cookie(&self) -> Option<&Rc<dyn Any>> {
98        self.platform_input_event_cookie.as_ref()
99    }
100
101    /// Whether gesture recognition is skipped for the pointer of the event.
102    pub(crate) fn is_gesture_recognition_skipped(&self) -> bool {
103        self.pointer.as_any().downcast_ref::<Pointer>().is_some_and(Pointer::is_gesture_recognition_skipped)
104    }
105
106    /// The key modifiers held down when the event occurred.
107    #[inline]
108    pub fn key_modifiers(&self) -> KeyModifiers {
109        self.key_modifiers
110    }
111
112    fn get_position_of(&self, pt: Point, relative_to: Option<&Visual>) -> Point {
113        let Some(relative_to) = relative_to else { return pt };
114
115        let Some(event_source) = &self.event_presentation_source else { return Point::default() };
116        let Some(target_source) = relative_to.presentation_source() else { return Point::default() };
117        let Some(target_root) = target_source.root_visual() else { return Point::default() };
118
119        let mut pt = pt;
120
121        if !std::ptr::addr_eq(Rc::as_ptr(&target_source), Rc::as_ptr(event_source)) {
122            match event_source.point_to_screen(pt).and_then(|screen_pt| target_source.point_to_client(screen_pt)) {
123                Some(target_client_pt) => pt = target_client_pt,
124                None => return Point::default(),
125            }
126        }
127
128        target_root.translate_point(pt, relative_to).unwrap_or_default()
129    }
130
131    /// Gets the pointer position relative to a control, or relative to the
132    /// root of the tree that observed the event when `relative_to` is
133    /// `None`.
134    pub fn get_position(&self, relative_to: Option<&Visual>) -> Point {
135        self.get_position_of(self.presentation_source_position, relative_to)
136    }
137
138    /// Returns the pointer point associated with the current event,
139    /// positioned relative to a control.
140    pub fn get_current_point(&self, relative_to: Option<&Visual>) -> PointerPoint {
141        PointerPoint::new(self.pointer.clone(), self.get_position(relative_to), self.properties)
142    }
143
144    /// Returns all points since the last relevant pointer event, positioned
145    /// relative to a control. The last one is the current point.
146    pub fn get_intermediate_points(&self, relative_to: Option<&Visual>) -> Vec<PointerPoint> {
147        let previous_points = match &self.previous_points {
148            Some(lazy) => match &***lazy {
149                Some(points) if !points.is_empty() => points.as_slice(),
150                _ => &[],
151            },
152            None => &[],
153        };
154
155        let mut points = Vec::with_capacity(previous_points.len() + 1);
156
157        for pt in previous_points {
158            let point_properties = PointerPointProperties::based_on(self.properties, *pt);
159            points.push(PointerPoint::new(
160                self.pointer.clone(),
161                self.get_position_of(pt.position, relative_to),
162                point_properties,
163            ));
164        }
165
166        points.push(self.get_current_point(relative_to));
167        points
168    }
169
170    /// Prevents this event from being handled by other gesture recognizers
171    /// in the route.
172    pub fn prevent_gesture_recognition(&self) {
173        if let Some(pointer) = self.pointer.as_any().downcast_ref::<Pointer>() {
174            pointer.set_is_gesture_recognition_skipped(true);
175        }
176    }
177
178    /// The state of the pointer device at the time of the event.
179    #[inline]
180    pub fn properties(&self) -> PointerPointProperties {
181        self.properties
182    }
183}
184
185impl IKeyModifiersEventArgs for PointerEventArgs {
186    fn key_modifiers(&self) -> KeyModifiers {
187        self.key_modifiers
188    }
189}
190
191/// Enumerates the mouse buttons.
192#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
193pub enum MouseButton {
194    #[default]
195    None,
196    Left,
197    Right,
198    Middle,
199    XButton1,
200    XButton2,
201}
202
203/// Provides data for the pointer pressed event.
204#[derive(Clone)]
205pub struct PointerPressedEventArgs {
206    base: PointerEventArgs,
207    click_count: i32,
208}
209
210ferro_routed_event_args!(PointerPressedEventArgs: PointerEventArgs);
211
212impl PointerPressedEventArgs {
213    /// Creates pointer pressed event args.
214    #[allow(clippy::too_many_arguments)]
215    pub fn new(
216        source: impl Into<Nullable<FerroObject>>,
217        pointer: Rc<dyn IPointer>,
218        root_visual: &Visual,
219        root_visual_position: Point,
220        timestamp: u64,
221        properties: PointerPointProperties,
222        modifiers: KeyModifiers,
223        click_count: i32,
224    ) -> Self {
225        Self {
226            base: PointerEventArgs::new(
227                Some(InputElement::pointer_pressed_event()),
228                source,
229                pointer,
230                Some(root_visual),
231                root_visual_position,
232                timestamp,
233                properties,
234                modifiers,
235            ),
236            click_count,
237        }
238    }
239
240    /// Adds the platform's cookie for the input event.
241    pub fn with_platform_input_event_cookie(mut self, cookie: Option<Rc<dyn Any>>) -> Self {
242        self.base = self.base.with_platform_input_event_cookie(cookie);
243        self
244    }
245
246    /// The number of clicks: 1 for a single click, 2 for a double click and
247    /// so on.
248    #[inline]
249    pub fn click_count(&self) -> i32 {
250        self.click_count
251    }
252}
253
254/// Provides data for the pointer released event.
255#[derive(Clone)]
256pub struct PointerReleasedEventArgs {
257    base: PointerEventArgs,
258    initial_press_mouse_button: MouseButton,
259}
260
261ferro_routed_event_args!(PointerReleasedEventArgs: PointerEventArgs);
262
263impl PointerReleasedEventArgs {
264    /// Creates pointer released event args.
265    #[allow(clippy::too_many_arguments)]
266    pub fn new(
267        source: impl Into<Nullable<FerroObject>>,
268        pointer: Rc<dyn IPointer>,
269        root_visual: &Visual,
270        root_visual_position: Point,
271        timestamp: u64,
272        properties: PointerPointProperties,
273        modifiers: KeyModifiers,
274        initial_press_mouse_button: MouseButton,
275    ) -> Self {
276        Self {
277            base: PointerEventArgs::new(
278                Some(InputElement::pointer_released_event()),
279                source,
280                pointer,
281                Some(root_visual),
282                root_visual_position,
283                timestamp,
284                properties,
285                modifiers,
286            ),
287            initial_press_mouse_button,
288        }
289    }
290
291    /// The mouse button that triggered the corresponding pointer pressed
292    /// event.
293    #[inline]
294    pub fn initial_press_mouse_button(&self) -> MouseButton {
295        self.initial_press_mouse_button
296    }
297}
298
299/// Provides data for the pointer capture lost event.
300#[derive(Clone)]
301pub struct PointerCaptureLostEventArgs {
302    base: RoutedEventArgs,
303    pointer: Rc<dyn IPointer>,
304}
305
306ferro_routed_event_args!(PointerCaptureLostEventArgs: RoutedEventArgs);
307
308impl PointerCaptureLostEventArgs {
309    /// Creates pointer capture lost event args.
310    pub fn new(source: impl Into<Nullable<FerroObject>>, pointer: Rc<dyn IPointer>) -> Self {
311        Self {
312            base: RoutedEventArgs::with_event_and_source(InputElement::pointer_capture_lost_event(), source),
313            pointer,
314        }
315    }
316
317    /// The pointer whose capture was lost.
318    #[inline]
319    pub fn pointer(&self) -> &Rc<dyn IPointer> {
320        &self.pointer
321    }
322}
323
324/// Provides data for the pointer capture changing event: raised before the
325/// capture of a pointer changes; marking it handled cancels the change.
326#[derive(Clone)]
327pub struct PointerCaptureChangingEventArgs {
328    base: RoutedEventArgs,
329    pointer: Rc<dyn IPointer>,
330    capture_source: CaptureSource,
331    new_value: Option<Ref<InputElement>>,
332}
333
334ferro_routed_event_args!(PointerCaptureChangingEventArgs: RoutedEventArgs);
335
336impl PointerCaptureChangingEventArgs {
337    pub(crate) fn new(
338        source: impl Into<Nullable<FerroObject>>,
339        pointer: Rc<dyn IPointer>,
340        new_value: Option<Ref<InputElement>>,
341        capture_source: CaptureSource,
342    ) -> Self {
343        Self {
344            base: RoutedEventArgs::with_event_and_source(InputElement::pointer_capture_changing_event(), source),
345            pointer,
346            capture_source,
347            new_value,
348        }
349    }
350
351    /// The pointer whose capture is changing.
352    #[inline]
353    pub fn pointer(&self) -> &Rc<dyn IPointer> {
354        &self.pointer
355    }
356
357    /// What requested the capture change.
358    #[inline]
359    pub fn capture_source(&self) -> CaptureSource {
360        self.capture_source
361    }
362
363    /// The element that will capture the pointer.
364    #[inline]
365    pub fn new_value(&self) -> Option<&Ref<InputElement>> {
366        self.new_value.as_ref()
367    }
368}