freya_components/
draggable_canvas.rs

1use freya_core::prelude::*;
2use torin::prelude::{
3    Area,
4    CursorPoint,
5    Position,
6};
7
8#[derive(Clone)]
9struct DraggableCanvasLayout(State<Area>);
10
11#[derive(Clone)]
12struct DraggableCanvasRegistry(State<Vec<usize>>);
13
14/// A canvas container that allows draggable elements within it.
15///
16/// # Example
17///
18/// ```rust
19/// # use freya::prelude::*;
20/// fn app() -> impl IntoElement {
21///     DraggableCanvas::new().child(Draggable::new().child("Draggable item"))
22/// }
23/// ```
24#[derive(PartialEq)]
25pub struct DraggableCanvas {
26    children: Vec<Element>,
27    layout: LayoutData,
28    key: DiffKey,
29}
30
31impl KeyExt for DraggableCanvas {
32    fn write_key(&mut self) -> &mut DiffKey {
33        &mut self.key
34    }
35}
36
37impl Default for DraggableCanvas {
38    fn default() -> Self {
39        Self::new()
40    }
41}
42
43impl DraggableCanvas {
44    pub fn new() -> Self {
45        Self {
46            children: vec![],
47            layout: LayoutData::default(),
48            key: DiffKey::None,
49        }
50    }
51}
52
53impl LayoutExt for DraggableCanvas {
54    fn get_layout(&mut self) -> &mut LayoutData {
55        &mut self.layout
56    }
57}
58
59impl ContainerExt for DraggableCanvas {}
60
61impl ChildrenExt for DraggableCanvas {
62    fn get_children(&mut self) -> &mut Vec<Element> {
63        &mut self.children
64    }
65}
66
67impl Component for DraggableCanvas {
68    fn render(&self) -> impl IntoElement {
69        let mut layout = use_state(Area::default);
70        use_provide_context(move || DraggableCanvasLayout(layout));
71        use_provide_context(|| DraggableCanvasRegistry(State::create(Vec::new())));
72        rect()
73            .layout(self.layout.clone())
74            .on_sized(move |e: Event<SizedEventData>| layout.set(e.visible_area))
75            .children(self.children.clone())
76    }
77    fn render_key(&self) -> DiffKey {
78        self.key.clone().or(self.default_key())
79    }
80}
81
82#[derive(PartialEq)]
83pub struct Draggable {
84    initial_position: CursorPoint,
85    children: Vec<Element>,
86    key: DiffKey,
87}
88
89impl Default for Draggable {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95impl Draggable {
96    pub fn new() -> Self {
97        Self {
98            initial_position: CursorPoint::zero(),
99            children: vec![],
100            key: DiffKey::None,
101        }
102    }
103
104    pub fn initial_position(mut self, initial_position: impl Into<CursorPoint>) -> Self {
105        self.initial_position = initial_position.into();
106        self
107    }
108}
109
110impl KeyExt for Draggable {
111    fn write_key(&mut self) -> &mut DiffKey {
112        &mut self.key
113    }
114}
115
116impl ChildrenExt for Draggable {
117    fn get_children(&mut self) -> &mut Vec<Element> {
118        &mut self.children
119    }
120}
121
122impl Component for Draggable {
123    fn render(&self) -> impl IntoElement {
124        let mut position = use_state(|| self.initial_position);
125        let mut dragging_position = use_state::<Option<CursorPoint>>(|| None);
126        let DraggableCanvasLayout(layout) = use_consume::<DraggableCanvasLayout>();
127        let DraggableCanvasRegistry(mut registry) = use_consume::<DraggableCanvasRegistry>();
128        let id = use_id::<DraggableCanvasLayout>();
129
130        use_hook(move || {
131            registry.write().push(id);
132        });
133
134        use_drop(move || {
135            registry.write().retain(|i| *i != id);
136        });
137
138        let on_global_mouse_move = move |e: Event<MouseEventData>| {
139            if let Some(dragging_position) = dragging_position() {
140                position.set(CursorPoint::new(
141                    e.global_location.x - dragging_position.x,
142                    e.global_location.y - dragging_position.y,
143                ));
144                e.stop_propagation();
145            }
146        };
147
148        let on_pointer_down = move |e: Event<PointerEventData>| {
149            dragging_position.set(Some(CursorPoint::new(
150                e.element_location().x + layout.read().min_x() as f64,
151                e.element_location().y + layout.read().min_y() as f64,
152            )));
153            e.stop_propagation();
154            let mut registry = registry.write();
155            registry.retain(|i| *i != id);
156            registry.insert(0, id);
157        };
158
159        let on_capture_global_mouse_up = move |e: Event<MouseEventData>| {
160            if dragging_position.read().is_some() {
161                e.stop_propagation();
162                e.prevent_default();
163                dragging_position.set(None);
164            }
165        };
166
167        let (left, top) = position().to_tuple();
168
169        let layer = registry
170            .read()
171            .iter()
172            .rev()
173            .position(|i| *i == id)
174            .map(|layer| layer * 1024)
175            .unwrap_or_default();
176
177        rect()
178            .on_global_mouse_move(on_global_mouse_move)
179            .on_pointer_down(on_pointer_down)
180            .on_capture_global_mouse_up(on_capture_global_mouse_up)
181            .position(Position::new_absolute().left(left as f32).top(top as f32))
182            .layer(layer as i16)
183            .children(self.children.clone())
184    }
185
186    fn render_key(&self) -> DiffKey {
187        self.key.clone().or(self.default_key())
188    }
189}