dioxus_dnd/core/world/
joined.rs1use dioxus::prelude::{ReadableExt, WritableExt};
5
6use crate::core::types::{Point, ZoneId};
7use crate::core::DropQuery;
8
9use super::geometry::{WindowGeometry, WindowKey};
10use super::state::{DndWorld, WindowRecord, ZoneLocation};
11
12pub(crate) struct WorldMembership<T: Clone + 'static>(pub(crate) Option<JoinedWindow<T>>);
17
18impl<T: Clone + 'static> Copy for WorldMembership<T> {}
19impl<T: Clone + 'static> Clone for WorldMembership<T> {
20 fn clone(&self) -> Self {
21 *self
22 }
23}
24
25pub struct JoinedWindow<T: Clone + 'static> {
29 pub world: DndWorld<T>,
30 pub key: WindowKey,
31 pub geometry: WindowGeometry,
32}
33
34impl<T: Clone + 'static> Copy for JoinedWindow<T> {}
35impl<T: Clone + 'static> Clone for JoinedWindow<T> {
36 fn clone(&self) -> Self {
37 *self
38 }
39}
40impl<T: Clone + 'static> PartialEq for JoinedWindow<T> {
41 fn eq(&self, other: &Self) -> bool {
42 self.key == other.key && self.world == other.world
43 }
44}
45
46pub(crate) enum WorldHit {
49 Zone(ZoneLocation),
51 Window,
53 Unresolved,
56}
57
58impl<T: Clone + 'static> JoinedWindow<T> {
59 pub(crate) fn zone_under(&self, client: Point) -> WorldHit {
62 let Some(global) = self.geometry.to_global(client) else {
63 return WorldHit::Unresolved;
64 };
65 let mut global_pointer = self.world.global_pointer;
66 if *global_pointer.peek() != Some(global) {
67 global_pointer.set(Some(global));
68 }
69 let Some((rec, local)) = self.world.resolve_global(global) else {
70 return WorldHit::Unresolved;
71 };
72 match rec.registry.hit_test(local) {
73 Some(zone) => WorldHit::Zone(ZoneLocation {
74 window: rec.key,
75 zone,
76 }),
77 None => WorldHit::Window,
78 }
79 }
80
81 pub(crate) fn zone_under_query(&self, client: Point, query: &DropQuery<T>) -> WorldHit {
83 let Some(global) = self.geometry.to_global(client) else {
84 return WorldHit::Unresolved;
85 };
86 let mut global_pointer = self.world.global_pointer;
87 if *global_pointer.peek() != Some(global) {
88 global_pointer.set(Some(global));
89 }
90 let Some((rec, local)) = self.world.resolve_global(global) else {
91 return WorldHit::Unresolved;
92 };
93 let current = self
94 .world
95 .over_location()
96 .filter(|location| location.window == rec.key)
97 .map(|location| location.zone);
98 match rec.registry.resolve_hover(
99 query,
100 local,
101 self.world.active_rect_in(rec, local),
102 current,
103 ) {
104 Some((zone, _)) => WorldHit::Zone(ZoneLocation {
105 window: rec.key,
106 zone,
107 }),
108 None => WorldHit::Window,
109 }
110 }
111
112 pub fn location(&self, zone: ZoneId) -> ZoneLocation {
114 ZoneLocation {
115 window: self.key,
116 zone,
117 }
118 }
119
120 pub fn enter(&self, location: ZoneLocation) {
123 self.world.enter_location(location);
124 }
125
126 pub fn clear_hover(&self) {
128 self.world.clear_hover();
129 }
130
131 pub fn is_over(&self, zone: ZoneId) -> bool {
133 self.world.over_location() == Some(self.location(zone))
134 }
135
136 pub fn local_pointer(&self) -> Option<Point> {
140 if let Some(local) = self
141 .world
142 .global_pointer()
143 .and_then(|global| self.geometry.to_client(global))
144 {
145 return Some(local);
146 }
147 (self.world.origin_window() == Some(self.key)).then(|| self.world.ctx.pointer())
148 }
149
150 pub(crate) fn foreign_window_under(&self, client: Point) -> Option<(WindowRecord<T>, Point)> {
155 let global = self.geometry.to_global(client)?;
156 let (rec, local) = self.world.resolve_global(global)?;
157 (rec.key != self.key).then_some((rec, local))
158 }
159
160 pub(crate) fn present_overlay(
170 &self,
171 pointer: Point,
172 grab: Point,
173 settling: bool,
174 ) -> Option<(Point, f64)> {
175 let raw = pointer - grab;
176 let Some(active) = self.world.active_drag() else {
177 return Some((raw, 1.0));
180 };
181 let origin = self.world.record(active.origin);
182 let origin_scale = origin
183 .map(|record| record.geometry.scale())
184 .unwrap_or(active.origin_scale);
185 let global_anchor = origin
186 .and_then(|record| record.geometry.to_global(raw))
187 .or_else(|| {
188 self.world.global_pointer().map(|global| {
189 Point::new(
190 global.x - grab.x * origin_scale,
191 global.y - grab.y * origin_scale,
192 )
193 })
194 });
195 let Some(global_anchor) = global_anchor else {
196 return (self.key == active.origin).then_some((raw, 1.0));
198 };
199 let presenting = if settling {
200 self.world.settling_in()?
201 } else {
202 let pointer_global = self
203 .world
204 .global_pointer()
205 .or_else(|| origin.and_then(|record| record.geometry.to_global(pointer)))
206 .unwrap_or(global_anchor);
207 self.world
208 .window_under(pointer_global)
209 .map(|r| r.key)
210 .unwrap_or(active.origin)
211 };
212 if presenting != self.key {
213 return None;
214 }
215 match self.geometry.to_client(global_anchor) {
216 Some(local) => {
217 let own_scale = self.geometry.scale();
218 let ratio = if own_scale > 0.0 {
219 origin_scale / own_scale
220 } else {
221 1.0
222 };
223 Some((local, ratio))
224 }
225 None => (self.key == active.origin).then_some((raw, 1.0)),
227 }
228 }
229}