1use cranpose_ui::FocusDirection;
2
3use super::*;
4
5impl<R> SurfaceMut<'_, R>
6where
7 R: Renderer,
8 R::Error: Debug,
9{
10 fn pointer_event(
11 &self,
12 kind: PointerEventKind,
13 position: Point,
14 global_position: Point,
15 event_time: PointerEventTime,
16 ) -> PointerEvent {
17 let screen_position = self.surface().screen_origin.map(|origin| Point {
18 x: origin.x + global_position.x,
19 y: origin.y + global_position.y,
20 });
21 let mut event = PointerEvent::new(kind, position, global_position)
22 .with_time_ms(event_time.platform_time_ms)
23 .with_animation_time_nanos(event_time.animation_time_nanos)
24 .with_screen_position(screen_position);
25 event.modifiers = self.shell.app.modifiers;
26 event
27 }
28
29 fn resolve_gesture_targets(
30 &self,
31 pointer: PointerId,
32 ) -> Vec<<<R as Renderer>::Scene as RenderScene>::HitTarget> {
33 self.resolve_hit_path(pointer)
34 }
35
36 fn resolve_hit_path(
37 &self,
38 pointer: PointerId,
39 ) -> Vec<<<R as Renderer>::Scene as RenderScene>::HitTarget> {
40 let Some(node_ids) = self.surface().hit_path_tracker.dispatch_order(pointer) else {
41 return Vec::new();
42 };
43
44 let scene = self.surface().renderer.scene();
45 let targets: Vec<_> = node_ids
46 .iter()
47 .filter_map(|&id| scene.find_target(id))
48 .collect();
49 log::trace!(
50 target: "cranpose::input",
51 "resolve_hit_path pointer={pointer:?} cached={node_ids:?} resolved_count={}",
52 targets.len()
53 );
54 targets
55 }
56
57 fn dispatch_targets<I>(&mut self, targets: I, event: PointerEvent, stop_on_consume: bool)
58 where
59 I: IntoIterator<Item = <<R as Renderer>::Scene as RenderScene>::HitTarget>,
60 {
61 let mut applier = self.shell.app.composition.applier_mut();
62 for target in targets {
63 let node_id = target.node_id();
64 target.dispatch_with_applier(&mut applier, event.clone());
65 log::trace!(
66 target: "cranpose::input",
67 "dispatch {:?} node={} consumed={} stop_on_consume={}",
68 event.kind,
69 node_id,
70 event.is_consumed(),
71 stop_on_consume,
72 );
73 if stop_on_consume && event.is_consumed() {
74 break;
75 }
76 }
77 event.finish_post_dispatch();
78 }
79
80 fn dispatch_to_hits(
81 &mut self,
82 hits: &[<<R as Renderer>::Scene as RenderScene>::HitTarget],
83 event: PointerEvent,
84 ) -> bool {
85 let capture_paths = hits
86 .iter()
87 .map(|hit| hit.capture_path())
88 .collect::<Vec<_>>();
89 let targets = crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
90 .into_iter()
91 .filter_map(|node_id| self.surface().renderer.scene().find_target(node_id))
92 .collect::<Vec<_>>();
93
94 self.dispatch_targets(targets, event.clone(), true);
95
96 event.is_consumed()
97 }
98
99 pub fn set_pointer_source(&mut self, source: PointerSource) {
105 self.surface_mut().pointer_source = source;
106 }
107
108 pub fn pointer_source(&self) -> PointerSource {
110 self.surface().pointer_source
111 }
112
113 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
114 self.set_cursor_at_time(x, y, None)
115 }
116
117 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
124 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
125 self.set_cursor_at_event_time(x, y, event_time)
126 }
127
128 pub fn set_cursor_at_event_time(
130 &mut self,
131 x: f32,
132 y: f32,
133 event_time: PointerEventTime,
134 ) -> bool {
135 let _event_handler = enter_event_handler_scope();
136 let app_context = Rc::clone(&self.shell.app.app_context);
137 let result = app_context.enter(|| {
138 run_in_mutable_snapshot(|| self.set_cursor_inner(x, y, event_time)).unwrap_or(false)
139 });
140 self.route_drag_and_drop();
141 if result {
142 self.mark_dirty();
143 }
144 log::trace!(
145 target: "cranpose::input",
146 "set_cursor ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
147 event_time.platform_time_ms,
148 event_time.animation_time_nanos,
149 );
150 result
151 }
152
153 fn set_cursor_inner(&mut self, x: f32, y: f32, event_time: PointerEventTime) -> bool {
154 self.surface_mut().cursor = (x, y);
155 if self.inspector_move(x, y) {
156 return true;
157 }
158
159 if self.surface().buttons_pressed != PointerButtons::NONE {
160 if self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
161 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
162 if !targets.is_empty() {
163 let event = self
164 .pointer_event(
165 PointerEventKind::Move,
166 Point { x, y },
167 Point { x, y },
168 event_time,
169 )
170 .with_buttons(self.surface().buttons_pressed)
171 .with_source(self.surface().pointer_source);
172 self.dispatch_targets(targets, event, false);
173 return true;
174 }
175
176 return false;
177 }
178
179 return false;
180 }
181
182 let hits = if self.inspector_blocks_pointer(x, y) {
183 Vec::new()
184 } else {
185 self.surface().renderer.scene().hit_test(x, y)
186 };
187 let new_ids: Vec<NodeId> = hits.iter().map(|h| h.node_id()).collect();
188
189 let pos = Point { x, y };
190 let previously_hovered = self.surface().hovered_nodes.clone();
191 for old_id in previously_hovered {
192 if !new_ids.contains(&old_id)
193 && let Some(target) = self.surface().renderer.scene().find_target(old_id)
194 {
195 let exit_event = self
196 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
197 .with_buttons(self.surface().buttons_pressed)
198 .with_source(self.surface().pointer_source);
199 self.dispatch_targets(std::iter::once(target), exit_event, false);
200 }
201 }
202
203 for hit in &hits {
204 if !self.surface().hovered_nodes.contains(&hit.node_id()) {
205 let enter_event = self
206 .pointer_event(PointerEventKind::Enter, pos, pos, event_time)
207 .with_buttons(self.surface().buttons_pressed)
208 .with_source(self.surface().pointer_source);
209 self.dispatch_targets(std::iter::once(hit.clone()), enter_event, false);
210 }
211 }
212
213 self.surface_mut().hovered_nodes = new_ids;
214 self.apply_hovered_pointer_icon(&hits);
215
216 if !hits.is_empty() {
217 let event = self
218 .pointer_event(PointerEventKind::Move, pos, pos, event_time)
219 .with_buttons(self.surface().buttons_pressed)
220 .with_source(self.surface().pointer_source);
221 self.dispatch_targets(hits, event, true);
222 true
223 } else {
224 false
225 }
226 }
227
228 pub fn accessibility_activate_at(&mut self, x: f32, y: f32) -> bool {
232 let event_time = self.shell.app.realtime_pointer_event_time(None);
233 let _event_handler = enter_event_handler_scope();
234 let app_context = Rc::clone(&self.shell.app.app_context);
235 let result = app_context.enter(|| {
236 run_in_mutable_snapshot(|| {
237 self.surface_mut().cursor = (x, y);
238 let pressed = self.pointer_pressed_inner(event_time);
239 self.release_app_pointer(event_time) || pressed
240 })
241 .unwrap_or(false)
242 });
243 if result {
244 self.mark_dirty();
245 }
246 result
247 }
248
249 pub fn pointer_pressed(&mut self) -> bool {
250 self.pointer_pressed_at_time(None)
251 }
252
253 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
256 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
257 self.pointer_pressed_at_event_time(event_time)
258 }
259
260 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
262 let (cursor_x, cursor_y) = self.surface().cursor;
263 if self.inspector_press(cursor_x, cursor_y) {
264 return true;
265 }
266 if self.dev_overlay_press(cursor_x, cursor_y) {
267 return true;
268 }
269 let _event_handler = enter_event_handler_scope();
270 let app_context = Rc::clone(&self.shell.app.app_context);
271 let result = app_context.enter(|| {
272 run_in_mutable_snapshot(|| self.pointer_pressed_inner(event_time)).unwrap_or(false)
273 });
274 if result {
275 self.mark_dirty();
276 }
277 log::trace!(
278 target: "cranpose::input",
279 "pointer_pressed time_ms={:?} animation_time_nanos={} -> {result}",
280 event_time.platform_time_ms,
281 event_time.animation_time_nanos,
282 );
283 result
284 }
285
286 fn pointer_pressed_inner(&mut self, event_time: PointerEventTime) -> bool {
287 self.activate();
288 self.note_focus_moved_by_keyboard(false);
289 self.surface_mut()
290 .buttons_pressed
291 .insert(PointerButton::Primary);
292
293 let (cursor_x, cursor_y) = self.surface().cursor;
294 let mut hits = self.surface().renderer.scene().hit_test(cursor_x, cursor_y);
295 if self.surface().pointer_source.is_touch_like()
296 && let Some(near) = self
297 .surface()
298 .renderer
299 .scene()
300 .hit_test_near(cursor_x, cursor_y)
301 && hits.iter().all(|hit| {
302 hit.node_id() != near.node_id() && near.capture_path().contains(&hit.node_id())
303 })
304 {
305 hits.insert(0, near);
306 }
307 if hits.is_empty() {
308 self.surface_mut()
309 .hit_path_tracker
310 .remove_path(PointerId::PRIMARY);
311 false
312 } else {
313 let event = self
314 .pointer_event(
315 PointerEventKind::Down,
316 Point {
317 x: self.surface().cursor.0,
318 y: self.surface().cursor.1,
319 },
320 Point {
321 x: self.surface().cursor.0,
322 y: self.surface().cursor.1,
323 },
324 event_time,
325 )
326 .with_buttons(self.surface().buttons_pressed)
327 .with_source(self.surface().pointer_source);
328
329 let mut delivered_capture_paths = Vec::new();
330 let (app, surface) = self.parts();
331 let mut applier = app.composition.applier_mut();
332 for hit in hits {
333 let node_id = hit.node_id();
334 delivered_capture_paths.push(hit.capture_path());
335 hit.dispatch_with_applier(&mut applier, event.clone());
336 log::trace!(
337 target: "cranpose::input",
338 "dispatch {:?} node={} consumed={} stop_on_consume=true",
339 event.kind,
340 node_id,
341 event.is_consumed(),
342 );
343 if event.is_consumed() {
344 break;
345 }
346 }
347
348 surface
349 .hit_path_tracker
350 .add_hit_path(PointerId::PRIMARY, delivered_capture_paths);
351 log::trace!(
352 target: "cranpose::input",
353 "pointer_pressed_inner cached_hit_path={:?}",
354 surface.hit_path_tracker.get_path(PointerId::PRIMARY),
355 );
356
357 true
358 }
359 }
360
361 pub fn pointer_released(&mut self) -> bool {
362 self.pointer_released_at_time(None)
363 }
364
365 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
378 self.pointer_released_at_position_time(x, y, None)
379 }
380
381 pub fn pointer_released_at_position_time(
385 &mut self,
386 x: f32,
387 y: f32,
388 time_ms: Option<i64>,
389 ) -> bool {
390 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
391 self.pointer_released_at_position_event_time(x, y, event_time)
392 }
393
394 pub fn pointer_released_at_position_event_time(
396 &mut self,
397 x: f32,
398 y: f32,
399 event_time: PointerEventTime,
400 ) -> bool {
401 let _event_handler = enter_event_handler_scope();
402 let app_context = Rc::clone(&self.shell.app.app_context);
403 let result = app_context.enter(|| {
404 run_in_mutable_snapshot(|| {
405 self.surface_mut().cursor = (x, y);
406 self.pointer_released_inner(event_time)
407 })
408 .unwrap_or(false)
409 });
410 self.route_drag_and_drop();
411 if result {
412 self.mark_dirty();
413 }
414 log::trace!(
415 target: "cranpose::input",
416 "pointer_released_at_position ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
417 event_time.platform_time_ms,
418 event_time.animation_time_nanos,
419 );
420 result
421 }
422
423 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
426 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
427 self.pointer_released_at_event_time(event_time)
428 }
429
430 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
432 let _event_handler = enter_event_handler_scope();
433 let app_context = Rc::clone(&self.shell.app.app_context);
434 let result = app_context.enter(|| {
435 run_in_mutable_snapshot(|| self.pointer_released_inner(event_time)).unwrap_or(false)
436 });
437 self.route_drag_and_drop();
438 if result {
439 self.mark_dirty();
440 }
441 log::trace!(
442 target: "cranpose::input",
443 "pointer_released time_ms={:?} animation_time_nanos={} -> {result}",
444 event_time.platform_time_ms,
445 event_time.animation_time_nanos,
446 );
447 result
448 }
449
450 fn pointer_released_inner(&mut self, event_time: PointerEventTime) -> bool {
451 if self.surface_mut().inspector.release_pointer() {
452 return true;
453 }
454 self.release_app_pointer(event_time)
455 }
456
457 fn release_app_pointer(&mut self, event_time: PointerEventTime) -> bool {
458 self.surface_mut()
459 .buttons_pressed
460 .remove(PointerButton::Primary);
461 let corrected_buttons = self.surface().buttons_pressed;
462 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
463
464 self.surface_mut()
465 .hit_path_tracker
466 .remove_path(PointerId::PRIMARY);
467
468 if !targets.is_empty() {
469 let event = self
470 .pointer_event(
471 PointerEventKind::Up,
472 Point {
473 x: self.surface().cursor.0,
474 y: self.surface().cursor.1,
475 },
476 Point {
477 x: self.surface().cursor.0,
478 y: self.surface().cursor.1,
479 },
480 event_time,
481 )
482 .with_buttons(corrected_buttons)
483 .with_source(self.surface().pointer_source);
484
485 self.dispatch_targets(targets, event, false);
486 true
487 } else {
488 false
489 }
490 }
491
492 pub fn secondary_pointer_pressed(
501 &mut self,
502 pointer_id: u64,
503 x: f32,
504 y: f32,
505 time_ms: Option<i64>,
506 ) -> bool {
507 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
508 self.dispatch_secondary_pointer(PointerEventKind::Down, pointer_id, x, y, event_time)
509 }
510
511 pub fn secondary_pointer_moved(
513 &mut self,
514 pointer_id: u64,
515 x: f32,
516 y: f32,
517 time_ms: Option<i64>,
518 ) -> bool {
519 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
520 self.dispatch_secondary_pointer(PointerEventKind::Move, pointer_id, x, y, event_time)
521 }
522
523 pub fn secondary_pointer_released(
525 &mut self,
526 pointer_id: u64,
527 x: f32,
528 y: f32,
529 time_ms: Option<i64>,
530 ) -> bool {
531 let event_time = self.shell.app.realtime_pointer_event_time(time_ms);
532 self.dispatch_secondary_pointer(PointerEventKind::Up, pointer_id, x, y, event_time)
533 }
534
535 fn dispatch_secondary_pointer(
536 &mut self,
537 kind: PointerEventKind,
538 pointer_id: u64,
539 x: f32,
540 y: f32,
541 event_time: PointerEventTime,
542 ) -> bool {
543 if pointer_id == 0 {
544 log::warn!(
545 target: "cranpose::input",
546 "secondary pointer dispatch called with the primary pointer id"
547 );
548 return false;
549 }
550
551 let _event_handler = enter_event_handler_scope();
552 let app_context = Rc::clone(&self.shell.app.app_context);
553 let result = app_context.enter(|| {
554 run_in_mutable_snapshot(|| {
555 if !self.surface().hit_path_tracker.has_path(PointerId::PRIMARY) {
556 return false;
557 }
558 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
559 if targets.is_empty() {
560 return false;
561 }
562 let pos = Point { x, y };
563 let event = self
564 .pointer_event(kind, pos, pos, event_time)
565 .with_buttons(self.surface().buttons_pressed)
566 .with_id(pointer_id)
567 .with_source(self.surface().pointer_source);
568 self.dispatch_targets(targets, event, false);
569 true
570 })
571 .unwrap_or(false)
572 });
573 if result {
574 self.mark_dirty();
575 }
576 log::trace!(
577 target: "cranpose::input",
578 "secondary_pointer {kind:?} id={pointer_id} ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
579 event_time.platform_time_ms,
580 event_time.animation_time_nanos,
581 );
582 result
583 }
584
585 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
591 let event_time = self.shell.app.realtime_pointer_event_time(None);
592 let _event_handler = enter_event_handler_scope();
593 let app_context = Rc::clone(&self.shell.app.app_context);
594 let result = app_context.enter(|| {
595 run_in_mutable_snapshot(|| self.pointer_zoomed_inner(zoom_factor, event_time))
596 .unwrap_or(false)
597 });
598 if result {
599 self.mark_dirty();
600 }
601 log::trace!(
602 target: "cranpose::input",
603 "pointer_zoomed factor={zoom_factor:.4} -> {result}"
604 );
605 result
606 }
607
608 fn pointer_zoomed_inner(&mut self, zoom_factor: f32, event_time: PointerEventTime) -> bool {
609 if !zoom_factor.is_finite() || zoom_factor <= 0.0 || zoom_factor == 1.0 {
610 return false;
611 }
612
613 let hits = self
614 .surface()
615 .renderer
616 .scene()
617 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
618 if hits.is_empty() {
619 return false;
620 }
621
622 let pos = Point {
623 x: self.surface().cursor.0,
624 y: self.surface().cursor.1,
625 };
626 let event = self
627 .pointer_event(PointerEventKind::Zoom, pos, pos, event_time)
628 .with_buttons(self.surface().buttons_pressed)
629 .with_zoom_delta(zoom_factor)
630 .with_source(self.surface().pointer_source);
631
632 self.dispatch_to_hits(&hits, event)
633 }
634
635 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
660 if self.inspector_scroll(wheel.delta.y) {
661 return true;
662 }
663 if wheel.is_zoom() {
664 let zoom_factor = wheel.zoom_factor();
665 log::trace!(
666 target: "cranpose::input",
667 "wheel zoom factor={zoom_factor:.4}"
668 );
669 return self.pointer_zoomed(zoom_factor);
670 }
671
672 let rotary =
673 RotaryScrollEvent::from_wheel_pixels(wheel.delta.y, wheel.delta.x, wheel.uptime_millis);
674 if self.rotary_scrolled(rotary) {
675 return true;
676 }
677
678 let delta = wheel.scroll_delta();
679 log::trace!(
680 target: "cranpose::input",
681 "wheel delta ({:.2},{:.2}) alt={}",
682 delta.x,
683 delta.y,
684 wheel.modifiers.alt
685 );
686 self.pointer_scrolled(delta.x, delta.y)
687 }
688
689 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
697 if self.inspector_scroll(delta_y) {
698 return true;
699 }
700 let event_time = self.shell.app.realtime_pointer_event_time(None);
701 let _event_handler = enter_event_handler_scope();
702 let app_context = Rc::clone(&self.shell.app.app_context);
703 let result = app_context.enter(|| {
704 run_in_mutable_snapshot(|| self.pointer_scrolled_inner(delta_x, delta_y, event_time))
705 .unwrap_or(false)
706 });
707 if result {
708 self.mark_dirty();
709 }
710 log::trace!(
711 target: "cranpose::input",
712 "pointer_scrolled ({delta_x:.2},{delta_y:.2}) -> {result}"
713 );
714 result
715 }
716
717 fn pointer_scrolled_inner(
718 &mut self,
719 delta_x: f32,
720 delta_y: f32,
721 event_time: PointerEventTime,
722 ) -> bool {
723 if delta_x.abs() <= f32::EPSILON && delta_y.abs() <= f32::EPSILON {
724 return false;
725 }
726
727 let hits = self
728 .surface()
729 .renderer
730 .scene()
731 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
732 if hits.is_empty() {
733 return false;
734 }
735
736 let event = self
737 .pointer_event(
738 PointerEventKind::Scroll,
739 Point {
740 x: self.surface().cursor.0,
741 y: self.surface().cursor.1,
742 },
743 Point {
744 x: self.surface().cursor.0,
745 y: self.surface().cursor.1,
746 },
747 event_time,
748 )
749 .with_buttons(self.surface().buttons_pressed)
750 .with_scroll_delta(Point {
751 x: delta_x,
752 y: delta_y,
753 })
754 .with_source(self.surface().pointer_source);
755
756 self.dispatch_to_hits(&hits, event)
757 }
758
759 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
770 where
771 F: Fn(RotaryScrollEvent) -> bool + 'static,
772 {
773 self.surface_mut().on_rotary_scroll = Some(Rc::new(handler));
774 }
775
776 pub fn clear_on_rotary_scroll(&mut self) {
778 self.surface_mut().on_rotary_scroll = None;
779 }
780
781 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
787 let factor = self.shell.app.rotary_scroll_factor;
788 self.rotary_scrolled(RotaryScrollEvent::from_detents(
789 detents,
790 factor,
791 factor,
792 uptime_millis,
793 ))
794 }
795
796 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
817 let _event_handler = enter_event_handler_scope();
818 let app_context = Rc::clone(&self.shell.app.app_context);
819 let result = app_context.enter(|| {
820 run_in_mutable_snapshot(|| self.rotary_scrolled_inner(event)).unwrap_or(false)
821 });
822 if result {
823 self.mark_dirty();
824 }
825 log::trace!(
826 target: "cranpose::input",
827 "rotary_scrolled v={:.2} h={:.2} uptime={} -> {result}",
828 event.vertical_scroll_pixels,
829 event.horizontal_scroll_pixels,
830 event.uptime_millis,
831 );
832 result
833 }
834
835 fn rotary_scrolled_inner(&mut self, rotary: RotaryScrollEvent) -> bool {
836 if rotary.is_empty() {
837 return false;
838 }
839
840 let bubble_order = self.rotary_dispatch_order();
841 let position = Point {
842 x: self.surface().cursor.0,
843 y: self.surface().cursor.1,
844 };
845
846 if !bubble_order.is_empty() {
847 let capture_targets = bubble_order
848 .iter()
849 .rev()
850 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
851 .collect::<Vec<_>>();
852 let mut capture_event =
853 PointerEvent::rotary(PointerEventKind::RotaryScrollPre, rotary, position);
854 capture_event.modifiers = self.shell.app.modifiers;
855 self.dispatch_targets(capture_targets, capture_event.clone(), true);
856 if capture_event.is_consumed() {
857 return true;
858 }
859
860 let bubble_targets = bubble_order
861 .iter()
862 .filter_map(|&node_id| self.surface().renderer.scene().find_target(node_id))
863 .collect::<Vec<_>>();
864 let mut bubble_event =
865 PointerEvent::rotary(PointerEventKind::RotaryScroll, rotary, position);
866 bubble_event.modifiers = self.shell.app.modifiers;
867 self.dispatch_targets(bubble_targets, bubble_event.clone(), true);
868 if bubble_event.is_consumed() {
869 return true;
870 }
871 }
872
873 if let Some(handler) = self.surface().on_rotary_scroll.clone() {
874 return handler(rotary);
875 }
876
877 false
878 }
879
880 fn rotary_dispatch_order(&self) -> Vec<NodeId> {
881 if let Some(focused) = cranpose_ui::active_focus_target()
882 && let Some(target) = self.surface().renderer.scene().find_target(focused)
883 {
884 let path = target.capture_path();
885 if !path.is_empty() {
886 return crate::hit_path_tracker::dispatch_order_for_paths(&[path]);
887 }
888 }
889
890 let hits = self
891 .surface()
892 .renderer
893 .scene()
894 .hit_test(self.surface().cursor.0, self.surface().cursor.1);
895 if hits.is_empty() {
896 return Vec::new();
897 }
898 let capture_paths = hits
899 .iter()
900 .map(|hit| hit.capture_path())
901 .collect::<Vec<_>>();
902 crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
903 }
904
905 pub fn cancel_gesture(&mut self) {
911 let event_time = self.shell.app.realtime_pointer_event_time(None);
912 let _event_handler = enter_event_handler_scope();
913 let app_context = Rc::clone(&self.shell.app.app_context);
914 let _ = app_context.enter(|| {
915 run_in_mutable_snapshot(|| {
916 self.cancel_gesture_inner(event_time);
917 })
918 });
919 self.route_drag_and_drop();
920 }
921
922 fn route_drag_and_drop(&mut self) {
923 let app_context = Rc::clone(&self.shell.app.app_context);
924 let source = self.index;
925 let shell = &mut *self.shell;
926 let routed = app_context.enter(|| {
927 run_in_mutable_snapshot(|| {
928 app_context
929 .drag_and_drop()
930 .route(|point| shell.drag_and_drop_target_at(point, source))
931 })
932 .unwrap_or(false)
933 });
934 if routed {
935 shell.mark_all_dirty();
936 }
937 }
938
939 pub fn cancel_gesture_unless_pressed(&mut self) {
948 if self.surface().buttons_pressed != PointerButtons::NONE
949 || self.surface().inspector.pointer_captured
950 {
951 return;
952 }
953 self.cancel_gesture();
954 }
955
956 fn cancel_gesture_inner(&mut self, event_time: PointerEventTime) {
957 self.surface_mut().inspector.cancel_pointer();
958 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
959
960 self.surface_mut().hit_path_tracker.clear();
961 self.surface_mut().buttons_pressed = PointerButtons::NONE;
962
963 if !targets.is_empty() {
964 let event = self
965 .pointer_event(
966 PointerEventKind::Cancel,
967 Point {
968 x: self.surface().cursor.0,
969 y: self.surface().cursor.1,
970 },
971 Point {
972 x: self.surface().cursor.0,
973 y: self.surface().cursor.1,
974 },
975 event_time,
976 )
977 .with_source(self.surface().pointer_source);
978
979 self.dispatch_targets(targets, event, false);
980 }
981
982 let pos = Point {
983 x: self.surface().cursor.0,
984 y: self.surface().cursor.1,
985 };
986 let hovered_nodes = self.surface().hovered_nodes.clone();
987 for node_id in hovered_nodes {
988 if let Some(target) = self.surface().renderer.scene().find_target(node_id) {
989 let exit_event = self
990 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
991 .with_source(self.surface().pointer_source);
992 self.dispatch_targets(std::iter::once(target), exit_event, false);
993 }
994 }
995 self.surface_mut().hovered_nodes.clear();
996 self.surface().pointer_icon.set(PointerIcon::DEFAULT);
997 }
998
999 fn apply_hovered_pointer_icon(
1000 &self,
1001 hits: &[<<R as Renderer>::Scene as RenderScene>::HitTarget],
1002 ) {
1003 let icon = hits
1004 .iter()
1005 .find_map(HitTestTarget::pointer_icon)
1006 .unwrap_or(PointerIcon::DEFAULT);
1007 self.surface().pointer_icon.set(icon);
1008 }
1009
1010 fn on_focus_key(&mut self, event: &KeyEvent) -> bool {
1011 if !plain_key_down(event) || event.key_code != KeyCode::Tab {
1012 return false;
1013 }
1014 self.on_group_navigation_key(event)
1015 }
1016
1017 fn on_group_navigation_key(&mut self, event: &KeyEvent) -> bool {
1018 let focused = cranpose_ui::active_focus_target();
1019 let target = self.with_layout_tree(|tree| {
1020 cranpose_ui::keyboard_focus_target(
1021 tree?,
1022 focused,
1023 event.key_code,
1024 event.modifiers.shift,
1025 )
1026 });
1027 let Some(target) = target else {
1028 return false;
1029 };
1030 let moved =
1031 run_in_mutable_snapshot(|| cranpose_ui::request_focus_from_platform(target.node_id))
1032 .unwrap_or(false);
1033 if moved {
1034 self.mark_dirty();
1035 self.note_focus_moved_by_keyboard(true);
1036 if target.activate {
1037 self.on_activation_key(&KeyEvent::key_down(KeyCode::Space, " "));
1038 }
1039 }
1040 moved
1041 }
1042
1043 fn on_activation_key(&mut self, event: &KeyEvent) -> bool {
1044 if !plain_key_down(event)
1045 || !matches!(event.key_code, KeyCode::Enter | KeyCode::Space)
1046 || cranpose_ui::text_field_focus::has_focused_field()
1047 {
1048 return false;
1049 }
1050 let Some(focused) = cranpose_ui::active_focus_target() else {
1051 return false;
1052 };
1053 let center = self.with_layout_tree(|layout_tree| {
1054 layout_tree
1055 .map(cranpose_ui::collect_focus_order)
1056 .unwrap_or_default()
1057 .iter()
1058 .find(|entry| entry.node_id == focused)
1059 .map(cranpose_ui::FocusEntry::center)
1060 });
1061 let Some((x, y)) = center else {
1062 return false;
1063 };
1064 self.set_cursor(x, y);
1065 let pressed = self.pointer_pressed();
1066 let released = self.pointer_released_at_position(x, y);
1067 self.note_focus_moved_by_keyboard(true);
1068 pressed || released
1069 }
1070
1071 fn on_arrow_key(&mut self, event: &KeyEvent) -> bool {
1072 if !plain_key_down(event) || cranpose_ui::text_field_focus::has_focused_field() {
1073 return false;
1074 }
1075 if self.on_group_navigation_key(event) {
1076 return true;
1077 }
1078 let direction = match event.key_code {
1079 KeyCode::ArrowLeft => FocusDirection::Left,
1080 KeyCode::ArrowRight => FocusDirection::Right,
1081 KeyCode::ArrowUp => FocusDirection::Up,
1082 KeyCode::ArrowDown => FocusDirection::Down,
1083 _ => return false,
1084 };
1085 let Some(focused) = cranpose_ui::active_focus_target() else {
1086 return false;
1087 };
1088 let order = self.with_layout_tree(|layout_tree| {
1089 let layout_tree = layout_tree?;
1090 let group = cranpose_ui::selectable_group_of(layout_tree, focused)?;
1091 Some(cranpose_ui::collect_focus_order_under(layout_tree, group))
1092 });
1093 order.is_some_and(|order| self.move_focus_in_order(order, direction))
1094 }
1095
1096 fn note_focus_moved_by_keyboard(&mut self, keyboard: bool) {
1097 if cranpose_ui::set_keyboard_focus_visible(keyboard) {
1098 cranpose_ui::request_render_invalidation();
1099 self.mark_dirty();
1100 }
1101 }
1102
1103 fn on_escape_key(&mut self, event: &KeyEvent) -> bool {
1104 if event.event_type != KeyEventType::KeyDown || event.key_code != KeyCode::Escape {
1105 return false;
1106 }
1107 self.dismiss_top_modal_in_context()
1108 }
1109
1110 pub fn dismiss_top_modal(&mut self) -> bool {
1113 let _event_handler = enter_event_handler_scope();
1114 let app_context = Rc::clone(&self.shell.app.app_context);
1115 app_context.enter(|| self.dismiss_top_modal_in_context())
1116 }
1117
1118 fn dismiss_top_modal_in_context(&mut self) -> bool {
1119 let closed = run_in_mutable_snapshot(|| {
1120 if cranpose_ui::modal_depth() > 0 {
1121 cranpose_ui::dispatch_modal_back()
1122 } else {
1123 cranpose_ui::dismiss_top_popup()
1124 }
1125 })
1126 .unwrap_or(false);
1127 if closed {
1128 self.mark_dirty();
1129 }
1130 closed
1131 }
1132
1133 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1136 let order = self.with_layout_tree(|layout_tree| {
1137 layout_tree
1138 .map(cranpose_ui::collect_focus_order)
1139 .unwrap_or_default()
1140 });
1141 self.move_focus_in_order(order, direction)
1142 }
1143
1144 fn move_focus_in_order(
1145 &mut self,
1146 order: Vec<cranpose_ui::FocusEntry>,
1147 direction: FocusDirection,
1148 ) -> bool {
1149 cranpose_ui::set_focus_order(order);
1150 let moved = run_in_mutable_snapshot(|| cranpose_ui::FocusManager.move_focus(direction))
1151 .unwrap_or(false);
1152 if moved {
1153 self.mark_dirty();
1154 self.note_focus_moved_by_keyboard(true);
1155 }
1156 moved
1157 }
1158
1159 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1160 let _event_handler = enter_event_handler_scope();
1161 let app_context = Rc::clone(&self.shell.app.app_context);
1162 app_context.enter(|| self.on_key_event_inner(event))
1163 }
1164
1165 fn on_key_event_inner(&mut self, event: &KeyEvent) -> bool {
1166 if self.inspector_key(event) {
1167 return true;
1168 }
1169 use KeyEventType::KeyDown;
1170
1171 if event.event_type == KeyDown && event.modifiers.command_or_ctrl() {
1172 #[cfg(all(
1173 feature = "clipboard-native",
1174 not(target_arch = "wasm32"),
1175 not(target_os = "android"),
1176 not(target_os = "ios")
1177 ))]
1178 {
1179 match event.key_code {
1180 KeyCode::C => {
1181 if let Some(text) = self.on_copy_inner() {
1182 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1183 return true;
1184 }
1185 }
1186 KeyCode::X => {
1187 if let Some(text) = self.on_cut_inner() {
1188 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1189 self.mark_dirty();
1190 self.shell.app.request_layout_pass();
1191 return true;
1192 }
1193 }
1194 KeyCode::V => {
1195 if let Some(text) = cranpose_ui::clipboard_session::clipboard_read_text()
1196 && self.on_paste_inner(&text)
1197 {
1198 return true;
1199 }
1200 }
1201 _ => {}
1202 }
1203 }
1204 }
1205
1206 if self.on_focus_key(event) {
1207 return true;
1208 }
1209
1210 if self.on_escape_key(event) {
1211 return true;
1212 }
1213
1214 if self.on_activation_key(event) || self.on_arrow_key(event) {
1215 return true;
1216 }
1217
1218 if !cranpose_ui::text_field_focus::has_focused_field() {
1219 return false;
1220 }
1221
1222 let handled =
1223 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_key_event(event))
1224 .unwrap_or(false);
1225
1226 if handled {
1227 self.mark_dirty();
1228 self.shell.app.request_layout_pass();
1229 }
1230
1231 handled
1232 }
1233
1234 pub fn on_paste(&mut self, text: &str) -> bool {
1238 if self.inspector_owns_keyboard() {
1239 return true;
1240 }
1241 let _event_handler = enter_event_handler_scope();
1242 let app_context = Rc::clone(&self.shell.app.app_context);
1243 app_context.enter(|| self.on_paste_inner(text))
1244 }
1245
1246 fn on_paste_inner(&mut self, text: &str) -> bool {
1247 let handled =
1248 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1249 .unwrap_or(false);
1250
1251 if handled {
1252 self.mark_dirty();
1253 self.shell.app.request_layout_pass();
1254 }
1255
1256 handled
1257 }
1258
1259 pub fn on_copy(&mut self) -> Option<String> {
1263 let app_context = Rc::clone(&self.shell.app.app_context);
1264 if self.inspector_owns_keyboard() {
1265 return None;
1266 }
1267 app_context.enter(|| self.on_copy_inner())
1268 }
1269
1270 fn on_copy_inner(&mut self) -> Option<String> {
1271 cranpose_ui::text_field_focus::dispatch_copy()
1272 }
1273
1274 pub fn on_cut(&mut self) -> Option<String> {
1278 let _event_handler = enter_event_handler_scope();
1279 if self.inspector_owns_keyboard() {
1280 return None;
1281 }
1282 let app_context = Rc::clone(&self.shell.app.app_context);
1283 app_context.enter(|| self.on_cut_inner())
1284 }
1285
1286 fn on_cut_inner(&mut self) -> Option<String> {
1287 let text =
1288 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1289
1290 if text.is_some() {
1291 self.mark_dirty();
1292 self.shell.app.request_layout_pass();
1293 }
1294
1295 text
1296 }
1297
1298 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1306 if self.inspector_owns_keyboard() {
1307 return true;
1308 }
1309 let _event_handler = enter_event_handler_scope();
1310 let app_context = Rc::clone(&self.shell.app.app_context);
1311 app_context.enter(|| self.on_ime_preedit_inner(text, cursor))
1312 }
1313
1314 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1315 let handled = run_in_mutable_snapshot(|| {
1316 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1317 })
1318 .unwrap_or(false);
1319
1320 if handled {
1321 self.mark_dirty();
1322 self.shell.app.request_layout_pass();
1323 }
1324
1325 handled
1326 }
1327
1328 pub fn on_ime_finish_composing(&mut self) -> bool {
1332 if self.inspector_owns_keyboard() {
1333 return true;
1334 }
1335 let _event_handler = enter_event_handler_scope();
1336 let app_context = Rc::clone(&self.shell.app.app_context);
1337 app_context.enter(|| self.on_ime_finish_composing_inner())
1338 }
1339
1340 fn on_ime_finish_composing_inner(&mut self) -> bool {
1341 let handled =
1342 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1343 .unwrap_or(false);
1344
1345 if handled {
1346 self.mark_dirty();
1347 self.shell.app.request_layout_pass();
1348 }
1349
1350 handled
1351 }
1352
1353 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1357 if self.inspector_owns_keyboard() {
1358 return true;
1359 }
1360 let _event_handler = enter_event_handler_scope();
1361 let app_context = Rc::clone(&self.shell.app.app_context);
1362 app_context.enter(|| {
1363 let handled = run_in_mutable_snapshot(|| {
1364 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(
1365 start_bytes,
1366 end_bytes,
1367 )
1368 })
1369 .unwrap_or(false);
1370
1371 if handled {
1372 self.mark_dirty();
1373 self.shell.app.request_layout_pass();
1374 }
1375
1376 handled
1377 })
1378 }
1379
1380 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1385 if self.inspector_owns_keyboard() {
1386 return true;
1387 }
1388 let _event_handler = enter_event_handler_scope();
1389 let app_context = Rc::clone(&self.shell.app.app_context);
1390 app_context.enter(|| {
1391 let handled = run_in_mutable_snapshot(|| {
1392 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1393 })
1394 .unwrap_or(false);
1395
1396 if handled {
1397 self.mark_dirty();
1398 }
1399
1400 handled
1401 })
1402 }
1403
1404 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1408 let app_context = Rc::clone(&self.shell.app.app_context);
1409 app_context.enter(cranpose_ui::text_field_focus::focused_editor_state)
1410 }
1411
1412 pub fn ime_caret_geometry(
1416 &mut self,
1417 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1418 let app_context = Rc::clone(&self.shell.app.app_context);
1419 app_context.enter(cranpose_ui::text_field_focus::focused_caret_geometry)
1420 }
1421
1422 pub fn clear_text_field_focus(&mut self) {
1425 let _event_handler = enter_event_handler_scope();
1426 let app_context = Rc::clone(&self.shell.app.app_context);
1427 app_context.enter(cranpose_ui::text_field_focus::clear_focus);
1428 self.mark_dirty();
1429 self.shell.app.request_layout_pass();
1430 }
1431
1432 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1435 if self.inspector_owns_keyboard() {
1436 return true;
1437 }
1438 let _event_handler = enter_event_handler_scope();
1439 let app_context = Rc::clone(&self.shell.app.app_context);
1440 app_context.enter(|| self.on_ime_delete_surrounding_inner(before_bytes, after_bytes))
1441 }
1442
1443 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1444 let handled = run_in_mutable_snapshot(|| {
1445 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1446 })
1447 .unwrap_or(false);
1448
1449 if handled {
1450 self.mark_dirty();
1451 self.shell.app.request_layout_pass();
1452 }
1453
1454 handled
1455 }
1456}
1457
1458impl<R> AppShell<R>
1459where
1460 R: Renderer,
1461 R::Error: Debug,
1462{
1463 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
1472 self.app.modifiers = Some(modifiers);
1473 }
1474
1475 pub fn modifiers(&self) -> Option<Modifiers> {
1479 self.app.modifiers
1480 }
1481
1482 pub fn rotary_scroll_factor(&self) -> f32 {
1485 self.app.rotary_scroll_factor
1486 }
1487
1488 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
1497 if factor.is_finite() && factor > 0.0 {
1498 self.app.rotary_scroll_factor = factor;
1499 }
1500 }
1501
1502 pub fn notify_app_paused(&mut self) {
1509 let app_context = Rc::clone(&self.app.app_context);
1510 app_context.enter(cranpose_ui::text_input_session::notify_app_paused);
1511 }
1512
1513 pub fn notify_app_resumed(&mut self) -> bool {
1521 let app_context = Rc::clone(&self.app.app_context);
1522 app_context.enter(cranpose_ui::text_input_session::notify_app_resumed)
1523 }
1524
1525 #[cfg(all(
1526 feature = "clipboard-native",
1527 target_os = "linux",
1528 not(target_arch = "wasm32")
1529 ))]
1530 pub fn set_primary_selection(&mut self, text: &str) {
1531 use arboard::{LinuxClipboardKind, SetExtLinux};
1532 if let Some(ref mut clipboard) = self.app.clipboard {
1533 let result = clipboard
1534 .set()
1535 .clipboard(LinuxClipboardKind::Primary)
1536 .text(text.to_string());
1537 if let Err(e) = result {
1538 log::debug!("Primary selection set failed: {:?}", e);
1539 }
1540 }
1541 }
1542
1543 #[cfg(not(all(
1544 feature = "clipboard-native",
1545 target_os = "linux",
1546 not(target_arch = "wasm32")
1547 )))]
1548 pub fn set_primary_selection(&mut self, _text: &str) {}
1549
1550 #[cfg(all(
1551 feature = "clipboard-native",
1552 target_os = "linux",
1553 not(target_arch = "wasm32")
1554 ))]
1555 pub fn get_primary_selection(&mut self) -> Option<String> {
1556 use arboard::{GetExtLinux, LinuxClipboardKind};
1557 if let Some(ref mut clipboard) = self.app.clipboard {
1558 clipboard
1559 .get()
1560 .clipboard(LinuxClipboardKind::Primary)
1561 .text()
1562 .ok()
1563 } else {
1564 None
1565 }
1566 }
1567
1568 #[cfg(not(all(
1569 feature = "clipboard-native",
1570 target_os = "linux",
1571 not(target_arch = "wasm32")
1572 )))]
1573 pub fn get_primary_selection(&mut self) -> Option<String> {
1574 None
1575 }
1576
1577 pub fn sync_selection_to_primary(&mut self) {
1580 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1581 {
1582 if let Some(text) = self.on_copy() {
1583 self.set_primary_selection(&text);
1584 }
1585 }
1586 }
1587
1588 pub fn set_pointer_source(&mut self, source: PointerSource) {
1590 self.primary().set_pointer_source(source);
1591 }
1592
1593 pub fn pointer_source(&self) -> PointerSource {
1595 self.surfaces[0].pointer_source
1596 }
1597
1598 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
1600 self.primary().set_cursor(x, y)
1601 }
1602
1603 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
1605 self.primary().set_cursor_at_time(x, y, time_ms)
1606 }
1607
1608 pub fn set_cursor_at_event_time(
1610 &mut self,
1611 x: f32,
1612 y: f32,
1613 event_time: PointerEventTime,
1614 ) -> bool {
1615 self.primary().set_cursor_at_event_time(x, y, event_time)
1616 }
1617
1618 pub fn pointer_pressed(&mut self) -> bool {
1620 self.primary().pointer_pressed()
1621 }
1622
1623 pub fn accessibility_activate_at(&mut self, x: f32, y: f32) -> bool {
1625 self.primary().accessibility_activate_at(x, y)
1626 }
1627
1628 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
1630 self.primary().pointer_pressed_at_time(time_ms)
1631 }
1632
1633 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1635 self.primary().pointer_pressed_at_event_time(event_time)
1636 }
1637
1638 pub fn pointer_released(&mut self) -> bool {
1640 self.primary().pointer_released()
1641 }
1642
1643 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
1645 self.primary().pointer_released_at_position(x, y)
1646 }
1647
1648 pub fn pointer_released_at_position_time(
1651 &mut self,
1652 x: f32,
1653 y: f32,
1654 time_ms: Option<i64>,
1655 ) -> bool {
1656 self.primary()
1657 .pointer_released_at_position_time(x, y, time_ms)
1658 }
1659
1660 pub fn pointer_released_at_position_event_time(
1663 &mut self,
1664 x: f32,
1665 y: f32,
1666 event_time: PointerEventTime,
1667 ) -> bool {
1668 self.primary()
1669 .pointer_released_at_position_event_time(x, y, event_time)
1670 }
1671
1672 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
1674 self.primary().pointer_released_at_time(time_ms)
1675 }
1676
1677 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1679 self.primary().pointer_released_at_event_time(event_time)
1680 }
1681
1682 pub fn secondary_pointer_pressed(
1684 &mut self,
1685 pointer_id: u64,
1686 x: f32,
1687 y: f32,
1688 time_ms: Option<i64>,
1689 ) -> bool {
1690 self.primary()
1691 .secondary_pointer_pressed(pointer_id, x, y, time_ms)
1692 }
1693
1694 pub fn secondary_pointer_moved(
1696 &mut self,
1697 pointer_id: u64,
1698 x: f32,
1699 y: f32,
1700 time_ms: Option<i64>,
1701 ) -> bool {
1702 self.primary()
1703 .secondary_pointer_moved(pointer_id, x, y, time_ms)
1704 }
1705
1706 pub fn secondary_pointer_released(
1708 &mut self,
1709 pointer_id: u64,
1710 x: f32,
1711 y: f32,
1712 time_ms: Option<i64>,
1713 ) -> bool {
1714 self.primary()
1715 .secondary_pointer_released(pointer_id, x, y, time_ms)
1716 }
1717
1718 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
1720 self.primary().pointer_zoomed(zoom_factor)
1721 }
1722
1723 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
1725 self.primary().wheel_scrolled(wheel)
1726 }
1727
1728 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
1730 self.primary().pointer_scrolled(delta_x, delta_y)
1731 }
1732
1733 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
1735 where
1736 F: Fn(RotaryScrollEvent) -> bool + 'static,
1737 {
1738 self.primary().set_on_rotary_scroll(handler);
1739 }
1740
1741 pub fn clear_on_rotary_scroll(&mut self) {
1743 self.primary().clear_on_rotary_scroll();
1744 }
1745
1746 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
1748 self.primary()
1749 .rotary_scrolled_by_detents(detents, uptime_millis)
1750 }
1751
1752 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
1754 self.primary().rotary_scrolled(event)
1755 }
1756
1757 pub fn cancel_gesture(&mut self) {
1759 self.primary().cancel_gesture();
1760 }
1761
1762 pub fn cancel_gesture_unless_pressed(&mut self) {
1764 self.primary().cancel_gesture_unless_pressed();
1765 }
1766
1767 pub fn refresh_pointer_icon(&self) {
1771 self.surfaces[0].pointer_icon.refresh();
1772 }
1773
1774 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
1781 self.surfaces[0].pointer_icon.take_change()
1782 }
1783
1784 pub fn set_platform_text_input(
1791 &mut self,
1792 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
1793 ) {
1794 self.primary().set_platform_text_input(handler);
1795 }
1796
1797 pub fn dismiss_top_modal(&mut self) -> bool {
1799 self.primary().dismiss_top_modal()
1800 }
1801
1802 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1804 self.primary().move_focus_in_context(direction)
1805 }
1806
1807 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1809 self.primary().on_key_event(event)
1810 }
1811
1812 pub fn on_paste(&mut self, text: &str) -> bool {
1814 self.primary().on_paste(text)
1815 }
1816
1817 pub fn on_copy(&mut self) -> Option<String> {
1819 self.primary().on_copy()
1820 }
1821
1822 pub fn on_cut(&mut self) -> Option<String> {
1824 self.primary().on_cut()
1825 }
1826
1827 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1829 self.primary().on_ime_preedit(text, cursor)
1830 }
1831
1832 pub fn on_ime_finish_composing(&mut self) -> bool {
1834 self.primary().on_ime_finish_composing()
1835 }
1836
1837 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1839 self.primary()
1840 .on_ime_set_composing_region(start_bytes, end_bytes)
1841 }
1842
1843 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1845 self.primary().on_ime_set_selection(start_bytes, end_bytes)
1846 }
1847
1848 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1850 self.primary().ime_editor_state()
1851 }
1852
1853 pub fn ime_caret_geometry(
1855 &mut self,
1856 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1857 self.primary().ime_caret_geometry()
1858 }
1859
1860 pub fn clear_text_field_focus(&mut self) {
1862 self.primary().clear_text_field_focus();
1863 }
1864
1865 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1867 self.primary()
1868 .on_ime_delete_surrounding(before_bytes, after_bytes)
1869 }
1870}
1871
1872fn plain_key_down(event: &KeyEvent) -> bool {
1873 event.event_type == KeyEventType::KeyDown
1874 && !event.modifiers.ctrl
1875 && !event.modifiers.meta
1876 && !event.modifiers.alt
1877}