vizia_core/modifiers/
drag.rs1use crate::prelude::*;
2use std::any::TypeId;
3
4pub(crate) struct DragModel {
5 pub(crate) on_drag_start: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
6 pub(crate) on_drag_enter: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
7 pub(crate) on_drag_leave: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
8 pub(crate) on_drag_move: Option<Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>>,
9 pub(crate) on_drop: Option<Box<dyn Fn(&mut EventContext, DropData) + Send + Sync>>,
10 pub(crate) dragging: Signal<bool>,
11}
12
13impl DragModel {
14 pub(crate) fn new() -> Self {
15 Self {
16 on_drag_start: None,
17 on_drag_enter: None,
18 on_drag_leave: None,
19 on_drag_move: None,
20 on_drop: None,
21 dragging: Signal::new(false),
22 }
23 }
24
25 fn try_start_drag(&mut self, cx: &mut EventContext) {
26 if cx.mouse.left.state == MouseButtonState::Pressed
27 && cx.mouse.left.pressed == cx.current()
28 && cx.is_draggable()
29 && reached_drag_distance_threshold(cx)
30 && !cx.has_drop_data()
31 {
32 if let Some(action) = &self.on_drag_start {
33 (action)(cx);
34 }
35
36 if cx.has_drop_data() {
37 cx.capture();
38 self.dragging.set(true);
39 }
40 }
41 }
42}
43
44pub(crate) enum DragEvent {
45 OnDragStart(Box<dyn Fn(&mut EventContext) + Send + Sync>),
46 OnDragEnter(Box<dyn Fn(&mut EventContext) + Send + Sync>),
47 OnDragLeave(Box<dyn Fn(&mut EventContext) + Send + Sync>),
48 OnDragMove(Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>),
49 OnDrop(Box<dyn Fn(&mut EventContext, DropData) + Send + Sync>),
50}
51
52fn reached_drag_distance_threshold(cx: &EventContext) -> bool {
53 let (press_x, press_y) = cx.mouse.left.pos_down;
54 let delta_x = cx.mouse.cursor_x - press_x;
55 let delta_y = cx.mouse.cursor_y - press_y;
56 let drag_distance = cx.environment().drag_distance.get() as f32;
57
58 delta_x * delta_x + delta_y * delta_y >= drag_distance * drag_distance
59}
60
61impl Model for DragModel {
62 fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
63 event.take(|drag_event, _| match drag_event {
64 DragEvent::OnDragStart(on_drag_start) => {
65 self.on_drag_start = Some(on_drag_start);
66 }
67
68 DragEvent::OnDragEnter(on_drag_enter) => {
69 self.on_drag_enter = Some(on_drag_enter);
70 }
71
72 DragEvent::OnDragLeave(on_drag_leave) => {
73 self.on_drag_leave = Some(on_drag_leave);
74 }
75
76 DragEvent::OnDragMove(on_drag_move) => {
77 self.on_drag_move = Some(on_drag_move);
78 }
79
80 DragEvent::OnDrop(on_drop) => {
81 self.on_drop = Some(on_drop);
82 }
83 });
84
85 event.map(|window_event, _meta| match window_event {
86 WindowEvent::Drop(drop_data) => {
87 if let Some(action) = &self.on_drop {
88 (action)(cx, drop_data.clone());
89 }
90 }
91
92 WindowEvent::DragEnter => {
93 if let Some(action) = &self.on_drag_enter {
94 (action)(cx);
95 }
96 }
97
98 WindowEvent::DragLeave => {
99 if let Some(action) = &self.on_drag_leave {
100 (action)(cx);
101 }
102 }
103
104 WindowEvent::DragMove(x, y) => {
105 if let Some(action) = &self.on_drag_move {
106 (action)(cx, *x, *y);
107 }
108 }
109
110 WindowEvent::MouseOut => {
111 self.try_start_drag(cx);
112 }
113
114 WindowEvent::MouseMove(_, _) => {
115 self.try_start_drag(cx);
116
117 if cx.mouse.left.state == MouseButtonState::Released {
118 self.dragging.set(false);
119 }
120 }
121
122 WindowEvent::MouseUp(MouseButton::Left) => {
123 cx.release();
126 self.dragging.set(false);
127 }
128
129 _ => {}
130 });
131 }
132}
133
134fn build_drag_model(cx: &mut Context, entity: Entity) {
135 if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<DragModel>())).is_none() {
136 cx.with_current(entity, |cx| {
137 DragModel::new().build(cx);
138 });
139 }
140}
141
142pub trait DragModifiers<V> {
144 fn on_drag<F>(self, action: F) -> Self
149 where
150 F: 'static + Fn(&mut EventContext) + Send + Sync;
151
152 fn on_drag_enter<F>(self, action: F) -> Self
154 where
155 F: 'static + Fn(&mut EventContext) + Send + Sync;
156
157 fn on_drag_leave<F>(self, action: F) -> Self
159 where
160 F: 'static + Fn(&mut EventContext) + Send + Sync;
161
162 fn on_drag_move<F>(self, action: F) -> Self
164 where
165 F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync;
166
167 fn on_drop<F>(self, action: F) -> Self
169 where
170 F: 'static + Fn(&mut EventContext, DropData) + Send + Sync;
171
172 fn on_drag_view<C: Fn(&mut Context) -> Handle<'_, T> + 'static, T: View>(
174 self,
175 content: C,
176 ) -> Self;
177}
178
179impl<V: View> DragModifiers<V> for Handle<'_, V> {
180 fn on_drag<F>(self, action: F) -> Self
181 where
182 F: 'static + Fn(&mut EventContext) + Send + Sync,
183 {
184 build_drag_model(self.cx, self.entity);
185
186 if let Some(abilities) = self.cx.style.abilities.get_mut(self.entity) {
187 abilities.set(Abilities::DRAGGABLE, true);
188 }
189
190 self.cx.emit_custom(
191 Event::new(DragEvent::OnDragStart(Box::new(action)))
192 .target(self.entity)
193 .origin(self.entity),
194 );
195
196 self
197 }
198
199 fn on_drag_enter<F>(self, action: F) -> Self
200 where
201 F: 'static + Fn(&mut EventContext) + Send + Sync,
202 {
203 build_drag_model(self.cx, self.entity);
204
205 self.cx.emit_custom(
206 Event::new(DragEvent::OnDragEnter(Box::new(action)))
207 .target(self.entity)
208 .origin(self.entity),
209 );
210
211 self
212 }
213
214 fn on_drag_leave<F>(self, action: F) -> Self
215 where
216 F: 'static + Fn(&mut EventContext) + Send + Sync,
217 {
218 build_drag_model(self.cx, self.entity);
219
220 self.cx.emit_custom(
221 Event::new(DragEvent::OnDragLeave(Box::new(action)))
222 .target(self.entity)
223 .origin(self.entity),
224 );
225
226 self
227 }
228
229 fn on_drag_move<F>(self, action: F) -> Self
230 where
231 F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync,
232 {
233 build_drag_model(self.cx, self.entity);
234
235 self.cx.emit_custom(
236 Event::new(DragEvent::OnDragMove(Box::new(action)))
237 .target(self.entity)
238 .origin(self.entity),
239 );
240
241 self
242 }
243
244 fn on_drop<F>(self, action: F) -> Self
245 where
246 F: 'static + Fn(&mut EventContext, DropData) + Send + Sync,
247 {
248 build_drag_model(self.cx, self.entity);
249
250 self.cx.emit_custom(
251 Event::new(DragEvent::OnDrop(Box::new(action))).target(self.entity).origin(self.entity),
252 );
253
254 self
255 }
256
257 fn on_drag_view<C: Fn(&mut Context) -> Handle<'_, T> + 'static, T: View>(
258 self,
259 content: C,
260 ) -> Self {
261 build_drag_model(self.cx, self.entity);
262
263 if let Some(abilities) = self.cx.style.abilities.get_mut(self.entity) {
264 abilities.set(Abilities::DRAGGABLE, true);
265 }
266
267 let source_entity = self.entity;
268
269 self.cx.with_current(source_entity, move |cx| {
270 let is_dragging = cx.data::<DragModel>().dragging;
271 let preview_entity: Signal<Option<Entity>> = Signal::new(None);
272 Binding::new(cx, is_dragging, move |cx| {
273 if is_dragging.get() {
274 if let Some(prev) = preview_entity.get() {
276 cx.remove(prev);
277 }
278
279 let window_entity = cx.parent_window();
280 let drag_entity = cx.with_current(window_entity, |cx| {
281 (content)(cx)
282 .position_type(PositionType::Absolute)
283 .left(Pixels(0.0))
284 .top(Pixels(0.0))
285 .display(Display::None)
286 .pointer_events(PointerEvents::None)
287 .z_index(10_000)
288 .entity()
289 });
290
291 preview_entity.set(Some(drag_entity));
292
293 let mut ex = EventContext::new(cx);
294 ex.set_active_drag_view(Some(drag_entity));
295 } else {
296 if let Some(entity) = preview_entity.get() {
298 cx.remove(entity);
299 preview_entity.set(None);
300 }
301
302 let mut ex = EventContext::new(cx);
303 ex.set_active_drag_view(None);
304 }
305 });
306 });
307
308 self
309 }
310}