Skip to main content

i_slint_core/items/
input_items.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use 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, KeyEvent,
12    KeyEventResult, KeyEventType, MouseEvent,
13};
14use crate::item_rendering::CachedRenderingData;
15use crate::layout::{LayoutInfo, Orientation};
16use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PointLengths};
17#[cfg(feature = "rtti")]
18use crate::rtti::*;
19use crate::window::{WindowAdapter, WindowInner};
20use crate::{Callback, Coord, Property};
21use alloc::rc::Rc;
22use const_field_offset::FieldOffsets;
23use core::cell::Cell;
24use core::pin::Pin;
25use i_slint_core_macros::*;
26
27/// The implementation of the `TouchArea` element
28#[repr(C)]
29#[derive(FieldOffsets, SlintElement, Default)]
30#[pin]
31pub struct TouchArea {
32    pub enabled: Property<bool>,
33    /// FIXME: We should annotate this as an "output" property.
34    pub pressed: Property<bool>,
35    pub has_hover: Property<bool>,
36    /// FIXME: there should be just one property for the point instead of two.
37    /// Could even be merged with pressed in a `Property<Option<Point>>` (of course, in the
38    /// implementation item only, for the compiler it would stay separate properties)
39    pub pressed_x: Property<LogicalLength>,
40    pub pressed_y: Property<LogicalLength>,
41    /// FIXME: should maybe be as parameter to the mouse event instead. Or at least just one property
42    pub mouse_x: Property<LogicalLength>,
43    pub mouse_y: Property<LogicalLength>,
44    pub mouse_cursor: Property<MouseCursor>,
45    pub clicked: Callback<VoidArg>,
46    pub double_clicked: Callback<VoidArg>,
47    pub moved: Callback<VoidArg>,
48    pub pointer_event: Callback<PointerEventArg>,
49    pub scroll_event: Callback<PointerScrollEventArg, EventResult>,
50    /// FIXME: remove this
51    pub cached_rendering_data: CachedRenderingData,
52    /// true when we are currently grabbing the mouse
53    grabbed: Cell<bool>,
54}
55
56impl Item for TouchArea {
57    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
58
59    fn layout_info(
60        self: Pin<&Self>,
61        _orientation: Orientation,
62        _window_adapter: &Rc<dyn WindowAdapter>,
63        _self_rc: &ItemRc,
64    ) -> LayoutInfo {
65        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
66    }
67
68    fn input_event_filter_before_children(
69        self: Pin<&Self>,
70        event: &MouseEvent,
71        window_adapter: &Rc<dyn WindowAdapter>,
72        _self_rc: &ItemRc,
73    ) -> InputEventFilterResult {
74        if !self.enabled() {
75            self.has_hover.set(false);
76            if self.grabbed.replace(false) {
77                self.pressed.set(false);
78                Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
79                    button: PointerEventButton::Other,
80                    kind: PointerEventKind::Cancel,
81                    modifiers: window_adapter.window().0.modifiers.get().into(),
82                    is_touch: false,
83                },));
84            }
85            return InputEventFilterResult::ForwardAndIgnore;
86        }
87        if matches!(event, MouseEvent::DragMove(..) | MouseEvent::Drop(..)) {
88            // Someone else has the grab, don't handle hover
89            return InputEventFilterResult::ForwardAndIgnore;
90        }
91        if let Some(pos) = event.position() {
92            Self::FIELD_OFFSETS.mouse_x.apply_pin(self).set(pos.x_length());
93            Self::FIELD_OFFSETS.mouse_y.apply_pin(self).set(pos.y_length());
94        }
95        let hovering = !matches!(event, MouseEvent::Exit);
96        Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(hovering);
97        if hovering {
98            if let Some(x) = window_adapter.internal(crate::InternalToken) {
99                x.set_mouse_cursor(self.mouse_cursor());
100            }
101        }
102        InputEventFilterResult::ForwardAndInterceptGrab
103    }
104
105    fn input_event(
106        self: Pin<&Self>,
107        event: &MouseEvent,
108        window_adapter: &Rc<dyn WindowAdapter>,
109        self_rc: &ItemRc,
110    ) -> InputEventResult {
111        if matches!(event, MouseEvent::Exit) {
112            Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
113            if let Some(x) = window_adapter.internal(crate::InternalToken) {
114                x.set_mouse_cursor(MouseCursor::Default);
115            }
116        }
117        if !self.enabled() {
118            return InputEventResult::EventIgnored;
119        }
120
121        match event {
122            MouseEvent::Pressed { position, button, is_touch, .. } => {
123                self.grabbed.set(true);
124                if *button == PointerEventButton::Left {
125                    Self::FIELD_OFFSETS.pressed_x.apply_pin(self).set(position.x_length());
126                    Self::FIELD_OFFSETS.pressed_y.apply_pin(self).set(position.y_length());
127                    Self::FIELD_OFFSETS.pressed.apply_pin(self).set(true);
128                }
129                Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
130                    button: *button,
131                    kind: PointerEventKind::Down,
132                    modifiers: window_adapter.window().0.modifiers.get().into(),
133                    is_touch: *is_touch,
134                },));
135
136                InputEventResult::GrabMouse
137            }
138            MouseEvent::Exit => {
139                Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false);
140                if self.grabbed.replace(false) {
141                    Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
142                        button: PointerEventButton::Other,
143                        kind: PointerEventKind::Cancel,
144                        modifiers: window_adapter.window().0.modifiers.get().into(),
145                        is_touch: false,
146                    },));
147                }
148
149                InputEventResult::EventAccepted
150            }
151
152            MouseEvent::Released { button, position, click_count, is_touch } => {
153                let geometry = self_rc.geometry();
154                if *button == PointerEventButton::Left
155                    && LogicalRect::new(LogicalPoint::default(), geometry.size).contains(*position)
156                    && self.pressed()
157                {
158                    Self::FIELD_OFFSETS.clicked.apply_pin(self).call(&());
159                    if (click_count % 2) == 1 {
160                        Self::FIELD_OFFSETS.double_clicked.apply_pin(self).call(&())
161                    }
162                }
163
164                self.grabbed.set(false);
165                if *button == PointerEventButton::Left {
166                    Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false);
167                }
168                Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
169                    button: *button,
170                    kind: PointerEventKind::Up,
171                    modifiers: window_adapter.window().0.modifiers.get().into(),
172                    is_touch: *is_touch,
173                },));
174
175                InputEventResult::EventAccepted
176            }
177            MouseEvent::Moved { is_touch, .. } => {
178                Self::FIELD_OFFSETS.pointer_event.apply_pin(self).call(&(PointerEvent {
179                    button: PointerEventButton::Other,
180                    kind: PointerEventKind::Move,
181                    modifiers: window_adapter.window().0.modifiers.get().into(),
182                    is_touch: *is_touch,
183                },));
184                if self.grabbed.get() {
185                    Self::FIELD_OFFSETS.moved.apply_pin(self).call(&());
186                    InputEventResult::GrabMouse
187                } else {
188                    InputEventResult::EventAccepted
189                }
190            }
191            MouseEvent::Wheel { delta_x, delta_y, .. } => {
192                let modifiers = window_adapter.window().0.modifiers.get().into();
193                let r =
194                    Self::FIELD_OFFSETS.scroll_event.apply_pin(self).call(&(PointerScrollEvent {
195                        delta_x: *delta_x,
196                        delta_y: *delta_y,
197                        modifiers,
198                    },));
199                if self.grabbed.get() {
200                    InputEventResult::GrabMouse
201                } else {
202                    match r {
203                        EventResult::Reject => {
204                            // We are ignoring the event, so we will be removed from the item_stack,
205                            // therefore we must remove the has_hover flag as there might be a scroll under us.
206                            // It will be put back later.
207                            Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
208                            InputEventResult::EventIgnored
209                        }
210                        EventResult::Accept => InputEventResult::EventAccepted,
211                    }
212                }
213            }
214            MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
215        }
216    }
217
218    fn capture_key_event(
219        self: Pin<&Self>,
220        _: &KeyEvent,
221        _window_adapter: &Rc<dyn WindowAdapter>,
222        _self_rc: &ItemRc,
223    ) -> KeyEventResult {
224        KeyEventResult::EventIgnored
225    }
226
227    fn key_event(
228        self: Pin<&Self>,
229        _: &KeyEvent,
230        _window_adapter: &Rc<dyn WindowAdapter>,
231        _self_rc: &ItemRc,
232    ) -> KeyEventResult {
233        KeyEventResult::EventIgnored
234    }
235
236    fn focus_event(
237        self: Pin<&Self>,
238        _: &FocusEvent,
239        _window_adapter: &Rc<dyn WindowAdapter>,
240        _self_rc: &ItemRc,
241    ) -> FocusEventResult {
242        FocusEventResult::FocusIgnored
243    }
244
245    fn render(
246        self: Pin<&Self>,
247        _backend: &mut ItemRendererRef,
248        _self_rc: &ItemRc,
249        _size: LogicalSize,
250    ) -> RenderingResult {
251        RenderingResult::ContinueRenderingChildren
252    }
253
254    fn bounding_rect(
255        self: core::pin::Pin<&Self>,
256        _window_adapter: &Rc<dyn WindowAdapter>,
257        _self_rc: &ItemRc,
258        mut geometry: LogicalRect,
259    ) -> LogicalRect {
260        geometry.size = LogicalSize::zero();
261        geometry
262    }
263
264    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
265        false
266    }
267}
268
269impl ItemConsts for TouchArea {
270    const cached_rendering_data_offset: const_field_offset::FieldOffset<
271        TouchArea,
272        CachedRenderingData,
273    > = TouchArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
274}
275
276/// A runtime item that exposes key
277#[repr(C)]
278#[derive(FieldOffsets, Default, SlintElement)]
279#[pin]
280pub struct FocusScope {
281    pub enabled: Property<bool>,
282    pub has_focus: Property<bool>,
283    pub focus_on_click: Property<bool>,
284    pub focus_on_tab_navigation: Property<bool>,
285    pub key_pressed: Callback<KeyEventArg, EventResult>,
286    pub key_released: Callback<KeyEventArg, EventResult>,
287    pub capture_key_pressed: Callback<KeyEventArg, EventResult>,
288    pub capture_key_released: Callback<KeyEventArg, EventResult>,
289    pub focus_changed_event: Callback<FocusReasonArg>,
290    pub focus_gained: Callback<FocusReasonArg>,
291    pub focus_lost: Callback<FocusReasonArg>,
292    /// FIXME: remove this
293    pub cached_rendering_data: CachedRenderingData,
294}
295
296impl Item for FocusScope {
297    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
298
299    fn layout_info(
300        self: Pin<&Self>,
301        _orientation: Orientation,
302        _window_adapter: &Rc<dyn WindowAdapter>,
303        _self_rc: &ItemRc,
304    ) -> LayoutInfo {
305        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
306    }
307
308    fn input_event_filter_before_children(
309        self: Pin<&Self>,
310        _: &MouseEvent,
311        _window_adapter: &Rc<dyn WindowAdapter>,
312        _self_rc: &ItemRc,
313    ) -> InputEventFilterResult {
314        InputEventFilterResult::ForwardEvent
315    }
316
317    fn input_event(
318        self: Pin<&Self>,
319        event: &MouseEvent,
320        window_adapter: &Rc<dyn WindowAdapter>,
321        self_rc: &ItemRc,
322    ) -> InputEventResult {
323        if self.enabled()
324            && self.focus_on_click()
325            && matches!(event, MouseEvent::Pressed { .. })
326            && !self.has_focus()
327        {
328            WindowInner::from_pub(window_adapter.window()).set_focus_item(
329                self_rc,
330                true,
331                FocusReason::PointerClick,
332            );
333            InputEventResult::EventAccepted
334        } else {
335            InputEventResult::EventIgnored
336        }
337    }
338
339    fn capture_key_event(
340        self: Pin<&Self>,
341        event: &KeyEvent,
342        _window_adapter: &Rc<dyn WindowAdapter>,
343        _self_rc: &ItemRc,
344    ) -> KeyEventResult {
345        let r = match event.event_type {
346            KeyEventType::KeyPressed => {
347                Self::FIELD_OFFSETS.capture_key_pressed.apply_pin(self).call(&(event.clone(),))
348            }
349            KeyEventType::KeyReleased => {
350                Self::FIELD_OFFSETS.capture_key_released.apply_pin(self).call(&(event.clone(),))
351            }
352            KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
353                EventResult::Reject
354            }
355        };
356        match r {
357            EventResult::Accept => KeyEventResult::EventAccepted,
358            EventResult::Reject => KeyEventResult::EventIgnored,
359        }
360    }
361
362    fn key_event(
363        self: Pin<&Self>,
364        event: &KeyEvent,
365        _window_adapter: &Rc<dyn WindowAdapter>,
366        _self_rc: &ItemRc,
367    ) -> KeyEventResult {
368        let r = match event.event_type {
369            KeyEventType::KeyPressed => {
370                Self::FIELD_OFFSETS.key_pressed.apply_pin(self).call(&(event.clone(),))
371            }
372            KeyEventType::KeyReleased => {
373                Self::FIELD_OFFSETS.key_released.apply_pin(self).call(&(event.clone(),))
374            }
375            KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
376                EventResult::Reject
377            }
378        };
379        match r {
380            EventResult::Accept => KeyEventResult::EventAccepted,
381            EventResult::Reject => KeyEventResult::EventIgnored,
382        }
383    }
384
385    fn focus_event(
386        self: Pin<&Self>,
387        event: &FocusEvent,
388        _window_adapter: &Rc<dyn WindowAdapter>,
389        _self_rc: &ItemRc,
390    ) -> FocusEventResult {
391        if !self.enabled() {
392            return FocusEventResult::FocusIgnored;
393        }
394
395        match event {
396            FocusEvent::FocusIn(reason) => {
397                match reason {
398                    FocusReason::TabNavigation if !self.focus_on_tab_navigation() => {
399                        return FocusEventResult::FocusIgnored;
400                    }
401                    FocusReason::PointerClick if !self.focus_on_click() => {
402                        return FocusEventResult::FocusIgnored;
403                    }
404                    _ => (),
405                };
406
407                self.has_focus.set(true);
408                Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&((*reason,)));
409                Self::FIELD_OFFSETS.focus_gained.apply_pin(self).call(&((*reason,)));
410            }
411            FocusEvent::FocusOut(reason) => {
412                self.has_focus.set(false);
413                Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&((*reason,)));
414                Self::FIELD_OFFSETS.focus_lost.apply_pin(self).call(&((*reason,)));
415            }
416        }
417        FocusEventResult::FocusAccepted
418    }
419
420    fn render(
421        self: Pin<&Self>,
422        _backend: &mut ItemRendererRef,
423        _self_rc: &ItemRc,
424        _size: LogicalSize,
425    ) -> RenderingResult {
426        RenderingResult::ContinueRenderingChildren
427    }
428
429    fn bounding_rect(
430        self: core::pin::Pin<&Self>,
431        _window_adapter: &Rc<dyn WindowAdapter>,
432        _self_rc: &ItemRc,
433        mut geometry: LogicalRect,
434    ) -> LogicalRect {
435        geometry.size = LogicalSize::zero();
436        geometry
437    }
438
439    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
440        false
441    }
442}
443
444impl ItemConsts for FocusScope {
445    const cached_rendering_data_offset: const_field_offset::FieldOffset<
446        FocusScope,
447        CachedRenderingData,
448    > = FocusScope::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
449}
450
451#[repr(C)]
452#[derive(FieldOffsets, Default, SlintElement)]
453#[pin]
454pub struct SwipeGestureHandler {
455    pub enabled: Property<bool>,
456    pub handle_swipe_left: Property<bool>,
457    pub handle_swipe_right: Property<bool>,
458    pub handle_swipe_up: Property<bool>,
459    pub handle_swipe_down: Property<bool>,
460
461    pub moved: Callback<VoidArg>,
462    pub swiped: Callback<VoidArg>,
463    pub cancelled: Callback<VoidArg>,
464
465    pub pressed_position: Property<LogicalPosition>,
466    pub current_position: Property<LogicalPosition>,
467    pub swiping: Property<bool>,
468
469    // true when the cursor is pressed down and we haven't cancelled yet for another reason
470    pressed: Cell<bool>,
471    // capture_events: Cell<bool>,
472    /// FIXME: remove this
473    pub cached_rendering_data: CachedRenderingData,
474}
475
476impl Item for SwipeGestureHandler {
477    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
478
479    fn layout_info(
480        self: Pin<&Self>,
481        _orientation: Orientation,
482        _window_adapter: &Rc<dyn WindowAdapter>,
483        _self_rc: &ItemRc,
484    ) -> LayoutInfo {
485        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
486    }
487
488    fn input_event_filter_before_children(
489        self: Pin<&Self>,
490        event: &MouseEvent,
491        _window_adapter: &Rc<dyn WindowAdapter>,
492        _self_rc: &ItemRc,
493    ) -> InputEventFilterResult {
494        if !self.enabled() {
495            if self.pressed.get() {
496                self.cancel_impl();
497            }
498            return InputEventFilterResult::ForwardAndIgnore;
499        }
500
501        match event {
502            MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => {
503                Self::FIELD_OFFSETS
504                    .pressed_position
505                    .apply_pin(self)
506                    .set(crate::lengths::logical_position_to_api(*position));
507                self.pressed.set(true);
508                InputEventFilterResult::DelayForwarding(
509                    super::flickable::FORWARD_DELAY.as_millis() as _
510                )
511            }
512            MouseEvent::Exit => {
513                self.cancel_impl();
514                InputEventFilterResult::ForwardAndIgnore
515            }
516            MouseEvent::Released { button: PointerEventButton::Left, .. } => {
517                if self.swiping() {
518                    InputEventFilterResult::Intercept
519                } else {
520                    self.pressed.set(false);
521                    InputEventFilterResult::ForwardEvent
522                }
523            }
524            MouseEvent::Moved { position, .. } => {
525                if self.swiping() {
526                    InputEventFilterResult::Intercept
527                } else if !self.pressed.get() {
528                    InputEventFilterResult::ForwardEvent
529                } else if self.is_over_threshold(position) {
530                    InputEventFilterResult::Intercept
531                } else {
532                    InputEventFilterResult::ForwardAndInterceptGrab
533                }
534            }
535            MouseEvent::Wheel { .. } => InputEventFilterResult::ForwardAndIgnore,
536            // Not the left button
537            MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
538                InputEventFilterResult::ForwardAndIgnore
539            }
540            MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
541                InputEventFilterResult::ForwardAndIgnore
542            }
543        }
544    }
545
546    fn input_event(
547        self: Pin<&Self>,
548        event: &MouseEvent,
549        _window_adapter: &Rc<dyn WindowAdapter>,
550        _self_rc: &ItemRc,
551    ) -> InputEventResult {
552        match event {
553            MouseEvent::Pressed { .. } => InputEventResult::GrabMouse,
554            MouseEvent::Exit => {
555                self.cancel_impl();
556                InputEventResult::EventIgnored
557            }
558            MouseEvent::Released { position, .. } => {
559                if !self.pressed.get() && !self.swiping() {
560                    return InputEventResult::EventIgnored;
561                }
562                self.current_position.set(crate::lengths::logical_position_to_api(*position));
563                self.pressed.set(false);
564                if self.swiping() {
565                    Self::FIELD_OFFSETS.swiping.apply_pin(self).set(false);
566                    Self::FIELD_OFFSETS.swiped.apply_pin(self).call(&());
567                    InputEventResult::EventAccepted
568                } else {
569                    InputEventResult::EventIgnored
570                }
571            }
572            MouseEvent::Moved { position, .. } => {
573                if !self.pressed.get() {
574                    return InputEventResult::EventAccepted;
575                }
576                self.current_position.set(crate::lengths::logical_position_to_api(*position));
577                let mut swiping = self.swiping();
578                if !swiping && self.is_over_threshold(position) {
579                    Self::FIELD_OFFSETS.swiping.apply_pin(self).set(true);
580                    swiping = true;
581                }
582                Self::FIELD_OFFSETS.moved.apply_pin(self).call(&());
583                if swiping { InputEventResult::GrabMouse } else { InputEventResult::EventAccepted }
584            }
585            MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
586            MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
587        }
588    }
589
590    fn capture_key_event(
591        self: Pin<&Self>,
592        _: &KeyEvent,
593        _window_adapter: &Rc<dyn WindowAdapter>,
594        _self_rc: &ItemRc,
595    ) -> KeyEventResult {
596        KeyEventResult::EventIgnored
597    }
598
599    fn key_event(
600        self: Pin<&Self>,
601        _event: &KeyEvent,
602        _window_adapter: &Rc<dyn WindowAdapter>,
603        _self_rc: &ItemRc,
604    ) -> KeyEventResult {
605        KeyEventResult::EventIgnored
606    }
607
608    fn focus_event(
609        self: Pin<&Self>,
610        _: &FocusEvent,
611        _window_adapter: &Rc<dyn WindowAdapter>,
612        _self_rc: &ItemRc,
613    ) -> FocusEventResult {
614        FocusEventResult::FocusIgnored
615    }
616
617    fn render(
618        self: Pin<&Self>,
619        _backend: &mut ItemRendererRef,
620        _self_rc: &ItemRc,
621        _size: LogicalSize,
622    ) -> RenderingResult {
623        RenderingResult::ContinueRenderingChildren
624    }
625
626    fn bounding_rect(
627        self: core::pin::Pin<&Self>,
628        _window_adapter: &Rc<dyn WindowAdapter>,
629        _self_rc: &ItemRc,
630        mut geometry: LogicalRect,
631    ) -> LogicalRect {
632        geometry.size = LogicalSize::zero();
633        geometry
634    }
635
636    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
637        false
638    }
639}
640
641impl ItemConsts for SwipeGestureHandler {
642    const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
643        Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
644}
645
646impl SwipeGestureHandler {
647    pub fn cancel(self: Pin<&Self>, _: &Rc<dyn WindowAdapter>, _: &ItemRc) {
648        self.cancel_impl();
649    }
650
651    fn cancel_impl(self: Pin<&Self>) {
652        if !self.pressed.replace(false) {
653            debug_assert!(!self.swiping());
654            return;
655        }
656        if self.swiping() {
657            Self::FIELD_OFFSETS.swiping.apply_pin(self).set(false);
658            Self::FIELD_OFFSETS.cancelled.apply_pin(self).call(&());
659        }
660    }
661
662    fn is_over_threshold(self: Pin<&Self>, position: &LogicalPoint) -> bool {
663        let pressed_pos = self.pressed_position();
664        let dx = position.x - pressed_pos.x as Coord;
665        let dy = position.y - pressed_pos.y as Coord;
666        let threshold = super::flickable::DISTANCE_THRESHOLD.get();
667        (self.handle_swipe_down() && dy > threshold && dy > dx.abs() / 2 as Coord)
668            || (self.handle_swipe_up() && dy < -threshold && dy < -dx.abs() / 2 as Coord)
669            || (self.handle_swipe_left() && dx < -threshold && dx < -dy.abs() / 2 as Coord)
670            || (self.handle_swipe_right() && dx > threshold && dx > dy.abs() / 2 as Coord)
671    }
672}
673
674#[cfg(feature = "ffi")]
675#[unsafe(no_mangle)]
676pub unsafe extern "C" fn slint_swipegesturehandler_cancel(
677    s: Pin<&SwipeGestureHandler>,
678    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
679    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
680    self_index: u32,
681) {
682    unsafe {
683        let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
684        let self_rc = ItemRc::new(self_component.clone(), self_index);
685        s.cancel(window_adapter, &self_rc);
686    }
687}