Skip to main content

ferroui_controls/primitives/
thumb.rs

1use super::{TemplatedControl, TemplatedControlImpl};
2use crate::metadata::PseudoClassesAttribute;
3use crate::ControlImpl;
4use ferroui_base::input::{
5    InputElementImpl, InputElementImplExt, PointerCaptureLostEventArgs, PointerEventArgs, PointerPressedEventArgs,
6    PointerReleasedEventArgs, VectorEventArgs,
7};
8use ferroui_base::interactivity::{Interactive, InteractiveImpl, RoutedEvent, RoutedEventHandlerToken, RoutingStrategies};
9use ferroui_base::layout::LayoutableImpl;
10use ferroui_base::{
11    ferro_class, ferro_impl_classes, ferro_routed_event, instantiate, FerroObjectImpl, Point,
12    Ref, StyledElementImpl, Vector, VisualImpl,
13};
14use std::cell::Cell;
15
16const PC_PRESSED: &str = ":pressed";
17
18/// A control that can be dragged by the user.
19#[repr(C)]
20pub struct Thumb {
21    base: TemplatedControl,
22    last_point: Cell<Option<Point>>,
23}
24
25ferro_class! {
26    Thumb: TemplatedControl, virtuals ThumbImpl: TemplatedControlImpl {
27        /// Invoked when an unhandled `DragStarted` event reaches an element
28        /// in its route that is derived from this class. Implement this
29        /// method to add class handling for this event.
30        fn on_drag_started(this, e: &VectorEventArgs);
31        /// Invoked when an unhandled `DragDelta` event reaches an element in
32        /// its route that is derived from this class. Implement this method
33        /// to add class handling for this event.
34        fn on_drag_delta(this, e: &VectorEventArgs);
35        /// Invoked when an unhandled `DragCompleted` event reaches an element
36        /// in its route that is derived from this class. Implement this
37        /// method to add class handling for this event.
38        fn on_drag_completed(this, e: &VectorEventArgs);
39    }
40}
41ferroui_base::ferro_class_info!(Thumb { new: Thumb::new });
42
43ferro_impl_classes!(Thumb: StyledElementImpl, VisualImpl, LayoutableImpl, InteractiveImpl, TemplatedControlImpl);
44
45impl ControlImpl for Thumb {
46    fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
47        crate::automation::peers::ThumbAutomationPeer::new(this).upcast()
48    }
49}
50
51ferroui_base::ferro_impl_classes!(Thumb: FerroObjectImpl);
52
53impl InputElementImpl for Thumb {
54    fn on_pointer_capture_lost(this: &Self, e: &PointerCaptureLostEventArgs) {
55        if let Some(last_point) = this.last_point.get() {
56            let mut ev = VectorEventArgs::new();
57            ev.set_routed_event(Some(Self::drag_completed_event()));
58            ev.vector = last_point.into();
59
60            this.last_point.set(None);
61
62            this.raise_event(&ev);
63        }
64
65        this.pseudo_classes().remove_pseudo(PC_PRESSED);
66
67        Self::parent_on_pointer_capture_lost(this, e);
68    }
69
70    fn on_pointer_moved(this: &Self, e: &PointerEventArgs) {
71        if let Some(last_point) = this.last_point.get() {
72            let mut ev = VectorEventArgs::new();
73            ev.set_routed_event(Some(Self::drag_delta_event()));
74            ev.vector = (e.get_position(Some(this)) - last_point).into();
75
76            this.raise_event(&ev);
77        }
78    }
79
80    fn on_pointer_pressed(this: &Self, e: &PointerPressedEventArgs) {
81        e.set_handled(true);
82        let last_point = e.get_position(Some(this));
83        this.last_point.set(Some(last_point));
84
85        let mut ev = VectorEventArgs::new();
86        ev.set_routed_event(Some(Self::drag_started_event()));
87        ev.vector = last_point.into();
88
89        this.pseudo_classes().add_pseudo(PC_PRESSED);
90
91        e.prevent_gesture_recognition();
92
93        this.raise_event(&ev);
94    }
95
96    fn on_pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
97        if this.last_point.get().is_some() {
98            e.set_handled(true);
99            this.last_point.set(None);
100
101            let mut ev = VectorEventArgs::new();
102            ev.set_routed_event(Some(Self::drag_completed_event()));
103            ev.vector = e.get_position(Some(this)).into();
104
105            this.raise_event(&ev);
106        }
107
108        this.pseudo_classes().remove_pseudo(PC_PRESSED);
109    }
110}
111
112impl ThumbImpl for Thumb {
113    fn on_drag_started(_this: &Self, _e: &VectorEventArgs) {}
114
115    fn on_drag_delta(_this: &Self, _e: &VectorEventArgs) {}
116
117    fn on_drag_completed(_this: &Self, _e: &VectorEventArgs) {}
118}
119
120impl Thumb {
121    /// The pseudoclasses set by the class.
122    pub const PSEUDO_CLASSES: PseudoClassesAttribute = PseudoClassesAttribute::new(&[PC_PRESSED]);
123
124    ferro_routed_event!(
125        /// Defines the `DragStarted` routed event.
126        pub fn drag_started_event() -> RoutedEvent<VectorEventArgs> {
127            RoutedEvent::register::<Thumb, _>("DragStarted", RoutingStrategies::BUBBLE)
128        }
129    );
130
131    ferro_routed_event!(
132        /// Defines the `DragDelta` routed event.
133        pub fn drag_delta_event() -> RoutedEvent<VectorEventArgs> {
134            RoutedEvent::register::<Thumb, _>("DragDelta", RoutingStrategies::BUBBLE)
135        }
136    );
137
138    ferro_routed_event!(
139        /// Defines the `DragCompleted` routed event.
140        pub fn drag_completed_event() -> RoutedEvent<VectorEventArgs> {
141            RoutedEvent::register::<Thumb, _>("DragCompleted", RoutingStrategies::BUBBLE)
142        }
143    );
144
145    fn static_constructor() {
146        Self::drag_started_event().add_class_handler_with::<Thumb>(
147            |x, e| x.on_drag_started(e),
148            RoutingStrategies::BUBBLE,
149            false,
150        );
151        Self::drag_delta_event().add_class_handler_with::<Thumb>(
152            |x, e| x.on_drag_delta(e),
153            RoutingStrategies::BUBBLE,
154            false,
155        );
156        Self::drag_completed_event().add_class_handler_with::<Thumb>(
157            |x, e| x.on_drag_completed(e),
158            RoutingStrategies::BUBBLE,
159            false,
160        );
161    }
162
163    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
164    pub fn construct() -> Self {
165        Self { base: TemplatedControl::construct(), last_point: Cell::new(None) }
166    }
167
168    pub fn new() -> Ref<Self> {
169        instantiate(Self::construct())
170    }
171
172    /// Raised when a drag of the thumb starts.
173    pub fn drag_started(&self, handler: impl Fn(&Interactive, &VectorEventArgs) + 'static) -> RoutedEventHandlerToken {
174        self.add_handler(Self::drag_started_event(), handler)
175    }
176
177    /// Raised while the thumb is dragged.
178    pub fn drag_delta(&self, handler: impl Fn(&Interactive, &VectorEventArgs) + 'static) -> RoutedEventHandlerToken {
179        self.add_handler(Self::drag_delta_event(), handler)
180    }
181
182    /// Raised when a drag of the thumb completes.
183    pub fn drag_completed(
184        &self,
185        handler: impl Fn(&Interactive, &VectorEventArgs) + 'static,
186    ) -> RoutedEventHandlerToken {
187        self.add_handler(Self::drag_completed_event(), handler)
188    }
189
190    /// Moves the reference point of the drag in progress by a vector.
191    pub(crate) fn adjust_drag(&self, v: Vector) {
192        if let Some(last_point) = self.last_point.get() {
193            self.last_point.set(Some(last_point + v));
194        }
195    }
196}