mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-16 07:58:33 +00:00
drag and drop experimental implementation
This commit is contained in:
parent
17be09eaef
commit
35c0cf0e6f
189
imgui/src/drag_drop.rs
Normal file
189
imgui/src/drag_drop.rs
Normal file
@ -0,0 +1,189 @@
|
||||
use std::{ffi, marker::PhantomData, ptr};
|
||||
|
||||
use crate::{sys, Condition, ImStr, Ui};
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags!(
|
||||
/// Flags for igBeginDragDropSource(), igAcceptDragDropPayload()
|
||||
#[repr(transparent)]
|
||||
pub struct DragDropFlags: u32 {
|
||||
/// By default, a successful call to igBeginDragDropSource opens a tooltip so you can
|
||||
/// display a preview or description of the source contents. This flag disable this
|
||||
/// behavior.
|
||||
const SOURCE_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_SourceNoPreviewTooltip;
|
||||
/// By default, when dragging we clear data so that igIsItemHovered() will return false, to
|
||||
/// avoid subsequent user code submitting tooltips. This flag disable this behavior so you
|
||||
/// can still call igIsItemHovered() on the source item.
|
||||
const SOURCE_NO_DISABLE_HOVER = sys::ImGuiDragDropFlags_SourceNoDisableHover;
|
||||
/// Disable the behavior that allows to open tree nodes and collapsing header by holding
|
||||
/// over them while dragging a source item.
|
||||
const SOURCE_NO_HOLD_TO_OPEN_OTHERS = sys::ImGuiDragDropFlags_SourceNoHoldToOpenOthers;
|
||||
/// Allow items such as igText(), igImage() that have no unique identifier to be used as
|
||||
/// drag source, by manufacturing a temporary identifier based on their window-relative
|
||||
/// position. This is extremely unusual within the dear imgui ecosystem and so we made it
|
||||
/// explicit.
|
||||
const SOURCE_ALLOW_NULL_ID = sys::ImGuiDragDropFlags_SourceAllowNullID;
|
||||
/// External source (from outside of imgui), won't attempt to read current item/window
|
||||
/// info. Will always return true. Only one Extern source can be active simultaneously.
|
||||
const SOURCE_EXTERN = sys::ImGuiDragDropFlags_SourceExtern;
|
||||
/// Automatically expire the payload if the source ceases to be submitted (otherwise
|
||||
/// payloads are persisting while being dragged)
|
||||
const SOURCE_AUTO_EXPIRE_PAYLOAD = sys::ImGuiDragDropFlags_SourceAutoExpirePayload;
|
||||
/// igAcceptDragDropPayload() will returns true even before the mouse button is released.
|
||||
/// You can then call igIsDelivery() to test if the payload needs to be delivered.
|
||||
const ACCEPT_BEFORE_DELIVERY = sys::ImGuiDragDropFlags_AcceptBeforeDelivery;
|
||||
/// Do not draw the default highlight rectangle when hovering over target.
|
||||
const ACCEPT_NO_DRAW_DEFAULT_RECT = sys::ImGuiDragDropFlags_AcceptNoDrawDefaultRect;
|
||||
/// Request hiding the igBeginDragDropSource tooltip from the igBeginDragDropTarget site.
|
||||
const ACCEPT_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_AcceptNoPreviewTooltip;
|
||||
/// For peeking ahead and inspecting the payload before delivery. This is just a convenience
|
||||
/// flag for the intersection of `ACCEPT_BEFORE_DELIVERY` and `ACCEPT_NO_DRAW_DEFAULT_RECT`
|
||||
const ACCEPT_PEEK_ONLY = sys::ImGuiDragDropFlags_AcceptPeekOnly;
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DragDropSource<'a> {
|
||||
name: &'a ImStr,
|
||||
flags: DragDropFlags,
|
||||
payload: *const ffi::c_void,
|
||||
size: usize,
|
||||
cond: Condition,
|
||||
}
|
||||
|
||||
impl<'a> DragDropSource<'a> {
|
||||
pub fn new(name: &'a ImStr) -> Self {
|
||||
Self {
|
||||
name,
|
||||
flags: DragDropFlags::empty(),
|
||||
payload: ptr::null(),
|
||||
size: 0,
|
||||
cond: Condition::Always,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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_EXTERN`, `SOURCE_AUTO_EXPIRE_PAYLOAD` make semantic sense, but any other flags will
|
||||
/// be accepted without panic.
|
||||
pub fn flags(mut self, flags: DragDropFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the condition on the [DragDropSource]. Defaults to [Always](Condition::Always).
|
||||
pub fn condition(mut self, cond: Condition) -> Self {
|
||||
self.cond = cond;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a payload. 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).
|
||||
///
|
||||
/// ## Safety
|
||||
/// 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),
|
||||
/// you can easily create memory safety problems.
|
||||
pub unsafe fn payload<T>(mut self, payload: *const T) -> Self {
|
||||
self.payload = payload as *const ffi::c_void;
|
||||
self.size = std::mem::size_of::<T>();
|
||||
self
|
||||
}
|
||||
|
||||
/// 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>> {
|
||||
let should_begin = unsafe { sys::igBeginDragDropSource(self.flags.bits() as i32) };
|
||||
|
||||
if should_begin {
|
||||
unsafe {
|
||||
sys::igSetDragDropPayload(self.name.as_ptr(), self.payload, self.size, self.cond as i32);
|
||||
|
||||
Some(DragDropSourceToolTip::push())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper struct for RAII drap-drop support.
|
||||
pub struct DragDropSourceToolTip<'ui>(PhantomData<Ui<'ui>>);
|
||||
|
||||
impl DragDropSourceToolTip<'_> {
|
||||
/// Creates a new tooltip internally.
|
||||
fn push() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
|
||||
/// Ends the tooltip directly. You could choose to simply allow this to drop
|
||||
/// by not calling this, which will also be fine.
|
||||
pub fn pop(self) {
|
||||
// left empty to invoke drop...
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DragDropSourceToolTip<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { sys::igEndDragDropSource() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DragDropPayload {
|
||||
/// Data which is copied and owned by ImGui. If you have accepted the payload, you can
|
||||
/// take ownership of the data; otherwise, view it immutably. Interacting with `data` is
|
||||
/// very unsafe.
|
||||
pub data: *const ffi::c_void,
|
||||
/// Set when [`accept_drag_drop_payload`](Self::accept_drag_drop_payload) was called
|
||||
/// and mouse has been hovering the target item (nb: handle overlapping drag targets).
|
||||
/// @fixme: literally what does this mean
|
||||
pub preview: bool,
|
||||
|
||||
/// Set when AcceptDragDropPayload() was called and mouse button is released over the target item.
|
||||
pub delivery: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DragDropTarget<'ui>(PhantomData<Ui<'ui>>);
|
||||
|
||||
impl<'ui> DragDropTarget<'ui> {
|
||||
pub fn new(_ui: &Ui<'_>) -> Option<Self> {
|
||||
let should_begin = unsafe { sys::igBeginDragDropTarget() };
|
||||
if should_begin {
|
||||
Some(Self(PhantomData))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn accept_drag_drop_payload(&self, name: &ImStr, flags: DragDropFlags) -> Option<DragDropPayload> {
|
||||
unsafe {
|
||||
let inner = sys::igAcceptDragDropPayload(name.as_ptr(), flags.bits() as i32);
|
||||
if inner.is_null() {
|
||||
None
|
||||
} else {
|
||||
let inner = *inner;
|
||||
|
||||
Some(DragDropPayload {
|
||||
data: inner.Data,
|
||||
preview: inner.Preview,
|
||||
delivery: inner.Delivery,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ends the current target. Ironically, this doesn't really do
|
||||
/// anything in ImGui or in imgui-rs, but it might in the future.
|
||||
pub fn pop(self) {
|
||||
// omitted...exists just to run Drop.
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DragDropTarget<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { sys::igEndDragDropTarget() }
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ use std::thread;
|
||||
|
||||
pub use self::clipboard::*;
|
||||
pub use self::context::*;
|
||||
pub use self::drag_drop::*;
|
||||
pub use self::draw_list::{ChannelsSplit, DrawListMut, ImColor};
|
||||
pub use self::fonts::atlas::*;
|
||||
pub use self::fonts::font::*;
|
||||
@ -52,6 +53,7 @@ use internal::RawCast;
|
||||
mod clipboard;
|
||||
mod columns;
|
||||
mod context;
|
||||
mod drag_drop;
|
||||
mod draw_list;
|
||||
mod fonts;
|
||||
mod input;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user