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 handled: bool,
171}
172
173impl PointerEventArgs {
174 #[allow(clippy::too_many_arguments)]
175 pub fn new(
176 target_handle: NodeHandle,
177 event_type: PointerEventType,
178 scene_x: f32,
179 scene_y: f32,
180 modifiers: u32,
181 pointer_id: i32,
182 pointer_type: PointerType,
183 button: i32,
184 buttons: u32,
185 pressure: f32,
186 width: f32,
187 height: f32,
188 click_count: i32,
189 ) -> Self {
190 Self {
191 target_handle,
192 event_type,
193 scene_x,
194 scene_y,
195 x: scene_x,
196 y: scene_y,
197 modifiers,
198 pointer_id,
199 pointer_type,
200 button: PointerButton::from_raw(button),
201 buttons: PointerButtons::from_raw(buttons),
202 pressure,
203 width,
204 height,
205 click_count,
206 handled: false,
207 }
208 }
209
210 pub fn capture_pointer(&self) {
211 crate::event::capture_pointer(self.target_handle);
212 unsafe { crate::ffi::fui_set_pointer_capture(self.target_handle.raw()) };
213 }
214
215 pub fn release_pointer_capture(&self) {
216 crate::event::release_pointer(self.target_handle);
217 unsafe { crate::ffi::fui_release_pointer_capture() };
218 }
219
220 pub(crate) fn target_handle(&self) -> NodeHandle {
221 self.target_handle
222 }
223}
224
225#[derive(Clone, Debug)]
226pub struct WheelEventArgs {
227 pub scene_x: f32,
228 pub scene_y: f32,
229 pub x: f32,
230 pub y: f32,
231 pub delta_x: f32,
232 pub delta_y: f32,
233 pub delta_mode: u32,
234 pub modifiers: u32,
235 pub handled: bool,
236}
237
238impl WheelEventArgs {
239 pub fn new(
240 scene_x: f32,
241 scene_y: f32,
242 delta_x: f32,
243 delta_y: f32,
244 delta_mode: u32,
245 modifiers: u32,
246 ) -> Self {
247 Self {
248 scene_x,
249 scene_y,
250 x: scene_x,
251 y: scene_y,
252 delta_x,
253 delta_y,
254 delta_mode,
255 modifiers,
256 handled: false,
257 }
258 }
259}
260
261#[derive(Clone, Debug)]
262pub struct GestureEventArgs {
263 pub phase: GestureEventPhase,
264 pub kind: GestureEventKind,
265 pub scene_x: f32,
266 pub scene_y: f32,
267 pub delta_x: f32,
268 pub delta_y: f32,
269 pub scale: f32,
270 pub pointer_count: i32,
271 pub x: f32,
272 pub y: f32,
273 pub handled: bool,
274}
275
276impl GestureEventArgs {
277 #[allow(clippy::too_many_arguments)]
278 pub fn new(
279 phase: GestureEventPhase,
280 kind: GestureEventKind,
281 scene_x: f32,
282 scene_y: f32,
283 delta_x: f32,
284 delta_y: f32,
285 scale: f32,
286 pointer_count: i32,
287 ) -> Self {
288 Self {
289 phase,
290 kind,
291 scene_x,
292 scene_y,
293 delta_x,
294 delta_y,
295 scale,
296 pointer_count,
297 x: scene_x,
298 y: scene_y,
299 handled: false,
300 }
301 }
302}
303
304#[derive(Clone, Debug)]
305pub struct KeyEventArgs {
306 pub event_type: KeyEventType,
307 pub key: String,
308 pub modifiers: u32,
309 pub handled: bool,
310}
311
312impl KeyEventArgs {
313 pub fn new(event_type: KeyEventType, key: String, modifiers: u32) -> Self {
314 Self {
315 event_type,
316 key,
317 modifiers,
318 handled: false,
319 }
320 }
321}
322
323type GlobalKeyFilter = Rc<dyn Fn(KeyEventType, &str, u32) -> bool>;
324type ScrollHook = Rc<dyn Fn()>;
325
326struct GlobalKeyFilterEntry {
327 token: u32,
328 callback: GlobalKeyFilter,
329}
330
331#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
332pub struct FocusChangedEventArgs {
333 pub focused: bool,
334}
335
336#[derive(Clone, Debug, Default, PartialEq, Eq)]
337pub struct TextChangedEventArgs {
338 pub text: String,
339}
340
341#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
342pub struct SelectionChangedEventArgs {
343 pub start: u32,
344 pub end: u32,
345}
346
347#[derive(Clone, Debug)]
348pub struct LongPressEventArgs {
349 pub scene_x: f32,
350 pub scene_y: f32,
351 pub pointer_id: i32,
352 pub pointer_type: PointerType,
353 pub modifiers: u32,
354 pub duration_ms: i32,
355 pub x: f32,
356 pub y: f32,
357 pub handled: bool,
358}
359
360impl LongPressEventArgs {
361 pub fn new(
362 scene_x: f32,
363 scene_y: f32,
364 pointer_id: i32,
365 pointer_type: PointerType,
366 modifiers: u32,
367 duration_ms: i32,
368 ) -> Self {
369 Self {
370 scene_x,
371 scene_y,
372 pointer_id,
373 pointer_type,
374 modifiers,
375 duration_ms,
376 x: scene_x,
377 y: scene_y,
378 handled: false,
379 }
380 }
381}
382
383pub(crate) struct EventRouter {
384 nodes: RefCell<HashMap<NodeHandle, WeakNodeRef>>,
385 focused: RefCell<Option<NodeHandle>>,
386 captured_pointer: RefCell<Option<NodeHandle>>,
387 selection_cursor_owner: RefCell<Option<NodeHandle>>,
388 hover_stack: RefCell<Vec<NodeHandle>>,
389 current_cursor_style: Cell<CursorStyle>,
390 key_filters: RefCell<Vec<GlobalKeyFilterEntry>>,
391 scroll_hooks: RefCell<Vec<ScrollHook>>,
392 next_key_filter_token: Cell<u32>,
393}
394
395impl EventRouter {
396 pub(crate) fn new() -> Self {
397 Self {
398 nodes: RefCell::new(HashMap::new()),
399 focused: RefCell::new(None),
400 captured_pointer: RefCell::new(None),
401 selection_cursor_owner: RefCell::new(None),
402 hover_stack: RefCell::new(Vec::new()),
403 current_cursor_style: Cell::new(CursorStyle::Default),
404 key_filters: RefCell::new(Vec::new()),
405 scroll_hooks: RefCell::new(Vec::new()),
406 next_key_filter_token: Cell::new(1),
407 }
408 }
409
410 fn register(&self, node: &NodeRef) {
411 self.nodes
412 .borrow_mut()
413 .insert(node.handle(), node.downgrade());
414 register_keyboard_scroll_node(node);
415 }
416
417 fn reset(&self) {
418 self.nodes.borrow_mut().clear();
419 self.focused.replace(None);
420 self.captured_pointer.replace(None);
421 self.selection_cursor_owner.replace(None);
422 self.hover_stack.borrow_mut().clear();
423 self.key_filters.borrow_mut().clear();
424 self.next_key_filter_token.set(1);
428 drag_drop::reset();
429 external_drop::reset();
430 self.apply_cursor(CursorStyle::Default);
431 reset_keyboard_scroll_tracking();
432 }
433
434 fn unregister(&self, handle: NodeHandle) {
435 if let Some(node) = self.resolve_node(handle) {
436 drag_drop::handle_node_destroyed(node.clone());
437 external_drop::handle_node_destroyed(node);
438 }
439 self.nodes.borrow_mut().remove(&handle);
440 self.pop_hover(handle);
441 if self.focused.borrow().as_ref() == Some(&handle) {
442 self.focused.replace(None);
443 }
444 if self.captured_pointer.borrow().as_ref() == Some(&handle) {
445 self.captured_pointer.replace(None);
446 }
447 if self.selection_cursor_owner.borrow().as_ref() == Some(&handle) {
448 self.selection_cursor_owner.replace(None);
449 }
450 self.apply_current_cursor();
451 unregister_keyboard_scroll_node(handle);
452 }
453
454 fn push_key_filter(&self, callback: GlobalKeyFilter) -> u32 {
455 let token = self.next_key_filter_token.get();
456 self.next_key_filter_token.set(token + 1);
457 self.key_filters
458 .borrow_mut()
459 .push(GlobalKeyFilterEntry { token, callback });
460 token
461 }
462
463 fn remove_key_filter(&self, token: u32) {
464 self.key_filters
465 .borrow_mut()
466 .retain(|entry| entry.token != token);
467 }
468
469 fn register_scroll_hook(&self, callback: ScrollHook) {
470 self.scroll_hooks.borrow_mut().push(callback);
471 }
472
473 fn focused_node_is_enabled_button(&self) -> bool {
474 let Some(handle) = *self.focused.borrow() else {
475 return false;
476 };
477 let Some(node) = self.resolve_node(handle) else {
478 return false;
479 };
480 node.semantic_role_for_routing() == Some(SemanticRole::Button)
481 && node.is_enabled_for_routing()
482 }
483
484 pub(crate) fn dispatch_pointer_event(
485 &self,
486 handle: NodeHandle,
487 event_type: PointerEventType,
488 scene_x: f32,
489 scene_y: f32,
490 modifiers: u32,
491 pointer_id: i32,
492 pointer_type: PointerType,
493 button: i32,
494 buttons: u32,
495 pressure: f32,
496 width: f32,
497 height: f32,
498 click_count: i32,
499 ) -> bool {
500 context_menu_manager::track_pointer_event(event_type, handle.raw());
501 selection_handle_adorner::record_pointer_event(event_type, pointer_type);
502 let pointed_node = self.resolve_node(handle);
503 if event_type == PointerEventType::Down
504 && (button == PointerButton::Primary as i32
505 || matches!(pointer_type, PointerType::Touch | PointerType::Pen))
506 && crate::controls::ContextMenu::dismiss_for_outside_pointer_down(scene_x, scene_y)
507 {
508 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
509 self.apply_current_cursor();
510 return true;
511 }
512 if event_type == PointerEventType::Down
513 && !preserves_selection_on_pointer_down_for_routing(pointed_node.as_ref())
514 && mobile_text_selection_toolbar::dismiss_for_outside_pointer_down(scene_x, scene_y)
515 {
516 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
517 self.apply_current_cursor();
518 return true;
519 }
520 if event_type == PointerEventType::Down
521 && button == PointerButton::Primary as i32
522 && pointer_type != PointerType::Touch
523 {
524 self.selection_cursor_owner.replace(
525 pointed_node
526 .as_ref()
527 .and_then(selection_cursor_target)
528 .map(|node| node.handle()),
529 );
530 } else if matches!(event_type, PointerEventType::Up | PointerEventType::Cancel) {
531 self.selection_cursor_owner.replace(None);
532 }
533 if event_type == PointerEventType::Up {
534 track_keyboard_scroll_pointer_up(pointed_node.clone(), scene_x, scene_y);
535 }
536 if matches!(
537 event_type,
538 PointerEventType::Move | PointerEventType::Up | PointerEventType::Cancel
539 ) {
540 let captured_handle = *self.captured_pointer.borrow();
541 if let Some(captured_handle) = captured_handle {
542 if let Some(captured_node) = self.resolve_node(captured_handle) {
543 let captured_at_dispatch = captured_handle;
544 let handled = self.dispatch_pointer_to_node(
545 captured_node,
546 captured_handle,
547 event_type,
548 scene_x,
549 scene_y,
550 modifiers,
551 pointer_id,
552 pointer_type,
553 button,
554 buttons,
555 pressure,
556 width,
557 height,
558 click_count,
559 );
560 let captured_after_dispatch = *self.captured_pointer.borrow();
561 if matches!(event_type, PointerEventType::Up | PointerEventType::Cancel)
562 && captured_after_dispatch == Some(captured_at_dispatch)
563 {
564 self.captured_pointer.replace(None);
565 }
566 drag_drop::handle_pointer_event(
567 pointed_node,
568 event_type,
569 scene_x,
570 scene_y,
571 modifiers,
572 );
573 self.apply_current_cursor();
574 return handled;
575 }
576 self.captured_pointer.replace(None);
577 self.apply_current_cursor();
578 }
579 }
580 if matches!(
581 event_type,
582 PointerEventType::Move | PointerEventType::Up | PointerEventType::Cancel
583 ) {
584 let mut selection_event = PointerEventArgs::new(
585 handle,
586 event_type,
587 scene_x,
588 scene_y,
589 modifiers,
590 pointer_id,
591 pointer_type,
592 button,
593 buttons,
594 pressure,
595 width,
596 height,
597 click_count,
598 );
599 if selection_handle_adorner::route_active_handle_drag_event(&mut selection_event) {
600 drag_drop::handle_pointer_event(
601 pointed_node,
602 event_type,
603 scene_x,
604 scene_y,
605 modifiers,
606 );
607 self.apply_current_cursor();
608 return selection_event.handled;
609 }
610 }
611
612 if handle == NodeHandle::INVALID {
613 if event_type == PointerEventType::Leave {
614 self.clear_hover_stack();
615 }
616 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
617 self.apply_current_cursor();
618 return false;
619 }
620
621 let Some(node) = pointed_node.clone() else {
622 if event_type == PointerEventType::Leave {
623 self.clear_hover_stack();
624 }
625 drag_drop::handle_pointer_event(None, event_type, scene_x, scene_y, modifiers);
626 self.apply_current_cursor();
627 return false;
628 };
629 if event_type == PointerEventType::Enter {
630 self.push_hover(handle);
631 } else if event_type == PointerEventType::Leave {
632 self.pop_hover(handle);
633 }
634 let handled = self.dispatch_pointer_to_node(
635 node,
636 handle,
637 event_type,
638 scene_x,
639 scene_y,
640 modifiers,
641 pointer_id,
642 pointer_type,
643 button,
644 buttons,
645 pressure,
646 width,
647 height,
648 click_count,
649 );
650 drag_drop::handle_pointer_event(pointed_node, event_type, scene_x, scene_y, modifiers);
651 self.apply_current_cursor();
652 handled
653 }
654
655 #[allow(clippy::too_many_arguments)]
656 fn dispatch_pointer_to_node(
657 &self,
658 node: NodeRef,
659 target_handle: NodeHandle,
660 event_type: PointerEventType,
661 scene_x: f32,
662 scene_y: f32,
663 modifiers: u32,
664 pointer_id: i32,
665 pointer_type: PointerType,
666 button: i32,
667 buttons: u32,
668 pressure: f32,
669 width: f32,
670 height: f32,
671 click_count: i32,
672 ) -> bool {
673 let mut event = PointerEventArgs::new(
674 target_handle,
675 event_type,
676 scene_x,
677 scene_y,
678 modifiers,
679 pointer_id,
680 pointer_type,
681 button,
682 buttons,
683 pressure,
684 width,
685 height,
686 click_count,
687 );
688 node.handle_pointer_event(&mut event);
689 let mut parent = node.parent();
690 while let Some(current) = parent {
691 if event.handled {
692 break;
693 }
694 current.handle_bubbled_pointer_event(&mut event);
695 parent = current.parent();
696 }
697 event.handled
698 }
699
700 fn capture_pointer(&self, handle: NodeHandle) {
701 if self.resolve_node(handle).is_some() {
702 self.captured_pointer.replace(Some(handle));
703 self.apply_current_cursor();
704 }
705 }
706
707 fn release_pointer(&self, handle: NodeHandle) {
708 let captured = *self.captured_pointer.borrow();
709 if captured == Some(handle) {
710 self.captured_pointer.replace(None);
711 self.apply_current_cursor();
712 }
713 }
714
715 pub(crate) fn dispatch_wheel_event(
716 &self,
717 handle: NodeHandle,
718 scene_x: f32,
719 scene_y: f32,
720 delta_x: f32,
721 delta_y: f32,
722 delta_mode: u32,
723 modifiers: u32,
724 ) -> bool {
725 let Some(mut node) = self.resolve_node(handle) else {
726 return false;
727 };
728 let mut event =
729 WheelEventArgs::new(scene_x, scene_y, delta_x, delta_y, delta_mode, modifiers);
730 loop {
731 node.handle_wheel_event(&mut event);
732 if event.handled {
733 return true;
734 }
735 let Some(parent) = node.parent() else {
736 return false;
737 };
738 node = parent;
739 }
740 }
741
742 pub(crate) fn dispatch_key_event(
743 &self,
744 event_type: KeyEventType,
745 key: String,
746 modifiers: u32,
747 ) -> bool {
748 let key_filters = self
749 .key_filters
750 .borrow()
751 .iter()
752 .map(|entry| entry.callback.clone())
753 .collect::<Vec<_>>();
754 for filter in key_filters.iter().rev() {
755 if filter(event_type, key.as_str(), modifiers) {
756 return true;
757 }
758 }
759 let Some(handle) = *self.focused.borrow() else {
760 return event_type == KeyEventType::Down
761 && handle_keyboard_scroll_fallback(key.as_str(), modifiers);
762 };
763 let Some(mut node) = self.resolve_node(handle) else {
764 return event_type == KeyEventType::Down
765 && handle_keyboard_scroll_fallback(key.as_str(), modifiers);
766 };
767 let fallback_key = key.clone();
768 let mut event = KeyEventArgs::new(event_type, key, modifiers);
769 loop {
770 node.handle_key_event(&mut event);
771 if event.handled {
772 return true;
773 }
774 let Some(parent) = node.parent() else {
775 return event_type == KeyEventType::Down
776 && handle_keyboard_scroll_fallback(fallback_key.as_str(), modifiers);
777 };
778 node = parent;
779 }
780 }
781
782 pub(crate) fn dispatch_focus_changed(&self, handle: NodeHandle, focused: bool) {
783 if focused {
784 self.focused.replace(Some(handle));
785 } else if self.focused.borrow().as_ref() == Some(&handle) {
786 self.focused.replace(None);
787 }
788
789 if let Some(node) = self.resolve_node(handle) {
790 node.handle_focus_changed(FocusChangedEventArgs { focused });
791 }
792 }
793
794 pub(crate) fn dispatch_scroll(
795 &self,
796 handle: NodeHandle,
797 offset_x: f32,
798 offset_y: f32,
799 content_width: f32,
800 content_height: f32,
801 viewport_width: f32,
802 viewport_height: f32,
803 ) {
804 tool_tip_manager::ToolTipManager::handle_scroll();
805 selection_handle_adorner::refresh_active_geometry();
806 mobile_text_selection_toolbar::refresh_active_geometry(
807 selection_handle_adorner::is_visible(),
808 );
809 let hooks = self.scroll_hooks.borrow().clone();
810 for hook in hooks {
811 hook();
812 }
813 if let Some(node) = self.resolve_node(handle) {
814 node.handle_scroll_changed(
815 offset_x,
816 offset_y,
817 content_width,
818 content_height,
819 viewport_width,
820 viewport_height,
821 );
822 }
823 }
824
825 pub(crate) fn dispatch_selection_changed(&self, handle: NodeHandle, start: u32, end: u32) {
826 let Some(node) = self.resolve_node(handle) else {
827 selection_handle_adorner::clear();
828 mobile_text_selection_toolbar::clear();
829 return;
830 };
831 let (chrome_handle, chrome_node) = selection_chrome_target(handle, &node);
832 selection_handle_adorner::handle_selection_changed(chrome_handle, start, end);
833 mobile_text_selection_toolbar::handle_selection_changed(
834 chrome_handle,
835 &chrome_node,
836 start,
837 end,
838 selection_handle_adorner::is_visible(),
839 );
840 node.handle_selection_changed(start, end);
841 }
842
843 pub(crate) fn dispatch_text_changed(&self, handle: NodeHandle, text: String) {
844 if let Some(node) = self.resolve_node(handle) {
845 node.handle_text_changed(text);
846 }
847 }
848
849 pub(crate) fn dispatch_text_replaced(
850 &self,
851 handle: NodeHandle,
852 start: u32,
853 end: u32,
854 text: String,
855 ) {
856 if let Some(node) = self.resolve_node(handle) {
857 node.handle_text_replaced(start, end, text);
858 }
859 }
860
861 pub(crate) fn dispatch_cross_selection_changed(&self, handle: NodeHandle, text: String) {
862 crate::context_menu_manager::handle_selection_changed(&text);
863 let Some(node) = self.resolve_node(handle) else {
864 selection_handle_adorner::clear();
865 mobile_text_selection_toolbar::clear();
866 return;
867 };
868 selection_handle_adorner::handle_cross_selection_changed(handle, &text);
869 mobile_text_selection_toolbar::handle_cross_selection_changed(
870 handle,
871 &node,
872 &text,
873 selection_handle_adorner::is_visible(),
874 );
875 node.handle_cross_selection_changed(text);
876 }
877
878 pub(crate) fn resolve_gesture_owner(&self, handle: NodeHandle) -> NodeHandle {
879 let mut node = self.resolve_node(handle);
880 while let Some(current) = node {
881 if current.is_effectively_enabled_for_routing()
882 && current.is_effectively_visible_for_routing()
883 && current.gesture_intent_for_routing() != GestureIntent::None
884 {
885 return current.handle();
886 }
887 node = current.parent();
888 }
889 NodeHandle::INVALID
890 }
891
892 pub(crate) fn get_gesture_intent(&self, handle: NodeHandle) -> GestureIntent {
893 self.resolve_node(handle)
894 .map(|node| node.gesture_intent_for_routing())
895 .unwrap_or(GestureIntent::None)
896 }
897
898 #[allow(clippy::too_many_arguments)]
899 pub(crate) fn dispatch_gesture_event(
900 &self,
901 handle: NodeHandle,
902 phase: GestureEventPhase,
903 kind: GestureEventKind,
904 scene_x: f32,
905 scene_y: f32,
906 delta_x: f32,
907 delta_y: f32,
908 scale: f32,
909 pointer_count: i32,
910 ) -> bool {
911 let Some(node) = self.resolve_node(handle) else {
912 return false;
913 };
914 let mut event = GestureEventArgs::new(
915 phase,
916 kind,
917 scene_x,
918 scene_y,
919 delta_x,
920 delta_y,
921 scale,
922 pointer_count,
923 );
924 node.handle_gesture_event(&mut event);
925 let mut parent = node.parent();
926 while let Some(current) = parent {
927 if event.handled {
928 break;
929 }
930 current.handle_bubbled_gesture_event(&mut event);
931 parent = current.parent();
932 }
933 event.handled
934 }
935
936 pub(crate) fn resolve_long_press_owner(&self, handle: NodeHandle) -> NodeHandle {
937 let mut node = self.resolve_node(handle);
938 while let Some(current) = node {
939 if current.is_effectively_enabled_for_routing()
940 && current.is_effectively_visible_for_routing()
941 && current.has_long_press_for_routing()
942 {
943 return current.handle();
944 }
945 node = current.parent();
946 }
947 let mut node = self.resolve_node(handle);
948 while let Some(current) = node {
949 if current.is_effectively_enabled_for_routing()
950 && current.is_effectively_visible_for_routing()
951 && (current.link_url_for_routing().is_some()
952 || current.is_image_or_svg_for_routing()
953 || self.has_image_context_menu_target_descendant(¤t))
954 {
955 return current.handle();
956 }
957 if current.is_effectively_enabled_for_routing()
958 && current.is_effectively_visible_for_routing()
959 && (current.is_selectable_text_for_routing()
960 || current.is_editable_text_for_routing())
961 {
962 return current.handle();
963 }
964 node = current.parent();
965 }
966 NodeHandle::INVALID
967 }
968
969 fn has_image_context_menu_target_descendant(&self, node: &NodeRef) -> bool {
970 for child in node.children() {
971 if child.is_image_or_svg_for_routing()
972 || self.has_image_context_menu_target_descendant(&child)
973 {
974 return true;
975 }
976 }
977 false
978 }
979
980 pub(crate) fn get_long_press_minimum_duration_ms(&self, handle: NodeHandle) -> i32 {
981 self.resolve_node(handle)
982 .map(|node| node.long_press_minimum_duration_ms_for_routing())
983 .unwrap_or(DEFAULT_LONG_PRESS_MINIMUM_DURATION_MS)
984 }
985
986 pub(crate) fn get_long_press_movement_tolerance(&self, handle: NodeHandle) -> f32 {
987 self.resolve_node(handle)
988 .map(|node| node.long_press_movement_tolerance_for_routing())
989 .unwrap_or(DEFAULT_LONG_PRESS_MOVEMENT_TOLERANCE)
990 }
991
992 pub(crate) fn dispatch_long_press_event(
993 &self,
994 handle: NodeHandle,
995 scene_x: f32,
996 scene_y: f32,
997 pointer_id: i32,
998 pointer_type: PointerType,
999 modifiers: u32,
1000 duration_ms: i32,
1001 ) -> bool {
1002 let Some(node) = self.resolve_node(handle) else {
1003 return false;
1004 };
1005 let mut event = LongPressEventArgs::new(
1006 scene_x,
1007 scene_y,
1008 pointer_id,
1009 pointer_type,
1010 modifiers,
1011 duration_ms,
1012 );
1013 node.handle_long_press_event(&mut event);
1014 let mut parent = node.parent();
1015 while let Some(current) = parent {
1016 if event.handled {
1017 break;
1018 }
1019 current.handle_bubbled_long_press_event(&mut event);
1020 parent = current.parent();
1021 }
1022 if !event.handled
1023 && matches!(pointer_type, PointerType::Touch | PointerType::Pen)
1024 && (node.is_selectable_text_for_routing() || node.is_editable_text_for_routing())
1025 {
1026 mobile_text_selection_toolbar::set_pending_cross_selection_text_handle(handle);
1027 event.handled = crate::bindings::ui::select_word_at(handle.raw(), scene_x, scene_y);
1028 if !event.handled {
1029 mobile_text_selection_toolbar::set_pending_cross_selection_text_handle(
1030 NodeHandle::INVALID,
1031 );
1032 }
1033 }
1034 if !event.handled {
1035 event.handled =
1036 context_menu_manager::show_for_long_press(handle.raw(), scene_x, scene_y);
1037 }
1038 event.handled
1039 }
1040
1041 pub(crate) fn dispatch_external_drop_event(
1042 &self,
1043 handle: NodeHandle,
1044 event_type: external_drop::ExternalDragEventType,
1045 x: f32,
1046 y: f32,
1047 modifiers: u32,
1048 items: Vec<external_drop::ExternalDropItemInfo>,
1049 ) -> crate::drag_drop::DragDropEffects {
1050 let pointed_node = if handle == NodeHandle::INVALID {
1051 None
1052 } else {
1053 self.resolve_node(handle)
1054 };
1055 external_drop::handle_event(pointed_node, event_type, x, y, modifiers, items)
1056 }
1057
1058 pub(crate) fn resolve_node(&self, handle: NodeHandle) -> Option<NodeRef> {
1059 self.nodes
1060 .borrow()
1061 .get(&handle)
1062 .and_then(WeakNodeRef::upgrade)
1063 }
1064
1065 fn handle_cursor_style_changed(&self, handle: NodeHandle) {
1066 let captured = *self.captured_pointer.borrow();
1067 if captured == Some(handle) {
1068 self.apply_current_cursor();
1069 return;
1070 }
1071 if captured.is_some() {
1072 return;
1073 }
1074 if self.hover_stack.borrow().last().copied() == Some(handle) {
1075 self.apply_current_cursor();
1076 }
1077 }
1078
1079 fn push_hover(&self, handle: NodeHandle) {
1080 let mut hover_stack = self.hover_stack.borrow_mut();
1081 if let Some(index) = hover_stack.iter().position(|hovered| *hovered == handle) {
1082 hover_stack.remove(index);
1083 }
1084 hover_stack.push(handle);
1085 }
1086
1087 fn pop_hover(&self, handle: NodeHandle) {
1088 let mut hover_stack = self.hover_stack.borrow_mut();
1089 if let Some(index) = hover_stack.iter().position(|hovered| *hovered == handle) {
1090 hover_stack.remove(index);
1091 }
1092 }
1093
1094 fn clear_hover_stack(&self) {
1095 self.hover_stack.borrow_mut().clear();
1096 }
1097
1098 fn apply_current_cursor(&self) {
1099 let drag_cursor = drag_drop::cursor_override_style();
1100 let style = if drag_cursor != CursorStyle::Default {
1101 drag_cursor
1102 } else if let Some(handle) = *self.captured_pointer.borrow() {
1103 self.resolve_node(handle)
1104 .map(|node| node.cursor_style_for_routing())
1105 .unwrap_or(CursorStyle::Default)
1106 } else if let Some(handle) = *self.selection_cursor_owner.borrow() {
1107 self.resolve_node(handle)
1108 .map(|node| node.cursor_style_for_routing())
1109 .unwrap_or(CursorStyle::Default)
1110 } else if let Some(handle) = self.hover_stack.borrow().last().copied() {
1111 self.resolve_node(handle)
1112 .map(|node| node.cursor_style_for_routing())
1113 .unwrap_or(CursorStyle::Default)
1114 } else {
1115 CursorStyle::Default
1116 };
1117 self.apply_cursor(style);
1118 }
1119
1120 fn apply_cursor(&self, style: CursorStyle) {
1121 if self.current_cursor_style.get() == style {
1122 return;
1123 }
1124 self.current_cursor_style.set(style);
1125 unsafe { crate::ffi::fui_set_cursor(style as u32) };
1126 }
1127}
1128
1129fn selection_cursor_target(node: &NodeRef) -> Option<NodeRef> {
1130 if !node.is_effectively_enabled_for_routing() || !node.is_effectively_visible_for_routing() {
1131 return None;
1132 }
1133 if node.is_selectable_text_for_routing() || node.is_editable_text_for_routing() {
1134 return Some(node.clone());
1135 }
1136 find_editable_text_descendant(node)
1137}
1138
1139fn selection_chrome_target(handle: NodeHandle, node: &NodeRef) -> (NodeHandle, NodeRef) {
1140 if node.is_selectable_text_for_routing() || node.is_editable_text_for_routing() {
1141 return (handle, node.clone());
1142 }
1143 if let Some(editor) = find_editable_text_descendant(node) {
1144 return (editor.handle(), editor);
1145 }
1146 (handle, node.clone())
1147}
1148
1149fn find_editable_text_descendant(node: &NodeRef) -> Option<NodeRef> {
1150 for child in node.children() {
1151 if child.is_editable_text_for_routing() {
1152 return Some(child);
1153 }
1154 if let Some(descendant) = find_editable_text_descendant(&child) {
1155 return Some(descendant);
1156 }
1157 }
1158 None
1159}
1160
1161fn preserves_selection_on_pointer_down_for_routing(node: Option<&NodeRef>) -> bool {
1162 let mut current = node.cloned();
1163 while let Some(value) = current {
1164 if value.preserves_selection_on_pointer_down_for_routing() {
1165 return true;
1166 }
1167 current = value.parent();
1168 }
1169 false
1170}
1171
1172thread_local! {
1173 static EVENT_ROUTER: EventRouter = EventRouter::new();
1174}
1175
1176fn with_event_router<T>(callback: impl FnOnce(&EventRouter) -> T) -> T {
1177 EVENT_ROUTER.with(callback)
1178}
1179
1180pub(crate) fn register_node(node: &NodeRef) {
1181 with_event_router(|router| router.register(node));
1182}
1183
1184pub(crate) fn unregister_node(handle: NodeHandle) {
1185 with_event_router(|router| router.unregister(handle));
1186}
1187
1188pub(crate) fn reset() {
1189 with_event_router(|router| router.reset());
1190}
1191
1192pub(crate) fn resolve_node(handle: NodeHandle) -> Option<NodeRef> {
1193 with_event_router(|router| router.resolve_node(handle))
1194}
1195
1196pub(crate) fn focused_node() -> Option<NodeRef> {
1197 with_event_router(|router| {
1198 let handle = *router.focused.borrow();
1199 handle.and_then(|handle| router.resolve_node(handle))
1200 })
1201}
1202
1203pub(crate) fn capture_pointer(handle: NodeHandle) {
1204 with_event_router(|router| router.capture_pointer(handle));
1205}
1206
1207pub(crate) fn release_pointer(handle: NodeHandle) {
1208 with_event_router(|router| router.release_pointer(handle));
1209}
1210
1211pub(crate) fn handle_cursor_style_changed(handle: NodeHandle) {
1212 with_event_router(|router| router.handle_cursor_style_changed(handle));
1213}
1214
1215pub(crate) fn begin_drag_session(source: NodeRef) -> bool {
1216 drag_drop::begin_session(source)
1217}
1218
1219pub(crate) fn cancel_drag_session(session: crate::drag_drop::DragSession) {
1220 drag_drop::cancel_session(session);
1221}
1222
1223pub(crate) fn cancel_drag_session_for_source(source: &NodeRef) {
1224 drag_drop::cancel_session_for_source(source);
1225}
1226
1227pub(crate) fn dispatch_pointer_event(
1228 handle: NodeHandle,
1229 event_type: PointerEventType,
1230 scene_x: f32,
1231 scene_y: f32,
1232 modifiers: u32,
1233 pointer_id: i32,
1234 pointer_type: PointerType,
1235 button: i32,
1236 buttons: u32,
1237 pressure: f32,
1238 width: f32,
1239 height: f32,
1240 click_count: i32,
1241) -> bool {
1242 focus_visibility::show_keyboard_focus_for_pointer_event(event_type);
1243 with_event_router(|router| {
1244 router.dispatch_pointer_event(
1245 handle,
1246 event_type,
1247 scene_x,
1248 scene_y,
1249 modifiers,
1250 pointer_id,
1251 pointer_type,
1252 button,
1253 buttons,
1254 pressure,
1255 width,
1256 height,
1257 click_count,
1258 )
1259 })
1260}
1261
1262pub(crate) fn dispatch_wheel_event(
1263 handle: NodeHandle,
1264 scene_x: f32,
1265 scene_y: f32,
1266 delta_x: f32,
1267 delta_y: f32,
1268 delta_mode: u32,
1269 modifiers: u32,
1270) -> bool {
1271 with_event_router(|router| {
1272 router.dispatch_wheel_event(
1273 handle, scene_x, scene_y, delta_x, delta_y, delta_mode, modifiers,
1274 )
1275 })
1276}
1277
1278pub(crate) fn dispatch_key_event(event_type: KeyEventType, key: String, modifiers: u32) -> bool {
1279 let handled =
1280 with_event_router(|router| router.dispatch_key_event(event_type, key.clone(), modifiers));
1281 if !handled || key.as_str() != "Tab" || modifiers != 0 {
1282 focus_visibility::show_keyboard_focus_for_key_event(event_type, key.as_str(), modifiers);
1283 }
1284 handled
1285}
1286
1287pub(crate) fn dispatch_focus_changed(handle: NodeHandle, focused: bool) {
1288 with_event_router(|router| router.dispatch_focus_changed(handle, focused));
1289}
1290
1291pub(crate) fn push_key_filter(callback: impl Fn(KeyEventType, &str, u32) -> bool + 'static) -> u32 {
1292 with_event_router(|router| router.push_key_filter(Rc::new(callback)))
1293}
1294
1295pub(crate) fn remove_key_filter(token: u32) {
1296 with_event_router(|router| router.remove_key_filter(token));
1297}
1298
1299pub(crate) fn register_scroll_hook(callback: impl Fn() + 'static) {
1300 with_event_router(|router| router.register_scroll_hook(Rc::new(callback)));
1301}
1302
1303pub(crate) fn focused_node_is_enabled_button() -> bool {
1304 with_event_router(|router| router.focused_node_is_enabled_button())
1305}
1306
1307pub(crate) fn dispatch_scroll(
1308 handle: NodeHandle,
1309 offset_x: f32,
1310 offset_y: f32,
1311 content_width: f32,
1312 content_height: f32,
1313 viewport_width: f32,
1314 viewport_height: f32,
1315) {
1316 with_event_router(|router| {
1317 router.dispatch_scroll(
1318 handle,
1319 offset_x,
1320 offset_y,
1321 content_width,
1322 content_height,
1323 viewport_width,
1324 viewport_height,
1325 )
1326 });
1327}
1328
1329pub(crate) fn dispatch_selection_changed(handle: NodeHandle, start: u32, end: u32) {
1330 with_event_router(|router| router.dispatch_selection_changed(handle, start, end));
1331}
1332
1333pub(crate) fn dispatch_text_changed(handle: NodeHandle, text: String) {
1334 with_event_router(|router| router.dispatch_text_changed(handle, text));
1335}
1336
1337pub(crate) fn dispatch_text_replaced(handle: NodeHandle, start: u32, end: u32, text: String) {
1338 with_event_router(|router| router.dispatch_text_replaced(handle, start, end, text));
1339}
1340
1341pub(crate) fn dispatch_cross_selection_changed(handle: NodeHandle, text: String) {
1342 with_event_router(|router| router.dispatch_cross_selection_changed(handle, text));
1343}
1344
1345pub(crate) fn resolve_gesture_owner(handle: NodeHandle) -> NodeHandle {
1346 with_event_router(|router| router.resolve_gesture_owner(handle))
1347}
1348
1349pub(crate) fn get_gesture_intent(handle: NodeHandle) -> GestureIntent {
1350 with_event_router(|router| router.get_gesture_intent(handle))
1351}
1352
1353#[allow(clippy::too_many_arguments)]
1354pub(crate) fn dispatch_gesture_event(
1355 handle: NodeHandle,
1356 phase: GestureEventPhase,
1357 kind: GestureEventKind,
1358 scene_x: f32,
1359 scene_y: f32,
1360 delta_x: f32,
1361 delta_y: f32,
1362 scale: f32,
1363 pointer_count: i32,
1364) -> bool {
1365 with_event_router(|router| {
1366 router.dispatch_gesture_event(
1367 handle,
1368 phase,
1369 kind,
1370 scene_x,
1371 scene_y,
1372 delta_x,
1373 delta_y,
1374 scale,
1375 pointer_count,
1376 )
1377 })
1378}
1379
1380pub(crate) fn resolve_long_press_owner(handle: NodeHandle) -> NodeHandle {
1381 with_event_router(|router| router.resolve_long_press_owner(handle))
1382}
1383
1384pub(crate) fn registered_node(handle: u64) -> Option<NodeRef> {
1385 with_event_router(|router| router.resolve_node(NodeHandle::from_raw(handle)))
1386}
1387
1388pub(crate) fn get_long_press_minimum_duration_ms(handle: NodeHandle) -> i32 {
1389 with_event_router(|router| router.get_long_press_minimum_duration_ms(handle))
1390}
1391
1392pub(crate) fn get_long_press_movement_tolerance(handle: NodeHandle) -> f32 {
1393 with_event_router(|router| router.get_long_press_movement_tolerance(handle))
1394}
1395
1396pub(crate) fn dispatch_long_press_event(
1397 handle: NodeHandle,
1398 scene_x: f32,
1399 scene_y: f32,
1400 pointer_id: i32,
1401 pointer_type: PointerType,
1402 modifiers: u32,
1403 duration_ms: i32,
1404) -> bool {
1405 with_event_router(|router| {
1406 router.dispatch_long_press_event(
1407 handle,
1408 scene_x,
1409 scene_y,
1410 pointer_id,
1411 pointer_type,
1412 modifiers,
1413 duration_ms,
1414 )
1415 })
1416}
1417
1418pub(crate) fn dispatch_external_drop_event(
1419 handle: NodeHandle,
1420 event_type: external_drop::ExternalDragEventType,
1421 x: f32,
1422 y: f32,
1423 modifiers: u32,
1424 items: Vec<external_drop::ExternalDropItemInfo>,
1425) -> crate::drag_drop::DragDropEffects {
1426 with_event_router(|router| {
1427 router.dispatch_external_drop_event(handle, event_type, x, y, modifiers, items)
1428 })
1429}
1430
1431#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1432pub extern "C" fn __fui_key_buffer() -> *const u8 {
1433 std::ptr::addr_of_mut!(KEY_BUFFER).cast::<u8>()
1434}
1435
1436#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1437pub extern "C" fn __fui_key_buffer_size() -> u32 {
1438 256
1439}
1440
1441#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1442pub extern "C" fn __fui_text_buffer() -> *const u8 {
1443 std::ptr::addr_of_mut!(TEXT_BUFFER).cast::<u8>()
1444}
1445
1446#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1447pub extern "C" fn __fui_text_buffer_size() -> u32 {
1448 (16 * 1024) as u32
1449}
1450
1451#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1452pub extern "C" fn __fui_on_pointer_event_with_metadata(
1453 event_type: u32,
1454 handle: u64,
1455 x: f32,
1456 y: f32,
1457 modifiers: u32,
1458 pointer_id: i32,
1459 pointer_type: u32,
1460 button: i32,
1461 buttons: u32,
1462 pressure: f32,
1463 width: f32,
1464 height: f32,
1465 click_count: i32,
1466) -> bool {
1467 let routed_event_type = match event_type {
1468 1 => PointerEventType::Down,
1469 2 => PointerEventType::Up,
1470 3 => PointerEventType::Move,
1471 4 => PointerEventType::Enter,
1472 5 => PointerEventType::Leave,
1473 _ => PointerEventType::Cancel,
1474 };
1475 crate::context_menu_manager::handle_pointer_selection_event(
1476 routed_event_type == PointerEventType::Down,
1477 handle,
1478 );
1479 dispatch_pointer_event(
1480 NodeHandle::from_raw(handle),
1481 routed_event_type,
1482 x,
1483 y,
1484 modifiers,
1485 pointer_id,
1486 PointerType::from_raw(pointer_type),
1487 button,
1488 buttons,
1489 pressure,
1490 width,
1491 height,
1492 click_count,
1493 )
1494}
1495
1496#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1497pub extern "C" fn __fui_on_wheel_event(
1498 handle: u64,
1499 x: f32,
1500 y: f32,
1501 delta_x: f32,
1502 delta_y: f32,
1503 delta_mode: u32,
1504 modifiers: u32,
1505) -> bool {
1506 dispatch_wheel_event(
1507 NodeHandle::from_raw(handle),
1508 x,
1509 y,
1510 delta_x,
1511 delta_y,
1512 delta_mode,
1513 modifiers,
1514 )
1515}
1516
1517#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1518pub unsafe extern "C" fn __fui_on_key_event(
1521 event_type: u32,
1522 key_ptr: *const u8,
1523 key_len: u32,
1524 modifiers: u32,
1525) -> bool {
1526 let key = if key_ptr.is_null() || key_len == 0 {
1527 String::new()
1528 } else {
1529 let bytes = unsafe { std::slice::from_raw_parts(key_ptr, key_len as usize) };
1530 String::from_utf8_lossy(bytes).into_owned()
1531 };
1532 dispatch_key_event(
1533 match event_type {
1534 2 => KeyEventType::Up,
1535 _ => KeyEventType::Down,
1536 },
1537 key,
1538 modifiers,
1539 )
1540}
1541
1542#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1543pub extern "C" fn __fui_on_focus_changed(handle: u64, focused: bool) {
1544 dispatch_focus_changed(NodeHandle::from_raw(handle), focused);
1545}
1546
1547#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1548pub unsafe extern "C" fn __fui_on_text_changed(handle: u64, text_ptr: *const u8, text_len: u32) {
1551 let text = if text_ptr.is_null() || text_len == 0 {
1552 String::new()
1553 } else {
1554 let bytes = unsafe { std::slice::from_raw_parts(text_ptr, text_len as usize) };
1555 String::from_utf8_lossy(bytes).into_owned()
1556 };
1557 dispatch_text_changed(NodeHandle::from_raw(handle), text);
1558}
1559
1560#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1561pub unsafe extern "C" fn __fui_on_text_replaced(
1564 handle: u64,
1565 start: u32,
1566 end: u32,
1567 text_ptr: *const u8,
1568 text_len: u32,
1569) {
1570 let text = if text_ptr.is_null() || text_len == 0 {
1571 String::new()
1572 } else {
1573 let bytes = unsafe { std::slice::from_raw_parts(text_ptr, text_len as usize) };
1574 String::from_utf8_lossy(bytes).into_owned()
1575 };
1576 dispatch_text_replaced(NodeHandle::from_raw(handle), start, end, text);
1577}
1578
1579#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1580pub extern "C" fn __fui_on_selection_changed(handle: u64, start: u32, end: u32) {
1581 dispatch_selection_changed(NodeHandle::from_raw(handle), start, end);
1582}
1583
1584#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1585pub unsafe extern "C" fn __fui_on_cross_selection_changed(
1588 handle: u64,
1589 text_ptr: *const u8,
1590 text_len: u32,
1591) {
1592 let text = if text_ptr.is_null() || text_len == 0 {
1593 String::new()
1594 } else {
1595 let bytes = unsafe { std::slice::from_raw_parts(text_ptr, text_len as usize) };
1596 String::from_utf8_lossy(bytes).into_owned()
1597 };
1598 dispatch_cross_selection_changed(NodeHandle::from_raw(handle), text);
1599}
1600
1601#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1602pub extern "C" fn __fui_resolve_gesture_owner(handle: u64) -> u64 {
1603 resolve_gesture_owner(NodeHandle::from_raw(handle)).raw()
1604}
1605
1606#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1607pub extern "C" fn __fui_get_gesture_intent(handle: u64) -> u32 {
1608 get_gesture_intent(NodeHandle::from_raw(handle)) as u32
1609}
1610
1611#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1612pub extern "C" fn __fui_on_gesture_event(
1613 handle: u64,
1614 phase: u32,
1615 kind: u32,
1616 scene_x: f32,
1617 scene_y: f32,
1618 delta_x: f32,
1619 delta_y: f32,
1620 scale: f32,
1621 pointer_count: i32,
1622) -> bool {
1623 dispatch_gesture_event(
1624 NodeHandle::from_raw(handle),
1625 GestureEventPhase::from_raw(phase),
1626 GestureEventKind::from_raw(kind),
1627 scene_x,
1628 scene_y,
1629 delta_x,
1630 delta_y,
1631 scale,
1632 pointer_count,
1633 )
1634}
1635
1636#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1637pub extern "C" fn __fui_resolve_long_press_owner(handle: u64) -> u64 {
1638 resolve_long_press_owner(NodeHandle::from_raw(handle)).raw()
1639}
1640
1641#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1642pub extern "C" fn __fui_get_long_press_minimum_duration_ms(handle: u64) -> i32 {
1643 get_long_press_minimum_duration_ms(NodeHandle::from_raw(handle))
1644}
1645
1646#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1647pub extern "C" fn __fui_get_long_press_movement_tolerance(handle: u64) -> f32 {
1648 get_long_press_movement_tolerance(NodeHandle::from_raw(handle))
1649}
1650
1651#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1652pub extern "C" fn __fui_long_press_continues_pointer_events(handle: u64) -> bool {
1653 registered_node(handle).is_some_and(|node| node.has_drag_source())
1654}
1655
1656#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1657pub extern "C" fn __fui_on_long_press_event(
1658 handle: u64,
1659 scene_x: f32,
1660 scene_y: f32,
1661 pointer_id: i32,
1662 pointer_type: u32,
1663 modifiers: u32,
1664 duration_ms: i32,
1665) -> bool {
1666 dispatch_long_press_event(
1667 NodeHandle::from_raw(handle),
1668 scene_x,
1669 scene_y,
1670 pointer_id,
1671 PointerType::from_raw(pointer_type),
1672 modifiers,
1673 duration_ms,
1674 )
1675}
1676
1677#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1678pub unsafe extern "C" fn __fui_on_external_drag_event(
1681 event_type: u32,
1682 handle: u64,
1683 x: f32,
1684 y: f32,
1685 modifiers: u32,
1686 payload_ptr: *const u8,
1687 payload_len: u32,
1688) -> u32 {
1689 let items = external_drop::decode_payload(payload_ptr, payload_len);
1690 let effect = dispatch_external_drop_event(
1691 NodeHandle::from_raw(handle),
1692 external_drop::ExternalDragEventType::from_raw(event_type),
1693 x,
1694 y,
1695 modifiers,
1696 items.clone(),
1697 );
1698 if !payload_ptr.is_null() && payload_len > 0 && items.is_empty() {
1699 crate::logger::error(
1700 "ExternalDrop",
1701 &format!(
1702 "Dropped malformed external payload for handle {}.",
1703 NodeHandle::from_raw(handle).raw()
1704 ),
1705 );
1706 }
1707 effect as u32
1708}
1709
1710#[cfg_attr(not(feature = "worker-runtime"), no_mangle)]
1711pub extern "C" fn fui_dispatch_custom_draw(handle: u64, canvas_ptr: usize) {
1712 if let Some(node) = resolve_node(NodeHandle::from_raw(handle)) {
1713 node.handle_custom_draw(canvas_ptr);
1714 }
1715}