use dioxus::prelude::{ReadableExt, WritableExt};
use crate::core::types::{Point, ZoneId};
use crate::core::DropQuery;
use super::geometry::{WindowGeometry, WindowKey};
use super::state::{DndWorld, WindowRecord, ZoneLocation};
pub(crate) struct WorldMembership<T: Clone + 'static>(pub(crate) Option<JoinedWindow<T>>);
impl<T: Clone + 'static> Copy for WorldMembership<T> {}
impl<T: Clone + 'static> Clone for WorldMembership<T> {
fn clone(&self) -> Self {
*self
}
}
pub struct JoinedWindow<T: Clone + 'static> {
pub world: DndWorld<T>,
pub key: WindowKey,
pub geometry: WindowGeometry,
}
impl<T: Clone + 'static> Copy for JoinedWindow<T> {}
impl<T: Clone + 'static> Clone for JoinedWindow<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Clone + 'static> PartialEq for JoinedWindow<T> {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && self.world == other.world
}
}
pub(crate) enum WorldHit {
Zone(ZoneLocation),
Window,
Unresolved,
}
impl<T: Clone + 'static> JoinedWindow<T> {
pub(crate) fn zone_under(&self, client: Point) -> WorldHit {
let Some(global) = self.geometry.to_global(client) else {
return WorldHit::Unresolved;
};
let mut global_pointer = self.world.global_pointer;
if *global_pointer.peek() != Some(global) {
global_pointer.set(Some(global));
}
let Some((rec, local)) = self.world.resolve_global(global) else {
return WorldHit::Unresolved;
};
match rec.registry.hit_test(local) {
Some(zone) => WorldHit::Zone(ZoneLocation {
window: rec.key,
zone,
}),
None => WorldHit::Window,
}
}
pub(crate) fn zone_under_query(&self, client: Point, query: &DropQuery<T>) -> WorldHit {
let Some(global) = self.geometry.to_global(client) else {
return WorldHit::Unresolved;
};
let mut global_pointer = self.world.global_pointer;
if *global_pointer.peek() != Some(global) {
global_pointer.set(Some(global));
}
let Some((rec, local)) = self.world.resolve_global(global) else {
return WorldHit::Unresolved;
};
let current = self
.world
.over_location()
.filter(|location| location.window == rec.key)
.map(|location| location.zone);
match rec.registry.resolve_hover(
query,
local,
self.world.active_rect_in(rec, local),
current,
) {
Some((zone, _)) => WorldHit::Zone(ZoneLocation {
window: rec.key,
zone,
}),
None => WorldHit::Window,
}
}
pub fn location(&self, zone: ZoneId) -> ZoneLocation {
ZoneLocation {
window: self.key,
zone,
}
}
pub fn enter(&self, location: ZoneLocation) {
self.world.enter_location(location);
}
pub fn clear_hover(&self) {
self.world.clear_hover();
}
pub fn is_over(&self, zone: ZoneId) -> bool {
self.world.over_location() == Some(self.location(zone))
}
pub fn local_pointer(&self) -> Option<Point> {
if let Some(local) = self
.world
.global_pointer()
.and_then(|global| self.geometry.to_client(global))
{
return Some(local);
}
(self.world.origin_window() == Some(self.key)).then(|| self.world.ctx.pointer())
}
pub(crate) fn foreign_window_under(&self, client: Point) -> Option<(WindowRecord<T>, Point)> {
let global = self.geometry.to_global(client)?;
let (rec, local) = self.world.resolve_global(global)?;
(rec.key != self.key).then_some((rec, local))
}
pub(crate) fn present_overlay(
&self,
pointer: Point,
grab: Point,
settling: bool,
) -> Option<(Point, f64)> {
let raw = pointer - grab;
let Some(active) = self.world.active_drag() else {
return Some((raw, 1.0));
};
let origin = self.world.record(active.origin);
let origin_scale = origin
.map(|record| record.geometry.scale())
.unwrap_or(active.origin_scale);
let global_anchor = origin
.and_then(|record| record.geometry.to_global(raw))
.or_else(|| {
self.world.global_pointer().map(|global| {
Point::new(
global.x - grab.x * origin_scale,
global.y - grab.y * origin_scale,
)
})
});
let Some(global_anchor) = global_anchor else {
return (self.key == active.origin).then_some((raw, 1.0));
};
let presenting = if settling {
self.world.settling_in()?
} else {
let pointer_global = self
.world
.global_pointer()
.or_else(|| origin.and_then(|record| record.geometry.to_global(pointer)))
.unwrap_or(global_anchor);
self.world
.window_under(pointer_global)
.map(|r| r.key)
.unwrap_or(active.origin)
};
if presenting != self.key {
return None;
}
match self.geometry.to_client(global_anchor) {
Some(local) => {
let own_scale = self.geometry.scale();
let ratio = if own_scale > 0.0 {
origin_scale / own_scale
} else {
1.0
};
Some((local, ratio))
}
None => (self.key == active.origin).then_some((raw, 1.0)),
}
}
}