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(cranpose_render_common::HitTestTarget::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 {
116 self.set_cursor_at_time(x, y, None)
117 }
118
119 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
126 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
127 self.set_cursor_at_event_time(x, y, event_time)
128 }
129
130 pub fn set_cursor_at_event_time(
132 &mut self,
133 x: f32,
134 y: f32,
135 event_time: PointerEventTime,
136 ) -> bool {
137 let _event_handler = enter_event_handler_scope();
138 let result = {
139 let _app_context = self.shell.app.app_context.enter_scope();
140 run_in_mutable_snapshot(|| self.set_cursor_inner(x, y, event_time)).unwrap_or(false)
141 };
142 self.route_drag_and_drop();
143 if result {
144 self.mark_dirty();
145 }
146 log::trace!(
147 target: "cranpose::input",
148 "set_cursor ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
149 event_time.platform_time_ms,
150 event_time.animation_time_nanos,
151 );
152 result
153 }
154
155 fn set_cursor_inner(&mut self, x: f32, y: f32, event_time: PointerEventTime) -> bool {
156 self.surface_mut().cursor = (x, y);
157 if self.inspector_move(x, y) {
158 return true;
159 }
160
161 if self.surface().buttons_pressed != PointerButtons::NONE {
162 if self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
163 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
164 if !targets.is_empty() {
165 let event = self
166 .pointer_event(
167 PointerEventKind::Move,
168 Point { x, y },
169 Point { x, y },
170 event_time,
171 )
172 .with_buttons(self.surface().buttons_pressed)
173 .with_source(self.surface().pointer_source);
174 self.dispatch_targets(targets, event, false);
175 return true;
176 }
177
178 return false;
179 }
180
181 return false;
182 }
183
184 let hits = if self.inspector_blocks_pointer(x, y) {
185 Vec::new()
186 } else {
187 self.surface().renderer.scene().hit_test(x, y)
188 };
189 let new_ids: Vec<NodeId> = hits
190 .iter()
191 .map(cranpose_render_common::HitTestTarget::node_id)
192 .collect();
193
194 let pos = Point { x, y };
195 let previously_hovered = self.surface().hovered_nodes.clone();
196 for old_id in previously_hovered {
197 if !new_ids.contains(&old_id)
198 && let Some(target) = self.surface().renderer.scene().find_target(old_id)
199 {
200 let exit_event = self
201 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
202 .with_buttons(self.surface().buttons_pressed)
203 .with_source(self.surface().pointer_source);
204 self.dispatch_targets(std::iter::once(target), exit_event, false);
205 }
206 }
207
208 for hit in &hits {
209 if !self.surface().hovered_nodes.contains(&hit.node_id()) {
210 let enter_event = self
211 .pointer_event(PointerEventKind::Enter, pos, pos, event_time)
212 .with_buttons(self.surface().buttons_pressed)
213 .with_source(self.surface().pointer_source);
214 self.dispatch_targets(std::iter::once(hit.clone()), enter_event, false);
215 }
216 }
217
218 self.surface_mut().hovered_nodes = new_ids;
219 self.apply_hovered_pointer_icon(&hits);
220
221 if !hits.is_empty() {
222 let event = self
223 .pointer_event(PointerEventKind::Move, pos, pos, event_time)
224 .with_buttons(self.surface().buttons_pressed)
225 .with_source(self.surface().pointer_source);
226 self.dispatch_targets(hits, event, true);
227 true
228 } else {
229 false
230 }
231 }
232
233 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
239 let event_time = self.shell.app.realtime_pointer_event_time(None);
240 let _event_handler = enter_event_handler_scope();
241 let result = {
242 let _app_context = self.shell.app.app_context.enter_scope();
243 run_in_mutable_snapshot(|| self.activate_node(node_id, canvas_key, event_time))
244 .unwrap_or(false)
245 };
246 if result {
247 self.mark_dirty();
248 }
249 result
250 }
251
252 fn activate_node(
253 &mut self,
254 node_id: NodeId,
255 canvas_key: Option<u64>,
256 event_time: PointerEventTime,
257 ) -> bool {
258 let target = {
259 let (app, surface) = self.parts();
260 surface
261 .semantics_tree_for_input(app)
262 .and_then(|tree| activation_target(tree.root(), node_id, canvas_key))
263 };
264 let (target_id, canvas_bounds) = match target {
265 Some(ActivationTarget::Direct(action)) => {
266 action.invoke();
267 return true;
268 }
269 Some(ActivationTarget::Edit(node_id)) => {
270 self.activate();
271 return cranpose_ui::request_focus_from_platform(node_id);
272 }
273 Some(ActivationTarget::Pointer(node_id, bounds)) => (node_id, bounds),
274 None => return false,
275 };
276 let position =
277 self.with_layout_tree(|tree| activation_input(tree?.root(), target_id, canvas_bounds));
278 let Some((position, local, handlers)) = position else {
279 return false;
280 };
281 for kind in [PointerEventKind::Down, PointerEventKind::Up] {
282 let event = self.pointer_event(kind, local, position, event_time);
283 handlers.dispatch_pointer_event(event.clone());
284 event.finish_post_dispatch();
285 }
286 true
287 }
288
289 pub fn pointer_pressed(&mut self) -> bool {
292 self.pointer_pressed_at_time(None)
293 }
294
295 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
298 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
299 self.pointer_pressed_at_event_time(event_time)
300 }
301
302 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
304 let (cursor_x, cursor_y) = self.surface().cursor;
305 if self.inspector_press(cursor_x, cursor_y) {
306 return true;
307 }
308 if self.dev_overlay_press(cursor_x, cursor_y) {
309 return true;
310 }
311 let _event_handler = enter_event_handler_scope();
312 let result = {
313 let _app_context = self.shell.app.app_context.enter_scope();
314 run_in_mutable_snapshot(|| self.pointer_pressed_inner(event_time)).unwrap_or(false)
315 };
316 if result {
317 self.mark_dirty();
318 }
319 log::trace!(
320 target: "cranpose::input",
321 "pointer_pressed time_ms={:?} animation_time_nanos={} -> {result}",
322 event_time.platform_time_ms,
323 event_time.animation_time_nanos,
324 );
325 result
326 }
327
328 fn pointer_pressed_inner(&mut self, event_time: PointerEventTime) -> bool {
329 self.activate();
330 self.note_focus_moved_by_keyboard(false);
331 self.surface_mut()
332 .buttons_pressed
333 .insert(PointerButton::Primary);
334
335 let (cursor_x, cursor_y) = self.surface().cursor;
336 let mut hits = self.surface().renderer.scene().hit_test(cursor_x, cursor_y);
337 if self.surface().pointer_source.is_touch_like()
338 && let Some(near) = self
339 .surface()
340 .renderer
341 .scene()
342 .hit_test_near(cursor_x, cursor_y)
343 && hits.iter().all(|hit| {
344 hit.node_id() != near.node_id() && near.capture_path().contains(&hit.node_id())
345 })
346 {
347 hits.insert(0, near);
348 }
349 let event = self.cursor_event(PointerEventKind::Down, event_time);
350 self.press_hits(hits, event, PointerId::PRIMARY)
351 }
352
353 fn cursor_event(&self, kind: PointerEventKind, event_time: PointerEventTime) -> PointerEvent {
356 let (x, y) = self.surface().cursor;
357 self.pointer_event(kind, Point { x, y }, Point { x, y }, event_time)
358 .with_buttons(self.surface().buttons_pressed)
359 .with_source(self.surface().pointer_source)
360 }
361
362 fn press_hits(
365 &mut self,
366 hits: Vec<<<R as Renderer>::Scene as RenderScene>::HitTarget>,
367 event: PointerEvent,
368 pointer: PointerId,
369 ) -> bool {
370 if hits.is_empty() {
371 self.surface_mut().hit_path_tracker.remove_path(pointer);
372 return false;
373 }
374 let mut delivered_capture_paths = Vec::new();
375 let (app, surface) = self.parts();
376 let mut applier = app.composition.applier_mut();
377 for hit in hits {
378 let node_id = hit.node_id();
379 delivered_capture_paths.push(hit.capture_path());
380 hit.dispatch_with_applier(&mut applier, event.clone());
381 log::trace!(
382 target: "cranpose::input",
383 "dispatch {:?} node={} consumed={} stop_on_consume=true",
384 event.kind,
385 node_id,
386 event.is_consumed(),
387 );
388 if event.is_consumed() {
389 break;
390 }
391 }
392
393 surface
394 .hit_path_tracker
395 .add_hit_path(pointer, delivered_capture_paths);
396 log::trace!(
397 target: "cranpose::input",
398 "press cached_hit_path={:?}",
399 surface.hit_path_tracker.get_path(pointer),
400 );
401 true
402 }
403
404 pub fn mouse_button_pressed(&mut self, button: PointerButton) -> bool {
411 if button == PointerButton::Primary {
412 return self.pointer_pressed();
413 }
414 self.mouse_button_change(|surface, event_time| {
415 surface.other_button_pressed(button, event_time)
416 })
417 }
418
419 pub fn mouse_button_released(&mut self, button: PointerButton) -> bool {
424 if button == PointerButton::Primary {
425 return self.pointer_released();
426 }
427 self.mouse_button_change(|surface, event_time| {
428 surface.other_button_released(button, event_time)
429 })
430 }
431
432 fn mouse_button_change(
433 &mut self,
434 change: impl FnOnce(&mut Self, PointerEventTime) -> bool,
435 ) -> bool {
436 let event_time = self.shell.app.realtime_pointer_event_time(None);
437 let _event_handler = enter_event_handler_scope();
438 let result = {
439 let _app_context = self.shell.app.app_context.enter_scope();
440 run_in_mutable_snapshot(|| change(self, event_time)).unwrap_or(false)
441 };
442 if result {
443 self.mark_dirty();
444 }
445 result
446 }
447
448 fn other_button_pressed(
449 &mut self,
450 button: PointerButton,
451 event_time: PointerEventTime,
452 ) -> bool {
453 self.activate();
454 let first = self.surface().buttons_pressed == PointerButtons::NONE;
455 self.surface_mut().buttons_pressed.insert(button);
456 let event = self.cursor_event(PointerEventKind::ButtonDown(button), event_time);
457 if first {
458 let (x, y) = self.surface().cursor;
459 let hits = self.surface().renderer.scene().hit_test(x, y);
460 return self.press_hits(hits, event, PointerId::PRIMARY);
461 }
462 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
463 if targets.is_empty() {
464 return false;
465 }
466 self.dispatch_targets(targets, event, false);
467 true
468 }
469
470 fn other_button_released(
471 &mut self,
472 button: PointerButton,
473 event_time: PointerEventTime,
474 ) -> bool {
475 if !self.surface().buttons_pressed.contains(button) {
476 return false;
477 }
478 self.surface_mut().buttons_pressed.remove(button);
479 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
480 if self.surface().buttons_pressed == PointerButtons::NONE {
481 self.surface_mut()
482 .hit_path_tracker
483 .remove_path(PointerId::PRIMARY);
484 }
485 if targets.is_empty() {
486 return false;
487 }
488 let event = self.cursor_event(PointerEventKind::ButtonUp(button), event_time);
489 self.dispatch_targets(targets, event, false);
490 true
491 }
492
493 pub fn pointer_released(&mut self) -> bool {
496 self.pointer_released_at_time(None)
497 }
498
499 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
512 self.pointer_released_at_position_time(x, y, None)
513 }
514
515 pub fn pointer_released_at_position_time(
519 &mut self,
520 x: f32,
521 y: f32,
522 time_ms: Option<i64>,
523 ) -> bool {
524 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
525 self.pointer_released_at_position_event_time(x, y, event_time)
526 }
527
528 pub fn pointer_released_at_position_event_time(
530 &mut self,
531 x: f32,
532 y: f32,
533 event_time: PointerEventTime,
534 ) -> bool {
535 let _event_handler = enter_event_handler_scope();
536 let result = {
537 let _app_context = self.shell.app.app_context.enter_scope();
538 run_in_mutable_snapshot(|| {
539 self.surface_mut().cursor = (x, y);
540 self.pointer_released_inner(event_time)
541 })
542 .unwrap_or(false)
543 };
544 self.route_drag_and_drop();
545 if result {
546 self.mark_dirty();
547 }
548 log::trace!(
549 target: "cranpose::input",
550 "pointer_released_at_position ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
551 event_time.platform_time_ms,
552 event_time.animation_time_nanos,
553 );
554 result
555 }
556
557 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
560 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
561 self.pointer_released_at_event_time(event_time)
562 }
563
564 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
566 let _event_handler = enter_event_handler_scope();
567 let result = {
568 let _app_context = self.shell.app.app_context.enter_scope();
569 run_in_mutable_snapshot(|| self.pointer_released_inner(event_time)).unwrap_or(false)
570 };
571 self.route_drag_and_drop();
572 if result {
573 self.mark_dirty();
574 }
575 log::trace!(
576 target: "cranpose::input",
577 "pointer_released time_ms={:?} animation_time_nanos={} -> {result}",
578 event_time.platform_time_ms,
579 event_time.animation_time_nanos,
580 );
581 result
582 }
583
584 fn pointer_released_inner(&mut self, event_time: PointerEventTime) -> bool {
585 if self.surface_mut().inspector.release_pointer() {
586 return true;
587 }
588 self.release_app_pointer(event_time)
589 }
590
591 fn release_app_pointer(&mut self, event_time: PointerEventTime) -> bool {
592 self.surface_mut()
593 .buttons_pressed
594 .remove(PointerButton::Primary);
595 let corrected_buttons = self.surface().buttons_pressed;
596 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
597
598 if corrected_buttons == PointerButtons::NONE {
601 self.surface_mut()
602 .hit_path_tracker
603 .remove_path(PointerId::PRIMARY);
604 }
605
606 if !targets.is_empty() {
607 let event = self
608 .pointer_event(
609 PointerEventKind::Up,
610 Point {
611 x: self.surface().cursor.0,
612 y: self.surface().cursor.1,
613 },
614 Point {
615 x: self.surface().cursor.0,
616 y: self.surface().cursor.1,
617 },
618 event_time,
619 )
620 .with_buttons(corrected_buttons)
621 .with_source(self.surface().pointer_source);
622
623 self.dispatch_targets(targets, event, false);
624 true
625 } else {
626 false
627 }
628 }
629
630 pub fn secondary_pointer_pressed(
639 &mut self,
640 pointer_id: u64,
641 x: f32,
642 y: f32,
643 time_ms: Option<i64>,
644 ) -> bool {
645 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
646 self.dispatch_secondary_pointer(PointerEventKind::Down, pointer_id, x, y, event_time)
647 }
648
649 pub fn secondary_pointer_moved(
651 &mut self,
652 pointer_id: u64,
653 x: f32,
654 y: f32,
655 time_ms: Option<i64>,
656 ) -> bool {
657 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
658 self.dispatch_secondary_pointer(PointerEventKind::Move, pointer_id, x, y, event_time)
659 }
660
661 pub fn secondary_pointer_released(
663 &mut self,
664 pointer_id: u64,
665 x: f32,
666 y: f32,
667 time_ms: Option<i64>,
668 ) -> bool {
669 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
670 self.dispatch_secondary_pointer(PointerEventKind::Up, pointer_id, x, y, event_time)
671 }
672
673 fn dispatch_secondary_pointer(
674 &mut self,
675 kind: PointerEventKind,
676 pointer_id: u64,
677 x: f32,
678 y: f32,
679 event_time: PointerEventTime,
680 ) -> bool {
681 if pointer_id == 0 {
682 log::warn!(
683 target: "cranpose::input",
684 "secondary pointer dispatch called with the primary pointer id"
685 );
686 return false;
687 }
688
689 let _event_handler = enter_event_handler_scope();
690 let result = {
691 let _app_context = self.shell.app.app_context.enter_scope();
692 run_in_mutable_snapshot(|| {
693 if !self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
694 return false;
695 }
696 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
697 if targets.is_empty() {
698 return false;
699 }
700 let pos = Point { x, y };
701 let event = self
702 .pointer_event(kind, pos, pos, event_time)
703 .with_buttons(self.surface().buttons_pressed)
704 .with_id(pointer_id)
705 .with_source(self.surface().pointer_source);
706 self.dispatch_targets(targets, event, false);
707 true
708 })
709 .unwrap_or(false)
710 };
711 if result {
712 self.mark_dirty();
713 }
714 log::trace!(
715 target: "cranpose::input",
716 "secondary_pointer {kind:?} id={pointer_id} ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
717 event_time.platform_time_ms,
718 event_time.animation_time_nanos,
719 );
720 result
721 }
722
723 pub fn ray_pointer(
732 &mut self,
733 (pointer_id, scene): (u32, NodeId),
734 ray: cranpose_ui_graphics::space::Ray,
735 kind: PointerEventKind,
736 ) -> bool {
737 if pointer_id == 0 {
738 log::warn!(
739 target: "cranpose::input",
740 "ray pointer dispatch called with the primary pointer id"
741 );
742 return false;
743 }
744 let event_time = self.shell.app.realtime_pointer_event_time(None);
745 let _event_handler = enter_event_handler_scope();
746 let result = {
747 let _app_context = self.shell.app.app_context.enter_scope();
748 run_in_mutable_snapshot(|| {
749 let pointer = PointerId(pointer_id);
750 let event = self
751 .pointer_event(kind, Point::default(), Point::default(), event_time)
752 .with_id(u64::from(pointer_id))
753 .with_ray(ray);
754 if kind == PointerEventKind::Down {
755 let hits = self.surface().renderer.scene().hit_test_ray(scene, ray);
756 return self.press_hits(hits, event, pointer);
757 }
758 let targets = self.resolve_gesture_targets(pointer);
759 if matches!(kind, PointerEventKind::Up | PointerEventKind::Cancel) {
760 self.surface_mut().hit_path_tracker.remove_path(pointer);
761 }
762 if targets.is_empty() {
763 return false;
764 }
765 self.dispatch_targets(targets, event, false);
766 true
767 })
768 .unwrap_or(false)
769 };
770 if result {
771 self.mark_dirty();
772 }
773 result
774 }
775
776 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
782 let event_time = self.shell.app.realtime_pointer_event_time(None);
783 let _event_handler = enter_event_handler_scope();
784 let result = {
785 let _app_context = self.shell.app.app_context.enter_scope();
786 run_in_mutable_snapshot(|| self.pointer_zoomed_inner(zoom_factor, event_time))
787 .unwrap_or(false)
788 };
789 if result {
790 self.mark_dirty();
791 }
792 log::trace!(
793 target: "cranpose::input",
794 "pointer_zoomed factor={zoom_factor:.4} -> {result}"
795 );
796 result
797 }
798
799 fn pointer_zoomed_inner(&mut self, zoom_factor: f32, event_time: PointerEventTime) -> bool {
800 if !zoom_factor.is_finite() || zoom_factor <= 0.0 || zoom_factor == 1.0 {
801 return false;
802 }
803
804 let hits = self
805 .surface()
806 .renderer
807 .scene()
808 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
809 if hits.is_empty() {
810 return false;
811 }
812
813 let pos = Point {
814 x: self.surface().cursor.0,
815 y: self.surface().cursor.1,
816 };
817 let event = self
818 .pointer_event(PointerEventKind::Zoom, pos, pos, event_time)
819 .with_buttons(self.surface().buttons_pressed)
820 .with_zoom_delta(zoom_factor)
821 .with_source(self.surface().pointer_source);
822
823 self.dispatch_to_hits(&hits, event)
824 }
825
826 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
851 if self.inspector_scroll(wheel.delta.y) {
852 return true;
853 }
854 if wheel.is_zoom() {
855 let zoom_factor = wheel.zoom_factor();
856 log::trace!(
857 target: "cranpose::input",
858 "wheel zoom factor={zoom_factor:.4}"
859 );
860 return self.pointer_zoomed(zoom_factor);
861 }
862
863 let rotary =
864 RotaryScrollEvent::from_wheel_pixels(wheel.delta.y, wheel.delta.x, wheel.uptime_millis);
865 if self.rotary_scrolled(rotary) {
866 return true;
867 }
868
869 let delta = wheel.scroll_delta();
870 log::trace!(
871 target: "cranpose::input",
872 "wheel delta ({:.2},{:.2}) alt={}",
873 delta.x,
874 delta.y,
875 wheel.modifiers.alt
876 );
877 self.pointer_scrolled(delta.x, delta.y)
878 }
879
880 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
888 if self.inspector_scroll(delta_y) {
889 return true;
890 }
891 let event_time = self.shell.app.realtime_pointer_event_time(None);
892 let _event_handler = enter_event_handler_scope();
893 let result = {
894 let _app_context = self.shell.app.app_context.enter_scope();
895 run_in_mutable_snapshot(|| self.pointer_scrolled_inner(delta_x, delta_y, event_time))
896 .unwrap_or(false)
897 };
898 if result {
899 self.mark_dirty();
900 }
901 log::trace!(
902 target: "cranpose::input",
903 "pointer_scrolled ({delta_x:.2},{delta_y:.2}) -> {result}"
904 );
905 result
906 }
907
908 fn pointer_scrolled_inner(
909 &mut self,
910 delta_x: f32,
911 delta_y: f32,
912 event_time: PointerEventTime,
913 ) -> bool {
914 if delta_x.abs() <= f32::EPSILON && delta_y.abs() <= f32::EPSILON {
915 return false;
916 }
917
918 let hits = self
919 .surface()
920 .renderer
921 .scene()
922 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
923 if hits.is_empty() {
924 return false;
925 }
926
927 let event = self
928 .pointer_event(
929 PointerEventKind::Scroll,
930 Point {
931 x: self.surface().cursor.0,
932 y: self.surface().cursor.1,
933 },
934 Point {
935 x: self.surface().cursor.0,
936 y: self.surface().cursor.1,
937 },
938 event_time,
939 )
940 .with_buttons(self.surface().buttons_pressed)
941 .with_scroll_delta(Point {
942 x: delta_x,
943 y: delta_y,
944 })
945 .with_source(self.surface().pointer_source);
946
947 self.dispatch_to_hits(&hits, event)
948 }
949
950 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
961 where
962 F: Fn(RotaryScrollEvent) -> bool + 'static,
963 {
964 self.surface_mut().on_rotary_scroll = Some(Rc::new(handler));
965 }
966
967 pub fn clear_on_rotary_scroll(&mut self) {
969 self.surface_mut().on_rotary_scroll = None;
970 }
971
972 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
978 let factor = self.shell.app.rotary_scroll_factor;
979 self.rotary_scrolled(RotaryScrollEvent::from_detents(
980 detents,
981 factor,
982 factor,
983 uptime_millis,
984 ))
985 }
986
987 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
1006 let _event_handler = enter_event_handler_scope();
1007 let result = {
1008 let _app_context = self.shell.app.app_context.enter_scope();
1009 run_in_mutable_snapshot(|| self.rotary_scrolled_inner(event)).unwrap_or(false)
1010 };
1011 if result {
1012 self.mark_dirty();
1013 }
1014 log::trace!(
1015 target: "cranpose::input",
1016 "rotary_scrolled v={:.2} h={:.2} uptime={} -> {result}",
1017 event.vertical_scroll_pixels,
1018 event.horizontal_scroll_pixels,
1019 event.uptime_millis,
1020 );
1021 result
1022 }
1023
1024 fn rotary_scrolled_inner(&mut self, rotary: RotaryScrollEvent) -> bool {
1025 if rotary.is_empty() {
1026 return false;
1027 }
1028
1029 let bubble_order = self.rotary_dispatch_order();
1030 let position = Point {
1031 x: self.surface().cursor.0,
1032 y: self.surface().cursor.1,
1033 };
1034
1035 if !bubble_order.is_empty() {
1036 let capture_targets = bubble_order
1037 .iter()
1038 .rev()
1039 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
1040 .collect::<Vec<_>>();
1041 let mut capture_event =
1042 PointerEvent::rotary(PointerEventKind::RotaryScrollPre, rotary, position);
1043 capture_event.modifiers = self.shell.app.modifiers;
1044 self.dispatch_targets(capture_targets, capture_event.clone(), true);
1045 if capture_event.is_consumed() {
1046 return true;
1047 }
1048
1049 let bubble_targets = bubble_order
1050 .iter()
1051 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
1052 .collect::<Vec<_>>();
1053 let mut bubble_event =
1054 PointerEvent::rotary(PointerEventKind::RotaryScroll, rotary, position);
1055 bubble_event.modifiers = self.shell.app.modifiers;
1056 self.dispatch_targets(bubble_targets, bubble_event.clone(), true);
1057 if bubble_event.is_consumed() {
1058 return true;
1059 }
1060 }
1061
1062 if let Some(handler) = self.surface().on_rotary_scroll.clone() {
1063 return handler(rotary);
1064 }
1065
1066 false
1067 }
1068
1069 fn rotary_dispatch_order(&self) -> Vec<NodeId> {
1070 if let Some(focused) = cranpose_ui::active_focus_target()
1071 && let Some(target) = self.surface().renderer.scene().find_target(focused)
1072 {
1073 let path = target.capture_path();
1074 if !path.is_empty() {
1075 return crate::hit_path_tracker::dispatch_order_for_paths(&[path]);
1076 }
1077 }
1078
1079 let hits = self
1080 .surface()
1081 .renderer
1082 .scene()
1083 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
1084 if hits.is_empty() {
1085 return Vec::new();
1086 }
1087 let capture_paths = hits
1088 .iter()
1089 .map(cranpose_render_common::HitTestTarget::capture_path)
1090 .collect::<Vec<_>>();
1091 crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
1092 }
1093
1094 pub fn cancel_gesture(&mut self) {
1100 let event_time = self.shell.app.realtime_pointer_event_time(None);
1101 let _event_handler = enter_event_handler_scope();
1102 let _ = {
1103 let _app_context = self.shell.app.app_context.enter_scope();
1104 run_in_mutable_snapshot(|| {
1105 self.cancel_gesture_inner(event_time);
1106 })
1107 };
1108 self.route_drag_and_drop();
1109 }
1110
1111 pub(crate) fn give_press_to(&mut self, node: NodeId) {
1117 let holds = self
1118 .surface()
1119 .hit_path_tracker
1120 .get_path(PointerId::PRIMARY)
1121 .is_some_and(|paths| paths.iter().any(|path| path.contains(&node)));
1122 if !holds {
1123 return;
1124 }
1125 let event_time = self.shell.app.realtime_pointer_event_time(None);
1126 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
1127 self.surface_mut()
1128 .hit_path_tracker
1129 .add_hit_path(PointerId::PRIMARY, vec![vec![node]]);
1130 let cursor = Point {
1131 x: self.surface().cursor.0,
1132 y: self.surface().cursor.1,
1133 };
1134 let event = self
1135 .pointer_event(PointerEventKind::Cancel, cursor, cursor, event_time)
1136 .with_source(self.surface().pointer_source);
1137 let _event_handler = enter_event_handler_scope();
1138 let _ = run_in_mutable_snapshot(|| self.dispatch_targets(targets, event, false));
1139 }
1140
1141 fn route_drag_and_drop(&mut self) {
1142 let source = self.index;
1143 let shell = &*self.shell;
1144 let app_context = &shell.app.app_context;
1145 let routed = app_context.enter(|| {
1146 run_in_mutable_snapshot(|| {
1147 app_context
1148 .drag_and_drop()
1149 .route(|point| shell.drag_and_drop_target_at(point, source))
1150 })
1151 .unwrap_or(false)
1152 });
1153 if routed {
1154 self.shell.mark_all_dirty();
1155 }
1156 }
1157
1158 pub fn cancel_gesture_unless_pressed(&mut self) {
1167 if self.surface().buttons_pressed != PointerButtons::NONE
1168 || self.surface().inspector.pointer_captured
1169 {
1170 return;
1171 }
1172 self.cancel_gesture();
1173 }
1174
1175 fn cancel_gesture_inner(&mut self, event_time: PointerEventTime) {
1176 self.surface_mut().inspector.cancel_pointer();
1177 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
1178
1179 self.surface_mut().hit_path_tracker.clear();
1180 self.surface_mut().buttons_pressed = PointerButtons::NONE;
1181
1182 if !targets.is_empty() {
1183 let event = self
1184 .pointer_event(
1185 PointerEventKind::Cancel,
1186 Point {
1187 x: self.surface().cursor.0,
1188 y: self.surface().cursor.1,
1189 },
1190 Point {
1191 x: self.surface().cursor.0,
1192 y: self.surface().cursor.1,
1193 },
1194 event_time,
1195 )
1196 .with_source(self.surface().pointer_source);
1197
1198 self.dispatch_targets(targets, event, false);
1199 }
1200
1201 let pos = Point {
1202 x: self.surface().cursor.0,
1203 y: self.surface().cursor.1,
1204 };
1205 let hovered_nodes = self.surface().hovered_nodes.clone();
1206 for node_id in hovered_nodes {
1207 if let Some(target) = self.surface().renderer.scene().find_target(node_id) {
1208 let exit_event = self
1209 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
1210 .with_source(self.surface().pointer_source);
1211 self.dispatch_targets(std::iter::once(target), exit_event, false);
1212 }
1213 }
1214 self.surface_mut().hovered_nodes.clear();
1215 self.surface().pointer_icon.set(PointerIcon::DEFAULT);
1216 }
1217
1218 fn apply_hovered_pointer_icon(
1219 &self,
1220 hits: &[<<R as Renderer>::Scene as RenderScene>::HitTarget],
1221 ) {
1222 let icon = hits
1223 .iter()
1224 .find_map(HitTestTarget::pointer_icon)
1225 .unwrap_or(PointerIcon::DEFAULT);
1226 self.surface().pointer_icon.set(icon);
1227 }
1228
1229 fn on_focus_key(&mut self, event: &KeyEvent) -> bool {
1230 if !plain_key_down(event) || event.key_code != KeyCode::Tab {
1231 return false;
1232 }
1233 self.on_group_navigation_key(event)
1234 }
1235
1236 fn on_group_navigation_key(&mut self, event: &KeyEvent) -> bool {
1237 let focused = cranpose_ui::active_focus_target();
1238 let target = self.with_layout_tree(|tree| {
1239 cranpose_ui::keyboard_focus_target(
1240 tree?,
1241 focused,
1242 event.key_code,
1243 event.modifiers.shift,
1244 )
1245 });
1246 let Some(target) = target else {
1247 return false;
1248 };
1249 let moved =
1250 run_in_mutable_snapshot(|| cranpose_ui::request_focus_from_platform(target.node_id))
1251 .unwrap_or(false);
1252 if moved {
1253 self.mark_dirty();
1254 self.note_focus_moved_by_keyboard(true);
1255 if target.activate {
1256 self.on_activation_key(&KeyEvent::key_down(KeyCode::Space, " "));
1257 }
1258 }
1259 moved
1260 }
1261
1262 fn on_activation_key(&mut self, event: &KeyEvent) -> bool {
1263 if !plain_key_down(event)
1264 || !matches!(event.key_code, KeyCode::Enter | KeyCode::Space)
1265 || cranpose_ui::text_field_focus::has_focused_field()
1266 {
1267 return false;
1268 }
1269 let Some(focused) = cranpose_ui::active_focus_target() else {
1270 return false;
1271 };
1272 let activated = self.accessibility_activate(focused, None);
1273 self.note_focus_moved_by_keyboard(true);
1274 activated
1275 }
1276
1277 fn on_arrow_key(&mut self, event: &KeyEvent) -> bool {
1278 if !plain_key_down(event) || cranpose_ui::text_field_focus::has_focused_field() {
1279 return false;
1280 }
1281 if self.on_group_navigation_key(event) {
1282 return true;
1283 }
1284 let direction = match event.key_code {
1285 KeyCode::ArrowLeft => FocusDirection::Left,
1286 KeyCode::ArrowRight => FocusDirection::Right,
1287 KeyCode::ArrowUp => FocusDirection::Up,
1288 KeyCode::ArrowDown => FocusDirection::Down,
1289 _ => return false,
1290 };
1291 let Some(focused) = cranpose_ui::active_focus_target() else {
1292 return false;
1293 };
1294 let order = self.with_layout_tree(|layout_tree| {
1295 let layout_tree = layout_tree?;
1296 let group = cranpose_ui::selectable_group_of(layout_tree, focused)?;
1297 Some(cranpose_ui::collect_focus_order_under(layout_tree, group))
1298 });
1299 order.is_some_and(|order| self.move_focus_in_order(order, direction))
1300 }
1301
1302 fn note_focus_moved_by_keyboard(&mut self, keyboard: bool) {
1303 if cranpose_ui::set_keyboard_focus_visible(keyboard) {
1304 cranpose_ui::request_render_invalidation();
1305 self.mark_dirty();
1306 }
1307 }
1308
1309 fn on_escape_key(&mut self, event: &KeyEvent) -> bool {
1310 if event.event_type != KeyEventType::KeyDown || event.key_code != KeyCode::Escape {
1311 return false;
1312 }
1313 self.dismiss_top_modal_in_context()
1314 }
1315
1316 pub fn dismiss_top_modal(&mut self) -> bool {
1319 let _event_handler = enter_event_handler_scope();
1320 let _app_context = self.shell.app.app_context.enter_scope();
1321 self.dismiss_top_modal_in_context()
1322 }
1323
1324 fn dismiss_top_modal_in_context(&mut self) -> bool {
1325 let closed = run_in_mutable_snapshot(|| {
1326 if cranpose_ui::modal_depth() > 0 {
1327 cranpose_ui::dispatch_modal_back()
1328 } else {
1329 cranpose_ui::dismiss_top_popup()
1330 }
1331 })
1332 .unwrap_or(false);
1333 if closed {
1334 self.mark_dirty();
1335 }
1336 closed
1337 }
1338
1339 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1342 let order = self.with_layout_tree(|layout_tree| {
1343 layout_tree
1344 .map(cranpose_ui::collect_focus_order)
1345 .unwrap_or_default()
1346 });
1347 self.move_focus_in_order(order, direction)
1348 }
1349
1350 fn move_focus_in_order(
1351 &mut self,
1352 order: Vec<cranpose_ui::FocusEntry>,
1353 direction: FocusDirection,
1354 ) -> bool {
1355 cranpose_ui::set_focus_order(order);
1356 let moved = run_in_mutable_snapshot(|| cranpose_ui::FocusManager.move_focus(direction))
1357 .unwrap_or(false);
1358 if moved {
1359 self.mark_dirty();
1360 self.note_focus_moved_by_keyboard(true);
1361 }
1362 moved
1363 }
1364
1365 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1368 let _event_handler = enter_event_handler_scope();
1369 let _app_context = self.shell.app.app_context.enter_scope();
1370 let handled = self.on_key_event_inner(event);
1371 self.run_requested_focus_move();
1372 handled
1373 }
1374
1375 fn run_requested_focus_move(&mut self) {
1377 if let Some(direction) = cranpose_ui::take_requested_focus_move() {
1378 self.move_focus_in_context(direction);
1379 }
1380 }
1381
1382 fn on_key_event_inner(&mut self, event: &KeyEvent) -> bool {
1383 if self.inspector_key(event) {
1384 return true;
1385 }
1386 let root = self.key_event_root();
1387 if root.is_none() && self.id() != RootId::Primary {
1388 return false;
1389 }
1390 let route =
1391 cranpose_ui::KeyEventRoute::new(&mut self.shell.app.composition.applier_mut(), root);
1392 if self.dispatch_modifier_key(&route, event, true) {
1393 return true;
1394 }
1395 if route.focus_is_current() && self.on_focused_key_event(event) {
1396 return true;
1397 }
1398 if self.dispatch_modifier_key(&route, event, false) {
1399 return true;
1400 }
1401 if !route.focus_is_current() || cranpose_ui::text_field_focus::has_focused_field() {
1402 return false;
1403 }
1404 let handled = run_in_mutable_snapshot(|| cranpose_ui::dispatch_unhandled_key_event(event))
1405 .unwrap_or(false);
1406 if handled {
1407 self.mark_dirty();
1408 self.shell.app.request_layout_pass();
1409 }
1410 handled
1411 }
1412
1413 fn dispatch_modifier_key(
1414 &mut self,
1415 route: &cranpose_ui::KeyEventRoute,
1416 event: &KeyEvent,
1417 preview: bool,
1418 ) -> bool {
1419 let handled = run_in_mutable_snapshot(|| {
1420 let mut applier = self.shell.app.composition.applier_mut();
1421 if preview {
1422 route.dispatch_preview(&mut applier, event)
1423 } else {
1424 route.dispatch_bubble(&mut applier, event)
1425 }
1426 })
1427 .unwrap_or(false);
1428 if handled {
1429 self.mark_dirty();
1430 self.shell.app.request_layout_pass();
1431 }
1432 handled
1433 }
1434
1435 fn key_event_root(&self) -> Option<cranpose_core::NodeId> {
1436 self.surface()
1437 .modal_focus
1438 .last()
1439 .map(|entry| entry.0)
1440 .or_else(|| self.surface().root)
1441 .or_else(|| match self.id() {
1442 RootId::Primary => self.shell.app.composition.root(),
1443 RootId::Window(_) => None,
1444 })
1445 }
1446
1447 fn receives_focused_input(&mut self) -> bool {
1448 let root = self.key_event_root();
1449 cranpose_ui::KeyEventRoute::contains_focus(
1450 &mut self.shell.app.composition.applier_mut(),
1451 root,
1452 )
1453 }
1454
1455 fn on_focused_key_event(&mut self, event: &KeyEvent) -> bool {
1456 use KeyEventType::KeyDown;
1457
1458 if event.event_type == KeyDown && event.modifiers.command_or_ctrl() {
1459 #[cfg(all(
1460 feature = "clipboard-native",
1461 not(target_arch = "wasm32"),
1462 not(target_os = "android"),
1463 not(target_os = "ios")
1464 ))]
1465 {
1466 match event.key_code {
1467 KeyCode::C => {
1468 if let Some(text) = self.on_copy_inner() {
1469 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1470 return true;
1471 }
1472 }
1473 KeyCode::X => {
1474 if let Some(text) = self.on_cut_inner() {
1475 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1476 self.mark_dirty();
1477 self.shell.app.request_layout_pass();
1478 return true;
1479 }
1480 }
1481 KeyCode::V => {
1482 if let Some(text) = cranpose_ui::clipboard_session::clipboard_read_text()
1483 && self.on_paste_inner(&text)
1484 {
1485 return true;
1486 }
1487 }
1488 _ => {}
1489 }
1490 }
1491 }
1492
1493 if self.on_focus_key(event) {
1494 return true;
1495 }
1496
1497 if self.on_escape_key(event) {
1498 return true;
1499 }
1500
1501 if self.on_activation_key(event) || self.on_arrow_key(event) {
1502 return true;
1503 }
1504
1505 let handled =
1506 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_key_event(event))
1507 .unwrap_or(false);
1508
1509 if handled {
1510 self.mark_dirty();
1511 self.shell.app.request_layout_pass();
1512 }
1513
1514 handled
1515 }
1516
1517 pub fn on_paste(&mut self, text: &str) -> bool {
1520 if self.inspector_owns_keyboard() {
1521 return true;
1522 }
1523 let _event_handler = enter_event_handler_scope();
1524 let _app_context = self.shell.app.app_context.enter_scope();
1525 self.on_paste_inner(text)
1526 }
1527
1528 fn on_paste_inner(&mut self, text: &str) -> bool {
1529 if !self.receives_focused_input() {
1530 return false;
1531 }
1532 let handled =
1533 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1534 .unwrap_or(false);
1535
1536 if handled {
1537 self.mark_dirty();
1538 self.shell.app.request_layout_pass();
1539 }
1540
1541 handled
1542 }
1543
1544 pub fn on_copy(&mut self) -> Option<String> {
1547 if self.inspector_owns_keyboard() {
1548 return None;
1549 }
1550 let _app_context = self.shell.app.app_context.enter_scope();
1551 self.on_copy_inner()
1552 }
1553
1554 fn on_copy_inner(&mut self) -> Option<String> {
1555 if !self.receives_focused_input() {
1556 return None;
1557 }
1558 cranpose_ui::text_field_focus::dispatch_copy()
1559 }
1560
1561 pub fn on_cut(&mut self) -> Option<String> {
1564 let _event_handler = enter_event_handler_scope();
1565 if self.inspector_owns_keyboard() {
1566 return None;
1567 }
1568 let _app_context = self.shell.app.app_context.enter_scope();
1569 self.on_cut_inner()
1570 }
1571
1572 fn on_cut_inner(&mut self) -> Option<String> {
1573 if !self.receives_focused_input() {
1574 return None;
1575 }
1576 let text =
1577 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1578
1579 if text.is_some() {
1580 self.mark_dirty();
1581 self.shell.app.request_layout_pass();
1582 }
1583
1584 text
1585 }
1586
1587 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1595 if self.inspector_owns_keyboard() {
1596 return true;
1597 }
1598 let _event_handler = enter_event_handler_scope();
1599 let _app_context = self.shell.app.app_context.enter_scope();
1600 self.on_ime_preedit_inner(text, cursor)
1601 }
1602
1603 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1604 if !self.receives_focused_input() {
1605 return false;
1606 }
1607 let handled = run_in_mutable_snapshot(|| {
1608 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1609 })
1610 .unwrap_or(false);
1611
1612 if handled {
1613 self.mark_dirty();
1614 self.shell.app.request_layout_pass();
1615 }
1616
1617 handled
1618 }
1619
1620 pub fn on_ime_finish_composing(&mut self) -> bool {
1624 if self.inspector_owns_keyboard() {
1625 return true;
1626 }
1627 let _event_handler = enter_event_handler_scope();
1628 let _app_context = self.shell.app.app_context.enter_scope();
1629 self.on_ime_finish_composing_inner()
1630 }
1631
1632 fn on_ime_finish_composing_inner(&mut self) -> bool {
1633 if !self.receives_focused_input() {
1634 return false;
1635 }
1636 let handled =
1637 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1638 .unwrap_or(false);
1639
1640 if handled {
1641 self.mark_dirty();
1642 self.shell.app.request_layout_pass();
1643 }
1644
1645 handled
1646 }
1647
1648 pub fn on_ime_action(&mut self, action: cranpose_ui::ImeAction) -> bool {
1654 if self.inspector_owns_keyboard() {
1655 return true;
1656 }
1657 let _event_handler = enter_event_handler_scope();
1658 let _app_context = self.shell.app.app_context.enter_scope();
1659 if !self.receives_focused_input() {
1660 return false;
1661 }
1662 let handled =
1663 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_ime_action(action))
1664 .unwrap_or(false);
1665 if handled {
1666 self.mark_dirty();
1667 self.shell.app.request_layout_pass();
1668 }
1669 self.run_requested_focus_move();
1670 handled
1671 }
1672
1673 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1677 if self.inspector_owns_keyboard() {
1678 return true;
1679 }
1680 let _event_handler = enter_event_handler_scope();
1681 let _app_context = self.shell.app.app_context.enter_scope();
1682 if !self.receives_focused_input() {
1683 return false;
1684 }
1685 let handled = run_in_mutable_snapshot(|| {
1686 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(start_bytes, end_bytes)
1687 })
1688 .unwrap_or(false);
1689
1690 if handled {
1691 self.mark_dirty();
1692 self.shell.app.request_layout_pass();
1693 }
1694
1695 handled
1696 }
1697
1698 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1703 if self.inspector_owns_keyboard() {
1704 return true;
1705 }
1706 let _event_handler = enter_event_handler_scope();
1707 let _app_context = self.shell.app.app_context.enter_scope();
1708 if !self.receives_focused_input() {
1709 return false;
1710 }
1711 let handled = run_in_mutable_snapshot(|| {
1712 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1713 })
1714 .unwrap_or(false);
1715
1716 if handled {
1717 self.mark_dirty();
1718 }
1719
1720 handled
1721 }
1722
1723 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1727 let _app_context = self.shell.app.app_context.enter_scope();
1728 self.receives_focused_input()
1729 .then(cranpose_ui::text_field_focus::focused_editor_state)
1730 .flatten()
1731 }
1732
1733 pub fn ime_caret_geometry(
1737 &mut self,
1738 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1739 let _app_context = self.shell.app.app_context.enter_scope();
1740 self.receives_focused_input()
1741 .then(cranpose_ui::text_field_focus::focused_caret_geometry)
1742 .flatten()
1743 }
1744
1745 pub fn ime_field_bounds(&mut self) -> Option<cranpose_ui_graphics::Rect> {
1749 let _app_context = self.shell.app.app_context.enter_scope();
1750 self.receives_focused_input()
1751 .then(cranpose_ui::text_field_focus::focused_field_bounds)
1752 .flatten()
1753 }
1754
1755 pub fn ime_field_is_password(&mut self) -> bool {
1762 let _app_context = self.shell.app.app_context.enter_scope();
1763 let Some(field) = self
1764 .receives_focused_input()
1765 .then(cranpose_ui::text_field_focus::focused_field_target)
1766 .flatten()
1767 else {
1768 return false;
1769 };
1770 let mut applier = self.shell.app.composition.applier_mut();
1771 applier
1772 .with_node::<LayoutNode, _>(field, |node| node.semantics_configuration())
1773 .or_else(|_| {
1774 applier.with_node::<SubcomposeLayoutNode, _>(field, |node| {
1775 node.semantics_configuration()
1776 })
1777 })
1778 .ok()
1779 .flatten()
1780 .is_some_and(|config| config.password)
1781 }
1782
1783 pub fn clear_text_field_focus(&mut self) {
1786 let _event_handler = enter_event_handler_scope();
1787 let _app_context = self.shell.app.app_context.enter_scope();
1788 if self.receives_focused_input() {
1789 cranpose_ui::text_field_focus::clear_focus();
1790 self.mark_dirty();
1791 self.shell.app.request_layout_pass();
1792 }
1793 }
1794
1795 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1798 if self.inspector_owns_keyboard() {
1799 return true;
1800 }
1801 let _event_handler = enter_event_handler_scope();
1802 let _app_context = self.shell.app.app_context.enter_scope();
1803 self.on_ime_delete_surrounding_inner(before_bytes, after_bytes)
1804 }
1805
1806 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1807 if !self.receives_focused_input() {
1808 return false;
1809 }
1810 let handled = run_in_mutable_snapshot(|| {
1811 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1812 })
1813 .unwrap_or(false);
1814
1815 if handled {
1816 self.mark_dirty();
1817 self.shell.app.request_layout_pass();
1818 }
1819
1820 handled
1821 }
1822}
1823
1824impl<R> AppShell<R>
1825where
1826 R: Renderer,
1827 R::Error: Debug,
1828{
1829 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
1838 self.app.modifiers = Some(modifiers);
1839 }
1840
1841 pub fn modifiers(&self) -> Option<Modifiers> {
1845 self.app.modifiers
1846 }
1847
1848 pub fn rotary_scroll_factor(&self) -> f32 {
1851 self.app.rotary_scroll_factor
1852 }
1853
1854 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
1863 if factor.is_finite() && factor > 0.0 {
1864 self.app.rotary_scroll_factor = factor;
1865 }
1866 }
1867
1868 pub fn notify_app_paused(&mut self) {
1875 self.app
1876 .app_context
1877 .enter(cranpose_ui::text_input_session::notify_app_paused);
1878 }
1879
1880 pub fn notify_app_resumed(&mut self) -> bool {
1888 self.app
1889 .app_context
1890 .enter(cranpose_ui::text_input_session::notify_app_resumed)
1891 }
1892
1893 #[cfg(all(
1895 feature = "clipboard-native",
1896 target_os = "linux",
1897 not(target_arch = "wasm32")
1898 ))]
1899 pub fn set_primary_selection(&mut self, text: &str) {
1900 use arboard::{LinuxClipboardKind, SetExtLinux};
1901 if let Some(ref mut clipboard) = self.app.clipboard {
1902 let result = clipboard
1903 .set()
1904 .clipboard(LinuxClipboardKind::Primary)
1905 .text(text.to_string());
1906 if let Err(e) = result {
1907 log::debug!("Primary selection set failed: {e:?}");
1908 }
1909 }
1910 }
1911
1912 #[cfg(not(all(
1915 feature = "clipboard-native",
1916 target_os = "linux",
1917 not(target_arch = "wasm32")
1918 )))]
1919 pub fn set_primary_selection(&mut self, _text: &str) {}
1920
1921 #[cfg(all(
1923 feature = "clipboard-native",
1924 target_os = "linux",
1925 not(target_arch = "wasm32")
1926 ))]
1927 pub fn get_primary_selection(&mut self) -> Option<String> {
1928 use arboard::{GetExtLinux, LinuxClipboardKind};
1929 if let Some(ref mut clipboard) = self.app.clipboard {
1930 clipboard
1931 .get()
1932 .clipboard(LinuxClipboardKind::Primary)
1933 .text()
1934 .ok()
1935 } else {
1936 None
1937 }
1938 }
1939
1940 #[cfg(not(all(
1942 feature = "clipboard-native",
1943 target_os = "linux",
1944 not(target_arch = "wasm32")
1945 )))]
1946 pub fn get_primary_selection(&mut self) -> Option<String> {
1947 None
1948 }
1949
1950 pub fn sync_selection_to_primary(&mut self) {
1953 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1954 {
1955 if let Some(text) = self.on_copy() {
1956 self.set_primary_selection(&text);
1957 }
1958 }
1959 }
1960
1961 pub fn set_pointer_source(&mut self, source: PointerSource) {
1963 self.primary().set_pointer_source(source);
1964 }
1965
1966 pub fn pointer_source(&self) -> PointerSource {
1968 self.surfaces[0].pointer_source
1969 }
1970
1971 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
1973 self.primary().set_cursor(x, y)
1974 }
1975
1976 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
1978 self.primary().set_cursor_at_time(x, y, time_ms)
1979 }
1980
1981 pub fn set_cursor_at_event_time(
1983 &mut self,
1984 x: f32,
1985 y: f32,
1986 event_time: PointerEventTime,
1987 ) -> bool {
1988 self.primary().set_cursor_at_event_time(x, y, event_time)
1989 }
1990
1991 pub fn pointer_pressed(&mut self) -> bool {
1993 self.primary().pointer_pressed()
1994 }
1995
1996 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
1998 self.primary().accessibility_activate(node_id, canvas_key)
1999 }
2000
2001 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
2003 self.primary().pointer_pressed_at_time(time_ms)
2004 }
2005
2006 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
2008 self.primary().pointer_pressed_at_event_time(event_time)
2009 }
2010
2011 pub fn pointer_released(&mut self) -> bool {
2013 self.primary().pointer_released()
2014 }
2015
2016 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
2018 self.primary().pointer_released_at_position(x, y)
2019 }
2020
2021 pub fn pointer_released_at_position_time(
2024 &mut self,
2025 x: f32,
2026 y: f32,
2027 time_ms: Option<i64>,
2028 ) -> bool {
2029 self.primary()
2030 .pointer_released_at_position_time(x, y, time_ms)
2031 }
2032
2033 pub fn pointer_released_at_position_event_time(
2036 &mut self,
2037 x: f32,
2038 y: f32,
2039 event_time: PointerEventTime,
2040 ) -> bool {
2041 self.primary()
2042 .pointer_released_at_position_event_time(x, y, event_time)
2043 }
2044
2045 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
2047 self.primary().pointer_released_at_time(time_ms)
2048 }
2049
2050 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
2052 self.primary().pointer_released_at_event_time(event_time)
2053 }
2054
2055 pub fn secondary_pointer_pressed(
2057 &mut self,
2058 pointer_id: u64,
2059 x: f32,
2060 y: f32,
2061 time_ms: Option<i64>,
2062 ) -> bool {
2063 self.primary()
2064 .secondary_pointer_pressed(pointer_id, x, y, time_ms)
2065 }
2066
2067 pub fn ray_pointer(
2069 &mut self,
2070 (pointer_id, scene): (u32, NodeId),
2071 ray: cranpose_ui_graphics::space::Ray,
2072 kind: PointerEventKind,
2073 ) -> bool {
2074 self.primary().ray_pointer((pointer_id, scene), ray, kind)
2075 }
2076
2077 pub fn secondary_pointer_moved(
2079 &mut self,
2080 pointer_id: u64,
2081 x: f32,
2082 y: f32,
2083 time_ms: Option<i64>,
2084 ) -> bool {
2085 self.primary()
2086 .secondary_pointer_moved(pointer_id, x, y, time_ms)
2087 }
2088
2089 pub fn secondary_pointer_released(
2091 &mut self,
2092 pointer_id: u64,
2093 x: f32,
2094 y: f32,
2095 time_ms: Option<i64>,
2096 ) -> bool {
2097 self.primary()
2098 .secondary_pointer_released(pointer_id, x, y, time_ms)
2099 }
2100
2101 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
2103 self.primary().pointer_zoomed(zoom_factor)
2104 }
2105
2106 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
2108 self.primary().wheel_scrolled(wheel)
2109 }
2110
2111 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
2113 self.primary().pointer_scrolled(delta_x, delta_y)
2114 }
2115
2116 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
2118 where
2119 F: Fn(RotaryScrollEvent) -> bool + 'static,
2120 {
2121 self.primary().set_on_rotary_scroll(handler);
2122 }
2123
2124 pub fn clear_on_rotary_scroll(&mut self) {
2126 self.primary().clear_on_rotary_scroll();
2127 }
2128
2129 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
2131 self.primary()
2132 .rotary_scrolled_by_detents(detents, uptime_millis)
2133 }
2134
2135 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
2137 self.primary().rotary_scrolled(event)
2138 }
2139
2140 pub fn cancel_gesture(&mut self) {
2142 self.primary().cancel_gesture();
2143 }
2144
2145 pub fn cancel_gesture_unless_pressed(&mut self) {
2147 self.primary().cancel_gesture_unless_pressed();
2148 }
2149
2150 pub fn refresh_pointer_icon(&self) {
2154 self.surfaces[0].pointer_icon.refresh();
2155 }
2156
2157 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
2164 self.surfaces[0].pointer_icon.take_change()
2165 }
2166
2167 pub fn set_platform_text_input(
2174 &mut self,
2175 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
2176 ) {
2177 self.primary().set_platform_text_input(handler);
2178 }
2179
2180 pub fn dismiss_top_modal(&mut self) -> bool {
2182 self.primary().dismiss_top_modal()
2183 }
2184
2185 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
2187 self.primary().move_focus_in_context(direction)
2188 }
2189
2190 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
2192 self.primary().on_key_event(event)
2193 }
2194
2195 pub fn on_paste(&mut self, text: &str) -> bool {
2197 self.primary().on_paste(text)
2198 }
2199
2200 pub fn on_copy(&mut self) -> Option<String> {
2202 self.primary().on_copy()
2203 }
2204
2205 pub fn on_cut(&mut self) -> Option<String> {
2207 self.primary().on_cut()
2208 }
2209
2210 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
2212 self.primary().on_ime_preedit(text, cursor)
2213 }
2214
2215 pub fn on_ime_finish_composing(&mut self) -> bool {
2217 self.primary().on_ime_finish_composing()
2218 }
2219
2220 pub fn on_ime_action(&mut self, action: cranpose_ui::ImeAction) -> bool {
2222 self.primary().on_ime_action(action)
2223 }
2224
2225 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
2227 self.primary()
2228 .on_ime_set_composing_region(start_bytes, end_bytes)
2229 }
2230
2231 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
2233 self.primary().on_ime_set_selection(start_bytes, end_bytes)
2234 }
2235
2236 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
2238 self.primary().ime_editor_state()
2239 }
2240
2241 pub fn ime_caret_geometry(
2243 &mut self,
2244 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
2245 self.primary().ime_caret_geometry()
2246 }
2247
2248 pub fn ime_field_bounds(&mut self) -> Option<cranpose_ui_graphics::Rect> {
2250 self.primary().ime_field_bounds()
2251 }
2252
2253 pub fn ime_field_is_password(&mut self) -> bool {
2255 self.primary().ime_field_is_password()
2256 }
2257
2258 pub fn clear_text_field_focus(&mut self) {
2260 self.primary().clear_text_field_focus();
2261 }
2262
2263 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
2265 self.primary()
2266 .on_ime_delete_surrounding(before_bytes, after_bytes)
2267 }
2268}
2269
2270enum ActivationTarget {
2271 Direct(cranpose_foundation::SemanticsCustomAction),
2272 Edit(NodeId),
2273 Pointer(NodeId, Option<Rect>),
2274}
2275
2276fn activation_target(
2277 node: &cranpose_ui::SemanticsNode,
2278 node_id: NodeId,
2279 canvas_key: Option<u64>,
2280) -> Option<ActivationTarget> {
2281 if node.hidden {
2282 return None;
2283 }
2284 if node.node_id != node_id {
2285 return node
2286 .children
2287 .iter()
2288 .find_map(|child| activation_target(child, node_id, canvas_key));
2289 }
2290 if !node.enabled {
2291 return None;
2292 }
2293 if let Some(key) = canvas_key {
2294 let child = node
2295 .details()
2296 .canvas_children
2297 .iter()
2298 .find(|child| child.key == key)?;
2299 return (child.enabled && child.clickable)
2300 .then_some(ActivationTarget::Pointer(node_id, Some(child.bounds)));
2301 }
2302 if let Some(action) = &node.on_click {
2303 return Some(ActivationTarget::Direct(action.clone()));
2304 }
2305 if node.details().editable_text && node.focusable {
2306 return Some(ActivationTarget::Edit(node_id));
2307 }
2308 node.actions.first().map(|action| match action {
2309 cranpose_ui::SemanticsAction::Click { handler } => {
2310 ActivationTarget::Pointer(handler.node_id(), None)
2311 }
2312 })
2313}
2314
2315fn activation_input(
2316 root: &LayoutBox,
2317 node_id: NodeId,
2318 canvas_bounds: Option<Rect>,
2319) -> Option<(Point, Point, Rc<cranpose_ui::ModifierNodeSlices>)> {
2320 if root.node_id != node_id {
2321 return root
2322 .children
2323 .iter()
2324 .find_map(|child| activation_input(child, node_id, canvas_bounds));
2325 }
2326 let bounds = canvas_bounds.map_or(root.rect, |bounds| Rect {
2327 x: root.rect.x + bounds.x,
2328 y: root.rect.y + bounds.y,
2329 ..bounds
2330 });
2331 let position = Point {
2332 x: bounds.x + bounds.width * 0.5,
2333 y: bounds.y + bounds.height * 0.5,
2334 };
2335 let local = Point {
2336 x: position.x - root.rect.x,
2337 y: position.y - root.rect.y,
2338 };
2339 (bounds.width > 0.0 && bounds.height > 0.0 && position.x.is_finite() && position.y.is_finite())
2340 .then(|| (position, local, Rc::clone(&root.node_data.modifier_slices)))
2341}
2342
2343fn plain_key_down(event: &KeyEvent) -> bool {
2344 event.event_type == KeyEventType::KeyDown
2345 && !event.modifiers.ctrl
2346 && !event.modifiers.meta
2347 && !event.modifiers.alt
2348}