1#![allow(clippy::too_many_arguments)]
2
3use crate::context_menu_manager;
4use crate::drag_drop;
5use crate::external_drop;
6use crate::ffi::{CursorStyle, KeyEventType, PointerEventType, SemanticRole};
7use crate::focus_visibility;
8use crate::keyboard_scroll::handle_keyboard_scroll_fallback;
9use crate::keyboard_scroll_tracker::{
10 register_keyboard_scroll_node, reset_keyboard_scroll_tracking,
11 track_keyboard_scroll_pointer_up, unregister_keyboard_scroll_node,
12};
13use crate::mobile_text_selection_toolbar;
14use crate::node::{NodeHandle, NodeRef, WeakNodeRef};
15use crate::selection_handle_adorner;
16use crate::tool_tip_manager;
17use std::cell::{Cell, RefCell};
18use std::collections::HashMap;
19use std::rc::Rc;
20
21static mut KEY_BUFFER: [u8; 256] = [0; 256];
22static mut TEXT_BUFFER: [u8; 16 * 1024] = [0; 16 * 1024];
23
24pub const DEFAULT_LONG_PRESS_MINIMUM_DURATION_MS: i32 = 500;
25pub const DEFAULT_LONG_PRESS_MOVEMENT_TOLERANCE: f32 = 10.0;
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum PointerType {
29 Unknown = 0,
30 Mouse = 1,
31 Touch = 2,
32 Pen = 3,
33}
34
35impl PointerType {
36 pub fn from_raw(value: u32) -> Self {
37 match value {
38 1 => Self::Mouse,
39 2 => Self::Touch,
40 3 => Self::Pen,
41 _ => Self::Unknown,
42 }
43 }
44}
45
46#[repr(i32)]
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum PointerButton {
49 None = -1,
50 Primary = 0,
51 Auxiliary = 1,
52 Secondary = 2,
53 Back = 3,
54 Forward = 4,
55}
56
57impl PointerButton {
58 pub fn from_raw(value: i32) -> Self {
59 match value {
60 0 => Self::Primary,
61 1 => Self::Auxiliary,
62 2 => Self::Secondary,
63 3 => Self::Back,
64 4 => Self::Forward,
65 _ => Self::None,
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
71pub struct PointerButtons(u32);
72
73impl PointerButtons {
74 pub const NONE: Self = Self(0);
75 pub const PRIMARY: Self = Self(1 << 0);
76 pub const SECONDARY: Self = Self(1 << 1);
77 pub const AUXILIARY: Self = Self(1 << 2);
78 pub const BACK: Self = Self(1 << 3);
79 pub const FORWARD: Self = Self(1 << 4);
80
81 pub const fn from_raw(value: u32) -> Self {
82 Self(value)
83 }
84
85 pub const fn bits(self) -> u32 {
86 self.0
87 }
88
89 pub const fn is_empty(self) -> bool {
90 self.0 == 0
91 }
92
93 pub const fn contains(self, buttons: Self) -> bool {
94 self.0 & buttons.0 == buttons.0
95 }
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99pub enum GestureIntent {
100 None = 0,
101 Pan = 1,
102 Pinch = 2,
103 PanAndPinch = 3,
104}
105
106impl GestureIntent {
107 pub fn from_callbacks(has_pan: bool, has_pinch: bool) -> Self {
108 match (has_pan, has_pinch) {
109 (false, false) => Self::None,
110 (true, false) => Self::Pan,
111 (false, true) => Self::Pinch,
112 (true, true) => Self::PanAndPinch,
113 }
114 }
115}
116
117#[derive(Clone, Copy, Debug, PartialEq, Eq)]
118pub enum GestureEventPhase {
119 Begin = 1,
120 Update = 2,
121 End = 3,
122 Cancel = 4,
123}
124
125impl GestureEventPhase {
126 pub fn from_raw(value: u32) -> Self {
127 match value {
128 1 => Self::Begin,
129 2 => Self::Update,
130 3 => Self::End,
131 _ => Self::Cancel,
132 }
133 }
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq)]
137pub enum GestureEventKind {
138 None = 0,
139 Pan = 1,
140 Pinch = 2,
141}
142
143impl GestureEventKind {
144 pub fn from_raw(value: u32) -> Self {
145 match value {
146 1 => Self::Pan,
147 2 => Self::Pinch,
148 _ => Self::None,
149 }
150 }
151}
152
153#[derive(Clone, Debug)]
154pub struct PointerEventArgs {
155 target_handle: NodeHandle,
156 pub event_type: PointerEventType,
157 pub scene_x: f32,
158 pub scene_y: f32,
159 pub x: f32,
160 pub y: f32,
161 pub modifiers: u32,
162 pub pointer_id: i32,
163 pub pointer_type: PointerType,
164 pub button: PointerButton,
165 pub buttons: PointerButtons,
166 pub pressure: f32,
167 pub width: f32,
168 pub height: f32,
169 pub click_count: i32,
170 pub is_primary: bool,
171 pub tangential_pressure: f32,
172 pub tilt_x: f32,
173 pub tilt_y: f32,
174 pub twist: f32,
175 pub handled: bool,
176}
177
178#[derive(Clone, Copy, Debug, PartialEq)]
179pub(crate) struct PointerExtendedMetadata {
180 pub is_primary: bool,
181 pub tangential_pressure: f32,
182 pub tilt_x: f32,
183 pub tilt_y: f32,
184 pub twist: f32,
185}
186
187impl Default for PointerExtendedMetadata {
188 fn default() -> Self {
189 Self {
190 is_primary: true,
191 tangential_pressure: 0.0,
192 tilt_x: 0.0,
193 tilt_y: 0.0,
194 twist: 0.0,
195 }
196 }
197}
198
199impl PointerEventArgs {
200 #[allow(clippy::too_many_arguments)]
201 pub fn new(
202 target_handle: NodeHandle,
203 event_type: PointerEventType,
204 scene_x: f32,
205 scene_y: f32,
206 modifiers: u32,
207 pointer_id: i32,
208 pointer_type: PointerType,
209 button: i32,
210 buttons: u32,
211 pressure: f32,
212 width: f32,
213 height: f32,
214 click_count: i32,
215 ) -> Self {
216 Self::new_with_metadata(
217 target_handle,
218 event_type,
219 scene_x,
220 scene_y,
221 modifiers,
222 pointer_id,
223 pointer_type,
224 button,
225 buttons,
226 pressure,
227 width,
228 height,
229 click_count,
230 PointerExtendedMetadata::default(),
231 )
232 }
233
234 #[allow(clippy::too_many_arguments)]
235 pub(crate) fn new_with_metadata(
236 target_handle: NodeHandle,
237 event_type: PointerEventType,
238 scene_x: f32,
239 scene_y: f32,
240 modifiers: u32,
241 pointer_id: i32,
242 pointer_type: PointerType,
243 button: i32,
244 buttons: u32,
245 pressure: f32,
246 width: f32,
247 height: f32,
248 click_count: i32,
249 extended: PointerExtendedMetadata,
250 ) -> Self {
251 Self {
252 target_handle,
253 event_type,
254 scene_x,
255 scene_y,
256 x: scene_x,
257 y: scene_y,
258 modifiers,
259 pointer_id,
260 pointer_type,
261 button: PointerButton::from_raw(button),
262 buttons: PointerButtons::from_raw(buttons),
263 pressure,
264 width,
265 height,
266 click_count,
267 is_primary: extended.is_primary,
268 tangential_pressure: extended.tangential_pressure,
269 tilt_x: extended.tilt_x,
270 tilt_y: extended.tilt_y,
271 twist: extended.twist,
272 handled: false,
273 }
274 }
275
276 pub fn capture_pointer(&self) {
277 crate::event::capture_pointer(self.target_handle);
278 unsafe { crate::ffi::fui_set_pointer_capture(self.target_handle.raw()) };
279 }
280
281 pub fn release_pointer_capture(&self) {
282 crate::event::release_pointer(self.target_handle);
283 unsafe { crate::ffi::fui_release_pointer_capture() };
284 }
285
286 pub(crate) fn target_handle(&self) -> NodeHandle {
287 self.target_handle
288 }
289}
290
291#[derive(Clone, Debug)]
292pub struct WheelEventArgs {
293 pub scene_x: f32,
294 pub scene_y: f32,
295 pub x: f32,
296 pub y: f32,
297 pub delta_x: f32,
298 pub delta_y: f32,
299 pub delta_mode: u32,
300 pub modifiers: u32,
301 pub handled: bool,
302}
303
304impl WheelEventArgs {
305 pub fn new(
306 scene_x: f32,
307 scene_y: f32,
308 delta_x: f32,
309 delta_y: f32,
310 delta_mode: u32,
311 modifiers: u32,
312 ) -> Self {
313 Self {
314 scene_x,
315 scene_y,
316 x: scene_x,
317 y: scene_y,
318 delta_x,
319 delta_y,
320 delta_mode,
321 modifiers,
322 handled: false,
323 }
324 }
325}
326
327#[derive(Clone, Debug)]
328pub struct GestureEventArgs {
329 pub phase: GestureEventPhase,
330 pub kind: GestureEventKind,
331 pub scene_x: f32,
332 pub scene_y: f32,
333 pub delta_x: f32,
334 pub delta_y: f32,
335 pub scale: f32,
336 pub pointer_count: i32,
337 pub x: f32,
338 pub y: f32,
339 pub handled: bool,
340}
341
342impl GestureEventArgs {
343 #[allow(clippy::too_many_arguments)]
344 pub fn new(
345 phase: GestureEventPhase,
346 kind: GestureEventKind,
347 scene_x: f32,
348 scene_y: f32,
349 delta_x: f32,
350 delta_y: f32,
351 scale: f32,
352 pointer_count: i32,
353 ) -> Self {
354 Self {
355 phase,
356 kind,
357 scene_x,
358 scene_y,
359 delta_x,
360 delta_y,
361 scale,
362 pointer_count,
363 x: scene_x,
364 y: scene_y,
365 handled: false,
366 }
367 }
368}
369
370#[derive(Clone, Debug)]
371pub struct KeyEventArgs {
372 pub event_type: KeyEventType,
373 pub key: String,
374 pub modifiers: u32,
375 pub handled: bool,
376}
377
378impl KeyEventArgs {
379 pub fn new(event_type: KeyEventType, key: String, modifiers: u32) -> Self {
380 Self {
381 event_type,
382 key,
383 modifiers,
384 handled: false,
385 }
386 }
387}
388
389type GlobalKeyFilter = Rc<dyn Fn(KeyEventType, &str, u32) -> bool>;
390type ScrollHook = Rc<dyn Fn()>;
391
392struct GlobalKeyFilterEntry {
393 token: u32,
394 callback: GlobalKeyFilter,
395}
396
397#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
398pub struct FocusChangedEventArgs {
399 pub focused: bool,
400}
401
402#[derive(Clone, Debug, Default, PartialEq, Eq)]
403pub struct TextChangedEventArgs {
404 pub text: String,
405}
406
407#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
408pub struct SelectionChangedEventArgs {
409 pub start: u32,
410 pub end: u32,
411}
412
413#[derive(Clone, Debug)]
414pub struct LongPressEventArgs {
415 pub scene_x: f32,
416 pub scene_y: f32,
417 pub pointer_id: i32,
418 pub pointer_type: PointerType,
419 pub modifiers: u32,
420 pub duration_ms: i32,
421 pub x: f32,
422 pub y: f32,
423 pub handled: bool,
424}
425
426impl LongPressEventArgs {
427 pub fn new(
428 scene_x: f32,
429 scene_y: f32,
430 pointer_id: i32,
431 pointer_type: PointerType,
432 modifiers: u32,
433 duration_ms: i32,
434 ) -> Self {
435 Self {
436 scene_x,
437 scene_y,
438 pointer_id,
439 pointer_type,
440 modifiers,
441 duration_ms,
442 x: scene_x,
443 y: scene_y,
444 handled: false,
445 }
446 }
447}
448
449pub(crate) struct EventRouter {
450 nodes: RefCell<HashMap<NodeHandle, WeakNodeRef>>,
451 focused: RefCell<Option<NodeHandle>>,
452 captured_pointer: RefCell<Option<NodeHandle>>,
453 selection_cursor_owner: RefCell<Option<NodeHandle>>,
454 hover_stack: RefCell<Vec<NodeHandle>>,
455 current_cursor_style: Cell<CursorStyle>,
456 key_filters: RefCell<Vec<GlobalKeyFilterEntry>>,
457 scroll_hooks: RefCell<Vec<ScrollHook>>,
458 next_key_filter_token: Cell<u32>,
459}
460
461impl EventRouter {
462 pub(crate) fn new() -> Self {
463 Self {
464 nodes: RefCell::new(HashMap::new()),
465 focused: RefCell::new(None),
466 captured_pointer: RefCell::new(None),
467 selection_cursor_owner: RefCell::new(None),
468 hover_stack: RefCell::new(Vec::new()),
469 current_cursor_style: Cell::new(CursorStyle::Default),
470 key_filters: RefCell::new(Vec::new()),
471 scroll_hooks: RefCell::new(Vec::new()),
472 next_key_filter_token: Cell::new(1),
473 }
474 }
475
476 fn register(&self, node: &NodeRef) {
477 self.nodes
478 .borrow_mut()
479 .insert(node.handle(), node.downgrade());
480 register_keyboard_scroll_node(node);
481 }
482
483 fn reset(&self) {
484 self.nodes.borrow_mut().clear();
485 self.focused.replace(None);
486 self.captured_pointer.replace(None);
487 self.selection_cursor_owner.replace(None);
488 self.hover_stack.borrow_mut().clear();
489 self.key_filters.borrow_mut().clear();
490 self.next_key_filter_token.set(1);
494 drag_drop::reset();
495 external_drop::reset();
496 self.apply_cursor(CursorStyle::Default);
497 reset_keyboard_scroll_tracking();
498 }
499
500 fn unregister(&self, handle: NodeHandle) {
501 if let Some(node) = self.resolve_node(handle) {
502 drag_drop::handle_node_destroyed(node.clone());
503 external_drop::handle_node_destroyed(node);
504 }
505 self.nodes.borrow_mut().remove(&handle);
506 self.pop_hover(handle);
507 if self.focused.borrow().as_ref() == Some(&handle) {
508 self.focused.replace(None);
509 }
510 if self.captured_pointer.borrow().as_ref() == Some(&handle) {
511 self.captured_pointer.replace(None);
512 }
513 if self.selection_cursor_owner.borrow().as_ref() == Some(&handle) {
514 self.selection_cursor_owner.replace(None);
515 }
516 self.apply_current_cursor();
517 unregister_keyboard_scroll_node(handle);
518 }
519
520 fn push_key_filter(&self, callback: GlobalKeyFilter) -> u32 {
521 let token = self.next_key_filter_token.get();
522 self.next_key_filter_token.set(token + 1);
523 self.key_filters
524 .borrow_mut()
525 .push(GlobalKeyFilterEntry { token, callback });
526 token
527 }
528
529 fn remove_key_filter(&self, token: u32) {
530 self.key_filters
531 .borrow_mut()
532 .retain(|entry| entry.token != token);
533 }
534
535 fn register_scroll_hook(&self, callback: ScrollHook) {
536 self.scroll_hooks.borrow_mut().push(callback);
537 }
538
539 fn focused_node_is_enabled_button(&self) -> bool {
540 let Some(handle) = *self.focused.borrow() else {
541 return false;
542 };
543 let Some(node) = self.resolve_node(handle) else {
544 return false;
545 };
546 node.semantic_role_for_routing() == Some(SemanticRole::Button)
547 && node.is_enabled_for_routing()
548 }
549
550 pub(crate) fn dispatch_pointer_event(
551 &self,
552 handle: NodeHandle,
553 event_type: PointerEventType,
554 scene_x: f32,
555 scene_y: f32,
556 modifiers: u32,
557 pointer_id: i32,
558 pointer_type: PointerType,
559 button: i32,
560 buttons: u32,
561 pressure: f32,
562 width: f32,
563 height: f32,
564 click_count: i32,
565 ) -> bool {
566 self.dispatch_pointer_event_with_metadata(
567 handle,
568 event_type,
569 scene_x,
570 scene_y,
571 modifiers,
572 pointer_id,
573 pointer_type,
574 button,
575 buttons,
576 pressure,
577 width,
578 height,
579 click_count,
580 PointerExtendedMetadata::default(),
581 )
582 }
583
584 #[allow(clippy::too_many_arguments)]
585 pub(crate) fn dispatch_pointer_event_with_metadata(
586 &self,
587 handle: NodeHandle,
588 event_type: PointerEventType,
589 scene_x: f32,
590 scene_y: f32,
591 modifiers: u32,
592 pointer_id: i32,
593 pointer_type: PointerType,
594 button: i32,
595 buttons: u32,
596 pressure: f32,
597 width: f32,
598 height: f32,
599 click_count: i32,
600 extended: PointerExtendedMetadata,
601 ) -> bool {
602 context_menu_manager::track_pointer_event(event_type, handle.raw());
603 selection_handle_adorner::record_pointer_event(event_type, pointer_type);
604 let pointed_node = self.resolve_node(handle);
605 if event_type == PointerEventType::Down
606 && (button == PointerButton::Primary as i32
607 || matches!(pointer_type, PointerType::Touch | PointerType::Pen))
608 && crate::controls::ContextMenu::dismiss_for_outside_pointer_down(scene_x, scene_y)
609 {
610 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
611 self.apply_current_cursor();
612 return true;
613 }
614 if event_type == PointerEventType::Down
615 && !preserves_selection_on_pointer_down_for_routing(pointed_node.as_ref())
616 && mobile_text_selection_toolbar::dismiss_for_outside_pointer_down(scene_x, scene_y)
617 {
618 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
619 self.apply_current_cursor();
620 return true;
621 }
622 if event_type == PointerEventType::Down
623 && button == PointerButton::Primary as i32
624 && pointer_type != PointerType::Touch
625 {
626 self.selection_cursor_owner.replace(
627 pointed_node
628 .as_ref()
629 .and_then(selection_cursor_target)
630 .map(|node| node.handle()),
631 );
632 } else if matches!(event_type, PointerEventType::Up | PointerEventType::Cancel) {
633 self.selection_cursor_owner.replace(None);
634 }
635 if event_type == PointerEventType::Up {
636 track_keyboard_scroll_pointer_up(pointed_node.clone(), scene_x, scene_y);
637 }
638 if matches!(
639 event_type,
640 PointerEventType::Move | PointerEventType::Up | PointerEventType::Cancel
641 ) {
642 let captured_handle = *self.captured_pointer.borrow();
643 if let Some(captured_handle) = captured_handle {
644 if let Some(captured_node) = self.resolve_node(captured_handle) {
645 let captured_at_dispatch = captured_handle;
646 let handled = self.dispatch_pointer_to_node(
647 captured_node,
648 captured_handle,
649 event_type,
650 scene_x,
651 scene_y,
652 modifiers,
653 pointer_id,
654 pointer_type,
655 button,
656 buttons,
657 pressure,
658 width,
659 height,
660 click_count,
661 extended,
662 );
663 let captured_after_dispatch = *self.captured_pointer.borrow();
664 if matches!(event_type, PointerEventType::Up | PointerEventType::Cancel)
665 && captured_after_dispatch == Some(captured_at_dispatch)
666 {
667 self.captured_pointer.replace(None);
668 }
669 drag_drop::handle_pointer_event(
670 pointed_node,
671 event_type,
672 scene_x,
673 scene_y,
674 modifiers,
675 );
676 self.apply_current_cursor();
677 return handled;
678 }
679 self.captured_pointer.replace(None);
680 self.apply_current_cursor();
681 }
682 }
683 if matches!(
684 event_type,
685 PointerEventType::Move | PointerEventType::Up | PointerEventType::Cancel
686 ) {
687 let mut selection_event = PointerEventArgs::new_with_metadata(
688 handle,
689 event_type,
690 scene_x,
691 scene_y,
692 modifiers,
693 pointer_id,
694 pointer_type,
695 button,
696 buttons,
697 pressure,
698 width,
699 height,
700 click_count,
701 extended,
702 );
703 if selection_handle_adorner::route_active_handle_drag_event(&mut selection_event) {
704 drag_drop::handle_pointer_event(
705 pointed_node,
706 event_type,
707 scene_x,
708 scene_y,
709 modifiers,
710 );
711 self.apply_current_cursor();
712 return selection_event.handled;
713 }
714 }
715
716 if handle == NodeHandle::INVALID {
717 if event_type == PointerEventType::Leave {
718 self.clear_hover_stack();
719 }
720 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
721 self.apply_current_cursor();
722 return false;
723 }
724
725 let Some(node) = pointed_node.clone() else {
726 if event_type == PointerEventType::Leave {
727 self.clear_hover_stack();
728 }
729 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
730 self.apply_current_cursor();
731 return false;
732 };
733 if event_type == PointerEventType::Enter {
734 self.push_hover(handle);
735 } else if event_type == PointerEventType::Leave {
736 self.pop_hover(handle);
737 }
738 let handled = self.dispatch_pointer_to_node(
739 node,
740 handle,
741 event_type,
742 scene_x,
743 scene_y,
744 modifiers,
745 pointer_id,
746 pointer_type,
747 button,
748 buttons,
749 pressure,
750 width,
751 height,
752 click_count,
753 extended,
754 );
755 drag_drop::handle_pointer_event(pointed_node, event_type, scene_x, scene_y, modifiers);
756 self.apply_current_cursor();
757 handled
758 }
759
760 #[allow(clippy::too_many_arguments)]
761 fn dispatch_pointer_to_node(
762 &self,
763 node: NodeRef,
764 target_handle: NodeHandle,
765 event_type: PointerEventType,
766 scene_x: f32,
767 scene_y: f32,
768 modifiers: u32,
769 pointer_id: i32,
770 pointer_type: PointerType,
771 button: i32,
772 buttons: u32,
773 pressure: f32,
774 width: f32,
775 height: f32,
776 click_count: i32,
777 extended: PointerExtendedMetadata,
778 ) -> bool {
779 let mut event = PointerEventArgs::new_with_metadata(
780 target_handle,
781 event_type,
782 scene_x,
783 scene_y,
784 modifiers,
785 pointer_id,
786 pointer_type,
787 button,
788 buttons,
789 pressure,
790 width,
791 height,
792 click_count,
793 extended,
794 );
795 node.handle_pointer_event(&mut event);
796 let mut parent = node.parent();
797 while let Some(current) = parent {
798 if event.handled {
799 break;
800 }
801 current.handle_bubbled_pointer_event(&mut event);
802 parent = current.parent();
803 }
804 event.handled
805 }
806
807 fn capture_pointer(&self, handle: NodeHandle) {
808 if self.resolve_node(handle).is_some() {
809 self.captured_pointer.replace(Some(handle));
810 self.apply_current_cursor();
811 }
812 }
813
814 fn release_pointer(&self, handle: NodeHandle) {
815 let captured = *self.captured_pointer.borrow();
816 if captured == Some(handle) {
817 self.captured_pointer.replace(None);
818 self.apply_current_cursor();
819 }
820 }
821
822 pub(crate) fn dispatch_wheel_event(
823 &self,
824 handle: NodeHandle,
825 scene_x: f32,
826 scene_y: f32,
827 delta_x: f32,
828 delta_y: f32,
829 delta_mode: u32,
830 modifiers: u32,
831 ) -> bool {
832 let Some(mut node) = self.resolve_node(handle) else {
833 return false;
834 };
835 let mut event =
836 WheelEventArgs::new(scene_x, scene_y, delta_x, delta_y, delta_mode, modifiers);
837 loop {
838 node.handle_wheel_event(&mut event);
839 if event.handled {
840 return true;
841 }
842 let Some(parent) = node.parent() else {
843 return false;
844 };
845 node = parent;
846 }
847 }
848
849 pub(crate) fn dispatch_key_event(
850 &self,
851 event_type: KeyEventType,
852 key: String,
853 modifiers: u32,
854 ) -> bool {
855 let key_filters = self
856 .key_filters
857 .borrow()
858 .iter()
859 .map(|entry| entry.callback.clone())
860 .collect::<Vec<_>>();
861 for filter in key_filters.iter().rev() {
862 if filter(event_type, key.as_str(), modifiers) {
863 return true;
864 }
865 }
866 let Some(handle) = *self.focused.borrow() else {
867 return event_type == KeyEventType::Down
868 && handle_keyboard_scroll_fallback(key.as_str(), modifiers);
869 };
870 let Some(mut node) = self.resolve_node(handle) else {
871 return event_type == KeyEventType::Down
872 && handle_keyboard_scroll_fallback(key.as_str(), modifiers);
873 };
874 let fallback_key = key.clone();
875 let mut event = KeyEventArgs::new(event_type, key, modifiers);
876 loop {
877 node.handle_key_event(&mut event);
878 if event.handled {
879 return true;
880 }
881 let Some(parent) = node.parent() else {
882 return event_type == KeyEventType::Down
883 && handle_keyboard_scroll_fallback(fallback_key.as_str(), modifiers);
884 };
885 node = parent;
886 }
887 }
888
889 pub(crate) fn dispatch_focus_changed(&self, handle: NodeHandle, focused: bool) {
890 if focused {
891 self.focused.replace(Some(handle));
892 } else if self.focused.borrow().as_ref() == Some(&handle) {
893 self.focused.replace(None);
894 }
895
896 if let Some(node) = self.resolve_node(handle) {
897 node.handle_focus_changed(FocusChangedEventArgs { focused });
898 }
899 }
900
901 pub(crate) fn dispatch_scroll(
902 &self,
903 handle: NodeHandle,
904 offset_x: f32,
905 offset_y: f32,
906 content_width: f32,
907 content_height: f32,
908 viewport_width: f32,
909 viewport_height: f32,
910 ) {
911 tool_tip_manager::ToolTipManager::handle_scroll();
912 selection_handle_adorner::refresh_active_geometry();
913 mobile_text_selection_toolbar::refresh_active_geometry(
914 selection_handle_adorner::is_visible(),
915 );
916 let hooks = self.scroll_hooks.borrow().clone();
917 for hook in hooks {
918 hook();
919 }
920 if let Some(node) = self.resolve_node(handle) {
921 node.handle_scroll_changed(
922 offset_x,
923 offset_y,
924 content_width,
925 content_height,
926 viewport_width,
927 viewport_height,
928 );
929 }
930 }
931
932 pub(crate) fn dispatch_selection_changed(&self, handle: NodeHandle, start: u32, end: u32) {
933 let Some(node) = self.resolve_node(handle) else {
934 selection_handle_adorner::clear();
935 mobile_text_selection_toolbar::clear();
936 return;
937 };
938 let (chrome_handle, chrome_node) = selection_chrome_target(handle, &node);
939 selection_handle_adorner::handle_selection_changed(chrome_handle, start, end);
940 mobile_text_selection_toolbar::handle_selection_changed(
941 chrome_handle,
942 &chrome_node,
943 start,
944 end,
945 selection_handle_adorner::is_visible(),
946 );
947 node.handle_selection_changed(start, end);
948 }
949
950 pub(crate) fn dispatch_text_changed(&self, handle: NodeHandle, text: String) {
951 if let Some(node) = self.resolve_node(handle) {
952 node.handle_text_changed(text);
953 }
954 }
955
956 pub(crate) fn dispatch_text_replaced(
957 &self,
958 handle: NodeHandle,
959 start: u32,
960 end: u32,
961 text: String,
962 ) {
963 if let Some(node) = self.resolve_node(handle) {
964 node.handle_text_replaced(start, end, text);
965 }
966 }
967
968 pub(crate) fn dispatch_cross_selection_changed(&self, handle: NodeHandle, text: String) {
969 crate::context_menu_manager::handle_selection_changed(&text);
970 let Some(node) = self.resolve_node(handle) else {
971 selection_handle_adorner::clear();
972 mobile_text_selection_toolbar::clear();
973 return;
974 };
975 selection_handle_adorner::handle_cross_selection_changed(handle, &text);
976 mobile_text_selection_toolbar::handle_cross_selection_changed(
977 handle,
978 &node,
979 &text,
980 selection_handle_adorner::is_visible(),
981 );
982 node.handle_cross_selection_changed(text);
983 }
984
985 pub(crate) fn resolve_gesture_owner(&self, handle: NodeHandle) -> NodeHandle {
986 let mut node = self.resolve_node(handle);
987 while let Some(current) = node {
988 if current.is_effectively_enabled_for_routing()
989 && current.is_effectively_visible_for_routing()
990 && current.gesture_intent_for_routing() != GestureIntent::None
991 {
992 return current.handle();
993 }
994 node = current.parent();
995 }
996 NodeHandle::INVALID
997 }
998
999 pub(crate) fn get_gesture_intent(&self, handle: NodeHandle) -> GestureIntent {
1000 self.resolve_node(handle)
1001 .map(|node| node.gesture_intent_for_routing())
1002 .unwrap_or(GestureIntent::None)
1003 }
1004
1005 #[allow(clippy::too_many_arguments)]
1006 pub(crate) fn dispatch_gesture_event(
1007 &self,
1008 handle: NodeHandle,
1009 phase: GestureEventPhase,
1010 kind: GestureEventKind,
1011 scene_x: f32,
1012 scene_y: f32,
1013 delta_x: f32,
1014 delta_y: f32,
1015 scale: f32,
1016 pointer_count: i32,
1017 ) -> bool {
1018 let Some(node) = self.resolve_node(handle) else {
1019 return false;
1020 };
1021 let mut event = GestureEventArgs::new(
1022 phase,
1023 kind,
1024 scene_x,
1025 scene_y,
1026 delta_x,
1027 delta_y,
1028 scale,
1029 pointer_count,
1030 );
1031 node.handle_gesture_event(&mut event);
1032 let mut parent = node.parent();
1033 while let Some(current) = parent {
1034 if event.handled {
1035 break;
1036 }
1037 current.handle_bubbled_gesture_event(&mut event);
1038 parent = current.parent();
1039 }
1040 event.handled
1041 }
1042
1043 pub(crate) fn resolve_long_press_owner(&self, handle: NodeHandle) -> NodeHandle {
1044 let mut node = self.resolve_node(handle);
1045 while let Some(current) = node {
1046 if current.is_effectively_enabled_for_routing()
1047 && current.is_effectively_visible_for_routing()
1048 && current.has_long_press_for_routing()
1049 {
1050 return current.handle();
1051 }
1052 node = current.parent();
1053 }
1054 let mut node = self.resolve_node(handle);
1055 while let Some(current) = node {
1056 if current.is_effectively_enabled_for_routing()
1057 && current.is_effectively_visible_for_routing()
1058 && (current.link_url_for_routing().is_some()
1059 || current.is_image_or_svg_for_routing()
1060 || self.has_image_context_menu_target_descendant(¤t))
1061 {
1062 return current.handle();
1063 }
1064 if current.is_effectively_enabled_for_routing()
1065 && current.is_effectively_visible_for_routing()
1066 && (current.is_selectable_text_for_routing()
1067 || current.is_editable_text_for_routing())
1068 {
1069 return current.handle();
1070 }
1071 node = current.parent();
1072 }
1073 NodeHandle::INVALID
1074 }
1075
1076 fn has_image_context_menu_target_descendant(&self, node: &NodeRef) -> bool {
1077 for child in node.children() {
1078 if child.is_image_or_svg_for_routing()
1079 || self.has_image_context_menu_target_descendant(&child)
1080 {
1081 return true;
1082 }
1083 }
1084 false
1085 }
1086
1087 pub(crate) fn get_long_press_minimum_duration_ms(&self, handle: NodeHandle) -> i32 {
1088 self.resolve_node(handle)
1089 .map(|node| node.long_press_minimum_duration_ms_for_routing())
1090 .unwrap_or(DEFAULT_LONG_PRESS_MINIMUM_DURATION_MS)
1091 }
1092
1093 pub(crate) fn get_long_press_movement_tolerance(&self, handle: NodeHandle) -> f32 {
1094 self.resolve_node(handle)
1095 .map(|node| node.long_press_movement_tolerance_for_routing())
1096 .unwrap_or(DEFAULT_LONG_PRESS_MOVEMENT_TOLERANCE)
1097 }
1098
1099 pub(crate) fn dispatch_long_press_event(
1100 &self,
1101 handle: NodeHandle,
1102 scene_x: f32,
1103 scene_y: f32,
1104 pointer_id: i32,
1105 pointer_type: PointerType,
1106 modifiers: u32,
1107 duration_ms: i32,
1108 ) -> bool {
1109 let Some(node) = self.resolve_node(handle) else {
1110 return false;
1111 };
1112 let mut event = LongPressEventArgs::new(
1113 scene_x,
1114 scene_y,
1115 pointer_id,
1116 pointer_type,
1117 modifiers,
1118 duration_ms,
1119 );
1120 node.handle_long_press_event(&mut event);
1121 let mut parent = node.parent();
1122 while let Some(current) = parent {
1123 if event.handled {
1124 break;
1125 }
1126 current.handle_bubbled_long_press_event(&mut event);
1127 parent = current.parent();
1128 }
1129 if !event.handled
1130 && matches!(pointer_type, PointerType::Touch | PointerType::Pen)
1131 && (node.is_selectable_text_for_routing() || node.is_editable_text_for_routing())
1132 {
1133 mobile_text_selection_toolbar::set_pending_cross_selection_text_handle(handle);
1134 event.handled = crate::bindings::ui::select_word_at(handle.raw(), scene_x, scene_y);
1135 if !event.handled {
1136 mobile_text_selection_toolbar::set_pending_cross_selection_text_handle(
1137 NodeHandle::INVALID,
1138 );
1139 }
1140 }
1141 if !event.handled {
1142 event.handled =
1143 context_menu_manager::show_for_long_press(handle.raw(), scene_x, scene_y);
1144 }
1145 event.handled
1146 }
1147
1148 pub(crate) fn dispatch_external_drop_event(
1149 &self,
1150 handle: NodeHandle,
1151 event_type: external_drop::ExternalDragEventType,
1152 x: f32,
1153 y: f32,
1154 modifiers: u32,
1155 items: Vec<external_drop::ExternalDropItemInfo>,
1156 ) -> crate::drag_drop::DragDropEffects {
1157 let pointed_node = if handle == NodeHandle::INVALID {
1158 None
1159 } else {
1160 self.resolve_node(handle)
1161 };
1162 external_drop::handle_event(pointed_node, event_type, x, y, modifiers, items)
1163 }
1164
1165 pub(crate) fn resolve_node(&self, handle: NodeHandle) -> Option<NodeRef> {
1166 self.nodes
1167 .borrow()
1168 .get(&handle)
1169 .and_then(WeakNodeRef::upgrade)
1170 }
1171
1172 fn handle_cursor_style_changed(&self, handle: NodeHandle) {
1173 let captured = *self.captured_pointer.borrow();
1174 if captured == Some(handle) {
1175 self.apply_current_cursor();
1176 return;
1177 }
1178 if captured.is_some() {
1179 return;
1180 }
1181 if self.hover_stack.borrow().last().copied() == Some(handle) {
1182 self.apply_current_cursor();
1183 }
1184 }
1185
1186 fn push_hover(&self, handle: NodeHandle) {
1187 let mut hover_stack = self.hover_stack.borrow_mut();
1188 if let Some(index) = hover_stack.iter().position(|hovered| *hovered == handle) {
1189 hover_stack.remove(index);
1190 }
1191 hover_stack.push(handle);
1192 }
1193
1194 fn pop_hover(&self, handle: NodeHandle) {
1195 let mut hover_stack = self.hover_stack.borrow_mut();
1196 if let Some(index) = hover_stack.iter().position(|hovered| *hovered == handle) {
1197 hover_stack.remove(index);
1198 }
1199 }
1200
1201 fn clear_hover_stack(&self) {
1202 self.hover_stack.borrow_mut().clear();
1203 }
1204
1205 fn apply_current_cursor(&self) {
1206 let drag_cursor = drag_drop::cursor_override_style();
1207 let style = if drag_cursor != CursorStyle::Default {
1208 drag_cursor
1209 } else if let Some(handle) = *self.captured_pointer.borrow() {
1210 self.resolve_node(handle)
1211 .map(|node| node.cursor_style_for_routing())
1212 .unwrap_or(CursorStyle::Default)
1213 } else if let Some(handle) = *self.selection_cursor_owner.borrow() {
1214 self.resolve_node(handle)
1215 .map(|node| node.cursor_style_for_routing())
1216 .unwrap_or(CursorStyle::Default)
1217 } else if let Some(handle) = self.hover_stack.borrow().last().copied() {
1218 self.resolve_node(handle)
1219 .map(|node| node.cursor_style_for_routing())
1220 .unwrap_or(CursorStyle::Default)
1221 } else {
1222 CursorStyle::Default
1223 };
1224 self.apply_cursor(style);
1225 }
1226
1227 fn apply_cursor(&self, style: CursorStyle) {
1228 if self.current_cursor_style.get() == style {
1229 return;
1230 }
1231 self.current_cursor_style.set(style);
1232 unsafe { crate::ffi::fui_set_cursor(style as u32) };
1233 }
1234}
1235
1236fn selection_cursor_target(node: &NodeRef) -> Option<NodeRef> {
1237 if !node.is_effectively_enabled_for_routing() || !node.is_effectively_visible_for_routing() {
1238 return None;
1239 }
1240 if node.is_selectable_text_for_routing() || node.is_editable_text_for_routing() {
1241 return Some(node.clone());
1242 }
1243 find_editable_text_descendant(node)
1244}
1245
1246fn selection_chrome_target(handle: NodeHandle, node: &NodeRef) -> (NodeHandle, NodeRef) {
1247 if node.is_selectable_text_for_routing() || node.is_editable_text_for_routing() {
1248 return (handle, node.clone());
1249 }
1250 if let Some(editor) = find_editable_text_descendant(node) {
1251 return (editor.handle(), editor);
1252 }
1253 (handle, node.clone())
1254}
1255
1256fn find_editable_text_descendant(node: &NodeRef) -> Option<NodeRef> {
1257 for child in node.children() {
1258 if child.is_editable_text_for_routing() {
1259 return Some(child);
1260 }
1261 if let Some(descendant) = find_editable_text_descendant(&child) {
1262 return Some(descendant);
1263 }
1264 }
1265 None
1266}
1267
1268fn preserves_selection_on_pointer_down_for_routing(node: Option<&NodeRef>) -> bool {
1269 let mut current = node.cloned();
1270 while let Some(value) = current {
1271 if value.preserves_selection_on_pointer_down_for_routing() {
1272 return true;
1273 }
1274 current = value.parent();
1275 }
1276 false
1277}
1278
1279thread_local! {
1280 static EVENT_ROUTER: EventRouter = EventRouter::new();
1281}
1282
1283fn with_event_router<T>(callback: impl FnOnce(&EventRouter) -> T) -> T {
1284 EVENT_ROUTER.with(callback)
1285}
1286
1287pub(crate) fn register_node(node: &NodeRef) {
1288 with_event_router(|router| router.register(node));
1289}
1290
1291pub(crate) fn unregister_node(handle: NodeHandle) {
1292 with_event_router(|router| router.unregister(handle));
1293}
1294
1295pub(crate) fn reset() {
1296 with_event_router(|router| router.reset());
1297}
1298
1299pub(crate) fn resolve_node(handle: NodeHandle) -> Option<NodeRef> {
1300 with_event_router(|router| router.resolve_node(handle))
1301}
1302
1303pub(crate) fn focused_node() -> Option<NodeRef> {
1304 with_event_router(|router| {
1305 let handle = *router.focused.borrow();
1306 handle.and_then(|handle| router.resolve_node(handle))
1307 })
1308}
1309
1310pub(crate) fn capture_pointer(handle: NodeHandle) {
1311 with_event_router(|router| router.capture_pointer(handle));
1312}
1313
1314pub(crate) fn release_pointer(handle: NodeHandle) {
1315 with_event_router(|router| router.release_pointer(handle));
1316}
1317
1318pub(crate) fn deactivate_subtree(root: &NodeRef) {
1319 fn belongs_to_subtree(node: &NodeRef, root: &NodeRef) -> bool {
1320 let mut current = Some(node.clone());
1321 while let Some(value) = current {
1322 if value.ptr_eq(root) {
1323 return true;
1324 }
1325 current = value.parent();
1326 }
1327 false
1328 }
1329 with_event_router(|router| {
1330 if router
1331 .captured_pointer
1332 .borrow()
1333 .and_then(|handle| router.resolve_node(handle))
1334 .is_some_and(|node| belongs_to_subtree(&node, root))
1335 {
1336 router.captured_pointer.replace(None);
1337 }
1338 if router
1339 .focused
1340 .borrow()
1341 .and_then(|handle| router.resolve_node(handle))
1342 .is_some_and(|node| belongs_to_subtree(&node, root))
1343 {
1344 router.focused.replace(None);
1345 crate::bindings::ui::request_focus(NodeHandle::INVALID.raw());
1346 }
1347 router.hover_stack.borrow_mut().retain(|handle| {
1348 router
1349 .resolve_node(*handle)
1350 .is_none_or(|node| !belongs_to_subtree(&node, root))
1351 });
1352 router.selection_cursor_owner.replace(None);
1353 drag_drop::reset();
1354 external_drop::reset();
1355 router.apply_current_cursor();
1356 });
1357 crate::context_menu_manager::hide_active_menu();
1358 crate::tool_tip_manager::ToolTipManager::clear();
1359 crate::selection_handle_adorner::clear();
1360 crate::mobile_text_selection_toolbar::clear();
1361}
1362
1363pub(crate) fn handle_cursor_style_changed(handle: NodeHandle) {
1364 with_event_router(|router| router.handle_cursor_style_changed(handle));
1365}
1366
1367pub(crate) fn begin_drag_session(source: NodeRef) -> bool {
1368 drag_drop::begin_session(source)
1369}
1370
1371pub(crate) fn cancel_drag_session(session: crate::drag_drop::DragSession) {
1372 drag_drop::cancel_session(session);
1373}
1374
1375pub(crate) fn cancel_drag_session_for_source(source: &NodeRef) {
1376 drag_drop::cancel_session_for_source(source);
1377}
1378
1379pub(crate) fn dispatch_pointer_event(
1380 handle: NodeHandle,
1381 event_type: PointerEventType,
1382 scene_x: f32,
1383 scene_y: f32,
1384 modifiers: u32,
1385 pointer_id: i32,
1386 pointer_type: PointerType,
1387 button: i32,
1388 buttons: u32,
1389 pressure: f32,
1390 width: f32,
1391 height: f32,
1392 click_count: i32,
1393) -> bool {
1394 focus_visibility::show_keyboard_focus_for_pointer_event(event_type);
1395 with_event_router(|router| {
1396 router.dispatch_pointer_event(
1397 handle,
1398 event_type,
1399 scene_x,
1400 scene_y,
1401 modifiers,
1402 pointer_id,
1403 pointer_type,
1404 button,
1405 buttons,
1406 pressure,
1407 width,
1408 height,
1409 click_count,
1410 )
1411 })
1412}
1413
1414#[allow(clippy::too_many_arguments)]
1415pub(crate) fn dispatch_pointer_event_with_metadata(
1416 handle: NodeHandle,
1417 event_type: PointerEventType,
1418 scene_x: f32,
1419 scene_y: f32,
1420 modifiers: u32,
1421 pointer_id: i32,
1422 pointer_type: PointerType,
1423 button: i32,
1424 buttons: u32,
1425 pressure: f32,
1426 width: f32,
1427 height: f32,
1428 click_count: i32,
1429 extended: PointerExtendedMetadata,
1430) -> bool {
1431 focus_visibility::show_keyboard_focus_for_pointer_event(event_type);
1432 with_event_router(|router| {
1433 router.dispatch_pointer_event_with_metadata(
1434 handle,
1435 event_type,
1436 scene_x,
1437 scene_y,
1438 modifiers,
1439 pointer_id,
1440 pointer_type,
1441 button,
1442 buttons,
1443 pressure,
1444 width,
1445 height,
1446 click_count,
1447 extended,
1448 )
1449 })
1450}
1451
1452pub(crate) fn dispatch_wheel_event(
1453 handle: NodeHandle,
1454 scene_x: f32,
1455 scene_y: f32,
1456 delta_x: f32,
1457 delta_y: f32,
1458 delta_mode: u32,
1459 modifiers: u32,
1460) -> bool {
1461 with_event_router(|router| {
1462 router.dispatch_wheel_event(
1463 handle, scene_x, scene_y, delta_x, delta_y, delta_mode, modifiers,
1464 )
1465 })
1466}
1467
1468pub(crate) fn dispatch_key_event(event_type: KeyEventType, key: String, modifiers: u32) -> bool {
1469 let handled =
1470 with_event_router(|router| router.dispatch_key_event(event_type, key.clone(), modifiers));
1471 if !handled || key.as_str() != "Tab" || modifiers != 0 {
1472 focus_visibility::show_keyboard_focus_for_key_event(event_type, key.as_str(), modifiers);
1473 }
1474 handled
1475}
1476
1477pub(crate) fn dispatch_focus_changed(handle: NodeHandle, focused: bool) {
1478 with_event_router(|router| router.dispatch_focus_changed(handle, focused));
1479}
1480
1481pub(crate) fn push_key_filter(callback: impl Fn(KeyEventType, &str, u32) -> bool + 'static) -> u32 {
1482 with_event_router(|router| router.push_key_filter(Rc::new(callback)))
1483}
1484
1485pub(crate) fn remove_key_filter(token: u32) {
1486 with_event_router(|router| router.remove_key_filter(token));
1487}
1488
1489pub(crate) fn register_scroll_hook(callback: impl Fn() + 'static) {
1490 with_event_router(|router| router.register_scroll_hook(Rc::new(callback)));
1491}
1492
1493pub(crate) fn focused_node_is_enabled_button() -> bool {
1494 with_event_router(|router| router.focused_node_is_enabled_button())
1495}
1496
1497pub(crate) fn dispatch_scroll(
1498 handle: NodeHandle,
1499 offset_x: f32,
1500 offset_y: f32,
1501 content_width: f32,
1502 content_height: f32,
1503 viewport_width: f32,
1504 viewport_height: f32,
1505) {
1506 with_event_router(|router| {
1507 router.dispatch_scroll(
1508 handle,
1509 offset_x,
1510 offset_y,
1511 content_width,
1512 content_height,
1513 viewport_width,
1514 viewport_height,
1515 )
1516 });
1517}
1518
1519pub(crate) fn dispatch_selection_changed(handle: NodeHandle, start: u32, end: u32) {
1520 with_event_router(|router| router.dispatch_selection_changed(handle, start, end));
1521}
1522
1523pub(crate) fn dispatch_text_changed(handle: NodeHandle, text: String) {
1524 with_event_router(|router| router.dispatch_text_changed(handle, text));
1525}
1526
1527pub(crate) fn dispatch_text_replaced(handle: NodeHandle, start: u32, end: u32, text: String) {
1528 with_event_router(|router| router.dispatch_text_replaced(handle, start, end, text));
1529}
1530
1531pub(crate) fn dispatch_cross_selection_changed(handle: NodeHandle, text: String) {
1532 with_event_router(|router| router.dispatch_cross_selection_changed(handle, text));
1533}
1534
1535pub(crate) fn resolve_gesture_owner(handle: NodeHandle) -> NodeHandle {
1536 with_event_router(|router| router.resolve_gesture_owner(handle))
1537}
1538
1539pub(crate) fn get_gesture_intent(handle: NodeHandle) -> GestureIntent {
1540 with_event_router(|router| router.get_gesture_intent(handle))
1541}
1542
1543#[allow(clippy::too_many_arguments)]
1544pub(crate) fn dispatch_gesture_event(
1545 handle: NodeHandle,
1546 phase: GestureEventPhase,
1547 kind: GestureEventKind,
1548 scene_x: f32,
1549 scene_y: f32,
1550 delta_x: f32,
1551 delta_y: f32,
1552 scale: f32,
1553 pointer_count: i32,
1554) -> bool {
1555 with_event_router(|router| {
1556 router.dispatch_gesture_event(
1557 handle,
1558 phase,
1559 kind,
1560 scene_x,
1561 scene_y,
1562 delta_x,
1563 delta_y,
1564 scale,
1565 pointer_count,
1566 )
1567 })
1568}
1569
1570pub(crate) fn resolve_long_press_owner(handle: NodeHandle) -> NodeHandle {
1571 with_event_router(|router| router.resolve_long_press_owner(handle))
1572}
1573
1574pub(crate) fn registered_node(handle: u64) -> Option<NodeRef> {
1575 with_event_router(|router| router.resolve_node(NodeHandle::from_raw(handle)))
1576}
1577
1578pub(crate) fn get_long_press_minimum_duration_ms(handle: NodeHandle) -> i32 {
1579 with_event_router(|router| router.get_long_press_minimum_duration_ms(handle))
1580}
1581
1582pub(crate) fn get_long_press_movement_tolerance(handle: NodeHandle) -> f32 {
1583 with_event_router(|router| router.get_long_press_movement_tolerance(handle))
1584}
1585
1586pub(crate) fn dispatch_long_press_event(
1587 handle: NodeHandle,
1588 scene_x: f32,
1589 scene_y: f32,
1590 pointer_id: i32,
1591 pointer_type: PointerType,
1592 modifiers: u32,
1593 duration_ms: i32,
1594) -> bool {
1595 with_event_router(|router| {
1596 router.dispatch_long_press_event(
1597 handle,
1598 scene_x,
1599 scene_y,
1600 pointer_id,
1601 pointer_type,
1602 modifiers,
1603 duration_ms,
1604 )
1605 })
1606}
1607
1608pub(crate) fn dispatch_external_drop_event(
1609 handle: NodeHandle,
1610 event_type: external_drop::ExternalDragEventType,
1611 x: f32,
1612 y: f32,
1613 modifiers: u32,
1614 items: Vec<external_drop::ExternalDropItemInfo>,
1615) -> crate::drag_drop::DragDropEffects {
1616 with_event_router(|router| {
1617 router.dispatch_external_drop_event(handle, event_type, x, y, modifiers, items)
1618 })
1619}
1620
1621#[cfg_attr(
1622 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1623 no_mangle
1624)]
1625pub extern "C" fn __fui_key_buffer() -> *const u8 {
1626 std::ptr::addr_of_mut!(KEY_BUFFER).cast::<u8>()
1627}
1628
1629#[cfg_attr(
1630 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1631 no_mangle
1632)]
1633pub extern "C" fn __fui_key_buffer_size() -> u32 {
1634 256
1635}
1636
1637#[cfg_attr(
1638 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1639 no_mangle
1640)]
1641pub extern "C" fn __fui_text_buffer() -> *const u8 {
1642 std::ptr::addr_of_mut!(TEXT_BUFFER).cast::<u8>()
1643}
1644
1645#[cfg_attr(
1646 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1647 no_mangle
1648)]
1649pub extern "C" fn __fui_text_buffer_size() -> u32 {
1650 (16 * 1024) as u32
1651}
1652
1653#[cfg_attr(
1654 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1655 no_mangle
1656)]
1657pub extern "C" fn __fui_on_pointer_event_with_metadata(
1658 event_type: u32,
1659 handle: u64,
1660 x: f32,
1661 y: f32,
1662 modifiers: u32,
1663 pointer_id: i32,
1664 pointer_type: u32,
1665 button: i32,
1666 buttons: u32,
1667 pressure: f32,
1668 width: f32,
1669 height: f32,
1670 click_count: i32,
1671 is_primary: bool,
1672 tangential_pressure: f32,
1673 tilt_x: f32,
1674 tilt_y: f32,
1675 twist: f32,
1676) -> bool {
1677 let routed_event_type = match event_type {
1678 1 => PointerEventType::Down,
1679 2 => PointerEventType::Up,
1680 3 => PointerEventType::Move,
1681 4 => PointerEventType::Enter,
1682 5 => PointerEventType::Leave,
1683 _ => PointerEventType::Cancel,
1684 };
1685 crate::context_menu_manager::handle_pointer_selection_event(
1686 routed_event_type == PointerEventType::Down,
1687 handle,
1688 );
1689 dispatch_pointer_event_with_metadata(
1690 NodeHandle::from_raw(handle),
1691 routed_event_type,
1692 x,
1693 y,
1694 modifiers,
1695 pointer_id,
1696 PointerType::from_raw(pointer_type),
1697 button,
1698 buttons,
1699 pressure,
1700 width,
1701 height,
1702 click_count,
1703 PointerExtendedMetadata {
1704 is_primary,
1705 tangential_pressure,
1706 tilt_x,
1707 tilt_y,
1708 twist,
1709 },
1710 )
1711}
1712
1713#[cfg_attr(
1714 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1715 no_mangle
1716)]
1717pub extern "C" fn __fui_on_wheel_event(
1718 handle: u64,
1719 x: f32,
1720 y: f32,
1721 delta_x: f32,
1722 delta_y: f32,
1723 delta_mode: u32,
1724 modifiers: u32,
1725) -> bool {
1726 dispatch_wheel_event(
1727 NodeHandle::from_raw(handle),
1728 x,
1729 y,
1730 delta_x,
1731 delta_y,
1732 delta_mode,
1733 modifiers,
1734 )
1735}
1736
1737#[cfg_attr(
1738 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1739 no_mangle
1740)]
1741pub unsafe extern "C" fn __fui_on_key_event(
1744 event_type: u32,
1745 key_ptr: *const u8,
1746 key_len: u32,
1747 modifiers: u32,
1748) -> bool {
1749 let key = if key_ptr.is_null() || key_len == 0 {
1750 String::new()
1751 } else {
1752 let bytes = unsafe { std::slice::from_raw_parts(key_ptr, key_len as usize) };
1753 String::from_utf8_lossy(bytes).into_owned()
1754 };
1755 dispatch_key_event(
1756 match event_type {
1757 2 => KeyEventType::Up,
1758 _ => KeyEventType::Down,
1759 },
1760 key,
1761 modifiers,
1762 )
1763}
1764
1765#[cfg_attr(
1766 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1767 no_mangle
1768)]
1769pub extern "C" fn __fui_on_focus_changed(handle: u64, focused: bool) {
1770 dispatch_focus_changed(NodeHandle::from_raw(handle), focused);
1771}
1772
1773#[cfg_attr(
1774 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1775 no_mangle
1776)]
1777pub unsafe extern "C" fn __fui_on_text_changed(handle: u64, text_ptr: *const u8, text_len: u32) {
1780 let text = if text_ptr.is_null() || text_len == 0 {
1781 String::new()
1782 } else {
1783 let bytes = unsafe { std::slice::from_raw_parts(text_ptr, text_len as usize) };
1784 String::from_utf8_lossy(bytes).into_owned()
1785 };
1786 dispatch_text_changed(NodeHandle::from_raw(handle), text);
1787}
1788
1789#[cfg_attr(
1790 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1791 no_mangle
1792)]
1793pub unsafe extern "C" fn __fui_on_text_replaced(
1796 handle: u64,
1797 start: u32,
1798 end: u32,
1799 text_ptr: *const u8,
1800 text_len: u32,
1801) {
1802 let text = if text_ptr.is_null() || text_len == 0 {
1803 String::new()
1804 } else {
1805 let bytes = unsafe { std::slice::from_raw_parts(text_ptr, text_len as usize) };
1806 String::from_utf8_lossy(bytes).into_owned()
1807 };
1808 dispatch_text_replaced(NodeHandle::from_raw(handle), start, end, text);
1809}
1810
1811#[cfg_attr(
1812 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1813 no_mangle
1814)]
1815pub extern "C" fn __fui_on_selection_changed(handle: u64, start: u32, end: u32) {
1816 dispatch_selection_changed(NodeHandle::from_raw(handle), start, end);
1817}
1818
1819#[cfg_attr(
1820 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1821 no_mangle
1822)]
1823pub unsafe extern "C" fn __fui_on_cross_selection_changed(
1826 handle: u64,
1827 text_ptr: *const u8,
1828 text_len: u32,
1829) {
1830 let text = if text_ptr.is_null() || text_len == 0 {
1831 String::new()
1832 } else {
1833 let bytes = unsafe { std::slice::from_raw_parts(text_ptr, text_len as usize) };
1834 String::from_utf8_lossy(bytes).into_owned()
1835 };
1836 dispatch_cross_selection_changed(NodeHandle::from_raw(handle), text);
1837}
1838
1839#[cfg_attr(
1840 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1841 no_mangle
1842)]
1843pub extern "C" fn __fui_resolve_gesture_owner(handle: u64) -> u64 {
1844 resolve_gesture_owner(NodeHandle::from_raw(handle)).raw()
1845}
1846
1847#[cfg_attr(
1848 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1849 no_mangle
1850)]
1851pub extern "C" fn __fui_get_gesture_intent(handle: u64) -> u32 {
1852 get_gesture_intent(NodeHandle::from_raw(handle)) as u32
1853}
1854
1855#[cfg_attr(
1856 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1857 no_mangle
1858)]
1859pub extern "C" fn __fui_on_gesture_event(
1860 handle: u64,
1861 phase: u32,
1862 kind: u32,
1863 scene_x: f32,
1864 scene_y: f32,
1865 delta_x: f32,
1866 delta_y: f32,
1867 scale: f32,
1868 pointer_count: i32,
1869) -> bool {
1870 dispatch_gesture_event(
1871 NodeHandle::from_raw(handle),
1872 GestureEventPhase::from_raw(phase),
1873 GestureEventKind::from_raw(kind),
1874 scene_x,
1875 scene_y,
1876 delta_x,
1877 delta_y,
1878 scale,
1879 pointer_count,
1880 )
1881}
1882
1883#[cfg_attr(
1884 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1885 no_mangle
1886)]
1887pub extern "C" fn __fui_resolve_long_press_owner(handle: u64) -> u64 {
1888 resolve_long_press_owner(NodeHandle::from_raw(handle)).raw()
1889}
1890
1891#[cfg_attr(
1892 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1893 no_mangle
1894)]
1895pub extern "C" fn __fui_get_long_press_minimum_duration_ms(handle: u64) -> i32 {
1896 get_long_press_minimum_duration_ms(NodeHandle::from_raw(handle))
1897}
1898
1899#[cfg_attr(
1900 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1901 no_mangle
1902)]
1903pub extern "C" fn __fui_get_long_press_movement_tolerance(handle: u64) -> f32 {
1904 get_long_press_movement_tolerance(NodeHandle::from_raw(handle))
1905}
1906
1907#[cfg_attr(
1908 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1909 no_mangle
1910)]
1911pub extern "C" fn __fui_long_press_continues_pointer_events(handle: u64) -> bool {
1912 registered_node(handle).is_some_and(|node| node.has_drag_source())
1913}
1914
1915#[cfg_attr(
1916 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1917 no_mangle
1918)]
1919pub extern "C" fn __fui_on_long_press_event(
1920 handle: u64,
1921 scene_x: f32,
1922 scene_y: f32,
1923 pointer_id: i32,
1924 pointer_type: u32,
1925 modifiers: u32,
1926 duration_ms: i32,
1927) -> bool {
1928 dispatch_long_press_event(
1929 NodeHandle::from_raw(handle),
1930 scene_x,
1931 scene_y,
1932 pointer_id,
1933 PointerType::from_raw(pointer_type),
1934 modifiers,
1935 duration_ms,
1936 )
1937}
1938
1939#[cfg_attr(
1940 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1941 no_mangle
1942)]
1943pub unsafe extern "C" fn __fui_on_external_drag_event(
1946 event_type: u32,
1947 handle: u64,
1948 x: f32,
1949 y: f32,
1950 modifiers: u32,
1951 payload_ptr: *const u8,
1952 payload_len: u32,
1953) -> u32 {
1954 let items = external_drop::decode_payload(payload_ptr, payload_len);
1955 let effect = dispatch_external_drop_event(
1956 NodeHandle::from_raw(handle),
1957 external_drop::ExternalDragEventType::from_raw(event_type),
1958 x,
1959 y,
1960 modifiers,
1961 items.clone(),
1962 );
1963 if !payload_ptr.is_null() && payload_len > 0 && items.is_empty() {
1964 crate::logger::error(
1965 "ExternalDrop",
1966 &format!(
1967 "Dropped malformed external payload for handle {}.",
1968 NodeHandle::from_raw(handle).raw()
1969 ),
1970 );
1971 }
1972 effect as u32
1973}
1974
1975#[cfg_attr(
1976 any(not(feature = "worker-runtime"), feature = "native-runtime"),
1977 no_mangle
1978)]
1979pub extern "C" fn fui_dispatch_custom_draw(handle: u64, canvas_ptr: usize) {
1980 if let Some(node) = resolve_node(NodeHandle::from_raw(handle)) {
1981 node.handle_custom_draw(canvas_ptr);
1982 }
1983}