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    PointerEvent, PointerEventArg, PointerEventButton, PointerEventKind, PointerScrollEvent,
7    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, MouseCursorInner};
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/// The implementation of the `TouchArea` element
31#[repr(C)]
32#[derive(FieldOffsets, SlintElement, Default)]
33#[pin]
34pub struct TouchArea {
35    pub enabled: Property<bool>,
36    /// FIXME: We should annotate this as an "output" property.
37    pub pressed: Property<bool>,
38    pub has_hover: Property<bool>,
39    /// FIXME: there should be just one property for the point instead of two.
40    /// Could even be merged with pressed in a `Property<Option<Point>>` (of course, in the
41    /// implementation item only, for the compiler it would stay separate properties)
42    pub pressed_x: Property<LogicalLength>,
43    pub pressed_y: Property<LogicalLength>,
44    /// FIXME: should maybe be as parameter to the mouse event instead. Or at least just one property
45    pub mouse_x: Property<LogicalLength>,
46    pub mouse_y: Property<LogicalLength>,
47    pub mouse_cursor: Property<MouseCursorInner>,
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    /// FIXME: remove this
54    pub cached_rendering_data: CachedRenderingData,
55    /// true when we are currently grabbing the mouse
56    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 MouseCursorInner,
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                    touch_finger_id: 0,
90                },));
91            }
92            return InputEventFilterResult::ForwardAndIgnore;
93        }
94        if matches!(event, MouseEvent::DragMove { .. } | MouseEvent::Drop { .. }) {
95            // Someone else has the grab, don't handle hover
96            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 MouseCursorInner,
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, touch_finger_id, .. } => {
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                    touch_finger_id: *touch_finger_id,
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                        touch_finger_id: 0,
148                    },));
149                }
150
151                InputEventResult::EventAccepted
152            }
153
154            MouseEvent::Released { button, position, click_count, touch_finger_id } => {
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                    touch_finger_id: *touch_finger_id,
175                },));
176
177                InputEventResult::EventAccepted
178            }
179            MouseEvent::Moved { touch_finger_id, .. } => {
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                    touch_finger_id: *touch_finger_id,
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                            // We are ignoring the event, so we will be removed from the item_stack,
204                            // therefore we must remove the has_hover flag as there might be a scroll under us.
205                            // It will be put back later.
206                            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/// The implementation of the `WindowMoveArea` element
286#[repr(C)]
287#[derive(FieldOffsets, Default, SlintElement)]
288#[pin]
289pub struct WindowMoveArea {
290    pub enabled: Property<bool>,
291    pressed: Cell<bool>,
292    pressed_position: Cell<LogicalPoint>,
293    pub cached_rendering_data: CachedRenderingData,
294}
295
296impl Item for WindowMoveArea {
297    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
298
299    fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
300
301    fn layout_info(
302        self: Pin<&Self>,
303        _: Orientation,
304        _cross_axis_constraint: Coord,
305        _window_adapter: &Rc<dyn WindowAdapter>,
306        _self_rc: &ItemRc,
307    ) -> LayoutInfo {
308        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
309    }
310
311    fn input_event_filter_before_children(
312        self: Pin<&Self>,
313        event: &MouseEvent,
314        _window_adapter: &Rc<dyn WindowAdapter>,
315        _self_rc: &ItemRc,
316        _: &mut MouseCursorInner,
317    ) -> InputEventFilterResult {
318        if !self.enabled() {
319            self.pressed.set(false);
320            return InputEventFilterResult::ForwardAndIgnore;
321        }
322        super::drag_n_drop::press_drag_filter(&self.pressed, &self.pressed_position, event)
323    }
324
325    fn input_event(
326        self: Pin<&Self>,
327        event: &MouseEvent,
328        window_adapter: &Rc<dyn WindowAdapter>,
329        _self_rc: &ItemRc,
330        _: &mut MouseCursorInner,
331    ) -> InputEventResult {
332        match event {
333            MouseEvent::Pressed { .. } => InputEventResult::EventAccepted,
334            MouseEvent::Exit | MouseEvent::Released { .. } => {
335                self.pressed.set(false);
336                InputEventResult::EventIgnored
337            }
338            MouseEvent::Moved { position, .. } => {
339                if !self.pressed.get() || !self.enabled() {
340                    return InputEventResult::EventIgnored;
341                }
342                if super::drag_n_drop::exceeds_drag_threshold(
343                    self.pressed_position.get(),
344                    *position,
345                ) {
346                    self.pressed.set(false);
347                    if let Some(internal) = window_adapter.internal(crate::InternalToken) {
348                        internal.start_window_move();
349                    }
350                }
351                InputEventResult::EventAccepted
352            }
353            MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
354            MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. } => {
355                InputEventResult::EventIgnored
356            }
357            MouseEvent::DragMove { .. } | MouseEvent::Drop { .. } => InputEventResult::EventIgnored,
358        }
359    }
360
361    fn capture_key_event(
362        self: Pin<&Self>,
363        _: &InternalKeyEvent,
364        _window_adapter: &Rc<dyn WindowAdapter>,
365        _self_rc: &ItemRc,
366    ) -> KeyEventResult {
367        KeyEventResult::EventIgnored
368    }
369
370    fn key_event(
371        self: Pin<&Self>,
372        _: &InternalKeyEvent,
373        _window_adapter: &Rc<dyn WindowAdapter>,
374        _self_rc: &ItemRc,
375    ) -> KeyEventResult {
376        KeyEventResult::EventIgnored
377    }
378
379    fn focus_event(
380        self: Pin<&Self>,
381        _: &FocusEvent,
382        _window_adapter: &Rc<dyn WindowAdapter>,
383        _self_rc: &ItemRc,
384    ) -> FocusEventResult {
385        FocusEventResult::FocusIgnored
386    }
387
388    fn render(
389        self: Pin<&Self>,
390        _: &mut ItemRendererRef,
391        _self_rc: &ItemRc,
392        _size: LogicalSize,
393    ) -> RenderingResult {
394        RenderingResult::ContinueRenderingChildren
395    }
396
397    fn bounding_rect(
398        self: core::pin::Pin<&Self>,
399        _window_adapter: &Rc<dyn WindowAdapter>,
400        _self_rc: &ItemRc,
401        mut geometry: LogicalRect,
402    ) -> LogicalRect {
403        geometry.size = LogicalSize::zero();
404        geometry
405    }
406
407    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
408        false
409    }
410}
411
412impl ItemConsts for WindowMoveArea {
413    const cached_rendering_data_offset: const_field_offset::FieldOffset<
414        WindowMoveArea,
415        CachedRenderingData,
416    > = WindowMoveArea::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
417}
418
419#[repr(C)]
420#[derive(FieldOffsets, Default, SlintElement)]
421#[pin]
422pub struct KeyBinding {
423    pub keys: Property<Keys>,
424    pub enabled: Property<bool>,
425    pub activated: Callback<VoidArg>,
426    pub cached_rendering_data: CachedRenderingData,
427}
428
429impl Item for KeyBinding {
430    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
431
432    fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
433
434    fn layout_info(
435        self: Pin<&Self>,
436        _orientation: crate::items::Orientation,
437        _cross_axis_constraint: Coord,
438        _window_adapter: &Rc<dyn WindowAdapter>,
439        _self_rc: &ItemRc,
440    ) -> crate::layout::LayoutInfo {
441        Default::default()
442    }
443
444    fn input_event_filter_before_children(
445        self: Pin<&Self>,
446        _: &crate::input::MouseEvent,
447        _window_adapter: &Rc<dyn WindowAdapter>,
448        _self_rc: &ItemRc,
449        _: &mut crate::cursor::MouseCursorInner,
450    ) -> crate::input::InputEventFilterResult {
451        Default::default()
452    }
453
454    fn input_event(
455        self: Pin<&Self>,
456        _: &crate::input::MouseEvent,
457        _window_adapter: &Rc<dyn WindowAdapter>,
458        _self_rc: &ItemRc,
459        _: &mut crate::cursor::MouseCursorInner,
460    ) -> crate::input::InputEventResult {
461        Default::default()
462    }
463
464    fn capture_key_event(
465        self: Pin<&Self>,
466        _: &InternalKeyEvent,
467        _window_adapter: &Rc<dyn WindowAdapter>,
468        _self_rc: &ItemRc,
469    ) -> crate::input::KeyEventResult {
470        crate::input::KeyEventResult::EventIgnored
471    }
472
473    fn key_event(
474        self: Pin<&Self>,
475        _: &InternalKeyEvent,
476        _window_adapter: &Rc<dyn WindowAdapter>,
477        _self_rc: &ItemRc,
478    ) -> crate::input::KeyEventResult {
479        Default::default()
480    }
481
482    fn focus_event(
483        self: Pin<&Self>,
484        _: &crate::input::FocusEvent,
485        _window_adapter: &Rc<dyn WindowAdapter>,
486        _self_rc: &ItemRc,
487    ) -> crate::input::FocusEventResult {
488        Default::default()
489    }
490
491    fn render(
492        self: Pin<&Self>,
493        _backend: &mut &mut dyn crate::item_rendering::ItemRenderer,
494        _self_rc: &ItemRc,
495        _size: crate::lengths::LogicalSize,
496    ) -> crate::items::RenderingResult {
497        Default::default()
498    }
499
500    fn bounding_rect(
501        self: Pin<&Self>,
502        _window_adapter: &Rc<dyn WindowAdapter>,
503        _self_rc: &ItemRc,
504        geometry: crate::lengths::LogicalRect,
505    ) -> crate::lengths::LogicalRect {
506        geometry
507    }
508
509    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
510        false
511    }
512}
513
514/// An optimized ShortcutList that is only initialized when it is
515/// first accessed.
516#[repr(C)]
517#[derive(Default)] // results in a null pointer, which we will initialize on first access
518pub struct MaybeKeyBindingList(Cell<*const KeyBindingList>);
519
520impl MaybeKeyBindingList {
521    fn ensure_init(&self) {
522        // This would be a race condition in Multi-threaded code, but
523        // this type isn't Sync, so this function cannot race with another thread.
524        if self.0.get().is_null() {
525            self.0.set(Box::leak(Box::default()));
526        }
527    }
528}
529
530impl Drop for MaybeKeyBindingList {
531    fn drop(&mut self) {
532        let ptr = self.0.replace(core::ptr::null());
533        if !ptr.is_null() {
534            // SAFETY: Must be a pointer returned by `Box::leak`, which is guaranteed by `ensure_init`.
535            drop(unsafe { Box::from_raw(ptr as *mut KeyBindingList) });
536        }
537    }
538}
539
540impl MaybeKeyBindingList {
541    fn deref_pin(self: Pin<&Self>) -> Pin<&KeyBindingList> {
542        self.ensure_init();
543        // SAFETY: Must be non-null and properly aligned, which is guaranteed by `ensure_init`.
544        unsafe { Pin::new_unchecked(&*self.get_ref().0.get()) }
545    }
546}
547
548#[derive(Default)]
549#[pin_project::pin_project]
550pub struct KeyBindingList {
551    found: core::cell::RefCell<Vec<VWeakMapped<ItemTreeVTable, KeyBinding>>>,
552    #[pin]
553    property_tracker: PropertyTracker,
554}
555
556/// A runtime item that exposes key events
557#[repr(C)]
558#[derive(FieldOffsets, Default, SlintElement)]
559#[pin]
560pub struct FocusScope {
561    pub enabled: Property<bool>,
562    pub has_focus: Property<bool>,
563    pub focus_on_click: Property<bool>,
564    pub focus_on_tab_navigation: Property<bool>,
565    pub key_pressed: Callback<KeyEventArg, EventResult>,
566    pub key_released: Callback<KeyEventArg, EventResult>,
567    pub capture_key_pressed: Callback<KeyEventArg, EventResult>,
568    pub capture_key_released: Callback<KeyEventArg, EventResult>,
569    pub focus_changed_event: Callback<FocusReasonArg>,
570    pub focus_gained: Callback<FocusReasonArg>,
571    pub focus_lost: Callback<FocusReasonArg>,
572    pub key_bindings: MaybeKeyBindingList,
573    /// FIXME: remove this
574    pub cached_rendering_data: CachedRenderingData,
575}
576
577impl FocusScope {
578    fn visit_enabled_key_bindings<R>(
579        self: Pin<&Self>,
580        self_rc: &ItemRc,
581        mut fun: impl FnMut(&VRcMapped<ItemTreeVTable, KeyBinding>) -> Option<R>,
582    ) -> Option<R> {
583        let list = Self::FIELD_OFFSETS.key_bindings().apply_pin(self);
584        let list = list.deref_pin();
585
586        list.project_ref().property_tracker.evaluate_if_dirty(|| {
587            let mut found = list.found.borrow_mut();
588            found.clear();
589
590            let mut next = self_rc.first_child();
591            while let Some(child) = next {
592                if let Some(key_binding) = ItemRc::downcast::<KeyBinding>(&child)
593                    && key_binding.as_pin_ref().enabled()
594                {
595                    found.push(VRcMapped::downgrade(&key_binding));
596                }
597                next = child.next_sibling();
598            }
599        });
600
601        let list = list.found.borrow();
602        for key_binding in &*list {
603            let Some(shortcut) = key_binding.upgrade() else {
604                crate::debug_log!("Warning: Found a dropped KeyBinding!");
605                continue;
606            };
607            if let Some(result) = fun(&shortcut) {
608                return Some(result);
609            }
610        }
611
612        None
613    }
614
615    /// Returns the first matching key binding and whether there are multiple matches.
616    fn key_binding_for_event(
617        self: Pin<&Self>,
618        self_rc: &ItemRc,
619        inter_key_event: &InternalKeyEvent,
620    ) -> Option<(VRcMapped<ItemTreeVTable, KeyBinding>, bool)> {
621        let mut first_match = None;
622
623        let ambiguous = self.visit_enabled_key_bindings(self_rc, |key_binding| {
624            let keys = key_binding.as_pin_ref().keys();
625            if keys.matches(&inter_key_event.key_event) {
626                match &first_match {
627                    Some(key_binding) => {
628                        return Some(VRcMapped::clone(key_binding));
629                    }
630                    None => {
631                        first_match = Some(VRcMapped::clone(key_binding));
632                    }
633                };
634            }
635            None
636        });
637
638        first_match.map(|binding| (VRcMapped::clone(&binding), ambiguous.is_some()))
639    }
640}
641
642impl Item for FocusScope {
643    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
644
645    fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
646
647    fn layout_info(
648        self: Pin<&Self>,
649        _orientation: Orientation,
650        _cross_axis_constraint: Coord,
651        _window_adapter: &Rc<dyn WindowAdapter>,
652        _self_rc: &ItemRc,
653    ) -> LayoutInfo {
654        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
655    }
656
657    fn input_event_filter_before_children(
658        self: Pin<&Self>,
659        _: &MouseEvent,
660        _window_adapter: &Rc<dyn WindowAdapter>,
661        _self_rc: &ItemRc,
662        _: &mut MouseCursorInner,
663    ) -> InputEventFilterResult {
664        InputEventFilterResult::ForwardEvent
665    }
666
667    fn input_event(
668        self: Pin<&Self>,
669        event: &MouseEvent,
670        window_adapter: &Rc<dyn WindowAdapter>,
671        self_rc: &ItemRc,
672        _: &mut MouseCursorInner,
673    ) -> InputEventResult {
674        if self.enabled()
675            && self.focus_on_click()
676            && matches!(event, MouseEvent::Pressed { .. })
677            && !self.has_focus()
678        {
679            WindowInner::from_pub(window_adapter.window()).set_focus_item(
680                self_rc,
681                true,
682                FocusReason::PointerClick,
683            );
684            InputEventResult::EventAccepted
685        } else {
686            InputEventResult::EventIgnored
687        }
688    }
689
690    fn capture_key_event(
691        self: Pin<&Self>,
692        event: &InternalKeyEvent,
693        _window_adapter: &Rc<dyn WindowAdapter>,
694        _self_rc: &ItemRc,
695    ) -> KeyEventResult {
696        let r = match event.event_type {
697            KeyEventType::KeyPressed => Self::FIELD_OFFSETS
698                .capture_key_pressed()
699                .apply_pin(self)
700                .call(&(event.key_event.clone(),)),
701            KeyEventType::KeyReleased => Self::FIELD_OFFSETS
702                .capture_key_released()
703                .apply_pin(self)
704                .call(&(event.key_event.clone(),)),
705            KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
706                EventResult::Reject
707            }
708        };
709        match r {
710            EventResult::Accept => KeyEventResult::EventAccepted,
711            EventResult::Reject => KeyEventResult::EventIgnored,
712        }
713    }
714
715    fn key_event(
716        self: Pin<&Self>,
717        event: &InternalKeyEvent,
718        _window_adapter: &Rc<dyn WindowAdapter>,
719        self_rc: &ItemRc,
720    ) -> KeyEventResult {
721        let r = match event.event_type {
722            KeyEventType::KeyPressed => {
723                if let Some((key_binding, ambiguous)) = self.key_binding_for_event(self_rc, event) {
724                    if ambiguous {
725                        let keys = KeyBinding::FIELD_OFFSETS
726                            .keys()
727                            .apply_pin(key_binding.as_pin_ref())
728                            .get();
729                        crate::debug_log!(
730                            "Warning: Multiple matching KeyBinding elements for keys {:?}!",
731                            keys
732                        );
733                    }
734                    KeyBinding::FIELD_OFFSETS
735                        .activated()
736                        .apply_pin(key_binding.as_pin_ref())
737                        .call(&());
738                    EventResult::Accept
739                } else {
740                    Self::FIELD_OFFSETS
741                        .key_pressed()
742                        .apply_pin(self)
743                        .call(&(event.key_event.clone(),))
744                }
745            }
746            KeyEventType::KeyReleased => {
747                let binding = self.key_binding_for_event(self_rc, event);
748                if binding.is_some() {
749                    // The binding should have already handled the key press,
750                    // so we just accept the release event if it matches a binding,
751                    // to ensure the events remain "symmetric" in the key-pressed and key-released callbacks.
752                    // Either both events appear in the callbacks, or neither do.
753                    EventResult::Accept
754                } else {
755                    Self::FIELD_OFFSETS
756                        .key_released()
757                        .apply_pin(self)
758                        .call(&(event.key_event.clone(),))
759                }
760            }
761            KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
762                EventResult::Reject
763            }
764        };
765        match r {
766            EventResult::Accept => KeyEventResult::EventAccepted,
767            EventResult::Reject => KeyEventResult::EventIgnored,
768        }
769    }
770
771    fn focus_event(
772        self: Pin<&Self>,
773        event: &FocusEvent,
774        _window_adapter: &Rc<dyn WindowAdapter>,
775        _self_rc: &ItemRc,
776    ) -> FocusEventResult {
777        if !self.enabled() {
778            return FocusEventResult::FocusIgnored;
779        }
780
781        match event {
782            FocusEvent::FocusIn(reason) => {
783                match reason {
784                    FocusReason::TabNavigation if !self.focus_on_tab_navigation() => {
785                        return FocusEventResult::FocusIgnored;
786                    }
787                    FocusReason::PointerClick if !self.focus_on_click() => {
788                        return FocusEventResult::FocusIgnored;
789                    }
790                    _ => (),
791                };
792
793                self.has_focus.set(true);
794                Self::FIELD_OFFSETS.focus_changed_event().apply_pin(self).call(&(*reason,));
795                Self::FIELD_OFFSETS.focus_gained().apply_pin(self).call(&(*reason,));
796            }
797            FocusEvent::FocusOut(reason) => {
798                self.has_focus.set(false);
799                Self::FIELD_OFFSETS.focus_changed_event().apply_pin(self).call(&(*reason,));
800                Self::FIELD_OFFSETS.focus_lost().apply_pin(self).call(&(*reason,));
801            }
802        }
803        FocusEventResult::FocusAccepted
804    }
805
806    fn render(
807        self: Pin<&Self>,
808        _backend: &mut ItemRendererRef,
809        _self_rc: &ItemRc,
810        _size: LogicalSize,
811    ) -> RenderingResult {
812        RenderingResult::ContinueRenderingChildren
813    }
814
815    fn bounding_rect(
816        self: core::pin::Pin<&Self>,
817        _window_adapter: &Rc<dyn WindowAdapter>,
818        _self_rc: &ItemRc,
819        mut geometry: LogicalRect,
820    ) -> LogicalRect {
821        geometry.size = LogicalSize::zero();
822        geometry
823    }
824
825    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
826        false
827    }
828}
829
830impl ItemConsts for FocusScope {
831    const cached_rendering_data_offset: const_field_offset::FieldOffset<
832        FocusScope,
833        CachedRenderingData,
834    > = FocusScope::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
835}
836
837#[repr(C)]
838#[derive(FieldOffsets, Default, SlintElement)]
839#[pin]
840pub struct SwipeGestureHandler {
841    pub enabled: Property<bool>,
842    pub handle_swipe_left: Property<bool>,
843    pub handle_swipe_right: Property<bool>,
844    pub handle_swipe_up: Property<bool>,
845    pub handle_swipe_down: Property<bool>,
846
847    pub moved: Callback<VoidArg>,
848    pub swiped: Callback<VoidArg>,
849    pub cancelled: Callback<VoidArg>,
850
851    pub pressed_position: Property<LogicalPosition>,
852    pub current_position: Property<LogicalPosition>,
853    pub swiping: Property<bool>,
854
855    // true when the cursor is pressed down and we haven't cancelled yet for another reason
856    pressed: Cell<bool>,
857    // capture_events: Cell<bool>,
858    /// FIXME: remove this
859    pub cached_rendering_data: CachedRenderingData,
860}
861
862impl Item for SwipeGestureHandler {
863    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
864
865    fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
866
867    fn layout_info(
868        self: Pin<&Self>,
869        _orientation: Orientation,
870        _cross_axis_constraint: Coord,
871        _window_adapter: &Rc<dyn WindowAdapter>,
872        _self_rc: &ItemRc,
873    ) -> LayoutInfo {
874        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
875    }
876
877    fn input_event_filter_before_children(
878        self: Pin<&Self>,
879        event: &MouseEvent,
880        _window_adapter: &Rc<dyn WindowAdapter>,
881        _self_rc: &ItemRc,
882        _: &mut MouseCursorInner,
883    ) -> InputEventFilterResult {
884        if !self.enabled() {
885            if self.pressed.get() {
886                self.cancel_impl();
887            }
888            return InputEventFilterResult::ForwardAndIgnore;
889        }
890
891        match event {
892            MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => {
893                Self::FIELD_OFFSETS
894                    .pressed_position()
895                    .apply_pin(self)
896                    .set(crate::lengths::logical_position_to_api(*position));
897                self.pressed.set(true);
898                InputEventFilterResult::DelayForwarding(
899                    super::flickable::FORWARD_DELAY.as_millis() as _
900                )
901            }
902            MouseEvent::Exit => {
903                self.cancel_impl();
904                InputEventFilterResult::ForwardAndIgnore
905            }
906            MouseEvent::Released { button: PointerEventButton::Left, .. } => {
907                if self.swiping() {
908                    InputEventFilterResult::Intercept
909                } else {
910                    self.pressed.set(false);
911                    InputEventFilterResult::ForwardEvent
912                }
913            }
914            MouseEvent::Moved { position, .. } => {
915                if self.swiping() {
916                    InputEventFilterResult::Intercept
917                } else if !self.pressed.get() {
918                    InputEventFilterResult::ForwardEvent
919                } else if self.is_over_threshold(position) {
920                    InputEventFilterResult::Intercept
921                } else {
922                    InputEventFilterResult::ForwardAndInterceptGrab
923                }
924            }
925            MouseEvent::Wheel { .. } => InputEventFilterResult::ForwardAndIgnore,
926            // Not the left button
927            MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
928                InputEventFilterResult::ForwardAndIgnore
929            }
930            MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. } => {
931                InputEventFilterResult::ForwardAndIgnore
932            }
933            MouseEvent::DragMove { .. } | MouseEvent::Drop { .. } => {
934                InputEventFilterResult::ForwardAndIgnore
935            }
936        }
937    }
938
939    fn input_event(
940        self: Pin<&Self>,
941        event: &MouseEvent,
942        _window_adapter: &Rc<dyn WindowAdapter>,
943        _self_rc: &ItemRc,
944        _: &mut MouseCursorInner,
945    ) -> InputEventResult {
946        match event {
947            MouseEvent::Pressed { .. } => InputEventResult::GrabMouse,
948            MouseEvent::Exit => {
949                self.cancel_impl();
950                InputEventResult::EventIgnored
951            }
952            MouseEvent::Released { position, .. } => {
953                if !self.pressed.get() && !self.swiping() {
954                    return InputEventResult::EventIgnored;
955                }
956                self.current_position.set(crate::lengths::logical_position_to_api(*position));
957                self.pressed.set(false);
958                if self.swiping() {
959                    Self::FIELD_OFFSETS.swiping().apply_pin(self).set(false);
960                    Self::FIELD_OFFSETS.swiped().apply_pin(self).call(&());
961                    InputEventResult::EventAccepted
962                } else {
963                    InputEventResult::EventIgnored
964                }
965            }
966            MouseEvent::Moved { position, .. } => {
967                if !self.pressed.get() {
968                    return InputEventResult::EventAccepted;
969                }
970                self.current_position.set(crate::lengths::logical_position_to_api(*position));
971                let mut swiping = self.swiping();
972                if !swiping && self.is_over_threshold(position) {
973                    Self::FIELD_OFFSETS.swiping().apply_pin(self).set(true);
974                    swiping = true;
975                }
976                Self::FIELD_OFFSETS.moved().apply_pin(self).call(&());
977                if swiping { InputEventResult::GrabMouse } else { InputEventResult::EventAccepted }
978            }
979            MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
980            MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. } => {
981                InputEventResult::EventIgnored
982            }
983            MouseEvent::DragMove { .. } | MouseEvent::Drop { .. } => InputEventResult::EventIgnored,
984        }
985    }
986
987    fn capture_key_event(
988        self: Pin<&Self>,
989        _: &InternalKeyEvent,
990        _window_adapter: &Rc<dyn WindowAdapter>,
991        _self_rc: &ItemRc,
992    ) -> KeyEventResult {
993        KeyEventResult::EventIgnored
994    }
995
996    fn key_event(
997        self: Pin<&Self>,
998        _event: &InternalKeyEvent,
999        _window_adapter: &Rc<dyn WindowAdapter>,
1000        _self_rc: &ItemRc,
1001    ) -> KeyEventResult {
1002        KeyEventResult::EventIgnored
1003    }
1004
1005    fn focus_event(
1006        self: Pin<&Self>,
1007        _: &FocusEvent,
1008        _window_adapter: &Rc<dyn WindowAdapter>,
1009        _self_rc: &ItemRc,
1010    ) -> FocusEventResult {
1011        FocusEventResult::FocusIgnored
1012    }
1013
1014    fn render(
1015        self: Pin<&Self>,
1016        _backend: &mut ItemRendererRef,
1017        _self_rc: &ItemRc,
1018        _size: LogicalSize,
1019    ) -> RenderingResult {
1020        RenderingResult::ContinueRenderingChildren
1021    }
1022
1023    fn bounding_rect(
1024        self: core::pin::Pin<&Self>,
1025        _window_adapter: &Rc<dyn WindowAdapter>,
1026        _self_rc: &ItemRc,
1027        mut geometry: LogicalRect,
1028    ) -> LogicalRect {
1029        geometry.size = LogicalSize::zero();
1030        geometry
1031    }
1032
1033    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
1034        false
1035    }
1036}
1037
1038impl ItemConsts for SwipeGestureHandler {
1039    const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
1040        Self::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
1041}
1042
1043impl SwipeGestureHandler {
1044    pub fn cancel(self: Pin<&Self>, _: &Rc<dyn WindowAdapter>, _: &ItemRc) {
1045        self.cancel_impl();
1046    }
1047
1048    fn cancel_impl(self: Pin<&Self>) {
1049        if !self.pressed.replace(false) {
1050            debug_assert!(!self.swiping());
1051            return;
1052        }
1053        if self.swiping() {
1054            Self::FIELD_OFFSETS.swiping().apply_pin(self).set(false);
1055            Self::FIELD_OFFSETS.cancelled().apply_pin(self).call(&());
1056        }
1057    }
1058
1059    fn is_over_threshold(self: Pin<&Self>, position: &LogicalPoint) -> bool {
1060        let pressed_pos = self.pressed_position();
1061        let dx = position.x - pressed_pos.x as Coord;
1062        let dy = position.y - pressed_pos.y as Coord;
1063        let threshold = super::flickable::DISTANCE_THRESHOLD.get();
1064        (self.handle_swipe_down() && dy > threshold && dy > dx.abs() / 2 as Coord)
1065            || (self.handle_swipe_up() && dy < -threshold && dy < -dx.abs() / 2 as Coord)
1066            || (self.handle_swipe_left() && dx < -threshold && dx < -dy.abs() / 2 as Coord)
1067            || (self.handle_swipe_right() && dx > threshold && dx > dy.abs() / 2 as Coord)
1068    }
1069}
1070
1071#[cfg(feature = "ffi")]
1072mod ffi {
1073    use super::*;
1074
1075    #[unsafe(no_mangle)]
1076    pub unsafe extern "C" fn slint_swipegesturehandler_cancel(
1077        s: Pin<&SwipeGestureHandler>,
1078        window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
1079        self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
1080        self_index: u32,
1081    ) {
1082        unsafe {
1083            let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
1084            let self_rc = ItemRc::new(self_component.clone(), self_index);
1085            s.cancel(window_adapter, &self_rc);
1086        }
1087    }
1088
1089    /// # Safety
1090    /// This must be called using a non-null pointer pointing to a chunk of memory big enough to
1091    /// hold a MaybeKeybindingList
1092    #[unsafe(no_mangle)]
1093    pub unsafe extern "C" fn slint_maybe_key_binding_list_init(list: *mut MaybeKeyBindingList) {
1094        unsafe {
1095            core::ptr::write(list, MaybeKeyBindingList::default());
1096        }
1097    }
1098
1099    /// # Safety
1100    /// This must be called using a non-null pointer pointing to an initialized MaybeKeybindingList
1101    #[unsafe(no_mangle)]
1102    pub unsafe extern "C" fn slint_maybe_key_binding_list_free(list: *mut MaybeKeyBindingList) {
1103        unsafe { core::ptr::drop_in_place(list) };
1104    }
1105}
1106
1107/// The implementation of the `ScaleRotateGestureHandler` element.
1108///
1109/// Provides an API surface for platform-recognized pinch gesture events.
1110/// Receives `MouseEvent::PinchGesture` events via the normal mouse event
1111/// tree-walk and exposes cumulative scale, center position, and lifecycle callbacks.
1112#[repr(C)]
1113#[derive(FieldOffsets, Default, SlintElement)]
1114#[pin]
1115pub struct ScaleRotateGestureHandler {
1116    pub enabled: Property<bool>,
1117
1118    // Output properties
1119    pub active: Property<bool>,
1120    /// Cumulative scale factor relative to gesture start. Always 1.0 when the
1121    /// gesture starts, then updated as the gesture progresses (e.g., 2.0 means
1122    /// doubled, 0.5 means halved).
1123    pub scale: Property<f32>,
1124    /// Cumulative rotation in degrees relative to gesture start. Always 0.0 when
1125    /// the gesture starts.
1126    pub rotation: Property<f32>,
1127    pub center: Property<LogicalPosition>,
1128
1129    // Callbacks
1130    pub started: Callback<VoidArg>,
1131    pub updated: Callback<VoidArg>,
1132    pub ended: Callback<VoidArg>,
1133    pub cancelled: Callback<VoidArg>,
1134
1135    /// FIXME: remove this
1136    pub cached_rendering_data: CachedRenderingData,
1137}
1138
1139impl Item for ScaleRotateGestureHandler {
1140    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {
1141        self.scale.set(1.0);
1142    }
1143
1144    fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
1145
1146    fn layout_info(
1147        self: Pin<&Self>,
1148        _orientation: Orientation,
1149        _cross_axis_constraint: Coord,
1150        _window_adapter: &Rc<dyn WindowAdapter>,
1151        _self_rc: &ItemRc,
1152    ) -> LayoutInfo {
1153        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
1154    }
1155
1156    fn input_event_filter_before_children(
1157        self: Pin<&Self>,
1158        event: &MouseEvent,
1159        _window_adapter: &Rc<dyn WindowAdapter>,
1160        _self_rc: &ItemRc,
1161        _: &mut MouseCursorInner,
1162    ) -> InputEventFilterResult {
1163        match event {
1164            // Forward gesture events so inner handlers get first shot
1165            MouseEvent::PinchGesture { .. } | MouseEvent::RotationGesture { .. }
1166                if self.enabled() =>
1167            {
1168                InputEventFilterResult::ForwardEvent
1169            }
1170            // While a gesture is active, intercept non-gesture events to
1171            // prevent Flickable and other items from processing them concurrently.
1172            _ if self.active() => InputEventFilterResult::Intercept,
1173            _ => InputEventFilterResult::ForwardAndIgnore,
1174        }
1175    }
1176
1177    fn input_event(
1178        self: Pin<&Self>,
1179        event: &MouseEvent,
1180        _window_adapter: &Rc<dyn WindowAdapter>,
1181        _self_rc: &ItemRc,
1182        _: &mut MouseCursorInner,
1183    ) -> InputEventResult {
1184        use crate::input::TouchPhase;
1185        match event {
1186            MouseEvent::PinchGesture { delta, phase, position } => {
1187                if !self.enabled() {
1188                    if self.active() {
1189                        self.cancel_impl();
1190                    }
1191                    return InputEventResult::EventIgnored;
1192                }
1193                let new_scale = self.scale() * (1.0 + delta);
1194                Self::FIELD_OFFSETS.scale().apply_pin(self).set(new_scale);
1195                let center = crate::lengths::logical_position_to_api(*position);
1196                Self::FIELD_OFFSETS.center().apply_pin(self).set(center);
1197                match phase {
1198                    TouchPhase::Started => {
1199                        if !self.active() {
1200                            Self::FIELD_OFFSETS.active().apply_pin(self).set(true);
1201                            Self::FIELD_OFFSETS.started().apply_pin(self).call(&());
1202                        }
1203                        InputEventResult::GrabMouse
1204                    }
1205                    TouchPhase::Moved => {
1206                        if !self.active() {
1207                            return InputEventResult::EventIgnored;
1208                        }
1209                        Self::FIELD_OFFSETS.updated().apply_pin(self).call(&());
1210                        InputEventResult::GrabMouse
1211                    }
1212                    TouchPhase::Ended => self.end_impl(),
1213                    TouchPhase::Cancelled => {
1214                        self.cancel_impl();
1215                        InputEventResult::EventAccepted
1216                    }
1217                }
1218            }
1219            MouseEvent::RotationGesture { delta, phase, position } => {
1220                if !self.enabled() {
1221                    return InputEventResult::EventIgnored;
1222                }
1223                let center = crate::lengths::logical_position_to_api(*position);
1224                Self::FIELD_OFFSETS.center().apply_pin(self).set(center);
1225                let new_rotation = self.rotation() + delta;
1226                Self::FIELD_OFFSETS.rotation().apply_pin(self).set(new_rotation);
1227                match phase {
1228                    TouchPhase::Started => {
1229                        if !self.active() {
1230                            Self::FIELD_OFFSETS.active().apply_pin(self).set(true);
1231                            Self::FIELD_OFFSETS.started().apply_pin(self).call(&());
1232                        }
1233                        InputEventResult::GrabMouse
1234                    }
1235                    TouchPhase::Moved => {
1236                        if !self.active() {
1237                            return InputEventResult::EventIgnored;
1238                        }
1239                        Self::FIELD_OFFSETS.updated().apply_pin(self).call(&());
1240                        InputEventResult::GrabMouse
1241                    }
1242                    TouchPhase::Ended => self.end_impl(),
1243                    TouchPhase::Cancelled => {
1244                        self.cancel_impl();
1245                        InputEventResult::EventAccepted
1246                    }
1247                }
1248            }
1249            // Grab mouse during active gesture to maintain exclusivity.
1250            _ if self.active() => InputEventResult::GrabMouse,
1251            _ => InputEventResult::EventIgnored,
1252        }
1253    }
1254
1255    fn capture_key_event(
1256        self: Pin<&Self>,
1257        _: &InternalKeyEvent,
1258        _window_adapter: &Rc<dyn WindowAdapter>,
1259        _self_rc: &ItemRc,
1260    ) -> KeyEventResult {
1261        KeyEventResult::EventIgnored
1262    }
1263
1264    fn key_event(
1265        self: Pin<&Self>,
1266        _event: &InternalKeyEvent,
1267        _window_adapter: &Rc<dyn WindowAdapter>,
1268        _self_rc: &ItemRc,
1269    ) -> KeyEventResult {
1270        KeyEventResult::EventIgnored
1271    }
1272
1273    fn focus_event(
1274        self: Pin<&Self>,
1275        _: &FocusEvent,
1276        _window_adapter: &Rc<dyn WindowAdapter>,
1277        _self_rc: &ItemRc,
1278    ) -> FocusEventResult {
1279        FocusEventResult::FocusIgnored
1280    }
1281
1282    fn render(
1283        self: Pin<&Self>,
1284        _backend: &mut ItemRendererRef,
1285        _self_rc: &ItemRc,
1286        _size: LogicalSize,
1287    ) -> RenderingResult {
1288        RenderingResult::ContinueRenderingChildren
1289    }
1290
1291    fn bounding_rect(
1292        self: core::pin::Pin<&Self>,
1293        _window_adapter: &Rc<dyn WindowAdapter>,
1294        _self_rc: &ItemRc,
1295        mut geometry: LogicalRect,
1296    ) -> LogicalRect {
1297        geometry.size = LogicalSize::zero();
1298        geometry
1299    }
1300
1301    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
1302        false
1303    }
1304}
1305
1306impl ItemConsts for ScaleRotateGestureHandler {
1307    const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
1308        Self::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
1309}
1310
1311impl ScaleRotateGestureHandler {
1312    fn cancel_impl(self: Pin<&Self>) {
1313        if !self.active() {
1314            return;
1315        }
1316        Self::FIELD_OFFSETS.active().apply_pin(self).set(false);
1317        Self::FIELD_OFFSETS.cancelled().apply_pin(self).call(&());
1318        // Reset after the callback so handlers can read the last known values
1319        // to animate back smoothly, matching the pattern where `ended` leaves
1320        // scale/rotation at their final values.
1321        Self::FIELD_OFFSETS.scale().apply_pin(self).set(1.0);
1322        Self::FIELD_OFFSETS.rotation().apply_pin(self).set(0.0);
1323    }
1324
1325    fn end_impl(self: Pin<&Self>) -> InputEventResult {
1326        if !self.active() {
1327            return InputEventResult::EventIgnored;
1328        }
1329        Self::FIELD_OFFSETS.ended().apply_pin(self).call(&());
1330        Self::FIELD_OFFSETS.active().apply_pin(self).set(false);
1331        Self::FIELD_OFFSETS.scale().apply_pin(self).set(1.0);
1332        Self::FIELD_OFFSETS.rotation().apply_pin(self).set(0.0);
1333        InputEventResult::EventAccepted
1334    }
1335}