ferroui_controls/primitives/
thumb.rs1use 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#[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 fn on_drag_started(this, e: &VectorEventArgs);
31 fn on_drag_delta(this, e: &VectorEventArgs);
35 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 pub const PSEUDO_CLASSES: PseudoClassesAttribute = PseudoClassesAttribute::new(&[PC_PRESSED]);
123
124 ferro_routed_event!(
125 pub fn drag_started_event() -> RoutedEvent<VectorEventArgs> {
127 RoutedEvent::register::<Thumb, _>("DragStarted", RoutingStrategies::BUBBLE)
128 }
129 );
130
131 ferro_routed_event!(
132 pub fn drag_delta_event() -> RoutedEvent<VectorEventArgs> {
134 RoutedEvent::register::<Thumb, _>("DragDelta", RoutingStrategies::BUBBLE)
135 }
136 );
137
138 ferro_routed_event!(
139 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 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 pub fn drag_started(&self, handler: impl Fn(&Interactive, &VectorEventArgs) + 'static) -> RoutedEventHandlerToken {
174 self.add_handler(Self::drag_started_event(), handler)
175 }
176
177 pub fn drag_delta(&self, handler: impl Fn(&Interactive, &VectorEventArgs) + 'static) -> RoutedEventHandlerToken {
179 self.add_handler(Self::drag_delta_event(), handler)
180 }
181
182 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 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}