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, 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#[repr(C)]
29#[derive(FieldOffsets, SlintElement, Default)]
30#[pin]
31pub struct TouchArea {
32 pub enabled: Property<bool>,
33 pub pressed: Property<bool>,
35 pub has_hover: Property<bool>,
36 pub pressed_x: Property<LogicalLength>,
40 pub pressed_y: Property<LogicalLength>,
41 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 pub cached_rendering_data: CachedRenderingData,
52 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 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 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#[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 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 pressed: Cell<bool>,
471 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 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}