use crate::core::{Bounds, Point, Size, px};
use crate::view::any_element::AnyElement;
use crate::view::interactivity::{EventContext, Handler};
use crate::view::window::Window;
use std::any::{Any, TypeId};
use std::rc::Rc;
const DRAG_THRESHOLD: f32 = 4.0;
#[derive(Clone)]
pub struct DragSource {
pub(crate) value: Rc<dyn Any>,
pub(crate) preview: Rc<dyn Fn() -> AnyElement>,
}
impl DragSource {
pub fn new<T: Any>(value: T, preview: impl Fn(&T) -> AnyElement + 'static) -> Self {
let value = Rc::new(value);
let shared = value.clone();
Self { value, preview: Rc::new(move || preview(&shared)) }
}
pub(crate) fn type_id(&self) -> TypeId {
(*self.value).type_id()
}
}
#[derive(Clone)]
pub struct DropTarget {
pub(crate) type_id: TypeId,
pub(crate) handler: Handler<Rc<dyn Any>>,
}
impl DropTarget {
pub fn new<T: Any>(handler: impl Fn(&T, &mut EventContext) + 'static) -> Self {
let handler = move |value: &Rc<dyn Any>, cx: &mut EventContext| {
if let Some(value) = value.downcast_ref::<T>() {
handler(value, cx);
}
};
Self { type_id: TypeId::of::<T>(), handler: Rc::new(handler) }
}
}
pub(crate) struct ActiveDrag {
source: DragSource,
origin: Point,
pub(crate) started: bool,
}
impl Window {
pub(crate) fn prepare_drag(&mut self, position: Point) {
let pressed = self.mouse.pressed;
let source =
self.frame.handlers.iter().find(|entry| Some(entry.hitbox) == pressed).and_then(|e| e.drag.clone());
self.drag = source.map(|source| ActiveDrag { source, origin: position, started: false });
}
pub(crate) fn update_drag(&mut self, position: Point) -> bool {
let Some(drag) = self.drag.as_mut() else { return false };
if !drag.started {
let (dx, dy) = (position.x.0 - drag.origin.x.0, position.y.0 - drag.origin.y.0);
drag.started = dx.abs() > DRAG_THRESHOLD || dy.abs() > DRAG_THRESHOLD;
}
drag.started
}
pub(crate) fn finish_drag(&mut self, position: Point) -> Option<bool> {
let drag = self.drag.take().filter(|drag| drag.started)?;
let type_id = drag.source.type_id();
let target = self.frame.hitboxes.hits_at(position).find_map(|hit| {
let entry = self.frame.handlers.iter().find(|entry| entry.hitbox == hit)?;
entry.drops.iter().find(|target| target.type_id == type_id).map(|target| target.handler.clone())
});
let mut context = EventContext { entities: &mut self.entities, effects: Default::default() };
context.effects.notify();
context.effects.pointer = Some(position);
if let Some(handler) = target {
handler(&drag.source.value, &mut context);
}
let effects = context.effects;
Some(self.finish(effects))
}
pub fn active_drag_type(&self) -> Option<TypeId> {
self.drag.as_ref().filter(|drag| drag.started).map(|drag| drag.source.type_id())
}
pub(crate) fn drag_preview(&self) -> Option<AnyElement> {
use crate::view::element::{IntoElement, ParentElement};
let drag = self.drag.as_ref().filter(|drag| drag.started)?;
let at = Bounds::new(self.mouse.position + Point::new(px(8.0), px(8.0)), Size::default());
Some(crate::view::overlay::anchored().anchor_to(at).child((drag.source.preview)()).into_element())
}
}