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