repose_core/dnd.rs
1use std::{any::Any, path::PathBuf, rc::Rc};
2
3/// Opaque payload moved during internal drag & drop.
4/// Use [`downcast_drag_payload`] on the receiver side to recover a typed value.
5pub type DragPayload = Rc<dyn Any>;
6
7/// Wrap a typed value into a [`DragPayload`] for a drag source.
8///
9/// ```ignore
10/// Modifier::new().on_drag_start(|_start| Some(drag_payload(MyItem { id: 1 })))
11/// ```
12pub fn drag_payload<T: 'static>(value: T) -> DragPayload {
13 Rc::new(value)
14}
15
16/// Try to downcast a drag payload to a typed reference. Used on the drop side.
17///
18/// ```ignore
19/// if let Some(item) = downcast_drag_payload::<MyItem>(&ev.payload) {
20/// // handle item
21/// }
22/// ```
23pub fn downcast_drag_payload<T: 'static>(payload: &DragPayload) -> Option<&T> {
24 payload.as_ref().downcast_ref::<T>()
25}
26
27/// Block-style convenience for [`Modifier::on_drag_start`] with a typed payload.
28///
29/// ```ignore
30/// use repose_core::{Modifier, drag_and_drop_source};
31/// struct MyItem { id: i32 }
32/// let m = drag_and_drop_source(Modifier::new(), |_start| Some(MyItem { id: 1 }));
33/// ```
34///
35/// is equivalent to:
36///
37/// ```ignore
38/// Modifier::new().on_drag_start(|_start| Some(drag_payload(MyItem { id: 1 })))
39/// ```
40pub fn drag_and_drop_source<T, F>(mut modifier: crate::Modifier, on_start: F) -> crate::Modifier
41where
42 T: 'static,
43 F: Fn(DragStart) -> Option<T> + 'static,
44{
45 modifier = modifier.on_drag_start(move |start| on_start(start).map(drag_payload::<T>));
46 modifier
47}
48
49/// Block-style convenience for [`Modifier::on_drop`] with a typed payload. The
50/// drop is accepted when the closure returns `true`; the typed payload is
51/// downcast before the closure is invoked.
52///
53/// ```ignore
54/// use repose_core::{Modifier, drag_and_drop_target};
55/// struct MyItem { id: i32 }
56/// let m = drag_and_drop_target(Modifier::new(), |_ev, item: &MyItem| {
57/// println!("got id {}", item.id);
58/// true
59/// });
60/// ```
61pub fn drag_and_drop_target<T, F>(mut modifier: crate::Modifier, on_drop: F) -> crate::Modifier
62where
63 T: 'static,
64 F: Fn(&DropEvent, &T) -> bool + 'static,
65{
66 modifier = modifier.on_drop(move |ev| match downcast_drag_payload::<T>(&ev.payload) {
67 Some(v) => on_drop(&ev, v),
68 None => false,
69 });
70 modifier
71}
72
73#[derive(Clone, Debug)]
74pub struct DragStart {
75 pub source_id: u64,
76 pub position: crate::Vec2,
77 pub modifiers: crate::Modifiers,
78}
79
80#[derive(Clone, Debug)]
81pub struct DragOver {
82 pub source_id: u64,
83 pub target_id: u64,
84 pub position: crate::Vec2,
85 pub modifiers: crate::Modifiers,
86 pub payload: DragPayload,
87}
88
89#[derive(Clone, Debug)]
90pub struct DropEvent {
91 pub source_id: u64,
92 pub target_id: u64,
93 pub position: crate::Vec2,
94 pub modifiers: crate::Modifiers,
95 pub payload: DragPayload,
96}
97
98/// Sent to the drag source when the drag ends (drop or cancel).
99#[derive(Clone, Copy, Debug)]
100pub struct DragEnd {
101 pub accepted: bool,
102}
103
104/// A single dropped file descriptor.
105/// - On desktop: `path` is `Some(PathBuf)`.
106/// - On web: `path` is usually `None` (browser doesn't expose local paths).
107#[derive(Clone, Debug)]
108pub struct DroppedFile {
109 pub name: String,
110 pub path: Option<PathBuf>,
111}
112
113/// Payload type for file drag/drop coming from the OS/browser.
114#[derive(Clone, Debug)]
115pub struct DroppedFiles {
116 pub files: Vec<DroppedFile>,
117}