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