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 {
11 press_point: CursorPoint,
12 offset: CursorPoint,
13 },
14 Dragging {
15 position: CursorPoint,
16 offset: CursorPoint,
17 },
18}
19
20pub fn use_drag<T: 'static>() -> State<Option<T>> {
26 match try_consume_root_context() {
27 Some(s) => s,
28 None => {
29 let state = State::<Option<T>>::create_in_scope(None, ScopeId::ROOT);
30 provide_context_for_scope_id(state, ScopeId::ROOT);
31 state
32 }
33 }
34}
35
36#[derive(Clone, PartialEq)]
38pub struct DragZone<T: Clone + 'static + PartialEq> {
39 drag_element: Option<Element>,
41 children: Element,
43 data: T,
45 show_while_dragging: bool,
47 drag_threshold: f64,
49 enabled: bool,
51 key: DiffKey,
52}
53
54impl<T: Clone + PartialEq + 'static> KeyExt for DragZone<T> {
55 fn write_key(&mut self) -> &mut DiffKey {
56 &mut self.key
57 }
58}
59
60impl<T: Clone + PartialEq + 'static> DragZone<T> {
61 pub fn new(data: T, children: impl Into<Element>) -> Self {
62 Self {
63 data,
64 children: children.into(),
65 drag_element: None,
66 show_while_dragging: true,
67 drag_threshold: 4.0,
68 enabled: true,
69 key: DiffKey::default(),
70 }
71 }
72
73 pub fn show_while_dragging(mut self, show_while_dragging: bool) -> Self {
74 self.show_while_dragging = show_while_dragging;
75 self
76 }
77
78 pub fn drag_element(mut self, drag_element: impl Into<Element>) -> Self {
79 self.drag_element = Some(drag_element.into());
80 self
81 }
82
83 pub fn drag_threshold(mut self, drag_threshold: f64) -> Self {
84 self.drag_threshold = drag_threshold;
85 self
86 }
87
88 pub fn enabled(mut self, enabled: bool) -> Self {
90 self.enabled = enabled;
91 self
92 }
93}
94
95impl<T: Clone + PartialEq> Component for DragZone<T> {
96 fn render(&self) -> impl IntoElement {
97 let mut drags = use_drag::<T>();
98 let mut phase = use_state(|| DragPhase::Idle);
99 let mut drag_element_size = use_state(|| None::<Size2D>);
100 let data = self.data.clone();
101 let drag_threshold = self.drag_threshold;
102
103 let on_global_pointer_move = move |e: Event<PointerEventData>| match phase() {
104 DragPhase::Dragging { offset, .. } => {
105 phase.set(DragPhase::Dragging {
106 position: e.global_location(),
107 offset,
108 });
109 }
110 DragPhase::Pressing {
111 press_point,
112 offset,
113 } => {
114 let current = e.global_location();
115 let dx = current.x - press_point.x;
116 let dy = current.y - press_point.y;
117
118 if (dx * dx + dy * dy).sqrt() >= drag_threshold {
119 phase.set(DragPhase::Dragging {
120 position: current,
121 offset,
122 });
123 *drags.write() = Some(data.clone());
124 }
125 }
126 DragPhase::Idle => {}
127 };
128
129 let on_pointer_down = move |e: Event<PointerEventData>| {
130 if e.data().button() != Some(MouseButton::Left) {
131 return;
132 }
133 phase.set(DragPhase::Pressing {
134 press_point: e.global_location(),
135 offset: e.element_location(),
136 });
137 };
138
139 let on_global_pointer_press = move |_: Event<PointerEventData>| {
140 if !matches!(phase(), DragPhase::Idle) {
141 phase.set(DragPhase::Idle);
142 *drags.write() = None;
143 }
144 };
145
146 let dragging = match phase() {
147 DragPhase::Dragging { position, offset } => Some((position, offset)),
148 _ => None,
149 };
150
151 rect()
152 .on_global_pointer_press(on_global_pointer_press)
153 .on_global_pointer_move(on_global_pointer_move)
154 .maybe(self.enabled, |rect| rect.on_pointer_down(on_pointer_down))
155 .maybe_child((dragging.zip(self.drag_element.clone())).map(
156 |((position, offset), drag_element)| {
157 let size = *drag_element_size.read();
158 let anchor = size.map_or(offset, |size| {
159 offset.min(CursorPoint::new(size.width as f64, size.height as f64))
160 });
161 let (x, y) = (position - anchor).to_f32().to_tuple();
162 rect()
163 .position(Position::new_global())
164 .layer(Layer::Overlay)
165 .interactive(false)
166 .opacity(if size.is_some() { 1. } else { 0. })
167 .offset_x(x + 1.)
169 .offset_y(y + 1.)
170 .on_sized(move |e: Event<SizedEventData>| {
171 drag_element_size.set_if_modified(Some(e.area.size))
172 })
173 .child(drag_element)
174 },
175 ))
176 .maybe_child(
177 (self.show_while_dragging || dragging.is_none()).then(|| self.children.clone()),
178 )
179 }
180
181 fn render_key(&self) -> DiffKey {
182 self.key.clone().or(self.default_key())
183 }
184}
185
186#[derive(PartialEq, Clone)]
187pub struct DropZone<T: 'static + PartialEq + Clone> {
188 children: Element,
189 on_drop: EventHandler<T>,
190 on_drag_over: Option<EventHandler<bool>>,
191 width: Size,
192 height: Size,
193 key: DiffKey,
194}
195
196impl<T: Clone + PartialEq + 'static> KeyExt for DropZone<T> {
197 fn write_key(&mut self) -> &mut DiffKey {
198 &mut self.key
199 }
200}
201
202impl<T: PartialEq + Clone + 'static> DropZone<T> {
203 pub fn new(children: impl Into<Element>, on_drop: impl Into<EventHandler<T>>) -> Self {
204 Self {
205 children: children.into(),
206 on_drop: on_drop.into(),
207 on_drag_over: None,
208 width: Size::auto(),
209 height: Size::auto(),
210 key: DiffKey::default(),
211 }
212 }
213
214 pub fn on_drag_over(mut self, on_drag_over: impl Into<EventHandler<bool>>) -> Self {
217 self.on_drag_over = Some(on_drag_over.into());
218 self
219 }
220}
221
222impl<T: Clone + PartialEq + 'static> Component for DropZone<T> {
223 fn render(&self) -> impl IntoElement {
224 let mut drags = use_drag::<T>();
225 let on_drop = self.on_drop.clone();
226 let on_drag_over = self.on_drag_over.clone();
227
228 let on_mouse_up = {
229 let on_drag_over = on_drag_over.clone();
230 move |e: Event<MouseEventData>| {
231 e.stop_propagation();
232 if let Some(current_drags) = &*drags.read() {
233 on_drop.call(current_drags.clone());
234 }
235 if drags.read().is_some() {
236 *drags.write() = None;
237 if let Some(on_drag_over) = &on_drag_over {
238 on_drag_over.call(false);
239 }
240 }
241 }
242 };
243
244 rect()
245 .on_mouse_up(on_mouse_up)
246 .width(self.width.clone())
247 .height(self.height.clone())
248 .map(on_drag_over, move |el, on_drag_over| {
249 el.on_pointer_enter({
250 let on_drag_over = on_drag_over.clone();
251 move |_| {
252 if drags.read().is_some() {
253 on_drag_over.call(true);
254 }
255 }
256 })
257 .on_pointer_leave(move |_| {
258 if drags.read().is_some() {
259 on_drag_over.call(false);
260 }
261 })
262 })
263 .child(self.children.clone())
264 }
265
266 fn render_key(&self) -> DiffKey {
267 self.key.clone().or(self.default_key())
268 }
269}