use dioxus::prelude::*;
use crate::core::components::{drop_query, DropCompletion, SettleRoute};
use crate::core::monitor::CancelReason;
use crate::core::session::DragCompletion;
use crate::core::types::{effective_effect, DragMode, Point, ZoneId};
use super::geometry::WindowKey;
use super::state::{DndWorld, ZoneLocation};
impl<T: Clone + 'static> DndWorld<T> {
pub fn modifiers(&self) -> Modifiers {
self.active
.read()
.as_ref()
.map_or_else(Modifiers::empty, |active| active.modifiers)
}
pub fn update_modifiers(&self, modifiers: Modifiers) {
if !self.ctx.dragging() {
return;
}
let mut active = self.active;
let Some(mut current) = *active.peek() else {
return;
};
if current.modifiers != modifiers {
current.modifiers = modifiers;
active.set(Some(current));
}
}
pub fn track_global(&self, global: Point) {
if !self.bridging_enabled() {
return;
}
let mut ctx = self.ctx;
if !ctx.dragging() || ctx.mode() != DragMode::Pointer {
return;
}
let Some(origin) = self.active_record() else {
return;
};
let Some((generation, session)) = self.drag_generation_peek() else {
return;
};
let mut global_pointer = self.global_pointer;
if *global_pointer.peek() != Some(global) {
global_pointer.set(Some(global));
}
if let Some(local) = origin.geometry.to_client(global) {
ctx.update_pointer(local);
}
if !self.is_drag_generation(generation, session) {
return;
}
let modifiers = self
.active
.peek()
.as_ref()
.map_or_else(Modifiers::empty, |active| active.modifiers);
let effect = effective_effect(ctx.effect(), modifiers);
ctx.set_proposed_effect(effect);
let location = self.resolve_global(global).and_then(|(rec, local)| {
let payload = ctx.payload()?;
let query = drop_query(&ctx, payload, effect);
let current = self
.over_location()
.filter(|location| location.window == rec.key)
.map(|location| location.zone);
rec.registry
.resolve_hover(&query, local, self.active_rect_in(rec, local), current)
.map(|(zone, _)| ZoneLocation {
window: rec.key,
zone,
})
});
match location {
Some(location) => self.enter_location(location),
None => self.clear_hover(),
}
}
pub fn drop_at_global(&self, global: Point) -> Option<ZoneId>
where
T: PartialEq,
{
if !self.bridging_enabled() {
return None;
}
let mut ctx = self.ctx;
if !ctx.dragging() || ctx.mode() != DragMode::Pointer {
return None;
}
let (generation, captured_session) = self.drag_generation_peek()?;
self.track_global(global);
if !self.is_drag_generation(generation, captured_session) {
return None;
}
let session = self.drag_session();
let Some((rec, local)) = self.resolve_global(global) else {
match session {
Some(session) => {
self.finish_session(session, DragCompletion::Cancelled(CancelReason::NoTarget));
}
None => self.finish_untracked(DragCompletion::Cancelled(CancelReason::NoTarget)),
}
return None;
};
let modifiers = self
.active
.peek()
.as_ref()
.map_or_else(Modifiers::empty, |active| active.modifiers);
let effect = effective_effect(ctx.effect(), modifiers);
let target = ctx.payload().and_then(|payload| {
let query = drop_query(&ctx, payload, effect);
let radius = rec.registry.release_policy().recovery_radius;
rec.registry
.resolve(&query, local, self.active_rect_in(rec, local), radius)
.map(|(zone, _)| zone)
});
let delivered = target.filter(|t| {
crate::core::components::deliver_drop(
rec.registry,
&mut ctx,
SettleRoute {
flag: Some(rec.settle),
owner: Some((self, rec.key)),
},
DropCompletion::World {
world: self,
session,
},
*t,
local,
effect,
)
});
match delivered {
Some(zone) => Some(zone),
None => {
match session {
Some(session) => {
self.finish_session(
session,
DragCompletion::Cancelled(CancelReason::NoTarget),
);
}
None => {
self.finish_untracked(DragCompletion::Cancelled(CancelReason::NoTarget));
}
}
None
}
}
}
pub fn cancel_drag(&self) {
if self.ctx.settling().is_some() {
let mut ctx = self.ctx;
ctx.finish_settle();
self.clear_world_state();
return;
}
if let Some(session) = self.drag_session() {
self.finish_session(session, DragCompletion::Cancelled(CancelReason::User));
} else if self.ctx.dragging() {
self.finish_untracked(DragCompletion::Cancelled(CancelReason::User));
}
}
pub fn origin_window(&self) -> Option<WindowKey> {
(self.ctx.dragging() || self.ctx.settling().is_some())
.then(|| self.active.peek().as_ref().map(|active| active.origin))
.flatten()
}
}