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::{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 },
37 Released { position: LogicalPoint, button: PointerEventButton, click_count: u8 },
42 Moved { position: LogicalPoint },
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 position(&self) -> Option<LogicalPoint> {
62 match self {
63 MouseEvent::Pressed { position, .. } => Some(*position),
64 MouseEvent::Released { position, .. } => Some(*position),
65 MouseEvent::Moved { position } => Some(*position),
66 MouseEvent::Wheel { position, .. } => Some(*position),
67 MouseEvent::DragMove(e) | MouseEvent::Drop(e) => {
68 Some(crate::lengths::logical_point_from_api(e.position))
69 }
70 MouseEvent::Exit => None,
71 }
72 }
73
74 pub fn translate(&mut self, vec: LogicalVector) {
76 let pos = match self {
77 MouseEvent::Pressed { position, .. } => Some(position),
78 MouseEvent::Released { position, .. } => Some(position),
79 MouseEvent::Moved { position } => Some(position),
80 MouseEvent::Wheel { position, .. } => Some(position),
81 MouseEvent::DragMove(e) | MouseEvent::Drop(e) => {
82 e.position = crate::api::LogicalPosition::from_euclid(
83 crate::lengths::logical_point_from_api(e.position) + vec,
84 );
85 None
86 }
87 MouseEvent::Exit => None,
88 };
89 if let Some(pos) = pos {
90 *pos += vec;
91 }
92 }
93
94 fn set_click_count(&mut self, count: u8) {
96 match self {
97 MouseEvent::Pressed { click_count, .. } | MouseEvent::Released { click_count, .. } => {
98 *click_count = count
99 }
100 _ => (),
101 }
102 }
103}
104
105#[repr(u8)]
110#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
111pub enum InputEventResult {
112 EventAccepted,
115 #[default]
117 EventIgnored,
118 GrabMouse,
120 StartDrag,
122}
123
124#[repr(C)]
128#[derive(Debug, Copy, Clone, PartialEq, Default)]
129pub enum InputEventFilterResult {
130 #[default]
133 ForwardEvent,
134 ForwardAndIgnore,
137 ForwardAndInterceptGrab,
140 Intercept,
143 DelayForwarding(u64),
150}
151
152#[allow(missing_docs, non_upper_case_globals)]
154pub mod key_codes {
155 macro_rules! declare_consts_for_special_keys {
156 ($($char:literal # $name:ident # $($_qt:ident)|* # $($_winit:ident $(($_pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
157 $(pub const $name : char = $char;)*
158
159 #[allow(missing_docs)]
160 #[derive(Debug, Copy, Clone, PartialEq)]
161 #[non_exhaustive]
162 pub enum Key {
177 $($name,)*
178 }
179
180 impl From<Key> for char {
181 fn from(k: Key) -> Self {
182 match k {
183 $(Key::$name => $name,)*
184 }
185 }
186 }
187
188 impl From<Key> for crate::SharedString {
189 fn from(k: Key) -> Self {
190 char::from(k).into()
191 }
192 }
193 };
194 }
195
196 i_slint_common::for_each_special_keys!(declare_consts_for_special_keys);
197}
198
199#[derive(Clone, Copy, Default, Debug)]
202pub(crate) struct InternalKeyboardModifierState {
203 left_alt: bool,
204 right_alt: bool,
205 altgr: bool,
206 left_control: bool,
207 right_control: bool,
208 left_meta: bool,
209 right_meta: bool,
210 left_shift: bool,
211 right_shift: bool,
212}
213
214impl InternalKeyboardModifierState {
215 pub(crate) fn state_update(mut self, pressed: bool, text: &SharedString) -> Option<Self> {
218 if let Some(key_code) = text.chars().next() {
219 match key_code {
220 key_codes::Alt => self.left_alt = pressed,
221 key_codes::AltGr => self.altgr = pressed,
222 key_codes::Control => self.left_control = pressed,
223 key_codes::ControlR => self.right_control = pressed,
224 key_codes::Shift => self.left_shift = pressed,
225 key_codes::ShiftR => self.right_shift = pressed,
226 key_codes::Meta => self.left_meta = pressed,
227 key_codes::MetaR => self.right_meta = pressed,
228 _ => return None,
229 };
230
231 debug_assert_eq!(key_code.len_utf8(), text.len());
235 }
236
237 #[cfg(target_os = "windows")]
239 {
240 if self.altgr {
241 self.left_control = false;
243 self.right_control = false;
244 } else if self.control() && self.alt() {
245 self.left_control = false;
247 self.right_control = false;
248 self.left_alt = false;
249 self.right_alt = false;
250 }
251 }
252
253 Some(self)
254 }
255
256 pub fn shift(&self) -> bool {
257 self.right_shift || self.left_shift
258 }
259 pub fn alt(&self) -> bool {
260 self.right_alt || self.left_alt
261 }
262 pub fn meta(&self) -> bool {
263 self.right_meta || self.left_meta
264 }
265 pub fn control(&self) -> bool {
266 self.right_control || self.left_control
267 }
268}
269
270impl From<InternalKeyboardModifierState> for KeyboardModifiers {
271 fn from(internal_state: InternalKeyboardModifierState) -> Self {
272 Self {
273 alt: internal_state.alt(),
274 control: internal_state.control(),
275 meta: internal_state.meta(),
276 shift: internal_state.shift(),
277 }
278 }
279}
280
281#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
283#[repr(u8)]
284pub enum KeyEventType {
285 #[default]
287 KeyPressed = 0,
288 KeyReleased = 1,
290 UpdateComposition = 2,
293 CommitComposition = 3,
295}
296
297impl KeyEvent {
298 pub fn shortcut(&self) -> Option<StandardShortcut> {
301 if self.modifiers.control && !self.modifiers.shift {
302 match self.text.as_str() {
303 #[cfg(not(target_arch = "wasm32"))]
304 "c" => Some(StandardShortcut::Copy),
305 #[cfg(not(target_arch = "wasm32"))]
306 "x" => Some(StandardShortcut::Cut),
307 #[cfg(not(target_arch = "wasm32"))]
308 "v" => Some(StandardShortcut::Paste),
309 "a" => Some(StandardShortcut::SelectAll),
310 "f" => Some(StandardShortcut::Find),
311 "s" => Some(StandardShortcut::Save),
312 "p" => Some(StandardShortcut::Print),
313 "z" => Some(StandardShortcut::Undo),
314 #[cfg(target_os = "windows")]
315 "y" => Some(StandardShortcut::Redo),
316 "r" => Some(StandardShortcut::Refresh),
317 _ => None,
318 }
319 } else if self.modifiers.control && self.modifiers.shift {
320 match self.text.as_str() {
321 #[cfg(not(target_os = "windows"))]
322 "z" => Some(StandardShortcut::Redo),
323 _ => None,
324 }
325 } else {
326 None
327 }
328 }
329
330 pub fn text_shortcut(&self) -> Option<TextShortcut> {
333 let keycode = self.text.chars().next()?;
334
335 let move_mod = if cfg!(target_os = "macos") {
336 self.modifiers.alt && !self.modifiers.control && !self.modifiers.meta
337 } else {
338 self.modifiers.control && !self.modifiers.alt && !self.modifiers.meta
339 };
340
341 if move_mod {
342 match keycode {
343 key_codes::LeftArrow => {
344 return Some(TextShortcut::Move(TextCursorDirection::BackwardByWord))
345 }
346 key_codes::RightArrow => {
347 return Some(TextShortcut::Move(TextCursorDirection::ForwardByWord))
348 }
349 key_codes::UpArrow => {
350 return Some(TextShortcut::Move(TextCursorDirection::StartOfParagraph))
351 }
352 key_codes::DownArrow => {
353 return Some(TextShortcut::Move(TextCursorDirection::EndOfParagraph))
354 }
355 key_codes::Backspace => {
356 return Some(TextShortcut::DeleteWordBackward);
357 }
358 key_codes::Delete => {
359 return Some(TextShortcut::DeleteWordForward);
360 }
361 _ => (),
362 };
363 }
364
365 #[cfg(not(target_os = "macos"))]
366 {
367 if self.modifiers.control && !self.modifiers.alt && !self.modifiers.meta {
368 match keycode {
369 key_codes::Home => {
370 return Some(TextShortcut::Move(TextCursorDirection::StartOfText))
371 }
372 key_codes::End => {
373 return Some(TextShortcut::Move(TextCursorDirection::EndOfText))
374 }
375 _ => (),
376 };
377 }
378 }
379
380 #[cfg(target_os = "macos")]
381 {
382 if self.modifiers.control {
383 match keycode {
384 key_codes::LeftArrow => {
385 return Some(TextShortcut::Move(TextCursorDirection::StartOfLine))
386 }
387 key_codes::RightArrow => {
388 return Some(TextShortcut::Move(TextCursorDirection::EndOfLine))
389 }
390 key_codes::UpArrow => {
391 return Some(TextShortcut::Move(TextCursorDirection::StartOfText))
392 }
393 key_codes::DownArrow => {
394 return Some(TextShortcut::Move(TextCursorDirection::EndOfText))
395 }
396 _ => (),
397 };
398 }
399 }
400
401 if let Ok(direction) = TextCursorDirection::try_from(keycode) {
402 Some(TextShortcut::Move(direction))
403 } else {
404 match keycode {
405 key_codes::Backspace => Some(TextShortcut::DeleteBackward),
406 key_codes::Delete => Some(TextShortcut::DeleteForward),
407 _ => None,
408 }
409 }
410 }
411}
412
413pub enum StandardShortcut {
415 Copy,
417 Cut,
419 Paste,
421 SelectAll,
423 Find,
425 Save,
427 Print,
429 Undo,
431 Redo,
433 Refresh,
435}
436
437pub enum TextShortcut {
439 Move(TextCursorDirection),
441 DeleteForward,
443 DeleteBackward,
445 DeleteWordForward,
447 DeleteWordBackward,
449}
450
451#[repr(u8)]
454#[derive(Debug, Clone, Copy, PartialEq, Default)]
455pub enum KeyEventResult {
456 EventAccepted,
458 #[default]
460 EventIgnored,
461}
462
463#[repr(u8)]
466#[derive(Debug, Clone, Copy, PartialEq, Default)]
467pub enum FocusEventResult {
468 FocusAccepted,
470 #[default]
472 FocusIgnored,
473}
474
475#[derive(Debug, Clone, Copy, PartialEq)]
478#[repr(u8)]
479pub enum FocusEvent {
480 FocusIn(FocusReason),
482 FocusOut(FocusReason),
484}
485
486#[derive(Default)]
488pub struct ClickState {
489 click_count_time_stamp: Cell<Option<crate::animations::Instant>>,
490 click_count: Cell<u8>,
491 click_position: Cell<LogicalPoint>,
492 click_button: Cell<PointerEventButton>,
493}
494
495impl ClickState {
496 fn restart(&self, position: LogicalPoint, button: PointerEventButton) {
498 self.click_count.set(0);
499 self.click_count_time_stamp.set(Some(crate::animations::Instant::now()));
500 self.click_position.set(position);
501 self.click_button.set(button);
502 }
503
504 pub fn reset(&self) {
506 self.click_count.set(0);
507 self.click_count_time_stamp.replace(None);
508 }
509
510 pub fn check_repeat(&self, mouse_event: MouseEvent, click_interval: Duration) -> MouseEvent {
512 match mouse_event {
513 MouseEvent::Pressed { position, button, .. } => {
514 let instant_now = crate::animations::Instant::now();
515
516 if let Some(click_count_time_stamp) = self.click_count_time_stamp.get() {
517 if instant_now - click_count_time_stamp < click_interval
518 && button == self.click_button.get()
519 && (position - self.click_position.get()).square_length() < 100 as _
520 {
521 self.click_count.set(self.click_count.get().wrapping_add(1));
522 self.click_count_time_stamp.set(Some(instant_now));
523 } else {
524 self.restart(position, button);
525 }
526 } else {
527 self.restart(position, button);
528 }
529
530 return MouseEvent::Pressed {
531 position,
532 button,
533 click_count: self.click_count.get(),
534 };
535 }
536 MouseEvent::Released { position, button, .. } => {
537 return MouseEvent::Released {
538 position,
539 button,
540 click_count: self.click_count.get(),
541 }
542 }
543 _ => {}
544 };
545
546 mouse_event
547 }
548}
549
550#[derive(Default)]
552pub struct MouseInputState {
553 item_stack: Vec<(ItemWeak, InputEventFilterResult)>,
556 pub(crate) offset: LogicalPoint,
558 grabbed: bool,
560 pub(crate) drag_data: Option<DropEvent>,
563 delayed: Option<(crate::timers::Timer, MouseEvent)>,
564 delayed_exit_items: Vec<ItemWeak>,
565}
566
567impl MouseInputState {
568 fn top_item(&self) -> Option<ItemRc> {
570 self.item_stack.last().and_then(|x| x.0.upgrade())
571 }
572
573 pub fn top_item_including_delayed(&self) -> Option<ItemRc> {
575 self.delayed_exit_items.last().and_then(|x| x.upgrade()).or_else(|| self.top_item())
576 }
577}
578
579pub(crate) fn handle_mouse_grab(
582 mouse_event: &MouseEvent,
583 window_adapter: &Rc<dyn WindowAdapter>,
584 mouse_input_state: &mut MouseInputState,
585) -> Option<MouseEvent> {
586 if !mouse_input_state.grabbed || mouse_input_state.item_stack.is_empty() {
587 return Some(mouse_event.clone());
588 };
589
590 let mut event = mouse_event.clone();
591 let mut intercept = false;
592 let mut invalid = false;
593
594 event.translate(-mouse_input_state.offset.to_vector());
595
596 mouse_input_state.item_stack.retain(|it| {
597 if invalid {
598 return false;
599 }
600 let item = if let Some(item) = it.0.upgrade() {
601 item
602 } else {
603 invalid = true;
604 return false;
605 };
606 if intercept {
607 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
608 return false;
609 }
610 let g = item.geometry();
611 event.translate(-g.origin.to_vector());
612
613 let interested = matches!(
614 it.1,
615 InputEventFilterResult::ForwardAndInterceptGrab
616 | InputEventFilterResult::DelayForwarding(_)
617 );
618
619 if interested
620 && item.borrow().as_ref().input_event_filter_before_children(
621 &event,
622 window_adapter,
623 &item,
624 ) == InputEventFilterResult::Intercept
625 {
626 intercept = true;
627 }
628 true
629 });
630 if invalid {
631 return Some(mouse_event.clone());
632 }
633
634 let grabber = mouse_input_state.top_item().unwrap();
635 let input_result = grabber.borrow().as_ref().input_event(&event, window_adapter, &grabber);
636 match input_result {
637 InputEventResult::GrabMouse => None,
638 InputEventResult::StartDrag => {
639 mouse_input_state.grabbed = false;
640 let drag_area_item = grabber.downcast::<crate::items::DragArea>().unwrap();
641 mouse_input_state.drag_data = Some(DropEvent {
642 mime_type: drag_area_item.as_pin_ref().mime_type(),
643 data: drag_area_item.as_pin_ref().data(),
644 position: Default::default(),
645 });
646 None
647 }
648 _ => {
649 mouse_input_state.grabbed = false;
650 Some(
652 mouse_event
653 .position()
654 .map_or(MouseEvent::Exit, |position| MouseEvent::Moved { position }),
655 )
656 }
657 }
658}
659
660pub(crate) fn send_exit_events(
661 old_input_state: &MouseInputState,
662 new_input_state: &mut MouseInputState,
663 mut pos: Option<LogicalPoint>,
664 window_adapter: &Rc<dyn WindowAdapter>,
665) {
666 for it in core::mem::take(&mut new_input_state.delayed_exit_items) {
667 let Some(item) = it.upgrade() else { continue };
668 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
669 }
670
671 let mut clipped = false;
672 for (idx, it) in old_input_state.item_stack.iter().enumerate() {
673 let Some(item) = it.0.upgrade() else { break };
674 let g = item.geometry();
675 let contains = pos.is_some_and(|p| g.contains(p));
676 if let Some(p) = pos.as_mut() {
677 *p -= g.origin.to_vector();
678 }
679 if !contains || clipped {
680 if item.borrow().as_ref().clips_children() {
681 clipped = true;
682 }
683 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
684 } else if new_input_state.item_stack.get(idx).map_or(true, |(x, _)| *x != it.0) {
685 if new_input_state.delayed.is_some() {
687 new_input_state.delayed_exit_items.push(it.0.clone());
688 } else {
689 item.borrow().as_ref().input_event(&MouseEvent::Exit, window_adapter, &item);
690 }
691 }
692 }
693}
694
695pub fn process_mouse_input(
699 root: ItemRc,
700 mouse_event: &MouseEvent,
701 window_adapter: &Rc<dyn WindowAdapter>,
702 mouse_input_state: MouseInputState,
703) -> MouseInputState {
704 let mut result = MouseInputState::default();
705 result.drag_data = mouse_input_state.drag_data.clone();
706 let r = send_mouse_event_to_item(
707 mouse_event,
708 root.clone(),
709 window_adapter,
710 &mut result,
711 mouse_input_state.top_item().as_ref(),
712 false,
713 );
714 if mouse_input_state.delayed.is_some()
715 && (!r.has_aborted()
716 || Option::zip(result.item_stack.last(), mouse_input_state.item_stack.last())
717 .map_or(true, |(a, b)| a.0 != b.0))
718 {
719 return mouse_input_state;
721 }
722 send_exit_events(&mouse_input_state, &mut result, mouse_event.position(), window_adapter);
723
724 if let MouseEvent::Wheel { position, .. } = mouse_event {
725 if r.has_aborted() {
726 return process_mouse_input(
728 root,
729 &MouseEvent::Moved { position: *position },
730 window_adapter,
731 result,
732 );
733 }
734 }
735
736 result
737}
738
739pub(crate) fn process_delayed_event(
740 window_adapter: &Rc<dyn WindowAdapter>,
741 mut mouse_input_state: MouseInputState,
742) -> MouseInputState {
743 let event = match mouse_input_state.delayed.take() {
745 Some(e) => e.1,
746 None => return mouse_input_state,
747 };
748
749 let top_item = match mouse_input_state.top_item() {
750 Some(i) => i,
751 None => return MouseInputState::default(),
752 };
753
754 let mut actual_visitor =
755 |component: &ItemTreeRc, index: u32, _: Pin<ItemRef>| -> VisitChildrenResult {
756 send_mouse_event_to_item(
757 &event,
758 ItemRc::new(component.clone(), index),
759 window_adapter,
760 &mut mouse_input_state,
761 Some(&top_item),
762 true,
763 )
764 };
765 vtable::new_vref!(let mut actual_visitor : VRefMut<crate::item_tree::ItemVisitorVTable> for crate::item_tree::ItemVisitor = &mut actual_visitor);
766 vtable::VRc::borrow_pin(top_item.item_tree()).as_ref().visit_children_item(
767 top_item.index() as isize,
768 crate::item_tree::TraversalOrder::FrontToBack,
769 actual_visitor,
770 );
771 mouse_input_state
772}
773
774fn send_mouse_event_to_item(
775 mouse_event: &MouseEvent,
776 item_rc: ItemRc,
777 window_adapter: &Rc<dyn WindowAdapter>,
778 result: &mut MouseInputState,
779 last_top_item: Option<&ItemRc>,
780 ignore_delays: bool,
781) -> VisitChildrenResult {
782 let item = item_rc.borrow();
783 let geom = item_rc.geometry();
784 let mut event_for_children = mouse_event.clone();
786 event_for_children.translate(-geom.origin.to_vector());
787
788 let filter_result = if mouse_event.position().is_some_and(|p| geom.contains(p))
789 || item.as_ref().clips_children()
790 {
791 item.as_ref().input_event_filter_before_children(
792 &event_for_children,
793 window_adapter,
794 &item_rc,
795 )
796 } else {
797 InputEventFilterResult::ForwardAndIgnore
798 };
799
800 let (forward_to_children, ignore) = match filter_result {
801 InputEventFilterResult::ForwardEvent => (true, false),
802 InputEventFilterResult::ForwardAndIgnore => (true, true),
803 InputEventFilterResult::ForwardAndInterceptGrab => (true, false),
804 InputEventFilterResult::Intercept => (false, false),
805 InputEventFilterResult::DelayForwarding(_) if ignore_delays => (true, false),
806 InputEventFilterResult::DelayForwarding(duration) => {
807 let timer = Timer::default();
808 let w = Rc::downgrade(window_adapter);
809 timer.start(
810 crate::timers::TimerMode::SingleShot,
811 Duration::from_millis(duration),
812 move || {
813 if let Some(w) = w.upgrade() {
814 WindowInner::from_pub(w.window()).process_delayed_event();
815 }
816 },
817 );
818 result.delayed = Some((timer, event_for_children));
819 result
820 .item_stack
821 .push((item_rc.downgrade(), InputEventFilterResult::DelayForwarding(duration)));
822 return VisitChildrenResult::abort(item_rc.index(), 0);
823 }
824 };
825
826 result.item_stack.push((item_rc.downgrade(), filter_result));
827 if forward_to_children {
828 let mut actual_visitor =
829 |component: &ItemTreeRc, index: u32, _: Pin<ItemRef>| -> VisitChildrenResult {
830 send_mouse_event_to_item(
831 &event_for_children,
832 ItemRc::new(component.clone(), index),
833 window_adapter,
834 result,
835 last_top_item,
836 ignore_delays,
837 )
838 };
839 vtable::new_vref!(let mut actual_visitor : VRefMut<crate::item_tree::ItemVisitorVTable> for crate::item_tree::ItemVisitor = &mut actual_visitor);
840 let r = vtable::VRc::borrow_pin(item_rc.item_tree()).as_ref().visit_children_item(
841 item_rc.index() as isize,
842 crate::item_tree::TraversalOrder::FrontToBack,
843 actual_visitor,
844 );
845 if r.has_aborted() {
846 return r;
847 }
848 };
849
850 let r = if ignore {
851 InputEventResult::EventIgnored
852 } else {
853 let mut event = mouse_event.clone();
854 event.translate(-geom.origin.to_vector());
855 if last_top_item.map_or(true, |x| *x != item_rc) {
856 event.set_click_count(0);
857 }
858 item.as_ref().input_event(&event, window_adapter, &item_rc)
859 };
860 match r {
861 InputEventResult::EventAccepted => VisitChildrenResult::abort(item_rc.index(), 0),
862 InputEventResult::EventIgnored => {
863 let _pop = result.item_stack.pop();
864 debug_assert_eq!(
865 _pop.map(|x| (x.0.upgrade().unwrap().index(), x.1)).unwrap(),
866 (item_rc.index(), filter_result)
867 );
868 VisitChildrenResult::CONTINUE
869 }
870 InputEventResult::GrabMouse => {
871 result.item_stack.last_mut().unwrap().1 =
872 InputEventFilterResult::ForwardAndInterceptGrab;
873 result.grabbed = true;
874 VisitChildrenResult::abort(item_rc.index(), 0)
875 }
876 InputEventResult::StartDrag => {
877 result.item_stack.last_mut().unwrap().1 =
878 InputEventFilterResult::ForwardAndInterceptGrab;
879 result.grabbed = false;
880 let drag_area_item = item_rc.downcast::<crate::items::DragArea>().unwrap();
881 result.drag_data = Some(DropEvent {
882 mime_type: drag_area_item.as_pin_ref().mime_type(),
883 data: drag_area_item.as_pin_ref().data(),
884 position: Default::default(),
885 });
886 VisitChildrenResult::abort(item_rc.index(), 0)
887 }
888 }
889}
890
891#[derive(FieldOffsets)]
898#[repr(C)]
899#[pin]
900pub(crate) struct TextCursorBlinker {
901 cursor_visible: Property<bool>,
902 cursor_blink_timer: crate::timers::Timer,
903}
904
905impl TextCursorBlinker {
906 pub fn new() -> Pin<Rc<Self>> {
909 Rc::pin(Self {
910 cursor_visible: Property::new(true),
911 cursor_blink_timer: Default::default(),
912 })
913 }
914
915 pub fn set_binding(
918 instance: Pin<Rc<TextCursorBlinker>>,
919 prop: &Property<bool>,
920 cycle_duration: Duration,
921 ) {
922 instance.as_ref().cursor_visible.set(true);
923 Self::start(&instance, cycle_duration);
925 prop.set_binding(move || {
926 TextCursorBlinker::FIELD_OFFSETS.cursor_visible.apply_pin(instance.as_ref()).get()
927 });
928 }
929
930 pub fn start(self: &Pin<Rc<Self>>, cycle_duration: Duration) {
933 if self.cursor_blink_timer.running() {
934 self.cursor_blink_timer.restart();
935 } else {
936 let toggle_cursor = {
937 let weak_blinker = pin_weak::rc::PinWeak::downgrade(self.clone());
938 move || {
939 if let Some(blinker) = weak_blinker.upgrade() {
940 let visible = TextCursorBlinker::FIELD_OFFSETS
941 .cursor_visible
942 .apply_pin(blinker.as_ref())
943 .get();
944 blinker.cursor_visible.set(!visible);
945 }
946 }
947 };
948 if !cycle_duration.is_zero() {
949 self.cursor_blink_timer.start(
950 crate::timers::TimerMode::Repeated,
951 cycle_duration / 2,
952 toggle_cursor,
953 );
954 }
955 }
956 }
957
958 pub fn stop(&self) {
961 self.cursor_blink_timer.stop()
962 }
963}