1use super::*;
2
3impl<R> AppShell<R>
4where
5 R: Renderer,
6 R::Error: Debug,
7{
8 fn pointer_event(
9 &self,
10 kind: PointerEventKind,
11 position: Point,
12 global_position: Point,
13 event_time: PointerEventTime,
14 ) -> PointerEvent {
15 PointerEvent::new(kind, position, global_position)
16 .with_time_ms(event_time.platform_time_ms)
17 .with_animation_time_nanos(event_time.animation_time_nanos)
18 }
19
20 fn resolve_gesture_targets(
21 &self,
22 pointer: PointerId,
23 ) -> Vec<<<R as Renderer>::Scene as RenderScene>::HitTarget> {
24 self.resolve_hit_path(pointer)
25 }
26
27 fn resolve_hit_path(
34 &self,
35 pointer: PointerId,
36 ) -> Vec<<<R as Renderer>::Scene as RenderScene>::HitTarget> {
37 let Some(node_ids) = self.hit_path_tracker.dispatch_order(pointer) else {
38 return Vec::new();
39 };
40
41 let scene = self.renderer.scene();
42 let targets: Vec<_> = node_ids
43 .iter()
44 .filter_map(|&id| scene.find_target(id))
45 .collect();
46 log::trace!(
47 target: "cranpose::input",
48 "resolve_hit_path pointer={pointer:?} cached={node_ids:?} resolved_count={}",
49 targets.len()
50 );
51 targets
52 }
53
54 fn dispatch_targets<I>(&mut self, targets: I, event: PointerEvent, stop_on_consume: bool)
55 where
56 I: IntoIterator<Item = <<R as Renderer>::Scene as RenderScene>::HitTarget>,
57 {
58 let mut applier = self.composition.applier_mut();
59 for target in targets {
60 let node_id = target.node_id();
61 target.dispatch_with_applier(&mut applier, event.clone());
62 log::trace!(
63 target: "cranpose::input",
64 "dispatch {:?} node={} consumed={} stop_on_consume={}",
65 event.kind,
66 node_id,
67 event.is_consumed(),
68 stop_on_consume,
69 );
70 if stop_on_consume && event.is_consumed() {
71 break;
72 }
73 }
74 }
75
76 pub fn set_pointer_source(&mut self, source: PointerSource) {
82 self.pointer_source = source;
83 }
84
85 pub fn pointer_source(&self) -> PointerSource {
87 self.pointer_source
88 }
89
90 pub fn set_cursor(&mut self, x: f32, y: f32) -> bool {
91 self.set_cursor_at_time(x, y, None)
92 }
93
94 pub fn set_cursor_at_time(&mut self, x: f32, y: f32, time_ms: Option<i64>) -> bool {
101 let event_time = self.realtime_pointer_event_time(time_ms);
102 self.set_cursor_at_event_time(x, y, event_time)
103 }
104
105 pub fn set_cursor_at_event_time(
107 &mut self,
108 x: f32,
109 y: f32,
110 event_time: PointerEventTime,
111 ) -> bool {
112 let _event_handler = enter_event_handler_scope();
113 let app_context = Rc::clone(&self.app_context);
114 let result = app_context.enter(|| {
115 run_in_mutable_snapshot(|| self.set_cursor_inner(x, y, event_time)).unwrap_or(false)
116 });
117 if result {
118 self.mark_dirty();
119 }
120 log::trace!(
121 target: "cranpose::input",
122 "set_cursor ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
123 event_time.platform_time_ms,
124 event_time.animation_time_nanos,
125 );
126 result
127 }
128
129 fn set_cursor_inner(&mut self, x: f32, y: f32, event_time: PointerEventTime) -> bool {
130 self.cursor = (x, y);
131
132 if self.buttons_pressed != PointerButtons::NONE {
136 if self.hit_path_tracker.has_path(PointerId::PRIMARY) {
137 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
138 if !targets.is_empty() {
139 let event = self
140 .pointer_event(
141 PointerEventKind::Move,
142 Point { x, y },
143 Point { x, y },
144 event_time,
145 )
146 .with_buttons(self.buttons_pressed)
147 .with_source(self.pointer_source);
148 self.dispatch_targets(targets, event, false);
149 return true;
150 }
151
152 return false;
153 }
154
155 return false;
158 }
159
160 let hits = self.renderer.scene().hit_test(x, y);
163 let new_ids: Vec<NodeId> = hits.iter().map(|h| h.node_id()).collect();
164
165 let pos = Point { x, y };
167 let previously_hovered = self.hovered_nodes.clone();
168 for old_id in previously_hovered {
169 if !new_ids.contains(&old_id) {
170 if let Some(target) = self.renderer.scene().find_target(old_id) {
171 let exit_event = self
172 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
173 .with_buttons(self.buttons_pressed)
174 .with_source(self.pointer_source);
175 self.dispatch_targets(std::iter::once(target), exit_event, false);
176 }
177 }
178 }
179
180 for hit in &hits {
182 if !self.hovered_nodes.contains(&hit.node_id()) {
183 let enter_event = self
184 .pointer_event(PointerEventKind::Enter, pos, pos, event_time)
185 .with_buttons(self.buttons_pressed)
186 .with_source(self.pointer_source);
187 self.dispatch_targets(std::iter::once(hit.clone()), enter_event, false);
188 }
189 }
190
191 self.hovered_nodes = new_ids;
192
193 if !hits.is_empty() {
194 let event = self
195 .pointer_event(PointerEventKind::Move, pos, pos, event_time)
196 .with_buttons(self.buttons_pressed)
197 .with_source(self.pointer_source);
198 self.dispatch_targets(hits, event, true);
199 true
200 } else {
201 false
202 }
203 }
204
205 pub fn pointer_pressed(&mut self) -> bool {
206 self.pointer_pressed_at_time(None)
207 }
208
209 pub fn pointer_pressed_at_time(&mut self, time_ms: Option<i64>) -> bool {
212 let event_time = self.realtime_pointer_event_time(time_ms);
213 self.pointer_pressed_at_event_time(event_time)
214 }
215
216 pub fn pointer_pressed_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
218 if self.dev_overlay_press(self.cursor.0, self.cursor.1) {
222 return true;
223 }
224 let _event_handler = enter_event_handler_scope();
225 let app_context = Rc::clone(&self.app_context);
226 let result = app_context.enter(|| {
227 run_in_mutable_snapshot(|| self.pointer_pressed_inner(event_time)).unwrap_or(false)
228 });
229 if result {
230 self.mark_dirty();
231 }
232 log::trace!(
233 target: "cranpose::input",
234 "pointer_pressed time_ms={:?} animation_time_nanos={} -> {result}",
235 event_time.platform_time_ms,
236 event_time.animation_time_nanos,
237 );
238 result
239 }
240
241 fn pointer_pressed_inner(&mut self, event_time: PointerEventTime) -> bool {
242 self.buttons_pressed.insert(PointerButton::Primary);
244
245 let hits = self.renderer.scene().hit_test(self.cursor.0, self.cursor.1);
253 if hits.is_empty() {
254 self.hit_path_tracker.remove_path(PointerId::PRIMARY);
255 false
256 } else {
257 let event = self
258 .pointer_event(
259 PointerEventKind::Down,
260 Point {
261 x: self.cursor.0,
262 y: self.cursor.1,
263 },
264 Point {
265 x: self.cursor.0,
266 y: self.cursor.1,
267 },
268 event_time,
269 )
270 .with_buttons(self.buttons_pressed)
271 .with_source(self.pointer_source);
272
273 let mut delivered_capture_paths = Vec::new();
274 let mut applier = self.composition.applier_mut();
275 for hit in hits {
276 let node_id = hit.node_id();
277 delivered_capture_paths.push(hit.capture_path());
278 hit.dispatch_with_applier(&mut applier, event.clone());
279 log::trace!(
280 target: "cranpose::input",
281 "dispatch {:?} node={} consumed={} stop_on_consume=true",
282 event.kind,
283 node_id,
284 event.is_consumed(),
285 );
286 if event.is_consumed() {
287 break;
288 }
289 }
290
291 self.hit_path_tracker
292 .add_hit_path(PointerId::PRIMARY, delivered_capture_paths);
293 log::trace!(
294 target: "cranpose::input",
295 "pointer_pressed_inner cached_hit_path={:?}",
296 self.hit_path_tracker.get_path(PointerId::PRIMARY),
297 );
298
299 true
300 }
301 }
302
303 pub fn pointer_released(&mut self) -> bool {
304 self.pointer_released_at_time(None)
305 }
306
307 pub fn pointer_released_at_position(&mut self, x: f32, y: f32) -> bool {
320 self.pointer_released_at_position_time(x, y, None)
321 }
322
323 pub fn pointer_released_at_position_time(
327 &mut self,
328 x: f32,
329 y: f32,
330 time_ms: Option<i64>,
331 ) -> bool {
332 let event_time = self.realtime_pointer_event_time(time_ms);
333 self.pointer_released_at_position_event_time(x, y, event_time)
334 }
335
336 pub fn pointer_released_at_position_event_time(
338 &mut self,
339 x: f32,
340 y: f32,
341 event_time: PointerEventTime,
342 ) -> bool {
343 let _event_handler = enter_event_handler_scope();
344 let app_context = Rc::clone(&self.app_context);
345 let result = app_context.enter(|| {
346 run_in_mutable_snapshot(|| {
347 self.cursor = (x, y);
348 self.pointer_released_inner(event_time)
349 })
350 .unwrap_or(false)
351 });
352 if result {
353 self.mark_dirty();
354 }
355 log::trace!(
356 target: "cranpose::input",
357 "pointer_released_at_position ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
358 event_time.platform_time_ms,
359 event_time.animation_time_nanos,
360 );
361 result
362 }
363
364 pub fn pointer_released_at_time(&mut self, time_ms: Option<i64>) -> bool {
367 let event_time = self.realtime_pointer_event_time(time_ms);
368 self.pointer_released_at_event_time(event_time)
369 }
370
371 pub fn pointer_released_at_event_time(&mut self, event_time: PointerEventTime) -> bool {
373 let _event_handler = enter_event_handler_scope();
374 let app_context = Rc::clone(&self.app_context);
375 let result = app_context.enter(|| {
376 run_in_mutable_snapshot(|| self.pointer_released_inner(event_time)).unwrap_or(false)
377 });
378 if result {
379 self.mark_dirty();
380 }
381 log::trace!(
382 target: "cranpose::input",
383 "pointer_released time_ms={:?} animation_time_nanos={} -> {result}",
384 event_time.platform_time_ms,
385 event_time.animation_time_nanos,
386 );
387 result
388 }
389
390 fn pointer_released_inner(&mut self, event_time: PointerEventTime) -> bool {
391 self.buttons_pressed.remove(PointerButton::Primary);
394 let corrected_buttons = self.buttons_pressed;
395 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
396
397 self.hit_path_tracker.remove_path(PointerId::PRIMARY);
399
400 if !targets.is_empty() {
401 let event = self
402 .pointer_event(
403 PointerEventKind::Up,
404 Point {
405 x: self.cursor.0,
406 y: self.cursor.1,
407 },
408 Point {
409 x: self.cursor.0,
410 y: self.cursor.1,
411 },
412 event_time,
413 )
414 .with_buttons(corrected_buttons)
415 .with_source(self.pointer_source);
416
417 self.dispatch_targets(targets, event, false);
418 true
419 } else {
420 false
421 }
422 }
423
424 pub fn secondary_pointer_pressed(
433 &mut self,
434 pointer_id: u64,
435 x: f32,
436 y: f32,
437 time_ms: Option<i64>,
438 ) -> bool {
439 let event_time = self.realtime_pointer_event_time(time_ms);
440 self.dispatch_secondary_pointer(PointerEventKind::Down, pointer_id, x, y, event_time)
441 }
442
443 pub fn secondary_pointer_moved(
445 &mut self,
446 pointer_id: u64,
447 x: f32,
448 y: f32,
449 time_ms: Option<i64>,
450 ) -> bool {
451 let event_time = self.realtime_pointer_event_time(time_ms);
452 self.dispatch_secondary_pointer(PointerEventKind::Move, pointer_id, x, y, event_time)
453 }
454
455 pub fn secondary_pointer_released(
457 &mut self,
458 pointer_id: u64,
459 x: f32,
460 y: f32,
461 time_ms: Option<i64>,
462 ) -> bool {
463 let event_time = self.realtime_pointer_event_time(time_ms);
464 self.dispatch_secondary_pointer(PointerEventKind::Up, pointer_id, x, y, event_time)
465 }
466
467 fn dispatch_secondary_pointer(
468 &mut self,
469 kind: PointerEventKind,
470 pointer_id: u64,
471 x: f32,
472 y: f32,
473 event_time: PointerEventTime,
474 ) -> bool {
475 if pointer_id == 0 {
476 log::warn!(
477 target: "cranpose::input",
478 "secondary pointer dispatch called with the primary pointer id"
479 );
480 return false;
481 }
482
483 let _event_handler = enter_event_handler_scope();
484 let app_context = Rc::clone(&self.app_context);
485 let result = app_context.enter(|| {
486 run_in_mutable_snapshot(|| {
487 if !self.hit_path_tracker.has_path(PointerId::PRIMARY) {
488 return false;
489 }
490 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
491 if targets.is_empty() {
492 return false;
493 }
494 let pos = Point { x, y };
495 let event = self
496 .pointer_event(kind, pos, pos, event_time)
497 .with_buttons(self.buttons_pressed)
498 .with_id(pointer_id)
499 .with_source(self.pointer_source);
500 self.dispatch_targets(targets, event, false);
501 true
502 })
503 .unwrap_or(false)
504 });
505 if result {
506 self.mark_dirty();
507 }
508 log::trace!(
509 target: "cranpose::input",
510 "secondary_pointer {kind:?} id={pointer_id} ({x:.2},{y:.2}) time_ms={:?} animation_time_nanos={} -> {result}",
511 event_time.platform_time_ms,
512 event_time.animation_time_nanos,
513 );
514 result
515 }
516
517 pub fn pointer_zoomed(&mut self, zoom_factor: f32) -> bool {
523 let event_time = self.realtime_pointer_event_time(None);
524 let _event_handler = enter_event_handler_scope();
525 let app_context = Rc::clone(&self.app_context);
526 let result = app_context.enter(|| {
527 run_in_mutable_snapshot(|| self.pointer_zoomed_inner(zoom_factor, event_time))
528 .unwrap_or(false)
529 });
530 if result {
531 self.mark_dirty();
532 }
533 log::trace!(
534 target: "cranpose::input",
535 "pointer_zoomed factor={zoom_factor:.4} -> {result}"
536 );
537 result
538 }
539
540 fn pointer_zoomed_inner(&mut self, zoom_factor: f32, event_time: PointerEventTime) -> bool {
541 if !zoom_factor.is_finite() || zoom_factor <= 0.0 || zoom_factor == 1.0 {
542 return false;
543 }
544
545 let hits = self.renderer.scene().hit_test(self.cursor.0, self.cursor.1);
546 if hits.is_empty() {
547 return false;
548 }
549
550 let pos = Point {
551 x: self.cursor.0,
552 y: self.cursor.1,
553 };
554 let event = self
555 .pointer_event(PointerEventKind::Zoom, pos, pos, event_time)
556 .with_buttons(self.buttons_pressed)
557 .with_zoom_delta(zoom_factor)
558 .with_source(self.pointer_source);
559
560 let capture_paths = hits
561 .iter()
562 .map(|hit| hit.capture_path())
563 .collect::<Vec<_>>();
564 let targets = crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
565 .into_iter()
566 .filter_map(|node_id| self.renderer.scene().find_target(node_id))
567 .collect::<Vec<_>>();
568
569 self.dispatch_targets(targets, event.clone(), true);
570
571 event.is_consumed()
572 }
573
574 pub fn wheel_scrolled(&mut self, wheel: crate::WheelScroll) -> bool {
599 if wheel.is_zoom() {
600 let zoom_factor = wheel.zoom_factor();
601 log::trace!(
602 target: "cranpose::input",
603 "wheel zoom factor={zoom_factor:.4}"
604 );
605 return self.pointer_zoomed(zoom_factor);
606 }
607
608 let rotary =
609 RotaryScrollEvent::from_wheel_pixels(wheel.delta.y, wheel.delta.x, wheel.uptime_millis);
610 if self.rotary_scrolled(rotary) {
611 return true;
612 }
613
614 let delta = wheel.scroll_delta();
615 log::trace!(
616 target: "cranpose::input",
617 "wheel delta ({:.2},{:.2}) alt={}",
618 delta.x,
619 delta.y,
620 wheel.modifiers.alt
621 );
622 self.pointer_scrolled(delta.x, delta.y)
623 }
624
625 pub fn pointer_scrolled(&mut self, delta_x: f32, delta_y: f32) -> bool {
633 let event_time = self.realtime_pointer_event_time(None);
634 let _event_handler = enter_event_handler_scope();
635 let app_context = Rc::clone(&self.app_context);
636 let result = app_context.enter(|| {
637 run_in_mutable_snapshot(|| self.pointer_scrolled_inner(delta_x, delta_y, event_time))
638 .unwrap_or(false)
639 });
640 if result {
641 self.mark_dirty();
642 }
643 log::trace!(
644 target: "cranpose::input",
645 "pointer_scrolled ({delta_x:.2},{delta_y:.2}) -> {result}"
646 );
647 result
648 }
649
650 fn pointer_scrolled_inner(
651 &mut self,
652 delta_x: f32,
653 delta_y: f32,
654 event_time: PointerEventTime,
655 ) -> bool {
656 if delta_x.abs() <= f32::EPSILON && delta_y.abs() <= f32::EPSILON {
657 return false;
658 }
659
660 let hits = self.renderer.scene().hit_test(self.cursor.0, self.cursor.1);
661 if hits.is_empty() {
662 return false;
663 }
664
665 let event = self
666 .pointer_event(
667 PointerEventKind::Scroll,
668 Point {
669 x: self.cursor.0,
670 y: self.cursor.1,
671 },
672 Point {
673 x: self.cursor.0,
674 y: self.cursor.1,
675 },
676 event_time,
677 )
678 .with_buttons(self.buttons_pressed)
679 .with_scroll_delta(Point {
680 x: delta_x,
681 y: delta_y,
682 })
683 .with_source(self.pointer_source);
684
685 let capture_paths = hits
686 .iter()
687 .map(|hit| hit.capture_path())
688 .collect::<Vec<_>>();
689 let targets = crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
690 .into_iter()
691 .filter_map(|node_id| self.renderer.scene().find_target(node_id))
692 .collect::<Vec<_>>();
693
694 self.dispatch_targets(targets, event.clone(), true);
695
696 event.is_consumed()
697 }
698
699 pub fn set_on_rotary_scroll<F>(&mut self, handler: F)
710 where
711 F: Fn(RotaryScrollEvent) -> bool + 'static,
712 {
713 self.on_rotary_scroll = Some(Rc::new(handler));
714 }
715
716 pub fn clear_on_rotary_scroll(&mut self) {
718 self.on_rotary_scroll = None;
719 }
720
721 pub fn rotary_scroll_factor(&self) -> f32 {
724 self.rotary_scroll_factor
725 }
726
727 pub fn set_rotary_scroll_factor(&mut self, factor: f32) {
736 if factor.is_finite() && factor > 0.0 {
737 self.rotary_scroll_factor = factor;
738 }
739 }
740
741 pub fn rotary_scrolled_by_detents(&mut self, detents: f32, uptime_millis: u64) -> bool {
747 let factor = self.rotary_scroll_factor;
748 self.rotary_scrolled(RotaryScrollEvent::from_detents(
749 detents,
750 factor,
751 factor,
752 uptime_millis,
753 ))
754 }
755
756 pub fn rotary_scrolled(&mut self, event: RotaryScrollEvent) -> bool {
777 let _event_handler = enter_event_handler_scope();
778 let app_context = Rc::clone(&self.app_context);
779 let result = app_context.enter(|| {
780 run_in_mutable_snapshot(|| self.rotary_scrolled_inner(event)).unwrap_or(false)
781 });
782 if result {
783 self.mark_dirty();
784 }
785 log::trace!(
786 target: "cranpose::input",
787 "rotary_scrolled v={:.2} h={:.2} uptime={} -> {result}",
788 event.vertical_scroll_pixels,
789 event.horizontal_scroll_pixels,
790 event.uptime_millis,
791 );
792 result
793 }
794
795 fn rotary_scrolled_inner(&mut self, rotary: RotaryScrollEvent) -> bool {
796 if rotary.is_empty() {
797 return false;
798 }
799
800 let bubble_order = self.rotary_dispatch_order();
803 let position = Point {
804 x: self.cursor.0,
805 y: self.cursor.1,
806 };
807
808 if !bubble_order.is_empty() {
809 let capture_targets = bubble_order
811 .iter()
812 .rev()
813 .filter_map(|&node_id| self.renderer.scene().find_target(node_id))
814 .collect::<Vec<_>>();
815 let capture_event =
816 PointerEvent::rotary(PointerEventKind::RotaryScrollPre, rotary, position);
817 self.dispatch_targets(capture_targets, capture_event.clone(), true);
818 if capture_event.is_consumed() {
819 return true;
820 }
821
822 let bubble_targets = bubble_order
824 .iter()
825 .filter_map(|&node_id| self.renderer.scene().find_target(node_id))
826 .collect::<Vec<_>>();
827 let bubble_event =
828 PointerEvent::rotary(PointerEventKind::RotaryScroll, rotary, position);
829 self.dispatch_targets(bubble_targets, bubble_event.clone(), true);
830 if bubble_event.is_consumed() {
831 return true;
832 }
833 }
834
835 if let Some(handler) = self.on_rotary_scroll.clone() {
837 return handler(rotary);
838 }
839
840 false
841 }
842
843 fn rotary_dispatch_order(&self) -> Vec<NodeId> {
849 if let Some(focused) = cranpose_ui::active_focus_target() {
850 if let Some(target) = self.renderer.scene().find_target(focused) {
851 let path = target.capture_path();
852 if !path.is_empty() {
853 return crate::hit_path_tracker::dispatch_order_for_paths(&[path]);
854 }
855 }
856 }
857
858 let hits = self.renderer.scene().hit_test(self.cursor.0, self.cursor.1);
859 if hits.is_empty() {
860 return Vec::new();
861 }
862 let capture_paths = hits
863 .iter()
864 .map(|hit| hit.capture_path())
865 .collect::<Vec<_>>();
866 crate::hit_path_tracker::dispatch_order_for_paths(&capture_paths)
867 }
868
869 pub fn cancel_gesture(&mut self) {
875 let event_time = self.realtime_pointer_event_time(None);
876 let _event_handler = enter_event_handler_scope();
877 let app_context = Rc::clone(&self.app_context);
878 let _ = app_context.enter(|| {
879 run_in_mutable_snapshot(|| {
880 self.cancel_gesture_inner(event_time);
881 })
882 });
883 }
884
885 fn cancel_gesture_inner(&mut self, event_time: PointerEventTime) {
886 let targets = self.resolve_gesture_targets(PointerId::PRIMARY);
887
888 self.hit_path_tracker.clear();
890 self.buttons_pressed = PointerButtons::NONE;
891
892 if !targets.is_empty() {
893 let event = self
894 .pointer_event(
895 PointerEventKind::Cancel,
896 Point {
897 x: self.cursor.0,
898 y: self.cursor.1,
899 },
900 Point {
901 x: self.cursor.0,
902 y: self.cursor.1,
903 },
904 event_time,
905 )
906 .with_source(self.pointer_source);
907
908 self.dispatch_targets(targets, event, false);
909 }
910
911 let pos = Point {
913 x: self.cursor.0,
914 y: self.cursor.1,
915 };
916 let hovered_nodes = self.hovered_nodes.clone();
917 for node_id in hovered_nodes {
918 if let Some(target) = self.renderer.scene().find_target(node_id) {
919 let exit_event = self
920 .pointer_event(PointerEventKind::Exit, pos, pos, event_time)
921 .with_source(self.pointer_source);
922 self.dispatch_targets(std::iter::once(target), exit_event, false);
923 }
924 }
925 self.hovered_nodes.clear();
926 }
927
928 pub fn set_platform_text_input(
935 &mut self,
936 handler: Rc<dyn cranpose_ui::PlatformTextInputHandler>,
937 ) {
938 let app_context = Rc::clone(&self.app_context);
939 app_context
940 .enter(|| cranpose_ui::text_input_session::set_platform_text_input_handler(handler));
941 }
942
943 pub fn clear_platform_text_input(&mut self) {
945 let app_context = Rc::clone(&self.app_context);
946 app_context.enter(cranpose_ui::text_input_session::clear_platform_text_input_handler);
947 }
948
949 pub fn notify_app_paused(&mut self) {
956 let app_context = Rc::clone(&self.app_context);
957 app_context.enter(cranpose_ui::text_input_session::notify_app_paused);
958 }
959
960 pub fn notify_app_resumed(&mut self) -> bool {
968 let app_context = Rc::clone(&self.app_context);
969 app_context.enter(cranpose_ui::text_input_session::notify_app_resumed)
970 }
971
972 pub fn on_key_event(&mut self, event: &KeyEvent) -> bool {
979 let _event_handler = enter_event_handler_scope();
980 let app_context = Rc::clone(&self.app_context);
981 app_context.enter(|| self.on_key_event_inner(event))
982 }
983
984 fn on_key_event_inner(&mut self, event: &KeyEvent) -> bool {
986 use KeyEventType::KeyDown;
987
988 if event.event_type == KeyDown && event.modifiers.command_or_ctrl() {
990 #[cfg(all(
991 feature = "clipboard-native",
992 not(target_arch = "wasm32"),
993 not(target_os = "android"),
994 not(target_os = "ios")
995 ))]
996 {
997 match event.key_code {
998 KeyCode::C => {
1000 if let Some(text) = self.on_copy_inner() {
1001 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1002 return true;
1003 }
1004 }
1005 KeyCode::X => {
1007 if let Some(text) = self.on_cut_inner() {
1008 cranpose_ui::clipboard_session::clipboard_write_text(&text);
1009 self.mark_dirty();
1010 self.request_layout_pass();
1011 return true;
1012 }
1013 }
1014 KeyCode::V => {
1016 if let Some(text) = cranpose_ui::clipboard_session::clipboard_read_text() {
1017 if self.on_paste_inner(&text) {
1018 return true;
1019 }
1020 }
1021 }
1022 _ => {}
1023 }
1024 }
1025 }
1026
1027 if !cranpose_ui::text_field_focus::has_focused_field() {
1029 return false;
1030 }
1031
1032 let handled = run_in_mutable_snapshot(|| {
1036 cranpose_ui::text_field_focus::dispatch_key_event(event)
1039 })
1040 .unwrap_or(false);
1041
1042 if handled {
1043 self.mark_dirty();
1045 self.request_layout_pass();
1046 }
1047
1048 handled
1049 }
1050
1051 pub fn on_paste(&mut self, text: &str) -> bool {
1055 let _event_handler = enter_event_handler_scope();
1056 let app_context = Rc::clone(&self.app_context);
1057 app_context.enter(|| self.on_paste_inner(text))
1058 }
1059
1060 fn on_paste_inner(&mut self, text: &str) -> bool {
1061 let handled =
1065 run_in_mutable_snapshot(|| cranpose_ui::text_field_focus::dispatch_paste(text))
1066 .unwrap_or(false);
1067
1068 if handled {
1069 self.mark_dirty();
1070 self.request_layout_pass();
1071 }
1072
1073 handled
1074 }
1075
1076 pub fn on_copy(&mut self) -> Option<String> {
1080 let app_context = Rc::clone(&self.app_context);
1081 app_context.enter(|| self.on_copy_inner())
1082 }
1083
1084 fn on_copy_inner(&mut self) -> Option<String> {
1085 cranpose_ui::text_field_focus::dispatch_copy()
1087 }
1088
1089 pub fn on_cut(&mut self) -> Option<String> {
1093 let _event_handler = enter_event_handler_scope();
1094 let app_context = Rc::clone(&self.app_context);
1095 app_context.enter(|| self.on_cut_inner())
1096 }
1097
1098 fn on_cut_inner(&mut self) -> Option<String> {
1099 let text =
1100 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_cut).unwrap_or(None);
1101
1102 if text.is_some() {
1103 self.mark_dirty();
1104 self.request_layout_pass();
1105 }
1106
1107 text
1108 }
1109
1110 #[cfg(all(
1114 feature = "clipboard-native",
1115 target_os = "linux",
1116 not(target_arch = "wasm32")
1117 ))]
1118 pub fn set_primary_selection(&mut self, text: &str) {
1119 use arboard::{LinuxClipboardKind, SetExtLinux};
1120 if let Some(ref mut clipboard) = self.clipboard {
1121 let result = clipboard
1122 .set()
1123 .clipboard(LinuxClipboardKind::Primary)
1124 .text(text.to_string());
1125 if let Err(e) = result {
1126 log::debug!("Primary selection set failed: {:?}", e);
1128 }
1129 }
1130 }
1131
1132 #[cfg(not(all(
1133 feature = "clipboard-native",
1134 target_os = "linux",
1135 not(target_arch = "wasm32")
1136 )))]
1137 pub fn set_primary_selection(&mut self, _text: &str) {}
1138
1139 #[cfg(all(
1142 feature = "clipboard-native",
1143 target_os = "linux",
1144 not(target_arch = "wasm32")
1145 ))]
1146 pub fn get_primary_selection(&mut self) -> Option<String> {
1147 use arboard::{GetExtLinux, LinuxClipboardKind};
1148 if let Some(ref mut clipboard) = self.clipboard {
1149 clipboard
1150 .get()
1151 .clipboard(LinuxClipboardKind::Primary)
1152 .text()
1153 .ok()
1154 } else {
1155 None
1156 }
1157 }
1158
1159 #[cfg(not(all(
1160 feature = "clipboard-native",
1161 target_os = "linux",
1162 not(target_arch = "wasm32")
1163 )))]
1164 pub fn get_primary_selection(&mut self) -> Option<String> {
1165 None
1166 }
1167
1168 pub fn sync_selection_to_primary(&mut self) {
1171 #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))]
1172 {
1173 if let Some(text) = self.on_copy() {
1174 self.set_primary_selection(&text);
1175 }
1176 }
1177 }
1178
1179 pub fn on_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1187 let _event_handler = enter_event_handler_scope();
1188 let app_context = Rc::clone(&self.app_context);
1189 app_context.enter(|| self.on_ime_preedit_inner(text, cursor))
1190 }
1191
1192 fn on_ime_preedit_inner(&mut self, text: &str, cursor: Option<(usize, usize)>) -> bool {
1193 let handled = run_in_mutable_snapshot(|| {
1195 cranpose_ui::text_field_focus::dispatch_ime_preedit(text, cursor)
1196 })
1197 .unwrap_or(false);
1198
1199 if handled {
1200 self.mark_dirty();
1201 self.request_layout_pass();
1203 }
1204
1205 handled
1206 }
1207
1208 pub fn on_ime_finish_composing(&mut self) -> bool {
1212 let _event_handler = enter_event_handler_scope();
1213 let app_context = Rc::clone(&self.app_context);
1214 app_context.enter(|| self.on_ime_finish_composing_inner())
1215 }
1216
1217 fn on_ime_finish_composing_inner(&mut self) -> bool {
1218 let handled =
1219 run_in_mutable_snapshot(cranpose_ui::text_field_focus::dispatch_ime_finish_composing)
1220 .unwrap_or(false);
1221
1222 if handled {
1223 self.mark_dirty();
1224 self.request_layout_pass();
1225 }
1226
1227 handled
1228 }
1229
1230 pub fn on_ime_set_composing_region(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1234 let _event_handler = enter_event_handler_scope();
1235 let app_context = Rc::clone(&self.app_context);
1236 app_context.enter(|| {
1237 let handled = run_in_mutable_snapshot(|| {
1238 cranpose_ui::text_field_focus::dispatch_ime_set_composing_region(
1239 start_bytes,
1240 end_bytes,
1241 )
1242 })
1243 .unwrap_or(false);
1244
1245 if handled {
1246 self.mark_dirty();
1247 self.request_layout_pass();
1248 }
1249
1250 handled
1251 })
1252 }
1253
1254 pub fn on_ime_set_selection(&mut self, start_bytes: usize, end_bytes: usize) -> bool {
1259 let _event_handler = enter_event_handler_scope();
1260 let app_context = Rc::clone(&self.app_context);
1261 app_context.enter(|| {
1262 let handled = run_in_mutable_snapshot(|| {
1263 cranpose_ui::text_field_focus::dispatch_ime_set_selection(start_bytes, end_bytes)
1264 })
1265 .unwrap_or(false);
1266
1267 if handled {
1270 self.mark_dirty();
1271 }
1272
1273 handled
1274 })
1275 }
1276
1277 pub fn ime_editor_state(&mut self) -> Option<cranpose_ui::text_field_focus::ImeEditorState> {
1281 let app_context = Rc::clone(&self.app_context);
1282 app_context.enter(cranpose_ui::text_field_focus::focused_editor_state)
1283 }
1284
1285 pub fn ime_caret_geometry(
1289 &mut self,
1290 ) -> Option<cranpose_ui::text_field_focus::ImeCaretGeometry> {
1291 let app_context = Rc::clone(&self.app_context);
1292 app_context.enter(cranpose_ui::text_field_focus::focused_caret_geometry)
1293 }
1294
1295 pub fn clear_text_field_focus(&mut self) {
1298 let _event_handler = enter_event_handler_scope();
1299 let app_context = Rc::clone(&self.app_context);
1300 app_context.enter(cranpose_ui::text_field_focus::clear_focus);
1301 self.mark_dirty();
1302 self.request_layout_pass();
1303 }
1304
1305 pub fn on_ime_delete_surrounding(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1308 let _event_handler = enter_event_handler_scope();
1309 let app_context = Rc::clone(&self.app_context);
1310 app_context.enter(|| self.on_ime_delete_surrounding_inner(before_bytes, after_bytes))
1311 }
1312
1313 fn on_ime_delete_surrounding_inner(&mut self, before_bytes: usize, after_bytes: usize) -> bool {
1314 let handled = run_in_mutable_snapshot(|| {
1315 cranpose_ui::text_field_focus::dispatch_delete_surrounding(before_bytes, after_bytes)
1316 })
1317 .unwrap_or(false);
1318
1319 if handled {
1320 self.mark_dirty();
1321 self.request_layout_pass();
1322 }
1323
1324 handled
1325 }
1326}