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 (target_id, canvas_bounds) = match target {
260 Some(ActivationTarget::Direct(action)) => {
261 action.invoke();
262 return true;
263 }
264 Some(ActivationTarget::Edit(node_id)) => {
265 self.activate();
266 return cranpose_ui::request_focus_from_platform(node_id);
267 }
268 Some(ActivationTarget::Pointer(node_id, bounds)) => (node_id, bounds),
269 None => return false,
270 };
271 let position =
272 self.with_layout_tree(|tree| activation_input(tree?.root(), target_id, canvas_bounds));
273 let Some((position, local, handlers)) = position else {
274 return false;
275 };
276 for kind in [PointerEventKind::Down, PointerEventKind::Up] {
277 let event = self.pointer_event(kind, local, position, event_time);
278 handlers.dispatch_pointer_event(event.clone());
279 event.finish_post_dispatch();
280 }
281 true
282 }
283
284 pub fn pointer_pressed(&mut self) -> bool {
285 self.pointer_pressed_at_time(None)
286 }
287
288 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
291 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
292 self.pointer_pressed_at_event_time(event_time)
293 }
294
295 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
297 let (cursor_x, cursor_y) = self.surface().cursor;
298 if self.inspector_press(cursor_x, cursor_y) {
299 return true;
300 }
301 if self.dev_overlay_press(cursor_x, cursor_y) {
302 return true;
303 }
304 let _event_handler = enter_event_handler_scope();
305 let app_context = Rc::clone(&self.shell.app.app_context);
306 let result = app_context.enter(|| {
307 run_in_mutable_snapshot(|| self.pointer_pressed_inner(event_time)).unwrap_or(false)
308 });
309 if result {
310 self.mark_dirty();
311 }
312 log::trace!(
313 target: "cranpose::input",
314 "pointer_pressed time_ms={:?} animation_time_nanos={} -> {result}",
315 event_time.platform_time_ms,
316 event_time.animation_time_nanos,
317 );
318 result
319 }
320
321 fn pointer_pressed_inner(&mut self, event_time: PointerEventTime) -> bool {
322 self.activate();
323 self.note_focus_moved_by_keyboard(false);
324 self.surface_mut()
325 .buttons_pressed
326 .insert(PointerButton::Primary);
327
328 let (cursor_x, cursor_y) = self.surface().cursor;
329 let mut hits = self.surface().renderer.scene().hit_test(cursor_x, cursor_y);
330 if self.surface().pointer_source.is_touch_like()
331 && let Some(near) = self
332 .surface()
333 .renderer
334 .scene()
335 .hit_test_near(cursor_x, cursor_y)
336 && hits.iter().all(|hit| {
337 hit.node_id() != near.node_id() && near.capture_path().contains(&hit.node_id())
338 })
339 {
340 hits.insert(0, near);
341 }
342 if hits.is_empty() {
343 self.surface_mut()
344 .hit_path_tracker
345 .remove_path(PointerId::PRIMARY);
346 false
347 } else {
348 let event = self
349 .pointer_event(
350 PointerEventKind::Down,
351 Point {
352 x: self.surface().cursor.0,
353 y: self.surface().cursor.1,
354 },
355 Point {
356 x: self.surface().cursor.0,
357 y: self.surface().cursor.1,
358 },
359 event_time,
360 )
361 .with_buttons(self.surface().buttons_pressed)
362 .with_source(self.surface().pointer_source);
363
364 let mut delivered_capture_paths = Vec::new();
365 let (app, surface) = self.parts();
366 let mut applier = app.composition.applier_mut();
367 for hit in hits {
368 let node_id = hit.node_id();
369 delivered_capture_paths.push(hit.capture_path());
370 hit.dispatch_with_applier(&mut applier, event.clone());
371 log::trace!(
372 target: "cranpose::input",
373 "dispatch {:?} node={} consumed={} stop_on_consume=true",
374 event.kind,
375 node_id,
376 event.is_consumed(),
377 );
378 if event.is_consumed() {
379 break;
380 }
381 }
382
383 surface
384 .hit_path_tracker
385 .add_hit_path(PointerId::PRIMARY, delivered_capture_paths);
386 log::trace!(
387 target: "cranpose::input",
388 "pointer_pressed_inner cached_hit_path={:?}",
389 surface.hit_path_tracker.get_path(PointerId::PRIMARY),
390 );
391
392 true
393 }
394 }
395
396 pub fn pointer_released(&mut self) -> bool {
397 self.pointer_released_at_time(None)
398 }
399
400 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
413 self.pointer_released_at_position_time(x, y, None)
414 }
415
416 pub fn pointer_released_at_position_time(
420 &mut self,
421 x: f32,
422 y: f32,
423 time_ms: Option<i64>,
424 ) -> bool {
425 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
426 self.pointer_released_at_position_event_time(x, y, event_time)
427 }
428
429 pub fn pointer_released_at_position_event_time(
431 &mut self,
432 x: f32,
433 y: f32,
434 event_time: PointerEventTime,
435 ) -> bool {
436 let _event_handler = enter_event_handler_scope();
437 let app_context = Rc::clone(&self.shell.app.app_context);
438 let result = app_context.enter(|| {
439 run_in_mutable_snapshot(|| {
440 self.surface_mut().cursor = (x, y);
441 self.pointer_released_inner(event_time)
442 })
443 .unwrap_or(false)
444 });
445 self.route_drag_and_drop();
446 if result {
447 self.mark_dirty();
448 }
449 log::trace!(
450 target: "cranpose::input",
451 "pointer_released_at_position ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
452 event_time.platform_time_ms,
453 event_time.animation_time_nanos,
454 );
455 result
456 }
457
458 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
461 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
462 self.pointer_released_at_event_time(event_time)
463 }
464
465 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
467 let _event_handler = enter_event_handler_scope();
468 let app_context = Rc::clone(&self.shell.app.app_context);
469 let result = app_context.enter(|| {
470 run_in_mutable_snapshot(|| self.pointer_released_inner(event_time)).unwrap_or(false)
471 });
472 self.route_drag_and_drop();
473 if result {
474 self.mark_dirty();
475 }
476 log::trace!(
477 target: "cranpose::input",
478 "pointer_released time_ms={:?} animation_time_nanos={} -> {result}",
479 event_time.platform_time_ms,
480 event_time.animation_time_nanos,
481 );
482 result
483 }
484
485 fn pointer_released_inner(&mut self, event_time: PointerEventTime) -> bool {
486 if self.surface_mut().inspector.release_pointer() {
487 return true;
488 }
489 self.release_app_pointer(event_time)
490 }
491
492 fn release_app_pointer(&mut self, event_time: PointerEventTime) -> bool {
493 self.surface_mut()
494 .buttons_pressed
495 .remove(PointerButton::Primary);
496 let corrected_buttons = self.surface().buttons_pressed;
497 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
498
499 self.surface_mut()
500 .hit_path_tracker
501 .remove_path(PointerId::PRIMARY);
502
503 if !targets.is_empty() {
504 let event = self
505 .pointer_event(
506 PointerEventKind::Up,
507 Point {
508 x: self.surface().cursor.0,
509 y: self.surface().cursor.1,
510 },
511 Point {
512 x: self.surface().cursor.0,
513 y: self.surface().cursor.1,
514 },
515 event_time,
516 )
517 .with_buttons(corrected_buttons)
518 .with_source(self.surface().pointer_source);
519
520 self.dispatch_targets(targets, event, false);
521 true
522 } else {
523 false
524 }
525 }
526
527 pub fn secondary_pointer_pressed(
536 &mut self,
537 pointer_id: u64,
538 x: f32,
539 y: f32,
540 time_ms: Option<i64>,
541 ) -> bool {
542 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
543 self.dispatch_secondary_pointer(PointerEventKind::Down, pointer_id, x, y, event_time)
544 }
545
546 pub fn secondary_pointer_moved(
548 &mut self,
549 pointer_id: u64,
550 x: f32,
551 y: f32,
552 time_ms: Option<i64>,
553 ) -> bool {
554 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
555 self.dispatch_secondary_pointer(PointerEventKind::Move, pointer_id, x, y, event_time)
556 }
557
558 pub fn secondary_pointer_released(
560 &mut self,
561 pointer_id: u64,
562 x: f32,
563 y: f32,
564 time_ms: Option<i64>,
565 ) -> bool {
566 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
567 self.dispatch_secondary_pointer(PointerEventKind::Up, pointer_id, x, y, event_time)
568 }
569
570 fn dispatch_secondary_pointer(
571 &mut self,
572 kind: PointerEventKind,
573 pointer_id: u64,
574 x: f32,
575 y: f32,
576 event_time: PointerEventTime,
577 ) -> bool {
578 if pointer_id == 0 {
579 log::warn!(
580 target: "cranpose::input",
581 "secondary pointer dispatch called with the primary pointer id"
582 );
583 return false;
584 }
585
586 let _event_handler = enter_event_handler_scope();
587 let app_context = Rc::clone(&self.shell.app.app_context);
588 let result = app_context.enter(|| {
589 run_in_mutable_snapshot(|| {
590 if !self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
591 return false;
592 }
593 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
594 if targets.is_empty() {
595 return false;
596 }
597 let pos = Point { x, y };
598 let event = self
599 .pointer_event(kind, pos, pos, event_time)
600 .with_buttons(self.surface().buttons_pressed)
601 .with_id(pointer_id)
602 .with_source(self.surface().pointer_source);
603 self.dispatch_targets(targets, event, false);
604 true
605 })
606 .unwrap_or(false)
607 });
608 if result {
609 self.mark_dirty();
610 }
611 log::trace!(
612 target: "cranpose::input",
613 "secondary_pointer {kind:?} id={pointer_id} ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
614 event_time.platform_time_ms,
615 event_time.animation_time_nanos,
616 );
617 result
618 }
619
620 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
626 let event_time = self.shell.app.realtime_pointer_event_time(None);
627 let _event_handler = enter_event_handler_scope();
628 let app_context = Rc::clone(&self.shell.app.app_context);
629 let result = app_context.enter(|| {
630 run_in_mutable_snapshot(|| self.pointer_zoomed_inner(zoom_factor, event_time))
631 .unwrap_or(false)
632 });
633 if result {
634 self.mark_dirty();
635 }
636 log::trace!(
637 target: "cranpose::input",
638 "pointer_zoomed factor={zoom_factor:.4} -> {result}"
639 );
640 result
641 }
642
643 fn pointer_zoomed_inner(&mut self, zoom_factor: f32, event_time: PointerEventTime) -> bool {
644 if !zoom_factor.is_finite() || zoom_factor <= 0.0 || zoom_factor == 1.0 {
645 return false;
646 }
647
648 let hits = self
649 .surface()
650 .renderer
651 .scene()
652 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
653 if hits.is_empty() {
654 return false;
655 }
656
657 let pos = Point {
658 x: self.surface().cursor.0,
659 y: self.surface().cursor.1,
660 };
661 let event = self
662 .pointer_event(PointerEventKind::Zoom, pos, pos, event_time)
663 .with_buttons(self.surface().buttons_pressed)
664 .with_zoom_delta(zoom_factor)
665 .with_source(self.surface().pointer_source);
666
667 self.dispatch_to_hits(&hits, event)
668 }
669
670 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
695 if self.inspector_scroll(wheel.delta.y) {
696 return true;
697 }
698 if wheel.is_zoom() {
699 let zoom_factor = wheel.zoom_factor();
700 log::trace!(
701 target: "cranpose::input",
702 "wheel zoom factor={zoom_factor:.4}"
703 );
704 return self.pointer_zoomed(zoom_factor);
705 }
706
707 let rotary =
708 RotaryScrollEvent::from_wheel_pixels(wheel.delta.y, wheel.delta.x, wheel.uptime_millis);
709 if self.rotary_scrolled(rotary) {
710 return true;
711 }
712
713 let delta = wheel.scroll_delta();
714 log::trace!(
715 target: "cranpose::input",
716 "wheel delta ({:.2},{:.2}) alt={}",
717 delta.x,
718 delta.y,
719 wheel.modifiers.alt
720 );
721 self.pointer_scrolled(delta.x, delta.y)
722 }
723
724 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
732 if self.inspector_scroll(delta_y) {
733 return true;
734 }
735 let event_time = self.shell.app.realtime_pointer_event_time(None);
736 let _event_handler = enter_event_handler_scope();
737 let app_context = Rc::clone(&self.shell.app.app_context);
738 let result = app_context.enter(|| {
739 run_in_mutable_snapshot(|| self.pointer_scrolled_inner(delta_x, delta_y, event_time))
740 .unwrap_or(false)
741 });
742 if result {
743 self.mark_dirty();
744 }
745 log::trace!(
746 target: "cranpose::input",
747 "pointer_scrolled ({delta_x:.2},{delta_y:.2}) -> {result}"
748 );
749 result
750 }
751
752 fn pointer_scrolled_inner(
753 &mut self,
754 delta_x: f32,
755 delta_y: f32,
756 event_time: PointerEventTime,
757 ) -> bool {
758 if delta_x.abs() <= f32::EPSILON && delta_y.abs() <= f32::EPSILON {
759 return false;
760 }
761
762 let hits = self
763 .surface()
764 .renderer
765 .scene()
766 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
767 if hits.is_empty() {
768 return false;
769 }
770
771 let event = self
772 .pointer_event(
773 PointerEventKind::Scroll,
774 Point {
775 x: self.surface().cursor.0,
776 y: self.surface().cursor.1,
777 },
778 Point {
779 x: self.surface().cursor.0,
780 y: self.surface().cursor.1,
781 },
782 event_time,
783 )
784 .with_buttons(self.surface().buttons_pressed)
785 .with_scroll_delta(Point {
786 x: delta_x,
787 y: delta_y,
788 })
789 .with_source(self.surface().pointer_source);
790
791 self.dispatch_to_hits(&hits, event)
792 }
793
794 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
805 where
806 F: Fn(RotaryScrollEvent) -> bool + 'static,
807 {
808 self.surface_mut().on_rotary_scroll = Some(Rc::new(handler));
809 }
810
811 pub fn clear_on_rotary_scroll(&mut self) {
813 self.surface_mut().on_rotary_scroll = None;
814 }
815
816 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
822 let factor = self.shell.app.rotary_scroll_factor;
823 self.rotary_scrolled(RotaryScrollEvent::from_detents(
824 detents,
825 factor,
826 factor,
827 uptime_millis,
828 ))
829 }
830
831 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
852 let _event_handler = enter_event_handler_scope();
853 let app_context = Rc::clone(&self.shell.app.app_context);
854 let result = app_context.enter(|| {
855 run_in_mutable_snapshot(|| self.rotary_scrolled_inner(event)).unwrap_or(false)
856 });
857 if result {
858 self.mark_dirty();
859 }
860 log::trace!(
861 target: "cranpose::input",
862 "rotary_scrolled v={:.2} h={:.2} uptime={} -> {result}",
863 event.vertical_scroll_pixels,
864 event.horizontal_scroll_pixels,
865 event.uptime_millis,
866 );
867 result
868 }
869
870 fn rotary_scrolled_inner(&mut self, rotary: RotaryScrollEvent) -> bool {
871 if rotary.is_empty() {
872 return false;
873 }
874
875 let bubble_order = self.rotary_dispatch_order();
876 let position = Point {
877 x: self.surface().cursor.0,
878 y: self.surface().cursor.1,
879 };
880
881 if !bubble_order.is_empty() {
882 let capture_targets = bubble_order
883 .iter()
884 .rev()
885 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
886 .collect::<Vec<_>>();
887 let mut capture_event =
888 PointerEvent::rotary(PointerEventKind::RotaryScrollPre, rotary, position);
889 capture_event.modifiers = self.shell.app.modifiers;
890 self.dispatch_targets(capture_targets, capture_event.clone(), true);
891 if capture_event.is_consumed() {
892 return true;
893 }
894
895 let bubble_targets = bubble_order
896 .iter()
897 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
898 .collect::<Vec<_>>();
899 let mut bubble_event =
900 PointerEvent::rotary(PointerEventKind::RotaryScroll, rotary, position);
901 bubble_event.modifiers = self.shell.app.modifiers;
902 self.dispatch_targets(bubble_targets, bubble_event.clone(), true);
903 if bubble_event.is_consumed() {
904 return true;
905 }
906 }
907
908 if let Some(handler) = self.surface().on_rotary_scroll.clone() {
909 return handler(rotary);
910 }
911
912 false
913 }
914
915 fn rotary_dispatch_order(&self) -> Vec<NodeId> {
916 if let Some(focused) = cranpose_ui::active_focus_target()
917 && let Some(target) = self.surface().renderer.scene().find_target(focused)
918 {
919 let path = target.capture_path();
920 if !path.is_empty() {
921 return crate::hit_path_tracker::dispatch_order_for_paths(&[path]);
922 }
923 }
924
925 let hits = self
926 .surface()
927 .renderer
928 .scene()
929 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
930 if hits.is_empty() {
931 return Vec::new();
932 }
933 let capture_paths = hits
934 .iter()
935 .map(|hit| hit.capture_path())
936 .collect::<Vec<_>>();
937 crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
938 }
939
940 pub fn cancel_gesture(&mut self) {
946 let event_time = self.shell.app.realtime_pointer_event_time(None);
947 let _event_handler = enter_event_handler_scope();
948 let app_context = Rc::clone(&self.shell.app.app_context);
949 let _ = app_context.enter(|| {
950 run_in_mutable_snapshot(|| {
951 self.cancel_gesture_inner(event_time);
952 })
953 });
954 self.route_drag_and_drop();
955 }
956
957 fn route_drag_and_drop(&mut self) {
958 let app_context = Rc::clone(&self.shell.app.app_context);
959 let source = self.index;
960 let shell = &mut *self.shell;
961 let routed = app_context.enter(|| {
962 run_in_mutable_snapshot(|| {
963 app_context
964 .drag_and_drop()
965 .route(|point| shell.drag_and_drop_target_at(point, source))
966 })
967 .unwrap_or(false)
968 });
969 if routed {
970 shell.mark_all_dirty();
971 }
972 }
973
974 pub fn cancel_gesture_unless_pressed(&mut self) {
983 if self.surface().buttons_pressed != PointerButtons::NONE
984 || self.surface().inspector.pointer_captured
985 {
986 return;
987 }
988 self.cancel_gesture();
989 }
990
991 fn cancel_gesture_inner(&mut self, event_time: PointerEventTime) {
992 self.surface_mut().inspector.cancel_pointer();
993 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
994
995 self.surface_mut().hit_path_tracker.clear();
996 self.surface_mut().buttons_pressed = PointerButtons::NONE;
997
998 if !targets.is_empty() {
999 let event = self
1000 .pointer_event(
1001 PointerEventKind::Cancel,
1002 Point {
1003 x: self.surface().cursor.0,
1004 y: self.surface().cursor.1,
1005 },
1006 Point {
1007 x: self.surface().cursor.0,
1008 y: self.surface().cursor.1,
1009 },
1010 event_time,
1011 )
1012 .with_source(self.surface().pointer_source);
1013
1014 self.dispatch_targets(targets, event, false);
1015 }
1016
1017 let pos = Point {
1018 x: self.surface().cursor.0,
1019 y: self.surface().cursor.1,
1020 };
1021 let hovered_nodes = self.surface().hovered_nodes.clone();
1022 for node_id in hovered_nodes {
1023 if let Some(target) = self.surface().renderer.scene().find_target(node_id) {
1024 let exit_event = self
1025 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
1026 .with_source(self.surface().pointer_source);
1027 self.dispatch_targets(std::iter::once(target), exit_event, false);
1028 }
1029 }
1030 self.surface_mut().hovered_nodes.clear();
1031 self.surface().pointer_icon.set(PointerIcon::DEFAULT);
1032 }
1033
1034 fn apply_hovered_pointer_icon(
1035 &self,
1036 hits: &[<<R as Renderer>::Scene as RenderScene>::HitTarget],
1037 ) {
1038 let icon = hits
1039 .iter()
1040 .find_map(HitTestTarget::pointer_icon)
1041 .unwrap_or(PointerIcon::DEFAULT);
1042 self.surface().pointer_icon.set(icon);
1043 }
1044
1045 fn on_focus_key(&mut self, event: &KeyEvent) -> bool {
1046 if !plain_key_down(event) || event.key_code != KeyCode::Tab {
1047 return false;
1048 }
1049 self.on_group_navigation_key(event)
1050 }
1051
1052 fn on_group_navigation_key(&mut self, event: &KeyEvent) -> bool {
1053 let focused = cranpose_ui::active_focus_target();
1054 let target = self.with_layout_tree(|tree| {
1055 cranpose_ui::keyboard_focus_target(
1056 tree?,
1057 focused,
1058 event.key_code,
1059 event.modifiers.shift,
1060 )
1061 });
1062 let Some(target) = target else {
1063 return false;
1064 };
1065 let moved =
1066 run_in_mutable_snapshot(|| cranpose_ui::request_focus_from_platform(target.node_id))
1067 .unwrap_or(false);
1068 if moved {
1069 self.mark_dirty();
1070 self.note_focus_moved_by_keyboard(true);
1071 if target.activate {
1072 self.on_activation_key(&KeyEvent::key_down(KeyCode::Space, " "));
1073 }
1074 }
1075 moved
1076 }
1077
1078 fn on_activation_key(&mut self, event: &KeyEvent) -> bool {
1079 if !plain_key_down(event)
1080 || !matches!(event.key_code, KeyCode::Enter | KeyCode::Space)
1081 || cranpose_ui::text_field_focus::has_focused_field()
1082 {
1083 return false;
1084 }
1085 let Some(focused) = cranpose_ui::active_focus_target() else {
1086 return false;
1087 };
1088 let activated = self.accessibility_activate(focused, None);
1089 self.note_focus_moved_by_keyboard(true);
1090 activated
1091 }
1092
1093 fn on_arrow_key(&mut self, event: &KeyEvent) -> bool {
1094 if !plain_key_down(event) || cranpose_ui::text_field_focus::has_focused_field() {
1095 return false;
1096 }
1097 if self.on_group_navigation_key(event) {
1098 return true;
1099 }
1100 let direction = match event.key_code {
1101 KeyCode::ArrowLeft => FocusDirection::Left,
1102 KeyCode::ArrowRight => FocusDirection::Right,
1103 KeyCode::ArrowUp => FocusDirection::Up,
1104 KeyCode::ArrowDown => FocusDirection::Down,
1105 _ => return false,
1106 };
1107 let Some(focused) = cranpose_ui::active_focus_target() else {
1108 return false;
1109 };
1110 let order = self.with_layout_tree(|layout_tree| {
1111 let layout_tree = layout_tree?;
1112 let group = cranpose_ui::selectable_group_of(layout_tree, focused)?;
1113 Some(cranpose_ui::collect_focus_order_under(layout_tree, group))
1114 });
1115 order.is_some_and(|order| self.move_focus_in_order(order, direction))
1116 }
1117
1118 fn note_focus_moved_by_keyboard(&mut self, keyboard: bool) {
1119 if cranpose_ui::set_keyboard_focus_visible(keyboard) {
1120 cranpose_ui::request_render_invalidation();
1121 self.mark_dirty();
1122 }
1123 }
1124
1125 fn on_escape_key(&mut self, event: &KeyEvent) -> bool {
1126 if event.event_type != KeyEventType::KeyDown || event.key_code != KeyCode::Escape {
1127 return false;
1128 }
1129 self.dismiss_top_modal_in_context()
1130 }
1131
1132 pub fn dismiss_top_modal(&mut self) -> bool {
1135 let _event_handler = enter_event_handler_scope();
1136 let app_context = Rc::clone(&self.shell.app.app_context);
1137 app_context.enter(|| self.dismiss_top_modal_in_context())
1138 }
1139
1140 fn dismiss_top_modal_in_context(&mut self) -> bool {
1141 let closed = run_in_mutable_snapshot(|| {
1142 if cranpose_ui::modal_depth() > 0 {
1143 cranpose_ui::dispatch_modal_back()
1144 } else {
1145 cranpose_ui::dismiss_top_popup()
1146 }
1147 })
1148 .unwrap_or(false);
1149 if closed {
1150 self.mark_dirty();
1151 }
1152 closed
1153 }
1154
1155 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1158 let order = self.with_layout_tree(|layout_tree| {
1159 layout_tree
1160 .map(cranpose_ui::collect_focus_order)
1161 .unwrap_or_default()
1162 });
1163 self.move_focus_in_order(order, direction)
1164 }
1165
1166 fn move_focus_in_order(
1167 &mut self,
1168 order: Vec<cranpose_ui::FocusEntry>,
1169 direction: FocusDirection,
1170 ) -> bool {
1171 cranpose_ui::set_focus_order(order);
1172 let moved = run_in_mutable_snapshot(|| cranpose_ui::FocusManager.move_focus(direction))
1173 .unwrap_or(false);
1174 if moved {
1175 self.mark_dirty();
1176 self.note_focus_moved_by_keyboard(true);
1177 }
1178 moved
1179 }
1180
1181 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1182 let _event_handler = enter_event_handler_scope();
1183 let app_context = Rc::clone(&self.shell.app.app_context);
1184 app_context.enter(|| self.on_key_event_inner(event))
1185 }
1186
1187 fn on_key_event_inner(&mut self, event: &KeyEvent) -> bool {
1188 if self.inspector_key(event) {
1189 return true;
1190 }
1191 use KeyEventType::KeyDown;
1192
1193 if event.event_type == KeyDown && event.modifiers.command_or_ctrl() {
1194 #[cfg(all(
1195 feature = "clipboard-native",
1196 not(target_arch = "wasm32"),
1197 not(target_os = "android"),
1198 not(target_os = "ios")
1199 ))]
1200 {
1201 match event.key_code {
1202 KeyCode::C => {
1203 if let Some(text) = self.on_copy_inner() {
1204 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1205 return true;
1206 }
1207 }
1208 KeyCode::X => {
1209 if let Some(text) = self.on_cut_inner() {
1210 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1211 self.mark_dirty();
1212 self.shell.app.request_layout_pass();
1213 return true;
1214 }
1215 }
1216 KeyCode::V => {
1217 if let Some(text) = cranpose_ui::clipboard_session::clipboard_read_text()
1218 && self.on_paste_inner(&text)
1219 {
1220 return true;
1221 }
1222 }
1223 _ => {}
1224 }
1225 }
1226 }
1227
1228 if self.on_focus_key(event) {
1229 return true;
1230 }
1231
1232 if self.on_escape_key(event) {
1233 return true;
1234 }
1235
1236 if self.on_activation_key(event) || self.on_arrow_key(event) {
1237 return true;
1238 }
1239
1240 let handled = if cranpose_ui::text_field_focus::has_focused_field() {
1241 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_key_event(event))
1242 } else {
1243 run_in_mutable_snapshot(|| cranpose_ui::dispatch_unhandled_key_event(event))
1244 }
1245 .unwrap_or(false);
1246
1247 if handled {
1248 self.mark_dirty();
1249 self.shell.app.request_layout_pass();
1250 }
1251
1252 handled
1253 }
1254
1255 pub fn on_paste(&mut self, text: &str) -> bool {
1259 if self.inspector_owns_keyboard() {
1260 return true;
1261 }
1262 let _event_handler = enter_event_handler_scope();
1263 let app_context = Rc::clone(&self.shell.app.app_context);
1264 app_context.enter(|| self.on_paste_inner(text))
1265 }
1266
1267 fn on_paste_inner(&mut self, text: &str) -> bool {
1268 let handled =
1269 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1270 .unwrap_or(false);
1271
1272 if handled {
1273 self.mark_dirty();
1274 self.shell.app.request_layout_pass();
1275 }
1276
1277 handled
1278 }
1279
1280 pub fn on_copy(&mut self) -> Option<String> {
1284 let app_context = Rc::clone(&self.shell.app.app_context);
1285 if self.inspector_owns_keyboard() {
1286 return None;
1287 }
1288 app_context.enter(|| self.on_copy_inner())
1289 }
1290
1291 fn on_copy_inner(&mut self) -> Option<String> {
1292 cranpose_ui::text_field_focus::dispatch_copy()
1293 }
1294
1295 pub fn on_cut(&mut self) -> Option<String> {
1299 let _event_handler = enter_event_handler_scope();
1300 if self.inspector_owns_keyboard() {
1301 return None;
1302 }
1303 let app_context = Rc::clone(&self.shell.app.app_context);
1304 app_context.enter(|| self.on_cut_inner())
1305 }
1306
1307 fn on_cut_inner(&mut self) -> Option<String> {
1308 let text =
1309 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1310
1311 if text.is_some() {
1312 self.mark_dirty();
1313 self.shell.app.request_layout_pass();
1314 }
1315
1316 text
1317 }
1318
1319 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1327 if self.inspector_owns_keyboard() {
1328 return true;
1329 }
1330 let _event_handler = enter_event_handler_scope();
1331 let app_context = Rc::clone(&self.shell.app.app_context);
1332 app_context.enter(|| self.on_ime_preedit_inner(text, cursor))
1333 }
1334
1335 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1336 let handled = run_in_mutable_snapshot(|| {
1337 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1338 })
1339 .unwrap_or(false);
1340
1341 if handled {
1342 self.mark_dirty();
1343 self.shell.app.request_layout_pass();
1344 }
1345
1346 handled
1347 }
1348
1349 pub fn on_ime_finish_composing(&mut self) -> bool {
1353 if self.inspector_owns_keyboard() {
1354 return true;
1355 }
1356 let _event_handler = enter_event_handler_scope();
1357 let app_context = Rc::clone(&self.shell.app.app_context);
1358 app_context.enter(|| self.on_ime_finish_composing_inner())
1359 }
1360
1361 fn on_ime_finish_composing_inner(&mut self) -> bool {
1362 let handled =
1363 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1364 .unwrap_or(false);
1365
1366 if handled {
1367 self.mark_dirty();
1368 self.shell.app.request_layout_pass();
1369 }
1370
1371 handled
1372 }
1373
1374 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1378 if self.inspector_owns_keyboard() {
1379 return true;
1380 }
1381 let _event_handler = enter_event_handler_scope();
1382 let app_context = Rc::clone(&self.shell.app.app_context);
1383 app_context.enter(|| {
1384 let handled = run_in_mutable_snapshot(|| {
1385 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(
1386 start_bytes,
1387 end_bytes,
1388 )
1389 })
1390 .unwrap_or(false);
1391
1392 if handled {
1393 self.mark_dirty();
1394 self.shell.app.request_layout_pass();
1395 }
1396
1397 handled
1398 })
1399 }
1400
1401 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1406 if self.inspector_owns_keyboard() {
1407 return true;
1408 }
1409 let _event_handler = enter_event_handler_scope();
1410 let app_context = Rc::clone(&self.shell.app.app_context);
1411 app_context.enter(|| {
1412 let handled = run_in_mutable_snapshot(|| {
1413 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1414 })
1415 .unwrap_or(false);
1416
1417 if handled {
1418 self.mark_dirty();
1419 }
1420
1421 handled
1422 })
1423 }
1424
1425 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1429 let app_context = Rc::clone(&self.shell.app.app_context);
1430 app_context.enter(cranpose_ui::text_field_focus::focused_editor_state)
1431 }
1432
1433 pub fn ime_caret_geometry(
1437 &mut self,
1438 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1439 let app_context = Rc::clone(&self.shell.app.app_context);
1440 app_context.enter(cranpose_ui::text_field_focus::focused_caret_geometry)
1441 }
1442
1443 pub fn clear_text_field_focus(&mut self) {
1446 let _event_handler = enter_event_handler_scope();
1447 let app_context = Rc::clone(&self.shell.app.app_context);
1448 app_context.enter(cranpose_ui::text_field_focus::clear_focus);
1449 self.mark_dirty();
1450 self.shell.app.request_layout_pass();
1451 }
1452
1453 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1456 if self.inspector_owns_keyboard() {
1457 return true;
1458 }
1459 let _event_handler = enter_event_handler_scope();
1460 let app_context = Rc::clone(&self.shell.app.app_context);
1461 app_context.enter(|| self.on_ime_delete_surrounding_inner(before_bytes, after_bytes))
1462 }
1463
1464 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1465 let handled = run_in_mutable_snapshot(|| {
1466 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1467 })
1468 .unwrap_or(false);
1469
1470 if handled {
1471 self.mark_dirty();
1472 self.shell.app.request_layout_pass();
1473 }
1474
1475 handled
1476 }
1477}
1478
1479impl<R> AppShell<R>
1480where
1481 R: Renderer,
1482 R::Error: Debug,
1483{
1484 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
1493 self.app.modifiers = Some(modifiers);
1494 }
1495
1496 pub fn modifiers(&self) -> Option<Modifiers> {
1500 self.app.modifiers
1501 }
1502
1503 pub fn rotary_scroll_factor(&self) -> f32 {
1506 self.app.rotary_scroll_factor
1507 }
1508
1509 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
1518 if factor.is_finite() && factor > 0.0 {
1519 self.app.rotary_scroll_factor = factor;
1520 }
1521 }
1522
1523 pub fn notify_app_paused(&mut self) {
1530 let app_context = Rc::clone(&self.app.app_context);
1531 app_context.enter(cranpose_ui::text_input_session::notify_app_paused);
1532 }
1533
1534 pub fn notify_app_resumed(&mut self) -> bool {
1542 let app_context = Rc::clone(&self.app.app_context);
1543 app_context.enter(cranpose_ui::text_input_session::notify_app_resumed)
1544 }
1545
1546 #[cfg(all(
1547 feature = "clipboard-native",
1548 target_os = "linux",
1549 not(target_arch = "wasm32")
1550 ))]
1551 pub fn set_primary_selection(&mut self, text: &str) {
1552 use arboard::{LinuxClipboardKind, SetExtLinux};
1553 if let Some(ref mut clipboard) = self.app.clipboard {
1554 let result = clipboard
1555 .set()
1556 .clipboard(LinuxClipboardKind::Primary)
1557 .text(text.to_string());
1558 if let Err(e) = result {
1559 log::debug!("Primary selection set failed: {:?}", e);
1560 }
1561 }
1562 }
1563
1564 #[cfg(not(all(
1565 feature = "clipboard-native",
1566 target_os = "linux",
1567 not(target_arch = "wasm32")
1568 )))]
1569 pub fn set_primary_selection(&mut self, _text: &str) {}
1570
1571 #[cfg(all(
1572 feature = "clipboard-native",
1573 target_os = "linux",
1574 not(target_arch = "wasm32")
1575 ))]
1576 pub fn get_primary_selection(&mut self) -> Option<String> {
1577 use arboard::{GetExtLinux, LinuxClipboardKind};
1578 if let Some(ref mut clipboard) = self.app.clipboard {
1579 clipboard
1580 .get()
1581 .clipboard(LinuxClipboardKind::Primary)
1582 .text()
1583 .ok()
1584 } else {
1585 None
1586 }
1587 }
1588
1589 #[cfg(not(all(
1590 feature = "clipboard-native",
1591 target_os = "linux",
1592 not(target_arch = "wasm32")
1593 )))]
1594 pub fn get_primary_selection(&mut self) -> Option<String> {
1595 None
1596 }
1597
1598 pub fn sync_selection_to_primary(&mut self) {
1601 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1602 {
1603 if let Some(text) = self.on_copy() {
1604 self.set_primary_selection(&text);
1605 }
1606 }
1607 }
1608
1609 pub fn set_pointer_source(&mut self, source: PointerSource) {
1611 self.primary().set_pointer_source(source);
1612 }
1613
1614 pub fn pointer_source(&self) -> PointerSource {
1616 self.surfaces[0].pointer_source
1617 }
1618
1619 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
1621 self.primary().set_cursor(x, y)
1622 }
1623
1624 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
1626 self.primary().set_cursor_at_time(x, y, time_ms)
1627 }
1628
1629 pub fn set_cursor_at_event_time(
1631 &mut self,
1632 x: f32,
1633 y: f32,
1634 event_time: PointerEventTime,
1635 ) -> bool {
1636 self.primary().set_cursor_at_event_time(x, y, event_time)
1637 }
1638
1639 pub fn pointer_pressed(&mut self) -> bool {
1641 self.primary().pointer_pressed()
1642 }
1643
1644 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
1646 self.primary().accessibility_activate(node_id, canvas_key)
1647 }
1648
1649 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
1651 self.primary().pointer_pressed_at_time(time_ms)
1652 }
1653
1654 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1656 self.primary().pointer_pressed_at_event_time(event_time)
1657 }
1658
1659 pub fn pointer_released(&mut self) -> bool {
1661 self.primary().pointer_released()
1662 }
1663
1664 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
1666 self.primary().pointer_released_at_position(x, y)
1667 }
1668
1669 pub fn pointer_released_at_position_time(
1672 &mut self,
1673 x: f32,
1674 y: f32,
1675 time_ms: Option<i64>,
1676 ) -> bool {
1677 self.primary()
1678 .pointer_released_at_position_time(x, y, time_ms)
1679 }
1680
1681 pub fn pointer_released_at_position_event_time(
1684 &mut self,
1685 x: f32,
1686 y: f32,
1687 event_time: PointerEventTime,
1688 ) -> bool {
1689 self.primary()
1690 .pointer_released_at_position_event_time(x, y, event_time)
1691 }
1692
1693 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
1695 self.primary().pointer_released_at_time(time_ms)
1696 }
1697
1698 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1700 self.primary().pointer_released_at_event_time(event_time)
1701 }
1702
1703 pub fn secondary_pointer_pressed(
1705 &mut self,
1706 pointer_id: u64,
1707 x: f32,
1708 y: f32,
1709 time_ms: Option<i64>,
1710 ) -> bool {
1711 self.primary()
1712 .secondary_pointer_pressed(pointer_id, x, y, time_ms)
1713 }
1714
1715 pub fn secondary_pointer_moved(
1717 &mut self,
1718 pointer_id: u64,
1719 x: f32,
1720 y: f32,
1721 time_ms: Option<i64>,
1722 ) -> bool {
1723 self.primary()
1724 .secondary_pointer_moved(pointer_id, x, y, time_ms)
1725 }
1726
1727 pub fn secondary_pointer_released(
1729 &mut self,
1730 pointer_id: u64,
1731 x: f32,
1732 y: f32,
1733 time_ms: Option<i64>,
1734 ) -> bool {
1735 self.primary()
1736 .secondary_pointer_released(pointer_id, x, y, time_ms)
1737 }
1738
1739 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
1741 self.primary().pointer_zoomed(zoom_factor)
1742 }
1743
1744 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
1746 self.primary().wheel_scrolled(wheel)
1747 }
1748
1749 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
1751 self.primary().pointer_scrolled(delta_x, delta_y)
1752 }
1753
1754 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
1756 where
1757 F: Fn(RotaryScrollEvent) -> bool + 'static,
1758 {
1759 self.primary().set_on_rotary_scroll(handler);
1760 }
1761
1762 pub fn clear_on_rotary_scroll(&mut self) {
1764 self.primary().clear_on_rotary_scroll();
1765 }
1766
1767 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
1769 self.primary()
1770 .rotary_scrolled_by_detents(detents, uptime_millis)
1771 }
1772
1773 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
1775 self.primary().rotary_scrolled(event)
1776 }
1777
1778 pub fn cancel_gesture(&mut self) {
1780 self.primary().cancel_gesture();
1781 }
1782
1783 pub fn cancel_gesture_unless_pressed(&mut self) {
1785 self.primary().cancel_gesture_unless_pressed();
1786 }
1787
1788 pub fn refresh_pointer_icon(&self) {
1792 self.surfaces[0].pointer_icon.refresh();
1793 }
1794
1795 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
1802 self.surfaces[0].pointer_icon.take_change()
1803 }
1804
1805 pub fn set_platform_text_input(
1812 &mut self,
1813 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
1814 ) {
1815 self.primary().set_platform_text_input(handler);
1816 }
1817
1818 pub fn dismiss_top_modal(&mut self) -> bool {
1820 self.primary().dismiss_top_modal()
1821 }
1822
1823 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1825 self.primary().move_focus_in_context(direction)
1826 }
1827
1828 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1830 self.primary().on_key_event(event)
1831 }
1832
1833 pub fn on_paste(&mut self, text: &str) -> bool {
1835 self.primary().on_paste(text)
1836 }
1837
1838 pub fn on_copy(&mut self) -> Option<String> {
1840 self.primary().on_copy()
1841 }
1842
1843 pub fn on_cut(&mut self) -> Option<String> {
1845 self.primary().on_cut()
1846 }
1847
1848 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1850 self.primary().on_ime_preedit(text, cursor)
1851 }
1852
1853 pub fn on_ime_finish_composing(&mut self) -> bool {
1855 self.primary().on_ime_finish_composing()
1856 }
1857
1858 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1860 self.primary()
1861 .on_ime_set_composing_region(start_bytes, end_bytes)
1862 }
1863
1864 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1866 self.primary().on_ime_set_selection(start_bytes, end_bytes)
1867 }
1868
1869 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1871 self.primary().ime_editor_state()
1872 }
1873
1874 pub fn ime_caret_geometry(
1876 &mut self,
1877 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1878 self.primary().ime_caret_geometry()
1879 }
1880
1881 pub fn clear_text_field_focus(&mut self) {
1883 self.primary().clear_text_field_focus();
1884 }
1885
1886 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1888 self.primary()
1889 .on_ime_delete_surrounding(before_bytes, after_bytes)
1890 }
1891}
1892
1893enum ActivationTarget {
1894 Direct(cranpose_foundation::SemanticsCustomAction),
1895 Edit(NodeId),
1896 Pointer(NodeId, Option<Rect>),
1897}
1898
1899fn activation_target(
1900 node: &cranpose_ui::SemanticsNode,
1901 node_id: NodeId,
1902 canvas_key: Option<u64>,
1903) -> Option<ActivationTarget> {
1904 if node.hidden {
1905 return None;
1906 }
1907 if node.node_id != node_id {
1908 return node
1909 .children
1910 .iter()
1911 .find_map(|child| activation_target(child, node_id, canvas_key));
1912 }
1913 if !node.enabled {
1914 return None;
1915 }
1916 if let Some(key) = canvas_key {
1917 let child = node.canvas_children.iter().find(|child| child.key == key)?;
1918 return (child.enabled && child.clickable)
1919 .then_some(ActivationTarget::Pointer(node_id, Some(child.bounds)));
1920 }
1921 if let Some(action) = &node.on_click {
1922 return Some(ActivationTarget::Direct(action.clone()));
1923 }
1924 if node.editable_text && node.focusable {
1925 return Some(ActivationTarget::Edit(node_id));
1926 }
1927 node.actions.first().map(|action| match action {
1928 cranpose_ui::SemanticsAction::Click { handler } => {
1929 ActivationTarget::Pointer(handler.node_id(), None)
1930 }
1931 })
1932}
1933
1934fn activation_input(
1935 root: &LayoutBox,
1936 node_id: NodeId,
1937 canvas_bounds: Option<Rect>,
1938) -> Option<(Point, Point, Rc<cranpose_ui::ModifierNodeSlices>)> {
1939 if root.node_id != node_id {
1940 return root
1941 .children
1942 .iter()
1943 .find_map(|child| activation_input(child, node_id, canvas_bounds));
1944 }
1945 let bounds = canvas_bounds.map_or(root.rect, |bounds| Rect {
1946 x: root.rect.x + bounds.x,
1947 y: root.rect.y + bounds.y,
1948 ..bounds
1949 });
1950 let position = Point {
1951 x: bounds.x + bounds.width * 0.5,
1952 y: bounds.y + bounds.height * 0.5,
1953 };
1954 let local = Point {
1955 x: position.x - root.rect.x,
1956 y: position.y - root.rect.y,
1957 };
1958 (bounds.width > 0.0 && bounds.height > 0.0 && position.x.is_finite() && position.y.is_finite())
1959 .then(|| (position, local, Rc::clone(&root.node_data.modifier_slices)))
1960}
1961
1962fn plain_key_down(event: &KeyEvent) -> bool {
1963 event.event_type == KeyEventType::KeyDown
1964 && !event.modifiers.ctrl
1965 && !event.modifiers.meta
1966 && !event.modifiers.alt
1967}