Skip to main content

tui_lipan/widgets/drag_drop/
payload.rs

1use std::any::Any;
2use std::fmt::Debug;
3use std::sync::Arc;
4
5use crate::style::Length;
6
7/// Type-erased payload for generic drag-and-drop.
8pub trait DragPayload: Any + Debug + 'static {
9    /// Downcast helper for payload inspection.
10    fn as_any(&self) -> &dyn Any;
11
12    /// Convert boxed payload into shared payload storage without double boxing.
13    fn into_arc(self: Box<Self>) -> Arc<dyn DragPayload>;
14}
15
16impl<T> DragPayload for T
17where
18    T: Any + Debug + 'static,
19{
20    fn as_any(&self) -> &dyn Any {
21        self
22    }
23
24    fn into_arc(self: Box<Self>) -> Arc<dyn DragPayload> {
25        let payload: Arc<Self> = Arc::from(self);
26        payload
27    }
28}
29
30impl dyn DragPayload {
31    /// Downcast payload to a concrete type.
32    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
33        self.as_any().downcast_ref::<T>().or_else(|| {
34            self.as_any()
35                .downcast_ref::<Box<dyn DragPayload>>()
36                .and_then(|boxed| boxed.as_ref().as_any().downcast_ref::<T>())
37        })
38    }
39}
40
41#[derive(Clone, Debug, PartialEq, Eq)]
42/// Event emitted when drag activation threshold is exceeded.
43pub struct DragStartEvent {
44    /// Pointer x coordinate when drag starts.
45    pub x: u16,
46    /// Pointer y coordinate when drag starts.
47    pub y: u16,
48}
49
50#[derive(Clone)]
51/// Event emitted while a compatible payload hovers a drop target.
52pub struct DragOverEvent {
53    /// Current pointer x coordinate.
54    pub x: u16,
55    /// Current pointer y coordinate.
56    pub y: u16,
57    /// Pointer `y` minus the hovered drop target's top edge (content coordinates).
58    pub local_y: u16,
59    /// Height of the hovered drop target in cells.
60    pub local_height: u16,
61    /// Active drag payload.
62    pub payload: Arc<dyn DragPayload>,
63}
64
65#[derive(Clone)]
66/// Event emitted when payload leaves a drop target.
67pub struct DragLeaveEvent {
68    /// Active drag payload.
69    pub payload: Arc<dyn DragPayload>,
70}
71
72#[derive(Clone)]
73/// Event emitted when payload is dropped on a compatible target.
74pub struct DropEvent {
75    /// Pointer x coordinate at drop time.
76    pub x: u16,
77    /// Pointer y coordinate at drop time.
78    pub y: u16,
79    /// Pointer `y` minus the drop target's top edge (content coordinates).
80    pub local_y: u16,
81    /// Height of the drop target in cells.
82    pub local_height: u16,
83    /// Active drag payload.
84    pub payload: Arc<dyn DragPayload>,
85}
86
87#[derive(Clone)]
88/// Fired once when a generic drag becomes active (after the movement threshold).
89pub struct DragStartedEvent {
90    /// Pointer x coordinate when the drag activated.
91    pub x: u16,
92    /// Pointer y coordinate when the drag activated.
93    pub y: u16,
94    /// Active drag payload.
95    pub payload: Arc<dyn DragPayload>,
96}
97
98#[derive(Clone)]
99/// Event emitted when active drag is canceled.
100pub struct DragCancelEvent {
101    /// Active drag payload.
102    pub payload: Arc<dyn DragPayload>,
103}
104
105#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
106/// Visual preview style rendered near pointer during drag.
107pub enum DragPreview {
108    /// Show this label near the pointer while dragging.
109    Label(Arc<str>),
110    /// Render a snapshot of the drag source's cells near the pointer while dragging.
111    SourceSnapshot,
112    /// Do not render a drag preview.
113    #[default]
114    None,
115}
116
117/// Default maximum width (cells) for a [`DragPreview::SourceSnapshot`] float preview.
118pub const DEFAULT_PREVIEW_MAX_WIDTH: u16 = 60;
119/// Default maximum height (cells) for a [`DragPreview::SourceSnapshot`] float preview.
120pub const DEFAULT_PREVIEW_MAX_HEIGHT: u16 = 20;
121
122/// Which axis [`DragSlot`] main-axis sizes apply to when measuring a drag source outside a stack
123/// (e.g. inside `Frame`). In `VStack` / `HStack`, the stack's axis always wins.
124#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
125pub enum DragSlotAxis {
126    /// Vertical main axis (height) when measuring outside a stack.
127    #[default]
128    Vertical,
129    /// Horizontal main axis (width) when measuring outside a stack.
130    Horizontal,
131}
132
133/// Main-axis space reserved at the [`crate::widgets::DragSource`] while dragging (when using
134/// [`DragPreview::SourceSnapshot`]).
135///
136/// In `VStack` / `HStack`, [`DragSlot::Specified`] uses the same main-axis rules as stack children.
137/// [`DragSlot::Collapse`] is **0 cells** on the stack main axis.
138#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
139pub enum DragSlot {
140    /// Zero cells on the stack main axis while dragging.
141    #[default]
142    Collapse,
143    /// Fixed main-axis length using the same rules as stack children.
144    Specified(Length),
145}
146
147/// What the [`crate::widgets::DropTarget`] renders in place of its child while a compatible
148/// [`DragPreview::SourceSnapshot`] drag is hovering over it.
149#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
150pub enum DropSlot {
151    /// Render the child normally (default). [`crate::widgets::DropHighlight`] still applies on top.
152    #[default]
153    Child,
154    /// Replace the child with the dragged source's snapshot cells and suppress the floating
155    /// cursor preview. The snapshot is rendered top-left aligned and clipped to the target rect.
156    /// [`crate::widgets::DropHighlight`] is still composited on top when set.
157    SourcePreview,
158}