1#![cfg(feature = "drag_drop")]
2
3use std::{mem, sync::Arc};
6
7use parking_lot::Mutex;
8use zng_app::{
9 AppExtension,
10 event::{AnyEventArgs, event, event_args},
11 static_id,
12 update::{EventUpdate, UPDATES},
13 view_process::raw_events::{
14 RAW_APP_DRAG_ENDED_EVENT, RAW_DRAG_CANCELLED_EVENT, RAW_DRAG_DROPPED_EVENT, RAW_DRAG_HOVERED_EVENT, RAW_DRAG_MOVED_EVENT,
15 },
16 widget::{
17 WIDGET, WidgetId,
18 info::{HitTestInfo, InteractionPath, WidgetInfo, WidgetInfoBuilder},
19 },
20 window::WindowId,
21};
22use zng_app_context::app_local;
23use zng_ext_window::{NestedWindowWidgetInfoExt as _, WINDOWS, WINDOWS_DRAG_DROP};
24use zng_handle::{Handle, HandleOwner, WeakHandle};
25use zng_layout::unit::{DipPoint, DipToPx as _, PxToDip as _};
26use zng_state_map::StateId;
27use zng_txt::{Txt, formatx};
28use zng_var::{ArcVar, ReadOnlyArcVar, Var, var};
29use zng_view_api::{DragDropId, mouse::ButtonState, touch::TouchPhase};
30
31use crate::{mouse::MOUSE_INPUT_EVENT, touch::TOUCH_INPUT_EVENT};
32
33pub use zng_view_api::drag_drop::{DragDropData, DragDropEffect};
34
35#[derive(Default)]
54pub struct DragDropManager {
55 pos: DipPoint,
57 pos_window: Option<WindowId>,
59 hits: Option<HitTestInfo>,
61 hovered: Option<InteractionPath>,
62}
63
64impl AppExtension for DragDropManager {
65 fn event_preview(&mut self, update: &mut EventUpdate) {
66 let mut update_sv = false;
67 if let Some(args) = RAW_DRAG_DROPPED_EVENT.on(update) {
68 let mut sv = DRAG_DROP_SV.write();
70 let len = sv.system_dragging.len();
71 for data in &args.data {
72 sv.system_dragging.retain(|d| d != data);
73 }
74 update_sv = len != sv.system_dragging.len();
75
76 if self.pos_window == Some(args.window_id) {
79 if let Some(hovered) = &self.hovered {
80 match &mut sv.pending_drop {
81 Some((id, target, data, allowed)) => {
82 if target != hovered {
83 tracing::error!("drop sequence across different hovered")
84 } else if *id != args.drop_id {
85 tracing::error!("drop_id changed mid sequence")
86 } else if *allowed != args.allowed {
87 tracing::error!("allowed effects changed mid sequence")
88 } else {
89 data.extend(args.data.iter().cloned());
90 }
91 }
92 None => sv.pending_drop = Some((args.drop_id, hovered.clone(), args.data.clone(), args.allowed)),
93 }
94 }
95 }
96 UPDATES.update(None);
97 } else if let Some(args) = RAW_DRAG_HOVERED_EVENT.on(update) {
98 update_sv = true;
100 DRAG_DROP_SV.write().system_dragging.extend(args.data.iter().cloned());
101 } else if let Some(args) = RAW_DRAG_MOVED_EVENT.on(update) {
102 let moved = self.pos != args.position || self.pos_window != Some(args.window_id);
104 if moved {
105 self.pos = args.position;
106 self.pos_window = Some(args.window_id);
107
108 let mut position = args.position;
109
110 let mut frame_info = match WINDOWS.widget_tree(args.window_id) {
112 Ok(f) => f,
113 Err(_) => {
114 if let Some(hovered) = self.hovered.take() {
116 DRAG_HOVERED_EVENT.notify(DragHoveredArgs::now(
117 Some(hovered),
118 None,
119 position,
120 HitTestInfo::no_hits(args.window_id),
121 ));
122 self.pos_window = None;
123 }
124 return;
125 }
126 };
127
128 let mut pos_hits = frame_info.root().hit_test(position.to_px(frame_info.scale_factor()));
129
130 let target = if let Some(t) = pos_hits.target() {
131 if let Some(w) = frame_info.get(t.widget_id) {
132 if let Some(f) = w.nested_window_tree() {
133 frame_info = f;
135 let factor = frame_info.scale_factor();
136 let pos = position.to_px(factor);
137 let pos = w.inner_transform().inverse().and_then(|t| t.transform_point(pos)).unwrap_or(pos);
138 pos_hits = frame_info.root().hit_test(pos);
139 position = pos.to_dip(factor);
140 pos_hits
141 .target()
142 .and_then(|h| frame_info.get(h.widget_id))
143 .map(|w| w.interaction_path())
144 .unwrap_or_else(|| frame_info.root().interaction_path())
145 } else {
146 w.interaction_path()
147 }
148 } else {
149 tracing::error!("hits target `{}` not found", t.widget_id);
150 frame_info.root().interaction_path()
151 }
152 } else {
153 frame_info.root().interaction_path()
154 }
155 .unblocked();
156
157 self.hits = Some(pos_hits.clone());
158
159 let hovered_args = if self.hovered != target {
161 let prev_target = mem::replace(&mut self.hovered, target.clone());
162 let args = DragHoveredArgs::now(prev_target, target.clone(), position, pos_hits.clone());
163 Some(args)
164 } else {
165 None
166 };
167
168 if let Some(target) = target {
170 let args = DragMoveArgs::now(frame_info.window_id(), args.coalesced_pos.clone(), position, pos_hits, target);
171 DRAG_MOVE_EVENT.notify(args);
172 }
173
174 if let Some(args) = hovered_args {
175 DRAG_HOVERED_EVENT.notify(args);
176 }
177 }
178 } else if let Some(args) = RAW_DRAG_CANCELLED_EVENT.on(update) {
179 let mut sv = DRAG_DROP_SV.write();
181 update_sv = !sv.system_dragging.is_empty();
182 sv.system_dragging.clear();
183
184 if let Some(prev) = self.hovered.take() {
185 self.pos_window = None;
186 DRAG_HOVERED_EVENT.notify(DragHoveredArgs::now(
187 Some(prev),
188 None,
189 self.pos,
190 self.hits.take().unwrap_or_else(|| HitTestInfo::no_hits(args.window_id)),
191 ));
192 }
193 } else if let Some(args) = RAW_APP_DRAG_ENDED_EVENT.on(update) {
194 let mut sv = DRAG_DROP_SV.write();
195 sv.app_dragging.retain(|d| {
196 if d.view_id != args.id {
197 return true;
198 }
199
200 if !args.applied.is_empty() && !d.allowed.contains(args.applied) {
201 tracing::error!(
202 "drop target applied disallowed effect, allowed={:?}, applied={:?}",
203 d.allowed,
204 args.applied
205 );
206 }
207
208 DRAG_END_EVENT.notify(DragEndArgs::now(d.target.clone(), args.applied));
209
210 false
211 });
212 }
213
214 if update_sv {
215 DRAG_DROP.update_var();
216 }
217 }
218
219 fn event(&mut self, update: &mut EventUpdate) {
220 if let Some(args) = MOUSE_INPUT_EVENT.on_unhandled(update) {
221 if matches!(args.state, ButtonState::Pressed) {
222 if let Some(wgt) = WINDOWS.widget_info(args.target.widget_id()) {
223 if let Some(wgt) = wgt.self_and_ancestors().find(|w| w.is_draggable()) {
224 args.propagation().stop();
226 let target = wgt.interaction_path();
227 let args = DragStartArgs::now(target.clone());
228 DRAG_START_EVENT.notify(args);
229 DRAG_DROP_SV.write().app_drag = Some(AppDragging {
230 target,
231 data: vec![],
232 handles: vec![],
233 allowed: DragDropEffect::empty(),
234 view_id: DragDropId(0),
235 }); }
237 }
238 }
239 } else if let Some(args) = TOUCH_INPUT_EVENT.on_unhandled(update) {
240 if matches!(args.phase, TouchPhase::Start) {
241 if let Some(wgt) = WINDOWS.widget_info(args.target.widget_id()) {
242 if let Some(wgt) = wgt.self_and_ancestors().find(|w| w.is_draggable()) {
243 args.propagation().stop();
245 let target = wgt.interaction_path();
246 let args = DragStartArgs::now(target.clone());
247 DRAG_START_EVENT.notify(args);
248 DRAG_DROP_SV.write().app_drag = Some(AppDragging {
249 target,
250 data: vec![],
251 handles: vec![],
252 allowed: DragDropEffect::empty(),
253 view_id: DragDropId(0),
254 }); }
256 }
257 }
258 } else if let Some(args) = DRAG_START_EVENT.on(update) {
259 let mut sv = DRAG_DROP_SV.write();
261 let mut data = sv.app_drag.take();
262 let mut cancel = args.propagation_handle.is_stopped();
263 if !cancel {
264 if let Some(d) = &mut data {
265 if d.data.is_empty() {
266 d.data.push(encode_widget_id(args.target.widget_id()));
267 d.allowed = DragDropEffect::all();
268 }
269 match WINDOWS_DRAG_DROP.start_drag_drop(d.target.window_id(), mem::take(&mut d.data), d.allowed) {
270 Ok(id) => {
271 d.view_id = id;
272 sv.app_dragging.push(data.take().unwrap());
273 }
274 Err(e) => {
275 tracing::error!("cannot start drag&drop, {e}");
276 cancel = true;
277 }
278 }
279 } else {
280 tracing::warn!("external notification of DRAG_START_EVENT ignored")
281 }
282 }
283 if cancel {
284 if let Some(d) = data {
285 DRAG_END_EVENT.notify(DragEndArgs::now(d.target, DragDropEffect::empty()));
286 }
287 }
288 } else if let Some(args) = DROP_EVENT.on(update) {
289 let _ = WINDOWS_DRAG_DROP.drag_dropped(args.target.window_id(), args.drop_id, *args.applied.lock());
290 }
291 }
292
293 fn update_preview(&mut self) {
294 let mut sv = DRAG_DROP_SV.write();
295
296 if let Some((id, target, data, allowed)) = sv.pending_drop.take() {
298 let window_id = self.pos_window.take().unwrap();
299 let hits = self.hits.take().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
300 DRAG_HOVERED_EVENT.notify(DragHoveredArgs::now(Some(target.clone()), None, self.pos, hits.clone()));
301 DROP_EVENT.notify(DropArgs::now(
302 target,
303 data,
304 allowed,
305 self.pos,
306 hits,
307 id,
308 Arc::new(Mutex::new(DragDropEffect::empty())),
309 ));
310 }
311 }
312}
313
314#[allow(non_camel_case_types)]
316pub struct DRAG_DROP;
317impl DRAG_DROP {
318 pub fn dragging_data(&self) -> ReadOnlyArcVar<Vec<DragDropData>> {
320 DRAG_DROP_SV.read().data.read_only()
321 }
322
323 pub fn drag(&self, data: DragDropData, allowed_effects: DragDropEffect) -> DragHandle {
334 let mut sv = DRAG_DROP_SV.write();
335 if let Some(d) = &mut sv.app_drag {
336 if allowed_effects.is_empty() {
337 tracing::error!("cannot drag, no `allowed_effects`");
338 return DragHandle::dummy();
339 }
340
341 if d.allowed.is_empty() {
342 d.allowed = allowed_effects;
343 } else {
344 if !d.allowed.contains(allowed_effects) {
345 tracing::error!("cannot drag, other data already set with incompatible `allowed_effects`");
346 return DragHandle::dummy();
347 }
348 d.allowed |= allowed_effects
349 }
350
351 d.data.push(data);
352 let (owner, handle) = DragHandle::new();
353 d.handles.push(owner);
354 return handle;
355 }
356 tracing::error!("cannot drag, not in `DRAG_START_EVENT` interval");
357 DragHandle::dummy()
358 }
359
360 fn update_var(&self) {
361 let sv = DRAG_DROP_SV.read();
362 sv.data.set(sv.system_dragging.clone());
363 }
364}
365
366app_local! {
367 static DRAG_DROP_SV: DragDropService = DragDropService {
368 data: var(vec![]),
369 system_dragging: vec![],
370 app_drag: None,
371 app_dragging: vec![],
372 pending_drop: None,
373 };
374}
375struct DragDropService {
376 data: ArcVar<Vec<DragDropData>>,
377
378 system_dragging: Vec<DragDropData>,
379
380 app_drag: Option<AppDragging>,
381 app_dragging: Vec<AppDragging>,
382
383 pending_drop: Option<(DragDropId, InteractionPath, Vec<DragDropData>, DragDropEffect)>,
384}
385struct AppDragging {
386 target: InteractionPath,
387 data: Vec<DragDropData>,
388 handles: Vec<HandleOwner<()>>,
389 allowed: DragDropEffect,
390 view_id: DragDropId,
391}
392
393#[derive(Clone, PartialEq, Eq, Hash, Debug)]
397#[repr(transparent)]
398#[must_use = "dropping the handle cancels the drag operation"]
399pub struct DragHandle(Handle<()>);
400impl DragHandle {
401 fn new() -> (HandleOwner<()>, Self) {
402 let (owner, handle) = Handle::new(());
403 (owner, Self(handle))
404 }
405
406 pub fn dummy() -> Self {
408 Self(Handle::dummy(()))
409 }
410
411 pub fn perm(self) {
415 self.0.perm();
416 }
417
418 pub fn is_permanent(&self) -> bool {
422 self.0.is_permanent()
423 }
424
425 pub fn cancel(self) {
427 self.0.force_drop()
428 }
429
430 pub fn is_canceled(&self) -> bool {
432 self.0.is_dropped()
433 }
434
435 pub fn downgrade(&self) -> WeakDragHandle {
437 WeakDragHandle(self.0.downgrade())
438 }
439}
440#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
442pub struct WeakDragHandle(WeakHandle<()>);
443impl WeakDragHandle {
444 pub fn new() -> Self {
446 Self(WeakHandle::new())
447 }
448
449 pub fn upgrade(&self) -> Option<DragHandle> {
451 self.0.upgrade().map(DragHandle)
452 }
453}
454
455pub trait WidgetInfoDragDropExt {
457 fn is_draggable(&self) -> bool;
459}
460impl WidgetInfoDragDropExt for WidgetInfo {
461 fn is_draggable(&self) -> bool {
462 self.meta().flagged(*IS_DRAGGABLE_ID)
463 }
464}
465
466pub trait WidgetInfoBuilderDragDropExt {
468 fn draggable(&mut self);
470}
471impl WidgetInfoBuilderDragDropExt for WidgetInfoBuilder {
472 fn draggable(&mut self) {
473 self.flag_meta(*IS_DRAGGABLE_ID);
474 }
475}
476
477static_id! {
478 static ref IS_DRAGGABLE_ID: StateId<()>;
479}
480
481event_args! {
482 pub struct DropArgs {
484 pub target: InteractionPath,
486 pub data: Vec<DragDropData>,
488 pub allowed: DragDropEffect,
490 pub position: DipPoint,
492 pub hits: HitTestInfo,
494
495 drop_id: DragDropId,
496 applied: Arc<Mutex<DragDropEffect>>,
497
498 ..
499
500 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
501 list.insert_wgt(&self.target);
502 }
503 }
504
505 pub struct DragHoveredArgs {
507 pub prev_target: Option<InteractionPath>,
509 pub target: Option<InteractionPath>,
511 pub position: DipPoint,
513 pub hits: HitTestInfo,
515
516 ..
517
518 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
519 if let Some(p) = &self.prev_target {
520 list.insert_wgt(p);
521 }
522 if let Some(p) = &self.target {
523 list.insert_wgt(p);
524 }
525 }
526 }
527
528 pub struct DragMoveArgs {
530 pub window_id: WindowId,
532
533 pub coalesced_pos: Vec<DipPoint>,
537
538 pub position: DipPoint,
540
541 pub hits: HitTestInfo,
543
544 pub target: InteractionPath,
546
547 ..
548
549 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
553 list.insert_wgt(&self.target);
554 }
555 }
556
557 pub struct DragStartArgs {
559 pub target: InteractionPath,
561
562 ..
563
564 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
565 list.insert_wgt(&self.target);
566 }
567 }
568
569 pub struct DragEndArgs {
571 pub target: InteractionPath,
573
574 pub applied: DragDropEffect,
578
579 ..
580
581 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
582 list.insert_wgt(&self.target);
583 }
584
585 fn validate(&self) -> Result<(), Txt> {
587 if self.applied.is_empty() && self.applied.len() > 1 {
588 return Err("only one or none `DragDropEffect` can be applied".into());
589 }
590 Ok(())
591 }
592 }
593}
594event! {
595 pub static DROP_EVENT: DropArgs;
597 pub static DRAG_HOVERED_EVENT: DragHoveredArgs;
599 pub static DRAG_MOVE_EVENT: DragMoveArgs;
601 pub static DRAG_START_EVENT: DragStartArgs;
608
609 pub static DRAG_END_EVENT: DragEndArgs;
611}
612
613impl DropArgs {
614 pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
618 self.target.interactivity_of(widget_id).map(|i| i.is_enabled()).unwrap_or(false)
619 }
620
621 pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
625 self.target.interactivity_of(widget_id).map(|i| i.is_disabled()).unwrap_or(false)
626 }
627
628 pub fn applied(&self, effect: DragDropEffect) {
638 assert!(effect.len() > 1, "can only apply one effect");
639 assert!(self.allowed.contains(effect), "source does not allow this effect");
640
641 let mut e = self.applied.lock();
642 if !self.propagation().is_stopped() {
643 self.propagation().stop();
644 *e = effect;
645 } else {
646 tracing::error!("drop already handled");
647 }
648 }
649}
650
651impl DragHoveredArgs {
652 pub fn data(&self) -> ReadOnlyArcVar<Vec<DragDropData>> {
656 DRAG_DROP.dragging_data()
657 }
658
659 pub fn is_drag_enter(&self) -> bool {
663 !self.was_over() && self.is_over()
664 }
665
666 pub fn is_drag_leave(&self) -> bool {
670 self.was_over() && !self.is_over()
671 }
672
673 pub fn was_over(&self) -> bool {
678 if let Some(t) = &self.prev_target {
679 return t.contains(WIDGET.id());
680 }
681
682 false
683 }
684
685 pub fn is_over(&self) -> bool {
690 if let Some(t) = &self.target {
691 return t.contains(WIDGET.id());
692 }
693
694 false
695 }
696
697 pub fn was_enabled(&self, widget_id: WidgetId) -> bool {
701 self.prev_target
702 .as_ref()
703 .and_then(|t| t.interactivity_of(widget_id))
704 .map(|itr| itr.is_enabled())
705 .unwrap_or(false)
706 }
707
708 pub fn was_disabled(&self, widget_id: WidgetId) -> bool {
712 self.prev_target
713 .as_ref()
714 .and_then(|t| t.interactivity_of(widget_id))
715 .map(|itr| itr.is_disabled())
716 .unwrap_or(false)
717 }
718
719 pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
723 self.target
724 .as_ref()
725 .and_then(|t| t.interactivity_of(widget_id))
726 .map(|itr| itr.is_enabled())
727 .unwrap_or(false)
728 }
729
730 pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
734 self.target
735 .as_ref()
736 .and_then(|t| t.interactivity_of(widget_id))
737 .map(|itr| itr.is_disabled())
738 .unwrap_or(false)
739 }
740
741 pub fn is_drag_enter_enabled(&self) -> bool {
745 (!self.was_over() || self.was_disabled(WIDGET.id())) && self.is_over() && self.is_enabled(WIDGET.id())
746 }
747
748 pub fn is_drag_leave_enabled(&self) -> bool {
752 self.was_over() && self.was_enabled(WIDGET.id()) && (!self.is_over() || self.is_disabled(WIDGET.id()))
753 }
754
755 pub fn is_drag_enter_disabled(&self) -> bool {
759 (!self.was_over() || self.was_enabled(WIDGET.id())) && self.is_over() && self.is_disabled(WIDGET.id())
760 }
761
762 pub fn is_drag_leave_disabled(&self) -> bool {
766 self.was_over() && self.was_disabled(WIDGET.id()) && (!self.is_over() || self.is_enabled(WIDGET.id()))
767 }
768}
769
770impl DragEndArgs {
771 pub fn was_dropped(&self) -> bool {
773 !self.applied.is_empty()
774 }
775
776 pub fn was_canceled(&self) -> bool {
778 self.applied.is_empty()
779 }
780}
781
782pub fn encode_widget_id(id: WidgetId) -> DragDropData {
784 DragDropData::Text {
785 format: formatx!("zng/{}", APP_GUID.read().simple()),
786 data: formatx!("wgt-{}", id.get()),
787 }
788}
789
790pub fn decode_widget_id(data: &DragDropData) -> Option<WidgetId> {
794 if let DragDropData::Text { format, data } = data {
795 if let Some(guid) = format.strip_prefix("zng/") {
796 if let Some(id) = data.strip_prefix("wgt-") {
797 if guid == APP_GUID.read().simple().to_string() {
798 if let Ok(id) = id.parse::<u64>() {
799 return Some(WidgetId::from_raw(id));
800 }
801 }
802 }
803 }
804 }
805 None
806}
807
808app_local! {
809 static APP_GUID: uuid::Uuid = uuid::Uuid::new_v4();
810}