Skip to main content

ferroui_base/input/
drag_drop_device.rs

1use super::raw::{IDragDropDevice, IRawInputEventArgs, RawDragEvent, RawDragEventType};
2use super::{DragDrop, DragDropEffects, DragEventArgs, IDataTransfer, IInputDevice, IInputRoot, KeyModifiers};
3use crate::interactivity::{Interactive, RoutedEvent};
4use crate::{Point, Ref};
5use std::any::Any;
6use std::cell::RefCell;
7use std::rc::Rc;
8
9crate::ferro_thread_local! {
10    static INSTANCE: Rc<DragDropDevice> = Rc::new(DragDropDevice { last_target: RefCell::new(None) });
11}
12
13/// The input device that turns the raw drag events of the platform into
14/// the routed drag-and-drop events of [`DragDrop`].
15///
16/// This is an implementation detail of the platform backends.
17pub struct DragDropDevice {
18    last_target: RefCell<Option<Ref<Interactive>>>,
19}
20
21impl DragDropDevice {
22    /// The drag-and-drop device of the current thread.
23    pub fn instance() -> Rc<DragDropDevice> {
24        INSTANCE.with(Rc::clone)
25    }
26
27    fn last_target(&self) -> Option<Ref<Interactive>> {
28        self.last_target.borrow().clone()
29    }
30
31    fn set_last_target(&self, value: Option<Ref<Interactive>>) {
32        let old = self.last_target.replace(value);
33        drop(old);
34    }
35
36    fn get_target(root: &Rc<dyn IInputRoot>, local: Point) -> Option<Ref<Interactive>> {
37        let hit = root.root_element().input_hit_test(local)?;
38        let target = hit.get_self_and_visual_ancestors().find_map(|visual| visual.downcast::<Interactive>().ok())?;
39
40        if DragDrop::get_allow_drop(&target) {
41            Some(target)
42        } else {
43            None
44        }
45    }
46
47    fn raise_drag_event(
48        target: Option<&Ref<Interactive>>,
49        input_root: &Rc<dyn IInputRoot>,
50        point: Point,
51        routed_event: &RoutedEvent<DragEventArgs>,
52        operation: DragDropEffects,
53        data_transfer: &Rc<dyn IDataTransfer>,
54        modifiers: KeyModifiers,
55    ) -> DragDropEffects {
56        let Some(target) = target else {
57            return DragDropEffects::NONE;
58        };
59
60        let Some(p) = input_root.root_element().translate_point(point, target) else {
61            return DragDropEffects::NONE;
62        };
63
64        let args = DragEventArgs::new(Some(routed_event), data_transfer.clone(), target.clone(), p, modifiers);
65        args.set_drag_effects(operation);
66
67        target.raise_event(&args);
68        args.drag_effects()
69    }
70
71    fn drag_enter(
72        &self,
73        input_root: &Rc<dyn IInputRoot>,
74        point: Point,
75        data: &Rc<dyn IDataTransfer>,
76        effects: DragDropEffects,
77        modifiers: KeyModifiers,
78    ) -> DragDropEffects {
79        let target = Self::get_target(input_root, point);
80        self.set_last_target(target.clone());
81        Self::raise_drag_event(
82            target.as_ref(),
83            input_root,
84            point,
85            DragDrop::drag_enter_event(),
86            effects,
87            data,
88            modifiers,
89        )
90    }
91
92    fn drag_over(
93        &self,
94        input_root: &Rc<dyn IInputRoot>,
95        point: Point,
96        data: &Rc<dyn IDataTransfer>,
97        effects: DragDropEffects,
98        modifiers: KeyModifiers,
99    ) -> DragDropEffects {
100        let target = Self::get_target(input_root, point);
101        let last_target = self.last_target();
102
103        if target == last_target {
104            return Self::raise_drag_event(
105                target.as_ref(),
106                input_root,
107                point,
108                DragDrop::drag_over_event(),
109                effects,
110                data,
111                modifiers,
112            );
113        }
114
115        // The last target is updated even when a handler panics.
116        let _finally = Finally(|| self.set_last_target(target.clone()));
117
118        if last_target.is_some() {
119            Self::raise_drag_event(
120                last_target.as_ref(),
121                input_root,
122                point,
123                DragDrop::drag_leave_event(),
124                effects,
125                data,
126                modifiers,
127            );
128        }
129
130        Self::raise_drag_event(
131            target.as_ref(),
132            input_root,
133            point,
134            DragDrop::drag_enter_event(),
135            effects,
136            data,
137            modifiers,
138        )
139    }
140
141    fn drag_leave(
142        &self,
143        input_root: &Rc<dyn IInputRoot>,
144        point: Point,
145        data: &Rc<dyn IDataTransfer>,
146        effects: DragDropEffects,
147        modifiers: KeyModifiers,
148    ) {
149        let last_target = self.last_target();
150        if last_target.is_none() {
151            return;
152        }
153
154        let _finally = Finally(|| self.set_last_target(None));
155
156        Self::raise_drag_event(
157            last_target.as_ref(),
158            input_root,
159            point,
160            DragDrop::drag_leave_event(),
161            effects,
162            data,
163            modifiers,
164        );
165    }
166
167    fn drop(
168        &self,
169        input_root: &Rc<dyn IInputRoot>,
170        point: Point,
171        data: &Rc<dyn IDataTransfer>,
172        effects: DragDropEffects,
173        modifiers: KeyModifiers,
174    ) -> DragDropEffects {
175        let last_target = self.last_target();
176        let _finally = Finally(|| self.set_last_target(None));
177
178        Self::raise_drag_event(last_target.as_ref(), input_root, point, DragDrop::drop_event(), effects, data, modifiers)
179    }
180
181    fn process_raw_drag_event(&self, e: &RawDragEvent) {
182        let (root, location, data, effects, modifiers) =
183            (e.root(), e.location(), e.data_transfer(), e.effects(), e.key_modifiers());
184
185        match e.type_() {
186            RawDragEventType::DragEnter => e.set_effects(self.drag_enter(root, location, data, effects, modifiers)),
187            RawDragEventType::DragOver => e.set_effects(self.drag_over(root, location, data, effects, modifiers)),
188            RawDragEventType::DragLeave => self.drag_leave(root, location, data, effects, modifiers),
189            RawDragEventType::Drop => e.set_effects(self.drop(root, location, data, effects, modifiers)),
190        }
191    }
192}
193
194/// Runs a closure when dropped: the `finally` block of the reference
195/// implementation.
196struct Finally<F: FnMut()>(F);
197
198impl<F: FnMut()> Drop for Finally<F> {
199    fn drop(&mut self) {
200        (self.0)()
201    }
202}
203
204impl IInputDevice for DragDropDevice {
205    fn process_raw_event(&self, e: &dyn IRawInputEventArgs) {
206        if !e.handled() {
207            if let Some(margs) = e.downcast_ref::<RawDragEvent>() {
208                self.process_raw_drag_event(margs);
209            }
210        }
211    }
212
213    fn as_any(&self) -> &dyn Any {
214        self
215    }
216}
217
218impl IDragDropDevice for DragDropDevice {}