1use std::{collections::HashMap, mem, num::NonZeroU32, time::*};
6
7use zng_app::{
8 APP, AppExtension, DInstant, INSTANT,
9 event::{EventPropagationHandle, event, event_args},
10 shortcut::ModifiersState,
11 timer::{DeadlineVar, TIMERS},
12 update::EventUpdate,
13 view_process::{
14 VIEW_PROCESS_INITED_EVENT,
15 raw_device_events::InputDeviceId,
16 raw_events::{
17 RAW_FRAME_RENDERED_EVENT, RAW_MOUSE_INPUT_EVENT, RAW_MOUSE_LEFT_EVENT, RAW_MOUSE_MOVED_EVENT, RAW_MOUSE_WHEEL_EVENT,
18 RAW_MULTI_CLICK_CONFIG_CHANGED_EVENT, RAW_WINDOW_FOCUS_EVENT,
19 },
20 },
21 widget::{
22 WIDGET, WidgetId,
23 info::{HitTestInfo, InteractionPath, WIDGET_INFO_CHANGED_EVENT, WidgetInfo, WidgetInfoBuilder},
24 },
25 window::WindowId,
26};
27use zng_app_context::app_local;
28use zng_ext_window::{NestedWindowWidgetInfoExt, WINDOWS};
29use zng_layout::unit::{Dip, DipPoint, DipToPx, Factor, PxPoint, PxToDip};
30use zng_state_map::{StateId, state_map, static_id};
31use zng_var::{IntoVar, Var, context_var, impl_from_and_into_var, var};
32use zng_view_api::touch::TouchPhase;
33pub use zng_view_api::{
34 config::MultiClickConfig,
35 mouse::{ButtonState, MouseButton, MouseScrollDelta},
36};
37
38use crate::{
39 keyboard::{KEYBOARD, MODIFIERS_CHANGED_EVENT},
40 pointer_capture::{CaptureInfo, CaptureMode, POINTER_CAPTURE, POINTER_CAPTURE_EVENT},
41};
42
43event_args! {
44 pub struct MouseMoveArgs {
46 pub window_id: WindowId,
48
49 pub device_id: InputDeviceId,
51
52 pub modifiers: ModifiersState,
54
55 pub coalesced_pos: Vec<DipPoint>,
59
60 pub position: DipPoint,
62
63 pub hits: HitTestInfo,
65
66 pub target: InteractionPath,
68
69 pub capture: Option<CaptureInfo>,
71
72 ..
73
74 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
79 list.insert_wgt(&self.target);
80 if let Some(c) = &self.capture {
81 list.insert_wgt(&c.target);
82 }
83 }
84 }
85
86 pub struct MouseInputArgs {
88 pub window_id: WindowId,
90
91 pub device_id: Option<InputDeviceId>,
93
94 pub button: MouseButton,
96
97 pub position: DipPoint,
99
100 pub modifiers: ModifiersState,
102
103 pub state: ButtonState,
107
108 pub hits: HitTestInfo,
110
111 pub target: InteractionPath,
115
116 pub capture: Option<CaptureInfo>,
118
119 pub is_click: bool,
123
124 ..
125
126 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
131 list.insert_wgt(&self.target);
132 if let Some(c) = &self.capture {
133 list.insert_wgt(&c.target);
134 }
135 }
136 }
137
138 pub struct MouseClickArgs {
140 pub window_id: WindowId,
142
143 pub device_id: InputDeviceId,
145
146 pub button: MouseButton,
148
149 pub position: DipPoint,
151
152 pub modifiers: ModifiersState,
154
155 pub click_count: NonZeroU32,
159
160 pub is_repeat: bool,
162
163 pub hits: HitTestInfo,
166
167 pub target: InteractionPath,
183
184 ..
185
186 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
190 list.insert_wgt(&self.target)
191 }
192 }
193
194 pub struct MouseHoverArgs {
196 pub window_id: WindowId,
198
199 pub device_id: Option<InputDeviceId>,
201
202 pub position: DipPoint,
204
205 pub hits: HitTestInfo,
207
208 pub prev_target: Option<InteractionPath>,
210
211 pub target: Option<InteractionPath>,
218
219 pub prev_capture: Option<CaptureInfo>,
221
222 pub capture: Option<CaptureInfo>,
224
225 ..
226
227 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
233 if let Some(p) = &self.prev_target {
234 list.insert_wgt(p);
235 }
236 if let Some(p) = &self.target {
237 list.insert_wgt(p);
238 }
239 if let Some(c) = &self.capture {
240 list.insert_wgt(&c.target);
241 }
242 }
243 }
244
245 pub struct MouseWheelArgs {
247 pub window_id: WindowId,
249 pub device_id: InputDeviceId,
251
252 pub position: DipPoint,
254 pub modifiers: ModifiersState,
256
257 pub delta: MouseScrollDelta,
259
260 pub phase: TouchPhase,
262
263 pub hits: HitTestInfo,
266
267 pub target: InteractionPath,
269
270 ..
271
272 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
276 list.insert_wgt(&self.target)
277 }
278 }
279}
280
281impl MouseHoverArgs {
282 pub fn capture_allows(&self) -> bool {
288 self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
289 }
290
291 pub fn is_mouse_move(&self) -> bool {
293 self.device_id.is_some()
294 }
295
296 pub fn is_widget_move(&self) -> bool {
298 self.device_id.is_none()
299 }
300
301 pub fn is_capture_change(&self) -> bool {
303 self.prev_capture != self.capture
304 }
305
306 pub fn is_mouse_enter(&self) -> bool {
310 !self.was_over() && self.is_over()
311 }
312
313 pub fn is_mouse_leave(&self) -> bool {
317 self.was_over() && !self.is_over()
318 }
319
320 pub fn is_mouse_enter_enabled(&self) -> bool {
324 (!self.was_over() || self.was_disabled(WIDGET.id())) && self.is_over() && self.is_enabled(WIDGET.id())
325 }
326
327 pub fn is_mouse_leave_enabled(&self) -> bool {
331 self.was_over() && self.was_enabled(WIDGET.id()) && (!self.is_over() || self.is_disabled(WIDGET.id()))
332 }
333
334 pub fn is_mouse_enter_disabled(&self) -> bool {
338 (!self.was_over() || self.was_enabled(WIDGET.id())) && self.is_over() && self.is_disabled(WIDGET.id())
339 }
340
341 pub fn is_mouse_leave_disabled(&self) -> bool {
345 self.was_over() && self.was_disabled(WIDGET.id()) && (!self.is_over() || self.is_enabled(WIDGET.id()))
346 }
347
348 pub fn was_over(&self) -> bool {
354 if let Some(cap) = &self.prev_capture
355 && !cap.allows()
356 {
357 return false;
358 }
359
360 if let Some(t) = &self.prev_target {
361 return t.contains(WIDGET.id());
362 }
363
364 false
365 }
366
367 pub fn is_over(&self) -> bool {
373 if let Some(cap) = &self.capture
374 && !cap.allows()
375 {
376 return false;
377 }
378
379 if let Some(t) = &self.target {
380 return t.contains(WIDGET.id());
381 }
382
383 false
384 }
385
386 pub fn was_enabled(&self, widget_id: WidgetId) -> bool {
390 match &self.prev_target {
391 Some(t) => t.contains_enabled(widget_id),
392 None => false,
393 }
394 }
395
396 pub fn was_disabled(&self, widget_id: WidgetId) -> bool {
400 match &self.prev_target {
401 Some(t) => t.contains_disabled(widget_id),
402 None => false,
403 }
404 }
405
406 pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
410 match &self.target {
411 Some(t) => t.contains_enabled(widget_id),
412 None => false,
413 }
414 }
415
416 pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
420 match &self.target {
421 Some(t) => t.contains_disabled(widget_id),
422 None => false,
423 }
424 }
425
426 pub fn position_wgt(&self) -> Option<PxPoint> {
428 WIDGET.win_point_to_wgt(self.position)
429 }
430}
431
432impl MouseMoveArgs {
433 pub fn capture_allows(&self) -> bool {
439 self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
440 }
441
442 pub fn position_wgt(&self) -> Option<PxPoint> {
444 WIDGET.win_point_to_wgt(self.position)
445 }
446}
447
448impl MouseInputArgs {
449 pub fn capture_allows(&self) -> bool {
455 self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
456 }
457
458 pub fn is_over(&self, widget_id: WidgetId) -> bool {
462 self.target.contains(widget_id)
463 }
464
465 pub fn is_primary(&self) -> bool {
469 self.button == MouseButton::Left
470 }
471
472 pub fn is_context(&self) -> bool {
474 self.button == MouseButton::Right
475 }
476
477 pub fn is_mouse_down(&self) -> bool {
481 self.state == ButtonState::Pressed
482 }
483
484 pub fn is_mouse_up(&self) -> bool {
488 self.state == ButtonState::Released
489 }
490
491 pub fn position_wgt(&self) -> Option<PxPoint> {
493 WIDGET.win_point_to_wgt(self.position)
494 }
495}
496
497impl MouseClickArgs {
498 pub fn is_primary(&self) -> bool {
500 self.button == MouseButton::Left
501 }
502
503 pub fn is_context(&self) -> bool {
505 self.button == MouseButton::Right
506 }
507
508 pub fn is_single(&self) -> bool {
510 self.click_count.get() == 1
511 }
512
513 pub fn is_double(&self) -> bool {
515 self.click_count.get() == 2
516 }
517
518 pub fn is_triple(&self) -> bool {
520 self.click_count.get() == 3
521 }
522
523 pub fn position_wgt(&self) -> Option<PxPoint> {
525 WIDGET.win_point_to_wgt(self.position)
526 }
527}
528
529impl MouseWheelArgs {
530 pub fn shifted_delta(&self) -> MouseScrollDelta {
534 if self.modifiers.has_shift() {
535 match self.delta {
536 MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(y, x),
537 MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(y, x),
538 _ => unimplemented!(),
539 }
540 } else {
541 self.delta
542 }
543 }
544
545 pub fn is_scroll(&self) -> bool {
554 if CTRL_SCROLL_VAR.get() {
555 self.modifiers
556 .is_only(ModifiersState::CTRL | ModifiersState::SHIFT | ModifiersState::ALT)
557 } else {
558 self.modifiers.is_empty() || self.modifiers.is_only(ModifiersState::SHIFT | ModifiersState::ALT)
559 }
560 }
561
562 pub fn scroll_delta(&self, alt_factor: impl Into<Factor>) -> Option<MouseScrollDelta> {
570 let mut modifiers = self.modifiers;
571 if CTRL_SCROLL_VAR.get() && !modifiers.take_ctrl() {
572 return None;
573 }
574 let mut delta = self.delta;
575 if modifiers.take_alt() {
576 let alt_factor = alt_factor.into();
577 delta = match delta {
578 MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(x * alt_factor.0, y * alt_factor.0),
579 MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(x * alt_factor.0, y * alt_factor.0),
580 _ => return None,
581 };
582 }
583
584 if modifiers.is_empty() {
585 Some(delta)
586 } else if modifiers.is_only_shift() {
587 Some(match delta {
588 MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(y, x),
589 MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(y, x),
590 _ => return None,
591 })
592 } else {
593 None
594 }
595 }
596
597 pub fn is_zoom(&self) -> bool {
605 if CTRL_SCROLL_VAR.get() {
606 self.modifiers.is_empty()
607 } else {
608 self.modifiers.is_only_ctrl()
609 }
610 }
611
612 pub fn zoom_delta(&self) -> Option<MouseScrollDelta> {
619 if self.is_zoom() { Some(self.delta) } else { None }
620 }
621
622 pub fn position_wgt(&self) -> Option<PxPoint> {
624 WIDGET.win_point_to_wgt(self.position)
625 }
626}
627
628context_var! {
629 pub static CTRL_SCROLL_VAR: bool = false;
636}
637
638event! {
639 pub static MOUSE_MOVE_EVENT: MouseMoveArgs;
641
642 pub static MOUSE_INPUT_EVENT: MouseInputArgs;
644
645 pub static MOUSE_CLICK_EVENT: MouseClickArgs;
647
648 pub static MOUSE_HOVERED_EVENT: MouseHoverArgs;
650
651 pub static MOUSE_WHEEL_EVENT: MouseWheelArgs;
653}
654
655struct ClickingInfo {
656 path: InteractionPath,
657 press_stop_handle: EventPropagationHandle,
658
659 pressed: bool,
660 last_pos: DipPoint,
661 last_click: DInstant,
662 click_count: u32,
663
664 repeat_timer: Option<DeadlineVar>,
665 repeat_count: u32,
666}
667
668pub struct MouseManager {
685 pos: DipPoint,
687 pos_window: Option<WindowId>,
689 pos_device: Option<InputDeviceId>,
690 hits: Option<HitTestInfo>,
692
693 modifiers: ModifiersState,
695
696 hovered: Option<InteractionPath>,
697 clicking: HashMap<MouseButton, ClickingInfo>,
698}
699impl Default for MouseManager {
700 fn default() -> Self {
701 MouseManager {
702 pos: DipPoint::zero(),
703 pos_window: None,
704 pos_device: None,
705 hits: None,
706
707 modifiers: ModifiersState::default(),
708
709 hovered: None,
710 clicking: HashMap::default(),
711 }
712 }
713}
714impl MouseManager {
715 fn on_mouse_input(&mut self, mut window_id: WindowId, device_id: InputDeviceId, state: ButtonState, button: MouseButton) {
716 let mouse = MOUSE_SV.read();
717
718 let mut position = if self.pos_window == Some(window_id) {
719 self.pos
720 } else {
721 DipPoint::default()
722 };
723
724 let hits = self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
725
726 let wgt_tree = match WINDOWS.widget_tree(hits.window_id()) {
727 Ok(t) => t,
728 Err(e) => {
729 tracing::error!("cannot find clicked window, {e:?}");
730 return;
731 }
732 };
733
734 if hits.window_id() != window_id {
735 window_id = hits.window_id();
737 position = hits.point().to_dip(wgt_tree.scale_factor());
738 }
739
740 let (wgt_path, click_mode) = hits
741 .target()
742 .and_then(|t| wgt_tree.get(t.widget_id).map(|w| (w.interaction_path(), w.click_mode())))
743 .unwrap_or_else(|| (wgt_tree.root().interaction_path(), wgt_tree.root().click_mode()));
744
745 let wgt_path = match wgt_path.unblocked() {
746 Some(p) => p,
747 None => return, };
749
750 match state {
751 ButtonState::Pressed => {
752 if !mouse.buttons.with(|b| b.contains(&button)) {
753 mouse.buttons.modify(move |btns| btns.push(button));
754 }
755 }
756 ButtonState::Released => {
757 if mouse.buttons.with(|b| b.contains(&button)) {
758 mouse.buttons.modify(move |btns| {
759 if let Some(i) = btns.iter().position(|k| *k == button) {
760 btns.swap_remove(i);
761 }
762 });
763 }
764 }
765 }
766
767 let stop_handle = EventPropagationHandle::new();
768
769 let entry = self.clicking.entry(button).or_insert_with(|| ClickingInfo {
770 path: wgt_path.clone(),
771 press_stop_handle: stop_handle.clone(),
772 last_click: DInstant::EPOCH,
773 last_pos: position,
774 pressed: false,
775 click_count: 0,
776 repeat_timer: None,
777 repeat_count: 0,
778 });
779
780 if entry.path != wgt_path {
781 let actual_change = entry.path.as_path() != wgt_path.as_path();
782 entry.path = wgt_path.clone();
785 if actual_change {
786 entry.press_stop_handle = stop_handle.clone();
787 entry.pressed = false;
788 entry.click_count = 0;
789 entry.repeat_timer = None;
790 entry.repeat_count = 0;
791 }
792 }
793
794 let multi_click_cfg = mouse.multi_click_config.get();
795
796 let double_allowed = entry.last_click.elapsed() <= multi_click_cfg.time && {
797 let dist = (entry.last_pos.to_vector() - position.to_vector()).abs();
798 let area = multi_click_cfg.area;
799 dist.x <= area.width && dist.y <= area.height
800 };
801
802 let click_gesture = if entry.click_count == 0 || !double_allowed {
803 entry.click_count = 0;
804 click_mode.single
805 } else {
806 click_mode.double
807 };
808
809 let click = match state {
810 ButtonState::Pressed => {
811 entry.pressed = true;
812 entry.press_stop_handle = stop_handle.clone();
813 matches!(click_gesture, ClickTrigger::Press)
814 }
815 ButtonState::Released => {
816 entry.repeat_count = 0;
817 entry.repeat_timer = None;
818 if mem::take(&mut entry.pressed) && !entry.press_stop_handle.is_stopped() {
819 matches!(click_gesture, ClickTrigger::PressRelease | ClickTrigger::Release)
820 } else {
821 matches!(click_gesture, ClickTrigger::Release)
822 }
823 }
824 };
825
826 if click_mode.repeat {
827 if click {
828 let t = mouse.repeat_config.get().start_delay;
829 entry.repeat_timer = Some(TIMERS.deadline(t));
830 entry.repeat_count = 0;
831 }
832 } else {
833 entry.repeat_timer = None;
834 entry.repeat_count = 0;
835 }
836
837 let capture_info = POINTER_CAPTURE.current_capture_value();
838
839 let now = INSTANT.now();
840 let args = MouseInputArgs::new(
841 now,
842 stop_handle.clone(),
843 window_id,
844 device_id,
845 button,
846 position,
847 self.modifiers,
848 state,
849 hits.clone(),
850 wgt_path.clone(),
851 capture_info,
852 click,
853 );
854
855 MOUSE_INPUT_EVENT.notify(args);
857
858 if click {
859 if double_allowed {
860 entry.click_count += 1;
861 } else {
862 entry.click_count = 1;
863 }
864
865 entry.last_click = now;
866 entry.last_pos = position;
867
868 let args = MouseClickArgs::new(
869 now,
870 stop_handle,
871 window_id,
872 device_id,
873 button,
874 position,
875 self.modifiers,
876 NonZeroU32::new(entry.click_count).unwrap(),
877 false,
878 hits,
879 wgt_path,
880 );
881
882 MOUSE_CLICK_EVENT.notify(args);
884 }
885 }
886
887 fn on_cursor_moved(&mut self, window_id: WindowId, device_id: InputDeviceId, coalesced_pos: Vec<DipPoint>, mut position: DipPoint) {
888 let mut moved = Some(window_id) != self.pos_window || Some(device_id) != self.pos_device;
889
890 if moved {
891 self.pos_window = Some(window_id);
893 self.pos_device = Some(device_id);
894 }
895
896 moved |= position != self.pos;
897
898 if moved {
899 self.pos = position;
902
903 let mut frame_info = match WINDOWS.widget_tree(window_id) {
905 Ok(f) => f,
906 Err(_) => {
907 if let Some(hovered) = self.hovered.take() {
909 let capture = POINTER_CAPTURE.current_capture_value();
910 let args = MouseHoverArgs::now(
911 window_id,
912 device_id,
913 position,
914 HitTestInfo::no_hits(window_id),
915 Some(hovered),
916 None,
917 capture.clone(),
918 capture,
919 );
920 MOUSE_HOVERED_EVENT.notify(args);
921 }
922 return;
923 }
924 };
925
926 let mut pos_hits = frame_info.root().hit_test(position.to_px(frame_info.scale_factor()));
927
928 let target = if let Some(t) = pos_hits.target() {
929 if let Some(w) = frame_info.get(t.widget_id) {
930 if let Some(f) = w.nested_window_tree() {
931 frame_info = f;
933 let factor = frame_info.scale_factor();
934 let pos = position.to_px(factor);
935 let pos = w.inner_transform().inverse().and_then(|t| t.transform_point(pos)).unwrap_or(pos);
936 pos_hits = frame_info.root().hit_test(pos);
937 position = pos.to_dip(factor);
938 pos_hits
939 .target()
940 .and_then(|h| frame_info.get(h.widget_id))
941 .map(|w| w.interaction_path())
942 .unwrap_or_else(|| frame_info.root().interaction_path())
943 } else {
944 w.interaction_path()
945 }
946 } else {
947 tracing::error!("hits target `{}` not found", t.widget_id);
948 frame_info.root().interaction_path()
949 }
950 } else {
951 frame_info.root().interaction_path()
952 }
953 .unblocked();
954
955 MOUSE_SV.read().position.set(Some(MousePosition {
956 window_id: frame_info.window_id(),
957 position,
958 timestamp: INSTANT.now(),
959 }));
960
961 self.hits = Some(pos_hits.clone());
962
963 let capture = POINTER_CAPTURE.current_capture_value();
964
965 let hovered_args = if self.hovered != target {
967 MOUSE_SV.read().hovered.set(target.clone());
968 let prev_target = mem::replace(&mut self.hovered, target.clone());
969 let args = MouseHoverArgs::now(
970 frame_info.window_id(),
971 device_id,
972 position,
973 pos_hits.clone(),
974 prev_target,
975 target.clone(),
976 capture.clone(),
977 capture.clone(),
978 );
979 Some(args)
980 } else {
981 None
982 };
983
984 if let Some(target) = target {
986 let args = MouseMoveArgs::now(
987 frame_info.window_id(),
988 device_id,
989 self.modifiers,
990 coalesced_pos,
991 position,
992 pos_hits,
993 target,
994 capture,
995 );
996 MOUSE_MOVE_EVENT.notify(args);
997 }
998
999 if let Some(args) = hovered_args {
1000 MOUSE_HOVERED_EVENT.notify(args);
1001 }
1002 } else if coalesced_pos.is_empty() {
1003 tracing::debug!("RawCursorMoved did not actually move")
1004 }
1005 }
1006
1007 fn on_scroll(&self, window_id: WindowId, device_id: InputDeviceId, delta: MouseScrollDelta, phase: TouchPhase) {
1008 let position = if self.pos_window == Some(window_id) {
1009 self.pos
1010 } else {
1011 DipPoint::default()
1012 };
1013
1014 let hits = self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
1015
1016 let frame_info = WINDOWS.widget_tree(hits.window_id()).unwrap();
1017
1018 let target = hits
1019 .target()
1020 .and_then(|t| frame_info.get(t.widget_id).map(|w| w.interaction_path()))
1021 .unwrap_or_else(|| frame_info.root().interaction_path());
1022
1023 if let Some(target) = target.unblocked() {
1024 let args = MouseWheelArgs::now(hits.window_id(), device_id, position, self.modifiers, delta, phase, hits, target);
1025 MOUSE_WHEEL_EVENT.notify(args);
1026 }
1027 }
1028
1029 fn on_cursor_left_window(&mut self, window_id: WindowId, device_id: InputDeviceId) {
1030 if Some(window_id) == self.pos_window.take() {
1031 MOUSE_SV.read().position.set(None);
1032 if let Some(path) = self.hovered.take() {
1033 MOUSE_SV.read().hovered.set(None);
1034 let capture = POINTER_CAPTURE.current_capture_value();
1035 let args = MouseHoverArgs::now(
1036 window_id,
1037 device_id,
1038 self.pos,
1039 HitTestInfo::no_hits(window_id),
1040 Some(path),
1041 None,
1042 capture.clone(),
1043 capture,
1044 );
1045 MOUSE_HOVERED_EVENT.notify(args);
1046 }
1047 }
1048 }
1049
1050 fn on_window_blur(&mut self, prev_window: WindowId, new_window: Option<WindowId>) {
1051 if new_window.is_some() {
1052 if let Some(p) = self.pos_window {
1053 if p == prev_window && (new_window.is_none() || new_window != self.hits.as_ref().map(|h| h.window_id())) {
1055 self.clean_all_state();
1056 }
1057 }
1058 } else {
1059 self.clean_all_state();
1060 }
1061 }
1062
1063 fn continue_hovered(&mut self, mut window_id: WindowId) {
1065 if self.pos_window == Some(window_id) {
1066 let mut frame_info = match WINDOWS.widget_tree(window_id) {
1068 Ok(f) => f,
1069 Err(_) => {
1070 self.clean_all_state();
1071 return;
1072 }
1073 };
1074 let mut pos_hits = frame_info.root().hit_test(self.pos.to_px(frame_info.scale_factor()));
1075 let mut position = self.pos;
1076 let target = if let Some(t) = pos_hits.target() {
1077 if let Some(w) = frame_info.get(t.widget_id) {
1078 if let Some(f) = w.nested_window_tree() {
1079 frame_info = f;
1080 let factor = frame_info.scale_factor();
1081 let pos = self.pos.to_px(factor);
1082 let pos = w.inner_transform().inverse().and_then(|t| t.transform_point(pos)).unwrap_or(pos);
1083 pos_hits = frame_info.root().hit_test(pos);
1084 window_id = frame_info.window_id();
1085 position = pos.to_dip(factor);
1086 pos_hits
1087 .target()
1088 .and_then(|h| frame_info.get(h.widget_id))
1089 .map(|w| w.interaction_path())
1090 .unwrap_or_else(|| frame_info.root().interaction_path())
1091 } else {
1092 w.interaction_path()
1093 }
1094 } else {
1095 tracing::error!("hits target `{}` not found", t.widget_id);
1096 frame_info.root().interaction_path()
1097 }
1098 } else {
1099 frame_info.root().interaction_path()
1100 }
1101 .unblocked();
1102 self.hits = Some(pos_hits.clone());
1103
1104 if self.hovered != target {
1105 let capture = POINTER_CAPTURE.current_capture_value();
1106 let prev = mem::replace(&mut self.hovered, target.clone());
1107 let args = MouseHoverArgs::now(window_id, None, position, pos_hits, prev, target, capture.clone(), capture);
1108 MOUSE_HOVERED_EVENT.notify(args);
1109 }
1110 }
1111 }
1112
1113 fn clean_all_state(&mut self) {
1114 let mouse = MOUSE_SV.read();
1115 if self.pos_window.take().is_some()
1116 && let Some(path) = self.hovered.take()
1117 {
1118 let window_id = path.window_id();
1119 mouse.buttons.with(|b| {
1120 for btn in b {
1121 let args = MouseInputArgs::now(
1122 window_id,
1123 None,
1124 *btn,
1125 DipPoint::new(Dip::new(-1), Dip::new(-1)),
1126 ModifiersState::empty(),
1127 ButtonState::Released,
1128 HitTestInfo::no_hits(window_id),
1129 path.clone(),
1130 None,
1131 false,
1132 );
1133 MOUSE_INPUT_EVENT.notify(args);
1134 }
1135 });
1136
1137 let args = MouseHoverArgs::now(
1138 window_id,
1139 None,
1140 DipPoint::new(Dip::new(-1), Dip::new(-1)),
1141 HitTestInfo::no_hits(window_id),
1142 Some(path),
1143 None,
1144 None,
1145 None,
1146 );
1147 MOUSE_HOVERED_EVENT.notify(args);
1148 }
1149 mouse.buttons.set(vec![]);
1150 self.clicking.clear();
1151 self.pos_device = None;
1152 self.pos_window = None;
1153 self.hits = None;
1154 mouse.position.set(None);
1155 mouse.hovered.set(None);
1156 }
1157}
1158impl AppExtension for MouseManager {
1159 fn event_preview(&mut self, update: &mut EventUpdate) {
1160 if let Some(args) = RAW_FRAME_RENDERED_EVENT.on(update) {
1161 self.continue_hovered(args.window_id);
1162 } else if let Some(args) = RAW_MOUSE_MOVED_EVENT.on(update) {
1163 self.on_cursor_moved(args.window_id, args.device_id, args.coalesced_pos.clone(), args.position);
1164 } else if let Some(args) = RAW_MOUSE_WHEEL_EVENT.on(update) {
1165 self.on_scroll(args.window_id, args.device_id, args.delta, args.phase);
1166 } else if let Some(args) = RAW_MOUSE_INPUT_EVENT.on(update) {
1167 self.on_mouse_input(args.window_id, args.device_id, args.state, args.button);
1168 } else if let Some(args) = MODIFIERS_CHANGED_EVENT.on(update) {
1169 self.modifiers = args.modifiers;
1170 } else if let Some(args) = RAW_MOUSE_LEFT_EVENT.on(update) {
1171 self.on_cursor_left_window(args.window_id, args.device_id);
1172 } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
1173 if let Some(window_id) = args.prev_focus {
1174 self.on_window_blur(window_id, args.new_focus);
1175 }
1176 } else if let Some(args) = WIDGET_INFO_CHANGED_EVENT.on(update) {
1177 self.continue_hovered(args.window_id);
1178 } else if let Some(args) = RAW_MULTI_CLICK_CONFIG_CHANGED_EVENT.on(update) {
1179 MOUSE_SV.read().multi_click_config.set(args.config);
1180 self.clicking.clear();
1181 } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
1182 && args.is_respawn
1183 {
1184 self.clean_all_state();
1185 }
1186 }
1187
1188 fn event(&mut self, update: &mut EventUpdate) {
1189 if let Some(args) = POINTER_CAPTURE_EVENT.on(update)
1190 && let Some(path) = &self.hovered
1191 && self.pos_window.is_some()
1192 {
1193 let window_id = path.window_id();
1194 let hover_args = MouseHoverArgs::now(
1195 window_id,
1196 self.pos_device.unwrap(),
1197 self.pos,
1198 self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id)),
1199 Some(path.clone()),
1200 Some(path.clone()),
1201 args.prev_capture.clone(),
1202 args.new_capture.clone(),
1203 );
1204 MOUSE_HOVERED_EVENT.notify(hover_args);
1205 }
1206 }
1207
1208 fn update_preview(&mut self) {
1209 for (btn, info) in self.clicking.iter_mut() {
1211 if let Some(timer) = info.repeat_timer.take() {
1212 if timer.with_new(|t| t.has_elapsed()).unwrap_or(false) {
1214 info.repeat_count = info.repeat_count.saturating_add(1);
1216
1217 if let Some(dv) = self.pos_device
1218 && let Ok(tree) = WINDOWS.widget_tree(info.path.window_id())
1219 {
1220 let hit_test = tree.root().hit_test(self.pos.to_px(tree.scale_factor()));
1223
1224 let mut target = None;
1226 if let Some(hit) = hit_test.target().map(|t| tree.get(t.widget_id).unwrap()) {
1227 target = hit.path().shared_ancestor(info.path.as_path()).map(|c| c.into_owned());
1228 }
1229 if let Some(c) = POINTER_CAPTURE.current_capture_value() {
1230 match c.mode {
1231 CaptureMode::Window => {
1232 if let Some(t) = &target {
1233 if t.window_id() != c.target.window_id() {
1234 target = None; }
1236 } else {
1237 target = Some(tree.root().path());
1239 }
1240 }
1241 CaptureMode::Subtree => {
1242 if let Some(t) = &target {
1243 target = c.target.shared_ancestor(t).map(|c| c.into_owned());
1244 } else {
1245 target = Some(c.target);
1246 }
1247 }
1248 CaptureMode::Widget => {
1249 target = Some(c.target);
1250 }
1251 }
1252 }
1253
1254 if let Some(target) = target {
1255 if let Some(target) = tree.get(target.widget_id()).and_then(|w| w.interaction_path().unblocked()) {
1257 let args = MouseClickArgs::now(
1261 target.window_id(),
1262 dv,
1263 *btn,
1264 self.pos,
1265 self.modifiers,
1266 NonZeroU32::new(info.repeat_count).unwrap(),
1267 true,
1268 hit_test,
1269 target,
1270 );
1271 MOUSE_CLICK_EVENT.notify(args);
1272
1273 let t = MOUSE.repeat_config().get().interval;
1275 info.repeat_timer = Some(TIMERS.deadline(t));
1276 }
1277 }
1278 }
1279 } else {
1280 info.repeat_timer = Some(timer);
1282 }
1283 }
1284 }
1285 }
1286}
1287
1288#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1290pub enum ClickTrigger {
1291 PressRelease,
1293 Press,
1295 Release,
1297}
1298
1299#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1301pub struct ClickMode {
1302 pub single: ClickTrigger,
1304
1305 pub double: ClickTrigger,
1307
1308 pub repeat: bool,
1310}
1311impl Default for ClickMode {
1312 fn default() -> Self {
1314 Self {
1315 single: ClickTrigger::PressRelease,
1316 double: ClickTrigger::Press,
1317 repeat: false,
1318 }
1319 }
1320}
1321impl IntoVar<Option<ClickMode>> for ClickTrigger {
1322 fn into_var(self) -> Var<Option<ClickMode>> {
1323 Some(ClickMode::from(self)).into_var()
1324 }
1325}
1326impl_from_and_into_var! {
1327 fn from(gesture: ClickTrigger) -> ClickMode {
1328 ClickMode {
1329 single: gesture,
1330 double: gesture,
1331 repeat: false,
1332 }
1333 }
1334
1335 fn from(some: ClickMode) -> Option<ClickMode>;
1336}
1337impl ClickMode {
1338 pub fn press() -> Self {
1340 Self {
1341 single: ClickTrigger::Press,
1342 double: ClickTrigger::Press,
1343 repeat: false,
1344 }
1345 }
1346
1347 pub fn release() -> Self {
1349 Self {
1350 single: ClickTrigger::Release,
1351 double: ClickTrigger::Release,
1352 repeat: false,
1353 }
1354 }
1355
1356 pub fn repeat() -> Self {
1358 Self {
1359 single: ClickTrigger::Press,
1360 double: ClickTrigger::Press,
1361 repeat: true,
1362 }
1363 }
1364
1365 pub fn mixed_repeat() -> Self {
1367 Self {
1368 single: ClickTrigger::PressRelease,
1369 double: ClickTrigger::Press,
1370 repeat: true,
1371 }
1372 }
1373}
1374
1375pub trait WidgetInfoMouseExt {
1377 fn click_mode(&self) -> ClickMode;
1379}
1380impl WidgetInfoMouseExt for WidgetInfo {
1381 fn click_mode(&self) -> ClickMode {
1382 for w in self.self_and_ancestors() {
1383 if let Some(m) = w.meta().get_clone(*CLICK_MODE_ID).flatten() {
1384 return m;
1385 }
1386 }
1387 ClickMode::default()
1388 }
1389}
1390
1391pub trait WidgetInfoBuilderMouseExt {
1393 fn set_click_mode(&mut self, mode: Option<ClickMode>);
1397}
1398impl WidgetInfoBuilderMouseExt for WidgetInfoBuilder {
1399 fn set_click_mode(&mut self, mode: Option<ClickMode>) {
1400 self.with_meta(|mut m| match m.entry(*CLICK_MODE_ID) {
1401 state_map::StateMapEntry::Occupied(mut e) => *e.get_mut() = mode,
1402 state_map::StateMapEntry::Vacant(e) => {
1403 if mode.is_some() {
1404 e.insert(mode);
1405 }
1406 }
1407 })
1408 }
1409}
1410
1411static_id! {
1412 static ref CLICK_MODE_ID: StateId<Option<ClickMode>>;
1413}
1414
1415#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1417pub struct ButtonRepeatConfig {
1418 pub start_delay: Duration,
1420 pub interval: Duration,
1422}
1423impl Default for ButtonRepeatConfig {
1424 fn default() -> Self {
1426 Self {
1427 start_delay: Duration::from_millis(600),
1428 interval: Duration::from_millis(100),
1429 }
1430 }
1431}
1432
1433pub struct MOUSE;
1445impl MOUSE {
1446 pub fn buttons(&self) -> Var<Vec<MouseButton>> {
1450 MOUSE_SV.read().buttons.read_only()
1451 }
1452
1453 pub fn multi_click_config(&self) -> Var<MultiClickConfig> {
1461 MOUSE_SV.read().multi_click_config.clone()
1462 }
1463
1464 pub fn sys_multi_click_config(&self) -> Var<MultiClickConfig> {
1473 MOUSE_SV.read().sys_multi_click_config.read_only()
1474 }
1475
1476 pub fn repeat_config(&self) -> Var<ButtonRepeatConfig> {
1483 MOUSE_SV.read().repeat_config.clone()
1484 }
1485
1486 pub fn position(&self) -> Var<Option<MousePosition>> {
1488 MOUSE_SV.read().position.read_only()
1489 }
1490
1491 pub fn hovered(&self) -> Var<Option<InteractionPath>> {
1493 MOUSE_SV.read().hovered.read_only()
1494 }
1495}
1496
1497#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1503pub struct MousePosition {
1504 pub window_id: WindowId,
1506 pub position: DipPoint,
1508 pub timestamp: DInstant,
1510}
1511
1512app_local! {
1513 static MOUSE_SV: MouseService = {
1514 APP.extensions().require::<MouseManager>();
1515 let sys_multi_click_config = var(MultiClickConfig::default());
1516 MouseService {
1517 multi_click_config: sys_multi_click_config.cow(),
1518 sys_multi_click_config,
1519 repeat_config: KEYBOARD
1520 .repeat_config()
1521 .map(|c| ButtonRepeatConfig {
1522 start_delay: c.start_delay,
1523 interval: c.interval,
1524 })
1525 .cow(),
1526 buttons: var(vec![]),
1527 hovered: var(None),
1528 position: var(None),
1529 }
1530 };
1531}
1532struct MouseService {
1533 multi_click_config: Var<MultiClickConfig>,
1534 sys_multi_click_config: Var<MultiClickConfig>,
1535 repeat_config: Var<ButtonRepeatConfig>,
1536 buttons: Var<Vec<MouseButton>>,
1537 hovered: Var<Option<InteractionPath>>,
1538 position: Var<Option<MousePosition>>,
1539}