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 if !cranpose_ui::text_field_focus::has_focused_field() {
1241 return false;
1242 }
1243
1244 let handled =
1245 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_key_event(event))
1246 .unwrap_or(false);
1247
1248 if handled {
1249 self.mark_dirty();
1250 self.shell.app.request_layout_pass();
1251 }
1252
1253 handled
1254 }
1255
1256 pub fn on_paste(&mut self, text: &str) -> bool {
1260 if self.inspector_owns_keyboard() {
1261 return true;
1262 }
1263 let _event_handler = enter_event_handler_scope();
1264 let app_context = Rc::clone(&self.shell.app.app_context);
1265 app_context.enter(|| self.on_paste_inner(text))
1266 }
1267
1268 fn on_paste_inner(&mut self, text: &str) -> bool {
1269 let handled =
1270 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1271 .unwrap_or(false);
1272
1273 if handled {
1274 self.mark_dirty();
1275 self.shell.app.request_layout_pass();
1276 }
1277
1278 handled
1279 }
1280
1281 pub fn on_copy(&mut self) -> Option<String> {
1285 let app_context = Rc::clone(&self.shell.app.app_context);
1286 if self.inspector_owns_keyboard() {
1287 return None;
1288 }
1289 app_context.enter(|| self.on_copy_inner())
1290 }
1291
1292 fn on_copy_inner(&mut self) -> Option<String> {
1293 cranpose_ui::text_field_focus::dispatch_copy()
1294 }
1295
1296 pub fn on_cut(&mut self) -> Option<String> {
1300 let _event_handler = enter_event_handler_scope();
1301 if self.inspector_owns_keyboard() {
1302 return None;
1303 }
1304 let app_context = Rc::clone(&self.shell.app.app_context);
1305 app_context.enter(|| self.on_cut_inner())
1306 }
1307
1308 fn on_cut_inner(&mut self) -> Option<String> {
1309 let text =
1310 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1311
1312 if text.is_some() {
1313 self.mark_dirty();
1314 self.shell.app.request_layout_pass();
1315 }
1316
1317 text
1318 }
1319
1320 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1328 if self.inspector_owns_keyboard() {
1329 return true;
1330 }
1331 let _event_handler = enter_event_handler_scope();
1332 let app_context = Rc::clone(&self.shell.app.app_context);
1333 app_context.enter(|| self.on_ime_preedit_inner(text, cursor))
1334 }
1335
1336 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1337 let handled = run_in_mutable_snapshot(|| {
1338 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1339 })
1340 .unwrap_or(false);
1341
1342 if handled {
1343 self.mark_dirty();
1344 self.shell.app.request_layout_pass();
1345 }
1346
1347 handled
1348 }
1349
1350 pub fn on_ime_finish_composing(&mut self) -> bool {
1354 if self.inspector_owns_keyboard() {
1355 return true;
1356 }
1357 let _event_handler = enter_event_handler_scope();
1358 let app_context = Rc::clone(&self.shell.app.app_context);
1359 app_context.enter(|| self.on_ime_finish_composing_inner())
1360 }
1361
1362 fn on_ime_finish_composing_inner(&mut self) -> bool {
1363 let handled =
1364 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1365 .unwrap_or(false);
1366
1367 if handled {
1368 self.mark_dirty();
1369 self.shell.app.request_layout_pass();
1370 }
1371
1372 handled
1373 }
1374
1375 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1379 if self.inspector_owns_keyboard() {
1380 return true;
1381 }
1382 let _event_handler = enter_event_handler_scope();
1383 let app_context = Rc::clone(&self.shell.app.app_context);
1384 app_context.enter(|| {
1385 let handled = run_in_mutable_snapshot(|| {
1386 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(
1387 start_bytes,
1388 end_bytes,
1389 )
1390 })
1391 .unwrap_or(false);
1392
1393 if handled {
1394 self.mark_dirty();
1395 self.shell.app.request_layout_pass();
1396 }
1397
1398 handled
1399 })
1400 }
1401
1402 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1407 if self.inspector_owns_keyboard() {
1408 return true;
1409 }
1410 let _event_handler = enter_event_handler_scope();
1411 let app_context = Rc::clone(&self.shell.app.app_context);
1412 app_context.enter(|| {
1413 let handled = run_in_mutable_snapshot(|| {
1414 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1415 })
1416 .unwrap_or(false);
1417
1418 if handled {
1419 self.mark_dirty();
1420 }
1421
1422 handled
1423 })
1424 }
1425
1426 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1430 let app_context = Rc::clone(&self.shell.app.app_context);
1431 app_context.enter(cranpose_ui::text_field_focus::focused_editor_state)
1432 }
1433
1434 pub fn ime_caret_geometry(
1438 &mut self,
1439 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1440 let app_context = Rc::clone(&self.shell.app.app_context);
1441 app_context.enter(cranpose_ui::text_field_focus::focused_caret_geometry)
1442 }
1443
1444 pub fn clear_text_field_focus(&mut self) {
1447 let _event_handler = enter_event_handler_scope();
1448 let app_context = Rc::clone(&self.shell.app.app_context);
1449 app_context.enter(cranpose_ui::text_field_focus::clear_focus);
1450 self.mark_dirty();
1451 self.shell.app.request_layout_pass();
1452 }
1453
1454 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1457 if self.inspector_owns_keyboard() {
1458 return true;
1459 }
1460 let _event_handler = enter_event_handler_scope();
1461 let app_context = Rc::clone(&self.shell.app.app_context);
1462 app_context.enter(|| self.on_ime_delete_surrounding_inner(before_bytes, after_bytes))
1463 }
1464
1465 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1466 let handled = run_in_mutable_snapshot(|| {
1467 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1468 })
1469 .unwrap_or(false);
1470
1471 if handled {
1472 self.mark_dirty();
1473 self.shell.app.request_layout_pass();
1474 }
1475
1476 handled
1477 }
1478}
1479
1480impl<R> AppShell<R>
1481where
1482 R: Renderer,
1483 R::Error: Debug,
1484{
1485 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
1494 self.app.modifiers = Some(modifiers);
1495 }
1496
1497 pub fn modifiers(&self) -> Option<Modifiers> {
1501 self.app.modifiers
1502 }
1503
1504 pub fn rotary_scroll_factor(&self) -> f32 {
1507 self.app.rotary_scroll_factor
1508 }
1509
1510 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
1519 if factor.is_finite() && factor > 0.0 {
1520 self.app.rotary_scroll_factor = factor;
1521 }
1522 }
1523
1524 pub fn notify_app_paused(&mut self) {
1531 let app_context = Rc::clone(&self.app.app_context);
1532 app_context.enter(cranpose_ui::text_input_session::notify_app_paused);
1533 }
1534
1535 pub fn notify_app_resumed(&mut self) -> bool {
1543 let app_context = Rc::clone(&self.app.app_context);
1544 app_context.enter(cranpose_ui::text_input_session::notify_app_resumed)
1545 }
1546
1547 #[cfg(all(
1548 feature = "clipboard-native",
1549 target_os = "linux",
1550 not(target_arch = "wasm32")
1551 ))]
1552 pub fn set_primary_selection(&mut self, text: &str) {
1553 use arboard::{LinuxClipboardKind, SetExtLinux};
1554 if let Some(ref mut clipboard) = self.app.clipboard {
1555 let result = clipboard
1556 .set()
1557 .clipboard(LinuxClipboardKind::Primary)
1558 .text(text.to_string());
1559 if let Err(e) = result {
1560 log::debug!("Primary selection set failed: {:?}", e);
1561 }
1562 }
1563 }
1564
1565 #[cfg(not(all(
1566 feature = "clipboard-native",
1567 target_os = "linux",
1568 not(target_arch = "wasm32")
1569 )))]
1570 pub fn set_primary_selection(&mut self, _text: &str) {}
1571
1572 #[cfg(all(
1573 feature = "clipboard-native",
1574 target_os = "linux",
1575 not(target_arch = "wasm32")
1576 ))]
1577 pub fn get_primary_selection(&mut self) -> Option<String> {
1578 use arboard::{GetExtLinux, LinuxClipboardKind};
1579 if let Some(ref mut clipboard) = self.app.clipboard {
1580 clipboard
1581 .get()
1582 .clipboard(LinuxClipboardKind::Primary)
1583 .text()
1584 .ok()
1585 } else {
1586 None
1587 }
1588 }
1589
1590 #[cfg(not(all(
1591 feature = "clipboard-native",
1592 target_os = "linux",
1593 not(target_arch = "wasm32")
1594 )))]
1595 pub fn get_primary_selection(&mut self) -> Option<String> {
1596 None
1597 }
1598
1599 pub fn sync_selection_to_primary(&mut self) {
1602 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1603 {
1604 if let Some(text) = self.on_copy() {
1605 self.set_primary_selection(&text);
1606 }
1607 }
1608 }
1609
1610 pub fn set_pointer_source(&mut self, source: PointerSource) {
1612 self.primary().set_pointer_source(source);
1613 }
1614
1615 pub fn pointer_source(&self) -> PointerSource {
1617 self.surfaces[0].pointer_source
1618 }
1619
1620 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
1622 self.primary().set_cursor(x, y)
1623 }
1624
1625 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
1627 self.primary().set_cursor_at_time(x, y, time_ms)
1628 }
1629
1630 pub fn set_cursor_at_event_time(
1632 &mut self,
1633 x: f32,
1634 y: f32,
1635 event_time: PointerEventTime,
1636 ) -> bool {
1637 self.primary().set_cursor_at_event_time(x, y, event_time)
1638 }
1639
1640 pub fn pointer_pressed(&mut self) -> bool {
1642 self.primary().pointer_pressed()
1643 }
1644
1645 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
1647 self.primary().accessibility_activate(node_id, canvas_key)
1648 }
1649
1650 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
1652 self.primary().pointer_pressed_at_time(time_ms)
1653 }
1654
1655 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1657 self.primary().pointer_pressed_at_event_time(event_time)
1658 }
1659
1660 pub fn pointer_released(&mut self) -> bool {
1662 self.primary().pointer_released()
1663 }
1664
1665 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
1667 self.primary().pointer_released_at_position(x, y)
1668 }
1669
1670 pub fn pointer_released_at_position_time(
1673 &mut self,
1674 x: f32,
1675 y: f32,
1676 time_ms: Option<i64>,
1677 ) -> bool {
1678 self.primary()
1679 .pointer_released_at_position_time(x, y, time_ms)
1680 }
1681
1682 pub fn pointer_released_at_position_event_time(
1685 &mut self,
1686 x: f32,
1687 y: f32,
1688 event_time: PointerEventTime,
1689 ) -> bool {
1690 self.primary()
1691 .pointer_released_at_position_event_time(x, y, event_time)
1692 }
1693
1694 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
1696 self.primary().pointer_released_at_time(time_ms)
1697 }
1698
1699 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1701 self.primary().pointer_released_at_event_time(event_time)
1702 }
1703
1704 pub fn secondary_pointer_pressed(
1706 &mut self,
1707 pointer_id: u64,
1708 x: f32,
1709 y: f32,
1710 time_ms: Option<i64>,
1711 ) -> bool {
1712 self.primary()
1713 .secondary_pointer_pressed(pointer_id, x, y, time_ms)
1714 }
1715
1716 pub fn secondary_pointer_moved(
1718 &mut self,
1719 pointer_id: u64,
1720 x: f32,
1721 y: f32,
1722 time_ms: Option<i64>,
1723 ) -> bool {
1724 self.primary()
1725 .secondary_pointer_moved(pointer_id, x, y, time_ms)
1726 }
1727
1728 pub fn secondary_pointer_released(
1730 &mut self,
1731 pointer_id: u64,
1732 x: f32,
1733 y: f32,
1734 time_ms: Option<i64>,
1735 ) -> bool {
1736 self.primary()
1737 .secondary_pointer_released(pointer_id, x, y, time_ms)
1738 }
1739
1740 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
1742 self.primary().pointer_zoomed(zoom_factor)
1743 }
1744
1745 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
1747 self.primary().wheel_scrolled(wheel)
1748 }
1749
1750 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
1752 self.primary().pointer_scrolled(delta_x, delta_y)
1753 }
1754
1755 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
1757 where
1758 F: Fn(RotaryScrollEvent) -> bool + 'static,
1759 {
1760 self.primary().set_on_rotary_scroll(handler);
1761 }
1762
1763 pub fn clear_on_rotary_scroll(&mut self) {
1765 self.primary().clear_on_rotary_scroll();
1766 }
1767
1768 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
1770 self.primary()
1771 .rotary_scrolled_by_detents(detents, uptime_millis)
1772 }
1773
1774 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
1776 self.primary().rotary_scrolled(event)
1777 }
1778
1779 pub fn cancel_gesture(&mut self) {
1781 self.primary().cancel_gesture();
1782 }
1783
1784 pub fn cancel_gesture_unless_pressed(&mut self) {
1786 self.primary().cancel_gesture_unless_pressed();
1787 }
1788
1789 pub fn refresh_pointer_icon(&self) {
1793 self.surfaces[0].pointer_icon.refresh();
1794 }
1795
1796 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
1803 self.surfaces[0].pointer_icon.take_change()
1804 }
1805
1806 pub fn set_platform_text_input(
1813 &mut self,
1814 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
1815 ) {
1816 self.primary().set_platform_text_input(handler);
1817 }
1818
1819 pub fn dismiss_top_modal(&mut self) -> bool {
1821 self.primary().dismiss_top_modal()
1822 }
1823
1824 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1826 self.primary().move_focus_in_context(direction)
1827 }
1828
1829 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1831 self.primary().on_key_event(event)
1832 }
1833
1834 pub fn on_paste(&mut self, text: &str) -> bool {
1836 self.primary().on_paste(text)
1837 }
1838
1839 pub fn on_copy(&mut self) -> Option<String> {
1841 self.primary().on_copy()
1842 }
1843
1844 pub fn on_cut(&mut self) -> Option<String> {
1846 self.primary().on_cut()
1847 }
1848
1849 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1851 self.primary().on_ime_preedit(text, cursor)
1852 }
1853
1854 pub fn on_ime_finish_composing(&mut self) -> bool {
1856 self.primary().on_ime_finish_composing()
1857 }
1858
1859 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1861 self.primary()
1862 .on_ime_set_composing_region(start_bytes, end_bytes)
1863 }
1864
1865 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1867 self.primary().on_ime_set_selection(start_bytes, end_bytes)
1868 }
1869
1870 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1872 self.primary().ime_editor_state()
1873 }
1874
1875 pub fn ime_caret_geometry(
1877 &mut self,
1878 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1879 self.primary().ime_caret_geometry()
1880 }
1881
1882 pub fn clear_text_field_focus(&mut self) {
1884 self.primary().clear_text_field_focus();
1885 }
1886
1887 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1889 self.primary()
1890 .on_ime_delete_surrounding(before_bytes, after_bytes)
1891 }
1892}
1893
1894enum ActivationTarget {
1895 Direct(cranpose_foundation::SemanticsCustomAction),
1896 Edit(NodeId),
1897 Pointer(NodeId, Option<Rect>),
1898}
1899
1900fn activation_target(
1901 node: &cranpose_ui::SemanticsNode,
1902 node_id: NodeId,
1903 canvas_key: Option<u64>,
1904) -> Option<ActivationTarget> {
1905 if node.hidden {
1906 return None;
1907 }
1908 if node.node_id != node_id {
1909 return node
1910 .children
1911 .iter()
1912 .find_map(|child| activation_target(child, node_id, canvas_key));
1913 }
1914 if !node.enabled {
1915 return None;
1916 }
1917 if let Some(key) = canvas_key {
1918 let child = node.canvas_children.iter().find(|child| child.key == key)?;
1919 return (child.enabled && child.clickable)
1920 .then_some(ActivationTarget::Pointer(node_id, Some(child.bounds)));
1921 }
1922 if let Some(action) = &node.on_click {
1923 return Some(ActivationTarget::Direct(action.clone()));
1924 }
1925 if node.editable_text && node.focusable {
1926 return Some(ActivationTarget::Edit(node_id));
1927 }
1928 node.actions.first().map(|action| match action {
1929 cranpose_ui::SemanticsAction::Click { handler } => {
1930 ActivationTarget::Pointer(handler.node_id(), None)
1931 }
1932 })
1933}
1934
1935fn activation_input(
1936 root: &LayoutBox,
1937 node_id: NodeId,
1938 canvas_bounds: Option<Rect>,
1939) -> Option<(Point, Point, Rc<cranpose_ui::ModifierNodeSlices>)> {
1940 if root.node_id != node_id {
1941 return root
1942 .children
1943 .iter()
1944 .find_map(|child| activation_input(child, node_id, canvas_bounds));
1945 }
1946 let bounds = canvas_bounds.map_or(root.rect, |bounds| Rect {
1947 x: root.rect.x + bounds.x,
1948 y: root.rect.y + bounds.y,
1949 ..bounds
1950 });
1951 let position = Point {
1952 x: bounds.x + bounds.width * 0.5,
1953 y: bounds.y + bounds.height * 0.5,
1954 };
1955 let local = Point {
1956 x: position.x - root.rect.x,
1957 y: position.y - root.rect.y,
1958 };
1959 (bounds.width > 0.0 && bounds.height > 0.0 && position.x.is_finite() && position.y.is_finite())
1960 .then(|| (position, local, Rc::clone(&root.node_data.modifier_slices)))
1961}
1962
1963fn plain_key_down(event: &KeyEvent) -> bool {
1964 event.event_type == KeyEventType::KeyDown
1965 && !event.modifiers.ctrl
1966 && !event.modifiers.meta
1967 && !event.modifiers.alt
1968}