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