use crate::{DockNodeFlags, Id, WindowClass, WindowClassError, WindowKey};
use thiserror::Error;
#[derive(Clone, Debug, PartialEq)]
pub enum DockLayout {
Tabs(Vec<WindowKey>),
Split {
direction: DockSplit,
ratio: f32,
first: Box<DockLayout>,
second: Box<DockLayout>,
},
}
impl DockLayout {
pub fn tabs(windows: impl IntoIterator<Item = impl Into<WindowKey>>) -> Self {
Self::Tabs(windows.into_iter().map(Into::into).collect())
}
pub fn split(direction: DockSplit, ratio: f32, first: DockLayout, second: DockLayout) -> Self {
Self::Split {
direction,
ratio,
first: Box::new(first),
second: Box::new(second),
}
}
pub fn validate(&self) -> Result<(), DockspaceError> {
super::compile::compile_layout(self).map(|_| ())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DockSplit {
Left,
Right,
Up,
Down,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum DockLayoutApply {
#[default]
IfMissing,
Replace,
}
#[derive(Clone, Debug)]
pub(crate) struct DockspaceConfig {
root_id: Id,
flags: DockNodeFlags,
window_class: Option<WindowClass>,
}
impl DockspaceConfig {
pub(crate) fn new(
root_id: Id,
flags: DockNodeFlags,
window_class: Option<WindowClass>,
) -> Self {
Self {
root_id,
flags,
window_class,
}
}
pub(crate) fn root_id(&self) -> Id {
self.root_id
}
pub(crate) fn dock_flags(&self) -> DockNodeFlags {
self.flags
}
pub(crate) fn window_class_ref(&self) -> Option<&WindowClass> {
self.window_class.as_ref()
}
pub(crate) fn validate(&self) -> Result<(), DockspaceError> {
if self.root_id.raw() == 0 {
return Err(DockspaceError::ZeroRootId);
}
let unsupported = self.flags.bits() & !DockNodeFlags::all().bits();
if unsupported != 0 {
return Err(DockspaceError::UnsupportedDockNodeFlags { bits: unsupported });
}
if let Some(window_class) = &self.window_class {
window_class.validate()?;
}
Ok(())
}
}
#[derive(Clone, Debug, Error, PartialEq)]
#[non_exhaustive]
pub enum DockspaceError {
#[error("a dockspace in the current window requires an explicit non-zero root ID")]
MissingRootId,
#[error("dockspace root ID must be non-zero")]
ZeroRootId,
#[error("dockspace flags contain unsupported ImGuiDockNodeFlags bits: 0x{bits:X}")]
UnsupportedDockNodeFlags { bits: i32 },
#[error("dockspace host position must contain finite values: {position:?}")]
InvalidHostPosition { position: [f32; 2] },
#[error(
"dockspace host size must be positive, finite, and safely truncatable to i32: {size:?}"
)]
InvalidHostSize { size: [f32; 2] },
#[error("dockspace host window name is {bytes} bytes; at most {max_bytes} bytes are supported")]
HostWindowNameTooLong { bytes: usize, max_bytes: usize },
#[error("invalid dockspace window class: {0}")]
InvalidWindowClass(#[from] WindowClassError),
#[error("dock split ratio must be finite and strictly between 0 and 1: {ratio}")]
InvalidSplitRatio { ratio: f32 },
#[error(
"dock window keys {first_key:?} and {second_key:?} resolve to the same Dear ImGui ID {id:?}"
)]
DuplicateWindowKey {
first_key: String,
second_key: String,
id: Id,
},
#[error("dock layout contains too many nodes")]
LayoutTooLarge,
#[error("docking is not enabled in ConfigFlags")]
DockingDisabled,
#[error("dockspace {root_id:?} was already submitted during this frame")]
DuplicateDockspaceSubmission { root_id: Id },
#[error("existing dock node {id:?} is not an explicit dockspace root")]
ExistingNodeIsNotDockspaceRoot { id: Id },
#[error("dockspace {root_id:?} must be submitted before any window hosted by its dock tree")]
WindowSubmittedBeforeDockspace { root_id: Id },
#[error("Dear ImGui could not create dock node {id:?}")]
NodeCreationFailed { id: Id },
#[error("Dear ImGui failed to split a docking node {direction:?} at ratio {ratio}")]
SplitFailed { direction: DockSplit, ratio: f32 },
}