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
14fn use_drag<T: 'static>() -> State<Option<T>> {
15 match try_consume_root_context() {
16 Some(s) => s,
17 None => {
18 let state = State::<Option<T>>::create_in_scope(None, ScopeId::ROOT);
19 provide_context_for_scope_id(state, ScopeId::ROOT);
20 state
21 }
22 }
23}
24
25#[derive(Clone, PartialEq)]
27pub struct DragZone<T: Clone + 'static + PartialEq> {
28 drag_element: Option<Element>,
30 children: Element,
32 data: T,
34 show_while_dragging: bool,
36 drag_threshold: f64,
38 key: DiffKey,
39}
40
41impl<T: Clone + PartialEq + 'static> KeyExt for DragZone<T> {
42 fn write_key(&mut self) -> &mut DiffKey {
43 &mut self.key
44 }
45}
46
47impl<T: Clone + PartialEq + 'static> DragZone<T> {
48 pub fn new(data: T, children: impl Into<Element>) -> Self {
49 Self {
50 data,
51 children: children.into(),
52 drag_element: None,
53 show_while_dragging: true,
54 drag_threshold: 4.0,
55 key: DiffKey::default(),
56 }
57 }
58
59 pub fn show_while_dragging(mut self, show_while_dragging: bool) -> Self {
60 self.show_while_dragging = show_while_dragging;
61 self
62 }
63
64 pub fn drag_element(mut self, drag_element: impl Into<Element>) -> Self {
65 self.drag_element = Some(drag_element.into());
66 self
67 }
68
69 pub fn drag_threshold(mut self, drag_threshold: f64) -> Self {
70 self.drag_threshold = drag_threshold;
71 self
72 }
73}
74
75impl<T: Clone + PartialEq> Component for DragZone<T> {
76 fn render(&self) -> impl IntoElement {
77 let mut drags = use_drag::<T>();
78 let mut phase = use_state(|| DragPhase::Idle);
79 let data = self.data.clone();
80 let drag_threshold = self.drag_threshold;
81
82 let on_global_pointer_move = move |e: Event<PointerEventData>| match phase() {
83 DragPhase::Dragging(_) => {
84 phase.set(DragPhase::Dragging(e.global_location()));
85 }
86 DragPhase::Pressing(press_point) => {
87 let current = e.global_location();
88 let dx = current.x - press_point.x;
89 let dy = current.y - press_point.y;
90
91 if (dx * dx + dy * dy).sqrt() >= drag_threshold {
92 phase.set(DragPhase::Dragging(current));
93 *drags.write() = Some(data.clone());
94 }
95 }
96 DragPhase::Idle => {}
97 };
98
99 let on_pointer_down = move |e: Event<PointerEventData>| {
100 if e.data().button() != Some(MouseButton::Left) {
101 return;
102 }
103 phase.set(DragPhase::Pressing(e.global_location()));
104 };
105
106 let on_global_pointer_press = move |_: Event<PointerEventData>| {
107 if !matches!(phase(), DragPhase::Idle) {
108 phase.set(DragPhase::Idle);
109 *drags.write() = None;
110 }
111 };
112
113 let dragging_position = match phase() {
114 DragPhase::Dragging(pos) => Some(pos),
115 _ => None,
116 };
117
118 rect()
119 .on_global_pointer_press(on_global_pointer_press)
120 .on_global_pointer_move(on_global_pointer_move)
121 .on_pointer_down(on_pointer_down)
122 .maybe_child((dragging_position.zip(self.drag_element.clone())).map(
123 |(position, drag_element)| {
124 let (x, y) = position.to_f32().to_tuple();
125 rect()
126 .position(Position::new_global())
127 .layer(Layer::Overlay)
128 .width(Size::px(0.))
129 .height(Size::px(0.))
130 .offset_x(x + 1.)
132 .offset_y(y + 1.)
133 .child(drag_element)
134 },
135 ))
136 .maybe_child(
137 (self.show_while_dragging || dragging_position.is_none())
138 .then(|| self.children.clone()),
139 )
140 }
141
142 fn render_key(&self) -> DiffKey {
143 self.key.clone().or(self.default_key())
144 }
145}
146
147#[derive(PartialEq, Clone)]
148pub struct DropZone<T: 'static + PartialEq + Clone> {
149 children: Element,
150 on_drop: EventHandler<T>,
151 width: Size,
152 height: Size,
153 key: DiffKey,
154}
155
156impl<T: Clone + PartialEq + 'static> KeyExt for DropZone<T> {
157 fn write_key(&mut self) -> &mut DiffKey {
158 &mut self.key
159 }
160}
161
162impl<T: PartialEq + Clone + 'static> DropZone<T> {
163 pub fn new(children: impl Into<Element>, on_drop: impl Into<EventHandler<T>>) -> Self {
164 Self {
165 children: children.into(),
166 on_drop: on_drop.into(),
167 width: Size::auto(),
168 height: Size::auto(),
169 key: DiffKey::default(),
170 }
171 }
172}
173
174impl<T: Clone + PartialEq + 'static> Component for DropZone<T> {
175 fn render(&self) -> impl IntoElement {
176 let mut drags = use_drag::<T>();
177 let on_drop = self.on_drop.clone();
178
179 let on_mouse_up = move |e: Event<MouseEventData>| {
180 e.stop_propagation();
181 if let Some(current_drags) = &*drags.read() {
182 on_drop.call(current_drags.clone());
183 }
184 if drags.read().is_some() {
185 *drags.write() = None;
186 }
187 };
188
189 rect()
190 .on_mouse_up(on_mouse_up)
191 .width(self.width.clone())
192 .height(self.height.clone())
193 .child(self.children.clone())
194 }
195
196 fn render_key(&self) -> DiffKey {
197 self.key.clone().or(self.default_key())
198 }
199}