freya_components/
drag_drop.rs1use freya_core::{
2 prelude::*,
3 scope_id::ScopeId,
4};
5use torin::prelude::*;
6
7fn use_drag<T: 'static>() -> State<Option<T>> {
8 match try_consume_root_context() {
9 Some(s) => s,
10 None => {
11 let state = State::<Option<T>>::create_in_scope(None, ScopeId::ROOT);
12 provide_context_for_scope_id(state, ScopeId::ROOT);
13 state
14 }
15 }
16}
17
18#[derive(Clone, PartialEq)]
20pub struct DragZone<T: Clone + 'static + PartialEq> {
21 drag_element: Option<Element>,
23 children: Element,
25 data: T,
27 show_while_dragging: bool,
29 key: DiffKey,
30}
31
32impl<T: Clone + PartialEq + 'static> KeyExt for DragZone<T> {
33 fn write_key(&mut self) -> &mut DiffKey {
34 &mut self.key
35 }
36}
37
38impl<T: Clone + PartialEq + 'static> DragZone<T> {
39 pub fn new(data: T, children: impl Into<Element>) -> Self {
40 Self {
41 data,
42 children: children.into(),
43 drag_element: None,
44 show_while_dragging: true,
45 key: DiffKey::default(),
46 }
47 }
48
49 pub fn show_while_dragging(mut self, show_while_dragging: bool) -> Self {
50 self.show_while_dragging = show_while_dragging;
51 self
52 }
53
54 pub fn drag_element(mut self, drag_element: impl Into<Element>) -> Self {
55 self.drag_element = Some(drag_element.into());
56 self
57 }
58}
59
60impl<T: Clone + PartialEq> Component for DragZone<T> {
61 fn render(&self) -> impl IntoElement {
62 let mut drags = use_drag::<T>();
63 let mut position = use_state::<Option<CursorPoint>>(|| None);
64 let data = self.data.clone();
65
66 let on_global_mouse_move = move |e: Event<MouseEventData>| {
67 if position.read().is_some() {
68 position.set(Some(e.global_location));
69 }
70 };
71
72 let on_pointer_down = move |e: Event<PointerEventData>| {
73 if e.data().button() != Some(MouseButton::Left) {
74 return;
75 }
76 position.set(Some(e.global_location()));
77 *drags.write() = Some(data.clone());
78 };
79
80 let on_global_mouse_up = move |_: Event<MouseEventData>| {
81 if position.read().is_some() {
82 position.set(None);
83 *drags.write() = None;
84 }
85 };
86
87 rect()
88 .on_global_mouse_up(on_global_mouse_up)
89 .on_global_mouse_move(on_global_mouse_move)
90 .on_pointer_down(on_pointer_down)
91 .maybe_child((position.read().zip(self.drag_element.clone())).map(
92 |(position, drag_element)| {
93 let (x, y) = position.to_f32().to_tuple();
94 rect()
95 .position(Position::new_global())
96 .width(Size::px(0.))
97 .height(Size::px(0.))
98 .offset_x(x + 1.)
100 .offset_y(y + 1.)
101 .child(drag_element)
102 },
103 ))
104 .maybe_child(
105 (self.show_while_dragging || position.read().is_none())
106 .then(|| self.children.clone()),
107 )
108 }
109
110 fn render_key(&self) -> DiffKey {
111 self.key.clone().or(self.default_key())
112 }
113}
114
115#[derive(PartialEq, Clone)]
116pub struct DropZone<T: 'static + PartialEq + Clone> {
117 children: Element,
118 on_drop: EventHandler<T>,
119 width: Size,
120 height: Size,
121 key: DiffKey,
122}
123
124impl<T: Clone + PartialEq + 'static> KeyExt for DropZone<T> {
125 fn write_key(&mut self) -> &mut DiffKey {
126 &mut self.key
127 }
128}
129
130impl<T: PartialEq + Clone + 'static> DropZone<T> {
131 pub fn new(children: impl Into<Element>, on_drop: impl Into<EventHandler<T>>) -> Self {
132 Self {
133 children: children.into(),
134 on_drop: on_drop.into(),
135 width: Size::auto(),
136 height: Size::auto(),
137 key: DiffKey::default(),
138 }
139 }
140}
141
142impl<T: Clone + PartialEq + 'static> Component for DropZone<T> {
143 fn render(&self) -> impl IntoElement {
144 let mut drags = use_drag::<T>();
145 let on_drop = self.on_drop.clone();
146
147 let on_mouse_up = move |e: Event<MouseEventData>| {
148 e.stop_propagation();
149 if let Some(current_drags) = &*drags.read() {
150 on_drop.call(current_drags.clone());
151 }
152 if drags.read().is_some() {
153 *drags.write() = None;
154 }
155 };
156
157 rect()
158 .on_mouse_up(on_mouse_up)
159 .width(self.width.clone())
160 .height(self.height.clone())
161 .child(self.children.clone())
162 }
163
164 fn render_key(&self) -> DiffKey {
165 self.key.clone().or(self.default_key())
166 }
167}