freya_components/
drag_drop.rs1use freya_core::{
2 prelude::*,
3 scope_id::ScopeId,
4};
5use torin::prelude::*;
6
7#[derive(Clone, Copy)]
8enum DragPhase {
9 Idle,
10 Pressing(CursorPoint),
11 Dragging(CursorPoint),
12}
13
14pub fn use_drag<T: 'static>() -> State<Option<T>> {
20 match try_consume_root_context() {
21 Some(s) => s,
22 None => {
23 let state = State::<Option<T>>::create_in_scope(None, ScopeId::ROOT);
24 provide_context_for_scope_id(state, ScopeId::ROOT);
25 state
26 }
27 }
28}
29
30#[derive(Clone, PartialEq)]
32pub struct DragZone<T: Clone + 'static + PartialEq> {
33 drag_element: Option<Element>,
35 children: Element,
37 data: T,
39 show_while_dragging: bool,
41 drag_threshold: f64,
43 key: DiffKey,
44}
45
46impl<T: Clone + PartialEq + 'static> KeyExt for DragZone<T> {
47 fn write_key(&mut self) -> &mut DiffKey {
48 &mut self.key
49 }
50}
51
52impl<T: Clone + PartialEq + 'static> DragZone<T> {
53 pub fn new(data: T, children: impl Into<Element>) -> Self {
54 Self {
55 data,
56 children: children.into(),
57 drag_element: None,
58 show_while_dragging: true,
59 drag_threshold: 4.0,
60 key: DiffKey::default(),
61 }
62 }
63
64 pub fn show_while_dragging(mut self, show_while_dragging: bool) -> Self {
65 self.show_while_dragging = show_while_dragging;
66 self
67 }
68
69 pub fn drag_element(mut self, drag_element: impl Into<Element>) -> Self {
70 self.drag_element = Some(drag_element.into());
71 self
72 }
73
74 pub fn drag_threshold(mut self, drag_threshold: f64) -> Self {
75 self.drag_threshold = drag_threshold;
76 self
77 }
78}
79
80impl<T: Clone + PartialEq> Component for DragZone<T> {
81 fn render(&self) -> impl IntoElement {
82 let mut drags = use_drag::<T>();
83 let mut phase = use_state(|| DragPhase::Idle);
84 let data = self.data.clone();
85 let drag_threshold = self.drag_threshold;
86
87 let on_global_pointer_move = move |e: Event<PointerEventData>| match phase() {
88 DragPhase::Dragging(_) => {
89 phase.set(DragPhase::Dragging(e.global_location()));
90 }
91 DragPhase::Pressing(press_point) => {
92 let current = e.global_location();
93 let dx = current.x - press_point.x;
94 let dy = current.y - press_point.y;
95
96 if (dx * dx + dy * dy).sqrt() >= drag_threshold {
97 phase.set(DragPhase::Dragging(current));
98 *drags.write() = Some(data.clone());
99 }
100 }
101 DragPhase::Idle => {}
102 };
103
104 let on_pointer_down = move |e: Event<PointerEventData>| {
105 if e.data().button() != Some(MouseButton::Left) {
106 return;
107 }
108 phase.set(DragPhase::Pressing(e.global_location()));
109 };
110
111 let on_global_pointer_press = move |_: Event<PointerEventData>| {
112 if !matches!(phase(), DragPhase::Idle) {
113 phase.set(DragPhase::Idle);
114 *drags.write() = None;
115 }
116 };
117
118 let dragging_position = match phase() {
119 DragPhase::Dragging(pos) => Some(pos),
120 _ => None,
121 };
122
123 rect()
124 .on_global_pointer_press(on_global_pointer_press)
125 .on_global_pointer_move(on_global_pointer_move)
126 .on_pointer_down(on_pointer_down)
127 .maybe_child((dragging_position.zip(self.drag_element.clone())).map(
128 |(position, drag_element)| {
129 let (x, y) = position.to_f32().to_tuple();
130 rect()
131 .position(Position::new_global())
132 .layer(Layer::Overlay)
133 .interactive(false)
134 .width(Size::px(0.))
135 .height(Size::px(0.))
136 .offset_x(x + 1.)
138 .offset_y(y + 1.)
139 .child(drag_element)
140 },
141 ))
142 .maybe_child(
143 (self.show_while_dragging || dragging_position.is_none())
144 .then(|| self.children.clone()),
145 )
146 }
147
148 fn render_key(&self) -> DiffKey {
149 self.key.clone().or(self.default_key())
150 }
151}
152
153#[derive(PartialEq, Clone)]
154pub struct DropZone<T: 'static + PartialEq + Clone> {
155 children: Element,
156 on_drop: EventHandler<T>,
157 on_drag_over: Option<EventHandler<bool>>,
158 width: Size,
159 height: Size,
160 key: DiffKey,
161}
162
163impl<T: Clone + PartialEq + 'static> KeyExt for DropZone<T> {
164 fn write_key(&mut self) -> &mut DiffKey {
165 &mut self.key
166 }
167}
168
169impl<T: PartialEq + Clone + 'static> DropZone<T> {
170 pub fn new(children: impl Into<Element>, on_drop: impl Into<EventHandler<T>>) -> Self {
171 Self {
172 children: children.into(),
173 on_drop: on_drop.into(),
174 on_drag_over: None,
175 width: Size::auto(),
176 height: Size::auto(),
177 key: DiffKey::default(),
178 }
179 }
180
181 pub fn on_drag_over(mut self, on_drag_over: impl Into<EventHandler<bool>>) -> Self {
184 self.on_drag_over = Some(on_drag_over.into());
185 self
186 }
187}
188
189impl<T: Clone + PartialEq + 'static> Component for DropZone<T> {
190 fn render(&self) -> impl IntoElement {
191 let mut drags = use_drag::<T>();
192 let on_drop = self.on_drop.clone();
193 let on_drag_over = self.on_drag_over.clone();
194
195 let on_mouse_up = {
196 let on_drag_over = on_drag_over.clone();
197 move |e: Event<MouseEventData>| {
198 e.stop_propagation();
199 if let Some(current_drags) = &*drags.read() {
200 on_drop.call(current_drags.clone());
201 }
202 if drags.read().is_some() {
203 *drags.write() = None;
204 if let Some(on_drag_over) = &on_drag_over {
205 on_drag_over.call(false);
206 }
207 }
208 }
209 };
210
211 rect()
212 .on_mouse_up(on_mouse_up)
213 .width(self.width.clone())
214 .height(self.height.clone())
215 .map(on_drag_over, move |el, on_drag_over| {
216 el.on_pointer_enter({
217 let on_drag_over = on_drag_over.clone();
218 move |_| {
219 if drags.read().is_some() {
220 on_drag_over.call(true);
221 }
222 }
223 })
224 .on_pointer_leave(move |_| {
225 if drags.read().is_some() {
226 on_drag_over.call(false);
227 }
228 })
229 })
230 .child(self.children.clone())
231 }
232
233 fn render_key(&self) -> DiffKey {
234 self.key.clone().or(self.default_key())
235 }
236}