1use cranpose_ui::FocusDirection;
2
3use super::*;
4
5impl<R> SurfaceMut<'_, R>
6where
7 R: Renderer,
8 R::Error: Debug,
9{
10 fn pointer_event(
11 &self,
12 kind: PointerEventKind,
13 position: Point,
14 global_position: Point,
15 event_time: PointerEventTime,
16 ) -> PointerEvent {
17 let screen_position = self.surface().screen_origin.map(|origin| Point {
18 x: origin.x + global_position.x,
19 y: origin.y + global_position.y,
20 });
21 let mut event = PointerEvent::new(kind, position, global_position)
22 .with_time_ms(event_time.platform_time_ms)
23 .with_animation_time_nanos(event_time.animation_time_nanos)
24 .with_screen_position(screen_position);
25 event.modifiers = self.shell.app.modifiers;
26 event
27 }
28
29 fn resolve_gesture_targets(
30 &self,
31 pointer: PointerId,
32 ) -> Vec<<<R as Renderer>::Scene as RenderScene>::HitTarget> {
33 self.resolve_hit_path(pointer)
34 }
35
36 fn resolve_hit_path(
37 &self,
38 pointer: PointerId,
39 ) -> Vec<<<R as Renderer>::Scene as RenderScene>::HitTarget> {
40 let Some(node_ids) = self.surface().hit_path_tracker.dispatch_order(pointer) else {
41 return Vec::new();
42 };
43
44 let scene = self.surface().renderer.scene();
45 let targets: Vec<_> = node_ids
46 .iter()
47 .filter_map(|&id| scene.find_target(id))
48 .collect();
49 log::trace!(
50 target: "cranpose::input",
51 "resolve_hit_path pointer={pointer:?} cached={node_ids:?} resolved_count={}",
52 targets.len()
53 );
54 targets
55 }
56
57 fn dispatch_targets<I>(&mut self, targets: I, event: PointerEvent, stop_on_consume: bool)
58 where
59 I: IntoIterator<Item = <<R as Renderer>::Scene as RenderScene>::HitTarget>,
60 {
61 let mut applier = self.shell.app.composition.applier_mut();
62 for target in targets {
63 let node_id = target.node_id();
64 target.dispatch_with_applier(&mut applier, event.clone());
65 log::trace!(
66 target: "cranpose::input",
67 "dispatch {:?} node={} consumed={} stop_on_consume={}",
68 event.kind,
69 node_id,
70 event.is_consumed(),
71 stop_on_consume,
72 );
73 if stop_on_consume && event.is_consumed() {
74 break;
75 }
76 }
77 event.finish_post_dispatch();
78 }
79
80 fn dispatch_to_hits(
81 &mut self,
82 hits: &[<<R as Renderer>::Scene as RenderScene>::HitTarget],
83 event: PointerEvent,
84 ) -> bool {
85 let capture_paths = hits
86 .iter()
87 .map(|hit| hit.capture_path())
88 .collect::<Vec<_>>();
89 let targets = crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
90 .into_iter()
91 .filter_map(|node_id| self.surface().renderer.scene().find_target(node_id))
92 .collect::<Vec<_>>();
93
94 self.dispatch_targets(targets, event.clone(), true);
95
96 event.is_consumed()
97 }
98
99 pub fn set_pointer_source(&mut self, source: PointerSource) {
105 self.surface_mut().pointer_source = source;
106 }
107
108 pub fn pointer_source(&self) -> PointerSource {
110 self.surface().pointer_source
111 }
112
113 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
114 self.set_cursor_at_time(x, y, None)
115 }
116
117 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
124 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
125 self.set_cursor_at_event_time(x, y, event_time)
126 }
127
128 pub fn set_cursor_at_event_time(
130 &mut self,
131 x: f32,
132 y: f32,
133 event_time: PointerEventTime,
134 ) -> bool {
135 let _event_handler = enter_event_handler_scope();
136 let app_context = Rc::clone(&self.shell.app.app_context);
137 let result = app_context.enter(|| {
138 run_in_mutable_snapshot(|| self.set_cursor_inner(x, y, event_time)).unwrap_or(false)
139 });
140 self.route_drag_and_drop();
141 if result {
142 self.mark_dirty();
143 }
144 log::trace!(
145 target: "cranpose::input",
146 "set_cursor ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
147 event_time.platform_time_ms,
148 event_time.animation_time_nanos,
149 );
150 result
151 }
152
153 fn set_cursor_inner(&mut self, x: f32, y: f32, event_time: PointerEventTime) -> bool {
154 self.surface_mut().cursor = (x, y);
155 if self.inspector_move(x, y) {
156 return true;
157 }
158
159 if self.surface().buttons_pressed != PointerButtons::NONE {
160 if self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
161 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
162 if !targets.is_empty() {
163 let event = self
164 .pointer_event(
165 PointerEventKind::Move,
166 Point { x, y },
167 Point { x, y },
168 event_time,
169 )
170 .with_buttons(self.surface().buttons_pressed)
171 .with_source(self.surface().pointer_source);
172 self.dispatch_targets(targets, event, false);
173 return true;
174 }
175
176 return false;
177 }
178
179 return false;
180 }
181
182 let hits = if self.inspector_blocks_pointer(x, y) {
183 Vec::new()
184 } else {
185 self.surface().renderer.scene().hit_test(x, y)
186 };
187 let new_ids: Vec<NodeId> = hits.iter().map(|h| h.node_id()).collect();
188
189 let pos = Point { x, y };
190 let previously_hovered = self.surface().hovered_nodes.clone();
191 for old_id in previously_hovered {
192 if !new_ids.contains(&old_id)
193 && let Some(target) = self.surface().renderer.scene().find_target(old_id)
194 {
195 let exit_event = self
196 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
197 .with_buttons(self.surface().buttons_pressed)
198 .with_source(self.surface().pointer_source);
199 self.dispatch_targets(std::iter::once(target), exit_event, false);
200 }
201 }
202
203 for hit in &hits {
204 if !self.surface().hovered_nodes.contains(&hit.node_id()) {
205 let enter_event = self
206 .pointer_event(PointerEventKind::Enter, pos, pos, event_time)
207 .with_buttons(self.surface().buttons_pressed)
208 .with_source(self.surface().pointer_source);
209 self.dispatch_targets(std::iter::once(hit.clone()), enter_event, false);
210 }
211 }
212
213 self.surface_mut().hovered_nodes = new_ids;
214 self.apply_hovered_pointer_icon(&hits);
215
216 if !hits.is_empty() {
217 let event = self
218 .pointer_event(PointerEventKind::Move, pos, pos, event_time)
219 .with_buttons(self.surface().buttons_pressed)
220 .with_source(self.surface().pointer_source);
221 self.dispatch_targets(hits, event, true);
222 true
223 } else {
224 false
225 }
226 }
227
228 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
234 let event_time = self.shell.app.realtime_pointer_event_time(None);
235 let _event_handler = enter_event_handler_scope();
236 let app_context = Rc::clone(&self.shell.app.app_context);
237 let result = app_context.enter(|| {
238 run_in_mutable_snapshot(|| self.activate_node(node_id, canvas_key, event_time))
239 .unwrap_or(false)
240 });
241 if result {
242 self.mark_dirty();
243 }
244 result
245 }
246
247 fn activate_node(
248 &mut self,
249 node_id: NodeId,
250 canvas_key: Option<u64>,
251 event_time: PointerEventTime,
252 ) -> bool {
253 let target = {
254 let (app, surface) = self.parts();
255 surface
256 .semantics_tree_for_input(app)
257 .and_then(|tree| activation_target(tree.root(), node_id, canvas_key))
258 };
259 let Some((target_id, canvas_bounds)) = target else {
260 return false;
261 };
262 let position = self
263 .with_layout_tree(|tree| activation_position(tree?.root(), target_id, canvas_bounds));
264 let Some(position) = position else {
265 return false;
266 };
267 let Some(target) = self.surface().renderer.scene().find_target(target_id) else {
268 return false;
269 };
270 for kind in [PointerEventKind::Down, PointerEventKind::Up] {
271 let event = self.pointer_event(kind, position, position, event_time);
272 self.dispatch_targets(std::iter::once(target.clone()), event, false);
273 }
274 true
275 }
276
277 pub fn pointer_pressed(&mut self) -> bool {
278 self.pointer_pressed_at_time(None)
279 }
280
281 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
284 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
285 self.pointer_pressed_at_event_time(event_time)
286 }
287
288 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
290 let (cursor_x, cursor_y) = self.surface().cursor;
291 if self.inspector_press(cursor_x, cursor_y) {
292 return true;
293 }
294 if self.dev_overlay_press(cursor_x, cursor_y) {
295 return true;
296 }
297 let _event_handler = enter_event_handler_scope();
298 let app_context = Rc::clone(&self.shell.app.app_context);
299 let result = app_context.enter(|| {
300 run_in_mutable_snapshot(|| self.pointer_pressed_inner(event_time)).unwrap_or(false)
301 });
302 if result {
303 self.mark_dirty();
304 }
305 log::trace!(
306 target: "cranpose::input",
307 "pointer_pressed time_ms={:?} animation_time_nanos={} -> {result}",
308 event_time.platform_time_ms,
309 event_time.animation_time_nanos,
310 );
311 result
312 }
313
314 fn pointer_pressed_inner(&mut self, event_time: PointerEventTime) -> bool {
315 self.activate();
316 self.note_focus_moved_by_keyboard(false);
317 self.surface_mut()
318 .buttons_pressed
319 .insert(PointerButton::Primary);
320
321 let (cursor_x, cursor_y) = self.surface().cursor;
322 let mut hits = self.surface().renderer.scene().hit_test(cursor_x, cursor_y);
323 if self.surface().pointer_source.is_touch_like()
324 && let Some(near) = self
325 .surface()
326 .renderer
327 .scene()
328 .hit_test_near(cursor_x, cursor_y)
329 && hits.iter().all(|hit| {
330 hit.node_id() != near.node_id() && near.capture_path().contains(&hit.node_id())
331 })
332 {
333 hits.insert(0, near);
334 }
335 if hits.is_empty() {
336 self.surface_mut()
337 .hit_path_tracker
338 .remove_path(PointerId::PRIMARY);
339 false
340 } else {
341 let event = self
342 .pointer_event(
343 PointerEventKind::Down,
344 Point {
345 x: self.surface().cursor.0,
346 y: self.surface().cursor.1,
347 },
348 Point {
349 x: self.surface().cursor.0,
350 y: self.surface().cursor.1,
351 },
352 event_time,
353 )
354 .with_buttons(self.surface().buttons_pressed)
355 .with_source(self.surface().pointer_source);
356
357 let mut delivered_capture_paths = Vec::new();
358 let (app, surface) = self.parts();
359 let mut applier = app.composition.applier_mut();
360 for hit in hits {
361 let node_id = hit.node_id();
362 delivered_capture_paths.push(hit.capture_path());
363 hit.dispatch_with_applier(&mut applier, event.clone());
364 log::trace!(
365 target: "cranpose::input",
366 "dispatch {:?} node={} consumed={} stop_on_consume=true",
367 event.kind,
368 node_id,
369 event.is_consumed(),
370 );
371 if event.is_consumed() {
372 break;
373 }
374 }
375
376 surface
377 .hit_path_tracker
378 .add_hit_path(PointerId::PRIMARY, delivered_capture_paths);
379 log::trace!(
380 target: "cranpose::input",
381 "pointer_pressed_inner cached_hit_path={:?}",
382 surface.hit_path_tracker.get_path(PointerId::PRIMARY),
383 );
384
385 true
386 }
387 }
388
389 pub fn pointer_released(&mut self) -> bool {
390 self.pointer_released_at_time(None)
391 }
392
393 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
406 self.pointer_released_at_position_time(x, y, None)
407 }
408
409 pub fn pointer_released_at_position_time(
413 &mut self,
414 x: f32,
415 y: f32,
416 time_ms: Option<i64>,
417 ) -> bool {
418 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
419 self.pointer_released_at_position_event_time(x, y, event_time)
420 }
421
422 pub fn pointer_released_at_position_event_time(
424 &mut self,
425 x: f32,
426 y: f32,
427 event_time: PointerEventTime,
428 ) -> bool {
429 let _event_handler = enter_event_handler_scope();
430 let app_context = Rc::clone(&self.shell.app.app_context);
431 let result = app_context.enter(|| {
432 run_in_mutable_snapshot(|| {
433 self.surface_mut().cursor = (x, y);
434 self.pointer_released_inner(event_time)
435 })
436 .unwrap_or(false)
437 });
438 self.route_drag_and_drop();
439 if result {
440 self.mark_dirty();
441 }
442 log::trace!(
443 target: "cranpose::input",
444 "pointer_released_at_position ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
445 event_time.platform_time_ms,
446 event_time.animation_time_nanos,
447 );
448 result
449 }
450
451 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
454 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
455 self.pointer_released_at_event_time(event_time)
456 }
457
458 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
460 let _event_handler = enter_event_handler_scope();
461 let app_context = Rc::clone(&self.shell.app.app_context);
462 let result = app_context.enter(|| {
463 run_in_mutable_snapshot(|| self.pointer_released_inner(event_time)).unwrap_or(false)
464 });
465 self.route_drag_and_drop();
466 if result {
467 self.mark_dirty();
468 }
469 log::trace!(
470 target: "cranpose::input",
471 "pointer_released time_ms={:?} animation_time_nanos={} -> {result}",
472 event_time.platform_time_ms,
473 event_time.animation_time_nanos,
474 );
475 result
476 }
477
478 fn pointer_released_inner(&mut self, event_time: PointerEventTime) -> bool {
479 if self.surface_mut().inspector.release_pointer() {
480 return true;
481 }
482 self.release_app_pointer(event_time)
483 }
484
485 fn release_app_pointer(&mut self, event_time: PointerEventTime) -> bool {
486 self.surface_mut()
487 .buttons_pressed
488 .remove(PointerButton::Primary);
489 let corrected_buttons = self.surface().buttons_pressed;
490 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
491
492 self.surface_mut()
493 .hit_path_tracker
494 .remove_path(PointerId::PRIMARY);
495
496 if !targets.is_empty() {
497 let event = self
498 .pointer_event(
499 PointerEventKind::Up,
500 Point {
501 x: self.surface().cursor.0,
502 y: self.surface().cursor.1,
503 },
504 Point {
505 x: self.surface().cursor.0,
506 y: self.surface().cursor.1,
507 },
508 event_time,
509 )
510 .with_buttons(corrected_buttons)
511 .with_source(self.surface().pointer_source);
512
513 self.dispatch_targets(targets, event, false);
514 true
515 } else {
516 false
517 }
518 }
519
520 pub fn secondary_pointer_pressed(
529 &mut self,
530 pointer_id: u64,
531 x: f32,
532 y: f32,
533 time_ms: Option<i64>,
534 ) -> bool {
535 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
536 self.dispatch_secondary_pointer(PointerEventKind::Down, pointer_id, x, y, event_time)
537 }
538
539 pub fn secondary_pointer_moved(
541 &mut self,
542 pointer_id: u64,
543 x: f32,
544 y: f32,
545 time_ms: Option<i64>,
546 ) -> bool {
547 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
548 self.dispatch_secondary_pointer(PointerEventKind::Move, pointer_id, x, y, event_time)
549 }
550
551 pub fn secondary_pointer_released(
553 &mut self,
554 pointer_id: u64,
555 x: f32,
556 y: f32,
557 time_ms: Option<i64>,
558 ) -> bool {
559 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
560 self.dispatch_secondary_pointer(PointerEventKind::Up, pointer_id, x, y, event_time)
561 }
562
563 fn dispatch_secondary_pointer(
564 &mut self,
565 kind: PointerEventKind,
566 pointer_id: u64,
567 x: f32,
568 y: f32,
569 event_time: PointerEventTime,
570 ) -> bool {
571 if pointer_id == 0 {
572 log::warn!(
573 target: "cranpose::input",
574 "secondary pointer dispatch called with the primary pointer id"
575 );
576 return false;
577 }
578
579 let _event_handler = enter_event_handler_scope();
580 let app_context = Rc::clone(&self.shell.app.app_context);
581 let result = app_context.enter(|| {
582 run_in_mutable_snapshot(|| {
583 if !self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
584 return false;
585 }
586 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
587 if targets.is_empty() {
588 return false;
589 }
590 let pos = Point { x, y };
591 let event = self
592 .pointer_event(kind, pos, pos, event_time)
593 .with_buttons(self.surface().buttons_pressed)
594 .with_id(pointer_id)
595 .with_source(self.surface().pointer_source);
596 self.dispatch_targets(targets, event, false);
597 true
598 })
599 .unwrap_or(false)
600 });
601 if result {
602 self.mark_dirty();
603 }
604 log::trace!(
605 target: "cranpose::input",
606 "secondary_pointer {kind:?} id={pointer_id} ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
607 event_time.platform_time_ms,
608 event_time.animation_time_nanos,
609 );
610 result
611 }
612
613 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
619 let event_time = self.shell.app.realtime_pointer_event_time(None);
620 let _event_handler = enter_event_handler_scope();
621 let app_context = Rc::clone(&self.shell.app.app_context);
622 let result = app_context.enter(|| {
623 run_in_mutable_snapshot(|| self.pointer_zoomed_inner(zoom_factor, event_time))
624 .unwrap_or(false)
625 });
626 if result {
627 self.mark_dirty();
628 }
629 log::trace!(
630 target: "cranpose::input",
631 "pointer_zoomed factor={zoom_factor:.4} -> {result}"
632 );
633 result
634 }
635
636 fn pointer_zoomed_inner(&mut self, zoom_factor: f32, event_time: PointerEventTime) -> bool {
637 if !zoom_factor.is_finite() || zoom_factor <= 0.0 || zoom_factor == 1.0 {
638 return false;
639 }
640
641 let hits = self
642 .surface()
643 .renderer
644 .scene()
645 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
646 if hits.is_empty() {
647 return false;
648 }
649
650 let pos = Point {
651 x: self.surface().cursor.0,
652 y: self.surface().cursor.1,
653 };
654 let event = self
655 .pointer_event(PointerEventKind::Zoom, pos, pos, event_time)
656 .with_buttons(self.surface().buttons_pressed)
657 .with_zoom_delta(zoom_factor)
658 .with_source(self.surface().pointer_source);
659
660 self.dispatch_to_hits(&hits, event)
661 }
662
663 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
688 if self.inspector_scroll(wheel.delta.y) {
689 return true;
690 }
691 if wheel.is_zoom() {
692 let zoom_factor = wheel.zoom_factor();
693 log::trace!(
694 target: "cranpose::input",
695 "wheel zoom factor={zoom_factor:.4}"
696 );
697 return self.pointer_zoomed(zoom_factor);
698 }
699
700 let rotary =
701 RotaryScrollEvent::from_wheel_pixels(wheel.delta.y, wheel.delta.x, wheel.uptime_millis);
702 if self.rotary_scrolled(rotary) {
703 return true;
704 }
705
706 let delta = wheel.scroll_delta();
707 log::trace!(
708 target: "cranpose::input",
709 "wheel delta ({:.2},{:.2}) alt={}",
710 delta.x,
711 delta.y,
712 wheel.modifiers.alt
713 );
714 self.pointer_scrolled(delta.x, delta.y)
715 }
716
717 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
725 if self.inspector_scroll(delta_y) {
726 return true;
727 }
728 let event_time = self.shell.app.realtime_pointer_event_time(None);
729 let _event_handler = enter_event_handler_scope();
730 let app_context = Rc::clone(&self.shell.app.app_context);
731 let result = app_context.enter(|| {
732 run_in_mutable_snapshot(|| self.pointer_scrolled_inner(delta_x, delta_y, event_time))
733 .unwrap_or(false)
734 });
735 if result {
736 self.mark_dirty();
737 }
738 log::trace!(
739 target: "cranpose::input",
740 "pointer_scrolled ({delta_x:.2},{delta_y:.2}) -> {result}"
741 );
742 result
743 }
744
745 fn pointer_scrolled_inner(
746 &mut self,
747 delta_x: f32,
748 delta_y: f32,
749 event_time: PointerEventTime,
750 ) -> bool {
751 if delta_x.abs() <= f32::EPSILON && delta_y.abs() <= f32::EPSILON {
752 return false;
753 }
754
755 let hits = self
756 .surface()
757 .renderer
758 .scene()
759 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
760 if hits.is_empty() {
761 return false;
762 }
763
764 let event = self
765 .pointer_event(
766 PointerEventKind::Scroll,
767 Point {
768 x: self.surface().cursor.0,
769 y: self.surface().cursor.1,
770 },
771 Point {
772 x: self.surface().cursor.0,
773 y: self.surface().cursor.1,
774 },
775 event_time,
776 )
777 .with_buttons(self.surface().buttons_pressed)
778 .with_scroll_delta(Point {
779 x: delta_x,
780 y: delta_y,
781 })
782 .with_source(self.surface().pointer_source);
783
784 self.dispatch_to_hits(&hits, event)
785 }
786
787 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
798 where
799 F: Fn(RotaryScrollEvent) -> bool + 'static,
800 {
801 self.surface_mut().on_rotary_scroll = Some(Rc::new(handler));
802 }
803
804 pub fn clear_on_rotary_scroll(&mut self) {
806 self.surface_mut().on_rotary_scroll = None;
807 }
808
809 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
815 let factor = self.shell.app.rotary_scroll_factor;
816 self.rotary_scrolled(RotaryScrollEvent::from_detents(
817 detents,
818 factor,
819 factor,
820 uptime_millis,
821 ))
822 }
823
824 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
845 let _event_handler = enter_event_handler_scope();
846 let app_context = Rc::clone(&self.shell.app.app_context);
847 let result = app_context.enter(|| {
848 run_in_mutable_snapshot(|| self.rotary_scrolled_inner(event)).unwrap_or(false)
849 });
850 if result {
851 self.mark_dirty();
852 }
853 log::trace!(
854 target: "cranpose::input",
855 "rotary_scrolled v={:.2} h={:.2} uptime={} -> {result}",
856 event.vertical_scroll_pixels,
857 event.horizontal_scroll_pixels,
858 event.uptime_millis,
859 );
860 result
861 }
862
863 fn rotary_scrolled_inner(&mut self, rotary: RotaryScrollEvent) -> bool {
864 if rotary.is_empty() {
865 return false;
866 }
867
868 let bubble_order = self.rotary_dispatch_order();
869 let position = Point {
870 x: self.surface().cursor.0,
871 y: self.surface().cursor.1,
872 };
873
874 if !bubble_order.is_empty() {
875 let capture_targets = bubble_order
876 .iter()
877 .rev()
878 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
879 .collect::<Vec<_>>();
880 let mut capture_event =
881 PointerEvent::rotary(PointerEventKind::RotaryScrollPre, rotary, position);
882 capture_event.modifiers = self.shell.app.modifiers;
883 self.dispatch_targets(capture_targets, capture_event.clone(), true);
884 if capture_event.is_consumed() {
885 return true;
886 }
887
888 let bubble_targets = bubble_order
889 .iter()
890 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
891 .collect::<Vec<_>>();
892 let mut bubble_event =
893 PointerEvent::rotary(PointerEventKind::RotaryScroll, rotary, position);
894 bubble_event.modifiers = self.shell.app.modifiers;
895 self.dispatch_targets(bubble_targets, bubble_event.clone(), true);
896 if bubble_event.is_consumed() {
897 return true;
898 }
899 }
900
901 if let Some(handler) = self.surface().on_rotary_scroll.clone() {
902 return handler(rotary);
903 }
904
905 false
906 }
907
908 fn rotary_dispatch_order(&self) -> Vec<NodeId> {
909 if let Some(focused) = cranpose_ui::active_focus_target()
910 && let Some(target) = self.surface().renderer.scene().find_target(focused)
911 {
912 let path = target.capture_path();
913 if !path.is_empty() {
914 return crate::hit_path_tracker::dispatch_order_for_paths(&[path]);
915 }
916 }
917
918 let hits = self
919 .surface()
920 .renderer
921 .scene()
922 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
923 if hits.is_empty() {
924 return Vec::new();
925 }
926 let capture_paths = hits
927 .iter()
928 .map(|hit| hit.capture_path())
929 .collect::<Vec<_>>();
930 crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
931 }
932
933 pub fn cancel_gesture(&mut self) {
939 let event_time = self.shell.app.realtime_pointer_event_time(None);
940 let _event_handler = enter_event_handler_scope();
941 let app_context = Rc::clone(&self.shell.app.app_context);
942 let _ = app_context.enter(|| {
943 run_in_mutable_snapshot(|| {
944 self.cancel_gesture_inner(event_time);
945 })
946 });
947 self.route_drag_and_drop();
948 }
949
950 fn route_drag_and_drop(&mut self) {
951 let app_context = Rc::clone(&self.shell.app.app_context);
952 let source = self.index;
953 let shell = &mut *self.shell;
954 let routed = app_context.enter(|| {
955 run_in_mutable_snapshot(|| {
956 app_context
957 .drag_and_drop()
958 .route(|point| shell.drag_and_drop_target_at(point, source))
959 })
960 .unwrap_or(false)
961 });
962 if routed {
963 shell.mark_all_dirty();
964 }
965 }
966
967 pub fn cancel_gesture_unless_pressed(&mut self) {
976 if self.surface().buttons_pressed != PointerButtons::NONE
977 || self.surface().inspector.pointer_captured
978 {
979 return;
980 }
981 self.cancel_gesture();
982 }
983
984 fn cancel_gesture_inner(&mut self, event_time: PointerEventTime) {
985 self.surface_mut().inspector.cancel_pointer();
986 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
987
988 self.surface_mut().hit_path_tracker.clear();
989 self.surface_mut().buttons_pressed = PointerButtons::NONE;
990
991 if !targets.is_empty() {
992 let event = self
993 .pointer_event(
994 PointerEventKind::Cancel,
995 Point {
996 x: self.surface().cursor.0,
997 y: self.surface().cursor.1,
998 },
999 Point {
1000 x: self.surface().cursor.0,
1001 y: self.surface().cursor.1,
1002 },
1003 event_time,
1004 )
1005 .with_source(self.surface().pointer_source);
1006
1007 self.dispatch_targets(targets, event, false);
1008 }
1009
1010 let pos = Point {
1011 x: self.surface().cursor.0,
1012 y: self.surface().cursor.1,
1013 };
1014 let hovered_nodes = self.surface().hovered_nodes.clone();
1015 for node_id in hovered_nodes {
1016 if let Some(target) = self.surface().renderer.scene().find_target(node_id) {
1017 let exit_event = self
1018 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
1019 .with_source(self.surface().pointer_source);
1020 self.dispatch_targets(std::iter::once(target), exit_event, false);
1021 }
1022 }
1023 self.surface_mut().hovered_nodes.clear();
1024 self.surface().pointer_icon.set(PointerIcon::DEFAULT);
1025 }
1026
1027 fn apply_hovered_pointer_icon(
1028 &self,
1029 hits: &[<<R as Renderer>::Scene as RenderScene>::HitTarget],
1030 ) {
1031 let icon = hits
1032 .iter()
1033 .find_map(HitTestTarget::pointer_icon)
1034 .unwrap_or(PointerIcon::DEFAULT);
1035 self.surface().pointer_icon.set(icon);
1036 }
1037
1038 fn on_focus_key(&mut self, event: &KeyEvent) -> bool {
1039 if !plain_key_down(event) || event.key_code != KeyCode::Tab {
1040 return false;
1041 }
1042 self.on_group_navigation_key(event)
1043 }
1044
1045 fn on_group_navigation_key(&mut self, event: &KeyEvent) -> bool {
1046 let focused = cranpose_ui::active_focus_target();
1047 let target = self.with_layout_tree(|tree| {
1048 cranpose_ui::keyboard_focus_target(
1049 tree?,
1050 focused,
1051 event.key_code,
1052 event.modifiers.shift,
1053 )
1054 });
1055 let Some(target) = target else {
1056 return false;
1057 };
1058 let moved =
1059 run_in_mutable_snapshot(|| cranpose_ui::request_focus_from_platform(target.node_id))
1060 .unwrap_or(false);
1061 if moved {
1062 self.mark_dirty();
1063 self.note_focus_moved_by_keyboard(true);
1064 if target.activate {
1065 self.on_activation_key(&KeyEvent::key_down(KeyCode::Space, " "));
1066 }
1067 }
1068 moved
1069 }
1070
1071 fn on_activation_key(&mut self, event: &KeyEvent) -> bool {
1072 if !plain_key_down(event)
1073 || !matches!(event.key_code, KeyCode::Enter | KeyCode::Space)
1074 || cranpose_ui::text_field_focus::has_focused_field()
1075 {
1076 return false;
1077 }
1078 let Some(focused) = cranpose_ui::active_focus_target() else {
1079 return false;
1080 };
1081 let activated = self.accessibility_activate(focused, None);
1082 self.note_focus_moved_by_keyboard(true);
1083 activated
1084 }
1085
1086 fn on_arrow_key(&mut self, event: &KeyEvent) -> bool {
1087 if !plain_key_down(event) || cranpose_ui::text_field_focus::has_focused_field() {
1088 return false;
1089 }
1090 if self.on_group_navigation_key(event) {
1091 return true;
1092 }
1093 let direction = match event.key_code {
1094 KeyCode::ArrowLeft => FocusDirection::Left,
1095 KeyCode::ArrowRight => FocusDirection::Right,
1096 KeyCode::ArrowUp => FocusDirection::Up,
1097 KeyCode::ArrowDown => FocusDirection::Down,
1098 _ => return false,
1099 };
1100 let Some(focused) = cranpose_ui::active_focus_target() else {
1101 return false;
1102 };
1103 let order = self.with_layout_tree(|layout_tree| {
1104 let layout_tree = layout_tree?;
1105 let group = cranpose_ui::selectable_group_of(layout_tree, focused)?;
1106 Some(cranpose_ui::collect_focus_order_under(layout_tree, group))
1107 });
1108 order.is_some_and(|order| self.move_focus_in_order(order, direction))
1109 }
1110
1111 fn note_focus_moved_by_keyboard(&mut self, keyboard: bool) {
1112 if cranpose_ui::set_keyboard_focus_visible(keyboard) {
1113 cranpose_ui::request_render_invalidation();
1114 self.mark_dirty();
1115 }
1116 }
1117
1118 fn on_escape_key(&mut self, event: &KeyEvent) -> bool {
1119 if event.event_type != KeyEventType::KeyDown || event.key_code != KeyCode::Escape {
1120 return false;
1121 }
1122 self.dismiss_top_modal_in_context()
1123 }
1124
1125 pub fn dismiss_top_modal(&mut self) -> bool {
1128 let _event_handler = enter_event_handler_scope();
1129 let app_context = Rc::clone(&self.shell.app.app_context);
1130 app_context.enter(|| self.dismiss_top_modal_in_context())
1131 }
1132
1133 fn dismiss_top_modal_in_context(&mut self) -> bool {
1134 let closed = run_in_mutable_snapshot(|| {
1135 if cranpose_ui::modal_depth() > 0 {
1136 cranpose_ui::dispatch_modal_back()
1137 } else {
1138 cranpose_ui::dismiss_top_popup()
1139 }
1140 })
1141 .unwrap_or(false);
1142 if closed {
1143 self.mark_dirty();
1144 }
1145 closed
1146 }
1147
1148 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1151 let order = self.with_layout_tree(|layout_tree| {
1152 layout_tree
1153 .map(cranpose_ui::collect_focus_order)
1154 .unwrap_or_default()
1155 });
1156 self.move_focus_in_order(order, direction)
1157 }
1158
1159 fn move_focus_in_order(
1160 &mut self,
1161 order: Vec<cranpose_ui::FocusEntry>,
1162 direction: FocusDirection,
1163 ) -> bool {
1164 cranpose_ui::set_focus_order(order);
1165 let moved = run_in_mutable_snapshot(|| cranpose_ui::FocusManager.move_focus(direction))
1166 .unwrap_or(false);
1167 if moved {
1168 self.mark_dirty();
1169 self.note_focus_moved_by_keyboard(true);
1170 }
1171 moved
1172 }
1173
1174 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1175 let _event_handler = enter_event_handler_scope();
1176 let app_context = Rc::clone(&self.shell.app.app_context);
1177 app_context.enter(|| self.on_key_event_inner(event))
1178 }
1179
1180 fn on_key_event_inner(&mut self, event: &KeyEvent) -> bool {
1181 if self.inspector_key(event) {
1182 return true;
1183 }
1184 use KeyEventType::KeyDown;
1185
1186 if event.event_type == KeyDown && event.modifiers.command_or_ctrl() {
1187 #[cfg(all(
1188 feature = "clipboard-native",
1189 not(target_arch = "wasm32"),
1190 not(target_os = "android"),
1191 not(target_os = "ios")
1192 ))]
1193 {
1194 match event.key_code {
1195 KeyCode::C => {
1196 if let Some(text) = self.on_copy_inner() {
1197 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1198 return true;
1199 }
1200 }
1201 KeyCode::X => {
1202 if let Some(text) = self.on_cut_inner() {
1203 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1204 self.mark_dirty();
1205 self.shell.app.request_layout_pass();
1206 return true;
1207 }
1208 }
1209 KeyCode::V => {
1210 if let Some(text) = cranpose_ui::clipboard_session::clipboard_read_text()
1211 && self.on_paste_inner(&text)
1212 {
1213 return true;
1214 }
1215 }
1216 _ => {}
1217 }
1218 }
1219 }
1220
1221 if self.on_focus_key(event) {
1222 return true;
1223 }
1224
1225 if self.on_escape_key(event) {
1226 return true;
1227 }
1228
1229 if self.on_activation_key(event) || self.on_arrow_key(event) {
1230 return true;
1231 }
1232
1233 if !cranpose_ui::text_field_focus::has_focused_field() {
1234 return false;
1235 }
1236
1237 let handled =
1238 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_key_event(event))
1239 .unwrap_or(false);
1240
1241 if handled {
1242 self.mark_dirty();
1243 self.shell.app.request_layout_pass();
1244 }
1245
1246 handled
1247 }
1248
1249 pub fn on_paste(&mut self, text: &str) -> bool {
1253 if self.inspector_owns_keyboard() {
1254 return true;
1255 }
1256 let _event_handler = enter_event_handler_scope();
1257 let app_context = Rc::clone(&self.shell.app.app_context);
1258 app_context.enter(|| self.on_paste_inner(text))
1259 }
1260
1261 fn on_paste_inner(&mut self, text: &str) -> bool {
1262 let handled =
1263 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1264 .unwrap_or(false);
1265
1266 if handled {
1267 self.mark_dirty();
1268 self.shell.app.request_layout_pass();
1269 }
1270
1271 handled
1272 }
1273
1274 pub fn on_copy(&mut self) -> Option<String> {
1278 let app_context = Rc::clone(&self.shell.app.app_context);
1279 if self.inspector_owns_keyboard() {
1280 return None;
1281 }
1282 app_context.enter(|| self.on_copy_inner())
1283 }
1284
1285 fn on_copy_inner(&mut self) -> Option<String> {
1286 cranpose_ui::text_field_focus::dispatch_copy()
1287 }
1288
1289 pub fn on_cut(&mut self) -> Option<String> {
1293 let _event_handler = enter_event_handler_scope();
1294 if self.inspector_owns_keyboard() {
1295 return None;
1296 }
1297 let app_context = Rc::clone(&self.shell.app.app_context);
1298 app_context.enter(|| self.on_cut_inner())
1299 }
1300
1301 fn on_cut_inner(&mut self) -> Option<String> {
1302 let text =
1303 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1304
1305 if text.is_some() {
1306 self.mark_dirty();
1307 self.shell.app.request_layout_pass();
1308 }
1309
1310 text
1311 }
1312
1313 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1321 if self.inspector_owns_keyboard() {
1322 return true;
1323 }
1324 let _event_handler = enter_event_handler_scope();
1325 let app_context = Rc::clone(&self.shell.app.app_context);
1326 app_context.enter(|| self.on_ime_preedit_inner(text, cursor))
1327 }
1328
1329 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1330 let handled = run_in_mutable_snapshot(|| {
1331 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1332 })
1333 .unwrap_or(false);
1334
1335 if handled {
1336 self.mark_dirty();
1337 self.shell.app.request_layout_pass();
1338 }
1339
1340 handled
1341 }
1342
1343 pub fn on_ime_finish_composing(&mut self) -> bool {
1347 if self.inspector_owns_keyboard() {
1348 return true;
1349 }
1350 let _event_handler = enter_event_handler_scope();
1351 let app_context = Rc::clone(&self.shell.app.app_context);
1352 app_context.enter(|| self.on_ime_finish_composing_inner())
1353 }
1354
1355 fn on_ime_finish_composing_inner(&mut self) -> bool {
1356 let handled =
1357 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1358 .unwrap_or(false);
1359
1360 if handled {
1361 self.mark_dirty();
1362 self.shell.app.request_layout_pass();
1363 }
1364
1365 handled
1366 }
1367
1368 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1372 if self.inspector_owns_keyboard() {
1373 return true;
1374 }
1375 let _event_handler = enter_event_handler_scope();
1376 let app_context = Rc::clone(&self.shell.app.app_context);
1377 app_context.enter(|| {
1378 let handled = run_in_mutable_snapshot(|| {
1379 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(
1380 start_bytes,
1381 end_bytes,
1382 )
1383 })
1384 .unwrap_or(false);
1385
1386 if handled {
1387 self.mark_dirty();
1388 self.shell.app.request_layout_pass();
1389 }
1390
1391 handled
1392 })
1393 }
1394
1395 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1400 if self.inspector_owns_keyboard() {
1401 return true;
1402 }
1403 let _event_handler = enter_event_handler_scope();
1404 let app_context = Rc::clone(&self.shell.app.app_context);
1405 app_context.enter(|| {
1406 let handled = run_in_mutable_snapshot(|| {
1407 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1408 })
1409 .unwrap_or(false);
1410
1411 if handled {
1412 self.mark_dirty();
1413 }
1414
1415 handled
1416 })
1417 }
1418
1419 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1423 let app_context = Rc::clone(&self.shell.app.app_context);
1424 app_context.enter(cranpose_ui::text_field_focus::focused_editor_state)
1425 }
1426
1427 pub fn ime_caret_geometry(
1431 &mut self,
1432 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1433 let app_context = Rc::clone(&self.shell.app.app_context);
1434 app_context.enter(cranpose_ui::text_field_focus::focused_caret_geometry)
1435 }
1436
1437 pub fn clear_text_field_focus(&mut self) {
1440 let _event_handler = enter_event_handler_scope();
1441 let app_context = Rc::clone(&self.shell.app.app_context);
1442 app_context.enter(cranpose_ui::text_field_focus::clear_focus);
1443 self.mark_dirty();
1444 self.shell.app.request_layout_pass();
1445 }
1446
1447 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1450 if self.inspector_owns_keyboard() {
1451 return true;
1452 }
1453 let _event_handler = enter_event_handler_scope();
1454 let app_context = Rc::clone(&self.shell.app.app_context);
1455 app_context.enter(|| self.on_ime_delete_surrounding_inner(before_bytes, after_bytes))
1456 }
1457
1458 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1459 let handled = run_in_mutable_snapshot(|| {
1460 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1461 })
1462 .unwrap_or(false);
1463
1464 if handled {
1465 self.mark_dirty();
1466 self.shell.app.request_layout_pass();
1467 }
1468
1469 handled
1470 }
1471}
1472
1473impl<R> AppShell<R>
1474where
1475 R: Renderer,
1476 R::Error: Debug,
1477{
1478 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
1487 self.app.modifiers = Some(modifiers);
1488 }
1489
1490 pub fn modifiers(&self) -> Option<Modifiers> {
1494 self.app.modifiers
1495 }
1496
1497 pub fn rotary_scroll_factor(&self) -> f32 {
1500 self.app.rotary_scroll_factor
1501 }
1502
1503 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
1512 if factor.is_finite() && factor > 0.0 {
1513 self.app.rotary_scroll_factor = factor;
1514 }
1515 }
1516
1517 pub fn notify_app_paused(&mut self) {
1524 let app_context = Rc::clone(&self.app.app_context);
1525 app_context.enter(cranpose_ui::text_input_session::notify_app_paused);
1526 }
1527
1528 pub fn notify_app_resumed(&mut self) -> bool {
1536 let app_context = Rc::clone(&self.app.app_context);
1537 app_context.enter(cranpose_ui::text_input_session::notify_app_resumed)
1538 }
1539
1540 #[cfg(all(
1541 feature = "clipboard-native",
1542 target_os = "linux",
1543 not(target_arch = "wasm32")
1544 ))]
1545 pub fn set_primary_selection(&mut self, text: &str) {
1546 use arboard::{LinuxClipboardKind, SetExtLinux};
1547 if let Some(ref mut clipboard) = self.app.clipboard {
1548 let result = clipboard
1549 .set()
1550 .clipboard(LinuxClipboardKind::Primary)
1551 .text(text.to_string());
1552 if let Err(e) = result {
1553 log::debug!("Primary selection set failed: {:?}", e);
1554 }
1555 }
1556 }
1557
1558 #[cfg(not(all(
1559 feature = "clipboard-native",
1560 target_os = "linux",
1561 not(target_arch = "wasm32")
1562 )))]
1563 pub fn set_primary_selection(&mut self, _text: &str) {}
1564
1565 #[cfg(all(
1566 feature = "clipboard-native",
1567 target_os = "linux",
1568 not(target_arch = "wasm32")
1569 ))]
1570 pub fn get_primary_selection(&mut self) -> Option<String> {
1571 use arboard::{GetExtLinux, LinuxClipboardKind};
1572 if let Some(ref mut clipboard) = self.app.clipboard {
1573 clipboard
1574 .get()
1575 .clipboard(LinuxClipboardKind::Primary)
1576 .text()
1577 .ok()
1578 } else {
1579 None
1580 }
1581 }
1582
1583 #[cfg(not(all(
1584 feature = "clipboard-native",
1585 target_os = "linux",
1586 not(target_arch = "wasm32")
1587 )))]
1588 pub fn get_primary_selection(&mut self) -> Option<String> {
1589 None
1590 }
1591
1592 pub fn sync_selection_to_primary(&mut self) {
1595 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1596 {
1597 if let Some(text) = self.on_copy() {
1598 self.set_primary_selection(&text);
1599 }
1600 }
1601 }
1602
1603 pub fn set_pointer_source(&mut self, source: PointerSource) {
1605 self.primary().set_pointer_source(source);
1606 }
1607
1608 pub fn pointer_source(&self) -> PointerSource {
1610 self.surfaces[0].pointer_source
1611 }
1612
1613 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
1615 self.primary().set_cursor(x, y)
1616 }
1617
1618 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
1620 self.primary().set_cursor_at_time(x, y, time_ms)
1621 }
1622
1623 pub fn set_cursor_at_event_time(
1625 &mut self,
1626 x: f32,
1627 y: f32,
1628 event_time: PointerEventTime,
1629 ) -> bool {
1630 self.primary().set_cursor_at_event_time(x, y, event_time)
1631 }
1632
1633 pub fn pointer_pressed(&mut self) -> bool {
1635 self.primary().pointer_pressed()
1636 }
1637
1638 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
1640 self.primary().accessibility_activate(node_id, canvas_key)
1641 }
1642
1643 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
1645 self.primary().pointer_pressed_at_time(time_ms)
1646 }
1647
1648 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1650 self.primary().pointer_pressed_at_event_time(event_time)
1651 }
1652
1653 pub fn pointer_released(&mut self) -> bool {
1655 self.primary().pointer_released()
1656 }
1657
1658 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
1660 self.primary().pointer_released_at_position(x, y)
1661 }
1662
1663 pub fn pointer_released_at_position_time(
1666 &mut self,
1667 x: f32,
1668 y: f32,
1669 time_ms: Option<i64>,
1670 ) -> bool {
1671 self.primary()
1672 .pointer_released_at_position_time(x, y, time_ms)
1673 }
1674
1675 pub fn pointer_released_at_position_event_time(
1678 &mut self,
1679 x: f32,
1680 y: f32,
1681 event_time: PointerEventTime,
1682 ) -> bool {
1683 self.primary()
1684 .pointer_released_at_position_event_time(x, y, event_time)
1685 }
1686
1687 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
1689 self.primary().pointer_released_at_time(time_ms)
1690 }
1691
1692 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1694 self.primary().pointer_released_at_event_time(event_time)
1695 }
1696
1697 pub fn secondary_pointer_pressed(
1699 &mut self,
1700 pointer_id: u64,
1701 x: f32,
1702 y: f32,
1703 time_ms: Option<i64>,
1704 ) -> bool {
1705 self.primary()
1706 .secondary_pointer_pressed(pointer_id, x, y, time_ms)
1707 }
1708
1709 pub fn secondary_pointer_moved(
1711 &mut self,
1712 pointer_id: u64,
1713 x: f32,
1714 y: f32,
1715 time_ms: Option<i64>,
1716 ) -> bool {
1717 self.primary()
1718 .secondary_pointer_moved(pointer_id, x, y, time_ms)
1719 }
1720
1721 pub fn secondary_pointer_released(
1723 &mut self,
1724 pointer_id: u64,
1725 x: f32,
1726 y: f32,
1727 time_ms: Option<i64>,
1728 ) -> bool {
1729 self.primary()
1730 .secondary_pointer_released(pointer_id, x, y, time_ms)
1731 }
1732
1733 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
1735 self.primary().pointer_zoomed(zoom_factor)
1736 }
1737
1738 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
1740 self.primary().wheel_scrolled(wheel)
1741 }
1742
1743 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
1745 self.primary().pointer_scrolled(delta_x, delta_y)
1746 }
1747
1748 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
1750 where
1751 F: Fn(RotaryScrollEvent) -> bool + 'static,
1752 {
1753 self.primary().set_on_rotary_scroll(handler);
1754 }
1755
1756 pub fn clear_on_rotary_scroll(&mut self) {
1758 self.primary().clear_on_rotary_scroll();
1759 }
1760
1761 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
1763 self.primary()
1764 .rotary_scrolled_by_detents(detents, uptime_millis)
1765 }
1766
1767 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
1769 self.primary().rotary_scrolled(event)
1770 }
1771
1772 pub fn cancel_gesture(&mut self) {
1774 self.primary().cancel_gesture();
1775 }
1776
1777 pub fn cancel_gesture_unless_pressed(&mut self) {
1779 self.primary().cancel_gesture_unless_pressed();
1780 }
1781
1782 pub fn refresh_pointer_icon(&self) {
1786 self.surfaces[0].pointer_icon.refresh();
1787 }
1788
1789 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
1796 self.surfaces[0].pointer_icon.take_change()
1797 }
1798
1799 pub fn set_platform_text_input(
1806 &mut self,
1807 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
1808 ) {
1809 self.primary().set_platform_text_input(handler);
1810 }
1811
1812 pub fn dismiss_top_modal(&mut self) -> bool {
1814 self.primary().dismiss_top_modal()
1815 }
1816
1817 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1819 self.primary().move_focus_in_context(direction)
1820 }
1821
1822 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1824 self.primary().on_key_event(event)
1825 }
1826
1827 pub fn on_paste(&mut self, text: &str) -> bool {
1829 self.primary().on_paste(text)
1830 }
1831
1832 pub fn on_copy(&mut self) -> Option<String> {
1834 self.primary().on_copy()
1835 }
1836
1837 pub fn on_cut(&mut self) -> Option<String> {
1839 self.primary().on_cut()
1840 }
1841
1842 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1844 self.primary().on_ime_preedit(text, cursor)
1845 }
1846
1847 pub fn on_ime_finish_composing(&mut self) -> bool {
1849 self.primary().on_ime_finish_composing()
1850 }
1851
1852 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1854 self.primary()
1855 .on_ime_set_composing_region(start_bytes, end_bytes)
1856 }
1857
1858 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1860 self.primary().on_ime_set_selection(start_bytes, end_bytes)
1861 }
1862
1863 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1865 self.primary().ime_editor_state()
1866 }
1867
1868 pub fn ime_caret_geometry(
1870 &mut self,
1871 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1872 self.primary().ime_caret_geometry()
1873 }
1874
1875 pub fn clear_text_field_focus(&mut self) {
1877 self.primary().clear_text_field_focus();
1878 }
1879
1880 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1882 self.primary()
1883 .on_ime_delete_surrounding(before_bytes, after_bytes)
1884 }
1885}
1886
1887fn activation_target(
1888 node: &cranpose_ui::SemanticsNode,
1889 node_id: NodeId,
1890 canvas_key: Option<u64>,
1891) -> Option<(NodeId, Option<Rect>)> {
1892 if node.hidden {
1893 return None;
1894 }
1895 if node.node_id != node_id {
1896 return node
1897 .children
1898 .iter()
1899 .find_map(|child| activation_target(child, node_id, canvas_key));
1900 }
1901 if !node.enabled {
1902 return None;
1903 }
1904 if let Some(key) = canvas_key {
1905 let child = node.canvas_children.iter().find(|child| child.key == key)?;
1906 return (child.enabled && child.clickable).then_some((node_id, Some(child.bounds)));
1907 }
1908 node.actions.first().map(|action| match action {
1909 cranpose_ui::SemanticsAction::Click { handler } => (handler.node_id(), None),
1910 })
1911}
1912
1913fn activation_position(
1914 root: &LayoutBox,
1915 node_id: NodeId,
1916 canvas_bounds: Option<Rect>,
1917) -> Option<Point> {
1918 if root.node_id != node_id {
1919 return root
1920 .children
1921 .iter()
1922 .find_map(|child| activation_position(child, node_id, canvas_bounds));
1923 }
1924 let bounds = canvas_bounds.map_or(root.rect, |bounds| Rect {
1925 x: root.rect.x + bounds.x,
1926 y: root.rect.y + bounds.y,
1927 ..bounds
1928 });
1929 let position = Point {
1930 x: bounds.x + bounds.width * 0.5,
1931 y: bounds.y + bounds.height * 0.5,
1932 };
1933 (bounds.width > 0.0 && bounds.height > 0.0 && position.x.is_finite() && position.y.is_finite())
1934 .then_some(position)
1935}
1936
1937fn plain_key_down(event: &KeyEvent) -> bool {
1938 event.event_type == KeyEventType::KeyDown
1939 && !event.modifiers.ctrl
1940 && !event.modifiers.meta
1941 && !event.modifiers.alt
1942}