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 MouseOut,
448 MouseDown,
450 MouseUp,
452 Click,
454 DoubleClick,
456 ContextMenu,
458
459 KeyDown,
462 KeyUp,
464 KeyPress,
466
467 CompositionStart,
470 CompositionUpdate,
472 CompositionEnd,
474
475 Focus,
478 Blur,
480 FocusIn,
482 FocusOut,
484
485 Input,
488 Change,
490 Submit,
492 Reset,
494 Invalid,
496
497 Scroll,
500 ScrollStart,
502 ScrollEnd,
504
505 DragStart,
508 Drag,
510 DragEnd,
512 DragEnter,
514 DragOver,
516 DragLeave,
518 Drop,
520
521 TouchStart,
524 TouchMove,
526 TouchEnd,
528 TouchCancel,
530
531 LongPress,
534 SwipeLeft,
536 SwipeRight,
538 SwipeUp,
540 SwipeDown,
542 PinchIn,
544 PinchOut,
546 RotateClockwise,
548 RotateCounterClockwise,
550
551 Copy,
554 Cut,
556 Paste,
558
559 Play,
562 Pause,
564 Ended,
566 TimeUpdate,
568 VolumeChange,
570 MediaError,
572
573 Mount,
576 Unmount,
578 Update,
580 Resize,
582
583 WindowResize,
586 WindowMove,
588 WindowClose,
590 WindowFocusIn,
592 WindowFocusOut,
594 ThemeChange,
596 WindowDpiChanged,
598 WindowMonitorChanged,
600
601 MonitorConnected,
604 MonitorDisconnected,
606
607 FileHover,
610 FileDrop,
612 FileHoverCancel,
614}
615
616#[derive(Debug, Clone, PartialEq)]
621pub struct SyntheticEvent {
622 pub event_type: EventType,
624
625 pub source: EventSource,
627
628 pub phase: EventPhase,
630
631 pub target: DomNodeId,
633
634 pub current_target: DomNodeId,
636
637 pub timestamp: Instant,
639
640 pub data: EventData,
642
643 pub stopped: bool,
645
646 pub stopped_immediate: bool,
648
649 pub prevented_default: bool,
651}
652
653impl SyntheticEvent {
654 pub fn new(
659 event_type: EventType,
660 source: EventSource,
661 target: DomNodeId,
662 timestamp: Instant,
663 data: EventData,
664 ) -> Self {
665 Self {
666 event_type,
667 source,
668 phase: EventPhase::Target,
669 target,
670 current_target: target,
671 timestamp,
672 data,
673 stopped: false,
674 stopped_immediate: false,
675 prevented_default: false,
676 }
677 }
678
679 pub fn stop_propagation(&mut self) {
684 self.stopped = true;
685 }
686
687 pub fn stop_immediate_propagation(&mut self) {
692 self.stopped_immediate = true;
693 self.stopped = true;
694 }
695
696 pub fn prevent_default(&mut self) {
701 self.prevented_default = true;
702 }
703
704 pub fn is_propagation_stopped(&self) -> bool {
706 self.stopped
707 }
708
709 pub fn is_immediate_propagation_stopped(&self) -> bool {
711 self.stopped_immediate
712 }
713
714 pub fn is_default_prevented(&self) -> bool {
716 self.prevented_default
717 }
718}
719
720#[derive(Debug, Clone)]
724pub struct PropagationResult {
725 pub callbacks_to_invoke: Vec<(NodeId, EventFilter)>,
727 pub default_prevented: bool,
729}
730
731pub fn get_dom_path(
738 node_hierarchy: &crate::id::NodeHierarchy,
739 target_node: NodeHierarchyItemId,
740) -> Vec<NodeId> {
741 let mut path = Vec::new();
742 let target_node_id = match target_node.into_crate_internal() {
743 Some(id) => id,
744 None => return path,
745 };
746
747 let hier_ref = node_hierarchy.as_ref();
748
749 let mut current = Some(target_node_id);
751 while let Some(node_id) = current {
752 path.push(node_id);
753 current = hier_ref.get(node_id).and_then(|node| node.parent);
754 }
755
756 path.reverse();
758 path
759}
760
761pub fn propagate_event(
771 event: &mut SyntheticEvent,
772 node_hierarchy: &crate::id::NodeHierarchy,
773 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
774) -> PropagationResult {
775 let path = get_dom_path(node_hierarchy, event.target.node);
776 if path.is_empty() {
777 return PropagationResult::default();
778 }
779
780 let ancestors = &path[..path.len().saturating_sub(1)];
781 let target_node_id = *path.last().unwrap();
782
783 let mut result = PropagationResult::default();
784
785 propagate_phase(
787 event,
788 ancestors.iter().copied(),
789 EventPhase::Capture,
790 callbacks,
791 &mut result,
792 );
793
794 if !event.stopped {
796 propagate_target_phase(event, target_node_id, callbacks, &mut result);
797 }
798
799 if !event.stopped {
801 propagate_phase(
802 event,
803 ancestors.iter().rev().copied(),
804 EventPhase::Bubble,
805 callbacks,
806 &mut result,
807 );
808 }
809
810 result.default_prevented = event.prevented_default;
811 result
812}
813
814fn propagate_phase(
816 event: &mut SyntheticEvent,
817 nodes: impl Iterator<Item = NodeId>,
818 phase: EventPhase,
819 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
820 result: &mut PropagationResult,
821) {
822 event.phase = phase;
823
824 for node_id in nodes {
825 if event.stopped_immediate || event.stopped {
826 return;
827 }
828
829 event.current_target = DomNodeId {
830 dom: event.target.dom,
831 node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
832 };
833
834 collect_matching_callbacks(event, node_id, phase, callbacks, result);
835 }
836}
837
838fn propagate_target_phase(
840 event: &mut SyntheticEvent,
841 target_node_id: NodeId,
842 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
843 result: &mut PropagationResult,
844) {
845 event.phase = EventPhase::Target;
846 event.current_target = event.target;
847
848 collect_matching_callbacks(event, target_node_id, EventPhase::Target, callbacks, result);
849}
850
851fn collect_matching_callbacks(
853 event: &SyntheticEvent,
854 node_id: NodeId,
855 phase: EventPhase,
856 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
857 result: &mut PropagationResult,
858) {
859 let Some(node_callbacks) = callbacks.get(&node_id) else {
860 return;
861 };
862
863 let matching = node_callbacks
864 .iter()
865 .take_while(|_| !event.stopped_immediate)
866 .filter(|filter| matches_filter_phase(filter, event, phase))
867 .map(|filter| (node_id, *filter));
868
869 result.callbacks_to_invoke.extend(matching);
870}
871
872impl Default for PropagationResult {
873 fn default() -> Self {
874 Self {
875 callbacks_to_invoke: Vec::new(),
876 default_prevented: false,
877 }
878 }
879}
880
881#[derive(Debug, Clone, PartialEq, Eq, Hash)]
900#[repr(C, u8)]
901pub enum DefaultAction {
902 FocusNext,
904 FocusPrevious,
906 FocusFirst,
908 FocusLast,
910 ClearFocus,
912 ActivateFocusedElement {
915 target: DomNodeId,
916 },
917 SubmitForm {
919 form_node: DomNodeId,
920 },
921 CloseModal {
923 modal_node: DomNodeId,
924 },
925 ScrollFocusedContainer {
927 direction: ScrollDirection,
928 amount: ScrollAmount,
929 },
930 SelectAllText,
932 None,
934}
935
936#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
938#[repr(C)]
939pub enum ScrollAmount {
940 Line,
942 Page,
944 Document,
946}
947
948#[derive(Debug, Clone)]
955#[repr(C)]
956pub struct DefaultActionResult {
957 pub action: DefaultAction,
959 pub prevented: bool,
961}
962
963impl Default for DefaultActionResult {
964 fn default() -> Self {
965 Self {
966 action: DefaultAction::None,
967 prevented: false,
968 }
969 }
970}
971
972impl DefaultActionResult {
973 pub fn new(action: DefaultAction) -> Self {
975 Self {
976 action,
977 prevented: false,
978 }
979 }
980
981 pub fn prevented() -> Self {
983 Self {
984 action: DefaultAction::None,
985 prevented: true,
986 }
987 }
988
989 pub fn has_action(&self) -> bool {
991 !self.prevented && !matches!(self.action, DefaultAction::None)
992 }
993}
994
995pub trait ActivationBehavior {
1007 fn has_activation_behavior(&self) -> bool;
1009
1010 fn is_activatable(&self) -> bool;
1013}
1014
1015pub trait Focusable {
1017 fn get_tabindex(&self) -> Option<i32>;
1019
1020 fn is_focusable(&self) -> bool;
1022
1023 fn is_in_tab_order(&self) -> bool {
1025 match self.get_tabindex() {
1026 None => self.is_naturally_focusable(),
1027 Some(i) => i >= 0,
1028 }
1029 }
1030
1031 fn is_naturally_focusable(&self) -> bool;
1034}
1035
1036fn matches_filter_phase(
1041 filter: &EventFilter,
1042 event: &SyntheticEvent,
1043 current_phase: EventPhase,
1044) -> bool {
1045 match filter {
1049 EventFilter::Hover(hover_filter) => {
1050 matches_hover_filter(hover_filter, event, current_phase)
1051 }
1052 EventFilter::Focus(focus_filter) => {
1053 matches_focus_filter(focus_filter, event, current_phase)
1054 }
1055 EventFilter::Window(window_filter) => {
1056 matches_window_filter(window_filter, event, current_phase)
1057 }
1058 EventFilter::Not(_) => {
1059 false
1061 }
1062 EventFilter::Component(_) | EventFilter::Application(_) => {
1063 false
1065 }
1066 }
1067}
1068
1069fn matches_hover_filter(
1071 filter: &HoverEventFilter,
1072 event: &SyntheticEvent,
1073 _phase: EventPhase,
1074) -> bool {
1075 use HoverEventFilter::*;
1076
1077 match (filter, &event.event_type) {
1078 (MouseOver, EventType::MouseOver) => true,
1079 (MouseDown, EventType::MouseDown) => true,
1080 (LeftMouseDown, EventType::MouseDown) => {
1081 if let EventData::Mouse(mouse_data) = &event.data {
1083 mouse_data.button == MouseButton::Left
1084 } else {
1085 false
1086 }
1087 }
1088 (RightMouseDown, EventType::MouseDown) => {
1089 if let EventData::Mouse(mouse_data) = &event.data {
1090 mouse_data.button == MouseButton::Right
1091 } else {
1092 false
1093 }
1094 }
1095 (MiddleMouseDown, EventType::MouseDown) => {
1096 if let EventData::Mouse(mouse_data) = &event.data {
1097 mouse_data.button == MouseButton::Middle
1098 } else {
1099 false
1100 }
1101 }
1102 (MouseUp, EventType::MouseUp) => true,
1103 (LeftMouseUp, EventType::MouseUp) => {
1104 if let EventData::Mouse(mouse_data) = &event.data {
1105 mouse_data.button == MouseButton::Left
1106 } else {
1107 false
1108 }
1109 }
1110 (RightMouseUp, EventType::MouseUp) => {
1111 if let EventData::Mouse(mouse_data) = &event.data {
1112 mouse_data.button == MouseButton::Right
1113 } else {
1114 false
1115 }
1116 }
1117 (MiddleMouseUp, EventType::MouseUp) => {
1118 if let EventData::Mouse(mouse_data) = &event.data {
1119 mouse_data.button == MouseButton::Middle
1120 } else {
1121 false
1122 }
1123 }
1124 (MouseEnter, EventType::MouseEnter) => true,
1125 (MouseLeave, EventType::MouseLeave) => true,
1126 (Scroll, EventType::Scroll) => true,
1127 (ScrollStart, EventType::ScrollStart) => true,
1128 (ScrollEnd, EventType::ScrollEnd) => true,
1129 (TextInput, EventType::Input) => true,
1130 (VirtualKeyDown, EventType::KeyDown) => true,
1131 (VirtualKeyUp, EventType::KeyUp) => true,
1132 (HoveredFile, EventType::FileHover) => true,
1133 (DroppedFile, EventType::FileDrop) => true,
1134 (HoveredFileCancelled, EventType::FileHoverCancel) => true,
1135 (TouchStart, EventType::TouchStart) => true,
1136 (TouchMove, EventType::TouchMove) => true,
1137 (TouchEnd, EventType::TouchEnd) => true,
1138 (TouchCancel, EventType::TouchCancel) => true,
1139 (DragStart, EventType::DragStart) => true,
1140 (Drag, EventType::Drag) => true,
1141 (DragEnd, EventType::DragEnd) => true,
1142 (DragEnter, EventType::DragEnter) => true,
1143 (DragOver, EventType::DragOver) => true,
1144 (DragLeave, EventType::DragLeave) => true,
1145 (Drop, EventType::Drop) => true,
1146 (DoubleClick, EventType::DoubleClick) => true,
1147 _ => false,
1148 }
1149}
1150
1151fn matches_focus_filter(
1153 filter: &FocusEventFilter,
1154 event: &SyntheticEvent,
1155 _phase: EventPhase,
1156) -> bool {
1157 use FocusEventFilter::*;
1158
1159 match (filter, &event.event_type) {
1160 (MouseOver, EventType::MouseOver) => true,
1161 (MouseDown, EventType::MouseDown) => true,
1162 (LeftMouseDown, EventType::MouseDown) => {
1163 if let EventData::Mouse(mouse_data) = &event.data {
1164 mouse_data.button == MouseButton::Left
1165 } else {
1166 false
1167 }
1168 }
1169 (RightMouseDown, EventType::MouseDown) => {
1170 if let EventData::Mouse(mouse_data) = &event.data {
1171 mouse_data.button == MouseButton::Right
1172 } else {
1173 false
1174 }
1175 }
1176 (MiddleMouseDown, EventType::MouseDown) => {
1177 if let EventData::Mouse(mouse_data) = &event.data {
1178 mouse_data.button == MouseButton::Middle
1179 } else {
1180 false
1181 }
1182 }
1183 (MouseUp, EventType::MouseUp) => true,
1184 (LeftMouseUp, EventType::MouseUp) => {
1185 if let EventData::Mouse(mouse_data) = &event.data {
1186 mouse_data.button == MouseButton::Left
1187 } else {
1188 false
1189 }
1190 }
1191 (RightMouseUp, EventType::MouseUp) => {
1192 if let EventData::Mouse(mouse_data) = &event.data {
1193 mouse_data.button == MouseButton::Right
1194 } else {
1195 false
1196 }
1197 }
1198 (MiddleMouseUp, EventType::MouseUp) => {
1199 if let EventData::Mouse(mouse_data) = &event.data {
1200 mouse_data.button == MouseButton::Middle
1201 } else {
1202 false
1203 }
1204 }
1205 (MouseEnter, EventType::MouseEnter) => true,
1206 (MouseLeave, EventType::MouseLeave) => true,
1207 (Scroll, EventType::Scroll) => true,
1208 (ScrollStart, EventType::ScrollStart) => true,
1209 (ScrollEnd, EventType::ScrollEnd) => true,
1210 (TextInput, EventType::Input) => true,
1211 (VirtualKeyDown, EventType::KeyDown) => true,
1212 (VirtualKeyUp, EventType::KeyUp) => true,
1213 (FocusReceived, EventType::Focus) => true,
1214 (FocusLost, EventType::Blur) => true,
1215 (DragStart, EventType::DragStart) => true,
1216 (Drag, EventType::Drag) => true,
1217 (DragEnd, EventType::DragEnd) => true,
1218 (DragEnter, EventType::DragEnter) => true,
1219 (DragOver, EventType::DragOver) => true,
1220 (DragLeave, EventType::DragLeave) => true,
1221 (Drop, EventType::Drop) => true,
1222 _ => false,
1223 }
1224}
1225
1226fn matches_window_filter(
1228 filter: &WindowEventFilter,
1229 event: &SyntheticEvent,
1230 _phase: EventPhase,
1231) -> bool {
1232 use WindowEventFilter::*;
1233
1234 match (filter, &event.event_type) {
1235 (MouseOver, EventType::MouseOver) => true,
1236 (MouseDown, EventType::MouseDown) => true,
1237 (LeftMouseDown, EventType::MouseDown) => {
1238 if let EventData::Mouse(mouse_data) = &event.data {
1239 mouse_data.button == MouseButton::Left
1240 } else {
1241 false
1242 }
1243 }
1244 (RightMouseDown, EventType::MouseDown) => {
1245 if let EventData::Mouse(mouse_data) = &event.data {
1246 mouse_data.button == MouseButton::Right
1247 } else {
1248 false
1249 }
1250 }
1251 (MiddleMouseDown, EventType::MouseDown) => {
1252 if let EventData::Mouse(mouse_data) = &event.data {
1253 mouse_data.button == MouseButton::Middle
1254 } else {
1255 false
1256 }
1257 }
1258 (MouseUp, EventType::MouseUp) => true,
1259 (LeftMouseUp, EventType::MouseUp) => {
1260 if let EventData::Mouse(mouse_data) = &event.data {
1261 mouse_data.button == MouseButton::Left
1262 } else {
1263 false
1264 }
1265 }
1266 (RightMouseUp, EventType::MouseUp) => {
1267 if let EventData::Mouse(mouse_data) = &event.data {
1268 mouse_data.button == MouseButton::Right
1269 } else {
1270 false
1271 }
1272 }
1273 (MiddleMouseUp, EventType::MouseUp) => {
1274 if let EventData::Mouse(mouse_data) = &event.data {
1275 mouse_data.button == MouseButton::Middle
1276 } else {
1277 false
1278 }
1279 }
1280 (MouseEnter, EventType::MouseEnter) => true,
1281 (MouseLeave, EventType::MouseLeave) => true,
1282 (Scroll, EventType::Scroll) => true,
1283 (ScrollStart, EventType::ScrollStart) => true,
1284 (ScrollEnd, EventType::ScrollEnd) => true,
1285 (TextInput, EventType::Input) => true,
1286 (VirtualKeyDown, EventType::KeyDown) => true,
1287 (VirtualKeyUp, EventType::KeyUp) => true,
1288 (HoveredFile, EventType::FileHover) => true,
1289 (DroppedFile, EventType::FileDrop) => true,
1290 (HoveredFileCancelled, EventType::FileHoverCancel) => true,
1291 (Resized, EventType::WindowResize) => true,
1292 (Moved, EventType::WindowMove) => true,
1293 (TouchStart, EventType::TouchStart) => true,
1294 (TouchMove, EventType::TouchMove) => true,
1295 (TouchEnd, EventType::TouchEnd) => true,
1296 (TouchCancel, EventType::TouchCancel) => true,
1297 (FocusReceived, EventType::Focus) => true,
1298 (FocusLost, EventType::Blur) => true,
1299 (CloseRequested, EventType::WindowClose) => true,
1300 (ThemeChanged, EventType::ThemeChange) => true,
1301 (WindowFocusReceived, EventType::WindowFocusIn) => true,
1302 (WindowFocusLost, EventType::WindowFocusOut) => true,
1303 (DragStart, EventType::DragStart) => true,
1304 (Drag, EventType::Drag) => true,
1305 (DragEnd, EventType::DragEnd) => true,
1306 (DragEnter, EventType::DragEnter) => true,
1307 (DragOver, EventType::DragOver) => true,
1308 (DragLeave, EventType::DragLeave) => true,
1309 (Drop, EventType::Drop) => true,
1310 _ => false,
1311 }
1312}
1313
1314pub fn detect_lifecycle_events(
1324 old_dom_id: DomId,
1325 new_dom_id: DomId,
1326 old_hierarchy: Option<&crate::id::NodeHierarchy>,
1327 new_hierarchy: Option<&crate::id::NodeHierarchy>,
1328 old_layout: Option<&BTreeMap<NodeId, LogicalRect>>,
1329 new_layout: Option<&BTreeMap<NodeId, LogicalRect>>,
1330 timestamp: Instant,
1331) -> Vec<SyntheticEvent> {
1332 let old_nodes = collect_node_ids(old_hierarchy);
1333 let new_nodes = collect_node_ids(new_hierarchy);
1334
1335 let mut events = Vec::new();
1336
1337 if let Some(layout) = new_layout {
1339 for &node_id in new_nodes.difference(&old_nodes) {
1340 events.push(create_mount_event(node_id, new_dom_id, layout, ×tamp));
1341 }
1342 }
1343
1344 if let Some(layout) = old_layout {
1346 for &node_id in old_nodes.difference(&new_nodes) {
1347 events.push(create_unmount_event(
1348 node_id, old_dom_id, layout, ×tamp,
1349 ));
1350 }
1351 }
1352
1353 if let (Some(old_l), Some(new_l)) = (old_layout, new_layout) {
1355 for &node_id in old_nodes.intersection(&new_nodes) {
1356 if let Some(ev) = create_resize_event(node_id, new_dom_id, old_l, new_l, ×tamp) {
1357 events.push(ev);
1358 }
1359 }
1360 }
1361
1362 events
1363}
1364
1365fn collect_node_ids(hierarchy: Option<&crate::id::NodeHierarchy>) -> BTreeSet<NodeId> {
1366 hierarchy
1367 .map(|h| h.as_ref().linear_iter().collect())
1368 .unwrap_or_default()
1369}
1370
1371fn create_lifecycle_event(
1372 event_type: EventType,
1373 node_id: NodeId,
1374 dom_id: DomId,
1375 timestamp: &Instant,
1376 data: LifecycleEventData,
1377) -> SyntheticEvent {
1378 let dom_node_id = DomNodeId {
1379 dom: dom_id,
1380 node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
1381 };
1382 SyntheticEvent {
1383 event_type,
1384 source: EventSource::Lifecycle,
1385 phase: EventPhase::Target,
1386 target: dom_node_id,
1387 current_target: dom_node_id,
1388 timestamp: timestamp.clone(),
1389 data: EventData::Lifecycle(data),
1390 stopped: false,
1391 stopped_immediate: false,
1392 prevented_default: false,
1393 }
1394}
1395
1396fn create_mount_event(
1397 node_id: NodeId,
1398 dom_id: DomId,
1399 layout: &BTreeMap<NodeId, LogicalRect>,
1400 timestamp: &Instant,
1401) -> SyntheticEvent {
1402 let current_bounds = layout.get(&node_id).copied().unwrap_or(LogicalRect::zero());
1403 create_lifecycle_event(
1404 EventType::Mount,
1405 node_id,
1406 dom_id,
1407 timestamp,
1408 LifecycleEventData {
1409 reason: LifecycleReason::InitialMount,
1410 previous_bounds: None,
1411 current_bounds,
1412 },
1413 )
1414}
1415
1416fn create_unmount_event(
1417 node_id: NodeId,
1418 dom_id: DomId,
1419 layout: &BTreeMap<NodeId, LogicalRect>,
1420 timestamp: &Instant,
1421) -> SyntheticEvent {
1422 let previous_bounds = layout.get(&node_id).copied().unwrap_or(LogicalRect::zero());
1423 create_lifecycle_event(
1424 EventType::Unmount,
1425 node_id,
1426 dom_id,
1427 timestamp,
1428 LifecycleEventData {
1429 reason: LifecycleReason::InitialMount,
1430 previous_bounds: Some(previous_bounds),
1431 current_bounds: LogicalRect::zero(),
1432 },
1433 )
1434}
1435
1436fn create_resize_event(
1437 node_id: NodeId,
1438 dom_id: DomId,
1439 old_layout: &BTreeMap<NodeId, LogicalRect>,
1440 new_layout: &BTreeMap<NodeId, LogicalRect>,
1441 timestamp: &Instant,
1442) -> Option<SyntheticEvent> {
1443 let old_bounds = *old_layout.get(&node_id)?;
1444 let new_bounds = *new_layout.get(&node_id)?;
1445
1446 if old_bounds.size == new_bounds.size {
1447 return None;
1448 }
1449
1450 Some(create_lifecycle_event(
1451 EventType::Resize,
1452 node_id,
1453 dom_id,
1454 timestamp,
1455 LifecycleEventData {
1456 reason: LifecycleReason::Resize,
1457 previous_bounds: Some(old_bounds),
1458 current_bounds: new_bounds,
1459 },
1460 ))
1461}
1462
1463#[derive(Debug, Clone, Default)]
1468pub struct LifecycleEventResult {
1469 pub events: Vec<SyntheticEvent>,
1471 pub node_id_mapping: crate::FastHashMap<NodeId, NodeId>,
1474}
1475
1476pub fn detect_lifecycle_events_with_reconciliation(
1530 dom_id: DomId,
1531 old_node_data: &[crate::dom::NodeData],
1532 new_node_data: &[crate::dom::NodeData],
1533 old_layout: &crate::FastHashMap<NodeId, LogicalRect>,
1534 new_layout: &crate::FastHashMap<NodeId, LogicalRect>,
1535 timestamp: Instant,
1536) -> LifecycleEventResult {
1537 let diff_result = crate::diff::reconcile_dom(
1538 old_node_data,
1539 new_node_data,
1540 old_layout,
1541 new_layout,
1542 dom_id,
1543 timestamp,
1544 );
1545
1546 LifecycleEventResult {
1547 events: diff_result.events,
1548 node_id_mapping: crate::diff::create_migration_map(&diff_result.node_moves),
1549 }
1550}
1551
1552#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1556#[repr(C)]
1557pub enum HoverEventFilter {
1558 MouseOver,
1560 MouseDown,
1562 LeftMouseDown,
1564 RightMouseDown,
1566 MiddleMouseDown,
1568 MouseUp,
1570 LeftMouseUp,
1572 RightMouseUp,
1574 MiddleMouseUp,
1576 MouseEnter,
1578 MouseLeave,
1580 Scroll,
1582 ScrollStart,
1584 ScrollEnd,
1586 TextInput,
1588 VirtualKeyDown,
1590 VirtualKeyUp,
1592 HoveredFile,
1594 DroppedFile,
1596 HoveredFileCancelled,
1598 TouchStart,
1600 TouchMove,
1602 TouchEnd,
1604 TouchCancel,
1606 PenDown,
1608 PenMove,
1610 PenUp,
1612 PenEnter,
1614 PenLeave,
1616 DragStart,
1618 Drag,
1620 DragEnd,
1622 DragEnter,
1624 DragOver,
1626 DragLeave,
1628 Drop,
1630 DoubleClick,
1632 LongPress,
1634 SwipeLeft,
1636 SwipeRight,
1638 SwipeUp,
1640 SwipeDown,
1642 PinchIn,
1644 PinchOut,
1646 RotateClockwise,
1648 RotateCounterClockwise,
1650
1651 MouseOut,
1654
1655 FocusIn,
1658 FocusOut,
1660
1661 CompositionStart,
1664 CompositionUpdate,
1666 CompositionEnd,
1668
1669 #[doc(hidden)]
1671 SystemTextSingleClick,
1673 #[doc(hidden)]
1674 SystemTextDoubleClick,
1676 #[doc(hidden)]
1677 SystemTextTripleClick,
1679}
1680
1681impl HoverEventFilter {
1682 pub const fn is_system_internal(&self) -> bool {
1684 matches!(
1685 self,
1686 HoverEventFilter::SystemTextSingleClick
1687 | HoverEventFilter::SystemTextDoubleClick
1688 | HoverEventFilter::SystemTextTripleClick
1689 )
1690 }
1691
1692 pub fn to_focus_event_filter(&self) -> Option<FocusEventFilter> {
1693 match self {
1694 HoverEventFilter::MouseOver => Some(FocusEventFilter::MouseOver),
1695 HoverEventFilter::MouseDown => Some(FocusEventFilter::MouseDown),
1696 HoverEventFilter::LeftMouseDown => Some(FocusEventFilter::LeftMouseDown),
1697 HoverEventFilter::RightMouseDown => Some(FocusEventFilter::RightMouseDown),
1698 HoverEventFilter::MiddleMouseDown => Some(FocusEventFilter::MiddleMouseDown),
1699 HoverEventFilter::MouseUp => Some(FocusEventFilter::MouseUp),
1700 HoverEventFilter::LeftMouseUp => Some(FocusEventFilter::LeftMouseUp),
1701 HoverEventFilter::RightMouseUp => Some(FocusEventFilter::RightMouseUp),
1702 HoverEventFilter::MiddleMouseUp => Some(FocusEventFilter::MiddleMouseUp),
1703 HoverEventFilter::MouseEnter => Some(FocusEventFilter::MouseEnter),
1704 HoverEventFilter::MouseLeave => Some(FocusEventFilter::MouseLeave),
1705 HoverEventFilter::Scroll => Some(FocusEventFilter::Scroll),
1706 HoverEventFilter::ScrollStart => Some(FocusEventFilter::ScrollStart),
1707 HoverEventFilter::ScrollEnd => Some(FocusEventFilter::ScrollEnd),
1708 HoverEventFilter::TextInput => Some(FocusEventFilter::TextInput),
1709 HoverEventFilter::VirtualKeyDown => Some(FocusEventFilter::VirtualKeyDown),
1710 HoverEventFilter::VirtualKeyUp => Some(FocusEventFilter::VirtualKeyDown),
1711 HoverEventFilter::HoveredFile => None,
1712 HoverEventFilter::DroppedFile => None,
1713 HoverEventFilter::HoveredFileCancelled => None,
1714 HoverEventFilter::TouchStart => None,
1715 HoverEventFilter::TouchMove => None,
1716 HoverEventFilter::TouchEnd => None,
1717 HoverEventFilter::TouchCancel => None,
1718 HoverEventFilter::PenDown => Some(FocusEventFilter::PenDown),
1719 HoverEventFilter::PenMove => Some(FocusEventFilter::PenMove),
1720 HoverEventFilter::PenUp => Some(FocusEventFilter::PenUp),
1721 HoverEventFilter::PenEnter => None,
1722 HoverEventFilter::PenLeave => None,
1723 HoverEventFilter::DragStart => Some(FocusEventFilter::DragStart),
1724 HoverEventFilter::Drag => Some(FocusEventFilter::Drag),
1725 HoverEventFilter::DragEnd => Some(FocusEventFilter::DragEnd),
1726 HoverEventFilter::DragEnter => Some(FocusEventFilter::DragEnter),
1727 HoverEventFilter::DragOver => Some(FocusEventFilter::DragOver),
1728 HoverEventFilter::DragLeave => Some(FocusEventFilter::DragLeave),
1729 HoverEventFilter::Drop => Some(FocusEventFilter::Drop),
1730 HoverEventFilter::DoubleClick => Some(FocusEventFilter::DoubleClick),
1731 HoverEventFilter::LongPress => Some(FocusEventFilter::LongPress),
1732 HoverEventFilter::SwipeLeft => Some(FocusEventFilter::SwipeLeft),
1733 HoverEventFilter::SwipeRight => Some(FocusEventFilter::SwipeRight),
1734 HoverEventFilter::SwipeUp => Some(FocusEventFilter::SwipeUp),
1735 HoverEventFilter::SwipeDown => Some(FocusEventFilter::SwipeDown),
1736 HoverEventFilter::PinchIn => Some(FocusEventFilter::PinchIn),
1737 HoverEventFilter::PinchOut => Some(FocusEventFilter::PinchOut),
1738 HoverEventFilter::RotateClockwise => Some(FocusEventFilter::RotateClockwise),
1739 HoverEventFilter::RotateCounterClockwise => {
1740 Some(FocusEventFilter::RotateCounterClockwise)
1741 }
1742 HoverEventFilter::MouseOut => Some(FocusEventFilter::MouseLeave), HoverEventFilter::FocusIn => Some(FocusEventFilter::FocusIn),
1744 HoverEventFilter::FocusOut => Some(FocusEventFilter::FocusOut),
1745 HoverEventFilter::CompositionStart => Some(FocusEventFilter::CompositionStart),
1746 HoverEventFilter::CompositionUpdate => Some(FocusEventFilter::CompositionUpdate),
1747 HoverEventFilter::CompositionEnd => Some(FocusEventFilter::CompositionEnd),
1748 HoverEventFilter::SystemTextSingleClick => None,
1750 HoverEventFilter::SystemTextDoubleClick => None,
1751 HoverEventFilter::SystemTextTripleClick => None,
1752 }
1753 }
1754}
1755
1756#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1761#[repr(C)]
1762pub enum FocusEventFilter {
1763 MouseOver,
1765 MouseDown,
1767 LeftMouseDown,
1769 RightMouseDown,
1771 MiddleMouseDown,
1773 MouseUp,
1775 LeftMouseUp,
1777 RightMouseUp,
1779 MiddleMouseUp,
1781 MouseEnter,
1783 MouseLeave,
1785 Scroll,
1787 ScrollStart,
1789 ScrollEnd,
1791 TextInput,
1793 VirtualKeyDown,
1795 VirtualKeyUp,
1797 FocusReceived,
1799 FocusLost,
1801 PenDown,
1803 PenMove,
1805 PenUp,
1807 DragStart,
1809 Drag,
1811 DragEnd,
1813 DragEnter,
1815 DragOver,
1817 DragLeave,
1819 Drop,
1821 DoubleClick,
1823 LongPress,
1825 SwipeLeft,
1827 SwipeRight,
1829 SwipeUp,
1831 SwipeDown,
1833 PinchIn,
1835 PinchOut,
1837 RotateClockwise,
1839 RotateCounterClockwise,
1841
1842 FocusIn,
1845 FocusOut,
1847
1848 CompositionStart,
1851 CompositionUpdate,
1853 CompositionEnd,
1855}
1856
1857#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1860#[repr(C)]
1861pub enum WindowEventFilter {
1862 MouseOver,
1864 MouseDown,
1866 LeftMouseDown,
1868 RightMouseDown,
1870 MiddleMouseDown,
1872 MouseUp,
1874 LeftMouseUp,
1876 RightMouseUp,
1878 MiddleMouseUp,
1880 MouseEnter,
1882 MouseLeave,
1884 Scroll,
1886 ScrollStart,
1888 ScrollEnd,
1890 TextInput,
1892 VirtualKeyDown,
1894 VirtualKeyUp,
1896 HoveredFile,
1898 DroppedFile,
1900 HoveredFileCancelled,
1902 Resized,
1904 Moved,
1906 TouchStart,
1908 TouchMove,
1910 TouchEnd,
1912 TouchCancel,
1914 FocusReceived,
1916 FocusLost,
1918 CloseRequested,
1920 ThemeChanged,
1922 WindowFocusReceived,
1924 WindowFocusLost,
1926 PenDown,
1928 PenMove,
1930 PenUp,
1932 PenEnter,
1934 PenLeave,
1936 DragStart,
1938 Drag,
1940 DragEnd,
1942 DragEnter,
1944 DragOver,
1946 DragLeave,
1948 Drop,
1950 DoubleClick,
1952 LongPress,
1954 SwipeLeft,
1956 SwipeRight,
1958 SwipeUp,
1960 SwipeDown,
1962 PinchIn,
1964 PinchOut,
1966 RotateClockwise,
1968 RotateCounterClockwise,
1970 DpiChanged,
1973 MonitorChanged,
1976}
1977
1978impl WindowEventFilter {
1979 pub fn to_hover_event_filter(&self) -> Option<HoverEventFilter> {
1980 match self {
1981 WindowEventFilter::MouseOver => Some(HoverEventFilter::MouseOver),
1982 WindowEventFilter::MouseDown => Some(HoverEventFilter::MouseDown),
1983 WindowEventFilter::LeftMouseDown => Some(HoverEventFilter::LeftMouseDown),
1984 WindowEventFilter::RightMouseDown => Some(HoverEventFilter::RightMouseDown),
1985 WindowEventFilter::MiddleMouseDown => Some(HoverEventFilter::MiddleMouseDown),
1986 WindowEventFilter::MouseUp => Some(HoverEventFilter::MouseUp),
1987 WindowEventFilter::LeftMouseUp => Some(HoverEventFilter::LeftMouseUp),
1988 WindowEventFilter::RightMouseUp => Some(HoverEventFilter::RightMouseUp),
1989 WindowEventFilter::MiddleMouseUp => Some(HoverEventFilter::MiddleMouseUp),
1990 WindowEventFilter::Scroll => Some(HoverEventFilter::Scroll),
1991 WindowEventFilter::ScrollStart => Some(HoverEventFilter::ScrollStart),
1992 WindowEventFilter::ScrollEnd => Some(HoverEventFilter::ScrollEnd),
1993 WindowEventFilter::TextInput => Some(HoverEventFilter::TextInput),
1994 WindowEventFilter::VirtualKeyDown => Some(HoverEventFilter::VirtualKeyDown),
1995 WindowEventFilter::VirtualKeyUp => Some(HoverEventFilter::VirtualKeyDown),
1996 WindowEventFilter::HoveredFile => Some(HoverEventFilter::HoveredFile),
1997 WindowEventFilter::DroppedFile => Some(HoverEventFilter::DroppedFile),
1998 WindowEventFilter::HoveredFileCancelled => Some(HoverEventFilter::HoveredFileCancelled),
1999 WindowEventFilter::MouseEnter => None,
2002 WindowEventFilter::MouseLeave => None,
2003 WindowEventFilter::Resized => None,
2004 WindowEventFilter::Moved => None,
2005 WindowEventFilter::TouchStart => Some(HoverEventFilter::TouchStart),
2006 WindowEventFilter::TouchMove => Some(HoverEventFilter::TouchMove),
2007 WindowEventFilter::TouchEnd => Some(HoverEventFilter::TouchEnd),
2008 WindowEventFilter::TouchCancel => Some(HoverEventFilter::TouchCancel),
2009 WindowEventFilter::FocusReceived => None,
2010 WindowEventFilter::FocusLost => None,
2011 WindowEventFilter::CloseRequested => None,
2012 WindowEventFilter::ThemeChanged => None,
2013 WindowEventFilter::WindowFocusReceived => None, WindowEventFilter::WindowFocusLost => None, WindowEventFilter::PenDown => Some(HoverEventFilter::PenDown),
2016 WindowEventFilter::PenMove => Some(HoverEventFilter::PenMove),
2017 WindowEventFilter::PenUp => Some(HoverEventFilter::PenUp),
2018 WindowEventFilter::PenEnter => Some(HoverEventFilter::PenEnter),
2019 WindowEventFilter::PenLeave => Some(HoverEventFilter::PenLeave),
2020 WindowEventFilter::DragStart => Some(HoverEventFilter::DragStart),
2021 WindowEventFilter::Drag => Some(HoverEventFilter::Drag),
2022 WindowEventFilter::DragEnd => Some(HoverEventFilter::DragEnd),
2023 WindowEventFilter::DragEnter => Some(HoverEventFilter::DragEnter),
2024 WindowEventFilter::DragOver => Some(HoverEventFilter::DragOver),
2025 WindowEventFilter::DragLeave => Some(HoverEventFilter::DragLeave),
2026 WindowEventFilter::Drop => Some(HoverEventFilter::Drop),
2027 WindowEventFilter::DoubleClick => Some(HoverEventFilter::DoubleClick),
2028 WindowEventFilter::LongPress => Some(HoverEventFilter::LongPress),
2029 WindowEventFilter::SwipeLeft => Some(HoverEventFilter::SwipeLeft),
2030 WindowEventFilter::SwipeRight => Some(HoverEventFilter::SwipeRight),
2031 WindowEventFilter::SwipeUp => Some(HoverEventFilter::SwipeUp),
2032 WindowEventFilter::SwipeDown => Some(HoverEventFilter::SwipeDown),
2033 WindowEventFilter::PinchIn => Some(HoverEventFilter::PinchIn),
2034 WindowEventFilter::PinchOut => Some(HoverEventFilter::PinchOut),
2035 WindowEventFilter::RotateClockwise => Some(HoverEventFilter::RotateClockwise),
2036 WindowEventFilter::RotateCounterClockwise => {
2037 Some(HoverEventFilter::RotateCounterClockwise)
2038 }
2039 WindowEventFilter::DpiChanged => None,
2041 WindowEventFilter::MonitorChanged => None,
2042 }
2043 }
2044}
2045
2046#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2050#[repr(C, u8)]
2051pub enum NotEventFilter {
2052 Hover(HoverEventFilter),
2053 Focus(FocusEventFilter),
2054}
2055
2056impl NotEventFilter {
2057 pub fn as_event_filter(&self) -> EventFilter {
2058 match self {
2059 NotEventFilter::Hover(e) => EventFilter::Hover(*e),
2060 NotEventFilter::Focus(e) => EventFilter::Focus(*e),
2061 }
2062 }
2063}
2064
2065#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2067#[repr(C)]
2068pub enum ComponentEventFilter {
2069 AfterMount,
2071 BeforeUnmount,
2073 NodeResized,
2075 DefaultAction,
2077 Selected,
2079}
2080
2081#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2083#[repr(C)]
2084pub enum ApplicationEventFilter {
2085 DeviceConnected,
2087 DeviceDisconnected,
2089 MonitorConnected,
2092 MonitorDisconnected,
2094}
2095
2096#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2101#[repr(C, u8)]
2102pub enum EventFilter {
2103 Hover(HoverEventFilter),
2106 Not(NotEventFilter),
2111 Focus(FocusEventFilter),
2113 Window(WindowEventFilter),
2123 Component(ComponentEventFilter),
2125 Application(ApplicationEventFilter),
2127}
2128
2129impl EventFilter {
2130 pub const fn is_focus_callback(&self) -> bool {
2131 match self {
2132 EventFilter::Focus(_) => true,
2133 _ => false,
2134 }
2135 }
2136 pub const fn is_window_callback(&self) -> bool {
2137 match self {
2138 EventFilter::Window(_) => true,
2139 _ => false,
2140 }
2141 }
2142}
2143
2144macro_rules! get_single_enum_type {
2147 ($fn_name:ident, $enum_name:ident:: $variant:ident($return_type:ty)) => {
2148 pub fn $fn_name(&self) -> Option<$return_type> {
2149 use self::$enum_name::*;
2150 match self {
2151 $variant(e) => Some(*e),
2152 _ => None,
2153 }
2154 }
2155 };
2156}
2157
2158impl EventFilter {
2159 get_single_enum_type!(as_hover_event_filter, EventFilter::Hover(HoverEventFilter));
2160 get_single_enum_type!(as_focus_event_filter, EventFilter::Focus(FocusEventFilter));
2161 get_single_enum_type!(as_not_event_filter, EventFilter::Not(NotEventFilter));
2162 get_single_enum_type!(
2163 as_window_event_filter,
2164 EventFilter::Window(WindowEventFilter)
2165 );
2166}
2167
2168impl From<On> for EventFilter {
2174 fn from(input: On) -> EventFilter {
2175 use crate::dom::On::*;
2176 match input {
2177 MouseOver => EventFilter::Hover(HoverEventFilter::MouseOver),
2178 MouseDown => EventFilter::Hover(HoverEventFilter::MouseDown),
2179 LeftMouseDown => EventFilter::Hover(HoverEventFilter::LeftMouseDown),
2180 MiddleMouseDown => EventFilter::Hover(HoverEventFilter::MiddleMouseDown),
2181 RightMouseDown => EventFilter::Hover(HoverEventFilter::RightMouseDown),
2182 MouseUp => EventFilter::Hover(HoverEventFilter::MouseUp),
2183 LeftMouseUp => EventFilter::Hover(HoverEventFilter::LeftMouseUp),
2184 MiddleMouseUp => EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
2185 RightMouseUp => EventFilter::Hover(HoverEventFilter::RightMouseUp),
2186
2187 MouseEnter => EventFilter::Hover(HoverEventFilter::MouseEnter),
2188 MouseLeave => EventFilter::Hover(HoverEventFilter::MouseLeave),
2189 Scroll => EventFilter::Hover(HoverEventFilter::Scroll),
2190 TextInput => EventFilter::Focus(FocusEventFilter::TextInput), VirtualKeyDown => EventFilter::Window(WindowEventFilter::VirtualKeyDown), VirtualKeyUp => EventFilter::Window(WindowEventFilter::VirtualKeyUp), HoveredFile => EventFilter::Hover(HoverEventFilter::HoveredFile),
2194 DroppedFile => EventFilter::Hover(HoverEventFilter::DroppedFile),
2195 HoveredFileCancelled => EventFilter::Hover(HoverEventFilter::HoveredFileCancelled),
2196 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), }
2206 }
2207}
2208
2209pub fn should_recurse_callbacks<T: CallbackResultRef>(
2223 callback_results: &[T],
2224 max_depth: usize,
2225 current_depth: usize,
2226) -> bool {
2227 if current_depth >= max_depth {
2228 return false;
2229 }
2230
2231 for result in callback_results {
2233 if result.stop_propagation() {
2235 return false;
2236 }
2237
2238 if result.should_regenerate_dom() {
2240 return current_depth + 1 < max_depth;
2241 }
2242 }
2243
2244 false
2245}
2246
2247pub trait CallbackResultRef {
2250 fn stop_propagation(&self) -> bool;
2251 fn stop_immediate_propagation(&self) -> bool;
2252 fn prevent_default(&self) -> bool;
2253 fn should_regenerate_dom(&self) -> bool;
2254}
2255
2256pub trait EventProvider {
2264 fn get_pending_events(&self, timestamp: Instant) -> Vec<SyntheticEvent>;
2276}
2277
2278pub fn deduplicate_synthetic_events(mut events: Vec<SyntheticEvent>) -> Vec<SyntheticEvent> {
2282 if events.len() <= 1 {
2283 return events;
2284 }
2285
2286 events.sort_by_key(|e| (e.target.dom, e.target.node, e.event_type));
2287
2288 let mut result = Vec::with_capacity(events.len());
2290 let mut iter = events.into_iter();
2291
2292 if let Some(mut prev) = iter.next() {
2293 for curr in iter {
2294 if prev.target == curr.target && prev.event_type == curr.event_type {
2295 prev = if curr.timestamp > prev.timestamp {
2297 curr
2298 } else {
2299 prev
2300 };
2301 } else {
2302 result.push(prev);
2303 prev = curr;
2304 }
2305 }
2306 result.push(prev);
2307 }
2308
2309 result
2310}
2311
2312
2313
2314pub fn event_type_to_filters(event_type: EventType, event_data: &EventData) -> Vec<EventFilter> {
2319 use EventFilter as EF;
2320 use EventType as E;
2321 use FocusEventFilter as F;
2322 use HoverEventFilter as H;
2323 use WindowEventFilter as W;
2324
2325 let button_specific_down = || -> Option<EventFilter> {
2327 match event_data {
2328 EventData::Mouse(m) => match m.button {
2329 MouseButton::Left => Some(EF::Hover(H::LeftMouseDown)),
2330 MouseButton::Right => Some(EF::Hover(H::RightMouseDown)),
2331 MouseButton::Middle => Some(EF::Hover(H::MiddleMouseDown)),
2332 MouseButton::Other(_) => None, },
2334 _ => Some(EF::Hover(H::LeftMouseDown)), }
2336 };
2337
2338 let button_specific_up = || -> Option<EventFilter> {
2339 match event_data {
2340 EventData::Mouse(m) => match m.button {
2341 MouseButton::Left => Some(EF::Hover(H::LeftMouseUp)),
2342 MouseButton::Right => Some(EF::Hover(H::RightMouseUp)),
2343 MouseButton::Middle => Some(EF::Hover(H::MiddleMouseUp)),
2344 MouseButton::Other(_) => None, },
2346 _ => Some(EF::Hover(H::LeftMouseUp)), }
2348 };
2349
2350 match event_type {
2351 E::MouseDown => {
2353 let mut v = vec![EF::Hover(H::MouseDown)];
2354 if let Some(f) = button_specific_down() { v.push(f); }
2355 v
2356 }
2357 E::MouseUp => {
2358 let mut v = vec![EF::Hover(H::MouseUp)];
2359 if let Some(f) = button_specific_up() { v.push(f); }
2360 v
2361 }
2362
2363 E::Click => vec![EF::Hover(H::LeftMouseDown)],
2365
2366 E::MouseOver => vec![EF::Hover(H::MouseOver)],
2368 E::MouseEnter => vec![EF::Hover(H::MouseEnter)],
2369 E::MouseLeave => vec![EF::Hover(H::MouseLeave)],
2370 E::MouseOut => vec![EF::Hover(H::MouseOut)],
2371
2372 E::DoubleClick => vec![EF::Hover(H::DoubleClick), EF::Window(W::DoubleClick)],
2373 E::ContextMenu => vec![EF::Hover(H::RightMouseDown)],
2374
2375 E::KeyDown => vec![EF::Focus(F::VirtualKeyDown)],
2377 E::KeyUp => vec![EF::Focus(F::VirtualKeyUp)],
2378 E::KeyPress => vec![EF::Focus(F::TextInput)],
2379
2380 E::CompositionStart => vec![EF::Hover(H::CompositionStart), EF::Focus(F::CompositionStart)],
2382 E::CompositionUpdate => vec![EF::Hover(H::CompositionUpdate), EF::Focus(F::CompositionUpdate)],
2383 E::CompositionEnd => vec![EF::Hover(H::CompositionEnd), EF::Focus(F::CompositionEnd)],
2384
2385 E::Focus => vec![EF::Focus(F::FocusReceived)],
2387 E::Blur => vec![EF::Focus(F::FocusLost)],
2388 E::FocusIn => vec![EF::Hover(H::FocusIn), EF::Focus(F::FocusIn)],
2389 E::FocusOut => vec![EF::Hover(H::FocusOut), EF::Focus(F::FocusOut)],
2390
2391 E::Input | E::Change => vec![EF::Focus(F::TextInput)],
2393
2394 E::Scroll | E::ScrollStart | E::ScrollEnd => vec![EF::Hover(H::Scroll)],
2396
2397 E::DragStart => vec![EF::Hover(H::DragStart), EF::Window(W::DragStart)],
2399 E::Drag => vec![EF::Hover(H::Drag), EF::Window(W::Drag)],
2400 E::DragEnd => vec![EF::Hover(H::DragEnd), EF::Window(W::DragEnd)],
2401 E::DragEnter => vec![EF::Hover(H::DragEnter), EF::Window(W::DragEnter)],
2402 E::DragOver => vec![EF::Hover(H::DragOver), EF::Window(W::DragOver)],
2403 E::DragLeave => vec![EF::Hover(H::DragLeave), EF::Window(W::DragLeave)],
2404 E::Drop => vec![EF::Hover(H::Drop), EF::Window(W::Drop)],
2405
2406 E::TouchStart => vec![EF::Hover(H::TouchStart)],
2408 E::TouchMove => vec![EF::Hover(H::TouchMove)],
2409 E::TouchEnd => vec![EF::Hover(H::TouchEnd)],
2410 E::TouchCancel => vec![EF::Hover(H::TouchCancel)],
2411
2412 E::WindowResize => vec![EF::Window(W::Resized)],
2414 E::WindowMove => vec![EF::Window(W::Moved)],
2415 E::WindowClose => vec![EF::Window(W::CloseRequested)],
2416 E::WindowFocusIn => vec![EF::Window(W::WindowFocusReceived)],
2417 E::WindowFocusOut => vec![EF::Window(W::WindowFocusLost)],
2418 E::ThemeChange => vec![EF::Window(W::ThemeChanged)],
2419 E::WindowDpiChanged => vec![EF::Window(W::DpiChanged)],
2420 E::WindowMonitorChanged => vec![EF::Window(W::MonitorChanged)],
2421
2422 E::MonitorConnected => vec![EF::Application(ApplicationEventFilter::MonitorConnected)],
2424 E::MonitorDisconnected => vec![EF::Application(ApplicationEventFilter::MonitorDisconnected)],
2425
2426 E::FileHover => vec![EF::Hover(H::HoveredFile)],
2428 E::FileDrop => vec![EF::Hover(H::DroppedFile)],
2429 E::FileHoverCancel => vec![EF::Hover(H::HoveredFileCancelled)],
2430
2431 _ => vec![],
2433 }
2434}
2435
2436
2437
2438#[derive(Debug, Clone, PartialEq)]
2442pub struct PreCallbackFilterResult {
2443 pub internal_events: Vec<PreCallbackSystemEvent>,
2445 pub user_events: Vec<SyntheticEvent>,
2447}
2448
2449#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2451pub struct MouseButtonState {
2452 pub left_down: bool,
2453 pub right_down: bool,
2454 pub middle_down: bool,
2455}
2456
2457#[derive(Debug, Clone, PartialEq, Eq)]
2459pub enum PostCallbackSystemEvent {
2460 ApplyTextInput,
2462 FocusChanged,
2464 ApplyTextChangeset,
2466 ScrollIntoView,
2468 StartAutoScrollTimer,
2470 CancelAutoScrollTimer,
2472}
2473
2474#[derive(Debug, Clone, PartialEq)]
2476pub enum PreCallbackSystemEvent {
2477 TextClick {
2479 target: DomNodeId,
2480 position: LogicalPosition,
2481 click_count: u8,
2482 timestamp: Instant,
2483 },
2484 TextDragSelection {
2486 target: DomNodeId,
2487 start_position: LogicalPosition,
2488 current_position: LogicalPosition,
2489 is_dragging: bool,
2490 },
2491 ArrowKeyNavigation {
2493 target: DomNodeId,
2494 direction: ArrowDirection,
2495 extend_selection: bool, word_jump: bool, },
2498 KeyboardShortcut {
2500 target: DomNodeId,
2501 shortcut: KeyboardShortcut,
2502 },
2503 DeleteSelection {
2505 target: DomNodeId,
2506 forward: bool, },
2508}
2509
2510#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2512pub enum ArrowDirection {
2513 Left,
2514 Right,
2515 Up,
2516 Down,
2517}
2518
2519#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2521pub enum KeyboardShortcut {
2522 Copy, Cut, Paste, SelectAll, Undo, Redo, }
2529
2530#[derive(Debug, Clone, PartialEq)]
2532pub struct PostCallbackFilterResult {
2533 pub system_events: Vec<PostCallbackSystemEvent>,
2535}
2536
2537pub fn pre_callback_filter_internal_events<SM, FM>(
2541 events: &[SyntheticEvent],
2542 hit_test: Option<&FullHitTest>,
2543 keyboard_state: &crate::window::KeyboardState,
2544 mouse_state: &crate::window::MouseState,
2545 selection_manager: &SM,
2546 focus_manager: &FM,
2547) -> PreCallbackFilterResult
2548where
2549 SM: SelectionManagerQuery,
2550 FM: FocusManagerQuery,
2551{
2552 let ctx = FilterContext {
2553 hit_test,
2554 keyboard_state,
2555 mouse_state,
2556 click_count: selection_manager.get_click_count(),
2557 focused_node: focus_manager.get_focused_node_id(),
2558 drag_start_position: selection_manager.get_drag_start_position(),
2559 selection_manager,
2560 };
2561
2562 let (internal_events, user_events) = events.iter().fold(
2563 (Vec::new(), Vec::new()),
2564 |(mut internal, mut user), event| {
2565 match process_event_for_internal(&ctx, event) {
2566 Some(InternalEventAction::AddAndSkip(evt)) => {
2567 internal.push(evt);
2568 }
2569 Some(InternalEventAction::AddAndPass(evt)) => {
2570 internal.push(evt);
2571 user.push(event.clone());
2572 }
2573 None => {
2574 user.push(event.clone());
2575 }
2576 }
2577 (internal, user)
2578 },
2579 );
2580
2581 PreCallbackFilterResult {
2582 internal_events,
2583 user_events,
2584 }
2585}
2586
2587struct FilterContext<'a, SM> {
2589 hit_test: Option<&'a FullHitTest>,
2590 keyboard_state: &'a crate::window::KeyboardState,
2591 mouse_state: &'a crate::window::MouseState,
2592 click_count: u8,
2593 focused_node: Option<DomNodeId>,
2594 drag_start_position: Option<LogicalPosition>,
2595 selection_manager: &'a SM,
2596}
2597
2598fn process_event_for_internal<SM: SelectionManagerQuery>(
2600 ctx: &FilterContext<'_, SM>,
2601 event: &SyntheticEvent,
2602) -> Option<InternalEventAction> {
2603 match event.event_type {
2604 EventType::MouseDown => handle_mouse_down(event, ctx.hit_test, ctx.click_count, ctx.mouse_state),
2605 EventType::MouseOver => handle_mouse_over(
2606 event,
2607 ctx.hit_test,
2608 ctx.mouse_state,
2609 ctx.drag_start_position,
2610 ),
2611 EventType::KeyDown => handle_key_down(
2612 event,
2613 ctx.keyboard_state,
2614 ctx.selection_manager,
2615 ctx.focused_node,
2616 ),
2617 _ => None,
2618 }
2619}
2620
2621enum InternalEventAction {
2623 AddAndSkip(PreCallbackSystemEvent),
2625 AddAndPass(PreCallbackSystemEvent),
2627}
2628
2629fn get_first_hovered_node(hit_test: Option<&FullHitTest>) -> Option<DomNodeId> {
2631 let ht = hit_test?;
2632 let (dom_id, hit_data) = ht.hovered_nodes.iter().next()?;
2633 let node_id = hit_data.regular_hit_test_nodes.keys().next()?;
2634 Some(DomNodeId {
2635 dom: *dom_id,
2636 node: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
2637 })
2638}
2639
2640fn get_mouse_position_with_fallback(
2642 event: &SyntheticEvent,
2643 mouse_state: &crate::window::MouseState,
2644) -> LogicalPosition {
2645 match &event.data {
2646 EventData::Mouse(mouse_data) => mouse_data.position,
2647 _ => {
2648 mouse_state.cursor_position.get_position().unwrap_or(LogicalPosition::zero())
2652 }
2653 }
2654}
2655
2656fn handle_mouse_down(
2658 event: &SyntheticEvent,
2659 hit_test: Option<&FullHitTest>,
2660 click_count: u8,
2661 mouse_state: &crate::window::MouseState,
2662) -> Option<InternalEventAction> {
2663 let effective_click_count = if click_count == 0 { 1 } else { click_count };
2667
2668 if effective_click_count > 3 {
2669 return None;
2670 }
2671
2672 let target = get_first_hovered_node(hit_test)?;
2673 let position = get_mouse_position_with_fallback(event, mouse_state);
2674
2675 Some(InternalEventAction::AddAndPass(
2676 PreCallbackSystemEvent::TextClick {
2677 target,
2678 position,
2679 click_count: effective_click_count,
2680 timestamp: event.timestamp.clone(),
2681 },
2682 ))
2683}
2684
2685fn handle_mouse_over(
2687 event: &SyntheticEvent,
2688 hit_test: Option<&FullHitTest>,
2689 mouse_state: &crate::window::MouseState,
2690 drag_start_position: Option<LogicalPosition>,
2691) -> Option<InternalEventAction> {
2692 if !mouse_state.left_down {
2693 return None;
2694 }
2695
2696 let start_position = drag_start_position?;
2697
2698 let target = get_first_hovered_node(hit_test)?;
2699 let current_position = get_mouse_position_with_fallback(event, mouse_state);
2700
2701 Some(InternalEventAction::AddAndPass(
2702 PreCallbackSystemEvent::TextDragSelection {
2703 target,
2704 start_position,
2705 current_position,
2706 is_dragging: true,
2707 },
2708 ))
2709}
2710
2711fn handle_key_down<SM: SelectionManagerQuery>(
2713 event: &SyntheticEvent,
2714 keyboard_state: &crate::window::KeyboardState,
2715 selection_manager: &SM,
2716 focused_node: Option<DomNodeId>,
2717) -> Option<InternalEventAction> {
2718 use crate::window::VirtualKeyCode;
2719
2720 let target = focused_node?;
2721 let EventData::Keyboard(_) = &event.data else {
2722 return None;
2723 };
2724
2725 let ctrl = keyboard_state.ctrl_down();
2726 let shift = keyboard_state.shift_down();
2727 let vk = keyboard_state.current_virtual_keycode.as_ref()?;
2728
2729 if ctrl {
2731 let shortcut = match vk {
2732 VirtualKeyCode::C => Some(KeyboardShortcut::Copy),
2733 VirtualKeyCode::X => Some(KeyboardShortcut::Cut),
2734 VirtualKeyCode::V => Some(KeyboardShortcut::Paste),
2735 VirtualKeyCode::A => Some(KeyboardShortcut::SelectAll),
2736 VirtualKeyCode::Z if !shift => Some(KeyboardShortcut::Undo),
2737 VirtualKeyCode::Z if shift => Some(KeyboardShortcut::Redo),
2738 VirtualKeyCode::Y => Some(KeyboardShortcut::Redo),
2739 _ => None,
2740 };
2741 if let Some(shortcut) = shortcut {
2742 return Some(InternalEventAction::AddAndSkip(
2743 PreCallbackSystemEvent::KeyboardShortcut { target, shortcut },
2744 ));
2745 }
2746 }
2747
2748 let direction = match vk {
2750 VirtualKeyCode::Left => Some(ArrowDirection::Left),
2751 VirtualKeyCode::Up => Some(ArrowDirection::Up),
2752 VirtualKeyCode::Right => Some(ArrowDirection::Right),
2753 VirtualKeyCode::Down => Some(ArrowDirection::Down),
2754 _ => None,
2755 };
2756 if let Some(direction) = direction {
2757 return Some(InternalEventAction::AddAndSkip(
2758 PreCallbackSystemEvent::ArrowKeyNavigation {
2759 target,
2760 direction,
2761 extend_selection: shift,
2762 word_jump: ctrl,
2763 },
2764 ));
2765 }
2766
2767 if !selection_manager.has_selection() {
2769 return None;
2770 }
2771
2772 let forward = match vk {
2773 VirtualKeyCode::Back => Some(false),
2774 VirtualKeyCode::Delete => Some(true),
2775 _ => None,
2776 }?;
2777
2778 Some(InternalEventAction::AddAndSkip(
2779 PreCallbackSystemEvent::DeleteSelection { target, forward },
2780 ))
2781}
2782
2783pub trait SelectionManagerQuery {
2788 fn get_click_count(&self) -> u8;
2790
2791 fn get_drag_start_position(&self) -> Option<LogicalPosition>;
2793
2794 fn has_selection(&self) -> bool;
2796}
2797
2798pub trait FocusManagerQuery {
2803 fn get_focused_node_id(&self) -> Option<DomNodeId>;
2805}
2806
2807pub fn post_callback_filter_internal_events(
2809 prevent_default: bool,
2810 internal_events: &[PreCallbackSystemEvent],
2811 old_focus: Option<DomNodeId>,
2812 new_focus: Option<DomNodeId>,
2813) -> PostCallbackFilterResult {
2814 if prevent_default {
2815 let focus_event = (old_focus != new_focus).then_some(PostCallbackSystemEvent::FocusChanged);
2816 return PostCallbackFilterResult {
2817 system_events: focus_event.into_iter().collect(),
2818 };
2819 }
2820
2821 let event_actions = internal_events
2822 .iter()
2823 .filter_map(internal_event_to_system_event);
2824
2825 let focus_event = (old_focus != new_focus).then_some(PostCallbackSystemEvent::FocusChanged);
2826
2827 let system_events = core::iter::once(PostCallbackSystemEvent::ApplyTextInput)
2828 .chain(event_actions)
2829 .chain(focus_event)
2830 .collect();
2831
2832 PostCallbackFilterResult { system_events }
2833}
2834
2835fn internal_event_to_system_event(
2837 event: &PreCallbackSystemEvent,
2838) -> Option<PostCallbackSystemEvent> {
2839 use PostCallbackSystemEvent::*;
2840 use PreCallbackSystemEvent::*;
2841
2842 match event {
2843 TextClick { .. } | ArrowKeyNavigation { .. } | DeleteSelection { .. } => {
2844 Some(ScrollIntoView)
2845 }
2846 TextDragSelection { is_dragging, .. } => Some(if *is_dragging {
2847 StartAutoScrollTimer
2848 } else {
2849 CancelAutoScrollTimer
2850 }),
2851 KeyboardShortcut { shortcut, .. } => shortcut_to_system_event(*shortcut),
2852 }
2853}
2854
2855fn shortcut_to_system_event(shortcut: KeyboardShortcut) -> Option<PostCallbackSystemEvent> {
2857 use KeyboardShortcut::*;
2858 match shortcut {
2859 Cut | Paste | Undo | Redo => Some(PostCallbackSystemEvent::ScrollIntoView),
2860 Copy | SelectAll => None,
2861 }
2862}
2863
2864#[cfg(test)]
2865mod tests {
2866 use std::collections::BTreeMap;
2876
2877 use crate::{
2878 dom::{DomId, DomNodeId},
2879 events::*,
2880 geom::{LogicalPosition, LogicalRect, LogicalSize},
2881 id::{Node, NodeHierarchy, NodeId},
2882 styled_dom::NodeHierarchyItemId,
2883 task::{Instant, SystemTick},
2884 };
2885
2886 fn test_instant() -> Instant {
2888 Instant::Tick(SystemTick::new(0))
2889 }
2890
2891 fn create_test_hierarchy() -> NodeHierarchy {
2893 let nodes = vec![
2894 Node {
2895 parent: None,
2896 previous_sibling: None,
2897 next_sibling: None,
2898 last_child: Some(NodeId::new(1)),
2899 },
2900 Node {
2901 parent: Some(NodeId::new(0)),
2902 previous_sibling: None,
2903 next_sibling: None,
2904 last_child: Some(NodeId::new(2)),
2905 },
2906 Node {
2907 parent: Some(NodeId::new(1)),
2908 previous_sibling: None,
2909 next_sibling: None,
2910 last_child: None,
2911 },
2912 ];
2913 NodeHierarchy::new(nodes)
2914 }
2915
2916 #[test]
2917 fn test_event_source_enum() {
2918 let _user = EventSource::User;
2920 let _programmatic = EventSource::Programmatic;
2921 let _synthetic = EventSource::Synthetic;
2922 let _lifecycle = EventSource::Lifecycle;
2923 }
2924
2925 #[test]
2926 fn test_event_phase_enum() {
2927 let _capture = EventPhase::Capture;
2929 let _target = EventPhase::Target;
2930 let _bubble = EventPhase::Bubble;
2931
2932 assert_eq!(EventPhase::default(), EventPhase::Bubble);
2934 }
2935
2936 #[test]
2937 fn test_synthetic_event_creation() {
2938 let dom_id = DomId { inner: 1 };
2939 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2940 let target = DomNodeId {
2941 dom: dom_id,
2942 node: node_id,
2943 };
2944
2945 let event = SyntheticEvent::new(
2946 EventType::Click,
2947 EventSource::User,
2948 target,
2949 test_instant(),
2950 EventData::None,
2951 );
2952
2953 assert_eq!(event.event_type, EventType::Click);
2954 assert_eq!(event.source, EventSource::User);
2955 assert_eq!(event.phase, EventPhase::Target);
2956 assert_eq!(event.target, target);
2957 assert_eq!(event.current_target, target);
2958 assert!(!event.stopped);
2959 assert!(!event.stopped_immediate);
2960 assert!(!event.prevented_default);
2961 }
2962
2963 #[test]
2964 fn test_stop_propagation() {
2965 let dom_id = DomId { inner: 1 };
2966 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2967 let target = DomNodeId {
2968 dom: dom_id,
2969 node: node_id,
2970 };
2971
2972 let mut event = SyntheticEvent::new(
2973 EventType::Click,
2974 EventSource::User,
2975 target,
2976 test_instant(),
2977 EventData::None,
2978 );
2979
2980 assert!(!event.is_propagation_stopped());
2981
2982 event.stop_propagation();
2983
2984 assert!(event.is_propagation_stopped());
2985 assert!(!event.is_immediate_propagation_stopped());
2986 }
2987
2988 #[test]
2989 fn test_stop_immediate_propagation() {
2990 let dom_id = DomId { inner: 1 };
2991 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
2992 let target = DomNodeId {
2993 dom: dom_id,
2994 node: node_id,
2995 };
2996
2997 let mut event = SyntheticEvent::new(
2998 EventType::Click,
2999 EventSource::User,
3000 target,
3001 test_instant(),
3002 EventData::None,
3003 );
3004
3005 event.stop_immediate_propagation();
3006
3007 assert!(event.is_propagation_stopped());
3008 assert!(event.is_immediate_propagation_stopped());
3009 }
3010
3011 #[test]
3012 fn test_prevent_default() {
3013 let dom_id = DomId { inner: 1 };
3014 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
3015 let target = DomNodeId {
3016 dom: dom_id,
3017 node: node_id,
3018 };
3019
3020 let mut event = SyntheticEvent::new(
3021 EventType::Click,
3022 EventSource::User,
3023 target,
3024 test_instant(),
3025 EventData::None,
3026 );
3027
3028 assert!(!event.is_default_prevented());
3029
3030 event.prevent_default();
3031
3032 assert!(event.is_default_prevented());
3033 }
3034
3035 #[test]
3036 fn test_get_dom_path_single_node() {
3037 let hierarchy = NodeHierarchy::new(vec![Node {
3038 parent: None,
3039 previous_sibling: None,
3040 next_sibling: None,
3041 last_child: None,
3042 }]);
3043
3044 let target = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
3045 let path = get_dom_path(&hierarchy, target);
3046
3047 assert_eq!(path.len(), 1);
3048 assert_eq!(path[0], NodeId::new(0));
3049 }
3050
3051 #[test]
3052 fn test_get_dom_path_three_nodes() {
3053 let hierarchy = create_test_hierarchy();
3054
3055 let target = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(2)));
3057 let path = get_dom_path(&hierarchy, target);
3058
3059 assert_eq!(path.len(), 3);
3060 assert_eq!(path[0], NodeId::new(0)); assert_eq!(path[1], NodeId::new(1)); assert_eq!(path[2], NodeId::new(2)); }
3064
3065 #[test]
3066 fn test_get_dom_path_middle_node() {
3067 let hierarchy = create_test_hierarchy();
3068
3069 let target = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(1)));
3071 let path = get_dom_path(&hierarchy, target);
3072
3073 assert_eq!(path.len(), 2);
3074 assert_eq!(path[0], NodeId::new(0)); assert_eq!(path[1], NodeId::new(1)); }
3077
3078 #[test]
3079 fn test_propagate_event_empty_callbacks() {
3080 let hierarchy = create_test_hierarchy();
3081 let dom_id = DomId { inner: 1 };
3082 let target_node = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(2)));
3083 let target = DomNodeId {
3084 dom: dom_id,
3085 node: target_node,
3086 };
3087
3088 let mut event = SyntheticEvent::new(
3089 EventType::Click,
3090 EventSource::User,
3091 target,
3092 test_instant(),
3093 EventData::None,
3094 );
3095
3096 let callbacks: BTreeMap<NodeId, Vec<EventFilter>> = BTreeMap::new();
3097 let result = propagate_event(&mut event, &hierarchy, &callbacks);
3098
3099 assert_eq!(result.callbacks_to_invoke.len(), 0);
3101 assert!(!result.default_prevented);
3102 }
3103
3104 #[test]
3105 fn test_mouse_event_data_creation() {
3106 let mouse_data = MouseEventData {
3107 position: LogicalPosition { x: 100.0, y: 200.0 },
3108 button: MouseButton::Left,
3109 buttons: 1,
3110 modifiers: KeyModifiers::new(),
3111 };
3112
3113 assert_eq!(mouse_data.position.x, 100.0);
3114 assert_eq!(mouse_data.position.y, 200.0);
3115 assert_eq!(mouse_data.button, MouseButton::Left);
3116 }
3117
3118 #[test]
3119 fn test_key_modifiers() {
3120 let modifiers = KeyModifiers::new().with_shift().with_ctrl();
3121
3122 assert!(modifiers.shift);
3123 assert!(modifiers.ctrl);
3124 assert!(!modifiers.alt);
3125 assert!(!modifiers.meta);
3126 assert!(!modifiers.is_empty());
3127
3128 let empty = KeyModifiers::new();
3129 assert!(empty.is_empty());
3130 }
3131
3132 #[test]
3133 fn test_lifecycle_event_mount() {
3134 let dom_id = DomId { inner: 1 };
3135 let old_hierarchy = None;
3136 let new_hierarchy = create_test_hierarchy();
3137 let old_layout = None;
3138 let new_layout = {
3139 let mut map = BTreeMap::new();
3140 map.insert(
3141 NodeId::new(0),
3142 LogicalRect {
3143 origin: LogicalPosition { x: 0.0, y: 0.0 },
3144 size: LogicalSize {
3145 width: 100.0,
3146 height: 100.0,
3147 },
3148 },
3149 );
3150 map.insert(
3151 NodeId::new(1),
3152 LogicalRect {
3153 origin: LogicalPosition { x: 10.0, y: 10.0 },
3154 size: LogicalSize {
3155 width: 80.0,
3156 height: 80.0,
3157 },
3158 },
3159 );
3160 map.insert(
3161 NodeId::new(2),
3162 LogicalRect {
3163 origin: LogicalPosition { x: 20.0, y: 20.0 },
3164 size: LogicalSize {
3165 width: 60.0,
3166 height: 60.0,
3167 },
3168 },
3169 );
3170 Some(map)
3171 };
3172
3173 let events = detect_lifecycle_events(
3174 dom_id,
3175 dom_id,
3176 old_hierarchy,
3177 Some(&new_hierarchy),
3178 old_layout.as_ref(),
3179 new_layout.as_ref(),
3180 test_instant(),
3181 );
3182
3183 assert_eq!(events.len(), 3);
3185
3186 for event in &events {
3187 assert_eq!(event.event_type, EventType::Mount);
3188 assert_eq!(event.source, EventSource::Lifecycle);
3189
3190 if let EventData::Lifecycle(data) = &event.data {
3191 assert_eq!(data.reason, LifecycleReason::InitialMount);
3192 assert!(data.previous_bounds.is_none());
3193 } else {
3194 panic!("Expected Lifecycle event data");
3195 }
3196 }
3197 }
3198
3199 #[test]
3200 fn test_lifecycle_event_unmount() {
3201 let dom_id = DomId { inner: 1 };
3202 let old_hierarchy = create_test_hierarchy();
3203 let new_hierarchy = None;
3204 let old_layout = {
3205 let mut map = BTreeMap::new();
3206 map.insert(
3207 NodeId::new(0),
3208 LogicalRect {
3209 origin: LogicalPosition { x: 0.0, y: 0.0 },
3210 size: LogicalSize {
3211 width: 100.0,
3212 height: 100.0,
3213 },
3214 },
3215 );
3216 Some(map)
3217 };
3218 let new_layout = None;
3219
3220 let events = detect_lifecycle_events(
3221 dom_id,
3222 dom_id,
3223 Some(&old_hierarchy),
3224 new_hierarchy,
3225 old_layout.as_ref(),
3226 new_layout,
3227 test_instant(),
3228 );
3229
3230 assert_eq!(events.len(), 3);
3232
3233 for event in &events {
3234 assert_eq!(event.event_type, EventType::Unmount);
3235 assert_eq!(event.source, EventSource::Lifecycle);
3236 }
3237 }
3238
3239 #[test]
3240 fn test_lifecycle_event_resize() {
3241 let dom_id = DomId { inner: 1 };
3242 let hierarchy = create_test_hierarchy();
3243
3244 let old_layout = {
3245 let mut map = BTreeMap::new();
3246 map.insert(
3247 NodeId::new(0),
3248 LogicalRect {
3249 origin: LogicalPosition { x: 0.0, y: 0.0 },
3250 size: LogicalSize {
3251 width: 100.0,
3252 height: 100.0,
3253 },
3254 },
3255 );
3256 Some(map)
3257 };
3258
3259 let new_layout = {
3260 let mut map = BTreeMap::new();
3261 map.insert(
3262 NodeId::new(0),
3263 LogicalRect {
3264 origin: LogicalPosition { x: 0.0, y: 0.0 },
3265 size: LogicalSize {
3266 width: 200.0,
3267 height: 100.0,
3268 }, },
3270 );
3271 Some(map)
3272 };
3273
3274 let events = detect_lifecycle_events(
3275 dom_id,
3276 dom_id,
3277 Some(&hierarchy),
3278 Some(&hierarchy),
3279 old_layout.as_ref(),
3280 new_layout.as_ref(),
3281 test_instant(),
3282 );
3283
3284 assert_eq!(events.len(), 1);
3286 assert_eq!(events[0].event_type, EventType::Resize);
3287 assert_eq!(events[0].source, EventSource::Lifecycle);
3288
3289 if let EventData::Lifecycle(data) = &events[0].data {
3290 assert_eq!(data.reason, LifecycleReason::Resize);
3291 assert!(data.previous_bounds.is_some());
3292 assert_eq!(data.current_bounds.size.width, 200.0);
3293 } else {
3294 panic!("Expected Lifecycle event data");
3295 }
3296 }
3297
3298 #[test]
3299 fn test_event_filter_hover_match() {
3300 let dom_id = DomId { inner: 1 };
3301 let node_id = NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(0)));
3302 let target = DomNodeId {
3303 dom: dom_id,
3304 node: node_id,
3305 };
3306
3307 let _event = SyntheticEvent::new(
3308 EventType::MouseDown,
3309 EventSource::User,
3310 target,
3311 test_instant(),
3312 EventData::Mouse(MouseEventData {
3313 position: LogicalPosition { x: 0.0, y: 0.0 },
3314 button: MouseButton::Left,
3315 buttons: 1,
3316 modifiers: KeyModifiers::new(),
3317 }),
3318 );
3319
3320 }
3324}