mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-23 19:38:27 +00:00
Updated for pod data
This commit is contained in:
parent
07e0580d2d
commit
a8975b95c0
@ -16,6 +16,7 @@ exclude = ["/resources"]
|
|||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
imgui-sys = { version = "0.6.0", path = "../imgui-sys" }
|
imgui-sys = { version = "0.6.0", path = "../imgui-sys" }
|
||||||
parking_lot = "0.11"
|
parking_lot = "0.11"
|
||||||
|
bytemuck = "1.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
wasm = ["imgui-sys/wasm"]
|
wasm = ["imgui-sys/wasm"]
|
||||||
|
|||||||
@ -42,12 +42,53 @@ bitflags!(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// A drag-drop source without any payload. Typically, when dragging and dropping data in Dear ImGui,
|
||||||
|
/// a user will attach a payload to that drag and drop, so the accepter of the drop can read or
|
||||||
|
/// otherwise react to the drop. This struct attaches no data to the drag-drop, which means that an
|
||||||
|
/// accepter will simply be notified when the payload of a given type has been dropped.
|
||||||
|
///
|
||||||
|
/// This is still probably the most useful way in imgui-rs to handle payloads.
|
||||||
|
/// Using `once_cell` or some shared data, this pattern can be very powerful:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// fn show_ui(ui: &Ui<'_>, drop_message: &mut Option<String>) {
|
||||||
|
/// ui.button(im_str!("Drag me!"));
|
||||||
|
///
|
||||||
|
/// let drag_drop_name = im_str!("Test Drag");
|
||||||
|
///
|
||||||
|
/// // drag drop SOURCE
|
||||||
|
/// if DragDropSource::new(drag_drop_name).begin(ui).is_some() {
|
||||||
|
/// // warning -- this would allocate every frame if `DragDropSource` has
|
||||||
|
/// // condition `Always`, which it does by default. We're okay with that for
|
||||||
|
/// // this example, but real code probably wouldn't want to allocate so much.
|
||||||
|
/// *drop_message = Some("Test Payload".to_string());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// ui.button(im_str!("Target me!"));
|
||||||
|
///
|
||||||
|
/// // drag drop TARGET
|
||||||
|
/// if let Some(target) = imgui::DragDropTarget::new(ui) {
|
||||||
|
/// if target
|
||||||
|
/// .accept_drag_drop_payload(drag_drop_name, DragDropFlags::empty())
|
||||||
|
/// .is_some()
|
||||||
|
/// {
|
||||||
|
/// let msg = drop_message.take().unwrap();
|
||||||
|
/// assert_eq!(msg, "Test Payload");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// target.pop();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In the above, you'll see how the payload is really just a message passing service.
|
||||||
|
/// This method will give a user an easier time than using [DragDropSourcePayloadPod] or
|
||||||
|
/// the unsafe [DragDropSourcePayloadUnsafe], as the data for a user can be kept entirely in
|
||||||
|
/// Rust and never has to do a roundtrip into C++, which can cause unexpected issues.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DragDropSource<'a> {
|
pub struct DragDropSource<'a> {
|
||||||
name: &'a ImStr,
|
name: &'a ImStr,
|
||||||
flags: DragDropFlags,
|
flags: DragDropFlags,
|
||||||
payload: *const ffi::c_void,
|
|
||||||
size: usize,
|
|
||||||
cond: Condition,
|
cond: Condition,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,29 +100,27 @@ impl<'a> DragDropSource<'a> {
|
|||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
flags: DragDropFlags::empty(),
|
flags: DragDropFlags::empty(),
|
||||||
payload: ptr::null(),
|
|
||||||
size: 0,
|
|
||||||
cond: Condition::Always,
|
cond: Condition::Always,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [DragDropSource] with no flags and the `Condition::Always` with the given name.
|
// /// Creates a new [DragDropSource] with no flags and the `Condition::Always` with the given name.
|
||||||
/// ImGui refers to this `name` field as a `type`, but really it's just an identifier to match up
|
// /// ImGui refers to this `name` field as a `type`, but really it's just an identifier to match up
|
||||||
/// Source/Target for DragDrop.
|
// /// Source/Target for DragDrop.
|
||||||
///
|
// ///
|
||||||
/// This payload will be passed to ImGui, which will provide it to
|
// /// This payload will be passed to ImGui, which will provide it to
|
||||||
/// a target when it runs [accept_drag_drop_payload](DragDropTarget::accept_drag_drop_payload).
|
// /// a target when it runs [accept_drag_drop_payload](DragDropTarget::accept_drag_drop_payload).
|
||||||
///
|
// ///
|
||||||
/// ## Safety
|
// /// ## Safety
|
||||||
/// This function is not inherently unsafe, and won't panic itself, but using it opts you into
|
// /// This function is not inherently unsafe, and won't panic itself, but using it opts you into
|
||||||
/// managing the lifetime yourself. When you dereference the pointer given in [accept_drag_drop_payload](DragDropTarget::accept_drag_drop_payload),
|
// /// managing the lifetime yourself. When you dereference the pointer given in [accept_drag_drop_payload](DragDropTarget::accept_drag_drop_payload),
|
||||||
/// you can easily create memory safety problems.
|
// /// you can easily create memory safety problems.
|
||||||
pub unsafe fn payload<T>(name: &'a ImStr, payload: *const T) -> Self {
|
// pub unsafe fn payload<T>(name: &'a ImStr, payload: *const T) -> Self {
|
||||||
let mut output = Self::new(name);
|
// let mut output = Self::new(name);
|
||||||
output.payload = payload as *const ffi::c_void;
|
// output.payload = payload as *const ffi::c_void;
|
||||||
output.size = std::mem::size_of::<T>();
|
// output.size = std::mem::size_of::<T>();
|
||||||
output
|
// output
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Sets the flags on the [DragDropSource]. Only the flags `SOURCE_NO_PREVIEW_TOOLTIP`,
|
/// Sets the flags on the [DragDropSource]. Only the flags `SOURCE_NO_PREVIEW_TOOLTIP`,
|
||||||
/// `SOURCE_NO_DISABLE_HOVER`, `SOURCE_NO_HOLD_TO_OPEN_OTHERS`, `SOURCE_ALLOW_NULL_ID`,
|
/// `SOURCE_NO_DISABLE_HOVER`, `SOURCE_NO_HOLD_TO_OPEN_OTHERS`, `SOURCE_ALLOW_NULL_ID`,
|
||||||
@ -98,6 +137,72 @@ impl<'a> DragDropSource<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates the source of a drag and returns a handle on the tooltip.
|
||||||
|
/// This handle can be immediately dropped without binding it, in which case a default empty
|
||||||
|
/// circle will be used for the "blank" tooltip as this item is being dragged around.
|
||||||
|
///
|
||||||
|
/// Otherwise, use this tooltip to add data which will display as this item is dragged.
|
||||||
|
/// If `SOURCE_NO_PREVIEW_TOOLTIP` is enabled, however, no preview will be displayed
|
||||||
|
/// and this returned token does nothing. Additionally, a given target may use the flag
|
||||||
|
/// `ACCEPT_NO_PREVIEW_TOOLTIP`, which will also prevent this tooltip from being shown.
|
||||||
|
///
|
||||||
|
/// For more information on how to use payload-less drag/drops, please see [DragDropSource]'s
|
||||||
|
/// documentation.
|
||||||
|
pub fn begin<'ui>(self, _ui: &'ui Ui) -> Option<DragDropSourceToolTip<'ui>> {
|
||||||
|
let should_begin = unsafe { sys::igBeginDragDropSource(self.flags.bits() as i32) };
|
||||||
|
|
||||||
|
if should_begin {
|
||||||
|
unsafe {
|
||||||
|
sys::igSetDragDropPayload(self.name.as_ptr(), ptr::null(), 0, self.cond as i32);
|
||||||
|
|
||||||
|
Some(DragDropSourceToolTip::push())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DragDropSourcePayloadPod<'a, T> {
|
||||||
|
name: &'a ImStr,
|
||||||
|
payload: &'a T,
|
||||||
|
flags: DragDropFlags,
|
||||||
|
cond: Condition,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: bytemuck::Pod> DragDropSourcePayloadPod<'a, T> {
|
||||||
|
/// Creates a new [DragDropSourcePayloadPod] with no flags and the `Condition::Always` with the given name.
|
||||||
|
/// ImGui refers to this `name` field as a `type`, but really it's just an identifier to match up
|
||||||
|
/// Source/Target for DragDrop.
|
||||||
|
pub fn new(name: &'a ImStr, payload: &'a T) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
flags: DragDropFlags::empty(),
|
||||||
|
payload,
|
||||||
|
cond: Condition::Always,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the flags on the [DragDropSourcePayloadPod]. Only the flags `SOURCE_NO_PREVIEW_TOOLTIP`,
|
||||||
|
/// `SOURCE_NO_DISABLE_HOVER`, `SOURCE_NO_HOLD_TO_OPEN_OTHERS`, `SOURCE_ALLOW_NULL_ID`,
|
||||||
|
/// `SOURCE_EXTERN`, `SOURCE_AUTO_EXPIRE_PAYLOAD` make semantic sense, but any other flags will
|
||||||
|
/// be accepted without panic.
|
||||||
|
///
|
||||||
|
/// Defaults to empty.
|
||||||
|
pub fn flags(mut self, flags: DragDropFlags) -> Self {
|
||||||
|
self.flags = flags;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the condition on the [DragDropSourcePayloadPod].
|
||||||
|
///
|
||||||
|
/// Defaults to [Always](Condition::Always).
|
||||||
|
pub fn condition(mut self, cond: Condition) -> Self {
|
||||||
|
self.cond = cond;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
|
/// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
|
||||||
pub fn begin<'ui>(self, _ui: &'ui Ui) -> Option<DragDropSourceToolTip<'ui>> {
|
pub fn begin<'ui>(self, _ui: &'ui Ui) -> Option<DragDropSourceToolTip<'ui>> {
|
||||||
let should_begin = unsafe { sys::igBeginDragDropSource(self.flags.bits() as i32) };
|
let should_begin = unsafe { sys::igBeginDragDropSource(self.flags.bits() as i32) };
|
||||||
@ -106,8 +211,8 @@ impl<'a> DragDropSource<'a> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
sys::igSetDragDropPayload(
|
sys::igSetDragDropPayload(
|
||||||
self.name.as_ptr(),
|
self.name.as_ptr(),
|
||||||
self.payload,
|
self.payload as *const _ as *const ffi::c_void,
|
||||||
self.size,
|
std::mem::size_of::<T>(),
|
||||||
self.cond as i32,
|
self.cond as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -163,6 +268,27 @@ pub struct DragDropPayload {
|
|||||||
pub delivery: bool,
|
pub delivery: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DragDropEmptyPayload {
|
||||||
|
/// @fixme add docs.
|
||||||
|
pub preview: bool,
|
||||||
|
|
||||||
|
// @fixme add docs
|
||||||
|
pub delivery: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DragDropPodPayload<T: bytemuck::Pod> {
|
||||||
|
/// The kind data which was requested.
|
||||||
|
pub data: T,
|
||||||
|
|
||||||
|
/// @fixme add docs.
|
||||||
|
pub preview: bool,
|
||||||
|
|
||||||
|
// @fixme add docs
|
||||||
|
pub delivery: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DragDropTarget<'ui>(PhantomData<Ui<'ui>>);
|
pub struct DragDropTarget<'ui>(PhantomData<Ui<'ui>>);
|
||||||
|
|
||||||
@ -176,6 +302,50 @@ impl<'ui> DragDropTarget<'ui> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn accept_empty_payload(
|
||||||
|
&self,
|
||||||
|
name: &ImStr,
|
||||||
|
flags: DragDropFlags,
|
||||||
|
) -> Option<DragDropEmptyPayload> {
|
||||||
|
unsafe {
|
||||||
|
let inner = sys::igAcceptDragDropPayload(name.as_ptr(), flags.bits() as i32);
|
||||||
|
if inner.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let inner = *inner;
|
||||||
|
|
||||||
|
Some(DragDropEmptyPayload {
|
||||||
|
preview: inner.Preview,
|
||||||
|
delivery: inner.Delivery,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accept_pod_payload<T: bytemuck::Pod>(
|
||||||
|
&self,
|
||||||
|
name: &ImStr,
|
||||||
|
flags: DragDropFlags,
|
||||||
|
) -> Option<Result<DragDropPodPayload<T>, bytemuck::PodCastError>> {
|
||||||
|
unsafe {
|
||||||
|
let inner = sys::igAcceptDragDropPayload(name.as_ptr(), flags.bits() as i32);
|
||||||
|
if inner.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let inner = *inner;
|
||||||
|
let data = std::slice::from_raw_parts(inner.Data as *const u8, 1);
|
||||||
|
|
||||||
|
Some(
|
||||||
|
bytemuck::try_from_bytes(data).map(|data| DragDropPodPayload {
|
||||||
|
data: *data,
|
||||||
|
preview: inner.Preview,
|
||||||
|
delivery: inner.Delivery,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Accepts, popping the drag_drop payload, if it exists. If `DragDropFlags::ACCEPT_BEFORE_DELIVERY` is
|
/// Accepts, popping the drag_drop payload, if it exists. If `DragDropFlags::ACCEPT_BEFORE_DELIVERY` is
|
||||||
/// set, this function will return `Some` even if the type is wrong as long as there is a payload to accept.
|
/// set, this function will return `Some` even if the type is wrong as long as there is a payload to accept.
|
||||||
/// How do we possibly handle communicating that this data is somewhat immutable?
|
/// How do we possibly handle communicating that this data is somewhat immutable?
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user