1#[cfg(not(feature = "std"))]
4use alloc::string::{String, ToString};
5use alloc::{
6 boxed::Box,
7 collections::{btree_map::BTreeMap, btree_set::BTreeSet},
8 vec::Vec,
9};
10
11use azul_css::{
12 props::{
13 basic::{LayoutPoint, LayoutRect, LayoutSize},
14 property::CssProperty,
15 },
16 AzString, LayoutDebugMessage,
17};
18use rust_fontconfig::FcFontCache;
19
20use crate::{
21 callbacks::Update,
22 dom::{DomId, DomNodeId, On},
23 geom::{LogicalPosition, LogicalRect},
24 gl::OptionGlContextPtr,
25 gpu::GpuEventChanges,
26 hit_test::{FullHitTest, HitTestItem, ScrollPosition},
27 id::NodeId,
28 resources::{ImageCache, RendererResources},
29 styled_dom::{ChangedCssProperty, NodeHierarchyItemId},
30 task::Instant,
31 window::RawWindowHandle,
32 FastBTreeSet, FastHashMap,
33};
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum EasingFunction {
38 Linear,
39 EaseInOut,
40 EaseOut,
41}
42
43pub type RestyleNodes = BTreeMap<NodeId, Vec<ChangedCssProperty>>;
44pub type RelayoutNodes = BTreeMap<NodeId, Vec<ChangedCssProperty>>;
45pub type RelayoutWords = BTreeMap<NodeId, AzString>;
46
47#[derive(Debug, Clone, PartialEq)]
48pub struct FocusChange {
49 pub old: Option<DomNodeId>,
50 pub new: Option<DomNodeId>,
51}
52
53#[derive(Debug, Clone, PartialEq)]
54pub struct CallbackToCall {
55 pub node_id: NodeId,
56 pub hit_test_item: Option<HitTestItem>,
57 pub event_filter: EventFilter,
58}
59
60#[derive(Debug, Copy, Clone, PartialEq, Eq)]
61pub enum ProcessEventResult {
62 DoNothing = 0,
63 ShouldReRenderCurrentWindow = 1,
64 ShouldUpdateDisplayListCurrentWindow = 2,
65 UpdateHitTesterAndProcessAgain = 3,
68 ShouldRegenerateDomCurrentWindow = 4,
70 ShouldRegenerateDomAllWindows = 5,
71}
72
73impl ProcessEventResult {
74 pub fn order(&self) -> usize {
75 use self::ProcessEventResult::*;
76 match self {
77 DoNothing => 0,
78 ShouldReRenderCurrentWindow => 1,
79 ShouldUpdateDisplayListCurrentWindow => 2,
80 UpdateHitTesterAndProcessAgain => 3,
81 ShouldRegenerateDomCurrentWindow => 4,
82 ShouldRegenerateDomAllWindows => 5,
83 }
84 }
85}
86
87impl PartialOrd for ProcessEventResult {
88 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
89 self.order().partial_cmp(&other.order())
90 }
91}
92
93impl Ord for ProcessEventResult {
94 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
95 self.order().cmp(&other.order())
96 }
97}
98
99impl ProcessEventResult {
100 pub fn max_self(self, other: Self) -> Self {
101 self.max(other)
102 }
103}
104
105#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
112#[repr(C)]
113pub enum EventSource {
114 User,
116 Programmatic,
118 Synthetic,
120 Lifecycle,
122}
123
124#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
131#[repr(C)]
132pub enum EventPhase {
133 Capture,
135 Target,
137 Bubble,
139}
140
141impl Default for EventPhase {
142 fn default() -> Self {
143 EventPhase::Bubble
144 }
145}
146
147#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
149#[repr(C)]
150pub enum MouseButton {
151 Left,
152 Middle,
153 Right,
154 Other(u8),
155}
156
157#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
159#[repr(C)]
160pub enum ScrollDeltaMode {
161 Pixel,
163 Line,
165 Page,
167}
168
169#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
171#[repr(C)]
172pub enum ScrollDirection {
173 Up,
174 Down,
175 Left,
176 Right,
177}
178
179#[repr(C)]
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
189pub struct ScrollIntoViewOptions {
190 pub block: ScrollLogicalPosition,
192 pub inline_axis: ScrollLogicalPosition,
195 pub behavior: ScrollIntoViewBehavior,
197}
198
199impl ScrollIntoViewOptions {
200 pub fn nearest() -> Self {
202 Self {
203 block: ScrollLogicalPosition::Nearest,
204 inline_axis: ScrollLogicalPosition::Nearest,
205 behavior: ScrollIntoViewBehavior::Auto,
206 }
207 }
208
209 pub fn center() -> Self {
211 Self {
212 block: ScrollLogicalPosition::Center,
213 inline_axis: ScrollLogicalPosition::Center,
214 behavior: ScrollIntoViewBehavior::Auto,
215 }
216 }
217
218 pub fn start() -> Self {
220 Self {
221 block: ScrollLogicalPosition::Start,
222 inline_axis: ScrollLogicalPosition::Start,
223 behavior: ScrollIntoViewBehavior::Auto,
224 }
225 }
226
227 pub fn end() -> Self {
229 Self {
230 block: ScrollLogicalPosition::End,
231 inline_axis: ScrollLogicalPosition::End,
232 behavior: ScrollIntoViewBehavior::Auto,
233 }
234 }
235
236 pub fn with_instant(mut self) -> Self {
238 self.behavior = ScrollIntoViewBehavior::Instant;
239 self
240 }
241
242 pub fn with_smooth(mut self) -> Self {
244 self.behavior = ScrollIntoViewBehavior::Smooth;
245 self
246 }
247}
248
249#[repr(C)]
254#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
255pub enum ScrollLogicalPosition {
256 Start,
258 Center,
260 End,
262 #[default]
264 Nearest,
265}
266
267#[repr(C)]
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
273pub enum ScrollIntoViewBehavior {
274 #[default]
276 Auto,
277 Instant,
279 Smooth,
281}
282
283#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
285#[repr(C)]
286pub enum LifecycleReason {
287 InitialMount,
289 Remount,
291 Resize,
293 Update,
295}
296
297#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
299#[repr(C)]
300pub struct KeyModifiers {
301 pub shift: bool,
302 pub ctrl: bool,
303 pub alt: bool,
304 pub meta: bool,
305}
306
307impl KeyModifiers {
308 pub fn new() -> Self {
309 Self::default()
310 }
311
312 pub fn with_shift(mut self) -> Self {
313 self.shift = true;
314 self
315 }
316
317 pub fn with_ctrl(mut self) -> Self {
318 self.ctrl = true;
319 self
320 }
321
322 pub fn with_alt(mut self) -> Self {
323 self.alt = true;
324 self
325 }
326
327 pub fn with_meta(mut self) -> Self {
328 self.meta = true;
329 self
330 }
331
332 pub fn is_empty(&self) -> bool {
333 !self.shift && !self.ctrl && !self.alt && !self.meta
334 }
335}
336
337#[derive(Debug, Clone, PartialEq)]
339pub struct MouseEventData {
340 pub position: LogicalPosition,
342 pub button: MouseButton,
344 pub buttons: u8,
346 pub modifiers: KeyModifiers,
348}
349
350#[derive(Debug, Clone, PartialEq)]
352pub struct KeyboardEventData {
353 pub key_code: u32,
355 pub char_code: Option<char>,
357 pub modifiers: KeyModifiers,
359 pub repeat: bool,
361}
362
363#[derive(Debug, Clone, PartialEq)]
365pub struct ScrollEventData {
366 pub delta: LogicalPosition,
368 pub delta_mode: ScrollDeltaMode,
370}
371
372#[derive(Debug, Clone, PartialEq)]
374pub struct TouchEventData {
375 pub id: u64,
377 pub position: LogicalPosition,
379 pub force: f32,
381}
382
383#[derive(Debug, Clone, PartialEq)]
385pub struct ClipboardEventData {
386 pub content: Option<String>,
388}
389
390#[derive(Debug, Clone, PartialEq)]
392pub struct LifecycleEventData {
393 pub reason: LifecycleReason,
395 pub previous_bounds: Option<LogicalRect>,
397 pub current_bounds: LogicalRect,
399}
400
401#[derive(Debug, Clone, PartialEq)]
403pub struct WindowEventData {
404 pub size: Option<LogicalRect>,
406 pub position: Option<LogicalPosition>,
408}
409
410#[derive(Debug, Clone, PartialEq)]
412pub enum EventData {
413 Mouse(MouseEventData),
415 Keyboard(KeyboardEventData),
417 Scroll(ScrollEventData),
419 Touch(TouchEventData),
421 Clipboard(ClipboardEventData),
423 Lifecycle(LifecycleEventData),
425 Window(WindowEventData),
427 None,
429}
430
431#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
437#[repr(C)]
438pub enum EventType {
439 MouseOver,
442 MouseEnter,
444 MouseLeave,
446 MouseDown,
448 MouseUp,
450 Click,
452 DoubleClick,
454 ContextMenu,
456
457 KeyDown,
460 KeyUp,
462 KeyPress,
464
465 Focus,
468 Blur,
470 FocusIn,
472 FocusOut,
474
475 Input,
478 Change,
480 Submit,
482 Reset,
484 Invalid,
486
487 Scroll,
490 ScrollStart,
492 ScrollEnd,
494
495 DragStart,
498 Drag,
500 DragEnd,
502 DragEnter,
504 DragOver,
506 DragLeave,
508 Drop,
510
511 TouchStart,
514 TouchMove,
516 TouchEnd,
518 TouchCancel,
520
521 LongPress,
524 SwipeLeft,
526 SwipeRight,
528 SwipeUp,
530 SwipeDown,
532 PinchIn,
534 PinchOut,
536 RotateClockwise,
538 RotateCounterClockwise,
540
541 Copy,
544 Cut,
546 Paste,
548
549 Play,
552 Pause,
554 Ended,
556 TimeUpdate,
558 VolumeChange,
560 MediaError,
562
563 Mount,
566 Unmount,
568 Update,
570 Resize,
572
573 WindowResize,
576 WindowMove,
578 WindowClose,
580 WindowFocusIn,
582 WindowFocusOut,
584 ThemeChange,
586
587 FileHover,
590 FileDrop,
592 FileHoverCancel,
594}
595
596#[derive(Debug, Clone, PartialEq)]
601pub struct SyntheticEvent {
602 pub event_type: EventType,
604
605 pub source: EventSource,
607
608 pub phase: EventPhase,
610
611 pub target: DomNodeId,
613
614 pub current_target: DomNodeId,
616
617 pub timestamp: Instant,
619
620 pub data: EventData,
622
623 pub stopped: bool,
625
626 pub stopped_immediate: bool,
628
629 pub prevented_default: bool,
631}
632
633impl SyntheticEvent {
634 pub fn new(
639 event_type: EventType,
640 source: EventSource,
641 target: DomNodeId,
642 timestamp: Instant,
643 data: EventData,
644 ) -> Self {
645 Self {
646 event_type,
647 source,
648 phase: EventPhase::Target,
649 target,
650 current_target: target,
651 timestamp,
652 data,
653 stopped: false,
654 stopped_immediate: false,
655 prevented_default: false,
656 }
657 }
658
659 pub fn stop_propagation(&mut self) {
664 self.stopped = true;
665 }
666
667 pub fn stop_immediate_propagation(&mut self) {
672 self.stopped_immediate = true;
673 self.stopped = true;
674 }
675
676 pub fn prevent_default(&mut self) {
681 self.prevented_default = true;
682 }
683
684 pub fn is_propagation_stopped(&self) -> bool {
686 self.stopped
687 }
688
689 pub fn is_immediate_propagation_stopped(&self) -> bool {
691 self.stopped_immediate
692 }
693
694 pub fn is_default_prevented(&self) -> bool {
696 self.prevented_default
697 }
698}
699
700#[derive(Debug, Clone)]
704pub struct PropagationResult {
705 pub callbacks_to_invoke: Vec<(NodeId, EventFilter)>,
707 pub default_prevented: bool,
709}
710
711pub fn get_dom_path(
718 node_hierarchy: &crate::id::NodeHierarchy,
719 target_node: NodeHierarchyItemId,
720) -> Vec<NodeId> {
721 let mut path = Vec::new();
722 let target_node_id = match target_node.into_crate_internal() {
723 Some(id) => id,
724 None => return path,
725 };
726
727 let hier_ref = node_hierarchy.as_ref();
728
729 let mut current = Some(target_node_id);
731 while let Some(node_id) = current {
732 path.push(node_id);
733 current = hier_ref.get(node_id).and_then(|node| node.parent);
734 }
735
736 path.reverse();
738 path
739}
740
741pub fn propagate_event(
751 event: &mut SyntheticEvent,
752 node_hierarchy: &crate::id::NodeHierarchy,
753 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
754) -> PropagationResult {
755 let path = get_dom_path(node_hierarchy, event.target.node);
756 if path.is_empty() {
757 return PropagationResult::default();
758 }
759
760 let ancestors = &path[..path.len().saturating_sub(1)];
761 let target_node_id = *path.last().unwrap();
762
763 let mut result = PropagationResult::default();
764
765 propagate_phase(
767 event,
768 ancestors.iter().copied(),
769 EventPhase::Capture,
770 callbacks,
771 &mut result,
772 );
773
774 if !event.stopped {
776 propagate_target_phase(event, target_node_id, callbacks, &mut result);
777 }
778
779 if !event.stopped {
781 propagate_phase(
782 event,
783 ancestors.iter().rev().copied(),
784 EventPhase::Bubble,
785 callbacks,
786 &mut result,
787 );
788 }
789
790 result.default_prevented = event.prevented_default;
791 result
792}
793
794fn propagate_phase(
796 event: &mut SyntheticEvent,
797 nodes: impl Iterator<Item = NodeId>,
798 phase: EventPhase,
799 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
800 result: &mut PropagationResult,
801) {
802 event.phase = phase;
803
804 for node_id in nodes {
805 if event.stopped_immediate || event.stopped {
806 return;
807 }
808
809 event.current_target = DomNodeId {
810 dom: event.target.dom,
811 node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
812 };
813
814 collect_matching_callbacks(event, node_id, phase, callbacks, result);
815 }
816}
817
818fn propagate_target_phase(
820 event: &mut SyntheticEvent,
821 target_node_id: NodeId,
822 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
823 result: &mut PropagationResult,
824) {
825 event.phase = EventPhase::Target;
826 event.current_target = event.target;
827
828 collect_matching_callbacks(event, target_node_id, EventPhase::Target, callbacks, result);
829}
830
831fn collect_matching_callbacks(
833 event: &SyntheticEvent,
834 node_id: NodeId,
835 phase: EventPhase,
836 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
837 result: &mut PropagationResult,
838) {
839 let Some(node_callbacks) = callbacks.get(&node_id) else {
840 return;
841 };
842
843 let matching = node_callbacks
844 .iter()
845 .take_while(|_| !event.stopped_immediate)
846 .filter(|filter| matches_filter_phase(filter, event, phase))
847 .map(|filter| (node_id, *filter));
848
849 result.callbacks_to_invoke.extend(matching);
850}
851
852impl Default for PropagationResult {
853 fn default() -> Self {
854 Self {
855 callbacks_to_invoke: Vec::new(),
856 default_prevented: false,
857 }
858 }
859}
860
861#[derive(Debug, Clone, PartialEq, Eq, Hash)]
880#[repr(C, u8)]
881pub enum DefaultAction {
882 FocusNext,
884 FocusPrevious,
886 FocusFirst,
888 FocusLast,
890 ClearFocus,
892 ActivateFocusedElement {
895 target: DomNodeId,
896 },
897 SubmitForm {
899 form_node: DomNodeId,
900 },
901 CloseModal {
903 modal_node: DomNodeId,
904 },
905 ScrollFocusedContainer {
907 direction: ScrollDirection,
908 amount: ScrollAmount,
909 },
910 SelectAllText,
912 None,
914}
915
916#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
918#[repr(C)]
919pub enum ScrollAmount {
920 Line,
922 Page,
924 Document,
926}
927
928#[derive(Debug, Clone)]
935#[repr(C)]
936pub struct DefaultActionResult {
937 pub action: DefaultAction,
939 pub prevented: bool,
941}
942
943impl Default for DefaultActionResult {
944 fn default() -> Self {
945 Self {
946 action: DefaultAction::None,
947 prevented: false,
948 }
949 }
950}
951
952impl DefaultActionResult {
953 pub fn new(action: DefaultAction) -> Self {
955 Self {
956 action,
957 prevented: false,
958 }
959 }
960
961 pub fn prevented() -> Self {
963 Self {
964 action: DefaultAction::None,
965 prevented: true,
966 }
967 }
968
969 pub fn has_action(&self) -> bool {
971 !self.prevented && !matches!(self.action, DefaultAction::None)
972 }
973}
974
975pub trait ActivationBehavior {
987 fn has_activation_behavior(&self) -> bool;
989
990 fn is_activatable(&self) -> bool;
993}
994
995pub trait Focusable {
997 fn get_tabindex(&self) -> Option<i32>;
999
1000 fn is_focusable(&self) -> bool;
1002
1003 fn is_in_tab_order(&self) -> bool {
1005 match self.get_tabindex() {
1006 None => self.is_naturally_focusable(),
1007 Some(i) => i >= 0,
1008 }
1009 }
1010
1011 fn is_naturally_focusable(&self) -> bool;
1014}
1015
1016fn matches_filter_phase(
1021 filter: &EventFilter,
1022 event: &SyntheticEvent,
1023 current_phase: EventPhase,
1024) -> bool {
1025 match filter {
1029 EventFilter::Hover(hover_filter) => {
1030 matches_hover_filter(hover_filter, event, current_phase)
1031 }
1032 EventFilter::Focus(focus_filter) => {
1033 matches_focus_filter(focus_filter, event, current_phase)
1034 }
1035 EventFilter::Window(window_filter) => {
1036 matches_window_filter(window_filter, event, current_phase)
1037 }
1038 EventFilter::Not(_) => {
1039 false
1041 }
1042 EventFilter::Component(_) | EventFilter::Application(_) => {
1043 false
1045 }
1046 }
1047}
1048
1049fn matches_hover_filter(
1051 filter: &HoverEventFilter,
1052 event: &SyntheticEvent,
1053 _phase: EventPhase,
1054) -> bool {
1055 use HoverEventFilter::*;
1056
1057 match (filter, &event.event_type) {
1058 (MouseOver, EventType::MouseOver) => true,
1059 (MouseDown, EventType::MouseDown) => true,
1060 (LeftMouseDown, EventType::MouseDown) => {
1061 if let EventData::Mouse(mouse_data) = &event.data {
1063 mouse_data.button == MouseButton::Left
1064 } else {
1065 false
1066 }
1067 }
1068 (RightMouseDown, EventType::MouseDown) => {
1069 if let EventData::Mouse(mouse_data) = &event.data {
1070 mouse_data.button == MouseButton::Right
1071 } else {
1072 false
1073 }
1074 }
1075 (MiddleMouseDown, EventType::MouseDown) => {
1076 if let EventData::Mouse(mouse_data) = &event.data {
1077 mouse_data.button == MouseButton::Middle
1078 } else {
1079 false
1080 }
1081 }
1082 (MouseUp, EventType::MouseUp) => true,
1083 (LeftMouseUp, EventType::MouseUp) => {
1084 if let EventData::Mouse(mouse_data) = &event.data {
1085 mouse_data.button == MouseButton::Left
1086 } else {
1087 false
1088 }
1089 }
1090 (RightMouseUp, EventType::MouseUp) => {
1091 if let EventData::Mouse(mouse_data) = &event.data {
1092 mouse_data.button == MouseButton::Right
1093 } else {
1094 false
1095 }
1096 }
1097 (MiddleMouseUp, EventType::MouseUp) => {
1098 if let EventData::Mouse(mouse_data) = &event.data {
1099 mouse_data.button == MouseButton::Middle
1100 } else {
1101 false
1102 }
1103 }
1104 (MouseEnter, EventType::MouseEnter) => true,
1105 (MouseLeave, EventType::MouseLeave) => true,
1106 (Scroll, EventType::Scroll) => true,
1107 (ScrollStart, EventType::ScrollStart) => true,
1108 (ScrollEnd, EventType::ScrollEnd) => true,
1109 (TextInput, EventType::Input) => true,
1110 (VirtualKeyDown, EventType::KeyDown) => true,
1111 (VirtualKeyUp, EventType::KeyUp) => true,
1112 (HoveredFile, EventType::FileHover) => true,
1113 (DroppedFile, EventType::FileDrop) => true,
1114 (HoveredFileCancelled, EventType::FileHoverCancel) => true,
1115 (TouchStart, EventType::TouchStart) => true,
1116 (TouchMove, EventType::TouchMove) => true,
1117 (TouchEnd, EventType::TouchEnd) => true,
1118 (TouchCancel, EventType::TouchCancel) => true,
1119 _ => false,
1120 }
1121}
1122
1123fn matches_focus_filter(
1125 filter: &FocusEventFilter,
1126 event: &SyntheticEvent,
1127 _phase: EventPhase,
1128) -> bool {
1129 use FocusEventFilter::*;
1130
1131 match (filter, &event.event_type) {
1132 (MouseOver, EventType::MouseOver) => true,
1133 (MouseDown, EventType::MouseDown) => true,
1134 (LeftMouseDown, EventType::MouseDown) => {
1135 if let EventData::Mouse(mouse_data) = &event.data {
1136 mouse_data.button == MouseButton::Left
1137 } else {
1138 false
1139 }
1140 }
1141 (RightMouseDown, EventType::MouseDown) => {
1142 if let EventData::Mouse(mouse_data) = &event.data {
1143 mouse_data.button == MouseButton::Right
1144 } else {
1145 false
1146 }
1147 }
1148 (MiddleMouseDown, EventType::MouseDown) => {
1149 if let EventData::Mouse(mouse_data) = &event.data {
1150 mouse_data.button == MouseButton::Middle
1151 } else {
1152 false
1153 }
1154 }
1155 (MouseUp, EventType::MouseUp) => true,
1156 (LeftMouseUp, EventType::MouseUp) => {
1157 if let EventData::Mouse(mouse_data) = &event.data {
1158 mouse_data.button == MouseButton::Left
1159 } else {
1160 false
1161 }
1162 }
1163 (RightMouseUp, EventType::MouseUp) => {
1164 if let EventData::Mouse(mouse_data) = &event.data {
1165 mouse_data.button == MouseButton::Right
1166 } else {
1167 false
1168 }
1169 }
1170 (MiddleMouseUp, EventType::MouseUp) => {
1171 if let EventData::Mouse(mouse_data) = &event.data {
1172 mouse_data.button == MouseButton::Middle
1173 } else {
1174 false
1175 }
1176 }
1177 (MouseEnter, EventType::MouseEnter) => true,
1178 (MouseLeave, EventType::MouseLeave) => true,
1179 (Scroll, EventType::Scroll) => true,
1180 (ScrollStart, EventType::ScrollStart) => true,
1181 (ScrollEnd, EventType::ScrollEnd) => true,
1182 (TextInput, EventType::Input) => true,
1183 (VirtualKeyDown, EventType::KeyDown) => true,
1184 (VirtualKeyUp, EventType::KeyUp) => true,
1185 (FocusReceived, EventType::Focus) => true,
1186 (FocusLost, EventType::Blur) => true,
1187 _ => false,
1188 }
1189}
1190
1191fn matches_window_filter(
1193 filter: &WindowEventFilter,
1194 event: &SyntheticEvent,
1195 _phase: EventPhase,
1196) -> bool {
1197 use WindowEventFilter::*;
1198
1199 match (filter, &event.event_type) {
1200 (MouseOver, EventType::MouseOver) => true,
1201 (MouseDown, EventType::MouseDown) => true,
1202 (LeftMouseDown, EventType::MouseDown) => {
1203 if let EventData::Mouse(mouse_data) = &event.data {
1204 mouse_data.button == MouseButton::Left
1205 } else {
1206 false
1207 }
1208 }
1209 (RightMouseDown, EventType::MouseDown) => {
1210 if let EventData::Mouse(mouse_data) = &event.data {
1211 mouse_data.button == MouseButton::Right
1212 } else {
1213 false
1214 }
1215 }
1216 (MiddleMouseDown, EventType::MouseDown) => {
1217 if let EventData::Mouse(mouse_data) = &event.data {
1218 mouse_data.button == MouseButton::Middle
1219 } else {
1220 false
1221 }
1222 }
1223 (MouseUp, EventType::MouseUp) => true,
1224 (LeftMouseUp, EventType::MouseUp) => {
1225 if let EventData::Mouse(mouse_data) = &event.data {
1226 mouse_data.button == MouseButton::Left
1227 } else {
1228 false
1229 }
1230 }
1231 (RightMouseUp, EventType::MouseUp) => {
1232 if let EventData::Mouse(mouse_data) = &event.data {
1233 mouse_data.button == MouseButton::Right
1234 } else {
1235 false
1236 }
1237 }
1238 (MiddleMouseUp, EventType::MouseUp) => {
1239 if let EventData::Mouse(mouse_data) = &event.data {
1240 mouse_data.button == MouseButton::Middle
1241 } else {
1242 false
1243 }
1244 }
1245 (MouseEnter, EventType::MouseEnter) => true,
1246 (MouseLeave, EventType::MouseLeave) => true,
1247 (Scroll, EventType::Scroll) => true,
1248 (ScrollStart, EventType::ScrollStart) => true,
1249 (ScrollEnd, EventType::ScrollEnd) => true,
1250 (TextInput, EventType::Input) => true,
1251 (VirtualKeyDown, EventType::KeyDown) => true,
1252 (VirtualKeyUp, EventType::KeyUp) => true,
1253 (HoveredFile, EventType::FileHover) => true,
1254 (DroppedFile, EventType::FileDrop) => true,
1255 (HoveredFileCancelled, EventType::FileHoverCancel) => true,
1256 (Resized, EventType::WindowResize) => true,
1257 (Moved, EventType::WindowMove) => true,
1258 (TouchStart, EventType::TouchStart) => true,
1259 (TouchMove, EventType::TouchMove) => true,
1260 (TouchEnd, EventType::TouchEnd) => true,
1261 (TouchCancel, EventType::TouchCancel) => true,
1262 (FocusReceived, EventType::Focus) => true,
1263 (FocusLost, EventType::Blur) => true,
1264 (CloseRequested, EventType::WindowClose) => true,
1265 (ThemeChanged, EventType::ThemeChange) => true,
1266 (WindowFocusReceived, EventType::WindowFocusIn) => true,
1267 (WindowFocusLost, EventType::WindowFocusOut) => true,
1268 _ => false,
1269 }
1270}
1271
1272pub fn detect_lifecycle_events(
1282 old_dom_id: DomId,
1283 new_dom_id: DomId,
1284 old_hierarchy: Option<&crate::id::NodeHierarchy>,
1285 new_hierarchy: Option<&crate::id::NodeHierarchy>,
1286 old_layout: Option<&BTreeMap<NodeId, LogicalRect>>,
1287 new_layout: Option<&BTreeMap<NodeId, LogicalRect>>,
1288 timestamp: Instant,
1289) -> Vec<SyntheticEvent> {
1290 let old_nodes = collect_node_ids(old_hierarchy);
1291 let new_nodes = collect_node_ids(new_hierarchy);
1292
1293 let mut events = Vec::new();
1294
1295 if let Some(layout) = new_layout {
1297 for &node_id in new_nodes.difference(&old_nodes) {
1298 events.push(create_mount_event(node_id, new_dom_id, layout, ×tamp));
1299 }
1300 }
1301
1302 if let Some(layout) = old_layout {
1304 for &node_id in old_nodes.difference(&new_nodes) {
1305 events.push(create_unmount_event(
1306 node_id, old_dom_id, layout, ×tamp,
1307 ));
1308 }
1309 }
1310
1311 if let (Some(old_l), Some(new_l)) = (old_layout, new_layout) {
1313 for &node_id in old_nodes.intersection(&new_nodes) {
1314 if let Some(ev) = create_resize_event(node_id, new_dom_id, old_l, new_l, ×tamp) {
1315 events.push(ev);
1316 }
1317 }
1318 }
1319
1320 events
1321}
1322
1323fn collect_node_ids(hierarchy: Option<&crate::id::NodeHierarchy>) -> BTreeSet<NodeId> {
1324 hierarchy
1325 .map(|h| h.as_ref().linear_iter().collect())
1326 .unwrap_or_default()
1327}
1328
1329fn create_lifecycle_event(
1330 event_type: EventType,
1331 node_id: NodeId,
1332 dom_id: DomId,
1333 timestamp: &Instant,
1334 data: LifecycleEventData,
1335) -> SyntheticEvent {
1336 let dom_node_id = DomNodeId {
1337 dom: dom_id,
1338 node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
1339 };
1340 SyntheticEvent {
1341 event_type,
1342 source: EventSource::Lifecycle,
1343 phase: EventPhase::Target,
1344 target: dom_node_id,
1345 current_target: dom_node_id,
1346 timestamp: timestamp.clone(),
1347 data: EventData::Lifecycle(data),
1348 stopped: false,
1349 stopped_immediate: false,
1350 prevented_default: false,
1351 }
1352}
1353
1354fn create_mount_event(
1355 node_id: NodeId,
1356 dom_id: DomId,
1357 layout: &BTreeMap<NodeId, LogicalRect>,
1358 timestamp: &Instant,
1359) -> SyntheticEvent {
1360 let current_bounds = layout.get(&node_id).copied().unwrap_or(LogicalRect::zero());
1361 create_lifecycle_event(
1362 EventType::Mount,
1363 node_id,
1364 dom_id,
1365 timestamp,
1366 LifecycleEventData {
1367 reason: LifecycleReason::InitialMount,
1368 previous_bounds: None,
1369 current_bounds,
1370 },
1371 )
1372}
1373
1374fn create_unmount_event(
1375 node_id: NodeId,
1376 dom_id: DomId,
1377 layout: &BTreeMap<NodeId, LogicalRect>,
1378 timestamp: &Instant,
1379) -> SyntheticEvent {
1380 let previous_bounds = layout.get(&node_id).copied().unwrap_or(LogicalRect::zero());
1381 create_lifecycle_event(
1382 EventType::Unmount,
1383 node_id,
1384 dom_id,
1385 timestamp,
1386 LifecycleEventData {
1387 reason: LifecycleReason::InitialMount,
1388 previous_bounds: Some(previous_bounds),
1389 current_bounds: LogicalRect::zero(),
1390 },
1391 )
1392}
1393
1394fn create_resize_event(
1395 node_id: NodeId,
1396 dom_id: DomId,
1397 old_layout: &BTreeMap<NodeId, LogicalRect>,
1398 new_layout: &BTreeMap<NodeId, LogicalRect>,
1399 timestamp: &Instant,
1400) -> Option<SyntheticEvent> {
1401 let old_bounds = *old_layout.get(&node_id)?;
1402 let new_bounds = *new_layout.get(&node_id)?;
1403
1404 if old_bounds.size == new_bounds.size {
1405 return None;
1406 }
1407
1408 Some(create_lifecycle_event(
1409 EventType::Resize,
1410 node_id,
1411 dom_id,
1412 timestamp,
1413 LifecycleEventData {
1414 reason: LifecycleReason::Resize,
1415 previous_bounds: Some(old_bounds),
1416 current_bounds: new_bounds,
1417 },
1418 ))
1419}
1420
1421#[derive(Debug, Clone, Default)]
1426pub struct LifecycleEventResult {
1427 pub events: Vec<SyntheticEvent>,
1429 pub node_id_mapping: crate::FastHashMap<NodeId, NodeId>,
1432}
1433
1434pub fn detect_lifecycle_events_with_reconciliation(
1488 dom_id: DomId,
1489 old_node_data: &[crate::dom::NodeData],
1490 new_node_data: &[crate::dom::NodeData],
1491 old_layout: &crate::FastHashMap<NodeId, LogicalRect>,
1492 new_layout: &crate::FastHashMap<NodeId, LogicalRect>,
1493 timestamp: Instant,
1494) -> LifecycleEventResult {
1495 let diff_result = crate::diff::reconcile_dom(
1496 old_node_data,
1497 new_node_data,
1498 old_layout,
1499 new_layout,
1500 dom_id,
1501 timestamp,
1502 );
1503
1504 LifecycleEventResult {
1505 events: diff_result.events,
1506 node_id_mapping: crate::diff::create_migration_map(&diff_result.node_moves),
1507 }
1508}
1509
1510#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1514#[repr(C)]
1515pub enum HoverEventFilter {
1516 MouseOver,
1518 MouseDown,
1520 LeftMouseDown,
1522 RightMouseDown,
1524 MiddleMouseDown,
1526 MouseUp,
1528 LeftMouseUp,
1530 RightMouseUp,
1532 MiddleMouseUp,
1534 MouseEnter,
1536 MouseLeave,
1538 Scroll,
1540 ScrollStart,
1542 ScrollEnd,
1544 TextInput,
1546 VirtualKeyDown,
1548 VirtualKeyUp,
1550 HoveredFile,
1552 DroppedFile,
1554 HoveredFileCancelled,
1556 TouchStart,
1558 TouchMove,
1560 TouchEnd,
1562 TouchCancel,
1564 PenDown,
1566 PenMove,
1568 PenUp,
1570 PenEnter,
1572 PenLeave,
1574 DragStart,
1576 Drag,
1578 DragEnd,
1580 DoubleClick,
1582 LongPress,
1584 SwipeLeft,
1586 SwipeRight,
1588 SwipeUp,
1590 SwipeDown,
1592 PinchIn,
1594 PinchOut,
1596 RotateClockwise,
1598 RotateCounterClockwise,
1600
1601 #[doc(hidden)]
1603 SystemTextSingleClick,
1605 #[doc(hidden)]
1606 SystemTextDoubleClick,
1608 #[doc(hidden)]
1609 SystemTextTripleClick,
1611}
1612
1613impl HoverEventFilter {
1614 pub const fn is_system_internal(&self) -> bool {
1616 matches!(
1617 self,
1618 HoverEventFilter::SystemTextSingleClick
1619 | HoverEventFilter::SystemTextDoubleClick
1620 | HoverEventFilter::SystemTextTripleClick
1621 )
1622 }
1623
1624 pub fn to_focus_event_filter(&self) -> Option<FocusEventFilter> {
1625 match self {
1626 HoverEventFilter::MouseOver => Some(FocusEventFilter::MouseOver),
1627 HoverEventFilter::MouseDown => Some(FocusEventFilter::MouseDown),
1628 HoverEventFilter::LeftMouseDown => Some(FocusEventFilter::LeftMouseDown),
1629 HoverEventFilter::RightMouseDown => Some(FocusEventFilter::RightMouseDown),
1630 HoverEventFilter::MiddleMouseDown => Some(FocusEventFilter::MiddleMouseDown),
1631 HoverEventFilter::MouseUp => Some(FocusEventFilter::MouseUp),
1632 HoverEventFilter::LeftMouseUp => Some(FocusEventFilter::LeftMouseUp),
1633 HoverEventFilter::RightMouseUp => Some(FocusEventFilter::RightMouseUp),
1634 HoverEventFilter::MiddleMouseUp => Some(FocusEventFilter::MiddleMouseUp),
1635 HoverEventFilter::MouseEnter => Some(FocusEventFilter::MouseEnter),
1636 HoverEventFilter::MouseLeave => Some(FocusEventFilter::MouseLeave),
1637 HoverEventFilter::Scroll => Some(FocusEventFilter::Scroll),
1638 HoverEventFilter::ScrollStart => Some(FocusEventFilter::ScrollStart),
1639 HoverEventFilter::ScrollEnd => Some(FocusEventFilter::ScrollEnd),
1640 HoverEventFilter::TextInput => Some(FocusEventFilter::TextInput),
1641 HoverEventFilter::VirtualKeyDown => Some(FocusEventFilter::VirtualKeyDown),
1642 HoverEventFilter::VirtualKeyUp => Some(FocusEventFilter::VirtualKeyDown),
1643 HoverEventFilter::HoveredFile => None,
1644 HoverEventFilter::DroppedFile => None,
1645 HoverEventFilter::HoveredFileCancelled => None,
1646 HoverEventFilter::TouchStart => None,
1647 HoverEventFilter::TouchMove => None,
1648 HoverEventFilter::TouchEnd => None,
1649 HoverEventFilter::TouchCancel => None,
1650 HoverEventFilter::PenDown => Some(FocusEventFilter::PenDown),
1651 HoverEventFilter::PenMove => Some(FocusEventFilter::PenMove),
1652 HoverEventFilter::PenUp => Some(FocusEventFilter::PenUp),
1653 HoverEventFilter::PenEnter => None,
1654 HoverEventFilter::PenLeave => None,
1655 HoverEventFilter::DragStart => Some(FocusEventFilter::DragStart),
1656 HoverEventFilter::Drag => Some(FocusEventFilter::Drag),
1657 HoverEventFilter::DragEnd => Some(FocusEventFilter::DragEnd),
1658 HoverEventFilter::DoubleClick => Some(FocusEventFilter::DoubleClick),
1659 HoverEventFilter::LongPress => Some(FocusEventFilter::LongPress),
1660 HoverEventFilter::SwipeLeft => Some(FocusEventFilter::SwipeLeft),
1661 HoverEventFilter::SwipeRight => Some(FocusEventFilter::SwipeRight),
1662 HoverEventFilter::SwipeUp => Some(FocusEventFilter::SwipeUp),
1663 HoverEventFilter::SwipeDown => Some(FocusEventFilter::SwipeDown),
1664 HoverEventFilter::PinchIn => Some(FocusEventFilter::PinchIn),
1665 HoverEventFilter::PinchOut => Some(FocusEventFilter::PinchOut),
1666 HoverEventFilter::RotateClockwise => Some(FocusEventFilter::RotateClockwise),
1667 HoverEventFilter::RotateCounterClockwise => {
1668 Some(FocusEventFilter::RotateCounterClockwise)
1669 }
1670 HoverEventFilter::SystemTextSingleClick => None,
1672 HoverEventFilter::SystemTextDoubleClick => None,
1673 HoverEventFilter::SystemTextTripleClick => None,
1674 }
1675 }
1676}
1677
1678#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1683#[repr(C)]
1684pub enum FocusEventFilter {
1685 MouseOver,
1687 MouseDown,
1689 LeftMouseDown,
1691 RightMouseDown,
1693 MiddleMouseDown,
1695 MouseUp,
1697 LeftMouseUp,
1699 RightMouseUp,
1701 MiddleMouseUp,
1703 MouseEnter,
1705 MouseLeave,
1707 Scroll,
1709 ScrollStart,
1711 ScrollEnd,
1713 TextInput,
1715 VirtualKeyDown,
1717 VirtualKeyUp,
1719 FocusReceived,
1721 FocusLost,
1723 PenDown,
1725 PenMove,
1727 PenUp,
1729 DragStart,
1731 Drag,
1733 DragEnd,
1735 DoubleClick,
1737 LongPress,
1739 SwipeLeft,
1741 SwipeRight,
1743 SwipeUp,
1745 SwipeDown,
1747 PinchIn,
1749 PinchOut,
1751 RotateClockwise,
1753 RotateCounterClockwise,
1755}
1756
1757#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1760#[repr(C)]
1761pub enum WindowEventFilter {
1762 MouseOver,
1764 MouseDown,
1766 LeftMouseDown,
1768 RightMouseDown,
1770 MiddleMouseDown,
1772 MouseUp,
1774 LeftMouseUp,
1776 RightMouseUp,
1778 MiddleMouseUp,
1780 MouseEnter,
1782 MouseLeave,
1784 Scroll,
1786 ScrollStart,
1788 ScrollEnd,
1790 TextInput,
1792 VirtualKeyDown,
1794 VirtualKeyUp,
1796 HoveredFile,
1798 DroppedFile,
1800 HoveredFileCancelled,
1802 Resized,
1804 Moved,
1806 TouchStart,
1808 TouchMove,
1810 TouchEnd,
1812 TouchCancel,
1814 FocusReceived,
1816 FocusLost,
1818 CloseRequested,
1820 ThemeChanged,
1822 WindowFocusReceived,
1824 WindowFocusLost,
1826 PenDown,
1828 PenMove,
1830 PenUp,
1832 PenEnter,
1834 PenLeave,
1836 DragStart,
1838 Drag,
1840 DragEnd,
1842 DoubleClick,
1844 LongPress,
1846 SwipeLeft,
1848 SwipeRight,
1850 SwipeUp,
1852 SwipeDown,
1854 PinchIn,
1856 PinchOut,
1858 RotateClockwise,
1860 RotateCounterClockwise,
1862}
1863
1864impl WindowEventFilter {
1865 pub fn to_hover_event_filter(&self) -> Option<HoverEventFilter> {
1866 match self {
1867 WindowEventFilter::MouseOver => Some(HoverEventFilter::MouseOver),
1868 WindowEventFilter::MouseDown => Some(HoverEventFilter::MouseDown),
1869 WindowEventFilter::LeftMouseDown => Some(HoverEventFilter::LeftMouseDown),
1870 WindowEventFilter::RightMouseDown => Some(HoverEventFilter::RightMouseDown),
1871 WindowEventFilter::MiddleMouseDown => Some(HoverEventFilter::MiddleMouseDown),
1872 WindowEventFilter::MouseUp => Some(HoverEventFilter::MouseUp),
1873 WindowEventFilter::LeftMouseUp => Some(HoverEventFilter::LeftMouseUp),
1874 WindowEventFilter::RightMouseUp => Some(HoverEventFilter::RightMouseUp),
1875 WindowEventFilter::MiddleMouseUp => Some(HoverEventFilter::MiddleMouseUp),
1876 WindowEventFilter::Scroll => Some(HoverEventFilter::Scroll),
1877 WindowEventFilter::ScrollStart => Some(HoverEventFilter::ScrollStart),
1878 WindowEventFilter::ScrollEnd => Some(HoverEventFilter::ScrollEnd),
1879 WindowEventFilter::TextInput => Some(HoverEventFilter::TextInput),
1880 WindowEventFilter::VirtualKeyDown => Some(HoverEventFilter::VirtualKeyDown),
1881 WindowEventFilter::VirtualKeyUp => Some(HoverEventFilter::VirtualKeyDown),
1882 WindowEventFilter::HoveredFile => Some(HoverEventFilter::HoveredFile),
1883 WindowEventFilter::DroppedFile => Some(HoverEventFilter::DroppedFile),
1884 WindowEventFilter::HoveredFileCancelled => Some(HoverEventFilter::HoveredFileCancelled),
1885 WindowEventFilter::MouseEnter => None,
1888 WindowEventFilter::MouseLeave => None,
1889 WindowEventFilter::Resized => None,
1890 WindowEventFilter::Moved => None,
1891 WindowEventFilter::TouchStart => Some(HoverEventFilter::TouchStart),
1892 WindowEventFilter::TouchMove => Some(HoverEventFilter::TouchMove),
1893 WindowEventFilter::TouchEnd => Some(HoverEventFilter::TouchEnd),
1894 WindowEventFilter::TouchCancel => Some(HoverEventFilter::TouchCancel),
1895 WindowEventFilter::FocusReceived => None,
1896 WindowEventFilter::FocusLost => None,
1897 WindowEventFilter::CloseRequested => None,
1898 WindowEventFilter::ThemeChanged => None,
1899 WindowEventFilter::WindowFocusReceived => None, WindowEventFilter::WindowFocusLost => None, WindowEventFilter::PenDown => Some(HoverEventFilter::PenDown),
1902 WindowEventFilter::PenMove => Some(HoverEventFilter::PenMove),
1903 WindowEventFilter::PenUp => Some(HoverEventFilter::PenUp),
1904 WindowEventFilter::PenEnter => Some(HoverEventFilter::PenEnter),
1905 WindowEventFilter::PenLeave => Some(HoverEventFilter::PenLeave),
1906 WindowEventFilter::DragStart => Some(HoverEventFilter::DragStart),
1907 WindowEventFilter::Drag => Some(HoverEventFilter::Drag),
1908 WindowEventFilter::DragEnd => Some(HoverEventFilter::DragEnd),
1909 WindowEventFilter::DoubleClick => Some(HoverEventFilter::DoubleClick),
1910 WindowEventFilter::LongPress => Some(HoverEventFilter::LongPress),
1911 WindowEventFilter::SwipeLeft => Some(HoverEventFilter::SwipeLeft),
1912 WindowEventFilter::SwipeRight => Some(HoverEventFilter::SwipeRight),
1913 WindowEventFilter::SwipeUp => Some(HoverEventFilter::SwipeUp),
1914 WindowEventFilter::SwipeDown => Some(HoverEventFilter::SwipeDown),
1915 WindowEventFilter::PinchIn => Some(HoverEventFilter::PinchIn),
1916 WindowEventFilter::PinchOut => Some(HoverEventFilter::PinchOut),
1917 WindowEventFilter::RotateClockwise => Some(HoverEventFilter::RotateClockwise),
1918 WindowEventFilter::RotateCounterClockwise => {
1919 Some(HoverEventFilter::RotateCounterClockwise)
1920 }
1921 }
1922 }
1923}
1924
1925#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1929#[repr(C, u8)]
1930pub enum NotEventFilter {
1931 Hover(HoverEventFilter),
1932 Focus(FocusEventFilter),
1933}
1934
1935impl NotEventFilter {
1936 pub fn as_event_filter(&self) -> EventFilter {
1937 match self {
1938 NotEventFilter::Hover(e) => EventFilter::Hover(*e),
1939 NotEventFilter::Focus(e) => EventFilter::Focus(*e),
1940 }
1941 }
1942}
1943
1944#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1946#[repr(C)]
1947pub enum ComponentEventFilter {
1948 AfterMount,
1950 BeforeUnmount,
1952 NodeResized,
1954 DefaultAction,
1956 Selected,
1958}
1959
1960#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1962#[repr(C)]
1963pub enum ApplicationEventFilter {
1964 DeviceConnected,
1966 DeviceDisconnected,
1968 }
1970
1971#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1976#[repr(C, u8)]
1977pub enum EventFilter {
1978 Hover(HoverEventFilter),
1981 Not(NotEventFilter),
1986 Focus(FocusEventFilter),
1988 Window(WindowEventFilter),
1998 Component(ComponentEventFilter),
2000 Application(ApplicationEventFilter),
2002}
2003
2004impl EventFilter {
2005 pub const fn is_focus_callback(&self) -> bool {
2006 match self {
2007 EventFilter::Focus(_) => true,
2008 _ => false,
2009 }
2010 }
2011 pub const fn is_window_callback(&self) -> bool {
2012 match self {
2013 EventFilter::Window(_) => true,
2014 _ => false,
2015 }
2016 }
2017}
2018
2019macro_rules! get_single_enum_type {
2022 ($fn_name:ident, $enum_name:ident:: $variant:ident($return_type:ty)) => {
2023 pub fn $fn_name(&self) -> Option<$return_type> {
2024 use self::$enum_name::*;
2025 match self {
2026 $variant(e) => Some(*e),
2027 _ => None,
2028 }
2029 }
2030 };
2031}
2032
2033impl EventFilter {
2034 get_single_enum_type!(as_hover_event_filter, EventFilter::Hover(HoverEventFilter));
2035 get_single_enum_type!(as_focus_event_filter, EventFilter::Focus(FocusEventFilter));
2036 get_single_enum_type!(as_not_event_filter, EventFilter::Not(NotEventFilter));
2037 get_single_enum_type!(
2038 as_window_event_filter,
2039 EventFilter::Window(WindowEventFilter)
2040 );
2041}
2042
2043impl From<On> for EventFilter {
2049 fn from(input: On) -> EventFilter {
2050 use crate::dom::On::*;
2051 match input {
2052 MouseOver => EventFilter::Hover(HoverEventFilter::MouseOver),
2053 MouseDown => EventFilter::Hover(HoverEventFilter::MouseDown),
2054 LeftMouseDown => EventFilter::Hover(HoverEventFilter::LeftMouseDown),
2055 MiddleMouseDown => EventFilter::Hover(HoverEventFilter::MiddleMouseDown),
2056 RightMouseDown => EventFilter::Hover(HoverEventFilter::RightMouseDown),
2057 MouseUp => EventFilter::Hover(HoverEventFilter::MouseUp),
2058 LeftMouseUp => EventFilter::Hover(HoverEventFilter::LeftMouseUp),
2059 MiddleMouseUp => EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
2060 RightMouseUp => EventFilter::Hover(HoverEventFilter::RightMouseUp),
2061
2062 MouseEnter => EventFilter::Hover(HoverEventFilter::MouseEnter),
2063 MouseLeave => EventFilter::Hover(HoverEventFilter::MouseLeave),
2064 Scroll => EventFilter::Hover(HoverEventFilter::Scroll),
2065 TextInput => EventFilter::Focus(FocusEventFilter::TextInput), VirtualKeyDown => EventFilter::Window(WindowEventFilter::VirtualKeyDown), VirtualKeyUp => EventFilter::Window(WindowEventFilter::VirtualKeyUp), HoveredFile => EventFilter::Hover(HoverEventFilter::HoveredFile),
2069 DroppedFile => EventFilter::Hover(HoverEventFilter::DroppedFile),
2070 HoveredFileCancelled => EventFilter::Hover(HoverEventFilter::HoveredFileCancelled),
2071 FocusReceived => EventFilter::Focus(FocusEventFilter::FocusReceived), FocusLost => EventFilter::Focus(FocusEventFilter::FocusLost), Default => EventFilter::Hover(HoverEventFilter::MouseUp), Collapse => EventFilter::Hover(HoverEventFilter::MouseUp), Expand => EventFilter::Hover(HoverEventFilter::MouseUp), Increment => EventFilter::Hover(HoverEventFilter::MouseUp), Decrement => EventFilter::Hover(HoverEventFilter::MouseUp), }
2081 }
2082}
2083
2084#[derive(Debug, Clone, PartialEq, Eq)]
2088pub enum CallbackTarget {
2089 Node { dom_id: DomId, node_id: NodeId },
2091 RootNodes,
2093}
2094
2095#[derive(Debug, Clone, PartialEq)]
2097pub struct CallbackToInvoke {
2098 pub target: CallbackTarget,
2100 pub event_filter: EventFilter,
2102 pub hit_test_item: Option<HitTestItem>,
2104}
2105
2106#[derive(Debug, Clone, PartialEq)]
2108pub struct EventDispatchResult {
2109 pub callbacks: Vec<CallbackToInvoke>,
2111 pub propagation_stopped: bool,
2113}
2114
2115impl EventDispatchResult {
2116 pub fn empty() -> Self {
2117 Self {
2118 callbacks: Vec::new(),
2119 propagation_stopped: false,
2120 }
2121 }
2122
2123 pub fn is_empty(&self) -> bool {
2124 self.callbacks.is_empty()
2125 }
2126}
2127
2128pub fn should_recurse_callbacks<T: CallbackResultRef>(
2137 callback_results: &[T],
2138 max_depth: usize,
2139 current_depth: usize,
2140) -> bool {
2141 if current_depth >= max_depth {
2142 return false;
2143 }
2144
2145 for result in callback_results {
2147 if result.stop_propagation() {
2149 return false;
2150 }
2151
2152 if result.should_regenerate_dom() {
2154 return current_depth + 1 < max_depth;
2155 }
2156 }
2157
2158 false
2159}
2160
2161pub trait CallbackResultRef {
2164 fn stop_propagation(&self) -> bool;
2165 fn prevent_default(&self) -> bool;
2166 fn should_regenerate_dom(&self) -> bool;
2167}
2168
2169pub trait EventProvider {
2177 fn get_pending_events(&self, timestamp: Instant) -> Vec<SyntheticEvent>;
2189}
2190
2191pub fn deduplicate_synthetic_events(mut events: Vec<SyntheticEvent>) -> Vec<SyntheticEvent> {
2195 if events.len() <= 1 {
2196 return events;
2197 }
2198
2199 events.sort_by_key(|e| (e.target.dom, e.target.node, e.event_type));
2200
2201 let mut result = Vec::with_capacity(events.len());
2203 let mut iter = events.into_iter();
2204
2205 if let Some(mut prev) = iter.next() {
2206 for curr in iter {
2207 if prev.target == curr.target && prev.event_type == curr.event_type {
2208 prev = if curr.timestamp > prev.timestamp {
2210 curr
2211 } else {
2212 prev
2213 };
2214 } else {
2215 result.push(prev);
2216 prev = curr;
2217 }
2218 }
2219 result.push(prev);
2220 }
2221
2222 result
2223}
2224
2225pub fn dispatch_synthetic_events(
2230 events: &[SyntheticEvent],
2231 hit_test: Option<&FullHitTest>,
2232) -> EventDispatchResult {
2233 let callbacks = events
2234 .iter()
2235 .flat_map(|event| dispatch_single_event(event, hit_test))
2236 .collect();
2237
2238 EventDispatchResult {
2239 callbacks,
2240 propagation_stopped: false,
2241 }
2242}
2243
2244fn event_type_to_filters(event_type: EventType) -> Vec<EventFilter> {
2249 use EventFilter as EF;
2250 use EventType as E;
2251 use FocusEventFilter as F;
2252 use HoverEventFilter as H;
2253 use WindowEventFilter as W;
2254
2255 match event_type {
2256 E::MouseDown => vec![EF::Hover(H::MouseDown), EF::Hover(H::LeftMouseDown)],
2259 E::MouseUp => vec![EF::Hover(H::MouseUp), EF::Hover(H::LeftMouseUp)],
2260
2261 E::MouseOver => vec![EF::Hover(H::MouseOver)],
2263 E::MouseEnter => vec![EF::Hover(H::MouseEnter)],
2264 E::MouseLeave => vec![EF::Hover(H::MouseLeave)],
2265 E::Click => vec![EF::Hover(H::MouseDown), EF::Hover(H::LeftMouseDown)],
2266 E::DoubleClick => vec![EF::Window(W::DoubleClick)],
2267 E::ContextMenu => vec![EF::Hover(H::RightMouseDown)],
2268
2269 E::KeyDown => vec![EF::Focus(F::VirtualKeyDown)],
2271 E::KeyUp => vec![EF::Focus(F::VirtualKeyUp)],
2272 E::KeyPress => vec![EF::Focus(F::TextInput)],
2273
2274 E::Focus | E::FocusIn => vec![EF::Focus(F::FocusReceived)],
2276 E::Blur | E::FocusOut => vec![EF::Focus(F::FocusLost)],
2277
2278 E::Input | E::Change => vec![EF::Focus(F::TextInput)],
2280
2281 E::Scroll | E::ScrollStart | E::ScrollEnd => vec![EF::Hover(H::Scroll)],
2283
2284 E::DragStart => vec![EF::Hover(H::DragStart)],
2286 E::Drag => vec![EF::Hover(H::Drag)],
2287 E::DragEnd => vec![EF::Hover(H::DragEnd)],
2288 E::DragEnter => vec![EF::Hover(H::MouseEnter)],
2289 E::DragOver => vec![EF::Hover(H::MouseOver)],
2290 E::DragLeave => vec![EF::Hover(H::MouseLeave)],
2291 E::Drop => vec![EF::Hover(H::DroppedFile)],
2292
2293 E::TouchStart => vec![EF::Hover(H::TouchStart)],
2295 E::TouchMove => vec![EF::Hover(H::TouchMove)],
2296 E::TouchEnd => vec![EF::Hover(H::TouchEnd)],
2297 E::TouchCancel => vec![EF::Hover(H::TouchCancel)],
2298
2299 E::WindowResize => vec![EF::Window(W::Resized)],
2301 E::WindowMove => vec![EF::Window(W::Moved)],
2302 E::WindowClose => vec![EF::Window(W::CloseRequested)],
2303 E::WindowFocusIn => vec![EF::Window(W::WindowFocusReceived)],
2304 E::WindowFocusOut => vec![EF::Window(W::WindowFocusLost)],
2305 E::ThemeChange => vec![EF::Window(W::ThemeChanged)],
2306
2307 E::FileHover => vec![EF::Hover(H::HoveredFile)],
2309 E::FileDrop => vec![EF::Hover(H::DroppedFile)],
2310 E::FileHoverCancel => vec![EF::Hover(H::HoveredFileCancelled)],
2311
2312 _ => vec![],
2314 }
2315}
2316
2317fn get_callback_target(event: &SyntheticEvent) -> Option<CallbackTarget> {
2319 let root_node = NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO));
2320
2321 if event.target.node == root_node {
2322 return Some(CallbackTarget::RootNodes);
2323 }
2324
2325 event
2326 .target
2327 .node
2328 .into_crate_internal()
2329 .map(|node_id| CallbackTarget::Node {
2330 dom_id: event.target.dom,
2331 node_id,
2332 })
2333}
2334
2335fn get_hit_test_item(
2337 target: &CallbackTarget,
2338 hit_test: Option<&FullHitTest>,
2339) -> Option<HitTestItem> {
2340 let CallbackTarget::Node { dom_id, node_id } = target else {
2341 return None;
2342 };
2343
2344 hit_test?
2345 .hovered_nodes
2346 .get(dom_id)?
2347 .regular_hit_test_nodes
2348 .get(node_id)
2349 .cloned()
2350}
2351
2352fn dispatch_single_event(
2354 event: &SyntheticEvent,
2355 hit_test: Option<&FullHitTest>,
2356) -> Vec<CallbackToInvoke> {
2357 let event_filters = event_type_to_filters(event.event_type);
2358 let target = match get_callback_target(event) {
2359 Some(t) => t,
2360 None => return Vec::new(),
2361 };
2362 let hit_test_item = get_hit_test_item(&target, hit_test);
2363
2364 event_filters
2365 .into_iter()
2366 .map(|event_filter| CallbackToInvoke {
2367 target: target.clone(),
2368 event_filter,
2369 hit_test_item: hit_test_item.clone(),
2370 })
2371 .collect()
2372}
2373
2374#[derive(Debug, Clone, PartialEq)]
2378pub struct PreCallbackFilterResult {
2379 pub internal_events: Vec<PreCallbackSystemEvent>,
2381 pub user_events: Vec<SyntheticEvent>,
2383}
2384
2385#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2387pub struct MouseButtonState {
2388 pub left_down: bool,
2389 pub right_down: bool,
2390 pub middle_down: bool,
2391}
2392
2393#[derive(Debug, Clone, PartialEq, Eq)]
2395pub enum PostCallbackSystemEvent {
2396 ApplyTextInput,
2398 FocusChanged,
2400 ApplyTextChangeset,
2402 ScrollIntoView,
2404 StartAutoScrollTimer,
2406 CancelAutoScrollTimer,
2408}
2409
2410#[derive(Debug, Clone, PartialEq)]
2412pub enum PreCallbackSystemEvent {
2413 TextClick {
2415 target: DomNodeId,
2416 position: LogicalPosition,
2417 click_count: u8,
2418 timestamp: Instant,
2419 },
2420 TextDragSelection {
2422 target: DomNodeId,
2423 start_position: LogicalPosition,
2424 current_position: LogicalPosition,
2425 is_dragging: bool,
2426 },
2427 ArrowKeyNavigation {
2429 target: DomNodeId,
2430 direction: ArrowDirection,
2431 extend_selection: bool, word_jump: bool, },
2434 KeyboardShortcut {
2436 target: DomNodeId,
2437 shortcut: KeyboardShortcut,
2438 },
2439 DeleteSelection {
2441 target: DomNodeId,
2442 forward: bool, },
2444}
2445
2446#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2448pub enum ArrowDirection {
2449 Left,
2450 Right,
2451 Up,
2452 Down,
2453}
2454
2455#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2457pub enum KeyboardShortcut {
2458 Copy, Cut, Paste, SelectAll, Undo, Redo, }
2465
2466#[derive(Debug, Clone, PartialEq)]
2468pub struct PostCallbackFilterResult {
2469 pub system_events: Vec<PostCallbackSystemEvent>,
2471}
2472
2473pub fn pre_callback_filter_internal_events<SM, FM>(
2477 events: &[SyntheticEvent],
2478 hit_test: Option<&FullHitTest>,
2479 keyboard_state: &crate::window::KeyboardState,
2480 mouse_state: &crate::window::MouseState,
2481 selection_manager: &SM,
2482 focus_manager: &FM,
2483) -> PreCallbackFilterResult
2484where
2485 SM: SelectionManagerQuery,
2486 FM: FocusManagerQuery,
2487{
2488 let ctx = FilterContext {
2489 hit_test,
2490 keyboard_state,
2491 mouse_state,
2492 click_count: selection_manager.get_click_count(),
2493 focused_node: focus_manager.get_focused_node_id(),
2494 drag_start_position: selection_manager.get_drag_start_position(),
2495 selection_manager,
2496 };
2497
2498 let (internal_events, user_events) = events.iter().fold(
2499 (Vec::new(), Vec::new()),
2500 |(mut internal, mut user), event| {
2501 match process_event_for_internal(&ctx, event) {
2502 Some(InternalEventAction::AddAndSkip(evt)) => {
2503 internal.push(evt);
2504 }
2505 Some(InternalEventAction::AddAndPass(evt)) => {
2506 internal.push(evt);
2507 user.push(event.clone());
2508 }
2509 None => {
2510 user.push(event.clone());
2511 }
2512 }
2513 (internal, user)
2514 },
2515 );
2516
2517 PreCallbackFilterResult {
2518 internal_events,
2519 user_events,
2520 }
2521}
2522
2523struct FilterContext<'a, SM> {
2525 hit_test: Option<&'a FullHitTest>,
2526 keyboard_state: &'a crate::window::KeyboardState,
2527 mouse_state: &'a crate::window::MouseState,
2528 click_count: u8,
2529 focused_node: Option<DomNodeId>,
2530 drag_start_position: Option<LogicalPosition>,
2531 selection_manager: &'a SM,
2532}
2533
2534fn process_event_for_internal<SM: SelectionManagerQuery>(
2536 ctx: &FilterContext<'_, SM>,
2537 event: &SyntheticEvent,
2538) -> Option<InternalEventAction> {
2539 match event.event_type {
2540 EventType::MouseDown => handle_mouse_down(event, ctx.hit_test, ctx.click_count, ctx.mouse_state),
2541 EventType::MouseOver => handle_mouse_over(
2542 event,
2543 ctx.hit_test,
2544 ctx.mouse_state,
2545 ctx.drag_start_position,
2546 ),
2547 EventType::KeyDown => handle_key_down(
2548 event,
2549 ctx.keyboard_state,
2550 ctx.selection_manager,
2551 ctx.focused_node,
2552 ),
2553 _ => None,
2554 }
2555}
2556
2557enum InternalEventAction {
2559 AddAndSkip(PreCallbackSystemEvent),
2561 AddAndPass(PreCallbackSystemEvent),
2563}
2564
2565fn get_first_hovered_node(hit_test: Option<&FullHitTest>) -> Option<DomNodeId> {
2567 let ht = hit_test?;
2568 let (dom_id, hit_data) = ht.hovered_nodes.iter().next()?;
2569 let node_id = hit_data.regular_hit_test_nodes.keys().next()?;
2570 Some(DomNodeId {
2571 dom: *dom_id,
2572 node: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
2573 })
2574}
2575
2576fn get_mouse_position_with_fallback(
2578 event: &SyntheticEvent,
2579 mouse_state: &crate::window::MouseState,
2580) -> LogicalPosition {
2581 match &event.data {
2582 EventData::Mouse(mouse_data) => mouse_data.position,
2583 _ => {
2584 mouse_state.cursor_position.get_position().unwrap_or(LogicalPosition::zero())
2588 }
2589 }
2590}
2591
2592fn handle_mouse_down(
2594 event: &SyntheticEvent,
2595 hit_test: Option<&FullHitTest>,
2596 click_count: u8,
2597 mouse_state: &crate::window::MouseState,
2598) -> Option<InternalEventAction> {
2599 let effective_click_count = if click_count == 0 { 1 } else { click_count };
2603
2604 if effective_click_count > 3 {
2605 return None;
2606 }
2607
2608 let target = get_first_hovered_node(hit_test)?;
2609 let position = get_mouse_position_with_fallback(event, mouse_state);
2610
2611 Some(InternalEventAction::AddAndPass(
2612 PreCallbackSystemEvent::TextClick {
2613 target,
2614 position,
2615 click_count: effective_click_count,
2616 timestamp: event.timestamp.clone(),
2617 },
2618 ))
2619}
2620
2621fn handle_mouse_over(
2623 event: &SyntheticEvent,
2624 hit_test: Option<&FullHitTest>,
2625 mouse_state: &crate::window::MouseState,
2626 drag_start_position: Option<LogicalPosition>,
2627) -> Option<InternalEventAction> {
2628 if !mouse_state.left_down {
2629 return None;
2630 }
2631
2632 let start_position = drag_start_position?;
2633
2634 #[cfg(feature = "std")]
2635 if let Some(ht) = hit_test {
2636 for (dom_id, hit_data) in &ht.hovered_nodes {
2637 eprintln!("[DEBUG] handle_mouse_over: dom {:?} has {} regular nodes, {} cursor nodes",
2638 dom_id, hit_data.regular_hit_test_nodes.len(), hit_data.cursor_hit_test_nodes.len());
2639 }
2640 }
2641
2642 let target = get_first_hovered_node(hit_test)?;
2643 let current_position = get_mouse_position_with_fallback(event, mouse_state);
2644
2645 #[cfg(feature = "std")]
2646 eprintln!("[DEBUG] handle_mouse_over: generating TextDragSelection for target {:?}", target);
2647
2648 Some(InternalEventAction::AddAndPass(
2649 PreCallbackSystemEvent::TextDragSelection {
2650 target,
2651 start_position,
2652 current_position,
2653 is_dragging: true,
2654 },
2655 ))
2656}
2657
2658fn handle_key_down<SM: SelectionManagerQuery>(
2660 event: &SyntheticEvent,
2661 keyboard_state: &crate::window::KeyboardState,
2662 selection_manager: &SM,
2663 focused_node: Option<DomNodeId>,
2664) -> Option<InternalEventAction> {
2665 use crate::window::VirtualKeyCode;
2666
2667 let target = focused_node?;
2668 let EventData::Keyboard(_) = &event.data else {
2669 return None;
2670 };
2671
2672 let ctrl = keyboard_state.ctrl_down();
2673 let shift = keyboard_state.shift_down();
2674 let vk = keyboard_state.current_virtual_keycode.as_ref()?;
2675
2676 if ctrl {
2678 let shortcut = match vk {
2679 VirtualKeyCode::C => Some(KeyboardShortcut::Copy),
2680 VirtualKeyCode::X => Some(KeyboardShortcut::Cut),
2681 VirtualKeyCode::V => Some(KeyboardShortcut::Paste),
2682 VirtualKeyCode::A => Some(KeyboardShortcut::SelectAll),
2683 VirtualKeyCode::Z if !shift => Some(KeyboardShortcut::Undo),
2684 VirtualKeyCode::Z if shift => Some(KeyboardShortcut::Redo),
2685 VirtualKeyCode::Y => Some(KeyboardShortcut::Redo),
2686 _ => None,
2687 };
2688 if let Some(shortcut) = shortcut {
2689 return Some(InternalEventAction::AddAndSkip(
2690 PreCallbackSystemEvent::KeyboardShortcut { target, shortcut },
2691 ));
2692 }
2693 }
2694
2695 let direction = match vk {
2697 VirtualKeyCode::Left => Some(ArrowDirection::Left),
2698 VirtualKeyCode::Up => Some(ArrowDirection::Up),
2699 VirtualKeyCode::Right => Some(ArrowDirection::Right),
2700 VirtualKeyCode::Down => Some(ArrowDirection::Down),
2701 _ => None,
2702 };
2703 if let Some(direction) = direction {
2704 return Some(InternalEventAction::AddAndSkip(
2705 PreCallbackSystemEvent::ArrowKeyNavigation {
2706 target,
2707 direction,
2708 extend_selection: shift,
2709 word_jump: ctrl,
2710 },
2711 ));
2712 }
2713
2714 if !selection_manager.has_selection() {
2716 return None;
2717 }
2718
2719 let forward = match vk {
2720 VirtualKeyCode::Back => Some(false),
2721 VirtualKeyCode::Delete => Some(true),
2722 _ => None,
2723 }?;
2724
2725 Some(InternalEventAction::AddAndSkip(
2726 PreCallbackSystemEvent::DeleteSelection { target, forward },
2727 ))
2728}
2729
2730pub trait SelectionManagerQuery {
2735 fn get_click_count(&self) -> u8;
2737
2738 fn get_drag_start_position(&self) -> Option<LogicalPosition>;
2740
2741 fn has_selection(&self) -> bool;
2743}
2744
2745pub trait FocusManagerQuery {
2750 fn get_focused_node_id(&self) -> Option<DomNodeId>;
2752}
2753
2754pub fn post_callback_filter_internal_events(
2756 prevent_default: bool,
2757 internal_events: &[PreCallbackSystemEvent],
2758 old_focus: Option<DomNodeId>,
2759 new_focus: Option<DomNodeId>,
2760) -> PostCallbackFilterResult {
2761 if prevent_default {
2762 let focus_event = (old_focus != new_focus).then_some(PostCallbackSystemEvent::FocusChanged);
2763 return PostCallbackFilterResult {
2764 system_events: focus_event.into_iter().collect(),
2765 };
2766 }
2767
2768 let event_actions = internal_events
2769 .iter()
2770 .filter_map(internal_event_to_system_event);
2771
2772 let focus_event = (old_focus != new_focus).then_some(PostCallbackSystemEvent::FocusChanged);
2773
2774 let system_events = core::iter::once(PostCallbackSystemEvent::ApplyTextInput)
2775 .chain(event_actions)
2776 .chain(focus_event)
2777 .collect();
2778
2779 PostCallbackFilterResult { system_events }
2780}
2781
2782fn internal_event_to_system_event(
2784 event: &PreCallbackSystemEvent,
2785) -> Option<PostCallbackSystemEvent> {
2786 use PostCallbackSystemEvent::*;
2787 use PreCallbackSystemEvent::*;
2788
2789 match event {
2790 TextClick { .. } | ArrowKeyNavigation { .. } | DeleteSelection { .. } => {
2791 Some(ScrollIntoView)
2792 }
2793 TextDragSelection { is_dragging, .. } => Some(if *is_dragging {
2794 StartAutoScrollTimer
2795 } else {
2796 CancelAutoScrollTimer
2797 }),
2798 KeyboardShortcut { shortcut, .. } => shortcut_to_system_event(*shortcut),
2799 }
2800}
2801
2802fn shortcut_to_system_event(shortcut: KeyboardShortcut) -> Option<PostCallbackSystemEvent> {
2804 use KeyboardShortcut::*;
2805 match shortcut {
2806 Cut | Paste | Undo | Redo => Some(PostCallbackSystemEvent::ScrollIntoView),
2807 Copy | SelectAll => None,
2808 }
2809}
2810
2811#[cfg(test)]
2812mod tests {
2813 use std::collections::BTreeMap;
2823
2824 use crate::{
2825 dom::{DomId, DomNodeId},
2826 events::*,
2827 geom::{LogicalPosition, LogicalRect, LogicalSize},
2828 id::{Node, NodeHierarchy, NodeId},
2829 styled_dom::NodeHierarchyItemId,
2830 task::{Instant, SystemTick},
2831 };
2832
2833 fn test_instant() -> Instant {
2835 Instant::Tick(SystemTick::new(0))
2836 }
2837
2838 fn create_test_hierarchy() -> NodeHierarchy {
2840 let nodes = vec![
2841 Node {
2842 parent: None,
2843 previous_sibling: None,
2844 next_sibling: None,
2845 last_child: Some(NodeId::new(1)),
2846 },
2847 Node {
2848 parent: Some(NodeId::new(0)),
2849 previous_sibling: None,
2850 next_sibling: None,
2851 last_child: Some(NodeId::new(2)),
2852 },
2853 Node {
2854 parent: Some(NodeId::new(1)),
2855 previous_sibling: None,
2856 next_sibling: None,
2857 last_child: None,
2858 },
2859 ];
2860 NodeHierarchy::new(nodes)
2861 }
2862
2863 #[test]
2864 fn test_event_source_enum() {
2865 let _user = EventSource::User;
2867 let _programmatic = EventSource::Programmatic;
2868 let _synthetic = EventSource::Synthetic;
2869 let _lifecycle = EventSource::Lifecycle;
2870 }
2871
2872 #[test]
2873 fn test_event_phase_enum() {
2874 let _capture = EventPhase::Capture;
2876 let _target = EventPhase::Target;
2877 let _bubble = EventPhase::Bubble;
2878
2879 assert_eq!(EventPhase::default(), EventPhase::Bubble);
2881 }
2882
2883 #[test]
2884 fn test_synthetic_event_creation() {
2885 let dom_id = DomId { inner: 1 };
2886 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2887 let target = DomNodeId {
2888 dom: dom_id,
2889 node: node_id,
2890 };
2891
2892 let event = SyntheticEvent::new(
2893 EventType::Click,
2894 EventSource::User,
2895 target,
2896 test_instant(),
2897 EventData::None,
2898 );
2899
2900 assert_eq!(event.event_type, EventType::Click);
2901 assert_eq!(event.source, EventSource::User);
2902 assert_eq!(event.phase, EventPhase::Target);
2903 assert_eq!(event.target, target);
2904 assert_eq!(event.current_target, target);
2905 assert!(!event.stopped);
2906 assert!(!event.stopped_immediate);
2907 assert!(!event.prevented_default);
2908 }
2909
2910 #[test]
2911 fn test_stop_propagation() {
2912 let dom_id = DomId { inner: 1 };
2913 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2914 let target = DomNodeId {
2915 dom: dom_id,
2916 node: node_id,
2917 };
2918
2919 let mut event = SyntheticEvent::new(
2920 EventType::Click,
2921 EventSource::User,
2922 target,
2923 test_instant(),
2924 EventData::None,
2925 );
2926
2927 assert!(!event.is_propagation_stopped());
2928
2929 event.stop_propagation();
2930
2931 assert!(event.is_propagation_stopped());
2932 assert!(!event.is_immediate_propagation_stopped());
2933 }
2934
2935 #[test]
2936 fn test_stop_immediate_propagation() {
2937 let dom_id = DomId { inner: 1 };
2938 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2939 let target = DomNodeId {
2940 dom: dom_id,
2941 node: node_id,
2942 };
2943
2944 let mut event = SyntheticEvent::new(
2945 EventType::Click,
2946 EventSource::User,
2947 target,
2948 test_instant(),
2949 EventData::None,
2950 );
2951
2952 event.stop_immediate_propagation();
2953
2954 assert!(event.is_propagation_stopped());
2955 assert!(event.is_immediate_propagation_stopped());
2956 }
2957
2958 #[test]
2959 fn test_prevent_default() {
2960 let dom_id = DomId { inner: 1 };
2961 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2962 let target = DomNodeId {
2963 dom: dom_id,
2964 node: node_id,
2965 };
2966
2967 let mut event = SyntheticEvent::new(
2968 EventType::Click,
2969 EventSource::User,
2970 target,
2971 test_instant(),
2972 EventData::None,
2973 );
2974
2975 assert!(!event.is_default_prevented());
2976
2977 event.prevent_default();
2978
2979 assert!(event.is_default_prevented());
2980 }
2981
2982 #[test]
2983 fn test_get_dom_path_single_node() {
2984 let hierarchy = NodeHierarchy::new(vec![Node {
2985 parent: None,
2986 previous_sibling: None,
2987 next_sibling: None,
2988 last_child: None,
2989 }]);
2990
2991 let target = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2992 let path = get_dom_path(&hierarchy, target);
2993
2994 assert_eq!(path.len(), 1);
2995 assert_eq!(path[0], NodeId::new(0));
2996 }
2997
2998 #[test]
2999 fn test_get_dom_path_three_nodes() {
3000 let hierarchy = create_test_hierarchy();
3001
3002 let target = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(2)));
3004 let path = get_dom_path(&hierarchy, target);
3005
3006 assert_eq!(path.len(), 3);
3007 assert_eq!(path[0], NodeId::new(0)); assert_eq!(path[1], NodeId::new(1)); assert_eq!(path[2], NodeId::new(2)); }
3011
3012 #[test]
3013 fn test_get_dom_path_middle_node() {
3014 let hierarchy = create_test_hierarchy();
3015
3016 let target = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(1)));
3018 let path = get_dom_path(&hierarchy, target);
3019
3020 assert_eq!(path.len(), 2);
3021 assert_eq!(path[0], NodeId::new(0)); assert_eq!(path[1], NodeId::new(1)); }
3024
3025 #[test]
3026 fn test_propagate_event_empty_callbacks() {
3027 let hierarchy = create_test_hierarchy();
3028 let dom_id = DomId { inner: 1 };
3029 let target_node = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(2)));
3030 let target = DomNodeId {
3031 dom: dom_id,
3032 node: target_node,
3033 };
3034
3035 let mut event = SyntheticEvent::new(
3036 EventType::Click,
3037 EventSource::User,
3038 target,
3039 test_instant(),
3040 EventData::None,
3041 );
3042
3043 let callbacks: BTreeMap<NodeId, Vec<EventFilter>> = BTreeMap::new();
3044 let result = propagate_event(&mut event, &hierarchy, &callbacks);
3045
3046 assert_eq!(result.callbacks_to_invoke.len(), 0);
3048 assert!(!result.default_prevented);
3049 }
3050
3051 #[test]
3052 fn test_mouse_event_data_creation() {
3053 let mouse_data = MouseEventData {
3054 position: LogicalPosition { x: 100.0, y: 200.0 },
3055 button: MouseButton::Left,
3056 buttons: 1,
3057 modifiers: KeyModifiers::new(),
3058 };
3059
3060 assert_eq!(mouse_data.position.x, 100.0);
3061 assert_eq!(mouse_data.position.y, 200.0);
3062 assert_eq!(mouse_data.button, MouseButton::Left);
3063 }
3064
3065 #[test]
3066 fn test_key_modifiers() {
3067 let modifiers = KeyModifiers::new().with_shift().with_ctrl();
3068
3069 assert!(modifiers.shift);
3070 assert!(modifiers.ctrl);
3071 assert!(!modifiers.alt);
3072 assert!(!modifiers.meta);
3073 assert!(!modifiers.is_empty());
3074
3075 let empty = KeyModifiers::new();
3076 assert!(empty.is_empty());
3077 }
3078
3079 #[test]
3080 fn test_lifecycle_event_mount() {
3081 let dom_id = DomId { inner: 1 };
3082 let old_hierarchy = None;
3083 let new_hierarchy = create_test_hierarchy();
3084 let old_layout = None;
3085 let new_layout = {
3086 let mut map = BTreeMap::new();
3087 map.insert(
3088 NodeId::new(0),
3089 LogicalRect {
3090 origin: LogicalPosition { x: 0.0, y: 0.0 },
3091 size: LogicalSize {
3092 width: 100.0,
3093 height: 100.0,
3094 },
3095 },
3096 );
3097 map.insert(
3098 NodeId::new(1),
3099 LogicalRect {
3100 origin: LogicalPosition { x: 10.0, y: 10.0 },
3101 size: LogicalSize {
3102 width: 80.0,
3103 height: 80.0,
3104 },
3105 },
3106 );
3107 map.insert(
3108 NodeId::new(2),
3109 LogicalRect {
3110 origin: LogicalPosition { x: 20.0, y: 20.0 },
3111 size: LogicalSize {
3112 width: 60.0,
3113 height: 60.0,
3114 },
3115 },
3116 );
3117 Some(map)
3118 };
3119
3120 let events = detect_lifecycle_events(
3121 dom_id,
3122 dom_id,
3123 old_hierarchy,
3124 Some(&new_hierarchy),
3125 old_layout.as_ref(),
3126 new_layout.as_ref(),
3127 test_instant(),
3128 );
3129
3130 assert_eq!(events.len(), 3);
3132
3133 for event in &events {
3134 assert_eq!(event.event_type, EventType::Mount);
3135 assert_eq!(event.source, EventSource::Lifecycle);
3136
3137 if let EventData::Lifecycle(data) = &event.data {
3138 assert_eq!(data.reason, LifecycleReason::InitialMount);
3139 assert!(data.previous_bounds.is_none());
3140 } else {
3141 panic!("Expected Lifecycle event data");
3142 }
3143 }
3144 }
3145
3146 #[test]
3147 fn test_lifecycle_event_unmount() {
3148 let dom_id = DomId { inner: 1 };
3149 let old_hierarchy = create_test_hierarchy();
3150 let new_hierarchy = None;
3151 let old_layout = {
3152 let mut map = BTreeMap::new();
3153 map.insert(
3154 NodeId::new(0),
3155 LogicalRect {
3156 origin: LogicalPosition { x: 0.0, y: 0.0 },
3157 size: LogicalSize {
3158 width: 100.0,
3159 height: 100.0,
3160 },
3161 },
3162 );
3163 Some(map)
3164 };
3165 let new_layout = None;
3166
3167 let events = detect_lifecycle_events(
3168 dom_id,
3169 dom_id,
3170 Some(&old_hierarchy),
3171 new_hierarchy,
3172 old_layout.as_ref(),
3173 new_layout,
3174 test_instant(),
3175 );
3176
3177 assert_eq!(events.len(), 3);
3179
3180 for event in &events {
3181 assert_eq!(event.event_type, EventType::Unmount);
3182 assert_eq!(event.source, EventSource::Lifecycle);
3183 }
3184 }
3185
3186 #[test]
3187 fn test_lifecycle_event_resize() {
3188 let dom_id = DomId { inner: 1 };
3189 let hierarchy = create_test_hierarchy();
3190
3191 let old_layout = {
3192 let mut map = BTreeMap::new();
3193 map.insert(
3194 NodeId::new(0),
3195 LogicalRect {
3196 origin: LogicalPosition { x: 0.0, y: 0.0 },
3197 size: LogicalSize {
3198 width: 100.0,
3199 height: 100.0,
3200 },
3201 },
3202 );
3203 Some(map)
3204 };
3205
3206 let new_layout = {
3207 let mut map = BTreeMap::new();
3208 map.insert(
3209 NodeId::new(0),
3210 LogicalRect {
3211 origin: LogicalPosition { x: 0.0, y: 0.0 },
3212 size: LogicalSize {
3213 width: 200.0,
3214 height: 100.0,
3215 }, },
3217 );
3218 Some(map)
3219 };
3220
3221 let events = detect_lifecycle_events(
3222 dom_id,
3223 dom_id,
3224 Some(&hierarchy),
3225 Some(&hierarchy),
3226 old_layout.as_ref(),
3227 new_layout.as_ref(),
3228 test_instant(),
3229 );
3230
3231 assert_eq!(events.len(), 1);
3233 assert_eq!(events[0].event_type, EventType::Resize);
3234 assert_eq!(events[0].source, EventSource::Lifecycle);
3235
3236 if let EventData::Lifecycle(data) = &events[0].data {
3237 assert_eq!(data.reason, LifecycleReason::Resize);
3238 assert!(data.previous_bounds.is_some());
3239 assert_eq!(data.current_bounds.size.width, 200.0);
3240 } else {
3241 panic!("Expected Lifecycle event data");
3242 }
3243 }
3244
3245 #[test]
3246 fn test_event_filter_hover_match() {
3247 let dom_id = DomId { inner: 1 };
3248 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
3249 let target = DomNodeId {
3250 dom: dom_id,
3251 node: node_id,
3252 };
3253
3254 let _event = SyntheticEvent::new(
3255 EventType::MouseDown,
3256 EventSource::User,
3257 target,
3258 test_instant(),
3259 EventData::Mouse(MouseEventData {
3260 position: LogicalPosition { x: 0.0, y: 0.0 },
3261 button: MouseButton::Left,
3262 buttons: 1,
3263 modifiers: KeyModifiers::new(),
3264 }),
3265 );
3266
3267 }
3271}