1use super::raw::{
2 IRawInputEventArgs, RawMouseWheelEventArgs, RawPointerEventArgs, RawPointerEventType, RawPointerGestureEventArgs,
3};
4use super::{
5 CaptureSource, IInputDevice, IInputRoot, IMouseDevice, IPointer, IPointerDevice, InputElement, IntermediatePoints,
6 KeyModifiers, MouseButton, Pointer, PointerDeltaEventArgs, PointerEventArgs, PointerPointProperties,
7 PointerPressedEventArgs, PointerReleasedEventArgs, PointerType, PointerWheelEventArgs,
8};
9use crate::interactivity::RoutedEvent;
10use crate::{Point, Rect, Ref, Size, Thickness, Vector};
11use std::any::Any;
12use std::cell::{Cell, RefCell};
13use std::rc::Rc;
14
15crate::ferro_thread_local! {
16 static PRIMARY: RefCell<Option<Rc<MouseDevice>>> = const { RefCell::new(None) };
17}
18
19pub struct MouseDevice {
22 click_count: Cell<i32>,
23 last_click_rect: Cell<Rect>,
24 last_click_time: Cell<u64>,
25 pointer: Rc<Pointer>,
26 disposed: Cell<bool>,
27 last_mouse_down_button: Cell<MouseButton>,
28}
29
30impl MouseDevice {
31 pub fn new() -> Rc<MouseDevice> {
33 Self::with_pointer(Pointer::new(Pointer::get_next_free_id(), PointerType::Mouse, true))
34 }
35
36 pub fn with_pointer(pointer: Rc<Pointer>) -> Rc<MouseDevice> {
38 Rc::new(MouseDevice {
39 click_count: Cell::new(0),
40 last_click_rect: Cell::new(Rect::default()),
41 last_click_time: Cell::new(0),
42 pointer,
43 disposed: Cell::new(false),
44 last_mouse_down_button: Cell::new(MouseButton::None),
45 })
46 }
47
48 pub fn primary() -> Rc<MouseDevice> {
51 if let Some(primary) = PRIMARY.with(|primary| primary.borrow().clone()) {
52 return primary;
53 }
54 let primary = MouseDevice::new();
55 PRIMARY.with(|slot| *slot.borrow_mut() = Some(primary.clone()));
56 primary
57 }
58
59 pub fn reset_primary_for_unit_tests() {
62 let old = PRIMARY.with(|primary| primary.take());
63 drop(old);
64 }
65
66 pub fn pointer(&self) -> &Rc<Pointer> {
68 &self.pointer
69 }
70
71 fn button_count(props: &PointerPointProperties) -> i32 {
72 let mut rv = 0;
73 if props.is_left_button_pressed {
74 rv += 1;
75 }
76 if props.is_middle_button_pressed {
77 rv += 1;
78 }
79 if props.is_right_button_pressed {
80 rv += 1;
81 }
82 if props.is_x_button_1_pressed {
83 rv += 1;
84 }
85 if props.is_x_button_2_pressed {
86 rv += 1;
87 }
88 rv
89 }
90
91 fn process_raw_pointer_event(&self, raw: &dyn IRawInputEventArgs, e: &RawPointerEventArgs) {
92 if let Some(mouse) = e.device().as_any().downcast_ref::<MouseDevice>() {
93 if mouse.disposed.get() {
94 return;
95 }
96 }
97
98 let props = Self::create_properties(e);
99 let key_modifiers = e.input_modifiers().to_key_modifiers();
100 let root = e.root();
101 let hit_test = e.input_hit_test_result().1;
102
103 match e.type_() {
104 RawPointerEventType::LeaveWindow | RawPointerEventType::NonClientLeftButtonDown => self.leave_window(),
105 RawPointerEventType::LeftButtonDown
106 | RawPointerEventType::RightButtonDown
107 | RawPointerEventType::MiddleButtonDown
108 | RawPointerEventType::XButton1Down
109 | RawPointerEventType::XButton2Down => {
110 let handled = if Self::button_count(&props) > 1 {
111 self.mouse_move(
112 e.timestamp(),
113 root,
114 e.position(),
115 props,
116 key_modifiers,
117 e.intermediate_points(),
118 hit_test,
119 )
120 } else {
121 self.mouse_down(
122 e.timestamp(),
123 root,
124 e.position(),
125 props,
126 key_modifiers,
127 e.platform_input_event_cookie(),
128 )
129 };
130 e.set_handled(handled);
131 }
132 RawPointerEventType::LeftButtonUp
133 | RawPointerEventType::RightButtonUp
134 | RawPointerEventType::MiddleButtonUp
135 | RawPointerEventType::XButton1Up
136 | RawPointerEventType::XButton2Up => {
137 let handled = if Self::button_count(&props) != 0 {
138 self.mouse_move(
139 e.timestamp(),
140 root,
141 e.position(),
142 props,
143 key_modifiers,
144 e.intermediate_points(),
145 hit_test,
146 )
147 } else {
148 self.mouse_up(e.timestamp(), root, e.position(), props, key_modifiers, hit_test)
149 };
150 e.set_handled(handled);
151 }
152 RawPointerEventType::Move => {
153 let handled = self.mouse_move(
154 e.timestamp(),
155 root,
156 e.position(),
157 props,
158 key_modifiers,
159 e.intermediate_points(),
160 hit_test,
161 );
162 e.set_handled(handled);
163 }
164 RawPointerEventType::Wheel => {
165 let delta = raw
166 .downcast_ref::<RawMouseWheelEventArgs>()
167 .expect("a raw wheel event must be a RawMouseWheelEventArgs")
168 .delta();
169 let handled = self.mouse_wheel(e.timestamp(), root, e.position(), props, delta, key_modifiers, hit_test);
170 e.set_handled(handled);
171 }
172 RawPointerEventType::Magnify | RawPointerEventType::Rotate | RawPointerEventType::Swipe => {
173 let delta = raw
174 .downcast_ref::<RawPointerGestureEventArgs>()
175 .expect("a raw gesture event must be a RawPointerGestureEventArgs")
176 .delta();
177 let routed_event = match e.type_() {
178 RawPointerEventType::Magnify => InputElement::pointer_touch_pad_gesture_magnify_event(),
179 RawPointerEventType::Rotate => InputElement::pointer_touch_pad_gesture_rotate_event(),
180 _ => InputElement::pointer_touch_pad_gesture_swipe_event(),
181 };
182 let handled =
183 self.gesture(routed_event, e.timestamp(), root, e.position(), props, delta, key_modifiers, hit_test);
184 e.set_handled(handled);
185 }
186 RawPointerEventType::CancelCapture => self.platform_capture_lost(),
187 RawPointerEventType::TouchBegin
188 | RawPointerEventType::TouchUpdate
189 | RawPointerEventType::TouchEnd
190 | RawPointerEventType::TouchCancel => {}
191 }
192 }
193
194 fn leave_window(&self) {}
195
196 fn create_properties(args: &RawPointerEventArgs) -> PointerPointProperties {
197 PointerPointProperties::new(args.input_modifiers(), args.type_().to_update_kind())
198 }
199
200 fn mouse_down(
201 &self,
202 timestamp: u64,
203 root: &Rc<dyn IInputRoot>,
204 p: Point,
205 properties: PointerPointProperties,
206 input_modifiers: KeyModifiers,
207 platform_input_event_cookie: Option<Rc<dyn Any>>,
208 ) -> bool {
209 let root_element = root.root_element();
210 let source = self.pointer.captured().or_else(|| root_element.input_hit_test(p));
211
212 let Some(source) = source else { return false };
213
214 self.pointer.capture_with_source(Some(&source), CaptureSource::Implicit);
215
216 if let Some(settings) = source.get_platform_settings() {
217 let double_click_time = settings.get_double_tap_time(PointerType::Mouse).as_secs_f64() * 1000.0;
218 let double_click_size = settings.get_double_tap_size(PointerType::Mouse);
219
220 if !self.last_click_rect.get().contains(p)
221 || timestamp.wrapping_sub(self.last_click_time.get()) as f64 > double_click_time
222 {
223 self.click_count.set(0);
224 }
225
226 self.click_count.set(self.click_count.get() + 1);
227 self.last_click_time.set(timestamp);
228 self.last_click_rect.set(
229 Rect::from_position_size(p, Size::default())
230 .inflate_thickness(Thickness::symmetric(double_click_size.width / 2.0, double_click_size.height / 2.0)),
231 );
232 }
233
234 self.last_mouse_down_button.set(properties.pointer_update_kind.get_mouse_button());
235
236 let e = PointerPressedEventArgs::new(
237 &source,
238 self.pointer.clone(),
239 &root_element,
240 p,
241 timestamp,
242 properties,
243 input_modifiers,
244 self.click_count.get(),
245 )
246 .with_platform_input_event_cookie(platform_input_event_cookie);
247
248 source.raise_event(&e);
249 e.handled()
250 }
251
252 #[allow(clippy::too_many_arguments)]
253 fn mouse_move(
254 &self,
255 timestamp: u64,
256 root: &Rc<dyn IInputRoot>,
257 p: Point,
258 properties: PointerPointProperties,
259 input_modifiers: KeyModifiers,
260 intermediate_points: Option<IntermediatePoints>,
261 hit_test: Option<Ref<InputElement>>,
262 ) -> bool {
263 let gesture_recognizer = self.pointer.captured_gesture_recognizer();
264 let source = gesture_recognizer
265 .as_ref()
266 .and_then(|recognizer| recognizer.target())
267 .or_else(|| self.pointer.captured())
268 .or(hit_test);
269 let Some(source) = source else { return false };
270 let root_element = root.root_element();
271
272 let e = PointerEventArgs::new(
273 Some(InputElement::pointer_moved_event()),
274 &source,
275 self.pointer.clone(),
276 Some(&root_element),
277 p,
278 timestamp,
279 properties,
280 input_modifiers,
281 )
282 .with_previous_points(intermediate_points);
283
284 match &gesture_recognizer {
285 Some(gesture_recognizer) => gesture_recognizer.pointer_moved_internal(&e),
286 None => source.raise_event(&e),
287 }
288
289 e.handled()
290 }
291
292 fn mouse_up(
293 &self,
294 timestamp: u64,
295 root: &Rc<dyn IInputRoot>,
296 p: Point,
297 props: PointerPointProperties,
298 input_modifiers: KeyModifiers,
299 hit_test: Option<Ref<InputElement>>,
300 ) -> bool {
301 let gesture_recognizer = self.pointer.captured_gesture_recognizer();
302 let source = gesture_recognizer
303 .as_ref()
304 .and_then(|recognizer| recognizer.target())
305 .or_else(|| self.pointer.captured())
306 .or(hit_test);
307 let Some(source) = source else { return false };
308 let root_element = root.root_element();
309
310 let e = PointerReleasedEventArgs::new(
311 &source,
312 self.pointer.clone(),
313 &root_element,
314 p,
315 timestamp,
316 props,
317 input_modifiers,
318 self.last_mouse_down_button.get(),
319 );
320
321 struct Finally<'a>(&'a MouseDevice);
323
324 impl Drop for Finally<'_> {
325 fn drop(&mut self) {
326 self.0.pointer.capture_lost(CaptureSource::Implicit);
327 self.0.last_mouse_down_button.set(MouseButton::None);
328 }
329 }
330
331 {
332 let _finally = Finally(self);
333 match &gesture_recognizer {
334 Some(gesture_recognizer) => gesture_recognizer.pointer_released_internal(&e),
335 None => source.raise_event(&e),
336 }
337 }
338
339 e.handled()
340 }
341
342 #[allow(clippy::too_many_arguments)]
343 fn mouse_wheel(
344 &self,
345 timestamp: u64,
346 root: &Rc<dyn IInputRoot>,
347 p: Point,
348 props: PointerPointProperties,
349 delta: Vector,
350 input_modifiers: KeyModifiers,
351 hit_test: Option<Ref<InputElement>>,
352 ) -> bool {
353 let Some(source) = self.pointer.captured().or(hit_test) else { return false };
354 let root_element = root.root_element();
355
356 let e = PointerWheelEventArgs::new(
357 &source,
358 self.pointer.clone(),
359 &root_element,
360 p,
361 timestamp,
362 props,
363 input_modifiers,
364 delta,
365 );
366
367 source.raise_event(&e);
368 e.handled()
369 }
370
371 #[allow(clippy::too_many_arguments)]
373 fn gesture(
374 &self,
375 routed_event: &RoutedEvent<PointerDeltaEventArgs>,
376 timestamp: u64,
377 root: &Rc<dyn IInputRoot>,
378 p: Point,
379 props: PointerPointProperties,
380 delta: Vector,
381 input_modifiers: KeyModifiers,
382 hit_test: Option<Ref<InputElement>>,
383 ) -> bool {
384 let Some(source) = self.pointer.captured().or(hit_test) else { return false };
385 let root_element = root.root_element();
386
387 let e = PointerDeltaEventArgs::new(
388 Some(routed_event),
389 &source,
390 self.pointer.clone(),
391 &root_element,
392 p,
393 timestamp,
394 props,
395 input_modifiers,
396 delta,
397 );
398
399 source.raise_event(&e);
400 e.handled()
401 }
402
403 pub fn dispose(&self) {
405 self.disposed.set(true);
406 self.pointer.dispose();
407 }
408
409 pub fn platform_capture_lost(&self) {
412 self.pointer.platform_capture_lost();
413 }
414}
415
416impl IInputDevice for MouseDevice {
417 fn process_raw_event(&self, e: &dyn IRawInputEventArgs) {
418 if !e.handled() {
419 if let Some(margs) = e.downcast_ref::<RawPointerEventArgs>() {
420 self.process_raw_pointer_event(e, margs);
421 }
422 }
423 }
424
425 fn as_any(&self) -> &dyn Any {
426 self
427 }
428
429 fn as_pointer_device(&self) -> Option<&dyn IPointerDevice> {
430 Some(self)
431 }
432}
433
434impl IPointerDevice for MouseDevice {
435 fn try_get_pointer(&self, _ev: &RawPointerEventArgs) -> Option<Rc<dyn IPointer>> {
436 Some(self.pointer.clone())
437 }
438}
439
440impl IMouseDevice for MouseDevice {}