1#![cfg(feature = "drag_drop")]
2
3use std::{mem, sync::Arc};
6
7use parking_lot::Mutex;
8use zng_app::{
9 APP, 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::{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 && let Some(hovered) = &self.hovered
80 {
81 match &mut sv.pending_drop {
82 Some((id, target, data, allowed)) => {
83 if target != hovered {
84 tracing::error!("drop sequence across different hovered")
85 } else if *id != args.drop_id {
86 tracing::error!("drop_id changed mid sequence")
87 } else if *allowed != args.allowed {
88 tracing::error!("allowed effects changed mid sequence")
89 } else {
90 data.extend(args.data.iter().cloned());
91 }
92 }
93 None => sv.pending_drop = Some((args.drop_id, hovered.clone(), args.data.clone(), args.allowed)),
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 && let Some(wgt) = WINDOWS.widget_info(args.target.widget_id())
223 && let Some(wgt) = wgt.self_and_ancestors().find(|w| w.is_draggable())
224 {
225 args.propagation().stop();
227 let target = wgt.interaction_path();
228 let args = DragStartArgs::now(target.clone());
229 DRAG_START_EVENT.notify(args);
230 DRAG_DROP_SV.write().app_drag = Some(AppDragging {
231 target,
232 data: vec![],
233 handles: vec![],
234 allowed: DragDropEffect::empty(),
235 view_id: DragDropId(0),
236 }); }
238 } else if let Some(args) = TOUCH_INPUT_EVENT.on_unhandled(update) {
239 if matches!(args.phase, TouchPhase::Start)
240 && let Some(wgt) = WINDOWS.widget_info(args.target.widget_id())
241 && let Some(wgt) = wgt.self_and_ancestors().find(|w| w.is_draggable())
242 {
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 } else if let Some(args) = DRAG_START_EVENT.on(update) {
257 let mut sv = DRAG_DROP_SV.write();
259 let mut data = sv.app_drag.take();
260 let mut cancel = args.propagation_handle.is_stopped();
261 if !cancel {
262 if let Some(d) = &mut data {
263 if d.data.is_empty() {
264 d.data.push(encode_widget_id(args.target.widget_id()));
265 d.allowed = DragDropEffect::all();
266 }
267 match WINDOWS_DRAG_DROP.start_drag_drop(d.target.window_id(), mem::take(&mut d.data), d.allowed) {
268 Ok(id) => {
269 d.view_id = id;
270 sv.app_dragging.push(data.take().unwrap());
271 }
272 Err(e) => {
273 tracing::error!("cannot start drag&drop, {e}");
274 cancel = true;
275 }
276 }
277 } else {
278 tracing::warn!("external notification of DRAG_START_EVENT ignored")
279 }
280 }
281 if cancel && let Some(d) = data {
282 DRAG_END_EVENT.notify(DragEndArgs::now(d.target, DragDropEffect::empty()));
283 }
284 } else if let Some(args) = DROP_EVENT.on(update) {
285 let _ = WINDOWS_DRAG_DROP.drag_dropped(args.target.window_id(), args.drop_id, *args.applied.lock());
286 }
287 }
288
289 fn update_preview(&mut self) {
290 let mut sv = DRAG_DROP_SV.write();
291
292 if let Some((id, target, data, allowed)) = sv.pending_drop.take() {
294 let window_id = self.pos_window.take().unwrap();
295 let hits = self.hits.take().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
296 DRAG_HOVERED_EVENT.notify(DragHoveredArgs::now(Some(target.clone()), None, self.pos, hits.clone()));
297 DROP_EVENT.notify(DropArgs::now(
298 target,
299 data,
300 allowed,
301 self.pos,
302 hits,
303 id,
304 Arc::new(Mutex::new(DragDropEffect::empty())),
305 ));
306 }
307 }
308}
309
310#[allow(non_camel_case_types)]
316pub struct DRAG_DROP;
317impl DRAG_DROP {
318 pub fn dragging_data(&self) -> Var<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 = {
368 APP.extensions().require::<DragDropManager>();
369 DragDropService {
370 data: var(vec![]),
371 system_dragging: vec![],
372 app_drag: None,
373 app_dragging: vec![],
374 pending_drop: None,
375 }
376 };
377}
378struct DragDropService {
379 data: Var<Vec<DragDropData>>,
380
381 system_dragging: Vec<DragDropData>,
382
383 app_drag: Option<AppDragging>,
384 app_dragging: Vec<AppDragging>,
385
386 pending_drop: Option<(DragDropId, InteractionPath, Vec<DragDropData>, DragDropEffect)>,
387}
388struct AppDragging {
389 target: InteractionPath,
390 data: Vec<DragDropData>,
391 handles: Vec<HandleOwner<()>>,
392 allowed: DragDropEffect,
393 view_id: DragDropId,
394}
395
396#[derive(Clone, PartialEq, Eq, Hash, Debug)]
400#[repr(transparent)]
401#[must_use = "dropping the handle cancels the drag operation"]
402pub struct DragHandle(Handle<()>);
403impl DragHandle {
404 fn new() -> (HandleOwner<()>, Self) {
405 let (owner, handle) = Handle::new(());
406 (owner, Self(handle))
407 }
408
409 pub fn dummy() -> Self {
411 Self(Handle::dummy(()))
412 }
413
414 pub fn perm(self) {
418 self.0.perm();
419 }
420
421 pub fn is_permanent(&self) -> bool {
425 self.0.is_permanent()
426 }
427
428 pub fn cancel(self) {
430 self.0.force_drop()
431 }
432
433 pub fn is_canceled(&self) -> bool {
435 self.0.is_dropped()
436 }
437
438 pub fn downgrade(&self) -> WeakDragHandle {
440 WeakDragHandle(self.0.downgrade())
441 }
442}
443#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
445pub struct WeakDragHandle(WeakHandle<()>);
446impl WeakDragHandle {
447 pub fn new() -> Self {
449 Self(WeakHandle::new())
450 }
451
452 pub fn upgrade(&self) -> Option<DragHandle> {
454 self.0.upgrade().map(DragHandle)
455 }
456}
457
458pub trait WidgetInfoDragDropExt {
460 fn is_draggable(&self) -> bool;
462}
463impl WidgetInfoDragDropExt for WidgetInfo {
464 fn is_draggable(&self) -> bool {
465 self.meta().flagged(*IS_DRAGGABLE_ID)
466 }
467}
468
469pub trait WidgetInfoBuilderDragDropExt {
471 fn draggable(&mut self);
473}
474impl WidgetInfoBuilderDragDropExt for WidgetInfoBuilder {
475 fn draggable(&mut self) {
476 self.flag_meta(*IS_DRAGGABLE_ID);
477 }
478}
479
480static_id! {
481 static ref IS_DRAGGABLE_ID: StateId<()>;
482}
483
484event_args! {
485 pub struct DropArgs {
487 pub target: InteractionPath,
489 pub data: Vec<DragDropData>,
491 pub allowed: DragDropEffect,
493 pub position: DipPoint,
495 pub hits: HitTestInfo,
497
498 drop_id: DragDropId,
499 applied: Arc<Mutex<DragDropEffect>>,
500
501 ..
502
503 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
504 list.insert_wgt(&self.target);
505 }
506 }
507
508 pub struct DragHoveredArgs {
510 pub prev_target: Option<InteractionPath>,
512 pub target: Option<InteractionPath>,
514 pub position: DipPoint,
516 pub hits: HitTestInfo,
518
519 ..
520
521 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
522 if let Some(p) = &self.prev_target {
523 list.insert_wgt(p);
524 }
525 if let Some(p) = &self.target {
526 list.insert_wgt(p);
527 }
528 }
529 }
530
531 pub struct DragMoveArgs {
533 pub window_id: WindowId,
535
536 pub coalesced_pos: Vec<DipPoint>,
540
541 pub position: DipPoint,
543
544 pub hits: HitTestInfo,
546
547 pub target: InteractionPath,
549
550 ..
551
552 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
556 list.insert_wgt(&self.target);
557 }
558 }
559
560 pub struct DragStartArgs {
562 pub target: InteractionPath,
564
565 ..
566
567 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
568 list.insert_wgt(&self.target);
569 }
570 }
571
572 pub struct DragEndArgs {
574 pub target: InteractionPath,
576
577 pub applied: DragDropEffect,
581
582 ..
583
584 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
585 list.insert_wgt(&self.target);
586 }
587
588 fn validate(&self) -> Result<(), Txt> {
590 if self.applied.is_empty() && self.applied.len() > 1 {
591 return Err("only one or none `DragDropEffect` can be applied".into());
592 }
593 Ok(())
594 }
595 }
596}
597event! {
598 pub static DROP_EVENT: DropArgs;
600 pub static DRAG_HOVERED_EVENT: DragHoveredArgs;
602 pub static DRAG_MOVE_EVENT: DragMoveArgs;
604 pub static DRAG_START_EVENT: DragStartArgs;
611
612 pub static DRAG_END_EVENT: DragEndArgs;
614}
615
616impl DropArgs {
617 pub fn applied(&self, effect: DragDropEffect) {
627 assert!(effect.len() > 1, "can only apply one effect");
628 assert!(self.allowed.contains(effect), "source does not allow this effect");
629
630 let mut e = self.applied.lock();
631 if !self.propagation().is_stopped() {
632 self.propagation().stop();
633 *e = effect;
634 } else {
635 tracing::error!("drop already handled");
636 }
637 }
638}
639
640impl DragHoveredArgs {
641 pub fn data(&self) -> Var<Vec<DragDropData>> {
645 DRAG_DROP.dragging_data()
646 }
647
648 pub fn is_drag_enter(&self) -> bool {
652 !self.was_over() && self.is_over()
653 }
654
655 pub fn is_drag_leave(&self) -> bool {
659 self.was_over() && !self.is_over()
660 }
661
662 pub fn was_over(&self) -> bool {
667 if let Some(t) = &self.prev_target {
668 return t.contains(WIDGET.id());
669 }
670
671 false
672 }
673
674 pub fn is_over(&self) -> bool {
679 if let Some(t) = &self.target {
680 return t.contains(WIDGET.id());
681 }
682
683 false
684 }
685
686 pub fn was_enabled(&self, widget_id: WidgetId) -> bool {
690 match &self.prev_target {
691 Some(t) => t.contains_enabled(widget_id),
692 None => false,
693 }
694 }
695
696 pub fn was_disabled(&self, widget_id: WidgetId) -> bool {
700 match &self.prev_target {
701 Some(t) => t.contains_disabled(widget_id),
702 None => false,
703 }
704 }
705
706 pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
710 match &self.target {
711 Some(t) => t.contains_enabled(widget_id),
712 None => false,
713 }
714 }
715
716 pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
720 match &self.target {
721 Some(t) => t.contains_disabled(widget_id),
722 None => false,
723 }
724 }
725
726 pub fn is_drag_enter_enabled(&self) -> bool {
730 (!self.was_over() || self.was_disabled(WIDGET.id())) && self.is_over() && self.is_enabled(WIDGET.id())
731 }
732
733 pub fn is_drag_leave_enabled(&self) -> bool {
737 self.was_over() && self.was_enabled(WIDGET.id()) && (!self.is_over() || self.is_disabled(WIDGET.id()))
738 }
739
740 pub fn is_drag_enter_disabled(&self) -> bool {
744 (!self.was_over() || self.was_enabled(WIDGET.id())) && self.is_over() && self.is_disabled(WIDGET.id())
745 }
746
747 pub fn is_drag_leave_disabled(&self) -> bool {
751 self.was_over() && self.was_disabled(WIDGET.id()) && (!self.is_over() || self.is_enabled(WIDGET.id()))
752 }
753}
754
755impl DragEndArgs {
756 pub fn was_dropped(&self) -> bool {
758 !self.applied.is_empty()
759 }
760
761 pub fn was_canceled(&self) -> bool {
763 self.applied.is_empty()
764 }
765}
766
767pub fn encode_widget_id(id: WidgetId) -> DragDropData {
769 DragDropData::Text {
770 format: formatx!("zng/{}", APP_GUID.read().simple()),
771 data: formatx!("wgt-{}", id.get()),
772 }
773}
774
775pub fn decode_widget_id(data: &DragDropData) -> Option<WidgetId> {
779 if let DragDropData::Text { format, data } = data
780 && let Some(guid) = format.strip_prefix("zng/")
781 && let Some(id) = data.strip_prefix("wgt-")
782 && guid == APP_GUID.read().simple().to_string()
783 && let Ok(id) = id.parse::<u64>()
784 {
785 return Some(WidgetId::from_raw(id));
786 }
787 None
788}
789
790app_local! {
791 static APP_GUID: uuid::Uuid = uuid::Uuid::new_v4();
792}