Skip to main content

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