Skip to main content

dear_imgui_rs/drag_drop/
source.rs

1use super::flags::{DragDropPayloadCond, DragDropSourceFlags, validate_drag_drop_source_flags};
2use super::payload::{TypedPayload, make_typed_payload};
3use super::validation::validate_payload_submission;
4use crate::{Ui, sys};
5use std::ffi;
6
7/// Builder for creating drag drop sources
8///
9/// This struct is created by [`Ui::drag_drop_source_config`] and provides
10/// a fluent interface for configuring drag sources.
11#[derive(Debug)]
12pub struct DragDropSource<'ui, T> {
13    pub(super) name: T,
14    pub(super) flags: DragDropSourceFlags,
15    pub(super) cond: DragDropPayloadCond,
16    pub(super) ui: &'ui Ui,
17}
18
19impl<'ui, T: AsRef<str>> DragDropSource<'ui, T> {
20    /// Set flags for this drag source
21    ///
22    /// # Arguments
23    /// * `flags` - Combination of source-related `DragDropSourceFlags`
24    #[inline]
25    pub fn flags(mut self, flags: DragDropSourceFlags) -> Self {
26        validate_drag_drop_source_flags("DragDropSource::flags()", flags);
27        self.flags = flags;
28        self
29    }
30
31    /// Set condition for when to update the payload
32    ///
33    /// # Arguments
34    /// * `cond` - When to update the payload data
35    #[inline]
36    pub fn condition(mut self, cond: DragDropPayloadCond) -> Self {
37        self.cond = cond;
38        self
39    }
40
41    /// Begin drag source with empty payload
42    ///
43    /// This is the safest option for simple drag and drop operations.
44    /// Use shared state or other mechanisms to transfer actual data.
45    ///
46    /// Returns a tooltip token if dragging started, `None` otherwise.
47    #[inline]
48    pub fn begin(self) -> Option<DragDropSourceTooltip<'ui>> {
49        self.begin_payload(())
50    }
51
52    /// Begin drag source with typed payload
53    ///
54    /// The payload data will be copied and managed by ImGui.
55    /// The data must be `Copy + 'static` for safety.
56    ///
57    /// # Arguments
58    /// * `payload` - Data to transfer (must be Copy + 'static)
59    ///
60    /// Returns a tooltip token if dragging started, `None` otherwise.
61    #[inline]
62    pub fn begin_payload<P: Copy + 'static>(
63        self,
64        payload: P,
65    ) -> Option<DragDropSourceTooltip<'ui>> {
66        unsafe {
67            let payload_size = std::mem::size_of::<TypedPayload<P>>();
68            assert!(
69                payload_size <= i32::MAX as usize,
70                "DragDropSource::begin_payload() payload size exceeds Dear ImGui's i32 payload range"
71            );
72
73            let payload = make_typed_payload(payload);
74            self.begin_payload_unchecked(&payload as *const _ as *const ffi::c_void, payload_size)
75        }
76    }
77
78    /// Begin drag source with raw payload data (unsafe)
79    ///
80    /// # Safety
81    /// The caller must ensure:
82    /// - `ptr` points to valid data of `size` bytes
83    /// - The data remains valid for the duration of the drag operation
84    /// - The data layout matches what targets expect
85    ///
86    /// # Arguments
87    /// * `ptr` - Pointer to payload data
88    /// * `size` - Size of payload data in bytes
89    pub unsafe fn begin_payload_unchecked(
90        &self,
91        ptr: *const ffi::c_void,
92        size: usize,
93    ) -> Option<DragDropSourceTooltip<'ui>> {
94        validate_payload_submission(
95            self.name.as_ref(),
96            ptr,
97            size,
98            "DragDropSource::begin_payload_unchecked()",
99        );
100        validate_drag_drop_source_flags("DragDropSource::begin_payload_unchecked()", self.flags);
101        self.ui.run_with_bound_context(|| unsafe {
102            let should_begin = sys::igBeginDragDropSource(self.flags.bits() as i32);
103
104            if should_begin {
105                sys::igSetDragDropPayload(
106                    self.ui.scratch_txt(self.name.as_ref()),
107                    ptr,
108                    size,
109                    self.cond as i32,
110                );
111
112                Some(DragDropSourceTooltip::new(self.ui))
113            } else {
114                None
115            }
116        })
117    }
118}
119
120/// Token representing an active drag source tooltip
121///
122/// While this token exists, you can add UI elements that will be shown
123/// as a tooltip during the drag operation.
124#[derive(Debug)]
125pub struct DragDropSourceTooltip<'ui> {
126    _ui: &'ui Ui,
127}
128
129impl<'ui> DragDropSourceTooltip<'ui> {
130    fn new(ui: &'ui Ui) -> Self {
131        Self { _ui: ui }
132    }
133
134    /// End the drag source tooltip manually
135    ///
136    /// This is called automatically when the token is dropped.
137    pub fn end(self) {
138        // Drop will handle cleanup
139    }
140}
141
142impl Drop for DragDropSourceTooltip<'_> {
143    fn drop(&mut self) {
144        self._ui
145            .run_with_bound_context(|| unsafe { sys::igEndDragDropSource() });
146    }
147}