1#![warn(missing_docs)]
7
8use crate::item_tree::ItemTreeRc;
9use crate::item_tree::{ItemRc, ItemWeak, VisitChildrenResult};
10pub use crate::items::PointerEventButton;
11use crate::items::{DropEvent, ItemRef, TextCursorDirection};
12pub use crate::items::{FocusReason, KeyEvent, KeyboardModifiers};
13use crate::lengths::{ItemTransform, LogicalPoint, LogicalVector};
14use crate::timers::Timer;
15use crate::window::{WindowAdapter, WindowInner};
16use crate::{Coord, Property, SharedString};
17use alloc::rc::Rc;
18use alloc::vec::Vec;
19use const_field_offset::FieldOffsets;
20use core::cell::Cell;
21use core::pin::Pin;
22use core::time::Duration;
23
24#[repr(C)]
29#[derive(Debug, Clone, PartialEq)]
30#[allow(missing_docs)]
31pub enum MouseEvent {
32 Pressed { position: LogicalPoint, button: PointerEventButton, click_count: u8, is_touch: bool },
37 Released { position: LogicalPoint, button: PointerEventButton, click_count: u8, is_touch: bool },
42 Moved { position: LogicalPoint, is_touch: bool },
44 Wheel { position: LogicalPoint, delta_x: Coord, delta_y: Coord },
49 DragMove(DropEvent),
53 Drop(DropEvent),
55 Exit,
57}
58
59impl MouseEvent {
60 pub fn is_touch(&self) -> Option<bool> {
62 match self {
63 MouseEvent::Pressed { is_touch, .. } => Some(*is_touch),
64 MouseEvent::Released { is_touch, .. } => Some(*is_touch),
65 MouseEvent::Moved { is_touch, .. } => Some(*is_touch),
66 MouseEvent::Wheel { .. } => None,
67 MouseEvent::DragMove(..) | MouseEvent::Drop(..) => None,
68 MouseEvent::Exit => None,
69 }
70 }
71
72 pub fn position(&self) -> Option<LogicalPoint> {
74 match self {
75 MouseEvent::Pressed { position, .. } => Some(*position),
76 MouseEvent::Released { position, .. } => Some(*position),
77 MouseEvent::Moved { position, .. } => Some(*position),
78 MouseEvent::Wheel { position, .. } => Some(*position),
79 MouseEvent::DragMove(e) | MouseEvent::Drop(e) => {
80 Some(crate::lengths::logical_point_from_api(e.position))
81 }
82 MouseEvent::Exit => None,
83 }
84 }
85
86 pub fn translate(&mut self, vec: LogicalVector) {
88 let pos = match self {
89 MouseEvent::Pressed { position, .. } => Some(position),
90 MouseEvent::Released { position, .. } => Some(position),
91 MouseEvent::Moved { position, .. } => Some(position),
92 MouseEvent::Wheel { position, .. } => Some(position),
93 MouseEvent::DragMove(e) | MouseEvent::Drop(e) => {
94 e.position = crate::api::LogicalPosition::from_euclid(
95 crate::lengths::logical_point_from_api(e.position) + vec,
96 );
97 None
98 }
99 MouseEvent::Exit => None,
100 };
101 if let Some(pos) = pos {
102 *pos += vec;
103 }
104 }
105
106 pub fn transform(&mut self, transform: ItemTransform) {
108 let pos = match self {
109 MouseEvent::Pressed { position, .. } => Some(position),
110 MouseEvent::Released { position, .. } => Some(position),
111 MouseEvent::Moved { position, .. } => Some(position),
112 MouseEvent::Wheel { position, .. } => Some(position),
113 MouseEvent::DragMove(e) | MouseEvent::Drop(e) => {
114 e.position = crate::api::LogicalPosition::from_euclid(
115 transform
116 .transform_point(crate::lengths::logical_point_from_api(e.position).cast())
117 .cast(),
118 );
119 None
120 }
121 MouseEvent::Exit => None,
122 };
123 if let Some(pos) = pos {
124 *pos = transform.transform_point(pos.cast()).cast();
125 }
126 }
127
128 fn set_click_count(&mut self, count: u8) {
130 match self {
131 MouseEvent::Pressed { click_count, .. } | MouseEvent::Released { click_count, .. } => {
132 *click_count = count
133 }
134 _ => (),
135 }
136 }
137}
138
139#[repr(u8)]
144#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
145pub enum InputEventResult {
146 EventAccepted,
149 #[default]
151 EventIgnored,
152 GrabMouse,
154 StartDrag,
156}
157
158#[repr(C)]
162#[derive(Debug, Copy, Clone, PartialEq, Default)]
163pub enum InputEventFilterResult {
164 #[default]
167 ForwardEvent,
168 ForwardAndIgnore,
171 ForwardAndInterceptGrab,
174 Intercept,
177 DelayForwarding(u64),
184}
185
186#[allow(missing_docs, non_upper_case_globals)]
188pub mod key_codes {
189 macro_rules! declare_consts_for_special_keys {
190 ($($char:literal # $name:ident # $($_qt:ident)|* # $($_winit:ident $(($_pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
191 $(pub const $name : char = $char;)*
192
193 #[allow(missing_docs)]
194 #[derive(Debug, Copy, Clone, PartialEq)]
195 #[non_exhaustive]
196 pub enum Key {
211 $($name,)*
212 }
213
214 impl From<Key> for char {
215 fn from(k: Key) -> Self {
216 match k {
217 $(Key::$name => $name,)*
218 }
219 }
220 }
221
222 impl From<Key> for crate::SharedString {
223 fn from(k: Key) -> Self {
224 char::from(k).into()
225 }
226 }
227 };
228 }
229
230 i_slint_common::for_each_special_keys!(declare_consts_for_special_keys);
231}
232
233#[derive(Clone, Copy, Default, Debug)]
236pub(crate) struct InternalKeyboardModifierState {
237 left_alt: bool,
238 right_alt: bool,
239 altgr: bool,
240 left_control: bool,
241 right_control: bool,
242 left_meta: bool,
243 right_meta: bool,
244 left_shift: bool,
245 right_shift: bool,
246}
247
248impl InternalKeyboardModifierState {
249 pub(crate) fn state_update(mut self, pressed: bool, text: &SharedString) -> Option<Self> {
252 if let Some(key_code) = text.chars().next() {
253 match key_code {
254 key_codes::Alt => self.left_alt = pressed,
255 key_codes::AltGr => self.altgr = pressed,
256 key_codes::Control => self.left_control = pressed,
257 key_codes::ControlR => self.right_control = pressed,
258 key_codes::Shift => self.left_shift = pressed,
259 key_codes::ShiftR => self.right_shift = pressed,
260 key_codes::Meta => self.left_meta = pressed,
261 key_codes::MetaR => self.right_meta = pressed,
262 _ => return None,
263 };
264
265 debug_assert_eq!(key_code.len_utf8(), text.len());
269 }
270
271 #[cfg(target_os = "windows")]
273 {
274 if self.altgr {
275 self.left_control = false;
277 self.right_control = false;
278 } else if self.control() && self.alt() {
279 self.left_control = false;
281 self.right_control = false;
282 self.left_alt = false;
283 self.right_alt = false;
284 }
285 }
286
287 Some(self)
288 }
289
290 pub fn shift(&self) -> bool {
291 self.right_shift || self.left_shift
292 }
293 pub fn alt(&self) -> bool {
294 self.right_alt || self.left_alt
295 }
296 pub fn meta(&self) -> bool {
297 self.right_meta || self.left_meta
298 }
299 pub fn control(&self) -> bool {
300 self.right_control || self.left_control
301 }
302}
303
304impl From<InternalKeyboardModifierState> for KeyboardModifiers {
305 fn from(internal_state: InternalKeyboardModifierState) -> Self {
306 Self {
307 alt: internal_state.alt(),
308 control: internal_state.control(),
309 meta: internal_state.meta(),
310 shift: internal_state.shift(),
311 }
312 }
313}
314
315#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
317#[repr(u8)]
318pub enum KeyEventType {
319 #[default]
321 KeyPressed = 0,
322 KeyReleased = 1,
324 UpdateComposition = 2,
327 CommitComposition = 3,
329}
330
331impl KeyEvent {
332 pub fn shortcut(&self) -> Option<StandardShortcut> {
335 if self.modifiers.control && !self.modifiers.shift {
336 match self.text.as_str() {
337 #[cfg(not(target_arch = "wasm32"))]
338 "c" => Some(StandardShortcut::Copy),
339 #[cfg(not(target_arch = "wasm32"))]
340 "x" => Some(StandardShortcut::Cut),
341 #[cfg(not(target_arch = "wasm32"))]
342 "v" => Some(StandardShortcut::Paste),
343 "a" => Some(StandardShortcut::SelectAll),
344 "f" => Some(StandardShortcut::Find),
345 "s" => Some(StandardShortcut::Save),
346 "p" => Some(StandardShortcut::Print),
347 "z" => Some(StandardShortcut::Undo),
348 #[cfg(target_os = "windows")]
349 "y" => Some(StandardShortcut::Redo),
350 "r" => Some(StandardShortcut::Refresh),
351 _ => None,
352 }
353 } else if self.modifiers.control && self.modifiers.shift {
354 match self.text.as_str() {
355 #[cfg(not(target_os = "windows"))]
356 "z" => Some(StandardShortcut::Redo),
357 _ => None,
358 }
359 } else {
360 None
361 }
362 }
363
364 pub fn text_shortcut(&self) -> Option<TextShortcut> {
367 let keycode = self.text.chars().next()?;
368
369 let is_apple = crate::is_apple_platform();
370
371 let move_mod = if is_apple {
372 self.modifiers.alt && !self.modifiers.control && !self.modifiers.meta
373 } else {
374 self.modifiers.control && !self.modifiers.alt && !self.modifiers.meta
375 };
376
377 if move_mod {
378 match keycode {
379 key_codes::LeftArrow => {
380 return Some(TextShortcut::Move(TextCursorDirection::BackwardByWord));
381 }
382 key_codes::RightArrow => {
383 return Some(TextShortcut::Move(TextCursorDirection::ForwardByWord));
384 }
385 key_codes::UpArrow => {
386 return Some(TextShortcut::Move(TextCursorDirection::StartOfParagraph));
387 }
388 key_codes::DownArrow => {
389 return Some(TextShortcut::Move(TextCursorDirection::EndOfParagraph));
390 }
391 key_codes::Backspace => {
392 return Some(TextShortcut::DeleteWordBackward);
393 }
394 key_codes::Delete => {
395 return Some(TextShortcut::DeleteWordForward);
396 }
397 _ => (),
398 };
399 }
400
401 #[cfg(not(target_os = "macos"))]
402 {
403 if self.modifiers.control && !self.modifiers.alt && !self.modifiers.meta {
404 match keycode {
405 key_codes::Home => {
406 return Some(TextShortcut::Move(TextCursorDirection::StartOfText));
407 }
408 key_codes::End => {
409 return Some(TextShortcut::Move(TextCursorDirection::EndOfText));
410 }
411 _ => (),
412 };
413 }
414 }
415
416 if is_apple && self.modifiers.control {
417 match keycode {
418 key_codes::LeftArrow => {
419 return Some(TextShortcut::Move(TextCursorDirection::StartOfLine));
420 }
421 key_codes::RightArrow => {
422 return Some(TextShortcut::Move(TextCursorDirection::EndOfLine));
423 }
424 key_codes::UpArrow => {
425 return Some(TextShortcut::Move(TextCursorDirection::StartOfText));
426 }
427 key_codes::DownArrow => {
428 return Some(TextShortcut::Move(TextCursorDirection::EndOfText));
429 }
430 key_codes::Backspace => {
431 return Some(TextShortcut::DeleteToStartOfLine);
432 }
433 _ => (),
434 };
435 }
436
437 if let Ok(direction) = TextCursorDirection::try_from(keycode) {
438 Some(TextShortcut::Move(direction))
439 } else {
440 match keycode {
441 key_codes::Backspace => Some(TextShortcut::DeleteBackward),
442 key_codes::Delete => Some(TextShortcut::DeleteForward),
443 _ => None,
444 }
445 }
446 }
447}
448
449pub enum StandardShortcut {
451 Copy,
453 Cut,
455 Paste,
457 SelectAll,
459 Find,
461 Save,
463 Print,
465 Undo,
467 Redo,
469 Refresh,
471}
472
473pub enum TextShortcut {
475 Move(TextCursorDirection),
477 DeleteForward,
479 DeleteBackward,
481 DeleteWordForward,
483 DeleteWordBackward,
485 DeleteToStartOfLine,
487}
488
489#[repr(u8)]
492#[derive(Debug, Clone, Copy, PartialEq, Default)]
493pub enum KeyEventResult {
494 EventAccepted,
496 #[default]
498 EventIgnored,
499}
500
501#[repr(u8)]
504#[derive(Debug, Clone, Copy, PartialEq, Default)]
505pub enum FocusEventResult {
506 FocusAccepted,
508 #[default]
510 FocusIgnored,
511}
512
513#[derive(Debug, Clone, Copy, PartialEq)]
516#[repr(u8)]
517pub enum FocusEvent {
518 FocusIn(FocusReason),
520 FocusOut(FocusReason),
522}
523
524#[derive(Default)]
526pub struct ClickState {
527 click_count_time_stamp: Cell<Option<crate::animations::Instant>>,
528 click_count: Cell<u8>,
529 click_position: Cell<LogicalPoint>,
530 click_button: Cell<PointerEventButton>,
531}
532
533impl ClickState {
534 fn restart(&self, position: LogicalPoint, button: PointerEventButton) {
536 self.click_count.set(0);
537 self.click_count_time_stamp.set(Some(crate::animations::Instant::now()));
538 self.click_position.set(position);
539 self.click_button.set(button);
540 }
541
542 pub fn reset(&self) {
544 self.click_count.set(0);
545 self.click_count_time_stamp.replace(None);
546 }
547
548 pub fn check_repeat(&self, mouse_event: MouseEvent, click_interval: Duration) -> MouseEvent {
550 match mouse_event {
551 MouseEvent::Pressed { position, button, is_touch, .. } => {
552 let instant_now = crate::animations::Instant::now();
553
554 if let Some(click_count_time_stamp) = self.click_count_time_stamp.get() {
555 if instant_now - click_count_time_stamp < click_interval
556 && button == self.click_button.get()
557 && (position - self.click_position.get()).square_length() < 100 as _
558 {
559 self.click_count.set(self.click_count.get().wrapping_add(1));
560 self.click_count_time_stamp.set(Some(instant_now));
561 } else {
562 self.restart(position, button);
563 }
564 } else {
565 self.restart(position, button);
566 }
567
568 return MouseEvent::Pressed {
569 position,
570 button,
571 click_count: self.click_count.get(),
572 is_touch,
573 };
574 }
575 MouseEvent::Released { position, button, is_touch, .. } => {
576 return MouseEvent::Released {
577 position,
578 button,
579 click_count: self.click_count.get(),
580 is_touch,
581 };
582 }
583 _ => {}
584 };
585
586 mouse_event
587 }
588}
589
590#[derive(Default)]
592pub struct MouseInputState {
593 item_stack: Vec<(ItemWeak, InputEventFilterResult)>,
596 pub(crate) offset: LogicalPoint,
598 grabbed: bool,
600 pub(crate) drag_data: Option<DropEvent>,
603 delayed: Option<(crate::timers::Timer, MouseEvent)>,
604 delayed_exit_items: Vec<ItemWeak>,
605}
606
607impl MouseInputState {
608 fn top_item(&self) -> Option<ItemRc> {
610 self.item_stack.last().and_then(|x| x.0.upgrade())
611 }
612
613 pub fn top_item_including_delayed(&self) -> Option<ItemRc> {
615 self.delayed_exit_items.last().and_then(|x| x.upgrade()).or_else(|| self.top_item())
616 }
617}
618
619pub(crate) fn handle_mouse_grab(
622 mouse_event: &MouseEvent,
623 window_adapter: &Rc<dyn WindowAdapter>,
624 mouse_input_state: &mut MouseInputState,
625) -> Option<MouseEvent> {
626 if !mouse_input_state.grabbed || mouse_input_state.item_stack.is_empty() {
627 return Some(mouse_event.clone());
628 };
629
630 let mut event = mouse_event.clone();
631 let mut intercept = false;
632 let mut invalid = false;
633
634 event.translate(-mouse_input_state.offset.to_vector());
635
636 mouse_input_state.item_stack.retain(|it| {
637 if invalid {
638 return false;
639 }
640 let item = if let Some(item) = it.0.upgrade() {
641 item
642 } else {
643 invalid = true;
644 return false;
645 };
646 if intercept {
647 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
648 return false;
649 }
650 let g = item.geometry();
651 event.translate(-g.origin.to_vector());
652 if window_adapter.renderer().supports_transformations() {
653 if let Some(inverse_transform) = item.inverse_children_transform() {
654 event.transform(inverse_transform);
655 }
656 }
657
658 let interested = matches!(
659 it.1,
660 InputEventFilterResult::ForwardAndInterceptGrab
661 | InputEventFilterResult::DelayForwarding(_)
662 );
663
664 if interested
665 && item.borrow().as_ref().input_event_filter_before_children(
666 &event,
667 window_adapter,
668 &item,
669 ) == InputEventFilterResult::Intercept
670 {
671 intercept = true;
672 }
673 true
674 });
675 if invalid {
676 return Some(mouse_event.clone());
677 }
678
679 let grabber = mouse_input_state.top_item().unwrap();
680 let input_result = grabber.borrow().as_ref().input_event(&event, window_adapter, &grabber);
681 match input_result {
682 InputEventResult::GrabMouse => None,
683 InputEventResult::StartDrag => {
684 mouse_input_state.grabbed = false;
685 let drag_area_item = grabber.downcast::<crate::items::DragArea>().unwrap();
686 mouse_input_state.drag_data = Some(DropEvent {
687 mime_type: drag_area_item.as_pin_ref().mime_type(),
688 data: drag_area_item.as_pin_ref().data(),
689 position: Default::default(),
690 });
691 None
692 }
693 _ => {
694 mouse_input_state.grabbed = false;
695 Some(mouse_event.position().map_or(MouseEvent::Exit, |position| MouseEvent::Moved {
697 position,
698 is_touch: mouse_event.is_touch().unwrap_or(false),
699 }))
700 }
701 }
702}
703
704pub(crate) fn send_exit_events(
705 old_input_state: &MouseInputState,
706 new_input_state: &mut MouseInputState,
707 mut pos: Option<LogicalPoint>,
708 window_adapter: &Rc<dyn WindowAdapter>,
709) {
710 for it in core::mem::take(&mut new_input_state.delayed_exit_items) {
711 let Some(item) = it.upgrade() else { continue };
712 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
713 }
714
715 let mut clipped = false;
716 for (idx, it) in old_input_state.item_stack.iter().enumerate() {
717 let Some(item) = it.0.upgrade() else { break };
718 let g = item.geometry();
719 let contains = pos.is_some_and(|p| g.contains(p));
720 if let Some(p) = pos.as_mut() {
721 *p -= g.origin.to_vector();
722 if window_adapter.renderer().supports_transformations() {
723 if let Some(inverse_transform) = item.inverse_children_transform() {
724 *p = inverse_transform.transform_point(p.cast()).cast();
725 }
726 }
727 }
728 if !contains || clipped {
729 if item.borrow().as_ref().clips_children() {
730 clipped = true;
731 }
732 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
733 } else if new_input_state.item_stack.get(idx).is_none_or(|(x, _)| *x != it.0) {
734 if new_input_state.delayed.is_some() {
736 new_input_state.delayed_exit_items.push(it.0.clone());
737 } else {
738 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
739 }
740 }
741 }
742}
743
744pub fn process_mouse_input(
748 root: ItemRc,
749 mouse_event: &MouseEvent,
750 window_adapter: &Rc<dyn WindowAdapter>,
751 mouse_input_state: MouseInputState,
752) -> MouseInputState {
753 let mut result =
754 MouseInputState { drag_data: mouse_input_state.drag_data.clone(), ..Default::default() };
755 let r = send_mouse_event_to_item(
756 mouse_event,
757 root.clone(),
758 window_adapter,
759 &mut result,
760 mouse_input_state.top_item().as_ref(),
761 false,
762 );
763 if mouse_input_state.delayed.is_some()
764 && (!r.has_aborted()
765 || Option::zip(result.item_stack.last(), mouse_input_state.item_stack.last())
766 .is_none_or(|(a, b)| a.0 != b.0))
767 {
768 return mouse_input_state;
770 }
771 send_exit_events(&mouse_input_state, &mut result, mouse_event.position(), window_adapter);
772
773 if let MouseEvent::Wheel { position, .. } = mouse_event {
774 if r.has_aborted() {
775 return process_mouse_input(
777 root,
778 &MouseEvent::Moved { position: *position, is_touch: false },
779 window_adapter,
780 result,
781 );
782 }
783 }
784
785 result
786}
787
788pub(crate) fn process_delayed_event(
789 window_adapter: &Rc<dyn WindowAdapter>,
790 mut mouse_input_state: MouseInputState,
791) -> MouseInputState {
792 let event = match mouse_input_state.delayed.take() {
794 Some(e) => e.1,
795 None => return mouse_input_state,
796 };
797
798 let top_item = match mouse_input_state.top_item() {
799 Some(i) => i,
800 None => return MouseInputState::default(),
801 };
802
803 let mut actual_visitor =
804 |component: &ItemTreeRc, index: u32, _: Pin<ItemRef>| -> VisitChildrenResult {
805 send_mouse_event_to_item(
806 &event,
807 ItemRc::new(component.clone(), index),
808 window_adapter,
809 &mut mouse_input_state,
810 Some(&top_item),
811 true,
812 )
813 };
814 vtable::new_vref!(let mut actual_visitor : VRefMut<crate::item_tree::ItemVisitorVTable> for crate::item_tree::ItemVisitor = &mut actual_visitor);
815 vtable::VRc::borrow_pin(top_item.item_tree()).as_ref().visit_children_item(
816 top_item.index() as isize,
817 crate::item_tree::TraversalOrder::FrontToBack,
818 actual_visitor,
819 );
820 mouse_input_state
821}
822
823fn send_mouse_event_to_item(
824 mouse_event: &MouseEvent,
825 item_rc: ItemRc,
826 window_adapter: &Rc<dyn WindowAdapter>,
827 result: &mut MouseInputState,
828 last_top_item: Option<&ItemRc>,
829 ignore_delays: bool,
830) -> VisitChildrenResult {
831 let item = item_rc.borrow();
832 let geom = item_rc.geometry();
833 let mut event_for_children = mouse_event.clone();
835 event_for_children.translate(-geom.origin.to_vector());
837 if window_adapter.renderer().supports_transformations() {
838 if let Some(inverse_transform) = item_rc.inverse_children_transform() {
840 event_for_children.transform(inverse_transform);
841 }
842 }
843
844 let filter_result = if mouse_event.position().is_some_and(|p| geom.contains(p))
845 || item.as_ref().clips_children()
846 {
847 item.as_ref().input_event_filter_before_children(
848 &event_for_children,
849 window_adapter,
850 &item_rc,
851 )
852 } else {
853 InputEventFilterResult::ForwardAndIgnore
854 };
855
856 let (forward_to_children, ignore) = match filter_result {
857 InputEventFilterResult::ForwardEvent => (true, false),
858 InputEventFilterResult::ForwardAndIgnore => (true, true),
859 InputEventFilterResult::ForwardAndInterceptGrab => (true, false),
860 InputEventFilterResult::Intercept => (false, false),
861 InputEventFilterResult::DelayForwarding(_) if ignore_delays => (true, false),
862 InputEventFilterResult::DelayForwarding(duration) => {
863 let timer = Timer::default();
864 let w = Rc::downgrade(window_adapter);
865 timer.start(
866 crate::timers::TimerMode::SingleShot,
867 Duration::from_millis(duration),
868 move || {
869 if let Some(w) = w.upgrade() {
870 WindowInner::from_pub(w.window()).process_delayed_event();
871 }
872 },
873 );
874 result.delayed = Some((timer, event_for_children));
875 result
876 .item_stack
877 .push((item_rc.downgrade(), InputEventFilterResult::DelayForwarding(duration)));
878 return VisitChildrenResult::abort(item_rc.index(), 0);
879 }
880 };
881
882 result.item_stack.push((item_rc.downgrade(), filter_result));
883 if forward_to_children {
884 let mut actual_visitor =
885 |component: &ItemTreeRc, index: u32, _: Pin<ItemRef>| -> VisitChildrenResult {
886 send_mouse_event_to_item(
887 &event_for_children,
888 ItemRc::new(component.clone(), index),
889 window_adapter,
890 result,
891 last_top_item,
892 ignore_delays,
893 )
894 };
895 vtable::new_vref!(let mut actual_visitor : VRefMut<crate::item_tree::ItemVisitorVTable> for crate::item_tree::ItemVisitor = &mut actual_visitor);
896 let r = vtable::VRc::borrow_pin(item_rc.item_tree()).as_ref().visit_children_item(
897 item_rc.index() as isize,
898 crate::item_tree::TraversalOrder::FrontToBack,
899 actual_visitor,
900 );
901 if r.has_aborted() {
902 return r;
903 }
904 };
905
906 let r = if ignore {
907 InputEventResult::EventIgnored
908 } else {
909 let mut event = mouse_event.clone();
910 event.translate(-geom.origin.to_vector());
911 if last_top_item.is_none_or(|x| *x != item_rc) {
912 event.set_click_count(0);
913 }
914 item.as_ref().input_event(&event, window_adapter, &item_rc)
915 };
916 match r {
917 InputEventResult::EventAccepted => VisitChildrenResult::abort(item_rc.index(), 0),
918 InputEventResult::EventIgnored => {
919 let _pop = result.item_stack.pop();
920 debug_assert_eq!(
921 _pop.map(|x| (x.0.upgrade().unwrap().index(), x.1)).unwrap(),
922 (item_rc.index(), filter_result)
923 );
924 VisitChildrenResult::CONTINUE
925 }
926 InputEventResult::GrabMouse => {
927 result.item_stack.last_mut().unwrap().1 =
928 InputEventFilterResult::ForwardAndInterceptGrab;
929 result.grabbed = true;
930 VisitChildrenResult::abort(item_rc.index(), 0)
931 }
932 InputEventResult::StartDrag => {
933 result.item_stack.last_mut().unwrap().1 =
934 InputEventFilterResult::ForwardAndInterceptGrab;
935 result.grabbed = false;
936 let drag_area_item = item_rc.downcast::<crate::items::DragArea>().unwrap();
937 result.drag_data = Some(DropEvent {
938 mime_type: drag_area_item.as_pin_ref().mime_type(),
939 data: drag_area_item.as_pin_ref().data(),
940 position: Default::default(),
941 });
942 VisitChildrenResult::abort(item_rc.index(), 0)
943 }
944 }
945}
946
947#[derive(FieldOffsets)]
954#[repr(C)]
955#[pin]
956pub(crate) struct TextCursorBlinker {
957 cursor_visible: Property<bool>,
958 cursor_blink_timer: crate::timers::Timer,
959}
960
961impl TextCursorBlinker {
962 pub fn new() -> Pin<Rc<Self>> {
965 Rc::pin(Self {
966 cursor_visible: Property::new(true),
967 cursor_blink_timer: Default::default(),
968 })
969 }
970
971 pub fn set_binding(
974 instance: Pin<Rc<TextCursorBlinker>>,
975 prop: &Property<bool>,
976 cycle_duration: Duration,
977 ) {
978 instance.as_ref().cursor_visible.set(true);
979 Self::start(&instance, cycle_duration);
981 prop.set_binding(move || {
982 TextCursorBlinker::FIELD_OFFSETS.cursor_visible.apply_pin(instance.as_ref()).get()
983 });
984 }
985
986 pub fn start(self: &Pin<Rc<Self>>, cycle_duration: Duration) {
989 if self.cursor_blink_timer.running() {
990 self.cursor_blink_timer.restart();
991 } else {
992 let toggle_cursor = {
993 let weak_blinker = pin_weak::rc::PinWeak::downgrade(self.clone());
994 move || {
995 if let Some(blinker) = weak_blinker.upgrade() {
996 let visible = TextCursorBlinker::FIELD_OFFSETS
997 .cursor_visible
998 .apply_pin(blinker.as_ref())
999 .get();
1000 blinker.cursor_visible.set(!visible);
1001 }
1002 }
1003 };
1004 if !cycle_duration.is_zero() {
1005 self.cursor_blink_timer.start(
1006 crate::timers::TimerMode::Repeated,
1007 cycle_duration / 2,
1008 toggle_cursor,
1009 );
1010 }
1011 }
1012 }
1013
1014 pub fn stop(&self) {
1017 self.cursor_blink_timer.stop()
1018 }
1019}