freya_components/
draggable_canvas.rs1use freya_core::prelude::*;
2use torin::{
3 prelude::{
4 Area,
5 CursorPoint,
6 Position,
7 Size2D,
8 },
9 size::Size,
10};
11
12#[derive(Clone)]
13struct DraggableCanvasRegistry(State<Vec<usize>>);
14
15#[derive(PartialEq)]
26pub struct DraggableCanvas {
27 children: Vec<Element>,
28 layout: LayoutData,
29 key: DiffKey,
30}
31
32impl KeyExt for DraggableCanvas {
33 fn write_key(&mut self) -> &mut DiffKey {
34 &mut self.key
35 }
36}
37
38impl Default for DraggableCanvas {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl DraggableCanvas {
45 pub fn new() -> Self {
46 Self {
47 children: vec![],
48 layout: LayoutData::default(),
49 key: DiffKey::None,
50 }
51 }
52}
53
54impl LayoutExt for DraggableCanvas {
55 fn get_layout(&mut self) -> &mut LayoutData {
56 &mut self.layout
57 }
58}
59
60impl ContainerExt for DraggableCanvas {}
61
62impl ChildrenExt for DraggableCanvas {
63 fn get_children(&mut self) -> &mut Vec<Element> {
64 &mut self.children
65 }
66}
67
68impl Component for DraggableCanvas {
69 fn render(&self) -> impl IntoElement {
70 let mut layout = use_state(Area::default);
71 use_provide_context(|| DraggableCanvasRegistry(State::create(Vec::new())));
72 let focus = use_focus();
73 let mut offset = use_state(CursorPoint::zero);
74 let mut dragging_position = use_state::<Option<CursorPoint>>(|| None);
75
76 let on_mouse_move = move |e: Event<MouseEventData>| {
77 if let Some(dragging_position) = dragging_position() {
78 offset.set((e.element_location - dragging_position).to_point());
79 e.stop_propagation();
80 }
81 };
82
83 let on_pointer_down = move |e: Event<PointerEventData>| {
84 dragging_position.set(Some((offset() - e.element_location()).to_point()));
85 e.stop_propagation();
86 };
87
88 let on_global_mouse_up = move |e: Event<MouseEventData>| {
89 if dragging_position.read().is_some() {
90 e.stop_propagation();
91 e.prevent_default();
92 dragging_position.set(None);
93 }
94 };
95
96 let on_wheel = move |e: Event<WheelEventData>| {
97 let mut current_offset = offset.write();
98 current_offset.x += e.delta_x;
99 current_offset.y += e.delta_y;
100 };
101
102 let (offset_x, offset_y) = offset().to_tuple();
103
104 rect()
105 .layout(self.layout.clone())
106 .on_sized(move |e: Event<SizedEventData>| layout.set(e.visible_area))
107 .on_mouse_move(on_mouse_move)
108 .on_pointer_down(on_pointer_down)
109 .on_global_mouse_up(on_global_mouse_up)
110 .on_wheel(on_wheel)
111 .offset_x(offset_x as f32)
112 .offset_y(offset_y as f32)
113 .a11y_id(focus.a11y_id())
114 .a11y_role(AccessibilityRole::ScrollView)
115 .a11y_builder(move |node| {
116 node.set_scroll_x(offset_x);
117 node.set_scroll_y(offset_y)
118 })
119 .scrollable(true)
120 .overflow(Overflow::Clip)
121 .children(self.children.clone())
122 }
123 fn render_key(&self) -> DiffKey {
124 self.key.clone().or(self.default_key())
125 }
126}
127
128#[derive(PartialEq)]
129pub struct Draggable {
130 initial_position: CursorPoint,
131 children: Vec<Element>,
132 key: DiffKey,
133}
134
135impl Default for Draggable {
136 fn default() -> Self {
137 Self::new()
138 }
139}
140
141impl Draggable {
142 pub fn new() -> Self {
143 Self {
144 initial_position: CursorPoint::zero(),
145 children: vec![],
146 key: DiffKey::None,
147 }
148 }
149
150 pub fn initial_position(mut self, initial_position: impl Into<CursorPoint>) -> Self {
151 self.initial_position = initial_position.into();
152 self
153 }
154}
155
156impl KeyExt for Draggable {
157 fn write_key(&mut self) -> &mut DiffKey {
158 &mut self.key
159 }
160}
161
162impl ChildrenExt for Draggable {
163 fn get_children(&mut self) -> &mut Vec<Element> {
164 &mut self.children
165 }
166}
167
168impl Component for Draggable {
169 fn render(&self) -> impl IntoElement {
170 let mut position = use_state(|| self.initial_position);
171 let mut dragging_position = use_state::<Option<CursorPoint>>(|| None);
172 let DraggableCanvasRegistry(mut registry) = use_consume::<DraggableCanvasRegistry>();
173 let id = use_id::<DraggableCanvas>();
174
175 use_hook(move || {
176 registry.write().push(id);
177 });
178
179 use_drop(move || {
180 registry.write().retain(|i| *i != id);
181 });
182
183 let on_global_mouse_move = move |e: Event<MouseEventData>| {
184 if let Some(dragging_position) = dragging_position() {
185 position.set((e.global_location - dragging_position).to_point());
186 e.stop_propagation();
187 }
188 };
189
190 let on_pointer_down = move |e: Event<PointerEventData>| {
191 dragging_position.set(Some((e.global_location() - position()).to_point()));
192 e.stop_propagation();
193 let mut registry = registry.write();
194 registry.retain(|i| *i != id);
195 registry.insert(0, id);
196 };
197
198 let on_capture_global_mouse_up = move |e: Event<MouseEventData>| {
199 if dragging_position.read().is_some() {
200 e.stop_propagation();
201 e.prevent_default();
202 dragging_position.set(None);
203 }
204 };
205
206 let (left, top) = position().to_f32().to_tuple();
207
208 let layer = registry
209 .read()
210 .iter()
211 .rev()
212 .position(|i| *i == id)
213 .map(|layer| layer * 1024)
214 .unwrap_or_default();
215
216 rect()
217 .on_global_mouse_move(on_global_mouse_move)
218 .on_pointer_down(on_pointer_down)
219 .on_capture_global_mouse_up(on_capture_global_mouse_up)
220 .position(Position::new_absolute().left(left).top(top))
221 .layer(layer as i16)
222 .children(self.children.clone())
223 }
224
225 fn render_key(&self) -> DiffKey {
226 self.key.clone().or(self.default_key())
227 }
228}
229
230#[derive(Clone, Copy, PartialEq, Debug)]
232enum ResizeEdge {
233 Right,
234 Bottom,
235 BottomRight,
236}
237
238#[derive(PartialEq)]
239pub struct ResizableDraggable {
240 initial_position: CursorPoint,
241 initial_size: Size2D,
242 handle_size: f32,
243 corner_size: f32,
244 children: Vec<Element>,
245 key: DiffKey,
246}
247
248impl ResizableDraggable {
249 pub fn new(initial_size: impl Into<Size2D>) -> Self {
250 Self {
251 initial_position: CursorPoint::zero(),
252 initial_size: initial_size.into(),
253 handle_size: 4.,
254 corner_size: 12.,
255 children: vec![],
256 key: DiffKey::None,
257 }
258 }
259
260 pub fn initial_position(mut self, initial_position: impl Into<CursorPoint>) -> Self {
261 self.initial_position = initial_position.into();
262 self
263 }
264}
265
266impl KeyExt for ResizableDraggable {
267 fn write_key(&mut self) -> &mut DiffKey {
268 &mut self.key
269 }
270}
271
272impl ChildrenExt for ResizableDraggable {
273 fn get_children(&mut self) -> &mut Vec<Element> {
274 &mut self.children
275 }
276}
277
278impl Component for ResizableDraggable {
279 fn render(&self) -> impl IntoElement {
280 let mut position = use_state(|| self.initial_position);
281 let mut size = use_state(|| self.initial_size);
282 let mut dragging_position = use_state::<Option<CursorPoint>>(|| None);
283 let mut resizing = use_state::<Option<(ResizeEdge, CursorPoint)>>(|| None);
284 let DraggableCanvasRegistry(mut registry) = use_consume::<DraggableCanvasRegistry>();
285 let id = use_id::<DraggableCanvas>();
286
287 use_hook(move || {
288 registry.write().push(id);
289 });
290
291 use_drop(move || {
292 registry.write().retain(|i| *i != id);
293 });
294
295 let on_global_mouse_move = move |e: Event<MouseEventData>| {
296 if let Some(dragging_position) = dragging_position() {
297 position.set((e.global_location - dragging_position).to_point());
298 e.stop_propagation();
299 }
300 if let Some((edge, start_point)) = resizing() {
301 const MIN: f32 = 20.;
303
304 let delta = (e.global_location - start_point).to_f32().to_point();
305 let (current_width, current_height) = size().to_tuple();
306 let new_width = if matches!(edge, ResizeEdge::Right | ResizeEdge::BottomRight) {
307 (current_width + delta.x).max(MIN)
308 } else {
309 current_width
310 };
311 let new_height = if matches!(edge, ResizeEdge::Bottom | ResizeEdge::BottomRight) {
312 (current_height + delta.y).max(MIN)
313 } else {
314 current_height
315 };
316 size.set((new_width, new_height).into());
317 resizing.set(Some((edge, e.global_location)));
318 e.stop_propagation();
319 }
320 };
321
322 let on_pointer_down = move |e: Event<PointerEventData>| {
323 if resizing.read().is_some() {
325 return;
326 }
327 dragging_position.set(Some((e.global_location() - position()).to_point()));
328 e.stop_propagation();
329 let mut registry_write = registry.write();
330 registry_write.retain(|i| *i != id);
331 registry_write.insert(0, id);
332 };
333
334 let on_capture_global_mouse_up = move |e: Event<MouseEventData>| {
335 if dragging_position.read().is_some() {
336 e.stop_propagation();
337 e.prevent_default();
338 dragging_position.set(None);
339 }
340 if resizing.read().is_some() {
341 e.stop_propagation();
342 e.prevent_default();
343 resizing.set(None);
344 Cursor::set(CursorIcon::default());
345 }
346 };
347
348 let (left, top) = position().to_f32().to_tuple();
349 let (width, height) = size().to_tuple();
350
351 let layer = registry
352 .read()
353 .iter()
354 .rev()
355 .position(|i| *i == id)
356 .map(|layer| layer * 1024)
357 .unwrap_or_default();
358
359 let handle = self.handle_size;
360 let corner = self.corner_size;
361
362 let right_handle = rect()
363 .width(Size::px(handle))
364 .height(Size::px(height - corner))
365 .position(Position::new_absolute().right(-handle).top(0.))
366 .background(Color::WHITE)
367 .opacity(0.)
368 .on_pointer_enter(move |_: Event<PointerEventData>| {
369 Cursor::set(CursorIcon::ColResize);
370 })
371 .on_pointer_leave(move |_: Event<PointerEventData>| {
372 if resizing().is_none() {
373 Cursor::set(CursorIcon::default());
374 }
375 })
376 .on_pointer_down(move |e: Event<PointerEventData>| {
377 e.stop_propagation();
378 resizing.set(Some((ResizeEdge::Right, e.global_location())));
379 let mut registry = registry.write();
380 registry.retain(|i| *i != id);
381 registry.insert(0, id);
382 });
383
384 let bottom_handle = rect()
385 .width(Size::px(width - corner))
386 .height(Size::px(handle))
387 .position(Position::new_absolute().left(0.).bottom(-handle))
388 .background(Color::WHITE)
389 .opacity(0.)
390 .on_pointer_enter(move |_: Event<PointerEventData>| {
391 Cursor::set(CursorIcon::RowResize);
392 })
393 .on_pointer_leave(move |_: Event<PointerEventData>| {
394 if resizing().is_none() {
395 Cursor::set(CursorIcon::default());
396 }
397 })
398 .on_pointer_down(move |e: Event<PointerEventData>| {
399 e.stop_propagation();
400 resizing.set(Some((ResizeEdge::Bottom, e.global_location())));
401 let mut registry = registry.write();
402 registry.retain(|i| *i != id);
403 registry.insert(0, id);
404 });
405
406 let corner_handle = rect()
407 .width(Size::px(corner))
408 .height(Size::px(corner))
409 .position(Position::new_absolute().right(-handle).bottom(-handle))
410 .background(Color::WHITE)
411 .opacity(0.)
412 .on_pointer_enter(move |_: Event<PointerEventData>| {
413 Cursor::set(CursorIcon::SeResize);
414 })
415 .on_pointer_leave(move |_: Event<PointerEventData>| {
416 if resizing().is_none() {
417 Cursor::set(CursorIcon::default());
418 }
419 })
420 .on_pointer_down(move |e: Event<PointerEventData>| {
421 e.stop_propagation();
422 resizing.set(Some((ResizeEdge::BottomRight, e.global_location())));
423 let mut registry = registry.write();
424 registry.retain(|i| *i != id);
425 registry.insert(0, id);
426 });
427
428 rect()
429 .on_global_mouse_move(on_global_mouse_move)
430 .on_pointer_down(on_pointer_down)
431 .on_capture_global_mouse_up(on_capture_global_mouse_up)
432 .position(Position::new_absolute().left(left).top(top))
433 .width(Size::px(width))
434 .height(Size::px(height))
435 .layer(layer as i16)
436 .child(
437 rect()
438 .overflow(Overflow::Clip)
439 .children(self.children.clone()),
440 )
441 .child(right_handle)
442 .child(bottom_handle)
443 .child(corner_handle)
444 }
445
446 fn render_key(&self) -> DiffKey {
447 self.key.clone().or(self.default_key())
448 }
449}