use std::sync::atomic::{AtomicU64, Ordering};
use dioxus::prelude::*;
use crate::core::types::{DragSessionId, Point};
use super::geometry::WindowKey;
use super::state::{DndWorld, WindowRecord, ZoneLocation};
static NEXT_WORLD_DRAG_GENERATION: AtomicU64 = AtomicU64::new(1);
#[derive(Debug, Clone, Copy, PartialEq)]
pub(super) struct ActiveDrag {
pub(super) origin: WindowKey,
pub(super) generation: u64,
pub(super) session: Option<DragSessionId>,
pub(super) origin_scale: f64,
pub(super) source_location: Option<ZoneLocation>,
pub(super) modifiers: Modifiers,
}
impl<T: Clone + 'static> DndWorld<T> {
pub fn begin_from(&self, key: WindowKey) {
let origin = self.record(key);
let active_drag = ActiveDrag {
origin: key,
generation: NEXT_WORLD_DRAG_GENERATION.fetch_add(1, Ordering::Relaxed),
session: self
.ctx
.active_session()
.filter(|session| self.ctx.session_result(*session).is_none()),
origin_scale: origin.map_or(1.0, |record| record.geometry.scale()),
source_location: self
.ctx
.source()
.map(|zone| ZoneLocation { window: key, zone }),
modifiers: Modifiers::empty(),
};
let mut active = self.active;
if *active.peek() != Some(active_drag) {
active.set(Some(active_drag));
}
let mut settle_claim = self.settle_claim;
if settle_claim.peek().is_some() {
settle_claim.set(None);
}
let mut global_pointer = self.global_pointer;
let initial_global =
origin.and_then(|record| record.geometry.to_global(self.ctx.pointer()));
if *global_pointer.peek() != initial_global {
global_pointer.set(initial_global);
}
let mut over_location = self.over_location;
if over_location.peek().is_some() {
over_location.set(None);
}
}
pub fn active_record(&self) -> Option<WindowRecord<T>> {
let origin = self.active.peek().as_ref()?.origin;
self.record(origin)
}
pub(super) fn active_drag(&self) -> Option<ActiveDrag> {
*self.active.peek()
}
pub fn global_pointer(&self) -> Option<Point> {
*self.global_pointer.read()
}
pub fn source_location(&self) -> Option<ZoneLocation> {
self.active
.read()
.as_ref()
.and_then(|active| active.source_location)
}
pub fn over_location(&self) -> Option<ZoneLocation> {
*self.over_location.read()
}
pub fn drag_session(&self) -> Option<DragSessionId> {
self.active.peek().as_ref()?.session
}
#[cfg_attr(not(feature = "desktop"), allow(dead_code))]
pub(crate) fn drag_generation(&self) -> Option<(u64, Option<DragSessionId>)> {
let active = self.active.read();
let active = active.as_ref()?;
Some((active.generation, active.session))
}
#[cfg_attr(not(feature = "desktop"), allow(dead_code))]
pub(crate) fn drag_generation_peek(&self) -> Option<(u64, Option<DragSessionId>)> {
let active = self.active_drag()?;
Some((active.generation, active.session))
}
#[cfg_attr(not(feature = "desktop"), allow(dead_code))]
pub(crate) fn is_drag_generation(
&self,
generation: u64,
session: Option<DragSessionId>,
) -> bool {
let Some(active) = self.active_drag() else {
return false;
};
if !self.ctx.dragging() || active.generation != generation || active.session != session {
return false;
}
session.is_none_or(|session| self.ctx.is_session(session))
}
pub(crate) fn is_drag_session(&self, session: DragSessionId) -> bool {
self.drag_session() == Some(session) && self.ctx.is_session(session)
}
pub(crate) fn commit_session(&self, session: DragSessionId, dropped: bool) -> bool {
if !self.is_drag_session(session) {
return false;
}
let mut ctx = self.ctx;
ctx.commit_source(session, dropped)
}
pub(crate) fn finalize_session(&self, session: DragSessionId) -> bool {
let Some(result) = self.ctx.session_result(session) else {
return false;
};
self.finish_session(session, result)
}
pub(crate) fn finish_session(&self, session: DragSessionId, dropped: bool) -> bool {
let mut ctx = self.ctx;
if !ctx.is_session(session) {
return false;
}
let owns_metadata = self.drag_session() == Some(session);
let result = ctx.session_result(session).unwrap_or(dropped);
let finished = if ctx.session_result(session).is_some() {
ctx.finalize_source(session)
} else if dropped {
ctx.finish_source(session, true)
} else {
ctx.cancel_session(session)
};
if !finished {
return false;
}
if !owns_metadata || self.drag_session() != Some(session) {
return true;
}
if ctx.dragging() {
return true;
}
if result && ctx.settling().is_some() {
let mut active = self.active;
let current = *active.peek();
if let Some(mut current) = current {
current.session = None;
active.set(Some(current));
}
self.clear_hover();
} else {
self.clear_world_state();
}
true
}
pub(crate) fn finish_untracked(&self, dropped: bool) {
let mut ctx = self.ctx;
if !dropped && ctx.dragging() {
ctx.cancel();
}
if ctx.dragging() {
return;
}
if dropped && ctx.settling().is_some() {
self.clear_hover();
} else {
self.clear_world_state();
}
}
pub(super) fn clear_world_state(&self) {
let mut active = self.active;
active.set(None);
let mut global_pointer = self.global_pointer;
global_pointer.set(None);
let mut over_location = self.over_location;
over_location.set(None);
let mut settle_claim = self.settle_claim;
settle_claim.set(None);
}
pub(super) fn enter_location(&self, location: ZoneLocation) {
let mut over_location = self.over_location;
if *over_location.peek() != Some(location) {
over_location.set(Some(location));
}
let mut ctx = self.ctx;
ctx.enter(location.zone);
}
pub(super) fn clear_hover(&self) {
let mut ctx = self.ctx;
if let Some(over) = ctx.over() {
ctx.leave(over);
}
let mut over_location = self.over_location;
if over_location.peek().is_some() {
over_location.set(None);
}
}
}