egui/
drag_and_drop.rs

1use std::{any::Any, sync::Arc};
2
3use crate::{Context, CursorIcon, Id};
4
5/// Tracking of drag-and-drop payload.
6///
7/// This is a low-level API.
8///
9/// For a higher-level API, see:
10/// - [`crate::Ui::dnd_drag_source`]
11/// - [`crate::Ui::dnd_drop_zone`]
12/// - [`crate::Response::dnd_set_drag_payload`]
13/// - [`crate::Response::dnd_hover_payload`]
14/// - [`crate::Response::dnd_release_payload`]
15///
16/// See [this example](https://github.com/emilk/egui/blob/master/crates/egui_demo_lib/src/demo/drag_and_drop.rs).
17#[doc(alias = "drag and drop")]
18#[derive(Clone, Default)]
19pub struct DragAndDrop {
20    /// If set, something is currently being dragged
21    payload: Option<Arc<dyn Any + Send + Sync>>,
22}
23
24impl DragAndDrop {
25    pub(crate) fn register(ctx: &Context) {
26        ctx.on_end_frame("debug_text", std::sync::Arc::new(Self::end_frame));
27    }
28
29    fn end_frame(ctx: &Context) {
30        let abort_dnd =
31            ctx.input(|i| i.pointer.any_released() || i.key_pressed(crate::Key::Escape));
32
33        let mut is_dragging = false;
34
35        ctx.data_mut(|data| {
36            let state = data.get_temp_mut_or_default::<Self>(Id::NULL);
37
38            if abort_dnd {
39                state.payload = None;
40            }
41
42            is_dragging = state.payload.is_some();
43        });
44
45        if is_dragging {
46            ctx.set_cursor_icon(CursorIcon::Grabbing);
47        }
48    }
49
50    /// Set a drag-and-drop payload.
51    ///
52    /// This can be read by [`Self::payload`] until the pointer is released.
53    pub fn set_payload<Payload>(ctx: &Context, payload: Payload)
54    where
55        Payload: Any + Send + Sync,
56    {
57        ctx.data_mut(|data| {
58            let state = data.get_temp_mut_or_default::<Self>(Id::NULL);
59            state.payload = Some(Arc::new(payload));
60        });
61    }
62
63    /// Clears the payload, setting it to `None`.
64    pub fn clear_payload(ctx: &Context) {
65        ctx.data_mut(|data| {
66            let state = data.get_temp_mut_or_default::<Self>(Id::NULL);
67            state.payload = None;
68        });
69    }
70
71    /// Retrieve the payload, if any.
72    ///
73    /// Returns `None` if there is no payload, or if it is not of the requested type.
74    ///
75    /// Returns `Some` both during a drag and on the frame the pointer is released
76    /// (if there is a payload).
77    pub fn payload<Payload>(ctx: &Context) -> Option<Arc<Payload>>
78    where
79        Payload: Any + Send + Sync,
80    {
81        ctx.data(|data| {
82            let state = data.get_temp::<Self>(Id::NULL)?;
83            let payload = state.payload?;
84            payload.downcast().ok()
85        })
86    }
87
88    /// Retrieve and clear the payload, if any.
89    ///
90    /// Returns `None` if there is no payload, or if it is not of the requested type.
91    ///
92    /// Returns `Some` both during a drag and on the frame the pointer is released
93    /// (if there is a payload).
94    pub fn take_payload<Payload>(ctx: &Context) -> Option<Arc<Payload>>
95    where
96        Payload: Any + Send + Sync,
97    {
98        ctx.data_mut(|data| {
99            let state = data.get_temp_mut_or_default::<Self>(Id::NULL);
100            let payload = state.payload.take()?;
101            payload.downcast().ok()
102        })
103    }
104
105    /// Are we carrying a payload of the given type?
106    ///
107    /// Returns `true` both during a drag and on the frame the pointer is released
108    /// (if there is a payload).
109    pub fn has_payload_of_type<Payload>(ctx: &Context) -> bool
110    where
111        Payload: Any + Send + Sync,
112    {
113        Self::payload::<Payload>(ctx).is_some()
114    }
115
116    /// Are we carrying a payload?
117    ///
118    /// Returns `true` both during a drag and on the frame the pointer is released
119    /// (if there is a payload).
120    pub fn has_any_payload(ctx: &Context) -> bool {
121        ctx.data(|data| {
122            let state = data.get_temp::<Self>(Id::NULL);
123            state.map_or(false, |state| state.payload.is_some())
124        })
125    }
126}