freya_components/
drag_drop.rs

1use freya_core::{
2    prelude::*,
3    scope_id::ScopeId,
4};
5use torin::prelude::*;
6
7fn use_drag<T: 'static>() -> State<Option<T>> {
8    match try_consume_root_context() {
9        Some(s) => s,
10        None => {
11            let state = State::<Option<T>>::create_in_scope(None, ScopeId::ROOT);
12            provide_context_for_scope_id(state, ScopeId::ROOT);
13            state
14        }
15    }
16}
17
18/// Properties for the [`DragZone`] component.
19#[derive(Clone, PartialEq)]
20pub struct DragZone<T: Clone + 'static + PartialEq> {
21    /// Element visible when dragging the element. This follows the cursor.
22    drag_element: Option<Element>,
23    /// Inner children for the DropZone.
24    children: Element,
25    /// Data that will be handled to the destination [`DropZone`].
26    data: T,
27    /// Show the children when dragging. Defaults to `true`.
28    show_while_dragging: bool,
29    key: DiffKey,
30}
31
32impl<T: Clone + PartialEq + 'static> KeyExt for DragZone<T> {
33    fn write_key(&mut self) -> &mut DiffKey {
34        &mut self.key
35    }
36}
37
38impl<T: Clone + PartialEq + 'static> DragZone<T> {
39    pub fn new(data: T, children: impl Into<Element>) -> Self {
40        Self {
41            data,
42            children: children.into(),
43            drag_element: None,
44            show_while_dragging: true,
45            key: DiffKey::default(),
46        }
47    }
48
49    pub fn show_while_dragging(mut self, show_while_dragging: bool) -> Self {
50        self.show_while_dragging = show_while_dragging;
51        self
52    }
53
54    pub fn drag_element(mut self, drag_element: impl Into<Element>) -> Self {
55        self.drag_element = Some(drag_element.into());
56        self
57    }
58}
59
60impl<T: Clone + PartialEq> Component for DragZone<T> {
61    fn render(&self) -> impl IntoElement {
62        let mut drags = use_drag::<T>();
63        let mut position = use_state::<Option<CursorPoint>>(|| None);
64        let data = self.data.clone();
65
66        let on_global_mouse_move = move |e: Event<MouseEventData>| {
67            if position.read().is_some() {
68                position.set(Some(e.global_location));
69            }
70        };
71
72        let on_pointer_down = move |e: Event<PointerEventData>| {
73            if e.data().button() != Some(MouseButton::Left) {
74                return;
75            }
76            position.set(Some(e.global_location()));
77            *drags.write() = Some(data.clone());
78        };
79
80        let on_global_mouse_up = move |_: Event<MouseEventData>| {
81            if position.read().is_some() {
82                position.set(None);
83                *drags.write() = None;
84            }
85        };
86
87        rect()
88            .on_global_mouse_up(on_global_mouse_up)
89            .on_global_mouse_move(on_global_mouse_move)
90            .on_pointer_down(on_pointer_down)
91            .maybe_child((position.read().zip(self.drag_element.clone())).map(
92                |(position, drag_element)| {
93                    let (x, y) = position.to_f32().to_tuple();
94                    rect()
95                        .position(Position::new_global())
96                        .width(Size::px(0.))
97                        .height(Size::px(0.))
98                        // Extend by 1. so that the cursor click can reach the drop zone
99                        .offset_x(x + 1.)
100                        .offset_y(y + 1.)
101                        .child(drag_element)
102                },
103            ))
104            .maybe_child(
105                (self.show_while_dragging || position.read().is_none())
106                    .then(|| self.children.clone()),
107            )
108    }
109
110    fn render_key(&self) -> DiffKey {
111        self.key.clone().or(self.default_key())
112    }
113}
114
115#[derive(PartialEq, Clone)]
116pub struct DropZone<T: 'static + PartialEq + Clone> {
117    children: Element,
118    on_drop: EventHandler<T>,
119    width: Size,
120    height: Size,
121    key: DiffKey,
122}
123
124impl<T: Clone + PartialEq + 'static> KeyExt for DropZone<T> {
125    fn write_key(&mut self) -> &mut DiffKey {
126        &mut self.key
127    }
128}
129
130impl<T: PartialEq + Clone + 'static> DropZone<T> {
131    pub fn new(children: impl Into<Element>, on_drop: impl Into<EventHandler<T>>) -> Self {
132        Self {
133            children: children.into(),
134            on_drop: on_drop.into(),
135            width: Size::auto(),
136            height: Size::auto(),
137            key: DiffKey::default(),
138        }
139    }
140}
141
142impl<T: Clone + PartialEq + 'static> Component for DropZone<T> {
143    fn render(&self) -> impl IntoElement {
144        let mut drags = use_drag::<T>();
145        let on_drop = self.on_drop.clone();
146
147        let on_mouse_up = move |e: Event<MouseEventData>| {
148            e.stop_propagation();
149            if let Some(current_drags) = &*drags.read() {
150                on_drop.call(current_drags.clone());
151            }
152            if drags.read().is_some() {
153                *drags.write() = None;
154            }
155        };
156
157        rect()
158            .on_mouse_up(on_mouse_up)
159            .width(self.width.clone())
160            .height(self.height.clone())
161            .child(self.children.clone())
162    }
163
164    fn render_key(&self) -> DiffKey {
165        self.key.clone().or(self.default_key())
166    }
167}