1use super::{
5 EventResult, FocusReasonArg, Item, ItemConsts, ItemRc, ItemRendererRef, KeyEventArg,
6 MouseCursor, PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind,
7 PointerScrollEvent, PointerScrollEventArg, RenderingResult, VoidArg,
8};
9use crate::api::LogicalPosition;
10use crate::input::{
11 FocusEvent, FocusEventResult, FocusReason, InputEventFilterResult, InputEventResult,
12 InternalKeyEvent, KeyEventResult, KeyEventType, Keys, MouseEvent,
13};
14use crate::item_rendering::CachedRenderingData;
15use crate::items::ItemTreeVTable;
16use crate::layout::{LayoutInfo, Orientation};
17use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PointLengths};
18use crate::properties::PropertyTracker;
19#[cfg(feature = "rtti")]
20use crate::rtti::*;
21use crate::window::{WindowAdapter, WindowInner};
22use crate::{Callback, Coord, Property};
23use alloc::{boxed::Box, rc::Rc, vec::Vec};
24use const_field_offset::FieldOffsets;
25use core::cell::Cell;
26use core::pin::Pin;
27use i_slint_core_macros::*;
28use vtable::{VRcMapped, VWeakMapped};
29
30#[repr(C)]
32#[derive(FieldOffsets, SlintElement, Default)]
33#[pin]
34pub struct TouchArea {
35 pub enabled: Property<bool>,
36 pub pressed: Property<bool>,
38 pub has_hover: Property<bool>,
39 pub pressed_x: Property<LogicalLength>,
43 pub pressed_y: Property<LogicalLength>,
44 pub mouse_x: Property<LogicalLength>,
46 pub mouse_y: Property<LogicalLength>,
47 pub mouse_cursor: Property<MouseCursor>,
48 pub clicked: Callback<VoidArg>,
49 pub double_clicked: Callback<VoidArg>,
50 pub moved: Callback<VoidArg>,
51 pub pointer_event: Callback<PointerEventArg>,
52 pub scroll_event: Callback<PointerScrollEventArg, EventResult>,
53 pub cached_rendering_data: CachedRenderingData,
55 grabbed: Cell<bool>,
57}
58
59impl Item for TouchArea {
60 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
61
62 fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
63
64 fn layout_info(
65 self: Pin<&Self>,
66 _orientation: Orientation,
67 _cross_axis_constraint: Coord,
68 _window_adapter: &Rc<dyn WindowAdapter>,
69 _self_rc: &ItemRc,
70 ) -> LayoutInfo {
71 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
72 }
73
74 fn input_event_filter_before_children(
75 self: Pin<&Self>,
76 event: &MouseEvent,
77 window_adapter: &Rc<dyn WindowAdapter>,
78 _self_rc: &ItemRc,
79 cursor: &mut MouseCursor,
80 ) -> InputEventFilterResult {
81 if !self.enabled() {
82 self.has_hover.set(false);
83 if self.grabbed.replace(false) {
84 self.pressed.set(false);
85 Self::FIELD_OFFSETS.pointer_event().apply_pin(self).call(&(PointerEvent {
86 button: PointerEventButton::Other,
87 kind: PointerEventKind::Cancel,
88 modifiers: window_adapter.window().0.context().0.modifiers.get().into(),
89 is_touch: false,
90 },));
91 }
92 return InputEventFilterResult::ForwardAndIgnore;
93 }
94 if matches!(event, MouseEvent::DragMove(..) | MouseEvent::Drop(..)) {
95 return InputEventFilterResult::ForwardAndIgnore;
97 }
98 if let Some(pos) = event.position() {
99 Self::FIELD_OFFSETS.mouse_x().apply_pin(self).set(pos.x_length());
100 Self::FIELD_OFFSETS.mouse_y().apply_pin(self).set(pos.y_length());
101 }
102 let hovering = !matches!(event, MouseEvent::Exit);
103 Self::FIELD_OFFSETS.has_hover().apply_pin(self).set(hovering);
104 if hovering {
105 *cursor = self.mouse_cursor();
106 }
107 InputEventFilterResult::ForwardAndInterceptGrab
108 }
109
110 fn input_event(
111 self: Pin<&Self>,
112 event: &MouseEvent,
113 window_adapter: &Rc<dyn WindowAdapter>,
114 self_rc: &ItemRc,
115 _: &mut MouseCursor,
116 ) -> InputEventResult {
117 if matches!(event, MouseEvent::Exit) {
118 Self::FIELD_OFFSETS.has_hover().apply_pin(self).set(false);
119 }
120 if !self.enabled() {
121 return InputEventResult::EventIgnored;
122 }
123 match event {
124 MouseEvent::Pressed { position, button, is_touch, .. } => {
125 self.grabbed.set(true);
126 if *button == PointerEventButton::Left {
127 Self::FIELD_OFFSETS.pressed_x().apply_pin(self).set(position.x_length());
128 Self::FIELD_OFFSETS.pressed_y().apply_pin(self).set(position.y_length());
129 Self::FIELD_OFFSETS.pressed().apply_pin(self).set(true);
130 }
131 Self::FIELD_OFFSETS.pointer_event().apply_pin(self).call(&(PointerEvent {
132 button: *button,
133 kind: PointerEventKind::Down,
134 modifiers: window_adapter.window().0.context().0.modifiers.get().into(),
135 is_touch: *is_touch,
136 },));
137
138 InputEventResult::GrabMouse
139 }
140 MouseEvent::Exit => {
141 Self::FIELD_OFFSETS.pressed().apply_pin(self).set(false);
142 if self.grabbed.replace(false) {
143 Self::FIELD_OFFSETS.pointer_event().apply_pin(self).call(&(PointerEvent {
144 button: PointerEventButton::Other,
145 kind: PointerEventKind::Cancel,
146 modifiers: window_adapter.window().0.context().0.modifiers.get().into(),
147 is_touch: false,
148 },));
149 }
150
151 InputEventResult::EventAccepted
152 }
153
154 MouseEvent::Released { button, position, click_count, is_touch } => {
155 let geometry = self_rc.geometry();
156 if *button == PointerEventButton::Left
157 && LogicalRect::new(LogicalPoint::default(), geometry.size).contains(*position)
158 && self.pressed()
159 {
160 Self::FIELD_OFFSETS.clicked().apply_pin(self).call(&());
161 if (click_count % 2) == 1 {
162 Self::FIELD_OFFSETS.double_clicked().apply_pin(self).call(&())
163 }
164 }
165
166 self.grabbed.set(false);
167 if *button == PointerEventButton::Left {
168 Self::FIELD_OFFSETS.pressed().apply_pin(self).set(false);
169 }
170 Self::FIELD_OFFSETS.pointer_event().apply_pin(self).call(&(PointerEvent {
171 button: *button,
172 kind: PointerEventKind::Up,
173 modifiers: window_adapter.window().0.context().0.modifiers.get().into(),
174 is_touch: *is_touch,
175 },));
176
177 InputEventResult::EventAccepted
178 }
179 MouseEvent::Moved { is_touch, .. } => {
180 Self::FIELD_OFFSETS.pointer_event().apply_pin(self).call(&(PointerEvent {
181 button: PointerEventButton::Other,
182 kind: PointerEventKind::Move,
183 modifiers: window_adapter.window().0.context().0.modifiers.get().into(),
184 is_touch: *is_touch,
185 },));
186 if self.grabbed.get() {
187 Self::FIELD_OFFSETS.moved().apply_pin(self).call(&());
188 InputEventResult::GrabMouse
189 } else {
190 InputEventResult::EventAccepted
191 }
192 }
193 MouseEvent::Wheel { delta_x, delta_y, .. } => {
194 let modifiers = window_adapter.window().0.context().0.modifiers.get().into();
195 let r = Self::FIELD_OFFSETS.scroll_event().apply_pin(self).call(&(
196 PointerScrollEvent { delta_x: *delta_x, delta_y: *delta_y, modifiers },
197 ));
198 if self.grabbed.get() {
199 InputEventResult::GrabMouse
200 } else {
201 match r {
202 EventResult::Reject => {
203 Self::FIELD_OFFSETS.has_hover().apply_pin(self).set(false);
207 InputEventResult::EventIgnored
208 }
209 EventResult::Accept => InputEventResult::EventAccepted,
210 }
211 }
212 }
213 MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. } => {
214 InputEventResult::EventIgnored
215 }
216 MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
217 }
218 }
219
220 fn capture_key_event(
221 self: Pin<&Self>,
222 _: &InternalKeyEvent,
223 _window_adapter: &Rc<dyn WindowAdapter>,
224 _self_rc: &ItemRc,
225 ) -> KeyEventResult {
226 KeyEventResult::EventIgnored
227 }
228
229 fn key_event(
230 self: Pin<&Self>,
231 _: &InternalKeyEvent,
232 _window_adapter: &Rc<dyn WindowAdapter>,
233 _self_rc: &ItemRc,
234 ) -> KeyEventResult {
235 KeyEventResult::EventIgnored
236 }
237
238 fn focus_event(
239 self: Pin<&Self>,
240 _: &FocusEvent,
241 _window_adapter: &Rc<dyn WindowAdapter>,
242 _self_rc: &ItemRc,
243 ) -> FocusEventResult {
244 FocusEventResult::FocusIgnored
245 }
246
247 fn render(
248 self: Pin<&Self>,
249 _backend: &mut ItemRendererRef,
250 _self_rc: &ItemRc,
251 _size: LogicalSize,
252 ) -> RenderingResult {
253 RenderingResult::ContinueRenderingChildren
254 }
255
256 fn bounding_rect(
257 self: core::pin::Pin<&Self>,
258 _window_adapter: &Rc<dyn WindowAdapter>,
259 _self_rc: &ItemRc,
260 mut geometry: LogicalRect,
261 ) -> LogicalRect {
262 geometry.size = LogicalSize::zero();
263 geometry
264 }
265
266 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
267 false
268 }
269}
270
271impl ItemConsts for TouchArea {
272 const cached_rendering_data_offset: const_field_offset::FieldOffset<
273 TouchArea,
274 CachedRenderingData,
275 > = TouchArea::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
276}
277
278impl ItemConsts for KeyBinding {
279 const cached_rendering_data_offset: const_field_offset::FieldOffset<
280 KeyBinding,
281 CachedRenderingData,
282 > = KeyBinding::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
283}
284
285#[repr(C)]
286#[derive(FieldOffsets, Default, SlintElement)]
287#[pin]
288pub struct KeyBinding {
289 pub keys: Property<Keys>,
290 pub enabled: Property<bool>,
291 pub activated: Callback<VoidArg>,
292 pub cached_rendering_data: CachedRenderingData,
293}
294
295impl Item for KeyBinding {
296 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
297
298 fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
299
300 fn layout_info(
301 self: Pin<&Self>,
302 _orientation: crate::items::Orientation,
303 _cross_axis_constraint: Coord,
304 _window_adapter: &Rc<dyn WindowAdapter>,
305 _self_rc: &ItemRc,
306 ) -> crate::layout::LayoutInfo {
307 Default::default()
308 }
309
310 fn input_event_filter_before_children(
311 self: Pin<&Self>,
312 _: &crate::input::MouseEvent,
313 _window_adapter: &Rc<dyn WindowAdapter>,
314 _self_rc: &ItemRc,
315 _: &mut crate::items::MouseCursor,
316 ) -> crate::input::InputEventFilterResult {
317 Default::default()
318 }
319
320 fn input_event(
321 self: Pin<&Self>,
322 _: &crate::input::MouseEvent,
323 _window_adapter: &Rc<dyn WindowAdapter>,
324 _self_rc: &ItemRc,
325 _: &mut crate::items::MouseCursor,
326 ) -> crate::input::InputEventResult {
327 Default::default()
328 }
329
330 fn capture_key_event(
331 self: Pin<&Self>,
332 _: &InternalKeyEvent,
333 _window_adapter: &Rc<dyn WindowAdapter>,
334 _self_rc: &ItemRc,
335 ) -> crate::input::KeyEventResult {
336 crate::input::KeyEventResult::EventIgnored
337 }
338
339 fn key_event(
340 self: Pin<&Self>,
341 _: &InternalKeyEvent,
342 _window_adapter: &Rc<dyn WindowAdapter>,
343 _self_rc: &ItemRc,
344 ) -> crate::input::KeyEventResult {
345 Default::default()
346 }
347
348 fn focus_event(
349 self: Pin<&Self>,
350 _: &crate::input::FocusEvent,
351 _window_adapter: &Rc<dyn WindowAdapter>,
352 _self_rc: &ItemRc,
353 ) -> crate::input::FocusEventResult {
354 Default::default()
355 }
356
357 fn render(
358 self: Pin<&Self>,
359 _backend: &mut &mut dyn crate::item_rendering::ItemRenderer,
360 _self_rc: &ItemRc,
361 _size: crate::lengths::LogicalSize,
362 ) -> crate::items::RenderingResult {
363 Default::default()
364 }
365
366 fn bounding_rect(
367 self: Pin<&Self>,
368 _window_adapter: &Rc<dyn WindowAdapter>,
369 _self_rc: &ItemRc,
370 geometry: crate::lengths::LogicalRect,
371 ) -> crate::lengths::LogicalRect {
372 geometry
373 }
374
375 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
376 false
377 }
378}
379
380#[repr(C)]
383#[derive(Default)] pub struct MaybeKeyBindingList(Cell<*const KeyBindingList>);
385
386impl MaybeKeyBindingList {
387 fn ensure_init(&self) {
388 if self.0.get().is_null() {
391 self.0.set(Box::leak(Box::default()));
392 }
393 }
394}
395
396impl Drop for MaybeKeyBindingList {
397 fn drop(&mut self) {
398 let ptr = self.0.replace(core::ptr::null());
399 if !ptr.is_null() {
400 drop(unsafe { Box::from_raw(ptr as *mut KeyBindingList) });
402 }
403 }
404}
405
406impl MaybeKeyBindingList {
407 fn deref_pin(self: Pin<&Self>) -> Pin<&KeyBindingList> {
408 self.ensure_init();
409 unsafe { Pin::new_unchecked(&*self.get_ref().0.get()) }
411 }
412}
413
414#[derive(Default)]
415#[pin_project::pin_project]
416pub struct KeyBindingList {
417 found: core::cell::RefCell<Vec<VWeakMapped<ItemTreeVTable, KeyBinding>>>,
418 #[pin]
419 property_tracker: PropertyTracker,
420}
421
422#[repr(C)]
424#[derive(FieldOffsets, Default, SlintElement)]
425#[pin]
426pub struct FocusScope {
427 pub enabled: Property<bool>,
428 pub has_focus: Property<bool>,
429 pub focus_on_click: Property<bool>,
430 pub focus_on_tab_navigation: Property<bool>,
431 pub key_pressed: Callback<KeyEventArg, EventResult>,
432 pub key_released: Callback<KeyEventArg, EventResult>,
433 pub capture_key_pressed: Callback<KeyEventArg, EventResult>,
434 pub capture_key_released: Callback<KeyEventArg, EventResult>,
435 pub focus_changed_event: Callback<FocusReasonArg>,
436 pub focus_gained: Callback<FocusReasonArg>,
437 pub focus_lost: Callback<FocusReasonArg>,
438 pub key_bindings: MaybeKeyBindingList,
439 pub cached_rendering_data: CachedRenderingData,
441}
442
443impl FocusScope {
444 fn visit_enabled_key_bindings<R>(
445 self: Pin<&Self>,
446 self_rc: &ItemRc,
447 mut fun: impl FnMut(&VRcMapped<ItemTreeVTable, KeyBinding>) -> Option<R>,
448 ) -> Option<R> {
449 let list = Self::FIELD_OFFSETS.key_bindings().apply_pin(self);
450 let list = list.deref_pin();
451
452 list.project_ref().property_tracker.evaluate_if_dirty(|| {
453 let mut found = list.found.borrow_mut();
454 found.clear();
455
456 let mut next = self_rc.first_child();
457 while let Some(child) = next {
458 if let Some(key_binding) = ItemRc::downcast::<KeyBinding>(&child)
459 && key_binding.as_pin_ref().enabled()
460 {
461 found.push(VRcMapped::downgrade(&key_binding));
462 }
463 next = child.next_sibling();
464 }
465 });
466
467 let list = list.found.borrow();
468 for key_binding in &*list {
469 let Some(shortcut) = key_binding.upgrade() else {
470 crate::debug_log!("Warning: Found a dropped KeyBinding!");
471 continue;
472 };
473 if let Some(result) = fun(&shortcut) {
474 return Some(result);
475 }
476 }
477
478 None
479 }
480
481 fn key_binding_for_event(
483 self: Pin<&Self>,
484 self_rc: &ItemRc,
485 inter_key_event: &InternalKeyEvent,
486 ) -> Option<(VRcMapped<ItemTreeVTable, KeyBinding>, bool)> {
487 let mut first_match = None;
488
489 let ambiguous = self.visit_enabled_key_bindings(self_rc, |key_binding| {
490 let keys = key_binding.as_pin_ref().keys();
491 if keys.matches(&inter_key_event.key_event) {
492 match &first_match {
493 Some(key_binding) => {
494 return Some(VRcMapped::clone(key_binding));
495 }
496 None => {
497 first_match = Some(VRcMapped::clone(key_binding));
498 }
499 };
500 }
501 None
502 });
503
504 first_match.map(|binding| (VRcMapped::clone(&binding), ambiguous.is_some()))
505 }
506}
507
508impl Item for FocusScope {
509 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
510
511 fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
512
513 fn layout_info(
514 self: Pin<&Self>,
515 _orientation: Orientation,
516 _cross_axis_constraint: Coord,
517 _window_adapter: &Rc<dyn WindowAdapter>,
518 _self_rc: &ItemRc,
519 ) -> LayoutInfo {
520 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
521 }
522
523 fn input_event_filter_before_children(
524 self: Pin<&Self>,
525 _: &MouseEvent,
526 _window_adapter: &Rc<dyn WindowAdapter>,
527 _self_rc: &ItemRc,
528 _: &mut MouseCursor,
529 ) -> InputEventFilterResult {
530 InputEventFilterResult::ForwardEvent
531 }
532
533 fn input_event(
534 self: Pin<&Self>,
535 event: &MouseEvent,
536 window_adapter: &Rc<dyn WindowAdapter>,
537 self_rc: &ItemRc,
538 _: &mut MouseCursor,
539 ) -> InputEventResult {
540 if self.enabled()
541 && self.focus_on_click()
542 && matches!(event, MouseEvent::Pressed { .. })
543 && !self.has_focus()
544 {
545 WindowInner::from_pub(window_adapter.window()).set_focus_item(
546 self_rc,
547 true,
548 FocusReason::PointerClick,
549 );
550 InputEventResult::EventAccepted
551 } else {
552 InputEventResult::EventIgnored
553 }
554 }
555
556 fn capture_key_event(
557 self: Pin<&Self>,
558 event: &InternalKeyEvent,
559 _window_adapter: &Rc<dyn WindowAdapter>,
560 _self_rc: &ItemRc,
561 ) -> KeyEventResult {
562 let r = match event.event_type {
563 KeyEventType::KeyPressed => Self::FIELD_OFFSETS
564 .capture_key_pressed()
565 .apply_pin(self)
566 .call(&(event.key_event.clone(),)),
567 KeyEventType::KeyReleased => Self::FIELD_OFFSETS
568 .capture_key_released()
569 .apply_pin(self)
570 .call(&(event.key_event.clone(),)),
571 KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
572 EventResult::Reject
573 }
574 };
575 match r {
576 EventResult::Accept => KeyEventResult::EventAccepted,
577 EventResult::Reject => KeyEventResult::EventIgnored,
578 }
579 }
580
581 fn key_event(
582 self: Pin<&Self>,
583 event: &InternalKeyEvent,
584 _window_adapter: &Rc<dyn WindowAdapter>,
585 self_rc: &ItemRc,
586 ) -> KeyEventResult {
587 let r = match event.event_type {
588 KeyEventType::KeyPressed => {
589 if let Some((key_binding, ambiguous)) = self.key_binding_for_event(self_rc, event) {
590 if ambiguous {
591 let keys = KeyBinding::FIELD_OFFSETS
592 .keys()
593 .apply_pin(key_binding.as_pin_ref())
594 .get();
595 crate::debug_log!(
596 "Warning: Multiple matching KeyBinding elements for keys {:?}!",
597 keys
598 );
599 }
600 KeyBinding::FIELD_OFFSETS
601 .activated()
602 .apply_pin(key_binding.as_pin_ref())
603 .call(&());
604 EventResult::Accept
605 } else {
606 Self::FIELD_OFFSETS
607 .key_pressed()
608 .apply_pin(self)
609 .call(&(event.key_event.clone(),))
610 }
611 }
612 KeyEventType::KeyReleased => {
613 let binding = self.key_binding_for_event(self_rc, event);
614 if binding.is_some() {
615 EventResult::Accept
620 } else {
621 Self::FIELD_OFFSETS
622 .key_released()
623 .apply_pin(self)
624 .call(&(event.key_event.clone(),))
625 }
626 }
627 KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
628 EventResult::Reject
629 }
630 };
631 match r {
632 EventResult::Accept => KeyEventResult::EventAccepted,
633 EventResult::Reject => KeyEventResult::EventIgnored,
634 }
635 }
636
637 fn focus_event(
638 self: Pin<&Self>,
639 event: &FocusEvent,
640 _window_adapter: &Rc<dyn WindowAdapter>,
641 _self_rc: &ItemRc,
642 ) -> FocusEventResult {
643 if !self.enabled() {
644 return FocusEventResult::FocusIgnored;
645 }
646
647 match event {
648 FocusEvent::FocusIn(reason) => {
649 match reason {
650 FocusReason::TabNavigation if !self.focus_on_tab_navigation() => {
651 return FocusEventResult::FocusIgnored;
652 }
653 FocusReason::PointerClick if !self.focus_on_click() => {
654 return FocusEventResult::FocusIgnored;
655 }
656 _ => (),
657 };
658
659 self.has_focus.set(true);
660 Self::FIELD_OFFSETS.focus_changed_event().apply_pin(self).call(&(*reason,));
661 Self::FIELD_OFFSETS.focus_gained().apply_pin(self).call(&(*reason,));
662 }
663 FocusEvent::FocusOut(reason) => {
664 self.has_focus.set(false);
665 Self::FIELD_OFFSETS.focus_changed_event().apply_pin(self).call(&(*reason,));
666 Self::FIELD_OFFSETS.focus_lost().apply_pin(self).call(&(*reason,));
667 }
668 }
669 FocusEventResult::FocusAccepted
670 }
671
672 fn render(
673 self: Pin<&Self>,
674 _backend: &mut ItemRendererRef,
675 _self_rc: &ItemRc,
676 _size: LogicalSize,
677 ) -> RenderingResult {
678 RenderingResult::ContinueRenderingChildren
679 }
680
681 fn bounding_rect(
682 self: core::pin::Pin<&Self>,
683 _window_adapter: &Rc<dyn WindowAdapter>,
684 _self_rc: &ItemRc,
685 mut geometry: LogicalRect,
686 ) -> LogicalRect {
687 geometry.size = LogicalSize::zero();
688 geometry
689 }
690
691 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
692 false
693 }
694}
695
696impl ItemConsts for FocusScope {
697 const cached_rendering_data_offset: const_field_offset::FieldOffset<
698 FocusScope,
699 CachedRenderingData,
700 > = FocusScope::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
701}
702
703#[repr(C)]
704#[derive(FieldOffsets, Default, SlintElement)]
705#[pin]
706pub struct SwipeGestureHandler {
707 pub enabled: Property<bool>,
708 pub handle_swipe_left: Property<bool>,
709 pub handle_swipe_right: Property<bool>,
710 pub handle_swipe_up: Property<bool>,
711 pub handle_swipe_down: Property<bool>,
712
713 pub moved: Callback<VoidArg>,
714 pub swiped: Callback<VoidArg>,
715 pub cancelled: Callback<VoidArg>,
716
717 pub pressed_position: Property<LogicalPosition>,
718 pub current_position: Property<LogicalPosition>,
719 pub swiping: Property<bool>,
720
721 pressed: Cell<bool>,
723 pub cached_rendering_data: CachedRenderingData,
726}
727
728impl Item for SwipeGestureHandler {
729 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
730
731 fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
732
733 fn layout_info(
734 self: Pin<&Self>,
735 _orientation: Orientation,
736 _cross_axis_constraint: Coord,
737 _window_adapter: &Rc<dyn WindowAdapter>,
738 _self_rc: &ItemRc,
739 ) -> LayoutInfo {
740 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
741 }
742
743 fn input_event_filter_before_children(
744 self: Pin<&Self>,
745 event: &MouseEvent,
746 _window_adapter: &Rc<dyn WindowAdapter>,
747 _self_rc: &ItemRc,
748 _: &mut MouseCursor,
749 ) -> InputEventFilterResult {
750 if !self.enabled() {
751 if self.pressed.get() {
752 self.cancel_impl();
753 }
754 return InputEventFilterResult::ForwardAndIgnore;
755 }
756
757 match event {
758 MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => {
759 Self::FIELD_OFFSETS
760 .pressed_position()
761 .apply_pin(self)
762 .set(crate::lengths::logical_position_to_api(*position));
763 self.pressed.set(true);
764 InputEventFilterResult::DelayForwarding(
765 super::flickable::FORWARD_DELAY.as_millis() as _
766 )
767 }
768 MouseEvent::Exit => {
769 self.cancel_impl();
770 InputEventFilterResult::ForwardAndIgnore
771 }
772 MouseEvent::Released { button: PointerEventButton::Left, .. } => {
773 if self.swiping() {
774 InputEventFilterResult::Intercept
775 } else {
776 self.pressed.set(false);
777 InputEventFilterResult::ForwardEvent
778 }
779 }
780 MouseEvent::Moved { position, .. } => {
781 if self.swiping() {
782 InputEventFilterResult::Intercept
783 } else if !self.pressed.get() {
784 InputEventFilterResult::ForwardEvent
785 } else if self.is_over_threshold(position) {
786 InputEventFilterResult::Intercept
787 } else {
788 InputEventFilterResult::ForwardAndInterceptGrab
789 }
790 }
791 MouseEvent::Wheel { .. } => InputEventFilterResult::ForwardAndIgnore,
792 MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
794 InputEventFilterResult::ForwardAndIgnore
795 }
796 MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. } => {
797 InputEventFilterResult::ForwardAndIgnore
798 }
799 MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
800 InputEventFilterResult::ForwardAndIgnore
801 }
802 }
803 }
804
805 fn input_event(
806 self: Pin<&Self>,
807 event: &MouseEvent,
808 _window_adapter: &Rc<dyn WindowAdapter>,
809 _self_rc: &ItemRc,
810 _: &mut MouseCursor,
811 ) -> InputEventResult {
812 match event {
813 MouseEvent::Pressed { .. } => InputEventResult::GrabMouse,
814 MouseEvent::Exit => {
815 self.cancel_impl();
816 InputEventResult::EventIgnored
817 }
818 MouseEvent::Released { position, .. } => {
819 if !self.pressed.get() && !self.swiping() {
820 return InputEventResult::EventIgnored;
821 }
822 self.current_position.set(crate::lengths::logical_position_to_api(*position));
823 self.pressed.set(false);
824 if self.swiping() {
825 Self::FIELD_OFFSETS.swiping().apply_pin(self).set(false);
826 Self::FIELD_OFFSETS.swiped().apply_pin(self).call(&());
827 InputEventResult::EventAccepted
828 } else {
829 InputEventResult::EventIgnored
830 }
831 }
832 MouseEvent::Moved { position, .. } => {
833 if !self.pressed.get() {
834 return InputEventResult::EventAccepted;
835 }
836 self.current_position.set(crate::lengths::logical_position_to_api(*position));
837 let mut swiping = self.swiping();
838 if !swiping && self.is_over_threshold(position) {
839 Self::FIELD_OFFSETS.swiping().apply_pin(self).set(true);
840 swiping = true;
841 }
842 Self::FIELD_OFFSETS.moved().apply_pin(self).call(&());
843 if swiping { InputEventResult::GrabMouse } else { InputEventResult::EventAccepted }
844 }
845 MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
846 MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. } => {
847 InputEventResult::EventIgnored
848 }
849 MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
850 }
851 }
852
853 fn capture_key_event(
854 self: Pin<&Self>,
855 _: &InternalKeyEvent,
856 _window_adapter: &Rc<dyn WindowAdapter>,
857 _self_rc: &ItemRc,
858 ) -> KeyEventResult {
859 KeyEventResult::EventIgnored
860 }
861
862 fn key_event(
863 self: Pin<&Self>,
864 _event: &InternalKeyEvent,
865 _window_adapter: &Rc<dyn WindowAdapter>,
866 _self_rc: &ItemRc,
867 ) -> KeyEventResult {
868 KeyEventResult::EventIgnored
869 }
870
871 fn focus_event(
872 self: Pin<&Self>,
873 _: &FocusEvent,
874 _window_adapter: &Rc<dyn WindowAdapter>,
875 _self_rc: &ItemRc,
876 ) -> FocusEventResult {
877 FocusEventResult::FocusIgnored
878 }
879
880 fn render(
881 self: Pin<&Self>,
882 _backend: &mut ItemRendererRef,
883 _self_rc: &ItemRc,
884 _size: LogicalSize,
885 ) -> RenderingResult {
886 RenderingResult::ContinueRenderingChildren
887 }
888
889 fn bounding_rect(
890 self: core::pin::Pin<&Self>,
891 _window_adapter: &Rc<dyn WindowAdapter>,
892 _self_rc: &ItemRc,
893 mut geometry: LogicalRect,
894 ) -> LogicalRect {
895 geometry.size = LogicalSize::zero();
896 geometry
897 }
898
899 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
900 false
901 }
902}
903
904impl ItemConsts for SwipeGestureHandler {
905 const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
906 Self::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
907}
908
909impl SwipeGestureHandler {
910 pub fn cancel(self: Pin<&Self>, _: &Rc<dyn WindowAdapter>, _: &ItemRc) {
911 self.cancel_impl();
912 }
913
914 fn cancel_impl(self: Pin<&Self>) {
915 if !self.pressed.replace(false) {
916 debug_assert!(!self.swiping());
917 return;
918 }
919 if self.swiping() {
920 Self::FIELD_OFFSETS.swiping().apply_pin(self).set(false);
921 Self::FIELD_OFFSETS.cancelled().apply_pin(self).call(&());
922 }
923 }
924
925 fn is_over_threshold(self: Pin<&Self>, position: &LogicalPoint) -> bool {
926 let pressed_pos = self.pressed_position();
927 let dx = position.x - pressed_pos.x as Coord;
928 let dy = position.y - pressed_pos.y as Coord;
929 let threshold = super::flickable::DISTANCE_THRESHOLD.get();
930 (self.handle_swipe_down() && dy > threshold && dy > dx.abs() / 2 as Coord)
931 || (self.handle_swipe_up() && dy < -threshold && dy < -dx.abs() / 2 as Coord)
932 || (self.handle_swipe_left() && dx < -threshold && dx < -dy.abs() / 2 as Coord)
933 || (self.handle_swipe_right() && dx > threshold && dx > dy.abs() / 2 as Coord)
934 }
935}
936
937#[cfg(feature = "ffi")]
938mod ffi {
939 use super::*;
940
941 #[unsafe(no_mangle)]
942 pub unsafe extern "C" fn slint_swipegesturehandler_cancel(
943 s: Pin<&SwipeGestureHandler>,
944 window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
945 self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
946 self_index: u32,
947 ) {
948 unsafe {
949 let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
950 let self_rc = ItemRc::new(self_component.clone(), self_index);
951 s.cancel(window_adapter, &self_rc);
952 }
953 }
954
955 #[unsafe(no_mangle)]
959 pub unsafe extern "C" fn slint_maybe_key_binding_list_init(list: *mut MaybeKeyBindingList) {
960 unsafe {
961 core::ptr::write(list, MaybeKeyBindingList::default());
962 }
963 }
964
965 #[unsafe(no_mangle)]
968 pub unsafe extern "C" fn slint_maybe_key_binding_list_free(list: *mut MaybeKeyBindingList) {
969 unsafe { core::ptr::drop_in_place(list) };
970 }
971}
972
973#[repr(C)]
979#[derive(FieldOffsets, Default, SlintElement)]
980#[pin]
981pub struct ScaleRotateGestureHandler {
982 pub enabled: Property<bool>,
983
984 pub active: Property<bool>,
986 pub scale: Property<f32>,
990 pub rotation: Property<f32>,
993 pub center: Property<LogicalPosition>,
994
995 pub started: Callback<VoidArg>,
997 pub updated: Callback<VoidArg>,
998 pub ended: Callback<VoidArg>,
999 pub cancelled: Callback<VoidArg>,
1000
1001 pub cached_rendering_data: CachedRenderingData,
1003}
1004
1005impl Item for ScaleRotateGestureHandler {
1006 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {
1007 self.scale.set(1.0);
1008 }
1009
1010 fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
1011
1012 fn layout_info(
1013 self: Pin<&Self>,
1014 _orientation: Orientation,
1015 _cross_axis_constraint: Coord,
1016 _window_adapter: &Rc<dyn WindowAdapter>,
1017 _self_rc: &ItemRc,
1018 ) -> LayoutInfo {
1019 LayoutInfo { stretch: 1., ..LayoutInfo::default() }
1020 }
1021
1022 fn input_event_filter_before_children(
1023 self: Pin<&Self>,
1024 event: &MouseEvent,
1025 _window_adapter: &Rc<dyn WindowAdapter>,
1026 _self_rc: &ItemRc,
1027 _: &mut MouseCursor,
1028 ) -> InputEventFilterResult {
1029 match event {
1030 MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. }
1032 if self.enabled() =>
1033 {
1034 InputEventFilterResult::ForwardEvent
1035 }
1036 _ if self.active() => InputEventFilterResult::Intercept,
1039 _ => InputEventFilterResult::ForwardAndIgnore,
1040 }
1041 }
1042
1043 fn input_event(
1044 self: Pin<&Self>,
1045 event: &MouseEvent,
1046 _window_adapter: &Rc<dyn WindowAdapter>,
1047 _self_rc: &ItemRc,
1048 _: &mut MouseCursor,
1049 ) -> InputEventResult {
1050 use crate::input::TouchPhase;
1051 match event {
1052 MouseEvent::PinchGesture { delta, phase, position } => {
1053 if !self.enabled() {
1054 if self.active() {
1055 self.cancel_impl();
1056 }
1057 return InputEventResult::EventIgnored;
1058 }
1059 let new_scale = self.scale() * (1.0 + delta);
1060 Self::FIELD_OFFSETS.scale().apply_pin(self).set(new_scale);
1061 let center = crate::lengths::logical_position_to_api(*position);
1062 Self::FIELD_OFFSETS.center().apply_pin(self).set(center);
1063 match phase {
1064 TouchPhase::Started => {
1065 if !self.active() {
1066 Self::FIELD_OFFSETS.active().apply_pin(self).set(true);
1067 Self::FIELD_OFFSETS.started().apply_pin(self).call(&());
1068 }
1069 InputEventResult::GrabMouse
1070 }
1071 TouchPhase::Moved => {
1072 if !self.active() {
1073 return InputEventResult::EventIgnored;
1074 }
1075 Self::FIELD_OFFSETS.updated().apply_pin(self).call(&());
1076 InputEventResult::GrabMouse
1077 }
1078 TouchPhase::Ended => self.end_impl(),
1079 TouchPhase::Cancelled => {
1080 self.cancel_impl();
1081 InputEventResult::EventAccepted
1082 }
1083 }
1084 }
1085 MouseEvent::RotationGesture { delta, phase, position } => {
1086 if !self.enabled() {
1087 return InputEventResult::EventIgnored;
1088 }
1089 let center = crate::lengths::logical_position_to_api(*position);
1090 Self::FIELD_OFFSETS.center().apply_pin(self).set(center);
1091 let new_rotation = self.rotation() + delta;
1092 Self::FIELD_OFFSETS.rotation().apply_pin(self).set(new_rotation);
1093 match phase {
1094 TouchPhase::Started => {
1095 if !self.active() {
1096 Self::FIELD_OFFSETS.active().apply_pin(self).set(true);
1097 Self::FIELD_OFFSETS.started().apply_pin(self).call(&());
1098 }
1099 InputEventResult::GrabMouse
1100 }
1101 TouchPhase::Moved => {
1102 if !self.active() {
1103 return InputEventResult::EventIgnored;
1104 }
1105 Self::FIELD_OFFSETS.updated().apply_pin(self).call(&());
1106 InputEventResult::GrabMouse
1107 }
1108 TouchPhase::Ended => self.end_impl(),
1109 TouchPhase::Cancelled => {
1110 self.cancel_impl();
1111 InputEventResult::EventAccepted
1112 }
1113 }
1114 }
1115 _ if self.active() => InputEventResult::GrabMouse,
1117 _ => InputEventResult::EventIgnored,
1118 }
1119 }
1120
1121 fn capture_key_event(
1122 self: Pin<&Self>,
1123 _: &InternalKeyEvent,
1124 _window_adapter: &Rc<dyn WindowAdapter>,
1125 _self_rc: &ItemRc,
1126 ) -> KeyEventResult {
1127 KeyEventResult::EventIgnored
1128 }
1129
1130 fn key_event(
1131 self: Pin<&Self>,
1132 _event: &InternalKeyEvent,
1133 _window_adapter: &Rc<dyn WindowAdapter>,
1134 _self_rc: &ItemRc,
1135 ) -> KeyEventResult {
1136 KeyEventResult::EventIgnored
1137 }
1138
1139 fn focus_event(
1140 self: Pin<&Self>,
1141 _: &FocusEvent,
1142 _window_adapter: &Rc<dyn WindowAdapter>,
1143 _self_rc: &ItemRc,
1144 ) -> FocusEventResult {
1145 FocusEventResult::FocusIgnored
1146 }
1147
1148 fn render(
1149 self: Pin<&Self>,
1150 _backend: &mut ItemRendererRef,
1151 _self_rc: &ItemRc,
1152 _size: LogicalSize,
1153 ) -> RenderingResult {
1154 RenderingResult::ContinueRenderingChildren
1155 }
1156
1157 fn bounding_rect(
1158 self: core::pin::Pin<&Self>,
1159 _window_adapter: &Rc<dyn WindowAdapter>,
1160 _self_rc: &ItemRc,
1161 mut geometry: LogicalRect,
1162 ) -> LogicalRect {
1163 geometry.size = LogicalSize::zero();
1164 geometry
1165 }
1166
1167 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
1168 false
1169 }
1170}
1171
1172impl ItemConsts for ScaleRotateGestureHandler {
1173 const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
1174 Self::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
1175}
1176
1177impl ScaleRotateGestureHandler {
1178 fn cancel_impl(self: Pin<&Self>) {
1179 if !self.active() {
1180 return;
1181 }
1182 Self::FIELD_OFFSETS.active().apply_pin(self).set(false);
1183 Self::FIELD_OFFSETS.cancelled().apply_pin(self).call(&());
1184 Self::FIELD_OFFSETS.scale().apply_pin(self).set(1.0);
1188 Self::FIELD_OFFSETS.rotation().apply_pin(self).set(0.0);
1189 }
1190
1191 fn end_impl(self: Pin<&Self>) -> InputEventResult {
1192 if !self.active() {
1193 return InputEventResult::EventIgnored;
1194 }
1195 Self::FIELD_OFFSETS.ended().apply_pin(self).call(&());
1196 Self::FIELD_OFFSETS.active().apply_pin(self).set(false);
1197 Self::FIELD_OFFSETS.scale().apply_pin(self).set(1.0);
1198 Self::FIELD_OFFSETS.rotation().apply_pin(self).set(0.0);
1199 InputEventResult::EventAccepted
1200 }
1201}