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::AzString;
12
13use crate::{
14 callbacks::Update,
15 dom::{DomId, DomNodeId, On},
16 geom::{LogicalPosition, LogicalRect},
17 hit_test::{FullHitTest, HitTestItem},
18 id::NodeId,
19 styled_dom::{ChangedCssProperty, NodeHierarchyItemId},
20 task::Instant,
21 OrderedMap,
22};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum EasingFunction {
27 Linear,
28 EaseInOut,
29 EaseOut,
30}
31
32pub type RestyleNodes = BTreeMap<NodeId, Vec<ChangedCssProperty>>;
33pub type RelayoutNodes = BTreeMap<NodeId, Vec<ChangedCssProperty>>;
34pub type RelayoutWords = BTreeMap<NodeId, AzString>;
35
36#[derive(Debug, Clone, PartialEq)]
37pub struct FocusChange {
38 pub old: Option<DomNodeId>,
39 pub new: Option<DomNodeId>,
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub struct CallbackToCall {
44 pub node_id: NodeId,
45 pub hit_test_item: Option<HitTestItem>,
46 pub event_filter: EventFilter,
47}
48
49impl CallbackToCall {
50 pub fn new(
51 node_id: NodeId,
52 hit_test_item: Option<HitTestItem>,
53 event_filter: EventFilter,
54 ) -> Self {
55 Self { node_id, hit_test_item, event_filter }
56 }
57
58 pub fn from_hit_test(
62 hit_test: &FullHitTest,
63 dom_id: DomId,
64 event_filter: EventFilter,
65 ) -> Vec<CallbackToCall> {
66 let Some(hit) = hit_test.hovered_nodes.get(&dom_id) else {
67 return Vec::new();
68 };
69 hit.regular_hit_test_nodes
70 .iter()
71 .map(|(node_id, item)| CallbackToCall {
72 node_id: *node_id,
73 hit_test_item: Some(item.clone()),
74 event_filter: event_filter.clone(),
75 })
76 .collect()
77 }
78}
79
80#[derive(Debug, Copy, Clone, PartialEq, Eq)]
81#[must_use = "ProcessEventResult must be used to determine if relayout/repaint is needed"]
82pub enum ProcessEventResult {
83 DoNothing = 0,
84 ShouldReRenderCurrentWindow = 1,
85 ShouldUpdateDisplayListCurrentWindow = 2,
86 UpdateHitTesterAndProcessAgain = 3,
89 ShouldIncrementalRelayout = 4,
92 ShouldRegenerateDomCurrentWindow = 5,
94 ShouldRegenerateDomAllWindows = 6,
95}
96
97impl ProcessEventResult {
98 pub fn order(&self) -> usize {
99 use self::ProcessEventResult::*;
100 match self {
101 DoNothing => 0,
102 ShouldReRenderCurrentWindow => 1,
103 ShouldUpdateDisplayListCurrentWindow => 2,
104 UpdateHitTesterAndProcessAgain => 3,
105 ShouldIncrementalRelayout => 4,
106 ShouldRegenerateDomCurrentWindow => 5,
107 ShouldRegenerateDomAllWindows => 6,
108 }
109 }
110}
111
112impl PartialOrd for ProcessEventResult {
113 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
114 self.order().partial_cmp(&other.order())
115 }
116}
117
118impl Ord for ProcessEventResult {
119 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
120 self.order().cmp(&other.order())
121 }
122}
123
124impl ProcessEventResult {
125 pub fn max_self(self, other: Self) -> Self {
126 self.max(other)
127 }
128}
129
130#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
135#[repr(C)]
136pub enum EventSource {
137 User,
139 Programmatic,
141 Synthetic,
143 Lifecycle,
145}
146
147#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
154#[repr(C)]
155pub enum EventPhase {
156 Capture,
158 Target,
160 Bubble,
162}
163
164impl Default for EventPhase {
165 fn default() -> Self {
166 EventPhase::Bubble
167 }
168}
169
170#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
172#[repr(C)]
173pub enum MouseButton {
174 Left,
175 Middle,
176 Right,
177 Other(u8),
178}
179
180#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
182#[repr(C)]
183pub enum ScrollDeltaMode {
184 Pixel,
186 Line,
188 Page,
190}
191
192#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
194#[repr(C)]
195pub enum ScrollDirection {
196 Up,
197 Down,
198 Left,
199 Right,
200}
201
202#[repr(C)]
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
212pub struct ScrollIntoViewOptions {
213 pub block: ScrollLogicalPosition,
215 pub inline_axis: ScrollLogicalPosition,
218 pub behavior: ScrollIntoViewBehavior,
220}
221
222impl ScrollIntoViewOptions {
223 pub fn nearest() -> Self {
225 Self {
226 block: ScrollLogicalPosition::Nearest,
227 inline_axis: ScrollLogicalPosition::Nearest,
228 behavior: ScrollIntoViewBehavior::Auto,
229 }
230 }
231
232 pub fn center() -> Self {
234 Self {
235 block: ScrollLogicalPosition::Center,
236 inline_axis: ScrollLogicalPosition::Center,
237 behavior: ScrollIntoViewBehavior::Auto,
238 }
239 }
240
241 pub fn start() -> Self {
243 Self {
244 block: ScrollLogicalPosition::Start,
245 inline_axis: ScrollLogicalPosition::Start,
246 behavior: ScrollIntoViewBehavior::Auto,
247 }
248 }
249
250 pub fn end() -> Self {
252 Self {
253 block: ScrollLogicalPosition::End,
254 inline_axis: ScrollLogicalPosition::End,
255 behavior: ScrollIntoViewBehavior::Auto,
256 }
257 }
258
259 pub fn with_instant(mut self) -> Self {
261 self.behavior = ScrollIntoViewBehavior::Instant;
262 self
263 }
264
265 pub fn with_smooth(mut self) -> Self {
267 self.behavior = ScrollIntoViewBehavior::Smooth;
268 self
269 }
270}
271
272#[repr(C)]
277#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
278pub enum ScrollLogicalPosition {
279 Start,
281 Center,
283 End,
285 #[default]
287 Nearest,
288}
289
290#[repr(C)]
295#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
296pub enum ScrollIntoViewBehavior {
297 #[default]
299 Auto,
300 Instant,
302 Smooth,
304}
305
306#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
308#[repr(C)]
309pub enum LifecycleReason {
310 InitialMount,
312 Remount,
314 Resize,
316 Update,
318 Unmount,
320}
321
322#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
324#[repr(C)]
325pub struct KeyModifiers {
326 pub shift: bool,
327 pub ctrl: bool,
328 pub alt: bool,
329 pub meta: bool,
330}
331
332impl KeyModifiers {
333 pub fn new() -> Self {
334 Self::default()
335 }
336
337 pub fn with_shift(mut self) -> Self {
338 self.shift = true;
339 self
340 }
341
342 pub fn with_ctrl(mut self) -> Self {
343 self.ctrl = true;
344 self
345 }
346
347 pub fn with_alt(mut self) -> Self {
348 self.alt = true;
349 self
350 }
351
352 pub fn with_meta(mut self) -> Self {
353 self.meta = true;
354 self
355 }
356
357 pub fn is_empty(&self) -> bool {
358 !self.shift && !self.ctrl && !self.alt && !self.meta
359 }
360}
361
362#[derive(Debug, Clone, PartialEq)]
364pub struct MouseEventData {
365 pub position: LogicalPosition,
367 pub button: MouseButton,
369 pub buttons: u8,
371 pub modifiers: KeyModifiers,
373}
374
375#[derive(Debug, Clone, PartialEq)]
377pub struct KeyboardEventData {
378 pub key_code: u32,
380 pub char_code: Option<char>,
382 pub modifiers: KeyModifiers,
384 pub repeat: bool,
386}
387
388#[derive(Debug, Clone, PartialEq)]
390pub struct ScrollEventData {
391 pub delta: LogicalPosition,
393 pub delta_mode: ScrollDeltaMode,
395}
396
397#[derive(Debug, Clone, PartialEq)]
399pub struct TouchEventData {
400 pub id: u64,
402 pub position: LogicalPosition,
404 pub force: f32,
406}
407
408#[derive(Debug, Clone, PartialEq)]
410pub struct ClipboardEventData {
411 pub content: Option<String>,
413}
414
415#[derive(Debug, Clone, PartialEq)]
417pub struct LifecycleEventData {
418 pub reason: LifecycleReason,
420 pub previous_bounds: Option<LogicalRect>,
422 pub current_bounds: LogicalRect,
424}
425
426#[derive(Debug, Clone, PartialEq)]
428pub struct WindowEventData {
429 pub size: Option<LogicalRect>,
431 pub position: Option<LogicalPosition>,
433}
434
435#[derive(Debug, Clone, PartialEq)]
437pub enum EventData {
438 Mouse(MouseEventData),
440 Keyboard(KeyboardEventData),
442 Scroll(ScrollEventData),
444 Touch(TouchEventData),
446 Clipboard(ClipboardEventData),
448 Lifecycle(LifecycleEventData),
450 Window(WindowEventData),
452 None,
454}
455
456#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
462#[repr(C)]
463pub enum EventType {
464 MouseOver,
467 MouseEnter,
469 MouseLeave,
471 MouseOut,
473 MouseDown,
475 MouseUp,
477 Click,
479 DoubleClick,
481 ContextMenu,
483
484 KeyDown,
487 KeyUp,
489 KeyPress,
491
492 CompositionStart,
495 CompositionUpdate,
497 CompositionEnd,
499
500 Focus,
503 Blur,
505 FocusIn,
507 FocusOut,
509
510 Input,
513 Change,
515 Submit,
517 Reset,
519 Invalid,
521
522 Scroll,
525 ScrollStart,
527 ScrollEnd,
529
530 DragStart,
533 Drag,
535 DragEnd,
537 DragEnter,
539 DragOver,
541 DragLeave,
543 Drop,
545
546 TouchStart,
549 TouchMove,
551 TouchEnd,
553 TouchCancel,
555
556 PenDown,
559 PenMove,
561 PenUp,
563 PenEnter,
565 PenLeave,
567
568 LongPress,
571 SwipeLeft,
573 SwipeRight,
575 SwipeUp,
577 SwipeDown,
579 PinchIn,
581 PinchOut,
583 RotateClockwise,
585 RotateCounterClockwise,
587
588 Copy,
591 Cut,
593 Paste,
595
596 Play,
599 Pause,
601 Ended,
603 TimeUpdate,
605 VolumeChange,
607 MediaError,
609
610 Mount,
613 Unmount,
615 Update,
617 Resize,
619
620 WindowResize,
623 WindowMove,
625 WindowClose,
627 WindowFocusIn,
629 WindowFocusOut,
631 ThemeChange,
633 WindowDpiChanged,
635 WindowMonitorChanged,
637
638 MonitorConnected,
641 MonitorDisconnected,
643
644 FileHover,
647 FileDrop,
649 FileHoverCancel,
651
652 SensorChanged,
656 GamepadInput,
659}
660
661#[derive(Debug, Clone, PartialEq)]
666pub struct SyntheticEvent {
667 pub event_type: EventType,
669
670 pub source: EventSource,
672
673 pub phase: EventPhase,
675
676 pub target: DomNodeId,
678
679 pub current_target: DomNodeId,
681
682 pub timestamp: Instant,
684
685 pub data: EventData,
687
688 pub stopped: bool,
690
691 pub stopped_immediate: bool,
693
694 pub prevented_default: bool,
696}
697
698impl SyntheticEvent {
699 pub fn new(
704 event_type: EventType,
705 source: EventSource,
706 target: DomNodeId,
707 timestamp: Instant,
708 data: EventData,
709 ) -> Self {
710 Self {
711 event_type,
712 source,
713 phase: EventPhase::Target,
714 target,
715 current_target: target,
716 timestamp,
717 data,
718 stopped: false,
719 stopped_immediate: false,
720 prevented_default: false,
721 }
722 }
723
724 pub fn stop_propagation(&mut self) {
729 self.stopped = true;
730 }
731
732 pub fn stop_immediate_propagation(&mut self) {
737 self.stopped_immediate = true;
738 self.stopped = true;
739 }
740
741 pub fn prevent_default(&mut self) {
746 self.prevented_default = true;
747 }
748
749 pub fn is_propagation_stopped(&self) -> bool {
751 self.stopped
752 }
753
754 pub fn is_immediate_propagation_stopped(&self) -> bool {
756 self.stopped_immediate
757 }
758
759 pub fn is_default_prevented(&self) -> bool {
761 self.prevented_default
762 }
763}
764
765#[derive(Debug, Clone)]
767pub struct PropagationResult {
768 pub callbacks_to_invoke: Vec<(NodeId, EventFilter)>,
770 pub default_prevented: bool,
772}
773
774pub fn get_dom_path(
781 node_hierarchy: &crate::id::NodeHierarchy,
782 target_node: NodeHierarchyItemId,
783) -> Vec<NodeId> {
784 let mut path = Vec::new();
785 let target_node_id = match target_node.into_crate_internal() {
786 Some(id) => id,
787 None => return path,
788 };
789
790 let hier_ref = node_hierarchy.as_ref();
791
792 let mut current = Some(target_node_id);
794 while let Some(node_id) = current {
795 path.push(node_id);
796 current = hier_ref.get(node_id).and_then(|node| node.parent);
797 }
798
799 path.reverse();
801 path
802}
803
804pub fn propagate_event(
814 event: &mut SyntheticEvent,
815 node_hierarchy: &crate::id::NodeHierarchy,
816 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
817) -> PropagationResult {
818 let path = get_dom_path(node_hierarchy, event.target.node);
819 if path.is_empty() {
820 return PropagationResult::default();
821 }
822
823 let ancestors = &path[..path.len().saturating_sub(1)];
824 let target_node_id = *path.last().unwrap();
825
826 let mut result = PropagationResult::default();
827
828 propagate_phase(
830 event,
831 ancestors.iter().copied(),
832 EventPhase::Capture,
833 callbacks,
834 &mut result,
835 );
836
837 if !event.stopped {
839 propagate_target_phase(event, target_node_id, callbacks, &mut result);
840 }
841
842 if !event.stopped {
844 propagate_phase(
845 event,
846 ancestors.iter().rev().copied(),
847 EventPhase::Bubble,
848 callbacks,
849 &mut result,
850 );
851 }
852
853 result.default_prevented = event.prevented_default;
854 result
855}
856
857fn propagate_phase(
859 event: &mut SyntheticEvent,
860 nodes: impl Iterator<Item = NodeId>,
861 phase: EventPhase,
862 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
863 result: &mut PropagationResult,
864) {
865 event.phase = phase;
866
867 for node_id in nodes {
868 if event.stopped_immediate || event.stopped {
869 return;
870 }
871
872 event.current_target = DomNodeId {
873 dom: event.target.dom,
874 node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
875 };
876
877 collect_matching_callbacks(event, node_id, phase, callbacks, result);
878 }
879}
880
881fn propagate_target_phase(
883 event: &mut SyntheticEvent,
884 target_node_id: NodeId,
885 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
886 result: &mut PropagationResult,
887) {
888 event.phase = EventPhase::Target;
889 event.current_target = event.target;
890
891 collect_matching_callbacks(event, target_node_id, EventPhase::Target, callbacks, result);
892}
893
894fn collect_matching_callbacks(
896 event: &SyntheticEvent,
897 node_id: NodeId,
898 phase: EventPhase,
899 callbacks: &BTreeMap<NodeId, Vec<EventFilter>>,
900 result: &mut PropagationResult,
901) {
902 let Some(node_callbacks) = callbacks.get(&node_id) else {
903 return;
904 };
905
906 let matching = node_callbacks
907 .iter()
908 .take_while(|_| !event.stopped_immediate)
909 .filter(|filter| matches_filter_phase(filter, event, phase))
910 .map(|filter| (node_id, *filter));
911
912 result.callbacks_to_invoke.extend(matching);
913}
914
915impl Default for PropagationResult {
916 fn default() -> Self {
917 Self {
918 callbacks_to_invoke: Vec::new(),
919 default_prevented: false,
920 }
921 }
922}
923
924#[derive(Debug, Clone, PartialEq, Eq, Hash)]
943#[repr(C, u8)]
944pub enum DefaultAction {
945 FocusNext,
947 FocusPrevious,
949 FocusFirst,
951 FocusLast,
953 ClearFocus,
955 ActivateFocusedElement {
958 target: DomNodeId,
959 },
960 SubmitForm {
962 form_node: DomNodeId,
963 },
964 CloseModal {
966 modal_node: DomNodeId,
967 },
968 ScrollFocusedContainer {
970 direction: ScrollDirection,
971 amount: ScrollAmount,
972 },
973 SelectAllText,
975 None,
977}
978
979#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
981#[repr(C)]
982pub enum ScrollAmount {
983 Line,
985 Page,
987 Document,
989}
990
991#[derive(Debug, Clone)]
998#[repr(C)]
999pub struct DefaultActionResult {
1000 pub action: DefaultAction,
1002 pub prevented: bool,
1004}
1005
1006impl Default for DefaultActionResult {
1007 fn default() -> Self {
1008 Self {
1009 action: DefaultAction::None,
1010 prevented: false,
1011 }
1012 }
1013}
1014
1015impl DefaultActionResult {
1016 pub fn new(action: DefaultAction) -> Self {
1018 Self {
1019 action,
1020 prevented: false,
1021 }
1022 }
1023
1024 pub fn prevented() -> Self {
1026 Self {
1027 action: DefaultAction::None,
1028 prevented: true,
1029 }
1030 }
1031
1032 pub fn has_action(&self) -> bool {
1034 !self.prevented && !matches!(self.action, DefaultAction::None)
1035 }
1036}
1037
1038pub trait ActivationBehavior {
1050 fn has_activation_behavior(&self) -> bool;
1052
1053 fn is_activatable(&self) -> bool;
1056}
1057
1058pub trait Focusable {
1060 fn get_tabindex(&self) -> Option<i32>;
1062
1063 fn is_focusable(&self) -> bool;
1065
1066 fn is_in_tab_order(&self) -> bool {
1068 match self.get_tabindex() {
1069 None => self.is_naturally_focusable(),
1070 Some(i) => i >= 0,
1071 }
1072 }
1073
1074 fn is_naturally_focusable(&self) -> bool;
1077}
1078
1079fn matches_filter_phase(
1084 filter: &EventFilter,
1085 event: &SyntheticEvent,
1086 current_phase: EventPhase,
1087) -> bool {
1088 match filter {
1092 EventFilter::Hover(hover_filter) => {
1093 matches_hover_filter(hover_filter, event, current_phase)
1094 }
1095 EventFilter::Focus(focus_filter) => {
1096 matches_focus_filter(focus_filter, event, current_phase)
1097 }
1098 EventFilter::Window(window_filter) => {
1099 matches_window_filter(window_filter, event, current_phase)
1100 }
1101 EventFilter::Component(component_filter) => {
1102 matches_component_filter(component_filter, event, current_phase)
1103 }
1104 EventFilter::Application(_) => {
1105 false
1107 }
1108 }
1109}
1110
1111fn matches_component_filter(
1119 filter: &ComponentEventFilter,
1120 event: &SyntheticEvent,
1121 _phase: EventPhase,
1122) -> bool {
1123 matches!(
1124 (filter, &event.event_type),
1125 (ComponentEventFilter::AfterMount, EventType::Mount)
1126 | (ComponentEventFilter::BeforeUnmount, EventType::Unmount)
1127 | (ComponentEventFilter::Updated, EventType::Update)
1128 | (ComponentEventFilter::NodeResized, EventType::Resize)
1129 )
1130}
1131
1132fn check_mouse_button(data: &EventData, expected: MouseButton) -> bool {
1134 if let EventData::Mouse(mouse_data) = data {
1135 mouse_data.button == expected
1136 } else {
1137 false
1138 }
1139}
1140
1141fn matches_hover_filter(
1143 filter: &HoverEventFilter,
1144 event: &SyntheticEvent,
1145 _phase: EventPhase,
1146) -> bool {
1147 use HoverEventFilter::*;
1148
1149 match (filter, &event.event_type) {
1150 (MouseOver, EventType::MouseOver) => true,
1151 (MouseDown, EventType::MouseDown) => true,
1152 (LeftMouseDown, EventType::MouseDown) => check_mouse_button(&event.data, MouseButton::Left),
1153 (RightMouseDown, EventType::MouseDown) => {
1154 check_mouse_button(&event.data, MouseButton::Right)
1155 }
1156 (MiddleMouseDown, EventType::MouseDown) => {
1157 check_mouse_button(&event.data, MouseButton::Middle)
1158 }
1159 (MouseUp, EventType::MouseUp) => true,
1160 (LeftMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Left),
1161 (RightMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Right),
1162 (MiddleMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Middle),
1163 (MouseEnter, EventType::MouseEnter) => true,
1164 (MouseLeave, EventType::MouseLeave) => true,
1165 (Scroll, EventType::Scroll) => true,
1166 (ScrollStart, EventType::ScrollStart) => true,
1167 (ScrollEnd, EventType::ScrollEnd) => true,
1168 (TextInput, EventType::Input) => true,
1169 (VirtualKeyDown, EventType::KeyDown) => true,
1170 (VirtualKeyUp, EventType::KeyUp) => true,
1171 (HoveredFile, EventType::FileHover) => true,
1172 (DroppedFile, EventType::FileDrop) => true,
1173 (HoveredFileCancelled, EventType::FileHoverCancel) => true,
1174 (TouchStart, EventType::TouchStart) => true,
1175 (TouchMove, EventType::TouchMove) => true,
1176 (TouchEnd, EventType::TouchEnd) => true,
1177 (TouchCancel, EventType::TouchCancel) => true,
1178 (PenDown, EventType::PenDown) => true,
1179 (PenMove, EventType::PenMove) => true,
1180 (PenUp, EventType::PenUp) => true,
1181 (PenEnter, EventType::PenEnter) => true,
1182 (PenLeave, EventType::PenLeave) => true,
1183 (DragStart, EventType::DragStart) => true,
1184 (Drag, EventType::Drag) => true,
1185 (DragEnd, EventType::DragEnd) => true,
1186 (DragEnter, EventType::DragEnter) => true,
1187 (DragOver, EventType::DragOver) => true,
1188 (DragLeave, EventType::DragLeave) => true,
1189 (Drop, EventType::Drop) => true,
1190 (DoubleClick, EventType::DoubleClick) => true,
1191 (SensorChanged, EventType::SensorChanged) => true,
1192 (GamepadInput, EventType::GamepadInput) => true,
1193 _ => false,
1194 }
1195}
1196
1197fn matches_focus_filter(
1199 filter: &FocusEventFilter,
1200 event: &SyntheticEvent,
1201 _phase: EventPhase,
1202) -> bool {
1203 use FocusEventFilter::*;
1204
1205 match (filter, &event.event_type) {
1206 (MouseOver, EventType::MouseOver) => true,
1207 (MouseDown, EventType::MouseDown) => true,
1208 (LeftMouseDown, EventType::MouseDown) => check_mouse_button(&event.data, MouseButton::Left),
1209 (RightMouseDown, EventType::MouseDown) => {
1210 check_mouse_button(&event.data, MouseButton::Right)
1211 }
1212 (MiddleMouseDown, EventType::MouseDown) => {
1213 check_mouse_button(&event.data, MouseButton::Middle)
1214 }
1215 (MouseUp, EventType::MouseUp) => true,
1216 (LeftMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Left),
1217 (RightMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Right),
1218 (MiddleMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Middle),
1219 (MouseEnter, EventType::MouseEnter) => true,
1220 (MouseLeave, EventType::MouseLeave) => true,
1221 (Scroll, EventType::Scroll) => true,
1222 (ScrollStart, EventType::ScrollStart) => true,
1223 (ScrollEnd, EventType::ScrollEnd) => true,
1224 (TextInput, EventType::Input) => true,
1225 (VirtualKeyDown, EventType::KeyDown) => true,
1226 (VirtualKeyUp, EventType::KeyUp) => true,
1227 (FocusReceived, EventType::Focus) => true,
1228 (FocusLost, EventType::Blur) => true,
1229 (DragStart, EventType::DragStart) => true,
1230 (Drag, EventType::Drag) => true,
1231 (DragEnd, EventType::DragEnd) => true,
1232 (DragEnter, EventType::DragEnter) => true,
1233 (DragOver, EventType::DragOver) => true,
1234 (DragLeave, EventType::DragLeave) => true,
1235 (Drop, EventType::Drop) => true,
1236 _ => false,
1237 }
1238}
1239
1240fn matches_window_filter(
1242 filter: &WindowEventFilter,
1243 event: &SyntheticEvent,
1244 _phase: EventPhase,
1245) -> bool {
1246 use WindowEventFilter::*;
1247
1248 match (filter, &event.event_type) {
1249 (MouseOver, EventType::MouseOver) => true,
1250 (MouseDown, EventType::MouseDown) => true,
1251 (LeftMouseDown, EventType::MouseDown) => check_mouse_button(&event.data, MouseButton::Left),
1252 (RightMouseDown, EventType::MouseDown) => {
1253 check_mouse_button(&event.data, MouseButton::Right)
1254 }
1255 (MiddleMouseDown, EventType::MouseDown) => {
1256 check_mouse_button(&event.data, MouseButton::Middle)
1257 }
1258 (MouseUp, EventType::MouseUp) => true,
1259 (LeftMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Left),
1260 (RightMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Right),
1261 (MiddleMouseUp, EventType::MouseUp) => check_mouse_button(&event.data, MouseButton::Middle),
1262 (MouseEnter, EventType::MouseEnter) => true,
1263 (MouseLeave, EventType::MouseLeave) => true,
1264 (Scroll, EventType::Scroll) => true,
1265 (ScrollStart, EventType::ScrollStart) => true,
1266 (ScrollEnd, EventType::ScrollEnd) => true,
1267 (TextInput, EventType::Input) => true,
1268 (VirtualKeyDown, EventType::KeyDown) => true,
1269 (VirtualKeyUp, EventType::KeyUp) => true,
1270 (HoveredFile, EventType::FileHover) => true,
1271 (DroppedFile, EventType::FileDrop) => true,
1272 (HoveredFileCancelled, EventType::FileHoverCancel) => true,
1273 (Resized, EventType::WindowResize) => true,
1274 (Moved, EventType::WindowMove) => true,
1275 (TouchStart, EventType::TouchStart) => true,
1276 (TouchMove, EventType::TouchMove) => true,
1277 (TouchEnd, EventType::TouchEnd) => true,
1278 (TouchCancel, EventType::TouchCancel) => true,
1279 (PenDown, EventType::PenDown) => true,
1280 (PenMove, EventType::PenMove) => true,
1281 (PenUp, EventType::PenUp) => true,
1282 (PenEnter, EventType::PenEnter) => true,
1283 (PenLeave, EventType::PenLeave) => true,
1284 (FocusReceived, EventType::Focus) => true,
1285 (FocusLost, EventType::Blur) => true,
1286 (CloseRequested, EventType::WindowClose) => true,
1287 (ThemeChanged, EventType::ThemeChange) => true,
1288 (WindowFocusReceived, EventType::WindowFocusIn) => true,
1289 (WindowFocusLost, EventType::WindowFocusOut) => true,
1290 (SensorChanged, EventType::SensorChanged) => true,
1291 (GamepadInput, EventType::GamepadInput) => true,
1292 (DragStart, EventType::DragStart) => true,
1293 (Drag, EventType::Drag) => true,
1294 (DragEnd, EventType::DragEnd) => true,
1295 (DragEnter, EventType::DragEnter) => true,
1296 (DragOver, EventType::DragOver) => true,
1297 (DragLeave, EventType::DragLeave) => true,
1298 (Drop, EventType::Drop) => true,
1299 _ => false,
1300 }
1301}
1302
1303pub fn detect_lifecycle_events(
1311 old_dom_id: DomId,
1312 new_dom_id: DomId,
1313 old_hierarchy: Option<&crate::id::NodeHierarchy>,
1314 new_hierarchy: Option<&crate::id::NodeHierarchy>,
1315 old_layout: Option<&BTreeMap<NodeId, LogicalRect>>,
1316 new_layout: Option<&BTreeMap<NodeId, LogicalRect>>,
1317 timestamp: Instant,
1318) -> Vec<SyntheticEvent> {
1319 let old_nodes = collect_node_ids(old_hierarchy);
1320 let new_nodes = collect_node_ids(new_hierarchy);
1321
1322 let mut events = Vec::new();
1323
1324 if let Some(layout) = new_layout {
1326 for &node_id in new_nodes.difference(&old_nodes) {
1327 events.push(create_mount_event(node_id, new_dom_id, layout, ×tamp));
1328 }
1329 }
1330
1331 if let Some(layout) = old_layout {
1333 for &node_id in old_nodes.difference(&new_nodes) {
1334 events.push(create_unmount_event(
1335 node_id, old_dom_id, layout, ×tamp,
1336 ));
1337 }
1338 }
1339
1340 if let (Some(old_l), Some(new_l)) = (old_layout, new_layout) {
1342 for &node_id in old_nodes.intersection(&new_nodes) {
1343 if let Some(ev) = create_resize_event(node_id, new_dom_id, old_l, new_l, ×tamp) {
1344 events.push(ev);
1345 }
1346 }
1347 }
1348
1349 events
1350}
1351
1352fn collect_node_ids(hierarchy: Option<&crate::id::NodeHierarchy>) -> BTreeSet<NodeId> {
1353 hierarchy
1354 .map(|h| h.as_ref().linear_iter().collect())
1355 .unwrap_or_default()
1356}
1357
1358fn create_lifecycle_event(
1359 event_type: EventType,
1360 node_id: NodeId,
1361 dom_id: DomId,
1362 timestamp: &Instant,
1363 data: LifecycleEventData,
1364) -> SyntheticEvent {
1365 let dom_node_id = DomNodeId {
1366 dom: dom_id,
1367 node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
1368 };
1369 SyntheticEvent {
1370 event_type,
1371 source: EventSource::Lifecycle,
1372 phase: EventPhase::Target,
1373 target: dom_node_id,
1374 current_target: dom_node_id,
1375 timestamp: timestamp.clone(),
1376 data: EventData::Lifecycle(data),
1377 stopped: false,
1378 stopped_immediate: false,
1379 prevented_default: false,
1380 }
1381}
1382
1383fn create_mount_event(
1384 node_id: NodeId,
1385 dom_id: DomId,
1386 layout: &BTreeMap<NodeId, LogicalRect>,
1387 timestamp: &Instant,
1388) -> SyntheticEvent {
1389 let current_bounds = layout.get(&node_id).copied().unwrap_or(LogicalRect::zero());
1390 create_lifecycle_event(
1391 EventType::Mount,
1392 node_id,
1393 dom_id,
1394 timestamp,
1395 LifecycleEventData {
1396 reason: LifecycleReason::InitialMount,
1397 previous_bounds: None,
1398 current_bounds,
1399 },
1400 )
1401}
1402
1403fn create_unmount_event(
1404 node_id: NodeId,
1405 dom_id: DomId,
1406 layout: &BTreeMap<NodeId, LogicalRect>,
1407 timestamp: &Instant,
1408) -> SyntheticEvent {
1409 let previous_bounds = layout.get(&node_id).copied().unwrap_or(LogicalRect::zero());
1410 create_lifecycle_event(
1411 EventType::Unmount,
1412 node_id,
1413 dom_id,
1414 timestamp,
1415 LifecycleEventData {
1416 reason: LifecycleReason::Unmount,
1417 previous_bounds: Some(previous_bounds),
1418 current_bounds: LogicalRect::zero(),
1419 },
1420 )
1421}
1422
1423fn create_resize_event(
1424 node_id: NodeId,
1425 dom_id: DomId,
1426 old_layout: &BTreeMap<NodeId, LogicalRect>,
1427 new_layout: &BTreeMap<NodeId, LogicalRect>,
1428 timestamp: &Instant,
1429) -> Option<SyntheticEvent> {
1430 let old_bounds = *old_layout.get(&node_id)?;
1431 let new_bounds = *new_layout.get(&node_id)?;
1432
1433 if old_bounds.size == new_bounds.size {
1434 return None;
1435 }
1436
1437 Some(create_lifecycle_event(
1438 EventType::Resize,
1439 node_id,
1440 dom_id,
1441 timestamp,
1442 LifecycleEventData {
1443 reason: LifecycleReason::Resize,
1444 previous_bounds: Some(old_bounds),
1445 current_bounds: new_bounds,
1446 },
1447 ))
1448}
1449
1450#[derive(Debug, Clone, Default)]
1455pub struct LifecycleEventResult {
1456 pub events: Vec<SyntheticEvent>,
1458 pub node_id_mapping: crate::OrderedMap<NodeId, NodeId>,
1461}
1462
1463pub fn detect_lifecycle_events_with_reconciliation(
1517 dom_id: DomId,
1518 old_node_data: &[crate::dom::NodeData],
1519 new_node_data: &[crate::dom::NodeData],
1520 old_hierarchy: &[crate::styled_dom::NodeHierarchyItem],
1521 new_hierarchy: &[crate::styled_dom::NodeHierarchyItem],
1522 old_layout: &crate::OrderedMap<NodeId, LogicalRect>,
1523 new_layout: &crate::OrderedMap<NodeId, LogicalRect>,
1524 timestamp: Instant,
1525) -> LifecycleEventResult {
1526 let diff_result = crate::diff::reconcile_dom(
1527 old_node_data,
1528 new_node_data,
1529 old_hierarchy,
1530 new_hierarchy,
1531 old_layout,
1532 new_layout,
1533 dom_id,
1534 timestamp,
1535 );
1536
1537 LifecycleEventResult {
1538 events: diff_result.events,
1539 node_id_mapping: crate::diff::create_migration_map(&diff_result.node_moves),
1540 }
1541}
1542
1543#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1545#[repr(C)]
1546pub enum HoverEventFilter {
1547 MouseOver,
1549 MouseDown,
1551 LeftMouseDown,
1553 RightMouseDown,
1555 MiddleMouseDown,
1557 MouseUp,
1559 LeftMouseUp,
1561 RightMouseUp,
1563 MiddleMouseUp,
1565 MouseEnter,
1567 MouseLeave,
1569 Scroll,
1571 ScrollStart,
1573 ScrollEnd,
1575 TextInput,
1577 VirtualKeyDown,
1579 VirtualKeyUp,
1581 HoveredFile,
1583 DroppedFile,
1585 HoveredFileCancelled,
1587 TouchStart,
1589 TouchMove,
1591 TouchEnd,
1593 TouchCancel,
1595 PenDown,
1597 PenMove,
1599 PenUp,
1601 PenEnter,
1603 PenLeave,
1605 PenSqueeze,
1610 PenDoubleTap,
1613 PenHover,
1619 GeolocationFix,
1623 GeolocationError,
1626 SensorChanged,
1629 GamepadInput,
1632 DragStart,
1634 Drag,
1636 DragEnd,
1638 DragEnter,
1640 DragOver,
1642 DragLeave,
1644 Drop,
1646 DoubleClick,
1648 LongPress,
1650 SwipeLeft,
1652 SwipeRight,
1654 SwipeUp,
1656 SwipeDown,
1658 PinchIn,
1660 PinchOut,
1662 RotateClockwise,
1664 RotateCounterClockwise,
1666
1667 MouseOut,
1670
1671 FocusIn,
1674 FocusOut,
1676
1677 CompositionStart,
1680 CompositionUpdate,
1682 CompositionEnd,
1684
1685 #[doc(hidden)]
1687 SystemTextSingleClick,
1689 #[doc(hidden)]
1690 SystemTextDoubleClick,
1692 #[doc(hidden)]
1693 SystemTextTripleClick,
1695}
1696
1697impl HoverEventFilter {
1698 pub const fn is_system_internal(&self) -> bool {
1700 matches!(
1701 self,
1702 HoverEventFilter::SystemTextSingleClick
1703 | HoverEventFilter::SystemTextDoubleClick
1704 | HoverEventFilter::SystemTextTripleClick
1705 )
1706 }
1707
1708 pub fn to_focus_event_filter(&self) -> Option<FocusEventFilter> {
1709 match self {
1710 HoverEventFilter::MouseOver => Some(FocusEventFilter::MouseOver),
1711 HoverEventFilter::MouseDown => Some(FocusEventFilter::MouseDown),
1712 HoverEventFilter::LeftMouseDown => Some(FocusEventFilter::LeftMouseDown),
1713 HoverEventFilter::RightMouseDown => Some(FocusEventFilter::RightMouseDown),
1714 HoverEventFilter::MiddleMouseDown => Some(FocusEventFilter::MiddleMouseDown),
1715 HoverEventFilter::MouseUp => Some(FocusEventFilter::MouseUp),
1716 HoverEventFilter::LeftMouseUp => Some(FocusEventFilter::LeftMouseUp),
1717 HoverEventFilter::RightMouseUp => Some(FocusEventFilter::RightMouseUp),
1718 HoverEventFilter::MiddleMouseUp => Some(FocusEventFilter::MiddleMouseUp),
1719 HoverEventFilter::MouseEnter => Some(FocusEventFilter::MouseEnter),
1720 HoverEventFilter::MouseLeave => Some(FocusEventFilter::MouseLeave),
1721 HoverEventFilter::Scroll => Some(FocusEventFilter::Scroll),
1722 HoverEventFilter::ScrollStart => Some(FocusEventFilter::ScrollStart),
1723 HoverEventFilter::ScrollEnd => Some(FocusEventFilter::ScrollEnd),
1724 HoverEventFilter::TextInput => Some(FocusEventFilter::TextInput),
1725 HoverEventFilter::VirtualKeyDown => Some(FocusEventFilter::VirtualKeyDown),
1726 HoverEventFilter::VirtualKeyUp => Some(FocusEventFilter::VirtualKeyUp),
1727 HoverEventFilter::HoveredFile => None,
1728 HoverEventFilter::DroppedFile => None,
1729 HoverEventFilter::HoveredFileCancelled => None,
1730 HoverEventFilter::TouchStart => None,
1731 HoverEventFilter::TouchMove => None,
1732 HoverEventFilter::TouchEnd => None,
1733 HoverEventFilter::TouchCancel => None,
1734 HoverEventFilter::PenDown => Some(FocusEventFilter::PenDown),
1735 HoverEventFilter::PenMove => Some(FocusEventFilter::PenMove),
1736 HoverEventFilter::PenUp => Some(FocusEventFilter::PenUp),
1737 HoverEventFilter::PenEnter => None,
1738 HoverEventFilter::PenLeave => None,
1739 HoverEventFilter::PenSqueeze => None,
1740 HoverEventFilter::PenDoubleTap => None,
1741 HoverEventFilter::PenHover => None,
1742 HoverEventFilter::GeolocationFix => None,
1743 HoverEventFilter::GeolocationError => None,
1744 HoverEventFilter::SensorChanged => None,
1745 HoverEventFilter::GamepadInput => None,
1746 HoverEventFilter::DragStart => Some(FocusEventFilter::DragStart),
1747 HoverEventFilter::Drag => Some(FocusEventFilter::Drag),
1748 HoverEventFilter::DragEnd => Some(FocusEventFilter::DragEnd),
1749 HoverEventFilter::DragEnter => Some(FocusEventFilter::DragEnter),
1750 HoverEventFilter::DragOver => Some(FocusEventFilter::DragOver),
1751 HoverEventFilter::DragLeave => Some(FocusEventFilter::DragLeave),
1752 HoverEventFilter::Drop => Some(FocusEventFilter::Drop),
1753 HoverEventFilter::DoubleClick => Some(FocusEventFilter::DoubleClick),
1754 HoverEventFilter::LongPress => Some(FocusEventFilter::LongPress),
1755 HoverEventFilter::SwipeLeft => Some(FocusEventFilter::SwipeLeft),
1756 HoverEventFilter::SwipeRight => Some(FocusEventFilter::SwipeRight),
1757 HoverEventFilter::SwipeUp => Some(FocusEventFilter::SwipeUp),
1758 HoverEventFilter::SwipeDown => Some(FocusEventFilter::SwipeDown),
1759 HoverEventFilter::PinchIn => Some(FocusEventFilter::PinchIn),
1760 HoverEventFilter::PinchOut => Some(FocusEventFilter::PinchOut),
1761 HoverEventFilter::RotateClockwise => Some(FocusEventFilter::RotateClockwise),
1762 HoverEventFilter::RotateCounterClockwise => {
1763 Some(FocusEventFilter::RotateCounterClockwise)
1764 }
1765 HoverEventFilter::MouseOut => Some(FocusEventFilter::MouseLeave), HoverEventFilter::FocusIn => Some(FocusEventFilter::FocusIn),
1767 HoverEventFilter::FocusOut => Some(FocusEventFilter::FocusOut),
1768 HoverEventFilter::CompositionStart => Some(FocusEventFilter::CompositionStart),
1769 HoverEventFilter::CompositionUpdate => Some(FocusEventFilter::CompositionUpdate),
1770 HoverEventFilter::CompositionEnd => Some(FocusEventFilter::CompositionEnd),
1771 HoverEventFilter::SystemTextSingleClick => None,
1773 HoverEventFilter::SystemTextDoubleClick => None,
1774 HoverEventFilter::SystemTextTripleClick => None,
1775 }
1776 }
1777}
1778
1779#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1784#[repr(C)]
1785pub enum FocusEventFilter {
1786 MouseOver,
1788 MouseDown,
1790 LeftMouseDown,
1792 RightMouseDown,
1794 MiddleMouseDown,
1796 MouseUp,
1798 LeftMouseUp,
1800 RightMouseUp,
1802 MiddleMouseUp,
1804 MouseEnter,
1806 MouseLeave,
1808 Scroll,
1810 ScrollStart,
1812 ScrollEnd,
1814 TextInput,
1816 VirtualKeyDown,
1818 VirtualKeyUp,
1820 FocusReceived,
1822 FocusLost,
1824 PenDown,
1826 PenMove,
1828 PenUp,
1830 DragStart,
1832 Drag,
1834 DragEnd,
1836 DragEnter,
1838 DragOver,
1840 DragLeave,
1842 Drop,
1844 DoubleClick,
1846 LongPress,
1848 SwipeLeft,
1850 SwipeRight,
1852 SwipeUp,
1854 SwipeDown,
1856 PinchIn,
1858 PinchOut,
1860 RotateClockwise,
1862 RotateCounterClockwise,
1864
1865 FocusIn,
1868 FocusOut,
1870
1871 CompositionStart,
1874 CompositionUpdate,
1876 CompositionEnd,
1878}
1879
1880#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1883#[repr(C)]
1884pub enum WindowEventFilter {
1885 MouseOver,
1887 MouseDown,
1889 LeftMouseDown,
1891 RightMouseDown,
1893 MiddleMouseDown,
1895 MouseUp,
1897 LeftMouseUp,
1899 RightMouseUp,
1901 MiddleMouseUp,
1903 MouseEnter,
1905 MouseLeave,
1907 Scroll,
1909 ScrollStart,
1911 ScrollEnd,
1913 TextInput,
1915 VirtualKeyDown,
1917 VirtualKeyUp,
1919 HoveredFile,
1921 DroppedFile,
1923 HoveredFileCancelled,
1925 Resized,
1927 Moved,
1929 TouchStart,
1931 TouchMove,
1933 TouchEnd,
1935 TouchCancel,
1937 FocusReceived,
1939 FocusLost,
1941 CloseRequested,
1943 ThemeChanged,
1945 WindowFocusReceived,
1947 WindowFocusLost,
1949 PenDown,
1951 PenMove,
1953 PenUp,
1955 PenEnter,
1957 PenLeave,
1959 PenSqueeze,
1962 PenDoubleTap,
1965 PenHover,
1968 GeolocationFix,
1976 GeolocationError,
1979 SensorChanged,
1982 GamepadInput,
1985 DragStart,
1987 Drag,
1989 DragEnd,
1991 DragEnter,
1993 DragOver,
1995 DragLeave,
1997 Drop,
1999 DoubleClick,
2001 LongPress,
2003 SwipeLeft,
2005 SwipeRight,
2007 SwipeUp,
2009 SwipeDown,
2011 PinchIn,
2013 PinchOut,
2015 RotateClockwise,
2017 RotateCounterClockwise,
2019 DpiChanged,
2022 MonitorChanged,
2025}
2026
2027impl WindowEventFilter {
2028 pub fn to_hover_event_filter(&self) -> Option<HoverEventFilter> {
2029 match self {
2030 WindowEventFilter::MouseOver => Some(HoverEventFilter::MouseOver),
2031 WindowEventFilter::MouseDown => Some(HoverEventFilter::MouseDown),
2032 WindowEventFilter::LeftMouseDown => Some(HoverEventFilter::LeftMouseDown),
2033 WindowEventFilter::RightMouseDown => Some(HoverEventFilter::RightMouseDown),
2034 WindowEventFilter::MiddleMouseDown => Some(HoverEventFilter::MiddleMouseDown),
2035 WindowEventFilter::MouseUp => Some(HoverEventFilter::MouseUp),
2036 WindowEventFilter::LeftMouseUp => Some(HoverEventFilter::LeftMouseUp),
2037 WindowEventFilter::RightMouseUp => Some(HoverEventFilter::RightMouseUp),
2038 WindowEventFilter::MiddleMouseUp => Some(HoverEventFilter::MiddleMouseUp),
2039 WindowEventFilter::Scroll => Some(HoverEventFilter::Scroll),
2040 WindowEventFilter::ScrollStart => Some(HoverEventFilter::ScrollStart),
2041 WindowEventFilter::ScrollEnd => Some(HoverEventFilter::ScrollEnd),
2042 WindowEventFilter::TextInput => Some(HoverEventFilter::TextInput),
2043 WindowEventFilter::VirtualKeyDown => Some(HoverEventFilter::VirtualKeyDown),
2044 WindowEventFilter::VirtualKeyUp => Some(HoverEventFilter::VirtualKeyUp),
2045 WindowEventFilter::HoveredFile => Some(HoverEventFilter::HoveredFile),
2046 WindowEventFilter::DroppedFile => Some(HoverEventFilter::DroppedFile),
2047 WindowEventFilter::HoveredFileCancelled => Some(HoverEventFilter::HoveredFileCancelled),
2048 WindowEventFilter::MouseEnter => None,
2051 WindowEventFilter::MouseLeave => None,
2052 WindowEventFilter::Resized => None,
2053 WindowEventFilter::Moved => None,
2054 WindowEventFilter::TouchStart => Some(HoverEventFilter::TouchStart),
2055 WindowEventFilter::TouchMove => Some(HoverEventFilter::TouchMove),
2056 WindowEventFilter::TouchEnd => Some(HoverEventFilter::TouchEnd),
2057 WindowEventFilter::TouchCancel => Some(HoverEventFilter::TouchCancel),
2058 WindowEventFilter::FocusReceived => None,
2059 WindowEventFilter::FocusLost => None,
2060 WindowEventFilter::CloseRequested => None,
2061 WindowEventFilter::ThemeChanged => None,
2062 WindowEventFilter::WindowFocusReceived => None, WindowEventFilter::WindowFocusLost => None, WindowEventFilter::PenDown => Some(HoverEventFilter::PenDown),
2065 WindowEventFilter::PenMove => Some(HoverEventFilter::PenMove),
2066 WindowEventFilter::PenUp => Some(HoverEventFilter::PenUp),
2067 WindowEventFilter::PenEnter => Some(HoverEventFilter::PenEnter),
2068 WindowEventFilter::PenLeave => Some(HoverEventFilter::PenLeave),
2069 WindowEventFilter::PenSqueeze => Some(HoverEventFilter::PenSqueeze),
2070 WindowEventFilter::PenDoubleTap => Some(HoverEventFilter::PenDoubleTap),
2071 WindowEventFilter::PenHover => Some(HoverEventFilter::PenHover),
2072 WindowEventFilter::GeolocationFix => Some(HoverEventFilter::GeolocationFix),
2073 WindowEventFilter::GeolocationError => Some(HoverEventFilter::GeolocationError),
2074 WindowEventFilter::SensorChanged => Some(HoverEventFilter::SensorChanged),
2075 WindowEventFilter::GamepadInput => Some(HoverEventFilter::GamepadInput),
2076 WindowEventFilter::DragStart => Some(HoverEventFilter::DragStart),
2077 WindowEventFilter::Drag => Some(HoverEventFilter::Drag),
2078 WindowEventFilter::DragEnd => Some(HoverEventFilter::DragEnd),
2079 WindowEventFilter::DragEnter => Some(HoverEventFilter::DragEnter),
2080 WindowEventFilter::DragOver => Some(HoverEventFilter::DragOver),
2081 WindowEventFilter::DragLeave => Some(HoverEventFilter::DragLeave),
2082 WindowEventFilter::Drop => Some(HoverEventFilter::Drop),
2083 WindowEventFilter::DoubleClick => Some(HoverEventFilter::DoubleClick),
2084 WindowEventFilter::LongPress => Some(HoverEventFilter::LongPress),
2085 WindowEventFilter::SwipeLeft => Some(HoverEventFilter::SwipeLeft),
2086 WindowEventFilter::SwipeRight => Some(HoverEventFilter::SwipeRight),
2087 WindowEventFilter::SwipeUp => Some(HoverEventFilter::SwipeUp),
2088 WindowEventFilter::SwipeDown => Some(HoverEventFilter::SwipeDown),
2089 WindowEventFilter::PinchIn => Some(HoverEventFilter::PinchIn),
2090 WindowEventFilter::PinchOut => Some(HoverEventFilter::PinchOut),
2091 WindowEventFilter::RotateClockwise => Some(HoverEventFilter::RotateClockwise),
2092 WindowEventFilter::RotateCounterClockwise => {
2093 Some(HoverEventFilter::RotateCounterClockwise)
2094 }
2095 WindowEventFilter::DpiChanged => None,
2097 WindowEventFilter::MonitorChanged => None,
2098 }
2099 }
2100}
2101
2102#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2104#[repr(C)]
2105pub enum ComponentEventFilter {
2106 AfterMount,
2108 BeforeUnmount,
2110 NodeResized,
2112 DefaultAction,
2114 Selected,
2116 Updated,
2118}
2119
2120#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2122#[repr(C)]
2123pub enum ApplicationEventFilter {
2124 DeviceConnected,
2126 DeviceDisconnected,
2128 MonitorConnected,
2131 MonitorDisconnected,
2133}
2134
2135#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2140#[repr(C, u8)]
2141pub enum EventFilter {
2142 Hover(HoverEventFilter),
2145 Focus(FocusEventFilter),
2147 Window(WindowEventFilter),
2157 Component(ComponentEventFilter),
2159 Application(ApplicationEventFilter),
2161}
2162
2163impl EventFilter {
2164 pub const fn is_focus_callback(&self) -> bool {
2165 match self {
2166 EventFilter::Focus(_) => true,
2167 _ => false,
2168 }
2169 }
2170 pub const fn is_window_callback(&self) -> bool {
2171 match self {
2172 EventFilter::Window(_) => true,
2173 _ => false,
2174 }
2175 }
2176}
2177
2178macro_rules! get_single_enum_type {
2181 ($fn_name:ident, $enum_name:ident:: $variant:ident($return_type:ty)) => {
2182 pub fn $fn_name(&self) -> Option<$return_type> {
2183 use self::$enum_name::*;
2184 match self {
2185 $variant(e) => Some(*e),
2186 _ => None,
2187 }
2188 }
2189 };
2190}
2191
2192impl EventFilter {
2193 get_single_enum_type!(as_hover_event_filter, EventFilter::Hover(HoverEventFilter));
2194 get_single_enum_type!(as_focus_event_filter, EventFilter::Focus(FocusEventFilter));
2195 get_single_enum_type!(
2196 as_window_event_filter,
2197 EventFilter::Window(WindowEventFilter)
2198 );
2199}
2200
2201impl From<On> for EventFilter {
2207 fn from(input: On) -> EventFilter {
2208 use crate::dom::On::*;
2209 match input {
2210 MouseOver => EventFilter::Hover(HoverEventFilter::MouseOver),
2211 MouseDown => EventFilter::Hover(HoverEventFilter::MouseDown),
2212 LeftMouseDown => EventFilter::Hover(HoverEventFilter::LeftMouseDown),
2213 MiddleMouseDown => EventFilter::Hover(HoverEventFilter::MiddleMouseDown),
2214 RightMouseDown => EventFilter::Hover(HoverEventFilter::RightMouseDown),
2215 MouseUp => EventFilter::Hover(HoverEventFilter::MouseUp),
2216 LeftMouseUp => EventFilter::Hover(HoverEventFilter::LeftMouseUp),
2217 MiddleMouseUp => EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
2218 RightMouseUp => EventFilter::Hover(HoverEventFilter::RightMouseUp),
2219
2220 MouseEnter => EventFilter::Hover(HoverEventFilter::MouseEnter),
2221 MouseLeave => EventFilter::Hover(HoverEventFilter::MouseLeave),
2222 Scroll => EventFilter::Hover(HoverEventFilter::Scroll),
2223 TextInput => EventFilter::Focus(FocusEventFilter::TextInput), VirtualKeyDown => EventFilter::Window(WindowEventFilter::VirtualKeyDown), VirtualKeyUp => EventFilter::Window(WindowEventFilter::VirtualKeyUp), HoveredFile => EventFilter::Hover(HoverEventFilter::HoveredFile),
2227 DroppedFile => EventFilter::Hover(HoverEventFilter::DroppedFile),
2228 HoveredFileCancelled => EventFilter::Hover(HoverEventFilter::HoveredFileCancelled),
2229 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), }
2239 }
2240}
2241
2242pub trait EventProvider {
2253 fn get_pending_events(&self, timestamp: Instant) -> Vec<SyntheticEvent>;
2265}
2266
2267pub fn deduplicate_synthetic_events(mut events: Vec<SyntheticEvent>) -> Vec<SyntheticEvent> {
2271 if events.len() <= 1 {
2272 return events;
2273 }
2274
2275 events.sort_by_key(|e| (e.target.dom, e.target.node, e.event_type));
2276
2277 let mut result = Vec::with_capacity(events.len());
2279 let mut iter = events.into_iter();
2280
2281 if let Some(mut prev) = iter.next() {
2282 for curr in iter {
2283 if prev.target == curr.target && prev.event_type == curr.event_type {
2284 prev = if curr.timestamp > prev.timestamp {
2286 curr
2287 } else {
2288 prev
2289 };
2290 } else {
2291 result.push(prev);
2292 prev = curr;
2293 }
2294 }
2295 result.push(prev);
2296 }
2297
2298 result
2299}
2300
2301
2302
2303pub fn event_type_to_filters(event_type: EventType, event_data: &EventData) -> Vec<EventFilter> {
2308 use EventFilter as EF;
2309 use EventType as E;
2310 use FocusEventFilter as F;
2311 use HoverEventFilter as H;
2312 use WindowEventFilter as W;
2313
2314 let button_specific_down = || -> Option<EventFilter> {
2316 match event_data {
2317 EventData::Mouse(m) => match m.button {
2318 MouseButton::Left => Some(EF::Hover(H::LeftMouseDown)),
2319 MouseButton::Right => Some(EF::Hover(H::RightMouseDown)),
2320 MouseButton::Middle => Some(EF::Hover(H::MiddleMouseDown)),
2321 MouseButton::Other(_) => None, },
2323 _ => Some(EF::Hover(H::LeftMouseDown)), }
2325 };
2326
2327 let button_specific_up = || -> Option<EventFilter> {
2328 match event_data {
2329 EventData::Mouse(m) => match m.button {
2330 MouseButton::Left => Some(EF::Hover(H::LeftMouseUp)),
2331 MouseButton::Right => Some(EF::Hover(H::RightMouseUp)),
2332 MouseButton::Middle => Some(EF::Hover(H::MiddleMouseUp)),
2333 MouseButton::Other(_) => None, },
2335 _ => Some(EF::Hover(H::LeftMouseUp)), }
2337 };
2338
2339 match event_type {
2340 E::MouseDown => {
2342 let mut v = vec![EF::Hover(H::MouseDown)];
2343 if let Some(f) = button_specific_down() { v.push(f); }
2344 v
2345 }
2346 E::MouseUp => {
2347 let mut v = vec![EF::Hover(H::MouseUp)];
2348 if let Some(f) = button_specific_up() { v.push(f); }
2349 v
2350 }
2351
2352 E::Click => vec![EF::Hover(H::LeftMouseDown)],
2354
2355 E::MouseOver => vec![EF::Hover(H::MouseOver)],
2357 E::MouseEnter => vec![EF::Hover(H::MouseEnter)],
2358 E::MouseLeave => vec![EF::Hover(H::MouseLeave)],
2359 E::MouseOut => vec![EF::Hover(H::MouseOut)],
2360
2361 E::DoubleClick => vec![EF::Hover(H::DoubleClick), EF::Window(W::DoubleClick)],
2362 E::ContextMenu => vec![EF::Hover(H::RightMouseDown)],
2363
2364 E::KeyDown => vec![EF::Focus(F::VirtualKeyDown)],
2366 E::KeyUp => vec![EF::Focus(F::VirtualKeyUp)],
2367 E::KeyPress => vec![EF::Focus(F::TextInput)],
2368
2369 E::CompositionStart => vec![EF::Hover(H::CompositionStart), EF::Focus(F::CompositionStart)],
2371 E::CompositionUpdate => vec![EF::Hover(H::CompositionUpdate), EF::Focus(F::CompositionUpdate)],
2372 E::CompositionEnd => vec![EF::Hover(H::CompositionEnd), EF::Focus(F::CompositionEnd)],
2373
2374 E::Focus => vec![EF::Focus(F::FocusReceived)],
2376 E::Blur => vec![EF::Focus(F::FocusLost)],
2377 E::FocusIn => vec![EF::Hover(H::FocusIn), EF::Focus(F::FocusIn)],
2378 E::FocusOut => vec![EF::Hover(H::FocusOut), EF::Focus(F::FocusOut)],
2379
2380 E::Input | E::Change => vec![EF::Focus(F::TextInput)],
2382
2383 E::Scroll | E::ScrollStart | E::ScrollEnd => vec![EF::Hover(H::Scroll)],
2385
2386 E::DragStart => vec![EF::Hover(H::DragStart), EF::Window(W::DragStart)],
2388 E::Drag => vec![EF::Hover(H::Drag), EF::Window(W::Drag)],
2389 E::DragEnd => vec![EF::Hover(H::DragEnd), EF::Window(W::DragEnd)],
2390 E::DragEnter => vec![EF::Hover(H::DragEnter), EF::Window(W::DragEnter)],
2391 E::DragOver => vec![EF::Hover(H::DragOver), EF::Window(W::DragOver)],
2392 E::DragLeave => vec![EF::Hover(H::DragLeave), EF::Window(W::DragLeave)],
2393 E::Drop => vec![EF::Hover(H::Drop), EF::Window(W::Drop)],
2394
2395 E::TouchStart => vec![EF::Hover(H::TouchStart)],
2397 E::TouchMove => vec![EF::Hover(H::TouchMove)],
2398 E::TouchEnd => vec![EF::Hover(H::TouchEnd)],
2399 E::TouchCancel => vec![EF::Hover(H::TouchCancel)],
2400
2401 E::WindowResize => vec![EF::Window(W::Resized)],
2403 E::WindowMove => vec![EF::Window(W::Moved)],
2404 E::WindowClose => vec![EF::Window(W::CloseRequested)],
2405 E::WindowFocusIn => vec![EF::Window(W::WindowFocusReceived)],
2406 E::WindowFocusOut => vec![EF::Window(W::WindowFocusLost)],
2407 E::ThemeChange => vec![EF::Window(W::ThemeChanged)],
2408 E::WindowDpiChanged => vec![EF::Window(W::DpiChanged)],
2409 E::WindowMonitorChanged => vec![EF::Window(W::MonitorChanged)],
2410
2411 E::MonitorConnected => vec![EF::Application(ApplicationEventFilter::MonitorConnected)],
2413 E::MonitorDisconnected => vec![EF::Application(ApplicationEventFilter::MonitorDisconnected)],
2414
2415 E::FileHover => vec![EF::Hover(H::HoveredFile)],
2417 E::FileDrop => vec![EF::Hover(H::DroppedFile)],
2418 E::FileHoverCancel => vec![EF::Hover(H::HoveredFileCancelled)],
2419
2420 E::Mount => vec![EF::Component(ComponentEventFilter::AfterMount)],
2425 E::Unmount => vec![EF::Component(ComponentEventFilter::BeforeUnmount)],
2426 E::Update => vec![EF::Component(ComponentEventFilter::Updated)],
2427 E::Resize => vec![EF::Component(ComponentEventFilter::NodeResized)],
2428
2429 E::SensorChanged => vec![EF::Hover(H::SensorChanged), EF::Window(W::SensorChanged)],
2432 E::GamepadInput => vec![EF::Hover(H::GamepadInput), EF::Window(W::GamepadInput)],
2433
2434 _ => vec![],
2436 }
2437}
2438
2439
2440
2441#[derive(Debug, Clone, PartialEq)]
2453#[must_use = "SystemChange must be processed through apply_system_change()"]
2454pub enum SystemChange {
2455 TextSelectionClick {
2459 position: LogicalPosition,
2460 timestamp: Instant,
2461 },
2462 TextSelectionDrag {
2464 start_position: LogicalPosition,
2465 current_position: LogicalPosition,
2466 },
2467 ApplySelectionOp {
2472 target: DomNodeId,
2473 op: SelectionOp,
2474 },
2475
2476 CopyToClipboard,
2480 CutToClipboard { target: DomNodeId },
2482 PasteFromClipboard,
2484 SelectAllText,
2486 UndoTextEdit { target: DomNodeId },
2488 RedoTextEdit { target: DomNodeId },
2490
2491 AddCursorAtClick {
2496 position: LogicalPosition,
2497 },
2498 SelectNextOccurrence {
2501 target: DomNodeId,
2502 },
2503
2504 ApplyPendingTextInput,
2508 ApplyTextChangeset,
2510
2511 ActivateNodeDrag {
2515 dom_id: crate::dom::DomId,
2516 node_id: crate::id::NodeId,
2517 },
2518 ActivateWindowDrag,
2520 InitDragVisualState,
2522 SetDragOverState { target: DomNodeId, active: bool },
2524 UpdateDropTarget { target: DomNodeId },
2526 UpdateDragGpuTransform,
2528 DeactivateDrag,
2530
2531 SetFocus {
2537 new_focus: Option<DomNodeId>,
2538 old_focus: Option<DomNodeId>,
2539 },
2540 ClearAllSelections,
2542 FinalizePendingFocusChanges,
2544
2545 ScrollSelectionIntoView,
2549 ScrollNodeIntoView { target: DomNodeId },
2551 ScrollCursorIntoViewAfterTextInput,
2553
2554 StartAutoScrollTimer,
2558 StopAutoScrollTimer,
2560}
2561
2562impl_option!(
2563 SystemChange,
2564 OptionSystemChange,
2565 copy = false,
2566 clone = false,
2567 [Debug, Clone, PartialEq]
2568);
2569
2570impl_vec!(SystemChange, SystemChangeVec, SystemChangeVecDestructor, SystemChangeVecDestructorType, SystemChangeVecSlice, OptionSystemChange);
2571impl_vec_debug!(SystemChange, SystemChangeVec);
2572impl_vec_clone!(SystemChange, SystemChangeVec, SystemChangeVecDestructor);
2573impl_vec_partialeq!(SystemChange, SystemChangeVec);
2574
2575#[derive(Debug, Clone, PartialEq)]
2577pub struct PreCallbackFilterResult {
2578 pub system_changes: Vec<SystemChange>,
2580 pub user_events: Vec<SyntheticEvent>,
2582}
2583
2584#[derive(Debug, Clone)]
2586pub struct InputInterpreterState {
2587 pub focused_node: Option<DomNodeId>,
2588 pub click_count: u8,
2589 pub drag_start_position: Option<LogicalPosition>,
2590 pub has_selection: bool,
2591}
2592
2593pub struct InputInterpreterInfo<'a> {
2598 pub events: &'a [SyntheticEvent],
2599 pub hit_test: Option<&'a FullHitTest>,
2600 pub keyboard_state: &'a crate::window::KeyboardState,
2601 pub mouse_state: &'a crate::window::MouseState,
2602 pub state: InputInterpreterState,
2603}
2604
2605pub type InputInterpreterCallbackType = extern "C" fn(
2615 crate::refany::RefAny,
2616 *const InputInterpreterInfo<'static>, ) -> PreCallbackFilterResult;
2618
2619#[repr(C)]
2629pub struct InputInterpreterCallback {
2630 pub cb: InputInterpreterCallbackType,
2631 pub ctx: crate::refany::OptionRefAny,
2632}
2633
2634impl_callback!(InputInterpreterCallback, InputInterpreterCallbackType);
2635
2636impl Default for InputInterpreterCallback {
2637 fn default() -> Self {
2638 Self {
2639 cb: default_input_interpreter_extern,
2640 ctx: crate::refany::OptionRefAny::None,
2641 }
2642 }
2643}
2644
2645pub type PostFilterCallbackType = extern "C" fn(
2647 crate::refany::RefAny,
2648 bool, SystemChangeVecSlice, DomNodeId, DomNodeId, ) -> SystemChangeVec;
2653
2654#[repr(C)]
2656pub struct PostFilterCallback {
2657 pub cb: PostFilterCallbackType,
2658 pub ctx: crate::refany::OptionRefAny,
2659}
2660
2661impl_callback!(PostFilterCallback, PostFilterCallbackType);
2662
2663impl Default for PostFilterCallback {
2664 fn default() -> Self {
2665 Self {
2666 cb: default_post_filter_extern,
2667 ctx: crate::refany::OptionRefAny::None,
2668 }
2669 }
2670}
2671
2672pub type InputInterpreterFn = fn(
2674 info: &InputInterpreterInfo,
2675) -> PreCallbackFilterResult;
2676
2677pub type PostFilterFn = fn(
2678 prevent_default: bool,
2679 pre_changes: &[SystemChange],
2680 old_focus: Option<DomNodeId>,
2681 new_focus: Option<DomNodeId>,
2682) -> Vec<SystemChange>;
2683
2684#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2686pub struct MouseButtonState {
2687 pub left_down: bool,
2688 pub right_down: bool,
2689 pub middle_down: bool,
2690}
2691
2692#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2694pub enum ArrowDirection {
2695 Left,
2696 Right,
2697 Up,
2698 Down,
2699 LineStart,
2701 LineEnd,
2703 DocumentStart,
2705 DocumentEnd,
2707}
2708
2709impl ArrowDirection {
2710 pub fn from_key(vk: crate::window::VirtualKeyCode, ctrl: bool) -> Option<Self> {
2713 use crate::window::VirtualKeyCode::*;
2714 Some(match vk {
2715 Left => ArrowDirection::Left,
2716 Right => ArrowDirection::Right,
2717 Up => ArrowDirection::Up,
2718 Down => ArrowDirection::Down,
2719 Home if ctrl => ArrowDirection::DocumentStart,
2720 Home => ArrowDirection::LineStart,
2721 End if ctrl => ArrowDirection::DocumentEnd,
2722 End => ArrowDirection::LineEnd,
2723 _ => return None,
2724 })
2725 }
2726
2727 pub fn to_selection(self, ctrl: bool) -> (SelectionDirection, SelectionStep) {
2730 match self {
2731 ArrowDirection::Left if ctrl => (SelectionDirection::Backward, SelectionStep::Word),
2732 ArrowDirection::Right if ctrl => (SelectionDirection::Forward, SelectionStep::Word),
2733 ArrowDirection::Left => (SelectionDirection::Backward, SelectionStep::Character),
2734 ArrowDirection::Right => (SelectionDirection::Forward, SelectionStep::Character),
2735 ArrowDirection::Up => (SelectionDirection::Backward, SelectionStep::VisualLine),
2736 ArrowDirection::Down => (SelectionDirection::Forward, SelectionStep::VisualLine),
2737 ArrowDirection::LineStart => (SelectionDirection::Backward, SelectionStep::Line),
2738 ArrowDirection::LineEnd => (SelectionDirection::Forward, SelectionStep::Line),
2739 ArrowDirection::DocumentStart => (SelectionDirection::Backward, SelectionStep::Document),
2740 ArrowDirection::DocumentEnd => (SelectionDirection::Forward, SelectionStep::Document),
2741 }
2742 }
2743}
2744
2745#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2747#[repr(C)]
2748pub enum SelectionDirection {
2749 Forward,
2750 Backward,
2751}
2752
2753#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2759#[repr(C)]
2760pub enum SelectionStep {
2761 Character,
2763 Word,
2765 Line,
2767 VisualLine,
2769 Document,
2771}
2772
2773#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2775#[repr(C)]
2776pub enum SelectionMode {
2777 Move,
2779 Extend,
2781 Delete,
2784}
2785
2786#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2798#[repr(C)]
2799pub struct SelectionOp {
2800 pub direction: SelectionDirection,
2801 pub step: SelectionStep,
2802 pub mode: SelectionMode,
2803 pub repeat: usize,
2804}
2805
2806impl SelectionOp {
2807 pub fn new(direction: SelectionDirection, step: SelectionStep, mode: SelectionMode) -> Self {
2808 Self { direction, step, mode, repeat: 1 }
2809 }
2810}
2811
2812#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2814pub enum KeyboardShortcut {
2815 Copy, Cut, Paste, SelectAll, Undo, Redo, }
2822
2823impl KeyboardShortcut {
2824 pub fn from_key(vk: crate::window::VirtualKeyCode, ctrl: bool, shift: bool) -> Option<Self> {
2828 use crate::window::VirtualKeyCode::*;
2829 if !ctrl {
2830 return None;
2831 }
2832 Some(match vk {
2833 C => KeyboardShortcut::Copy,
2834 X => KeyboardShortcut::Cut,
2835 V => KeyboardShortcut::Paste,
2836 A => KeyboardShortcut::SelectAll,
2837 Z if shift => KeyboardShortcut::Redo,
2838 Z => KeyboardShortcut::Undo,
2839 Y => KeyboardShortcut::Redo,
2840 _ => return None,
2841 })
2842 }
2843}
2844
2845pub extern "C" fn default_input_interpreter_extern(
2852 _user_data: crate::refany::RefAny,
2853 info_ptr: *const InputInterpreterInfo<'static>,
2854) -> PreCallbackFilterResult {
2855 if info_ptr.is_null() {
2856 return PreCallbackFilterResult {
2857 system_changes: Vec::new(),
2858 user_events: Vec::new(),
2859 };
2860 }
2861 let info = unsafe { &*info_ptr };
2862 default_input_interpreter(info)
2863}
2864
2865pub extern "C" fn default_post_filter_extern(
2867 _user_data: crate::refany::RefAny,
2868 prevent_default: bool,
2869 pre_changes: SystemChangeVecSlice,
2870 old_focus: DomNodeId,
2871 new_focus: DomNodeId,
2872) -> SystemChangeVec {
2873 let pre_changes_slice = pre_changes.as_slice();
2874 let old = old_focus.node.into_crate_internal().map(|_| old_focus);
2875 let new = new_focus.node.into_crate_internal().map(|_| new_focus);
2876 default_post_filter(prevent_default, pre_changes_slice, old, new).into()
2877}
2878
2879pub fn default_input_interpreter(
2880 info: &InputInterpreterInfo,
2881) -> PreCallbackFilterResult {
2882 let ctx = FilterContext {
2883 hit_test: info.hit_test,
2884 keyboard_state: info.keyboard_state,
2885 mouse_state: info.mouse_state,
2886 click_count: info.state.click_count,
2887 focused_node: info.state.focused_node,
2888 drag_start_position: info.state.drag_start_position,
2889 };
2890
2891 let (system_changes, user_events) = info.events.iter().fold(
2892 (Vec::new(), Vec::new()),
2893 |(mut internal, mut user), event| {
2894 match process_event_for_internal(&ctx, event) {
2895 Some(InternalEventAction::AddAndSkip(evt)) => {
2896 internal.push(evt);
2897 }
2898 Some(InternalEventAction::AddAndPass(evt)) => {
2899 internal.push(evt);
2900 user.push(event.clone());
2901 }
2902 None => {
2903 user.push(event.clone());
2904 }
2905 }
2906 (internal, user)
2907 },
2908 );
2909
2910 PreCallbackFilterResult {
2911 system_changes,
2912 user_events,
2913 }
2914}
2915
2916pub fn pre_callback_filter_internal_events<SM, FM>(
2918 events: &[SyntheticEvent],
2919 hit_test: Option<&FullHitTest>,
2920 keyboard_state: &crate::window::KeyboardState,
2921 mouse_state: &crate::window::MouseState,
2922 selection_manager: &SM,
2923 focus_manager: &FM,
2924) -> PreCallbackFilterResult
2925where
2926 SM: SelectionManagerQuery,
2927 FM: FocusManagerQuery,
2928{
2929 let info = InputInterpreterInfo {
2930 events,
2931 hit_test,
2932 keyboard_state,
2933 mouse_state,
2934 state: InputInterpreterState {
2935 focused_node: focus_manager.get_focused_node_id(),
2936 click_count: selection_manager.get_click_count(),
2937 drag_start_position: selection_manager.get_drag_start_position(),
2938 has_selection: selection_manager.has_selection(),
2939 },
2940 };
2941 default_input_interpreter(&info)
2942}
2943
2944struct FilterContext<'a> {
2946 hit_test: Option<&'a FullHitTest>,
2947 keyboard_state: &'a crate::window::KeyboardState,
2948 mouse_state: &'a crate::window::MouseState,
2949 click_count: u8,
2950 focused_node: Option<DomNodeId>,
2951 drag_start_position: Option<LogicalPosition>,
2952}
2953
2954fn process_event_for_internal(
2956 ctx: &FilterContext<'_>,
2957 event: &SyntheticEvent,
2958) -> Option<InternalEventAction> {
2959 match event.event_type {
2960 EventType::MouseDown => handle_mouse_down(event, ctx.hit_test, ctx.click_count, ctx.mouse_state, ctx.keyboard_state),
2961 EventType::MouseOver => handle_mouse_over(
2962 event,
2963 ctx.hit_test,
2964 ctx.mouse_state,
2965 ctx.drag_start_position,
2966 ),
2967 EventType::KeyDown => handle_key_down(
2968 event,
2969 ctx.keyboard_state,
2970 ctx.focused_node,
2971 ),
2972 _ => None,
2973 }
2974}
2975
2976enum InternalEventAction {
2978 AddAndSkip(SystemChange),
2980 AddAndPass(SystemChange),
2982}
2983
2984fn get_first_hovered_node(hit_test: Option<&FullHitTest>) -> Option<DomNodeId> {
2986 let ht = hit_test?;
2987 let (dom_id, hit_data) = ht.hovered_nodes.iter().next()?;
2988 let node_id = hit_data.regular_hit_test_nodes.keys().next()?;
2989 Some(DomNodeId {
2990 dom: *dom_id,
2991 node: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
2992 })
2993}
2994
2995fn get_mouse_position_with_fallback(
2997 event: &SyntheticEvent,
2998 mouse_state: &crate::window::MouseState,
2999) -> LogicalPosition {
3000 match &event.data {
3001 EventData::Mouse(mouse_data) => mouse_data.position,
3002 _ => {
3003 mouse_state.cursor_position.get_position().unwrap_or(LogicalPosition::zero())
3007 }
3008 }
3009}
3010
3011fn handle_mouse_down(
3013 event: &SyntheticEvent,
3014 hit_test: Option<&FullHitTest>,
3015 click_count: u8,
3016 mouse_state: &crate::window::MouseState,
3017 keyboard_state: &crate::window::KeyboardState,
3018) -> Option<InternalEventAction> {
3019 let effective_click_count = if click_count == 0 { 1 } else { click_count };
3020
3021 if effective_click_count > 3 {
3022 return None;
3023 }
3024
3025 let _target = get_first_hovered_node(hit_test)?;
3026 let position = get_mouse_position_with_fallback(event, mouse_state);
3027
3028 if keyboard_state.ctrl_down() && effective_click_count == 1 {
3030 return Some(InternalEventAction::AddAndPass(
3031 SystemChange::AddCursorAtClick { position },
3032 ));
3033 }
3034
3035 Some(InternalEventAction::AddAndPass(
3036 SystemChange::TextSelectionClick {
3037 position,
3038 timestamp: event.timestamp.clone(),
3039 },
3040 ))
3041}
3042
3043fn handle_mouse_over(
3045 event: &SyntheticEvent,
3046 hit_test: Option<&FullHitTest>,
3047 mouse_state: &crate::window::MouseState,
3048 drag_start_position: Option<LogicalPosition>,
3049) -> Option<InternalEventAction> {
3050 if !mouse_state.left_down {
3051 return None;
3052 }
3053
3054 let start_position = drag_start_position?;
3055
3056 let _target = get_first_hovered_node(hit_test)?;
3057 let current_position = get_mouse_position_with_fallback(event, mouse_state);
3058
3059 Some(InternalEventAction::AddAndPass(
3060 SystemChange::TextSelectionDrag {
3061 start_position,
3062 current_position,
3063 },
3064 ))
3065}
3066
3067fn handle_key_down(
3069 event: &SyntheticEvent,
3070 keyboard_state: &crate::window::KeyboardState,
3071 focused_node: Option<DomNodeId>,
3072) -> Option<InternalEventAction> {
3073 use crate::window::VirtualKeyCode;
3074
3075 let target = focused_node?;
3076 let EventData::Keyboard(_) = &event.data else {
3077 return None;
3078 };
3079
3080 let ctrl = keyboard_state.ctrl_down();
3081 let shift = keyboard_state.shift_down();
3082 let vk = keyboard_state.current_virtual_keycode.as_ref()?;
3083
3084 if ctrl {
3088 if let Some(shortcut) = KeyboardShortcut::from_key(*vk, ctrl, shift) {
3089 let change = match shortcut {
3090 KeyboardShortcut::Copy => SystemChange::CopyToClipboard,
3091 KeyboardShortcut::Cut => SystemChange::CutToClipboard { target },
3092 KeyboardShortcut::Paste => SystemChange::PasteFromClipboard,
3093 KeyboardShortcut::SelectAll => SystemChange::SelectAllText,
3094 KeyboardShortcut::Undo => SystemChange::UndoTextEdit { target },
3095 KeyboardShortcut::Redo => SystemChange::RedoTextEdit { target },
3096 };
3097 return Some(InternalEventAction::AddAndSkip(change));
3098 }
3099 if matches!(vk, VirtualKeyCode::D) {
3100 return Some(InternalEventAction::AddAndSkip(
3101 SystemChange::SelectNextOccurrence { target },
3102 ));
3103 }
3104 }
3105
3106 let mode_for_shift = if shift { SelectionMode::Extend } else { SelectionMode::Move };
3108 let selection_op = if let Some(arrow) = ArrowDirection::from_key(*vk, ctrl) {
3109 let (direction, step) = arrow.to_selection(ctrl);
3110 SelectionOp::new(direction, step, mode_for_shift)
3111 } else {
3112 match vk {
3113 VirtualKeyCode::Back => SelectionOp::new(
3115 SelectionDirection::Backward,
3116 if ctrl { SelectionStep::Word } else { SelectionStep::Character },
3117 SelectionMode::Delete,
3118 ),
3119 VirtualKeyCode::Delete => SelectionOp::new(
3120 SelectionDirection::Forward,
3121 if ctrl { SelectionStep::Word } else { SelectionStep::Character },
3122 SelectionMode::Delete,
3123 ),
3124 _ => return None,
3125 }
3126 };
3127
3128 Some(InternalEventAction::AddAndSkip(
3129 SystemChange::ApplySelectionOp { target, op: selection_op },
3130 ))
3131}
3132
3133pub trait SelectionManagerQuery {
3138 fn get_click_count(&self) -> u8;
3140
3141 fn get_drag_start_position(&self) -> Option<LogicalPosition>;
3143
3144 fn has_selection(&self) -> bool;
3146}
3147
3148pub trait FocusManagerQuery {
3153 fn get_focused_node_id(&self) -> Option<DomNodeId>;
3155}
3156
3157pub fn default_post_filter(
3163 prevent_default: bool,
3164 pre_changes: &[SystemChange],
3165 old_focus: Option<DomNodeId>,
3166 new_focus: Option<DomNodeId>,
3167) -> Vec<SystemChange> {
3168 post_callback_filter_system_changes(prevent_default, pre_changes, old_focus, new_focus)
3169}
3170
3171pub fn post_callback_filter_system_changes(
3172 prevent_default: bool,
3173 pre_changes: &[SystemChange],
3174 old_focus: Option<DomNodeId>,
3175 new_focus: Option<DomNodeId>,
3176) -> Vec<SystemChange> {
3177 let mut changes = Vec::new();
3178
3179 if prevent_default {
3180 if old_focus != new_focus {
3182 changes.push(SystemChange::SetFocus { new_focus, old_focus });
3183 }
3184 return changes;
3185 }
3186
3187 changes.push(SystemChange::ApplyPendingTextInput);
3189
3190 for change in pre_changes {
3192 match change {
3193 SystemChange::TextSelectionClick { .. }
3194 | SystemChange::ApplySelectionOp { .. }
3195 | SystemChange::AddCursorAtClick { .. }
3196 | SystemChange::SelectNextOccurrence { .. } => {
3197 changes.push(SystemChange::ScrollSelectionIntoView);
3198 }
3199 SystemChange::TextSelectionDrag { .. } => {
3200 changes.push(SystemChange::StartAutoScrollTimer);
3201 }
3202 SystemChange::CutToClipboard { .. }
3203 | SystemChange::PasteFromClipboard
3204 | SystemChange::UndoTextEdit { .. }
3205 | SystemChange::RedoTextEdit { .. }
3206 | SystemChange::SelectAllText => {
3207 changes.push(SystemChange::ScrollSelectionIntoView);
3208 }
3209 _ => {}
3211 }
3212 }
3213
3214 if old_focus != new_focus {
3216 changes.push(SystemChange::SetFocus { new_focus, old_focus });
3217 }
3218
3219 changes
3220}
3221
3222
3223#[cfg(test)]
3224mod tests {
3225 use super::*;
3226 use azul_css::AzString;
3227 use crate::dom::{DomId, DomNodeId};
3228 use crate::styled_dom::NodeHierarchyItemId;
3229 use crate::id::NodeId;
3230 use crate::window::{KeyboardState, MouseState, VirtualKeyCode, VirtualKeyCodeVec, OptionVirtualKeyCode};
3231 use crate::geom::LogicalPosition;
3232 use crate::task::{Instant, SystemTick};
3233
3234 struct MockSelectionManager {
3235 click_count: u8,
3236 has_sel: bool,
3237 }
3238 impl SelectionManagerQuery for MockSelectionManager {
3239 fn get_click_count(&self) -> u8 { self.click_count }
3240 fn get_drag_start_position(&self) -> Option<LogicalPosition> { None }
3241 fn has_selection(&self) -> bool { self.has_sel }
3242 }
3243
3244 struct MockFocusManager(Option<DomNodeId>);
3245 impl FocusManagerQuery for MockFocusManager {
3246 fn get_focused_node_id(&self) -> Option<DomNodeId> { self.0 }
3247 }
3248
3249 fn focused_node(node_idx: usize) -> DomNodeId {
3250 DomNodeId {
3251 dom: DomId { inner: 0 },
3252 node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(node_idx))),
3253 }
3254 }
3255
3256 fn make_keyboard_state(vk: VirtualKeyCode) -> KeyboardState {
3257 let mut ks = KeyboardState::default();
3258 ks.current_virtual_keycode = OptionVirtualKeyCode::Some(vk);
3259 ks.pressed_virtual_keycodes = VirtualKeyCodeVec::from_vec(vec![vk]);
3260 ks
3261 }
3262
3263 fn make_keydown_event(target: DomNodeId) -> SyntheticEvent {
3264 SyntheticEvent::new(
3265 EventType::KeyDown,
3266 EventSource::User,
3267 target,
3268 Instant::Tick(SystemTick::new(0)),
3269 EventData::Keyboard(KeyboardEventData {
3270 key_code: VirtualKeyCode::Back as u32,
3271 char_code: None,
3272 modifiers: KeyModifiers::default(),
3273 repeat: false,
3274 }),
3275 )
3276 }
3277
3278 #[test]
3279 fn backspace_generates_delete_text_selection() {
3280 let target = focused_node(2);
3281 let events = vec![make_keydown_event(target)];
3282 let kb = make_keyboard_state(VirtualKeyCode::Back);
3283 let mouse = MouseState::default();
3284 let sel = MockSelectionManager { click_count: 0, has_sel: false };
3285 let focus = MockFocusManager(Some(target));
3286
3287 let result = pre_callback_filter_internal_events(
3288 &events, None, &kb, &mouse, &sel, &focus,
3289 );
3290
3291 let ops: Vec<_> = result.system_changes.iter()
3292 .filter(|c| matches!(c, SystemChange::ApplySelectionOp { .. }))
3293 .collect();
3294 assert_eq!(ops.len(), 1, "Backspace should generate ApplySelectionOp");
3295 match &ops[0] {
3296 SystemChange::ApplySelectionOp { op, .. } => {
3297 assert_eq!(op.direction, SelectionDirection::Backward);
3298 assert_eq!(op.step, SelectionStep::Character);
3299 assert_eq!(op.mode, SelectionMode::Delete);
3300 }
3301 _ => unreachable!(),
3302 }
3303 }
3304
3305 #[test]
3306 fn delete_key_generates_forward_deletion() {
3307 let target = focused_node(2);
3308 let event = SyntheticEvent::new(
3309 EventType::KeyDown, EventSource::User, target,
3310 Instant::Tick(SystemTick::new(0)),
3311 EventData::Keyboard(KeyboardEventData {
3312 key_code: VirtualKeyCode::Delete as u32,
3313 char_code: None, modifiers: KeyModifiers::default(), repeat: false,
3314 }),
3315 );
3316 let kb = make_keyboard_state(VirtualKeyCode::Delete);
3317 let mouse = MouseState::default();
3318 let sel = MockSelectionManager { click_count: 0, has_sel: false };
3319 let focus = MockFocusManager(Some(target));
3320 let result = pre_callback_filter_internal_events(&[event], None, &kb, &mouse, &sel, &focus);
3321 let ops: Vec<_> = result.system_changes.iter()
3322 .filter(|c| matches!(c, SystemChange::ApplySelectionOp { .. }))
3323 .collect();
3324 assert_eq!(ops.len(), 1);
3325 match &ops[0] {
3326 SystemChange::ApplySelectionOp { op, .. } => {
3327 assert_eq!(op.direction, SelectionDirection::Forward);
3328 assert_eq!(op.step, SelectionStep::Character);
3329 assert_eq!(op.mode, SelectionMode::Delete);
3330 }
3331 _ => unreachable!(),
3332 }
3333 }
3334
3335 #[test]
3336 fn arrow_left_generates_navigation() {
3337 let target = focused_node(2);
3338 let event = SyntheticEvent::new(
3339 EventType::KeyDown, EventSource::User, target,
3340 Instant::Tick(SystemTick::new(0)),
3341 EventData::Keyboard(KeyboardEventData {
3342 key_code: VirtualKeyCode::Left as u32,
3343 char_code: None, modifiers: KeyModifiers::default(), repeat: false,
3344 }),
3345 );
3346 let kb = make_keyboard_state(VirtualKeyCode::Left);
3347 let mouse = MouseState::default();
3348 let sel = MockSelectionManager { click_count: 0, has_sel: false };
3349 let focus = MockFocusManager(Some(target));
3350 let result = pre_callback_filter_internal_events(&[event], None, &kb, &mouse, &sel, &focus);
3351 let ops: Vec<_> = result.system_changes.iter()
3352 .filter(|c| matches!(c, SystemChange::ApplySelectionOp { .. }))
3353 .collect();
3354 assert_eq!(ops.len(), 1, "Left arrow should generate ApplySelectionOp");
3355 match &ops[0] {
3356 SystemChange::ApplySelectionOp { op, .. } => {
3357 assert_eq!(op.direction, SelectionDirection::Backward);
3358 assert_eq!(op.step, SelectionStep::Character);
3359 assert_eq!(op.mode, SelectionMode::Move);
3360 }
3361 _ => unreachable!(),
3362 }
3363 }
3364
3365 #[test]
3366 fn no_focused_node_means_no_keyboard_system_changes() {
3367 let target = focused_node(2);
3368 let event = make_keydown_event(target);
3369 let kb = make_keyboard_state(VirtualKeyCode::Back);
3370 let mouse = MouseState::default();
3371 let sel = MockSelectionManager { click_count: 0, has_sel: false };
3372 let focus = MockFocusManager(None); let result = pre_callback_filter_internal_events(
3375 &[event], None, &kb, &mouse, &sel, &focus,
3376 );
3377
3378 assert!(result.system_changes.is_empty(),
3379 "No system changes should be generated without focused node");
3380 }
3381
3382 #[test]
3383 fn keydown_without_keyboard_data_generates_no_system_change() {
3384 let target = focused_node(2);
3385 let event = SyntheticEvent::new(
3386 EventType::KeyDown,
3387 EventSource::User,
3388 target,
3389 Instant::Tick(SystemTick::new(0)),
3390 EventData::None, );
3392 let kb = make_keyboard_state(VirtualKeyCode::Back);
3393 let mouse = MouseState::default();
3394 let sel = MockSelectionManager { click_count: 0, has_sel: false };
3395 let focus = MockFocusManager(Some(target));
3396
3397 let result = pre_callback_filter_internal_events(
3398 &[event], None, &kb, &mouse, &sel, &focus,
3399 );
3400
3401 assert!(result.system_changes.is_empty(),
3404 "EventData::None should not generate system changes (documents the old bug)");
3405 }
3406
3407 #[test]
3408 fn ctrl_c_generates_copy() {
3409 let target = focused_node(2);
3410 let event = SyntheticEvent::new(
3411 EventType::KeyDown,
3412 EventSource::User,
3413 target,
3414 Instant::Tick(SystemTick::new(0)),
3415 EventData::Keyboard(KeyboardEventData {
3416 key_code: VirtualKeyCode::C as u32,
3417 char_code: Some('c'),
3418 modifiers: KeyModifiers { ctrl: true, shift: false, alt: false, meta: false },
3419 repeat: false,
3420 }),
3421 );
3422 let mut kb = make_keyboard_state(VirtualKeyCode::C);
3423 kb.pressed_virtual_keycodes = VirtualKeyCodeVec::from_vec(
3424 vec![VirtualKeyCode::C, VirtualKeyCode::LControl]
3425 );
3426 let mouse = MouseState::default();
3427 let sel = MockSelectionManager { click_count: 0, has_sel: false };
3428 let focus = MockFocusManager(Some(target));
3429
3430 let result = pre_callback_filter_internal_events(
3431 &[event], None, &kb, &mouse, &sel, &focus,
3432 );
3433
3434 let copy_changes: Vec<_> = result.system_changes.iter()
3435 .filter(|c| matches!(c, SystemChange::CopyToClipboard))
3436 .collect();
3437
3438 assert_eq!(copy_changes.len(), 1, "Ctrl+C should generate CopyToClipboard");
3439 }
3440
3441 fn make_hit_test_with_node(node_idx: usize) -> crate::hit_test::FullHitTest {
3442 use crate::hit_test::{FullHitTest, HitTest, HitTestItem};
3443 use crate::dom::OptionDomNodeId;
3444 use std::collections::BTreeMap;
3445
3446 let node_id = NodeId::new(node_idx);
3447 let dom_id = DomId { inner: 0 };
3448
3449 let mut regular = BTreeMap::new();
3450 regular.insert(node_id, HitTestItem {
3451 point_in_viewport: LogicalPosition::new(100.0, 200.0),
3452 point_relative_to_item: LogicalPosition::new(50.0, 30.0),
3453 is_focusable: true,
3454 is_virtual_view_hit: None,
3455 hit_depth: 0,
3456 });
3457
3458 let mut hovered = BTreeMap::new();
3459 hovered.insert(dom_id, HitTest {
3460 regular_hit_test_nodes: regular,
3461 scroll_hit_test_nodes: BTreeMap::new(),
3462 scrollbar_hit_test_nodes: BTreeMap::new(),
3463 cursor_hit_test_nodes: BTreeMap::new(),
3464 });
3465
3466 FullHitTest {
3467 hovered_nodes: hovered,
3468 focused_node: OptionDomNodeId::None,
3469 }
3470 }
3471
3472 #[test]
3473 fn mousedown_generates_text_selection_click() {
3474 let target = focused_node(2);
3475 let event = SyntheticEvent::new(
3476 EventType::MouseDown,
3477 EventSource::User,
3478 target,
3479 Instant::Tick(SystemTick::new(0)),
3480 EventData::Mouse(MouseEventData {
3481 position: LogicalPosition::new(100.0, 200.0),
3482 button: crate::events::MouseButton::Left,
3483 buttons: 1,
3484 modifiers: KeyModifiers::default(),
3485 }),
3486 );
3487 let hit_test = make_hit_test_with_node(2);
3488 let kb = KeyboardState::default();
3489 let mouse = MouseState::default();
3490 let sel = MockSelectionManager { click_count: 1, has_sel: false };
3491 let focus = MockFocusManager(Some(target));
3492
3493 let result = pre_callback_filter_internal_events(
3494 &[event], Some(&hit_test), &kb, &mouse, &sel, &focus,
3495 );
3496
3497 let click_changes: Vec<_> = result.system_changes.iter()
3498 .filter(|c| matches!(c, SystemChange::TextSelectionClick { .. }))
3499 .collect();
3500
3501 assert_eq!(click_changes.len(), 1, "MouseDown with hit_test should generate TextSelectionClick");
3502 }
3503
3504 #[test]
3505 fn process_event_result_max_self_picks_higher_variant() {
3506 let lo = ProcessEventResult::ShouldReRenderCurrentWindow;
3507 let hi = ProcessEventResult::ShouldRegenerateDomCurrentWindow;
3508 assert_eq!(lo.max_self(hi), hi);
3509 assert_eq!(hi.max_self(lo), hi);
3510 assert_eq!(lo.max_self(lo), lo);
3511 }
3512
3513 #[test]
3514 fn arrow_direction_from_key_maps_arrows_and_home_end() {
3515 use crate::window::VirtualKeyCode::*;
3516 assert_eq!(ArrowDirection::from_key(Left, false), Some(ArrowDirection::Left));
3517 assert_eq!(ArrowDirection::from_key(Right, false), Some(ArrowDirection::Right));
3518 assert_eq!(ArrowDirection::from_key(Up, false), Some(ArrowDirection::Up));
3519 assert_eq!(ArrowDirection::from_key(Down, false), Some(ArrowDirection::Down));
3520 assert_eq!(ArrowDirection::from_key(Home, false), Some(ArrowDirection::LineStart));
3521 assert_eq!(ArrowDirection::from_key(End, false), Some(ArrowDirection::LineEnd));
3522 assert_eq!(ArrowDirection::from_key(Home, true), Some(ArrowDirection::DocumentStart));
3523 assert_eq!(ArrowDirection::from_key(End, true), Some(ArrowDirection::DocumentEnd));
3524 assert_eq!(ArrowDirection::from_key(C, false), None);
3525 }
3526
3527 #[test]
3528 fn arrow_direction_to_selection_respects_ctrl() {
3529 let (d, s) = ArrowDirection::Left.to_selection(false);
3530 assert_eq!((d, s), (SelectionDirection::Backward, SelectionStep::Character));
3531 let (d, s) = ArrowDirection::Left.to_selection(true);
3532 assert_eq!((d, s), (SelectionDirection::Backward, SelectionStep::Word));
3533 let (d, s) = ArrowDirection::Up.to_selection(false);
3534 assert_eq!((d, s), (SelectionDirection::Backward, SelectionStep::VisualLine));
3535 let (d, s) = ArrowDirection::DocumentEnd.to_selection(false);
3536 assert_eq!((d, s), (SelectionDirection::Forward, SelectionStep::Document));
3537 }
3538
3539 #[test]
3540 fn keyboard_shortcut_from_key_recognizes_editing_combos() {
3541 use crate::window::VirtualKeyCode::*;
3542 assert_eq!(KeyboardShortcut::from_key(C, true, false), Some(KeyboardShortcut::Copy));
3543 assert_eq!(KeyboardShortcut::from_key(X, true, false), Some(KeyboardShortcut::Cut));
3544 assert_eq!(KeyboardShortcut::from_key(V, true, false), Some(KeyboardShortcut::Paste));
3545 assert_eq!(KeyboardShortcut::from_key(A, true, false), Some(KeyboardShortcut::SelectAll));
3546 assert_eq!(KeyboardShortcut::from_key(Z, true, false), Some(KeyboardShortcut::Undo));
3547 assert_eq!(KeyboardShortcut::from_key(Z, true, true), Some(KeyboardShortcut::Redo));
3548 assert_eq!(KeyboardShortcut::from_key(Y, true, false), Some(KeyboardShortcut::Redo));
3549 assert_eq!(KeyboardShortcut::from_key(C, false, false), None);
3551 assert_eq!(KeyboardShortcut::from_key(D, true, false), None);
3553 }
3554
3555 #[test]
3556 fn mouse_button_state_round_trips_from_mouse_state() {
3557 let mut ms = MouseState::default();
3558 ms.left_down = true;
3559 ms.middle_down = true;
3560 let bs: MouseButtonState = (&ms).into();
3561 assert!(bs.left_down);
3562 assert!(!bs.right_down);
3563 assert!(bs.middle_down);
3564 assert!(bs.any_down());
3565
3566 let none = MouseButtonState { left_down: false, right_down: false, middle_down: false };
3567 assert!(!none.any_down());
3568 }
3569
3570 #[test]
3571 fn callback_to_call_collects_hits_for_dom() {
3572 let dom_id = DomId { inner: 0 };
3573 let hit_test = make_hit_test_with_node(2);
3574 let filter = EventFilter::Hover(HoverEventFilter::MouseDown);
3575 let calls = CallbackToCall::from_hit_test(&hit_test, dom_id, filter.clone());
3576 assert_eq!(calls.len(), 1);
3577 assert_eq!(calls[0].node_id, NodeId::new(2));
3578 assert_eq!(calls[0].event_filter, filter);
3579 assert!(calls[0].hit_test_item.is_some());
3580
3581 let other = CallbackToCall::from_hit_test(
3583 &hit_test,
3584 DomId { inner: 999 },
3585 EventFilter::Hover(HoverEventFilter::MouseUp),
3586 );
3587 assert!(other.is_empty());
3588
3589 let direct = CallbackToCall::new(
3591 NodeId::new(7),
3592 None,
3593 EventFilter::Focus(FocusEventFilter::FocusReceived),
3594 );
3595 assert_eq!(direct.node_id, NodeId::new(7));
3596 assert!(direct.hit_test_item.is_none());
3597 }
3598
3599 #[test]
3600 fn restyle_relayout_aliases_are_btreemap_compatible() {
3601 let restyle: RestyleNodes = BTreeMap::new();
3604 let relayout: RelayoutNodes = BTreeMap::new();
3605 assert!(restyle.is_empty());
3606 assert!(relayout.is_empty());
3607
3608 let mut words: RelayoutWords = BTreeMap::new();
3610 words.insert(NodeId::new(1), AzString::from_const_str("hello"));
3611 assert_eq!(words.get(&NodeId::new(1)).map(|s| s.as_str()), Some("hello"));
3612 }
3613
3614 #[test]
3615 fn detect_lifecycle_events_with_reconciliation_is_callable() {
3616 let dom_id = DomId { inner: 0 };
3620 let old_data: Vec<crate::dom::NodeData> = Vec::new();
3621 let new_data: Vec<crate::dom::NodeData> = Vec::new();
3622 let old_hier: Vec<crate::styled_dom::NodeHierarchyItem> = Vec::new();
3623 let new_hier: Vec<crate::styled_dom::NodeHierarchyItem> = Vec::new();
3624 let old_layout = OrderedMap::default();
3625 let new_layout = OrderedMap::default();
3626 let result: LifecycleEventResult = detect_lifecycle_events_with_reconciliation(
3627 dom_id,
3628 &old_data,
3629 &new_data,
3630 &old_hier,
3631 &new_hier,
3632 &old_layout,
3633 &new_layout,
3634 Instant::Tick(SystemTick::new(0)),
3635 );
3636 assert!(result.events.is_empty());
3637 assert!(result.node_id_mapping.is_empty());
3638 }
3639
3640 #[test]
3641 fn nodedata_focusable_and_activation_traits_are_wired() {
3642 use crate::dom::{NodeData, NodeType};
3643 use crate::events::{ActivationBehavior as _, Focusable as _};
3644
3645 let btn = NodeData::create_node(NodeType::Button);
3647 assert!(<NodeData as Focusable>::is_naturally_focusable(&btn));
3648 assert!(<NodeData as Focusable>::is_focusable(&btn));
3649 assert!(<NodeData as ActivationBehavior>::has_activation_behavior(&btn));
3650 assert!(<NodeData as ActivationBehavior>::is_activatable(&btn));
3651
3652 let div = NodeData::create_node(NodeType::Div);
3654 assert!(!<NodeData as Focusable>::is_naturally_focusable(&div));
3655 assert!(!<NodeData as ActivationBehavior>::has_activation_behavior(&div));
3656
3657 let input = NodeData::create_node(NodeType::Input);
3659 assert!(<NodeData as Focusable>::is_naturally_focusable(&input));
3660 }
3661}