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 let direction = if event.modifiers.shift {
1015 FocusDirection::Previous
1016 } else {
1017 FocusDirection::Next
1018 };
1019 self.move_focus_in_context(direction)
1020 }
1021
1022 fn on_activation_key(&mut self, event: &KeyEvent) -> bool {
1023 if !plain_key_down(event)
1024 || !matches!(event.key_code, KeyCode::Enter | KeyCode::Space)
1025 || cranpose_ui::text_field_focus::has_focused_field()
1026 {
1027 return false;
1028 }
1029 let Some(focused) = cranpose_ui::active_focus_target() else {
1030 return false;
1031 };
1032 let center = self.with_layout_tree(|layout_tree| {
1033 layout_tree
1034 .map(cranpose_ui::collect_focus_order)
1035 .unwrap_or_default()
1036 .iter()
1037 .find(|entry| entry.node_id == focused)
1038 .map(cranpose_ui::FocusEntry::center)
1039 });
1040 let Some((x, y)) = center else {
1041 return false;
1042 };
1043 self.set_cursor(x, y);
1044 let pressed = self.pointer_pressed();
1045 let released = self.pointer_released_at_position(x, y);
1046 self.note_focus_moved_by_keyboard(true);
1047 pressed || released
1048 }
1049
1050 fn on_arrow_key(&mut self, event: &KeyEvent) -> bool {
1051 if !plain_key_down(event) || cranpose_ui::text_field_focus::has_focused_field() {
1052 return false;
1053 }
1054 let direction = match event.key_code {
1055 KeyCode::ArrowLeft => FocusDirection::Left,
1056 KeyCode::ArrowRight => FocusDirection::Right,
1057 KeyCode::ArrowUp => FocusDirection::Up,
1058 KeyCode::ArrowDown => FocusDirection::Down,
1059 _ => return false,
1060 };
1061 let Some(focused) = cranpose_ui::active_focus_target() else {
1062 return false;
1063 };
1064 let order = self.with_layout_tree(|layout_tree| {
1065 let layout_tree = layout_tree?;
1066 let group = cranpose_ui::selectable_group_of(layout_tree, focused)?;
1067 Some(cranpose_ui::collect_focus_order_under(layout_tree, group))
1068 });
1069 order.is_some_and(|order| self.move_focus_in_order(order, direction))
1070 }
1071
1072 fn note_focus_moved_by_keyboard(&mut self, keyboard: bool) {
1073 if cranpose_ui::set_keyboard_focus_visible(keyboard) {
1074 cranpose_ui::request_render_invalidation();
1075 self.mark_dirty();
1076 }
1077 }
1078
1079 fn on_escape_key(&mut self, event: &KeyEvent) -> bool {
1080 if event.event_type != KeyEventType::KeyDown || event.key_code != KeyCode::Escape {
1081 return false;
1082 }
1083 self.dismiss_top_modal_in_context()
1084 }
1085
1086 pub fn dismiss_top_modal(&mut self) -> bool {
1089 let _event_handler = enter_event_handler_scope();
1090 let app_context = Rc::clone(&self.shell.app.app_context);
1091 app_context.enter(|| self.dismiss_top_modal_in_context())
1092 }
1093
1094 fn dismiss_top_modal_in_context(&mut self) -> bool {
1095 let closed = run_in_mutable_snapshot(|| {
1096 if cranpose_ui::modal_depth() > 0 {
1097 cranpose_ui::dispatch_modal_back()
1098 } else {
1099 cranpose_ui::dismiss_top_popup()
1100 }
1101 })
1102 .unwrap_or(false);
1103 if closed {
1104 self.mark_dirty();
1105 }
1106 closed
1107 }
1108
1109 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1112 let order = self.with_layout_tree(|layout_tree| {
1113 layout_tree
1114 .map(cranpose_ui::collect_focus_order)
1115 .unwrap_or_default()
1116 });
1117 self.move_focus_in_order(order, direction)
1118 }
1119
1120 fn move_focus_in_order(
1121 &mut self,
1122 order: Vec<cranpose_ui::FocusEntry>,
1123 direction: FocusDirection,
1124 ) -> bool {
1125 cranpose_ui::set_focus_order(order);
1126 let moved = run_in_mutable_snapshot(|| cranpose_ui::FocusManager.move_focus(direction))
1127 .unwrap_or(false);
1128 if moved {
1129 self.mark_dirty();
1130 self.note_focus_moved_by_keyboard(true);
1131 }
1132 moved
1133 }
1134
1135 pub fn on_key_event(&mut self, event: &KeyEvent) -> 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.on_key_event_inner(event))
1139 }
1140
1141 fn on_key_event_inner(&mut self, event: &KeyEvent) -> bool {
1142 if self.inspector_key(event) {
1143 return true;
1144 }
1145 use KeyEventType::KeyDown;
1146
1147 if event.event_type == KeyDown && event.modifiers.command_or_ctrl() {
1148 #[cfg(all(
1149 feature = "clipboard-native",
1150 not(target_arch = "wasm32"),
1151 not(target_os = "android"),
1152 not(target_os = "ios")
1153 ))]
1154 {
1155 match event.key_code {
1156 KeyCode::C => {
1157 if let Some(text) = self.on_copy_inner() {
1158 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1159 return true;
1160 }
1161 }
1162 KeyCode::X => {
1163 if let Some(text) = self.on_cut_inner() {
1164 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1165 self.mark_dirty();
1166 self.shell.app.request_layout_pass();
1167 return true;
1168 }
1169 }
1170 KeyCode::V => {
1171 if let Some(text) = cranpose_ui::clipboard_session::clipboard_read_text()
1172 && self.on_paste_inner(&text)
1173 {
1174 return true;
1175 }
1176 }
1177 _ => {}
1178 }
1179 }
1180 }
1181
1182 if self.on_focus_key(event) {
1183 return true;
1184 }
1185
1186 if self.on_escape_key(event) {
1187 return true;
1188 }
1189
1190 if self.on_activation_key(event) || self.on_arrow_key(event) {
1191 return true;
1192 }
1193
1194 if !cranpose_ui::text_field_focus::has_focused_field() {
1195 return false;
1196 }
1197
1198 let handled =
1199 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_key_event(event))
1200 .unwrap_or(false);
1201
1202 if handled {
1203 self.mark_dirty();
1204 self.shell.app.request_layout_pass();
1205 }
1206
1207 handled
1208 }
1209
1210 pub fn on_paste(&mut self, text: &str) -> bool {
1214 if self.inspector_owns_keyboard() {
1215 return true;
1216 }
1217 let _event_handler = enter_event_handler_scope();
1218 let app_context = Rc::clone(&self.shell.app.app_context);
1219 app_context.enter(|| self.on_paste_inner(text))
1220 }
1221
1222 fn on_paste_inner(&mut self, text: &str) -> bool {
1223 let handled =
1224 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1225 .unwrap_or(false);
1226
1227 if handled {
1228 self.mark_dirty();
1229 self.shell.app.request_layout_pass();
1230 }
1231
1232 handled
1233 }
1234
1235 pub fn on_copy(&mut self) -> Option<String> {
1239 let app_context = Rc::clone(&self.shell.app.app_context);
1240 if self.inspector_owns_keyboard() {
1241 return None;
1242 }
1243 app_context.enter(|| self.on_copy_inner())
1244 }
1245
1246 fn on_copy_inner(&mut self) -> Option<String> {
1247 cranpose_ui::text_field_focus::dispatch_copy()
1248 }
1249
1250 pub fn on_cut(&mut self) -> Option<String> {
1254 let _event_handler = enter_event_handler_scope();
1255 if self.inspector_owns_keyboard() {
1256 return None;
1257 }
1258 let app_context = Rc::clone(&self.shell.app.app_context);
1259 app_context.enter(|| self.on_cut_inner())
1260 }
1261
1262 fn on_cut_inner(&mut self) -> Option<String> {
1263 let text =
1264 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1265
1266 if text.is_some() {
1267 self.mark_dirty();
1268 self.shell.app.request_layout_pass();
1269 }
1270
1271 text
1272 }
1273
1274 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1282 if self.inspector_owns_keyboard() {
1283 return true;
1284 }
1285 let _event_handler = enter_event_handler_scope();
1286 let app_context = Rc::clone(&self.shell.app.app_context);
1287 app_context.enter(|| self.on_ime_preedit_inner(text, cursor))
1288 }
1289
1290 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1291 let handled = run_in_mutable_snapshot(|| {
1292 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1293 })
1294 .unwrap_or(false);
1295
1296 if handled {
1297 self.mark_dirty();
1298 self.shell.app.request_layout_pass();
1299 }
1300
1301 handled
1302 }
1303
1304 pub fn on_ime_finish_composing(&mut self) -> bool {
1308 if self.inspector_owns_keyboard() {
1309 return true;
1310 }
1311 let _event_handler = enter_event_handler_scope();
1312 let app_context = Rc::clone(&self.shell.app.app_context);
1313 app_context.enter(|| self.on_ime_finish_composing_inner())
1314 }
1315
1316 fn on_ime_finish_composing_inner(&mut self) -> bool {
1317 let handled =
1318 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1319 .unwrap_or(false);
1320
1321 if handled {
1322 self.mark_dirty();
1323 self.shell.app.request_layout_pass();
1324 }
1325
1326 handled
1327 }
1328
1329 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1333 if self.inspector_owns_keyboard() {
1334 return true;
1335 }
1336 let _event_handler = enter_event_handler_scope();
1337 let app_context = Rc::clone(&self.shell.app.app_context);
1338 app_context.enter(|| {
1339 let handled = run_in_mutable_snapshot(|| {
1340 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(
1341 start_bytes,
1342 end_bytes,
1343 )
1344 })
1345 .unwrap_or(false);
1346
1347 if handled {
1348 self.mark_dirty();
1349 self.shell.app.request_layout_pass();
1350 }
1351
1352 handled
1353 })
1354 }
1355
1356 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1361 if self.inspector_owns_keyboard() {
1362 return true;
1363 }
1364 let _event_handler = enter_event_handler_scope();
1365 let app_context = Rc::clone(&self.shell.app.app_context);
1366 app_context.enter(|| {
1367 let handled = run_in_mutable_snapshot(|| {
1368 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1369 })
1370 .unwrap_or(false);
1371
1372 if handled {
1373 self.mark_dirty();
1374 }
1375
1376 handled
1377 })
1378 }
1379
1380 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1384 let app_context = Rc::clone(&self.shell.app.app_context);
1385 app_context.enter(cranpose_ui::text_field_focus::focused_editor_state)
1386 }
1387
1388 pub fn ime_caret_geometry(
1392 &mut self,
1393 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1394 let app_context = Rc::clone(&self.shell.app.app_context);
1395 app_context.enter(cranpose_ui::text_field_focus::focused_caret_geometry)
1396 }
1397
1398 pub fn clear_text_field_focus(&mut self) {
1401 let _event_handler = enter_event_handler_scope();
1402 let app_context = Rc::clone(&self.shell.app.app_context);
1403 app_context.enter(cranpose_ui::text_field_focus::clear_focus);
1404 self.mark_dirty();
1405 self.shell.app.request_layout_pass();
1406 }
1407
1408 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1411 if self.inspector_owns_keyboard() {
1412 return true;
1413 }
1414 let _event_handler = enter_event_handler_scope();
1415 let app_context = Rc::clone(&self.shell.app.app_context);
1416 app_context.enter(|| self.on_ime_delete_surrounding_inner(before_bytes, after_bytes))
1417 }
1418
1419 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1420 let handled = run_in_mutable_snapshot(|| {
1421 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1422 })
1423 .unwrap_or(false);
1424
1425 if handled {
1426 self.mark_dirty();
1427 self.shell.app.request_layout_pass();
1428 }
1429
1430 handled
1431 }
1432}
1433
1434impl<R> AppShell<R>
1435where
1436 R: Renderer,
1437 R::Error: Debug,
1438{
1439 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
1448 self.app.modifiers = Some(modifiers);
1449 }
1450
1451 pub fn modifiers(&self) -> Option<Modifiers> {
1455 self.app.modifiers
1456 }
1457
1458 pub fn rotary_scroll_factor(&self) -> f32 {
1461 self.app.rotary_scroll_factor
1462 }
1463
1464 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
1473 if factor.is_finite() && factor > 0.0 {
1474 self.app.rotary_scroll_factor = factor;
1475 }
1476 }
1477
1478 pub fn notify_app_paused(&mut self) {
1485 let app_context = Rc::clone(&self.app.app_context);
1486 app_context.enter(cranpose_ui::text_input_session::notify_app_paused);
1487 }
1488
1489 pub fn notify_app_resumed(&mut self) -> bool {
1497 let app_context = Rc::clone(&self.app.app_context);
1498 app_context.enter(cranpose_ui::text_input_session::notify_app_resumed)
1499 }
1500
1501 #[cfg(all(
1502 feature = "clipboard-native",
1503 target_os = "linux",
1504 not(target_arch = "wasm32")
1505 ))]
1506 pub fn set_primary_selection(&mut self, text: &str) {
1507 use arboard::{LinuxClipboardKind, SetExtLinux};
1508 if let Some(ref mut clipboard) = self.app.clipboard {
1509 let result = clipboard
1510 .set()
1511 .clipboard(LinuxClipboardKind::Primary)
1512 .text(text.to_string());
1513 if let Err(e) = result {
1514 log::debug!("Primary selection set failed: {:?}", e);
1515 }
1516 }
1517 }
1518
1519 #[cfg(not(all(
1520 feature = "clipboard-native",
1521 target_os = "linux",
1522 not(target_arch = "wasm32")
1523 )))]
1524 pub fn set_primary_selection(&mut self, _text: &str) {}
1525
1526 #[cfg(all(
1527 feature = "clipboard-native",
1528 target_os = "linux",
1529 not(target_arch = "wasm32")
1530 ))]
1531 pub fn get_primary_selection(&mut self) -> Option<String> {
1532 use arboard::{GetExtLinux, LinuxClipboardKind};
1533 if let Some(ref mut clipboard) = self.app.clipboard {
1534 clipboard
1535 .get()
1536 .clipboard(LinuxClipboardKind::Primary)
1537 .text()
1538 .ok()
1539 } else {
1540 None
1541 }
1542 }
1543
1544 #[cfg(not(all(
1545 feature = "clipboard-native",
1546 target_os = "linux",
1547 not(target_arch = "wasm32")
1548 )))]
1549 pub fn get_primary_selection(&mut self) -> Option<String> {
1550 None
1551 }
1552
1553 pub fn sync_selection_to_primary(&mut self) {
1556 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1557 {
1558 if let Some(text) = self.on_copy() {
1559 self.set_primary_selection(&text);
1560 }
1561 }
1562 }
1563
1564 pub fn set_pointer_source(&mut self, source: PointerSource) {
1566 self.primary().set_pointer_source(source);
1567 }
1568
1569 pub fn pointer_source(&self) -> PointerSource {
1571 self.surfaces[0].pointer_source
1572 }
1573
1574 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
1576 self.primary().set_cursor(x, y)
1577 }
1578
1579 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
1581 self.primary().set_cursor_at_time(x, y, time_ms)
1582 }
1583
1584 pub fn set_cursor_at_event_time(
1586 &mut self,
1587 x: f32,
1588 y: f32,
1589 event_time: PointerEventTime,
1590 ) -> bool {
1591 self.primary().set_cursor_at_event_time(x, y, event_time)
1592 }
1593
1594 pub fn pointer_pressed(&mut self) -> bool {
1596 self.primary().pointer_pressed()
1597 }
1598
1599 pub fn accessibility_activate_at(&mut self, x: f32, y: f32) -> bool {
1601 self.primary().accessibility_activate_at(x, y)
1602 }
1603
1604 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
1606 self.primary().pointer_pressed_at_time(time_ms)
1607 }
1608
1609 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1611 self.primary().pointer_pressed_at_event_time(event_time)
1612 }
1613
1614 pub fn pointer_released(&mut self) -> bool {
1616 self.primary().pointer_released()
1617 }
1618
1619 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
1621 self.primary().pointer_released_at_position(x, y)
1622 }
1623
1624 pub fn pointer_released_at_position_time(
1627 &mut self,
1628 x: f32,
1629 y: f32,
1630 time_ms: Option<i64>,
1631 ) -> bool {
1632 self.primary()
1633 .pointer_released_at_position_time(x, y, time_ms)
1634 }
1635
1636 pub fn pointer_released_at_position_event_time(
1639 &mut self,
1640 x: f32,
1641 y: f32,
1642 event_time: PointerEventTime,
1643 ) -> bool {
1644 self.primary()
1645 .pointer_released_at_position_event_time(x, y, event_time)
1646 }
1647
1648 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
1650 self.primary().pointer_released_at_time(time_ms)
1651 }
1652
1653 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
1655 self.primary().pointer_released_at_event_time(event_time)
1656 }
1657
1658 pub fn secondary_pointer_pressed(
1660 &mut self,
1661 pointer_id: u64,
1662 x: f32,
1663 y: f32,
1664 time_ms: Option<i64>,
1665 ) -> bool {
1666 self.primary()
1667 .secondary_pointer_pressed(pointer_id, x, y, time_ms)
1668 }
1669
1670 pub fn secondary_pointer_moved(
1672 &mut self,
1673 pointer_id: u64,
1674 x: f32,
1675 y: f32,
1676 time_ms: Option<i64>,
1677 ) -> bool {
1678 self.primary()
1679 .secondary_pointer_moved(pointer_id, x, y, time_ms)
1680 }
1681
1682 pub fn secondary_pointer_released(
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_released(pointer_id, x, y, time_ms)
1692 }
1693
1694 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
1696 self.primary().pointer_zoomed(zoom_factor)
1697 }
1698
1699 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
1701 self.primary().wheel_scrolled(wheel)
1702 }
1703
1704 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
1706 self.primary().pointer_scrolled(delta_x, delta_y)
1707 }
1708
1709 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
1711 where
1712 F: Fn(RotaryScrollEvent) -> bool + 'static,
1713 {
1714 self.primary().set_on_rotary_scroll(handler);
1715 }
1716
1717 pub fn clear_on_rotary_scroll(&mut self) {
1719 self.primary().clear_on_rotary_scroll();
1720 }
1721
1722 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
1724 self.primary()
1725 .rotary_scrolled_by_detents(detents, uptime_millis)
1726 }
1727
1728 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
1730 self.primary().rotary_scrolled(event)
1731 }
1732
1733 pub fn cancel_gesture(&mut self) {
1735 self.primary().cancel_gesture();
1736 }
1737
1738 pub fn cancel_gesture_unless_pressed(&mut self) {
1740 self.primary().cancel_gesture_unless_pressed();
1741 }
1742
1743 pub fn refresh_pointer_icon(&self) {
1747 self.surfaces[0].pointer_icon.refresh();
1748 }
1749
1750 pub fn take_pointer_icon_change(&self) -> Option<PointerIcon> {
1757 self.surfaces[0].pointer_icon.take_change()
1758 }
1759
1760 pub fn set_platform_text_input(
1767 &mut self,
1768 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
1769 ) {
1770 self.primary().set_platform_text_input(handler);
1771 }
1772
1773 pub fn dismiss_top_modal(&mut self) -> bool {
1775 self.primary().dismiss_top_modal()
1776 }
1777
1778 pub fn move_focus_in_context(&mut self, direction: FocusDirection) -> bool {
1780 self.primary().move_focus_in_context(direction)
1781 }
1782
1783 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
1785 self.primary().on_key_event(event)
1786 }
1787
1788 pub fn on_paste(&mut self, text: &str) -> bool {
1790 self.primary().on_paste(text)
1791 }
1792
1793 pub fn on_copy(&mut self) -> Option<String> {
1795 self.primary().on_copy()
1796 }
1797
1798 pub fn on_cut(&mut self) -> Option<String> {
1800 self.primary().on_cut()
1801 }
1802
1803 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1805 self.primary().on_ime_preedit(text, cursor)
1806 }
1807
1808 pub fn on_ime_finish_composing(&mut self) -> bool {
1810 self.primary().on_ime_finish_composing()
1811 }
1812
1813 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1815 self.primary()
1816 .on_ime_set_composing_region(start_bytes, end_bytes)
1817 }
1818
1819 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1821 self.primary().on_ime_set_selection(start_bytes, end_bytes)
1822 }
1823
1824 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1826 self.primary().ime_editor_state()
1827 }
1828
1829 pub fn ime_caret_geometry(
1831 &mut self,
1832 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1833 self.primary().ime_caret_geometry()
1834 }
1835
1836 pub fn clear_text_field_focus(&mut self) {
1838 self.primary().clear_text_field_focus();
1839 }
1840
1841 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1843 self.primary()
1844 .on_ime_delete_surrounding(before_bytes, after_bytes)
1845 }
1846}
1847
1848fn plain_key_down(event: &KeyEvent) -> bool {
1849 event.event_type == KeyEventType::KeyDown
1850 && !event.modifiers.ctrl
1851 && !event.modifiers.meta
1852 && !event.modifiers.alt
1853}