use std::{any, ffi, marker::PhantomData};
use crate::{sys, Condition, Ui};
use bitflags::bitflags;
bitflags!(
#[repr(transparent)]
pub struct DragDropFlags: u32 {
const SOURCE_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_SourceNoPreviewTooltip;
const SOURCE_NO_DISABLE_HOVER = sys::ImGuiDragDropFlags_SourceNoDisableHover;
const SOURCE_NO_HOLD_TO_OPEN_OTHERS = sys::ImGuiDragDropFlags_SourceNoHoldToOpenOthers;
const SOURCE_ALLOW_NULL_ID = sys::ImGuiDragDropFlags_SourceAllowNullID;
const SOURCE_EXTERN = sys::ImGuiDragDropFlags_SourceExtern;
const SOURCE_AUTO_EXPIRE_PAYLOAD = sys::ImGuiDragDropFlags_SourceAutoExpirePayload;
const ACCEPT_BEFORE_DELIVERY = sys::ImGuiDragDropFlags_AcceptBeforeDelivery;
const ACCEPT_NO_DRAW_DEFAULT_RECT = sys::ImGuiDragDropFlags_AcceptNoDrawDefaultRect;
const ACCEPT_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_AcceptNoPreviewTooltip;
const ACCEPT_PEEK_ONLY = sys::ImGuiDragDropFlags_AcceptPeekOnly;
}
);
#[derive(Debug)]
pub struct DragDropSource<T> {
name: T,
flags: DragDropFlags,
cond: Condition,
}
impl<T: AsRef<str>> DragDropSource<T> {
pub fn new(name: T) -> Self {
Self {
name,
flags: DragDropFlags::empty(),
cond: Condition::Always,
}
}
#[inline]
pub fn flags(mut self, flags: DragDropFlags) -> Self {
self.flags = flags;
self
}
#[inline]
pub fn condition(mut self, cond: Condition) -> Self {
self.cond = cond;
self
}
#[inline]
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<DragDropSourceToolTip<'ui>> {
self.begin_payload(ui, ())
}
#[inline]
pub fn begin_payload<'ui, P: Copy + 'static>(
self,
ui: &Ui<'ui>,
payload: P,
) -> Option<DragDropSourceToolTip<'ui>> {
unsafe {
let payload = TypedPayload::new(payload);
self.begin_payload_unchecked(
ui,
&payload as *const _ as *const ffi::c_void,
std::mem::size_of::<TypedPayload<P>>(),
)
}
}
#[inline]
pub unsafe fn begin_payload_unchecked<'ui>(
&self,
ui: &Ui<'ui>,
ptr: *const ffi::c_void,
size: usize,
) -> Option<DragDropSourceToolTip<'ui>> {
let should_begin = sys::igBeginDragDropSource(self.flags.bits() as i32);
if should_begin {
sys::igSetDragDropPayload(ui.scratch_txt(&self.name), ptr, size, self.cond as i32);
Some(DragDropSourceToolTip::push())
} else {
None
}
}
}
pub struct DragDropSourceToolTip<'ui>(PhantomData<Ui<'ui>>);
impl DragDropSourceToolTip<'_> {
#[inline]
fn push() -> Self {
Self(PhantomData)
}
#[inline]
pub fn end(self) {
}
}
impl Drop for DragDropSourceToolTip<'_> {
fn drop(&mut self) {
unsafe { sys::igEndDragDropSource() }
}
}
#[derive(Debug)]
pub struct DragDropTarget<'ui>(&'ui Ui<'ui>);
impl<'ui> DragDropTarget<'ui> {
#[doc(alias = "BeginDragDropTarget")]
pub fn new(ui: &'ui Ui<'ui>) -> Option<Self> {
let should_begin = unsafe { sys::igBeginDragDropTarget() };
if should_begin {
Some(Self(ui))
} else {
None
}
}
pub fn accept_payload_empty(
&self,
name: impl AsRef<str>,
flags: DragDropFlags,
) -> Option<DragDropPayloadEmpty> {
self.accept_payload(name, flags)?
.ok()
.map(|payload_pod: DragDropPayloadPod<()>| DragDropPayloadEmpty {
preview: payload_pod.preview,
delivery: payload_pod.delivery,
})
}
pub fn accept_payload<T: 'static + Copy, Name: AsRef<str>>(
&self,
name: Name,
flags: DragDropFlags,
) -> Option<Result<DragDropPayloadPod<T>, PayloadIsWrongType>> {
let output = unsafe { self.accept_payload_unchecked(name, flags) };
output.map(|unsafe_payload| {
let received =
unsafe { (unsafe_payload.data as *const TypedPayloadHeader).read_unaligned() };
let expected = any::TypeId::of::<T>();
if received.type_id == expected {
let data =
unsafe { (unsafe_payload.data as *const TypedPayload<T>).read_unaligned() }
.data;
Ok(DragDropPayloadPod {
data,
preview: unsafe_payload.preview,
delivery: unsafe_payload.delivery,
})
} else {
Err(PayloadIsWrongType {
received,
expected: TypedPayloadHeader::new::<T>(),
})
}
})
}
pub unsafe fn accept_payload_unchecked(
&self,
name: impl AsRef<str>,
flags: DragDropFlags,
) -> Option<DragDropPayload> {
let inner = sys::igAcceptDragDropPayload(self.0.scratch_txt(name), flags.bits() as i32);
if inner.is_null() {
None
} else {
let inner = *inner;
Some(DragDropPayload {
data: inner.Data,
size: inner.DataSize as usize,
preview: inner.Preview,
delivery: inner.Delivery,
})
}
}
pub fn pop(self) {
}
}
impl Drop for DragDropTarget<'_> {
fn drop(&mut self) {
unsafe { sys::igEndDragDropTarget() }
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct DragDropPayloadEmpty {
pub preview: bool,
pub delivery: bool,
}
#[derive(Debug, Copy, Clone)]
#[non_exhaustive]
pub struct DragDropPayloadPod<T: 'static + Copy> {
pub data: T,
pub preview: bool,
pub delivery: bool,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct DragDropPayload {
pub data: *const ffi::c_void,
pub size: usize,
pub preview: bool,
pub delivery: bool,
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct TypedPayload<T> {
header: TypedPayloadHeader,
data: T,
}
impl<T: Copy + 'static> TypedPayload<T> {
fn new(data: T) -> Self {
Self {
header: TypedPayloadHeader::new::<T>(),
data,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
#[repr(C)]
struct TypedPayloadHeader {
type_id: any::TypeId,
#[cfg(debug_assertions)]
type_name: &'static str,
}
impl TypedPayloadHeader {
#[cfg(debug_assertions)]
fn new<T: 'static>() -> Self {
Self {
type_id: any::TypeId::of::<T>(),
type_name: any::type_name::<T>(),
}
}
#[cfg(not(debug_assertions))]
fn new<T: 'static>() -> Self {
Self {
type_id: any::TypeId::of::<T>(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
pub struct PayloadIsWrongType {
expected: TypedPayloadHeader,
received: TypedPayloadHeader,
}
#[cfg(debug_assertions)]
impl std::fmt::Display for PayloadIsWrongType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Payload is {} -- expected {}",
self.received.type_name, self.expected.type_name
)
}
}
#[cfg(not(debug_assertions))]
impl std::fmt::Display for PayloadIsWrongType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad("Payload is wrong type")
}
}
impl std::error::Error for PayloadIsWrongType {}