dioxus_dnd/core/world/
drag.rs1use std::sync::atomic::{AtomicU64, Ordering};
5
6use dioxus::prelude::*;
7
8use crate::core::types::{DragSessionId, Point};
9
10use super::geometry::WindowKey;
11use super::state::{DndWorld, WindowRecord, ZoneLocation};
12
13static NEXT_WORLD_DRAG_GENERATION: AtomicU64 = AtomicU64::new(1);
14
15#[derive(Debug, Clone, Copy, PartialEq)]
16pub(super) struct ActiveDrag {
17 pub(super) origin: WindowKey,
18 pub(super) generation: u64,
22 pub(super) session: Option<DragSessionId>,
23 pub(super) origin_scale: f64,
24 pub(super) source_location: Option<ZoneLocation>,
25 pub(super) modifiers: Modifiers,
26}
27
28impl<T: Clone + 'static> DndWorld<T> {
29 pub fn begin_from(&self, key: WindowKey) {
33 let origin = self.record(key);
34 let active_drag = ActiveDrag {
35 origin: key,
36 generation: NEXT_WORLD_DRAG_GENERATION.fetch_add(1, Ordering::Relaxed),
37 session: self
41 .ctx
42 .active_session()
43 .filter(|session| self.ctx.session_result(*session).is_none()),
44 origin_scale: origin.map_or(1.0, |record| record.geometry.scale()),
45 source_location: self
46 .ctx
47 .source()
48 .map(|zone| ZoneLocation { window: key, zone }),
49 modifiers: Modifiers::empty(),
50 };
51 let mut active = self.active;
52 if *active.peek() != Some(active_drag) {
53 active.set(Some(active_drag));
54 }
55 let mut settle_claim = self.settle_claim;
56 if settle_claim.peek().is_some() {
57 settle_claim.set(None);
58 }
59 let mut global_pointer = self.global_pointer;
60 let initial_global =
61 origin.and_then(|record| record.geometry.to_global(self.ctx.pointer()));
62 if *global_pointer.peek() != initial_global {
63 global_pointer.set(initial_global);
64 }
65 let mut over_location = self.over_location;
66 if over_location.peek().is_some() {
67 over_location.set(None);
68 }
69 }
70
71 pub fn active_record(&self) -> Option<WindowRecord<T>> {
73 let origin = self.active.peek().as_ref()?.origin;
74 self.record(origin)
75 }
76
77 pub(super) fn active_drag(&self) -> Option<ActiveDrag> {
78 *self.active.peek()
79 }
80
81 pub fn global_pointer(&self) -> Option<Point> {
84 *self.global_pointer.read()
85 }
86
87 pub fn source_location(&self) -> Option<ZoneLocation> {
90 self.active
91 .read()
92 .as_ref()
93 .and_then(|active| active.source_location)
94 }
95
96 pub fn over_location(&self) -> Option<ZoneLocation> {
97 *self.over_location.read()
98 }
99
100 pub fn drag_session(&self) -> Option<DragSessionId> {
102 self.active.peek().as_ref()?.session
103 }
104
105 #[cfg_attr(not(feature = "desktop"), allow(dead_code))]
109 pub(crate) fn drag_generation(&self) -> Option<(u64, Option<DragSessionId>)> {
110 let active = self.active.read();
111 let active = active.as_ref()?;
112 Some((active.generation, active.session))
113 }
114
115 #[cfg_attr(not(feature = "desktop"), allow(dead_code))]
119 pub(crate) fn drag_generation_peek(&self) -> Option<(u64, Option<DragSessionId>)> {
120 let active = self.active_drag()?;
121 Some((active.generation, active.session))
122 }
123
124 #[cfg_attr(not(feature = "desktop"), allow(dead_code))]
128 pub(crate) fn is_drag_generation(
129 &self,
130 generation: u64,
131 session: Option<DragSessionId>,
132 ) -> bool {
133 let Some(active) = self.active_drag() else {
134 return false;
135 };
136 if !self.ctx.dragging() || active.generation != generation || active.session != session {
137 return false;
138 }
139 session.is_none_or(|session| self.ctx.is_session(session))
140 }
141
142 pub(crate) fn is_drag_session(&self, session: DragSessionId) -> bool {
143 self.drag_session() == Some(session) && self.ctx.is_session(session)
144 }
145
146 pub(crate) fn commit_session(&self, session: DragSessionId, dropped: bool) -> bool {
147 if !self.is_drag_session(session) {
148 return false;
149 }
150 let mut ctx = self.ctx;
151 ctx.commit_source(session, dropped)
152 }
153
154 pub(crate) fn finalize_session(&self, session: DragSessionId) -> bool {
155 let Some(result) = self.ctx.session_result(session) else {
156 return false;
157 };
158 self.finish_session(session, result)
159 }
160
161 pub(crate) fn finish_session(&self, session: DragSessionId, dropped: bool) -> bool {
162 let mut ctx = self.ctx;
163 if !ctx.is_session(session) {
164 return false;
165 }
166 let owns_metadata = self.drag_session() == Some(session);
167 let result = ctx.session_result(session).unwrap_or(dropped);
168 let finished = if ctx.session_result(session).is_some() {
169 ctx.finalize_source(session)
170 } else if dropped {
171 ctx.finish_source(session, true)
172 } else {
173 ctx.cancel_session(session)
174 };
175 if !finished {
176 return false;
177 }
178 if !owns_metadata || self.drag_session() != Some(session) {
179 return true;
180 }
181 if ctx.dragging() {
184 return true;
185 }
186 if result && ctx.settling().is_some() {
187 let mut active = self.active;
188 let current = *active.peek();
189 if let Some(mut current) = current {
190 current.session = None;
191 active.set(Some(current));
192 }
193 self.clear_hover();
194 } else {
195 self.clear_world_state();
196 }
197 true
198 }
199
200 pub(crate) fn finish_untracked(&self, dropped: bool) {
201 let mut ctx = self.ctx;
202 if !dropped && ctx.dragging() {
203 ctx.cancel();
204 }
205 if ctx.dragging() {
206 return;
207 }
208 if dropped && ctx.settling().is_some() {
209 self.clear_hover();
210 } else {
211 self.clear_world_state();
212 }
213 }
214
215 pub(super) fn clear_world_state(&self) {
216 let mut active = self.active;
217 active.set(None);
218 let mut global_pointer = self.global_pointer;
219 global_pointer.set(None);
220 let mut over_location = self.over_location;
221 over_location.set(None);
222 let mut settle_claim = self.settle_claim;
223 settle_claim.set(None);
224 }
225
226 pub(super) fn enter_location(&self, location: ZoneLocation) {
227 let mut over_location = self.over_location;
228 if *over_location.peek() != Some(location) {
229 over_location.set(Some(location));
230 }
231 let mut ctx = self.ctx;
232 ctx.enter(location.zone);
233 }
234
235 pub(super) fn clear_hover(&self) {
236 let mut ctx = self.ctx;
237 if let Some(over) = ctx.over() {
238 ctx.leave(over);
239 }
240 let mut over_location = self.over_location;
241 if over_location.peek().is_some() {
242 over_location.set(None);
243 }
244 }
245}