i_slint_core/items/
drag_n_drop.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    DropEvent, Item, ItemConsts, ItemRc, MouseCursor, PointerEventButton, RenderingResult,
6};
7use crate::input::{
8    FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
9    KeyEventResult, MouseEvent,
10};
11use crate::item_rendering::{CachedRenderingData, ItemRenderer};
12use crate::layout::{LayoutInfo, Orientation};
13use crate::lengths::{LogicalPoint, LogicalRect, LogicalSize};
14#[cfg(feature = "rtti")]
15use crate::rtti::*;
16use crate::window::WindowAdapter;
17use crate::{Callback, Property, SharedString};
18use alloc::rc::Rc;
19use const_field_offset::FieldOffsets;
20use core::cell::Cell;
21use core::pin::Pin;
22use i_slint_core_macros::*;
23
24pub type DropEventArg = (DropEvent,);
25
26#[repr(C)]
27#[derive(FieldOffsets, Default, SlintElement)]
28#[pin]
29/// The implementation of the `DragArea` element
30pub struct DragArea {
31    pub enabled: Property<bool>,
32    pub mime_type: Property<SharedString>,
33    pub data: Property<SharedString>,
34    pressed: Cell<bool>,
35    pressed_position: Cell<LogicalPoint>,
36    pub cached_rendering_data: CachedRenderingData,
37}
38
39impl Item for DragArea {
40    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
41
42    fn layout_info(
43        self: Pin<&Self>,
44        _: Orientation,
45        _window_adapter: &Rc<dyn WindowAdapter>,
46        _self_rc: &ItemRc,
47    ) -> LayoutInfo {
48        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
49    }
50
51    fn input_event_filter_before_children(
52        self: Pin<&Self>,
53        event: &MouseEvent,
54        _window_adapter: &Rc<dyn WindowAdapter>,
55        _self_rc: &ItemRc,
56    ) -> InputEventFilterResult {
57        if !self.enabled() {
58            self.cancel();
59            return InputEventFilterResult::ForwardAndIgnore;
60        }
61
62        match event {
63            MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => {
64                self.pressed_position.set(*position);
65                self.pressed.set(true);
66                InputEventFilterResult::ForwardAndInterceptGrab
67            }
68            MouseEvent::Exit => {
69                self.cancel();
70                InputEventFilterResult::ForwardAndIgnore
71            }
72            MouseEvent::Released { button: PointerEventButton::Left, .. } => {
73                self.pressed.set(false);
74                InputEventFilterResult::ForwardAndIgnore
75            }
76
77            MouseEvent::Moved { position } => {
78                if !self.pressed.get() {
79                    InputEventFilterResult::ForwardEvent
80                } else {
81                    let pressed_pos = self.pressed_position.get();
82                    let dx = (position.x - pressed_pos.x).abs();
83                    let dy = (position.y - pressed_pos.y).abs();
84                    let threshold = super::flickable::DISTANCE_THRESHOLD.get();
85                    if dy > threshold || dx > threshold {
86                        InputEventFilterResult::Intercept
87                    } else {
88                        InputEventFilterResult::ForwardAndInterceptGrab
89                    }
90                }
91            }
92            MouseEvent::Wheel { .. } => InputEventFilterResult::ForwardAndIgnore,
93            // Not the left button
94            MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
95                InputEventFilterResult::ForwardAndIgnore
96            }
97            MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
98                InputEventFilterResult::ForwardAndIgnore
99            }
100        }
101    }
102
103    fn input_event(
104        self: Pin<&Self>,
105        event: &MouseEvent,
106        _window_adapter: &Rc<dyn WindowAdapter>,
107        _self_rc: &ItemRc,
108    ) -> InputEventResult {
109        match event {
110            MouseEvent::Pressed { .. } => InputEventResult::EventAccepted,
111            MouseEvent::Exit => {
112                self.cancel();
113                InputEventResult::EventIgnored
114            }
115            MouseEvent::Released { .. } => {
116                self.cancel();
117                InputEventResult::EventIgnored
118            }
119            MouseEvent::Moved { position } => {
120                if !self.pressed.get() || !self.enabled() {
121                    return InputEventResult::EventIgnored;
122                }
123                let pressed_pos = self.pressed_position.get();
124                let dx = (position.x - pressed_pos.x).abs();
125                let dy = (position.y - pressed_pos.y).abs();
126                let threshold = super::flickable::DISTANCE_THRESHOLD.get();
127                let start_drag = dx > threshold || dy > threshold;
128                if start_drag {
129                    self.pressed.set(false);
130                    InputEventResult::StartDrag
131                } else {
132                    InputEventResult::EventAccepted
133                }
134            }
135            MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
136            MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
137        }
138    }
139
140    fn capture_key_event(
141        self: Pin<&Self>,
142        _: &KeyEvent,
143        _window_adapter: &Rc<dyn WindowAdapter>,
144        _self_rc: &ItemRc,
145    ) -> KeyEventResult {
146        KeyEventResult::EventIgnored
147    }
148
149    fn key_event(
150        self: Pin<&Self>,
151        _: &KeyEvent,
152        _window_adapter: &Rc<dyn WindowAdapter>,
153        _self_rc: &ItemRc,
154    ) -> KeyEventResult {
155        KeyEventResult::EventIgnored
156    }
157
158    fn focus_event(
159        self: Pin<&Self>,
160        _: &FocusEvent,
161        _window_adapter: &Rc<dyn WindowAdapter>,
162        _self_rc: &ItemRc,
163    ) -> FocusEventResult {
164        FocusEventResult::FocusIgnored
165    }
166
167    fn render(
168        self: Pin<&Self>,
169        _: &mut &mut dyn ItemRenderer,
170        _self_rc: &ItemRc,
171        _size: LogicalSize,
172    ) -> RenderingResult {
173        RenderingResult::ContinueRenderingChildren
174    }
175
176    fn bounding_rect(
177        self: core::pin::Pin<&Self>,
178        _window_adapter: &Rc<dyn WindowAdapter>,
179        _self_rc: &ItemRc,
180        mut geometry: LogicalRect,
181    ) -> LogicalRect {
182        geometry.size = LogicalSize::zero();
183        geometry
184    }
185
186    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
187        false
188    }
189}
190
191impl ItemConsts for DragArea {
192    const cached_rendering_data_offset: const_field_offset::FieldOffset<
193        DragArea,
194        CachedRenderingData,
195    > = DragArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
196}
197
198impl DragArea {
199    fn cancel(self: Pin<&Self>) {
200        self.pressed.set(false)
201    }
202}
203
204#[repr(C)]
205#[derive(FieldOffsets, Default, SlintElement)]
206#[pin]
207/// The implementation of the `DropArea` element
208pub struct DropArea {
209    pub enabled: Property<bool>,
210    pub contains_drag: Property<bool>,
211    pub can_drop: Callback<DropEventArg, bool>,
212    pub dropped: Callback<DropEventArg>,
213
214    pub cached_rendering_data: CachedRenderingData,
215}
216
217impl Item for DropArea {
218    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
219
220    fn layout_info(
221        self: Pin<&Self>,
222        _: Orientation,
223        _window_adapter: &Rc<dyn WindowAdapter>,
224        _self_rc: &ItemRc,
225    ) -> LayoutInfo {
226        LayoutInfo { stretch: 1., ..LayoutInfo::default() }
227    }
228
229    fn input_event_filter_before_children(
230        self: Pin<&Self>,
231        _: &MouseEvent,
232        _window_adapter: &Rc<dyn WindowAdapter>,
233        _self_rc: &ItemRc,
234    ) -> InputEventFilterResult {
235        InputEventFilterResult::ForwardEvent
236    }
237
238    fn input_event(
239        self: Pin<&Self>,
240        event: &MouseEvent,
241        window_adapter: &Rc<dyn WindowAdapter>,
242        _self_rc: &ItemRc,
243    ) -> InputEventResult {
244        if !self.enabled() {
245            return InputEventResult::EventIgnored;
246        }
247        match event {
248            MouseEvent::DragMove(event) => {
249                let r = Self::FIELD_OFFSETS.can_drop.apply_pin(self).call(&(event.clone(),));
250                if r {
251                    self.contains_drag.set(true);
252                    if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
253                        window_adapter.set_mouse_cursor(MouseCursor::Copy);
254                    }
255                    InputEventResult::EventAccepted
256                } else {
257                    self.contains_drag.set(false);
258                    InputEventResult::EventIgnored
259                }
260            }
261            MouseEvent::Drop(event) => {
262                self.contains_drag.set(false);
263                Self::FIELD_OFFSETS.dropped.apply_pin(self).call(&(event.clone(),));
264                InputEventResult::EventAccepted
265            }
266            MouseEvent::Exit => {
267                self.contains_drag.set(false);
268                InputEventResult::EventIgnored
269            }
270            _ => InputEventResult::EventIgnored,
271        }
272    }
273
274    fn capture_key_event(
275        self: Pin<&Self>,
276        _: &KeyEvent,
277        _window_adapter: &Rc<dyn WindowAdapter>,
278        _self_rc: &ItemRc,
279    ) -> KeyEventResult {
280        KeyEventResult::EventIgnored
281    }
282
283    fn key_event(
284        self: Pin<&Self>,
285        _: &KeyEvent,
286        _window_adapter: &Rc<dyn WindowAdapter>,
287        _self_rc: &ItemRc,
288    ) -> KeyEventResult {
289        KeyEventResult::EventIgnored
290    }
291
292    fn focus_event(
293        self: Pin<&Self>,
294        _: &FocusEvent,
295        _window_adapter: &Rc<dyn WindowAdapter>,
296        _self_rc: &ItemRc,
297    ) -> FocusEventResult {
298        FocusEventResult::FocusIgnored
299    }
300
301    fn render(
302        self: Pin<&Self>,
303        _: &mut &mut dyn ItemRenderer,
304        _self_rc: &ItemRc,
305        _size: LogicalSize,
306    ) -> RenderingResult {
307        RenderingResult::ContinueRenderingChildren
308    }
309
310    fn bounding_rect(
311        self: core::pin::Pin<&Self>,
312        _window_adapter: &Rc<dyn WindowAdapter>,
313        _self_rc: &ItemRc,
314        mut geometry: LogicalRect,
315    ) -> LogicalRect {
316        geometry.size = LogicalSize::zero();
317        geometry
318    }
319
320    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
321        false
322    }
323}
324
325impl ItemConsts for DropArea {
326    const cached_rendering_data_offset: const_field_offset::FieldOffset<
327        DropArea,
328        CachedRenderingData,
329    > = DropArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
330}