use crate::Id;
use crate::sys;
use crate::ui::Ui;
use std::ffi::CString;
use std::ffi::c_char;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::slice;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SplitDirection {
Left,
Right,
Up,
Down,
}
impl From<SplitDirection> for sys::ImGuiDir {
fn from(dir: SplitDirection) -> Self {
match dir {
SplitDirection::Left => sys::ImGuiDir_Left,
SplitDirection::Right => sys::ImGuiDir_Right,
SplitDirection::Up => sys::ImGuiDir_Up,
SplitDirection::Down => sys::ImGuiDir_Down,
}
}
}
pub struct DockBuilder;
pub struct DockNode<'ui> {
raw: *mut sys::ImGuiDockNode,
_phantom: PhantomData<&'ui Ui>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NodeRect {
pub min: [f32; 2],
pub max: [f32; 2],
}
impl<'ui> DockNode<'ui> {
pub fn is_central(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsCentralNode(self.raw) }
}
pub fn is_dock_space(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsDockSpace(self.raw) }
}
pub fn is_empty(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsEmpty(self.raw) }
}
pub fn is_split(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsSplitNode(self.raw) }
}
pub fn is_root(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsRootNode(self.raw) }
}
pub fn is_floating(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsFloatingNode(self.raw) }
}
pub fn is_hidden_tab_bar(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsHiddenTabBar(self.raw) }
}
pub fn is_no_tab_bar(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsNoTabBar(self.raw) }
}
pub fn is_leaf(&self) -> bool {
unsafe { sys::ImGuiDockNode_IsLeafNode(self.raw) }
}
pub fn depth(&self) -> i32 {
unsafe { sys::igDockNodeGetDepth(self.raw as *const sys::ImGuiDockNode) as i32 }
}
pub fn window_menu_button_id(&self) -> sys::ImGuiID {
unsafe { sys::igDockNodeGetWindowMenuButtonId(self.raw as *const sys::ImGuiDockNode) }
}
pub fn root<'a>(&self, _ui: &'a Ui) -> Option<DockNode<'a>> {
let ptr = unsafe { sys::igDockNodeGetRootNode(self.raw) };
if ptr.is_null() {
None
} else {
Some(DockNode {
raw: ptr,
_phantom: PhantomData,
})
}
}
pub fn is_in_hierarchy_of(&self, parent: &DockNode<'_>) -> bool {
unsafe { sys::igDockNodeIsInHierarchyOf(self.raw, parent.raw) }
}
pub fn rect(&self) -> NodeRect {
let r = unsafe { sys::ImGuiDockNode_Rect(self.raw) };
NodeRect {
min: [r.Min.x, r.Min.y],
max: [r.Max.x, r.Max.y],
}
}
}
impl DockBuilder {
pub fn node<'ui>(_ui: &'ui Ui, node_id: Id) -> Option<DockNode<'ui>> {
let ptr = unsafe { sys::igDockBuilderGetNode(node_id.into()) };
if ptr.is_null() {
None
} else {
Some(DockNode {
raw: ptr,
_phantom: PhantomData,
})
}
}
pub fn central_node<'ui>(_ui: &'ui Ui, dockspace_id: Id) -> Option<DockNode<'ui>> {
let ptr = unsafe { sys::igDockBuilderGetCentralNode(dockspace_id.into()) };
if ptr.is_null() {
None
} else {
Some(DockNode {
raw: ptr,
_phantom: PhantomData,
})
}
}
pub fn node_exists(ui: &Ui, node_id: Id) -> bool {
Self::node(ui, node_id).is_some()
}
#[doc(alias = "DockBuilderAddNode")]
pub fn add_node(node_id: Id, flags: crate::DockNodeFlags) -> Id {
unsafe { Id::from(sys::igDockBuilderAddNode(node_id.into(), flags.bits())) }
}
#[doc(alias = "DockBuilderRemoveNode")]
pub fn remove_node(node_id: Id) {
unsafe { sys::igDockBuilderRemoveNode(node_id.into()) }
}
#[doc(alias = "DockBuilderRemoveNodeDockedWindows")]
pub fn remove_node_docked_windows(node_id: Id, clear_settings_refs: bool) {
unsafe { sys::igDockBuilderRemoveNodeDockedWindows(node_id.into(), clear_settings_refs) }
}
#[doc(alias = "DockBuilderRemoveNodeChildNodes")]
pub fn remove_node_child_nodes(node_id: Id) {
unsafe { sys::igDockBuilderRemoveNodeChildNodes(node_id.into()) }
}
#[doc(alias = "DockBuilderSetNodePos")]
pub fn set_node_pos(node_id: Id, pos: [f32; 2]) {
unsafe {
let pos_vec = sys::ImVec2 {
x: pos[0],
y: pos[1],
};
sys::igDockBuilderSetNodePos(node_id.into(), pos_vec)
}
}
#[doc(alias = "DockBuilderSetNodeSize")]
pub fn set_node_size(node_id: Id, size: [f32; 2]) {
unsafe {
let size_vec = sys::ImVec2 {
x: size[0],
y: size[1],
};
sys::igDockBuilderSetNodeSize(node_id.into(), size_vec)
}
}
#[doc(alias = "DockBuilderSplitNode")]
pub fn split_node(
node_id: Id,
split_dir: SplitDirection,
size_ratio_for_node_at_dir: f32,
) -> (Id, Id) {
unsafe {
let mut id_at_dir: sys::ImGuiID = 0;
let mut id_at_opposite: sys::ImGuiID = 0;
let _ = sys::igDockBuilderSplitNode(
node_id.into(),
split_dir.into(),
size_ratio_for_node_at_dir,
&mut id_at_dir,
&mut id_at_opposite,
);
(Id::from(id_at_dir), Id::from(id_at_opposite))
}
}
#[doc(alias = "DockBuilderDockWindow")]
pub fn dock_window(window_name: &str, node_id: Id) {
let window_name_ptr = crate::string::tls_scratch_txt(window_name);
unsafe { sys::igDockBuilderDockWindow(window_name_ptr, node_id.into()) }
}
#[doc(alias = "DockBuilderCopyDockSpace")]
pub fn copy_dock_space(src_dockspace_id: Id, dst_dockspace_id: Id) {
unsafe {
sys::igDockBuilderCopyDockSpace(
src_dockspace_id.into(),
dst_dockspace_id.into(),
std::ptr::null_mut(),
)
}
}
#[doc(alias = "DockBuilderCopyNode")]
pub fn copy_node(src_node_id: Id, dst_node_id: Id) {
unsafe {
sys::igDockBuilderCopyNode(src_node_id.into(), dst_node_id.into(), std::ptr::null_mut())
}
}
#[doc(alias = "DockBuilderCopyWindowSettings")]
pub fn copy_window_settings(src_name: &str, dst_name: &str) {
let (src_ptr, dst_ptr) = crate::string::tls_scratch_txt_two(src_name, dst_name);
unsafe { sys::igDockBuilderCopyWindowSettings(src_ptr, dst_ptr) }
}
#[doc(alias = "DockBuilderCopyDockSpace")]
pub fn copy_dock_space_with_window_remap(
src_dockspace_id: Id,
dst_dockspace_id: Id,
window_remaps: &[(&str, &str)],
) {
let mut cstrings: Vec<CString> = Vec::with_capacity(window_remaps.len() * 2);
for (src, dst) in window_remaps {
let Ok(src) = CString::new(*src) else {
continue;
};
let Ok(dst) = CString::new(*dst) else {
continue;
};
cstrings.push(src);
cstrings.push(dst);
}
if cstrings.is_empty() {
return;
}
let ptrs: Vec<*const c_char> = cstrings.iter().map(|s| s.as_ptr()).collect();
let mut boxed: Box<[*const c_char]> = ptrs.into_boxed_slice();
let boxed_len_i32 = match i32::try_from(boxed.len()) {
Ok(n) => n,
Err(_) => return,
};
let mut vec_in = sys::ImVector_const_charPtr {
Size: boxed_len_i32,
Capacity: boxed_len_i32,
Data: boxed.as_mut_ptr(),
};
unsafe {
sys::igDockBuilderCopyDockSpace(
src_dockspace_id.into(),
dst_dockspace_id.into(),
&mut vec_in,
);
}
drop(boxed);
drop(cstrings);
}
#[doc(alias = "DockBuilderCopyNode")]
pub fn copy_node_with_remap_out(src_node_id: Id, dst_node_id: Id) -> Vec<(Id, Id)> {
let mut out = sys::ImVector_ImGuiID::default();
unsafe {
sys::igDockBuilderCopyNode(src_node_id.into(), dst_node_id.into(), &mut out);
}
let mut result: Vec<(Id, Id)> = Vec::new();
unsafe {
if !out.Data.is_null() {
if out.Size > 0 {
let len = match usize::try_from(out.Size) {
Ok(len) => len,
Err(_) => {
sys::igMemFree(out.Data as *mut c_void);
return result;
}
};
let slice_ids = slice::from_raw_parts(out.Data, len);
for pair in slice_ids.chunks_exact(2) {
result.push((Id::from(pair[0]), Id::from(pair[1])));
}
}
sys::igMemFree(out.Data as *mut c_void);
}
}
result
}
#[doc(alias = "DockBuilderFinish")]
pub fn finish(node_id: Id) {
unsafe { sys::igDockBuilderFinish(node_id.into()) }
}
}