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 {
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
188 .iter()
189 .map(cranpose_render_common::HitTestTarget::node_id)
190 .collect();
191
192 let pos = Point { x, y };
193 let previously_hovered = self.surface().hovered_nodes.clone();
194 for old_id in previously_hovered {
195 if !new_ids.contains(&old_id)
196 && let Some(target) = self.surface().renderer.scene().find_target(old_id)
197 {
198 let exit_event = self
199 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
200 .with_buttons(self.surface().buttons_pressed)
201 .with_source(self.surface().pointer_source);
202 self.dispatch_targets(std::iter::once(target), exit_event, false);
203 }
204 }
205
206 for hit in &hits {
207 if !self.surface().hovered_nodes.contains(&hit.node_id()) {
208 let enter_event = self
209 .pointer_event(PointerEventKind::Enter, pos, pos, event_time)
210 .with_buttons(self.surface().buttons_pressed)
211 .with_source(self.surface().pointer_source);
212 self.dispatch_targets(std::iter::once(hit.clone()), enter_event, false);
213 }
214 }
215
216 self.surface_mut().hovered_nodes = new_ids;
217 self.apply_hovered_pointer_icon(&hits);
218
219 if !hits.is_empty() {
220 let event = self
221 .pointer_event(PointerEventKind::Move, pos, pos, event_time)
222 .with_buttons(self.surface().buttons_pressed)
223 .with_source(self.surface().pointer_source);
224 self.dispatch_targets(hits, event, true);
225 true
226 } else {
227 false
228 }
229 }
230
231 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
237 let event_time = self.shell.app.realtime_pointer_event_time(None);
238 let _event_handler = enter_event_handler_scope();
239 let app_context = Rc::clone(&self.shell.app.app_context);
240 let result = app_context.enter(|| {
241 run_in_mutable_snapshot(|| self.activate_node(node_id, canvas_key, event_time))
242 .unwrap_or(false)
243 });
244 if result {
245 self.mark_dirty();
246 }
247 result
248 }
249
250 fn activate_node(
251 &mut self,
252 node_id: NodeId,
253 canvas_key: Option<u64>,
254 event_time: PointerEventTime,
255 ) -> bool {
256 let target = {
257 let (app, surface) = self.parts();
258 surface
259 .semantics_tree_for_input(app)
260 .and_then(|tree| activation_target(tree.root(), node_id, canvas_key))
261 };
262 let (target_id, canvas_bounds) = match target {
263 Some(ActivationTarget::Direct(action)) => {
264 action.invoke();
265 return true;
266 }
267 Some(ActivationTarget::Edit(node_id)) => {
268 self.activate();
269 return cranpose_ui::request_focus_from_platform(node_id);
270 }
271 Some(ActivationTarget::Pointer(node_id, bounds)) => (node_id, bounds),
272 None => return false,
273 };
274 let position =
275 self.with_layout_tree(|tree| activation_input(tree?.root(), target_id, canvas_bounds));
276 let Some((position, local, handlers)) = position else {
277 return false;
278 };
279 for kind in [PointerEventKind::Down, PointerEventKind::Up] {
280 let event = self.pointer_event(kind, local, position, event_time);
281 handlers.dispatch_pointer_event(event.clone());
282 event.finish_post_dispatch();
283 }
284 true
285 }
286
287 pub fn pointer_pressed(&mut self) -> bool {
288 self.pointer_pressed_at_time(None)
289 }
290
291 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
294 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
295 self.pointer_pressed_at_event_time(event_time)
296 }
297
298 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
300 let (cursor_x, cursor_y) = self.surface().cursor;
301 if self.inspector_press(cursor_x, cursor_y) {
302 return true;
303 }
304 if self.dev_overlay_press(cursor_x, cursor_y) {
305 return true;
306 }
307 let _event_handler = enter_event_handler_scope();
308 let app_context = Rc::clone(&self.shell.app.app_context);
309 let result = app_context.enter(|| {
310 run_in_mutable_snapshot(|| self.pointer_pressed_inner(event_time)).unwrap_or(false)
311 });
312 if result {
313 self.mark_dirty();
314 }
315 log::trace!(
316 target: "cranpose::input",
317 "pointer_pressed time_ms={:?} animation_time_nanos={} -> {result}",
318 event_time.platform_time_ms,
319 event_time.animation_time_nanos,
320 );
321 result
322 }
323
324 fn pointer_pressed_inner(&mut self, event_time: PointerEventTime) -> bool {
325 self.activate();
326 self.note_focus_moved_by_keyboard(false);
327 self.surface_mut()
328 .buttons_pressed
329 .insert(PointerButton::Primary);
330
331 let (cursor_x, cursor_y) = self.surface().cursor;
332 let mut hits = self.surface().renderer.scene().hit_test(cursor_x, cursor_y);
333 if self.surface().pointer_source.is_touch_like()
334 && let Some(near) = self
335 .surface()
336 .renderer
337 .scene()
338 .hit_test_near(cursor_x, cursor_y)
339 && hits.iter().all(|hit| {
340 hit.node_id() != near.node_id() && near.capture_path().contains(&hit.node_id())
341 })
342 {
343 hits.insert(0, near);
344 }
345 if hits.is_empty() {
346 self.surface_mut()
347 .hit_path_tracker
348 .remove_path(PointerId::PRIMARY);
349 false
350 } else {
351 let event = self
352 .pointer_event(
353 PointerEventKind::Down,
354 Point {
355 x: self.surface().cursor.0,
356 y: self.surface().cursor.1,
357 },
358 Point {
359 x: self.surface().cursor.0,
360 y: self.surface().cursor.1,
361 },
362 event_time,
363 )
364 .with_buttons(self.surface().buttons_pressed)
365 .with_source(self.surface().pointer_source);
366
367 let mut delivered_capture_paths = Vec::new();
368 let (app, surface) = self.parts();
369 let mut applier = app.composition.applier_mut();
370 for hit in hits {
371 let node_id = hit.node_id();
372 delivered_capture_paths.push(hit.capture_path());
373 hit.dispatch_with_applier(&mut applier, event.clone());
374 log::trace!(
375 target: "cranpose::input",
376 "dispatch {:?} node={} consumed={} stop_on_consume=true",
377 event.kind,
378 node_id,
379 event.is_consumed(),
380 );
381 if event.is_consumed() {
382 break;
383 }
384 }
385
386 surface
387 .hit_path_tracker
388 .add_hit_path(PointerId::PRIMARY, delivered_capture_paths);
389 log::trace!(
390 target: "cranpose::input",
391 "pointer_pressed_inner cached_hit_path={:?}",
392 surface.hit_path_tracker.get_path(PointerId::PRIMARY),
393 );
394
395 true
396 }
397 }
398
399 pub fn pointer_released(&mut self) -> bool {
400 self.pointer_released_at_time(None)
401 }
402
403 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
416 self.pointer_released_at_position_time(x, y, None)
417 }
418
419 pub fn pointer_released_at_position_time(
423 &mut self,
424 x: f32,
425 y: f32,
426 time_ms: Option<i64>,
427 ) -> bool {
428 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
429 self.pointer_released_at_position_event_time(x, y, event_time)
430 }
431
432 pub fn pointer_released_at_position_event_time(
434 &mut self,
435 x: f32,
436 y: f32,
437 event_time: PointerEventTime,
438 ) -> bool {
439 let _event_handler = enter_event_handler_scope();
440 let app_context = Rc::clone(&self.shell.app.app_context);
441 let result = app_context.enter(|| {
442 run_in_mutable_snapshot(|| {
443 self.surface_mut().cursor = (x, y);
444 self.pointer_released_inner(event_time)
445 })
446 .unwrap_or(false)
447 });
448 self.route_drag_and_drop();
449 if result {
450 self.mark_dirty();
451 }
452 log::trace!(
453 target: "cranpose::input",
454 "pointer_released_at_position ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
455 event_time.platform_time_ms,
456 event_time.animation_time_nanos,
457 );
458 result
459 }
460
461 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
464 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
465 self.pointer_released_at_event_time(event_time)
466 }
467
468 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
470 let _event_handler = enter_event_handler_scope();
471 let app_context = Rc::clone(&self.shell.app.app_context);
472 let result = app_context.enter(|| {
473 run_in_mutable_snapshot(|| self.pointer_released_inner(event_time)).unwrap_or(false)
474 });
475 self.route_drag_and_drop();
476 if result {
477 self.mark_dirty();
478 }
479 log::trace!(
480 target: "cranpose::input",
481 "pointer_released time_ms={:?} animation_time_nanos={} -> {result}",
482 event_time.platform_time_ms,
483 event_time.animation_time_nanos,
484 );
485 result
486 }
487
488 fn pointer_released_inner(&mut self, event_time: PointerEventTime) -> bool {
489 if self.surface_mut().inspector.release_pointer() {
490 return true;
491 }
492 self.release_app_pointer(event_time)
493 }
494
495 fn release_app_pointer(&mut self, event_time: PointerEventTime) -> bool {
496 self.surface_mut()
497 .buttons_pressed
498 .remove(PointerButton::Primary);
499 let corrected_buttons = self.surface().buttons_pressed;
500 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
501
502 self.surface_mut()
503 .hit_path_tracker
504 .remove_path(PointerId::PRIMARY);
505
506 if !targets.is_empty() {
507 let event = self
508 .pointer_event(
509 PointerEventKind::Up,
510 Point {
511 x: self.surface().cursor.0,
512 y: self.surface().cursor.1,
513 },
514 Point {
515 x: self.surface().cursor.0,
516 y: self.surface().cursor.1,
517 },
518 event_time,
519 )
520 .with_buttons(corrected_buttons)
521 .with_source(self.surface().pointer_source);
522
523 self.dispatch_targets(targets, event, false);
524 true
525 } else {
526 false
527 }
528 }
529
530 pub fn secondary_pointer_pressed(
539 &mut self,
540 pointer_id: u64,
541 x: f32,
542 y: f32,
543 time_ms: Option<i64>,
544 ) -> bool {
545 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
546 self.dispatch_secondary_pointer(PointerEventKind::Down, pointer_id, x, y, event_time)
547 }
548
549 pub fn secondary_pointer_moved(
551 &mut self,
552 pointer_id: u64,
553 x: f32,
554 y: f32,
555 time_ms: Option<i64>,
556 ) -> bool {
557 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
558 self.dispatch_secondary_pointer(PointerEventKind::Move, pointer_id, x, y, event_time)
559 }
560
561 pub fn secondary_pointer_released(
563 &mut self,
564 pointer_id: u64,
565 x: f32,
566 y: f32,
567 time_ms: Option<i64>,
568 ) -> bool {
569 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
570 self.dispatch_secondary_pointer(PointerEventKind::Up, pointer_id, x, y, event_time)
571 }
572
573 fn dispatch_secondary_pointer(
574 &mut self,
575 kind: PointerEventKind,
576 pointer_id: u64,
577 x: f32,
578 y: f32,
579 event_time: PointerEventTime,
580 ) -> bool {
581 if pointer_id == 0 {
582 log::warn!(
583 target: "cranpose::input",
584 "secondary pointer dispatch called with the primary pointer id"
585 );
586 return false;
587 }
588
589 let _event_handler = enter_event_handler_scope();
590 let app_context = Rc::clone(&self.shell.app.app_context);
591 let result = app_context.enter(|| {
592 run_in_mutable_snapshot(|| {
593 if !self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
594 return false;
595 }
596 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
597 if targets.is_empty() {
598 return false;
599 }
600 let pos = Point { x, y };
601 let event = self
602 .pointer_event(kind, pos, pos, event_time)
603 .with_buttons(self.surface().buttons_pressed)
604 .with_id(pointer_id)
605 .with_source(self.surface().pointer_source);
606 self.dispatch_targets(targets, event, false);
607 true
608 })
609 .unwrap_or(false)
610 });
611 if result {
612 self.mark_dirty();
613 }
614 log::trace!(
615 target: "cranpose::input",
616 "secondary_pointer {kind:?} id={pointer_id} ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
617 event_time.platform_time_ms,
618 event_time.animation_time_nanos,
619 );
620 result
621 }
622
623 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
629 let event_time = self.shell.app.realtime_pointer_event_time(None);
630 let _event_handler = enter_event_handler_scope();
631 let app_context = Rc::clone(&self.shell.app.app_context);
632 let result = app_context.enter(|| {
633 run_in_mutable_snapshot(|| self.pointer_zoomed_inner(zoom_factor, event_time))
634 .unwrap_or(false)
635 });
636 if result {
637 self.mark_dirty();
638 }
639 log::trace!(
640 target: "cranpose::input",
641 "pointer_zoomed factor={zoom_factor:.4} -> {result}"
642 );
643 result
644 }
645
646 fn pointer_zoomed_inner(&mut self, zoom_factor: f32, event_time: PointerEventTime) -> bool {
647 if !zoom_factor.is_finite() || zoom_factor <= 0.0 || zoom_factor == 1.0 {
648 return false;
649 }
650
651 let hits = self
652 .surface()
653 .renderer
654 .scene()
655 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
656 if hits.is_empty() {
657 return false;
658 }
659
660 let pos = Point {
661 x: self.surface().cursor.0,
662 y: self.surface().cursor.1,
663 };
664 let event = self
665 .pointer_event(PointerEventKind::Zoom, pos, pos, event_time)
666 .with_buttons(self.surface().buttons_pressed)
667 .with_zoom_delta(zoom_factor)
668 .with_source(self.surface().pointer_source);
669
670 self.dispatch_to_hits(&hits, event)
671 }
672
673 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
698 if self.inspector_scroll(wheel.delta.y) {
699 return true;
700 }
701 if wheel.is_zoom() {
702 let zoom_factor = wheel.zoom_factor();
703 log::trace!(
704 target: "cranpose::input",
705 "wheel zoom factor={zoom_factor:.4}"
706 );
707 return self.pointer_zoomed(zoom_factor);
708 }
709
710 let rotary =
711 RotaryScrollEvent::from_wheel_pixels(wheel.delta.y, wheel.delta.x, wheel.uptime_millis);
712 if self.rotary_scrolled(rotary) {
713 return true;
714 }
715
716 let delta = wheel.scroll_delta();
717 log::trace!(
718 target: "cranpose::input",
719 "wheel delta ({:.2},{:.2}) alt={}",
720 delta.x,
721 delta.y,
722 wheel.modifiers.alt
723 );
724 self.pointer_scrolled(delta.x, delta.y)
725 }
726
727 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
735 if self.inspector_scroll(delta_y) {
736 return true;
737 }
738 let event_time = self.shell.app.realtime_pointer_event_time(None);
739 let _event_handler = enter_event_handler_scope();
740 let app_context = Rc::clone(&self.shell.app.app_context);
741 let result = app_context.enter(|| {
742 run_in_mutable_snapshot(|| self.pointer_scrolled_inner(delta_x, delta_y, event_time))
743 .unwrap_or(false)
744 });
745 if result {
746 self.mark_dirty();
747 }
748 log::trace!(
749 target: "cranpose::input",
750 "pointer_scrolled ({delta_x:.2},{delta_y:.2}) -> {result}"
751 );
752 result
753 }
754
755 fn pointer_scrolled_inner(
756 &mut self,
757 delta_x: f32,
758 delta_y: f32,
759 event_time: PointerEventTime,
760 ) -> bool {
761 if delta_x.abs() <= f32::EPSILON && delta_y.abs() <= f32::EPSILON {
762 return false;
763 }
764
765 let hits = self
766 .surface()
767 .renderer
768 .scene()
769 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
770 if hits.is_empty() {
771 return false;
772 }
773
774 let event = self
775 .pointer_event(
776 PointerEventKind::Scroll,
777 Point {
778 x: self.surface().cursor.0,
779 y: self.surface().cursor.1,
780 },
781 Point {
782 x: self.surface().cursor.0,
783 y: self.surface().cursor.1,
784 },
785 event_time,
786 )
787 .with_buttons(self.surface().buttons_pressed)
788 .with_scroll_delta(Point {
789 x: delta_x,
790 y: delta_y,
791 })
792 .with_source(self.surface().pointer_source);
793
794 self.dispatch_to_hits(&hits, event)
795 }
796
797 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
808 where
809 F: Fn(RotaryScrollEvent) -> bool + 'static,
810 {
811 self.surface_mut().on_rotary_scroll = Some(Rc::new(handler));
812 }
813
814 pub fn clear_on_rotary_scroll(&mut self) {
816 self.surface_mut().on_rotary_scroll = None;
817 }
818
819 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
825 let factor = self.shell.app.rotary_scroll_factor;
826 self.rotary_scrolled(RotaryScrollEvent::from_detents(
827 detents,
828 factor,
829 factor,
830 uptime_millis,
831 ))
832 }
833
834 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
853 let _event_handler = enter_event_handler_scope();
854 let app_context = Rc::clone(&self.shell.app.app_context);
855 let result = app_context.enter(|| {
856 run_in_mutable_snapshot(|| self.rotary_scrolled_inner(event)).unwrap_or(false)
857 });
858 if result {
859 self.mark_dirty();
860 }
861 log::trace!(
862 target: "cranpose::input",
863 "rotary_scrolled v={:.2} h={:.2} uptime={} -> {result}",
864 event.vertical_scroll_pixels,
865 event.horizontal_scroll_pixels,
866 event.uptime_millis,
867 );
868 result
869 }
870
871 fn rotary_scrolled_inner(&mut self, rotary: RotaryScrollEvent) -> bool {
872 if rotary.is_empty() {
873 return false;
874 }
875
876 let bubble_order = self.rotary_dispatch_order();
877 let position = Point {
878 x: self.surface().cursor.0,
879 y: self.surface().cursor.1,
880 };
881
882 if !bubble_order.is_empty() {
883 let capture_targets = bubble_order
884 .iter()
885 .rev()
886 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
887 .collect::<Vec<_>>();
888 let mut capture_event =
889 PointerEvent::rotary(PointerEventKind::RotaryScrollPre, rotary, position);
890 capture_event.modifiers = self.shell.app.modifiers;
891 self.dispatch_targets(capture_targets, capture_event.clone(), true);
892 if capture_event.is_consumed() {
893 return true;
894 }
895
896 let bubble_targets = bubble_order
897 .iter()
898 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
899 .collect::<Vec<_>>();
900 let mut bubble_event =
901 PointerEvent::rotary(PointerEventKind::RotaryScroll, rotary, position);
902 bubble_event.modifiers = self.shell.app.modifiers;
903 self.dispatch_targets(bubble_targets, bubble_event.clone(), true);
904 if bubble_event.is_consumed() {
905 return true;
906 }
907 }
908
909 if let Some(handler) = self.surface().on_rotary_scroll.clone() {
910 return handler(rotary);
911 }
912
913 false
914 }
915
916 fn rotary_dispatch_order(&self) -> Vec<NodeId> {
917 if let Some(focused) = cranpose_ui::active_focus_target()
918 && let Some(target) = self.surface().renderer.scene().find_target(focused)
919 {
920 let path = target.capture_path();
921 if !path.is_empty() {
922 return crate::hit_path_tracker::dispatch_order_for_paths(&[path]);
923 }
924 }
925
926 let hits = self
927 .surface()
928 .renderer
929 .scene()
930 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
931 if hits.is_empty() {
932 return Vec::new();
933 }
934 let capture_paths = hits
935 .iter()
936 .map(cranpose_render_common::HitTestTarget::capture_path)
937 .collect::<Vec<_>>();
938 crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
939 }
940
941 pub fn cancel_gesture(&mut self) {
947 let event_time = self.shell.app.realtime_pointer_event_time(None);
948 let _event_handler = enter_event_handler_scope();
949 let app_context = Rc::clone(&self.shell.app.app_context);
950 let _ = app_context.enter(|| {
951 run_in_mutable_snapshot(|| {
952 self.cancel_gesture_inner(event_time);
953 })
954 });
955 self.route_drag_and_drop();
956 }
957
958 fn route_drag_and_drop(&mut self) {
959 let app_context = Rc::clone(&self.shell.app.app_context);
960 let source = self.index;
961 let shell = &mut *self.shell;
962 let routed = app_context.enter(|| {
963 run_in_mutable_snapshot(|| {
964 app_context
965 .drag_and_drop()
966 .route(|point| shell.drag_and_drop_target_at(point, source))
967 })
968 .unwrap_or(false)
969 });
970 if routed {
971 shell.mark_all_dirty();
972 }
973 }
974
975 pub fn cancel_gesture_unless_pressed(&mut self) {
984 if self.surface().buttons_pressed != PointerButtons::NONE
985 || self.surface().inspector.pointer_captured
986 {
987 return;
988 }
989 self.cancel_gesture();
990 }
991
992 fn cancel_gesture_inner(&mut self, event_time: PointerEventTime) {
993 self.surface_mut().inspector.cancel_pointer();
994 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
995
996 self.surface_mut().hit_path_tracker.clear();
997 self.surface_mut().buttons_pressed = PointerButtons::NONE;
998
999 if !targets.is_empty() {
1000 let event = self
1001 .pointer_event(
1002 PointerEventKind::Cancel,
1003 Point {
1004 x: self.surface().cursor.0,
1005 y: self.surface().cursor.1,
1006 },
1007 Point {
1008 x: self.surface().cursor.0,
1009 y: self.surface().cursor.1,
1010 },
1011 event_time,
1012 )
1013 .with_source(self.surface().pointer_source);
1014
1015 self.dispatch_targets(targets, event, false);
1016 }
1017
1018 let pos = Point {
1019 x: self.surface().cursor.0,
1020 y: self.surface().cursor.1,
1021 };
1022 let hovered_nodes = self.surface().hovered_nodes.clone();
1023 for node_id in hovered_nodes {
1024 if let Some(target) = self.surface().renderer.scene().find_target(node_id) {
1025 let exit_event = self
1026 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
1027 .with_source(self.surface().pointer_source);
1028 self.dispatch_targets(std::iter::once(target), exit_event, false);
1029 }
1030 }
1031 self.surface_mut().hovered_nodes.clear();
1032 self.surface().pointer_icon.set(PointerIcon::DEFAULT);
1033 }
1034
1035 fn apply_hovered_pointer_icon(
1036 &self,
1037 hits: &[<<R as Renderer>::Scene as RenderScene>::HitTarget],
1038 ) {
1039 let icon = hits
1040 .iter()
1041 .find_map(HitTestTarget::pointer_icon)
1042 .unwrap_or(PointerIcon::DEFAULT);
1043 self.surface().pointer_icon.set(icon);
1044 }
1045
1046 fn on_focus_key(&mut self, event: &KeyEvent) -> bool {
1047 if !plain_key_down(event) || event.key_code != KeyCode::Tab {
1048 return false;
1049 }
1050 self.on_group_navigation_key(event)
1051 }
1052
1053 fn on_group_navigation_key(&mut self, event: &KeyEvent) -> bool {
1054 let focused = cranpose_ui::active_focus_target();
1055 let target = self.with_layout_tree(|tree| {
1056 cranpose_ui::keyboard_focus_target(
1057 tree?,
1058 focused,
1059 event.key_code,
1060 event.modifiers.shift,
1061 )
1062 });
1063 let Some(target) = target else {
1064 return false;
1065 };
1066 let moved =
1067 run_in_mutable_snapshot(|| cranpose_ui::request_focus_from_platform(target.node_id))
1068 .unwrap_or(false);
1069 if moved {
1070 self.mark_dirty();
1071 self.note_focus_moved_by_keyboard(true);
1072 if target.activate {
1073 self.on_activation_key(&KeyEvent::key_down(KeyCode::Space, " "));
1074 }
1075 }
1076 moved
1077 }
1078
1079 fn on_activation_key(&mut self, event: &KeyEvent) -> bool {
1080 if !plain_key_down(event)
1081 || !matches!(event.key_code, KeyCode::Enter | KeyCode::Space)
1082 || cranpose_ui::text_field_focus::has_focused_field()
1083 {
1084 return false;
1085 }
1086 let Some(focused) = cranpose_ui::active_focus_target() else {
1087 return false;
1088 };
1089 let activated = self.accessibility_activate(focused, None);
1090 self.note_focus_moved_by_keyboard(true);
1091 activated
1092 }
1093
1094 fn on_arrow_key(&mut self, event: &KeyEvent) -> bool {
1095 if !plain_key_down(event) || cranpose_ui::text_field_focus::has_focused_field() {
1096 return false;
1097 }
1098 if self.on_group_navigation_key(event) {
1099 return true;
1100 }
1101 let direction = match event.key_code {
1102 KeyCode::ArrowLeft => FocusDirection::Left,
1103 KeyCode::ArrowRight => FocusDirection::Right,
1104 KeyCode::ArrowUp => FocusDirection::Up,
1105 KeyCode::ArrowDown => FocusDirection::Down,
1106 _ => return false,
1107 };
1108 let Some(focused) = cranpose_ui::active_focus_target() else {
1109 return false;
1110 };
1111 let order = self.with_layout_tree(|layout_tree| {
1112 let layout_tree = layout_tree?;
1113 let group = cranpose_ui::selectable_group_of(layout_tree, focused)?;
1114 Some(cranpose_ui::collect_focus_order_under(layout_tree, group))
1115 });
1116 order.is_some_and(|order| self.move_focus_in_order(order, direction))
1117 }
1118
1119 fn note_focus_moved_by_keyboard(&mut self, keyboard: bool) {
1120 if cranpose_ui::set_keyboard_focus_visible(keyboard) {
1121 cranpose_ui::request_render_invalidation();
1122 self.mark_dirty();
1123 }
1124 }
1125
1126 fn on_escape_key(&mut self, event: &KeyEvent) -> bool {
1127 if event.event_type != KeyEventType::KeyDown || event.key_code != KeyCode::Escape {
1128 return false;
1129 }
1130 self.dismiss_top_modal_in_context()
1131 }
1132
1133 pub fn dismiss_top_modal(&mut self) -> bool {
1136 let _event_handler = enter_event_handler_scope();
1137 let app_context = Rc::clone(&self.shell.app.app_context);
1138 app_context.enter(|| self.dismiss_top_modal_in_context())
1139 }
1140
1141 fn dismiss_top_modal_in_context(&mut self) -> bool {
1142 let closed = run_in_mutable_snapshot(|| {
1143 if cranpose_ui::modal_depth() > 0 {
1144 cranpose_ui::dispatch_modal_back()
1145 } else {
1146 cranpose_ui::dismiss_top_popup()
1147 }
1148 })
1149 .unwrap_or(false);
1150 if closed {
1151 self.mark_dirty();
1152 }
1153 closed
1154 }
1155
1156 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1159 let order = self.with_layout_tree(|layout_tree| {
1160 layout_tree
1161 .map(cranpose_ui::collect_focus_order)
1162 .unwrap_or_default()
1163 });
1164 self.move_focus_in_order(order, direction)
1165 }
1166
1167 fn move_focus_in_order(
1168 &mut self,
1169 order: Vec<cranpose_ui::FocusEntry>,
1170 direction: FocusDirection,
1171 ) -> bool {
1172 cranpose_ui::set_focus_order(order);
1173 let moved = run_in_mutable_snapshot(|| cranpose_ui::FocusManager.move_focus(direction))
1174 .unwrap_or(false);
1175 if moved {
1176 self.mark_dirty();
1177 self.note_focus_moved_by_keyboard(true);
1178 }
1179 moved
1180 }
1181
1182 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1183 let _event_handler = enter_event_handler_scope();
1184 let app_context = Rc::clone(&self.shell.app.app_context);
1185 app_context.enter(|| {
1186 let handled = self.on_key_event_inner(event);
1187 self.run_requested_focus_move();
1188 handled
1189 })
1190 }
1191
1192 fn run_requested_focus_move(&mut self) {
1194 if let Some(direction) = cranpose_ui::take_requested_focus_move() {
1195 self.move_focus_in_context(direction);
1196 }
1197 }
1198
1199 fn on_key_event_inner(&mut self, event: &KeyEvent) -> bool {
1200 if self.inspector_key(event) {
1201 return true;
1202 }
1203 let root = self.key_event_root();
1204 if root.is_none() && self.id() != RootId::Primary {
1205 return false;
1206 }
1207 let route =
1208 cranpose_ui::KeyEventRoute::new(&mut self.shell.app.composition.applier_mut(), root);
1209 if self.dispatch_modifier_key(&route, event, true) {
1210 return true;
1211 }
1212 if route.focus_is_current() && self.on_focused_key_event(event) {
1213 return true;
1214 }
1215 if self.dispatch_modifier_key(&route, event, false) {
1216 return true;
1217 }
1218 if !route.focus_is_current() || cranpose_ui::text_field_focus::has_focused_field() {
1219 return false;
1220 }
1221 let handled = run_in_mutable_snapshot(|| cranpose_ui::dispatch_unhandled_key_event(event))
1222 .unwrap_or(false);
1223 if handled {
1224 self.mark_dirty();
1225 self.shell.app.request_layout_pass();
1226 }
1227 handled
1228 }
1229
1230 fn dispatch_modifier_key(
1231 &mut self,
1232 route: &cranpose_ui::KeyEventRoute,
1233 event: &KeyEvent,
1234 preview: bool,
1235 ) -> bool {
1236 let handled = run_in_mutable_snapshot(|| {
1237 let mut applier = self.shell.app.composition.applier_mut();
1238 if preview {
1239 route.dispatch_preview(&mut applier, event)
1240 } else {
1241 route.dispatch_bubble(&mut applier, event)
1242 }
1243 })
1244 .unwrap_or(false);
1245 if handled {
1246 self.mark_dirty();
1247 self.shell.app.request_layout_pass();
1248 }
1249 handled
1250 }
1251
1252 fn key_event_root(&self) -> Option<cranpose_core::NodeId> {
1253 self.surface()
1254 .modal_focus
1255 .last()
1256 .map(|entry| entry.0)
1257 .or_else(|| self.surface().root)
1258 .or_else(|| match self.id() {
1259 RootId::Primary => self.shell.app.composition.root(),
1260 RootId::Window(_) => None,
1261 })
1262 }
1263
1264 fn receives_focused_input(&mut self) -> bool {
1265 let root = self.key_event_root();
1266 cranpose_ui::KeyEventRoute::contains_focus(
1267 &mut self.shell.app.composition.applier_mut(),
1268 root,
1269 )
1270 }
1271
1272 fn on_focused_key_event(&mut self, event: &KeyEvent) -> bool {
1273 use KeyEventType::KeyDown;
1274
1275 if event.event_type == KeyDown && event.modifiers.command_or_ctrl() {
1276 #[cfg(all(
1277 feature = "clipboard-native",
1278 not(target_arch = "wasm32"),
1279 not(target_os = "android"),
1280 not(target_os = "ios")
1281 ))]
1282 {
1283 match event.key_code {
1284 KeyCode::C => {
1285 if let Some(text) = self.on_copy_inner() {
1286 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1287 return true;
1288 }
1289 }
1290 KeyCode::X => {
1291 if let Some(text) = self.on_cut_inner() {
1292 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1293 self.mark_dirty();
1294 self.shell.app.request_layout_pass();
1295 return true;
1296 }
1297 }
1298 KeyCode::V => {
1299 if let Some(text) = cranpose_ui::clipboard_session::clipboard_read_text()
1300 && self.on_paste_inner(&text)
1301 {
1302 return true;
1303 }
1304 }
1305 _ => {}
1306 }
1307 }
1308 }
1309
1310 if self.on_focus_key(event) {
1311 return true;
1312 }
1313
1314 if self.on_escape_key(event) {
1315 return true;
1316 }
1317
1318 if self.on_activation_key(event) || self.on_arrow_key(event) {
1319 return true;
1320 }
1321
1322 let handled =
1323 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_key_event(event))
1324 .unwrap_or(false);
1325
1326 if handled {
1327 self.mark_dirty();
1328 self.shell.app.request_layout_pass();
1329 }
1330
1331 handled
1332 }
1333
1334 pub fn on_paste(&mut self, text: &str) -> bool {
1337 if self.inspector_owns_keyboard() {
1338 return true;
1339 }
1340 let _event_handler = enter_event_handler_scope();
1341 let app_context = Rc::clone(&self.shell.app.app_context);
1342 app_context.enter(|| self.on_paste_inner(text))
1343 }
1344
1345 fn on_paste_inner(&mut self, text: &str) -> bool {
1346 if !self.receives_focused_input() {
1347 return false;
1348 }
1349 let handled =
1350 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1351 .unwrap_or(false);
1352
1353 if handled {
1354 self.mark_dirty();
1355 self.shell.app.request_layout_pass();
1356 }
1357
1358 handled
1359 }
1360
1361 pub fn on_copy(&mut self) -> Option<String> {
1364 let app_context = Rc::clone(&self.shell.app.app_context);
1365 if self.inspector_owns_keyboard() {
1366 return None;
1367 }
1368 app_context.enter(|| self.on_copy_inner())
1369 }
1370
1371 fn on_copy_inner(&mut self) -> Option<String> {
1372 if !self.receives_focused_input() {
1373 return None;
1374 }
1375 cranpose_ui::text_field_focus::dispatch_copy()
1376 }
1377
1378 pub fn on_cut(&mut self) -> Option<String> {
1381 let _event_handler = enter_event_handler_scope();
1382 if self.inspector_owns_keyboard() {
1383 return None;
1384 }
1385 let app_context = Rc::clone(&self.shell.app.app_context);
1386 app_context.enter(|| self.on_cut_inner())
1387 }
1388
1389 fn on_cut_inner(&mut self) -> Option<String> {
1390 if !self.receives_focused_input() {
1391 return None;
1392 }
1393 let text =
1394 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1395
1396 if text.is_some() {
1397 self.mark_dirty();
1398 self.shell.app.request_layout_pass();
1399 }
1400
1401 text
1402 }
1403
1404 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1412 if self.inspector_owns_keyboard() {
1413 return true;
1414 }
1415 let _event_handler = enter_event_handler_scope();
1416 let app_context = Rc::clone(&self.shell.app.app_context);
1417 app_context.enter(|| self.on_ime_preedit_inner(text, cursor))
1418 }
1419
1420 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1421 if !self.receives_focused_input() {
1422 return false;
1423 }
1424 let handled = run_in_mutable_snapshot(|| {
1425 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1426 })
1427 .unwrap_or(false);
1428
1429 if handled {
1430 self.mark_dirty();
1431 self.shell.app.request_layout_pass();
1432 }
1433
1434 handled
1435 }
1436
1437 pub fn on_ime_finish_composing(&mut self) -> bool {
1441 if self.inspector_owns_keyboard() {
1442 return true;
1443 }
1444 let _event_handler = enter_event_handler_scope();
1445 let app_context = Rc::clone(&self.shell.app.app_context);
1446 app_context.enter(|| self.on_ime_finish_composing_inner())
1447 }
1448
1449 fn on_ime_finish_composing_inner(&mut self) -> bool {
1450 if !self.receives_focused_input() {
1451 return false;
1452 }
1453 let handled =
1454 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1455 .unwrap_or(false);
1456
1457 if handled {
1458 self.mark_dirty();
1459 self.shell.app.request_layout_pass();
1460 }
1461
1462 handled
1463 }
1464
1465 pub fn on_ime_action(&mut self, action: cranpose_ui::ImeAction) -> bool {
1471 if self.inspector_owns_keyboard() {
1472 return true;
1473 }
1474 let _event_handler = enter_event_handler_scope();
1475 let app_context = Rc::clone(&self.shell.app.app_context);
1476 app_context.enter(|| {
1477 if !self.receives_focused_input() {
1478 return false;
1479 }
1480 let handled = run_in_mutable_snapshot(|| {
1481 cranpose_ui::text_field_focus::dispatch_ime_action(action)
1482 })
1483 .unwrap_or(false);
1484 if handled {
1485 self.mark_dirty();
1486 self.shell.app.request_layout_pass();
1487 }
1488 self.run_requested_focus_move();
1489 handled
1490 })
1491 }
1492
1493 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1497 if self.inspector_owns_keyboard() {
1498 return true;
1499 }
1500 let _event_handler = enter_event_handler_scope();
1501 let app_context = Rc::clone(&self.shell.app.app_context);
1502 app_context.enter(|| {
1503 if !self.receives_focused_input() {
1504 return false;
1505 }
1506 let handled = run_in_mutable_snapshot(|| {
1507 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(
1508 start_bytes,
1509 end_bytes,
1510 )
1511 })
1512 .unwrap_or(false);
1513
1514 if handled {
1515 self.mark_dirty();
1516 self.shell.app.request_layout_pass();
1517 }
1518
1519 handled
1520 })
1521 }
1522
1523 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1528 if self.inspector_owns_keyboard() {
1529 return true;
1530 }
1531 let _event_handler = enter_event_handler_scope();
1532 let app_context = Rc::clone(&self.shell.app.app_context);
1533 app_context.enter(|| {
1534 if !self.receives_focused_input() {
1535 return false;
1536 }
1537 let handled = run_in_mutable_snapshot(|| {
1538 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1539 })
1540 .unwrap_or(false);
1541
1542 if handled {
1543 self.mark_dirty();
1544 }
1545
1546 handled
1547 })
1548 }
1549
1550 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1554 let app_context = Rc::clone(&self.shell.app.app_context);
1555 app_context.enter(|| {
1556 self.receives_focused_input()
1557 .then(cranpose_ui::text_field_focus::focused_editor_state)
1558 .flatten()
1559 })
1560 }
1561
1562 pub fn ime_caret_geometry(
1566 &mut self,
1567 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1568 let app_context = Rc::clone(&self.shell.app.app_context);
1569 app_context.enter(|| {
1570 self.receives_focused_input()
1571 .then(cranpose_ui::text_field_focus::focused_caret_geometry)
1572 .flatten()
1573 })
1574 }
1575
1576 pub fn ime_field_bounds(&mut self) -> Option<cranpose_ui_graphics::Rect> {
1580 let app_context = Rc::clone(&self.shell.app.app_context);
1581 app_context.enter(|| {
1582 self.receives_focused_input()
1583 .then(cranpose_ui::text_field_focus::focused_field_bounds)
1584 .flatten()
1585 })
1586 }
1587
1588 pub fn ime_field_is_password(&mut self) -> bool {
1595 let app_context = Rc::clone(&self.shell.app.app_context);
1596 app_context.enter(|| {
1597 let Some(field) = self
1598 .receives_focused_input()
1599 .then(cranpose_ui::text_field_focus::focused_field_target)
1600 .flatten()
1601 else {
1602 return false;
1603 };
1604 let mut applier = self.shell.app.composition.applier_mut();
1605 applier
1606 .with_node::<LayoutNode, _>(field, |node| node.semantics_configuration())
1607 .or_else(|_| {
1608 applier.with_node::<SubcomposeLayoutNode, _>(field, |node| {
1609 node.semantics_configuration()
1610 })
1611 })
1612 .ok()
1613 .flatten()
1614 .is_some_and(|config| config.password)
1615 })
1616 }
1617
1618 pub fn clear_text_field_focus(&mut self) {
1621 let _event_handler = enter_event_handler_scope();
1622 let app_context = Rc::clone(&self.shell.app.app_context);
1623 app_context.enter(|| {
1624 if self.receives_focused_input() {
1625 cranpose_ui::text_field_focus::clear_focus();
1626 self.mark_dirty();
1627 self.shell.app.request_layout_pass();
1628 }
1629 });
1630 }
1631
1632 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1635 if self.inspector_owns_keyboard() {
1636 return true;
1637 }
1638 let _event_handler = enter_event_handler_scope();
1639 let app_context = Rc::clone(&self.shell.app.app_context);
1640 app_context.enter(|| self.on_ime_delete_surrounding_inner(before_bytes, after_bytes))
1641 }
1642
1643 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1644 if !self.receives_focused_input() {
1645 return false;
1646 }
1647 let handled = run_in_mutable_snapshot(|| {
1648 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1649 })
1650 .unwrap_or(false);
1651
1652 if handled {
1653 self.mark_dirty();
1654 self.shell.app.request_layout_pass();
1655 }
1656
1657 handled
1658 }
1659}
1660
1661impl<R> AppShell<R>
1662where
1663 R: Renderer,
1664 R::Error: Debug,
1665{
1666 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
1675 self.app.modifiers = Some(modifiers);
1676 }
1677
1678 pub fn modifiers(&self) -> Option<Modifiers> {
1682 self.app.modifiers
1683 }
1684
1685 pub fn rotary_scroll_factor(&self) -> f32 {
1688 self.app.rotary_scroll_factor
1689 }
1690
1691 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
1700 if factor.is_finite() && factor > 0.0 {
1701 self.app.rotary_scroll_factor = factor;
1702 }
1703 }
1704
1705 pub fn notify_app_paused(&mut self) {
1712 let app_context = Rc::clone(&self.app.app_context);
1713 app_context.enter(cranpose_ui::text_input_session::notify_app_paused);
1714 }
1715
1716 pub fn notify_app_resumed(&mut self) -> bool {
1724 let app_context = Rc::clone(&self.app.app_context);
1725 app_context.enter(cranpose_ui::text_input_session::notify_app_resumed)
1726 }
1727
1728 #[cfg(all(
1729 feature = "clipboard-native",
1730 target_os = "linux",
1731 not(target_arch = "wasm32")
1732 ))]
1733 pub fn set_primary_selection(&mut self, text: &str) {
1734 use arboard::{LinuxClipboardKind, SetExtLinux};
1735 if let Some(ref mut clipboard) = self.app.clipboard {
1736 let result = clipboard
1737 .set()
1738 .clipboard(LinuxClipboardKind::Primary)
1739 .text(text.to_string());
1740 if let Err(e) = result {
1741 log::debug!("Primary selection set failed: {e:?}");
1742 }
1743 }
1744 }
1745
1746 #[cfg(not(all(
1747 feature = "clipboard-native",
1748 target_os = "linux",
1749 not(target_arch = "wasm32")
1750 )))]
1751 pub fn set_primary_selection(&mut self, _text: &str) {}
1752
1753 #[cfg(all(
1754 feature = "clipboard-native",
1755 target_os = "linux",
1756 not(target_arch = "wasm32")
1757 ))]
1758 pub fn get_primary_selection(&mut self) -> Option<String> {
1759 use arboard::{GetExtLinux, LinuxClipboardKind};
1760 if let Some(ref mut clipboard) = self.app.clipboard {
1761 clipboard
1762 .get()
1763 .clipboard(LinuxClipboardKind::Primary)
1764 .text()
1765 .ok()
1766 } else {
1767 None
1768 }
1769 }
1770
1771 #[cfg(not(all(
1772 feature = "clipboard-native",
1773 target_os = "linux",
1774 not(target_arch = "wasm32")
1775 )))]
1776 pub fn get_primary_selection(&mut self) -> Option<String> {
1777 None
1778 }
1779
1780 pub fn sync_selection_to_primary(&mut self) {
1783 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1784 {
1785 if let Some(text) = self.on_copy() {
1786 self.set_primary_selection(&text);
1787 }
1788 }
1789 }
1790
1791 pub fn set_pointer_source(&mut self, source: PointerSource) {
1793 self.primary().set_pointer_source(source);
1794 }
1795
1796 pub fn pointer_source(&self) -> PointerSource {
1798 self.surfaces[0].pointer_source
1799 }
1800
1801 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
1803 self.primary().set_cursor(x, y)
1804 }
1805
1806 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
1808 self.primary().set_cursor_at_time(x, y, time_ms)
1809 }
1810
1811 pub fn set_cursor_at_event_time(
1813 &mut self,
1814 x: f32,
1815 y: f32,
1816 event_time: PointerEventTime,
1817 ) -> bool {
1818 self.primary().set_cursor_at_event_time(x, y, event_time)
1819 }
1820
1821 pub fn pointer_pressed(&mut self) -> bool {
1823 self.primary().pointer_pressed()
1824 }
1825
1826 pub fn accessibility_activate(&mut self, node_id: NodeId, canvas_key: Option<u64>) -> bool {
1828 self.primary().accessibility_activate(node_id, canvas_key)
1829 }
1830
1831 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
1833 self.primary().pointer_pressed_at_time(time_ms)
1834 }
1835
1836 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1838 self.primary().pointer_pressed_at_event_time(event_time)
1839 }
1840
1841 pub fn pointer_released(&mut self) -> bool {
1843 self.primary().pointer_released()
1844 }
1845
1846 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
1848 self.primary().pointer_released_at_position(x, y)
1849 }
1850
1851 pub fn pointer_released_at_position_time(
1854 &mut self,
1855 x: f32,
1856 y: f32,
1857 time_ms: Option<i64>,
1858 ) -> bool {
1859 self.primary()
1860 .pointer_released_at_position_time(x, y, time_ms)
1861 }
1862
1863 pub fn pointer_released_at_position_event_time(
1866 &mut self,
1867 x: f32,
1868 y: f32,
1869 event_time: PointerEventTime,
1870 ) -> bool {
1871 self.primary()
1872 .pointer_released_at_position_event_time(x, y, event_time)
1873 }
1874
1875 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
1877 self.primary().pointer_released_at_time(time_ms)
1878 }
1879
1880 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1882 self.primary().pointer_released_at_event_time(event_time)
1883 }
1884
1885 pub fn secondary_pointer_pressed(
1887 &mut self,
1888 pointer_id: u64,
1889 x: f32,
1890 y: f32,
1891 time_ms: Option<i64>,
1892 ) -> bool {
1893 self.primary()
1894 .secondary_pointer_pressed(pointer_id, x, y, time_ms)
1895 }
1896
1897 pub fn secondary_pointer_moved(
1899 &mut self,
1900 pointer_id: u64,
1901 x: f32,
1902 y: f32,
1903 time_ms: Option<i64>,
1904 ) -> bool {
1905 self.primary()
1906 .secondary_pointer_moved(pointer_id, x, y, time_ms)
1907 }
1908
1909 pub fn secondary_pointer_released(
1911 &mut self,
1912 pointer_id: u64,
1913 x: f32,
1914 y: f32,
1915 time_ms: Option<i64>,
1916 ) -> bool {
1917 self.primary()
1918 .secondary_pointer_released(pointer_id, x, y, time_ms)
1919 }
1920
1921 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
1923 self.primary().pointer_zoomed(zoom_factor)
1924 }
1925
1926 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
1928 self.primary().wheel_scrolled(wheel)
1929 }
1930
1931 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
1933 self.primary().pointer_scrolled(delta_x, delta_y)
1934 }
1935
1936 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
1938 where
1939 F: Fn(RotaryScrollEvent) -> bool + 'static,
1940 {
1941 self.primary().set_on_rotary_scroll(handler);
1942 }
1943
1944 pub fn clear_on_rotary_scroll(&mut self) {
1946 self.primary().clear_on_rotary_scroll();
1947 }
1948
1949 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
1951 self.primary()
1952 .rotary_scrolled_by_detents(detents, uptime_millis)
1953 }
1954
1955 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
1957 self.primary().rotary_scrolled(event)
1958 }
1959
1960 pub fn cancel_gesture(&mut self) {
1962 self.primary().cancel_gesture();
1963 }
1964
1965 pub fn cancel_gesture_unless_pressed(&mut self) {
1967 self.primary().cancel_gesture_unless_pressed();
1968 }
1969
1970 pub fn refresh_pointer_icon(&self) {
1974 self.surfaces[0].pointer_icon.refresh();
1975 }
1976
1977 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
1984 self.surfaces[0].pointer_icon.take_change()
1985 }
1986
1987 pub fn set_platform_text_input(
1994 &mut self,
1995 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
1996 ) {
1997 self.primary().set_platform_text_input(handler);
1998 }
1999
2000 pub fn dismiss_top_modal(&mut self) -> bool {
2002 self.primary().dismiss_top_modal()
2003 }
2004
2005 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
2007 self.primary().move_focus_in_context(direction)
2008 }
2009
2010 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
2012 self.primary().on_key_event(event)
2013 }
2014
2015 pub fn on_paste(&mut self, text: &str) -> bool {
2017 self.primary().on_paste(text)
2018 }
2019
2020 pub fn on_copy(&mut self) -> Option<String> {
2022 self.primary().on_copy()
2023 }
2024
2025 pub fn on_cut(&mut self) -> Option<String> {
2027 self.primary().on_cut()
2028 }
2029
2030 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
2032 self.primary().on_ime_preedit(text, cursor)
2033 }
2034
2035 pub fn on_ime_finish_composing(&mut self) -> bool {
2037 self.primary().on_ime_finish_composing()
2038 }
2039
2040 pub fn on_ime_action(&mut self, action: cranpose_ui::ImeAction) -> bool {
2042 self.primary().on_ime_action(action)
2043 }
2044
2045 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
2047 self.primary()
2048 .on_ime_set_composing_region(start_bytes, end_bytes)
2049 }
2050
2051 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
2053 self.primary().on_ime_set_selection(start_bytes, end_bytes)
2054 }
2055
2056 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
2058 self.primary().ime_editor_state()
2059 }
2060
2061 pub fn ime_caret_geometry(
2063 &mut self,
2064 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
2065 self.primary().ime_caret_geometry()
2066 }
2067
2068 pub fn ime_field_bounds(&mut self) -> Option<cranpose_ui_graphics::Rect> {
2070 self.primary().ime_field_bounds()
2071 }
2072
2073 pub fn ime_field_is_password(&mut self) -> bool {
2075 self.primary().ime_field_is_password()
2076 }
2077
2078 pub fn clear_text_field_focus(&mut self) {
2080 self.primary().clear_text_field_focus();
2081 }
2082
2083 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
2085 self.primary()
2086 .on_ime_delete_surrounding(before_bytes, after_bytes)
2087 }
2088}
2089
2090enum ActivationTarget {
2091 Direct(cranpose_foundation::SemanticsCustomAction),
2092 Edit(NodeId),
2093 Pointer(NodeId, Option<Rect>),
2094}
2095
2096fn activation_target(
2097 node: &cranpose_ui::SemanticsNode,
2098 node_id: NodeId,
2099 canvas_key: Option<u64>,
2100) -> Option<ActivationTarget> {
2101 if node.hidden {
2102 return None;
2103 }
2104 if node.node_id != node_id {
2105 return node
2106 .children
2107 .iter()
2108 .find_map(|child| activation_target(child, node_id, canvas_key));
2109 }
2110 if !node.enabled {
2111 return None;
2112 }
2113 if let Some(key) = canvas_key {
2114 let child = node
2115 .details()
2116 .canvas_children
2117 .iter()
2118 .find(|child| child.key == key)?;
2119 return (child.enabled && child.clickable)
2120 .then_some(ActivationTarget::Pointer(node_id, Some(child.bounds)));
2121 }
2122 if let Some(action) = &node.on_click {
2123 return Some(ActivationTarget::Direct(action.clone()));
2124 }
2125 if node.details().editable_text && node.focusable {
2126 return Some(ActivationTarget::Edit(node_id));
2127 }
2128 node.actions.first().map(|action| match action {
2129 cranpose_ui::SemanticsAction::Click { handler } => {
2130 ActivationTarget::Pointer(handler.node_id(), None)
2131 }
2132 })
2133}
2134
2135fn activation_input(
2136 root: &LayoutBox,
2137 node_id: NodeId,
2138 canvas_bounds: Option<Rect>,
2139) -> Option<(Point, Point, Rc<cranpose_ui::ModifierNodeSlices>)> {
2140 if root.node_id != node_id {
2141 return root
2142 .children
2143 .iter()
2144 .find_map(|child| activation_input(child, node_id, canvas_bounds));
2145 }
2146 let bounds = canvas_bounds.map_or(root.rect, |bounds| Rect {
2147 x: root.rect.x + bounds.x,
2148 y: root.rect.y + bounds.y,
2149 ..bounds
2150 });
2151 let position = Point {
2152 x: bounds.x + bounds.width * 0.5,
2153 y: bounds.y + bounds.height * 0.5,
2154 };
2155 let local = Point {
2156 x: position.x - root.rect.x,
2157 y: position.y - root.rect.y,
2158 };
2159 (bounds.width > 0.0 && bounds.height > 0.0 && position.x.is_finite() && position.y.is_finite())
2160 .then(|| (position, local, Rc::clone(&root.node_data.modifier_slices)))
2161}
2162
2163fn plain_key_down(event: &KeyEvent) -> bool {
2164 event.event_type == KeyEventType::KeyDown
2165 && !event.modifiers.ctrl
2166 && !event.modifiers.meta
2167 && !event.modifiers.alt
2168}