dear_imgui_rs/
dock_space.rs

1#![allow(
2    clippy::cast_possible_truncation,
3    clippy::cast_sign_loss,
4    clippy::as_conversions
5)]
6//! Docking space functionality for Dear ImGui
7//!
8//! This module provides high-level Rust bindings for Dear ImGui's docking system,
9//! allowing you to create dockable windows and manage dock spaces.
10//!
11//! # Notes
12//!
13//! Docking is always enabled in this crate; no feature flag required.
14//!
15//! # Basic Usage
16//!
17//! ```no_run
18//! # use dear_imgui_rs::*;
19//! # let mut ctx = Context::create();
20//! # let ui = ctx.frame();
21//! // Create a dockspace over the main viewport
22//! let dockspace_id = ui.dockspace_over_main_viewport();
23//!
24//! // Dock a window to the dockspace
25//! ui.set_next_window_dock_id(dockspace_id);
26//! ui.window("Tool Window").build(|| {
27//!     ui.text("This window is docked!");
28//! });
29//! ```
30
31use crate::Id;
32use crate::sys;
33use crate::ui::Ui;
34use std::ptr;
35
36bitflags::bitflags! {
37    /// Flags for dock nodes
38    #[repr(transparent)]
39    #[derive(Debug)]
40    pub struct DockNodeFlags: i32 {
41        /// No flags
42        const NONE = sys::ImGuiDockNodeFlags_None as i32;
43        /// Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked.
44        const KEEP_ALIVE_ONLY = sys::ImGuiDockNodeFlags_KeepAliveOnly as i32;
45        /// Disable docking over the Central Node, which will be always kept empty.
46        const NO_DOCKING_OVER_CENTRAL_NODE = sys::ImGuiDockNodeFlags_NoDockingOverCentralNode as i32;
47        /// Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background.
48        const PASSTHRU_CENTRAL_NODE = sys::ImGuiDockNodeFlags_PassthruCentralNode as i32;
49        /// Disable other windows/nodes from splitting this node.
50        const NO_DOCKING_SPLIT = sys::ImGuiDockNodeFlags_NoDockingSplit as i32;
51        /// Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces.
52        const NO_RESIZE = sys::ImGuiDockNodeFlags_NoResize as i32;
53        /// Tab bar will automatically hide when there is a single window in the dock node.
54        const AUTO_HIDE_TAB_BAR = sys::ImGuiDockNodeFlags_AutoHideTabBar as i32;
55        /// Disable undocking this node.
56        const NO_UNDOCKING = sys::ImGuiDockNodeFlags_NoUndocking as i32;
57    }
58}
59
60/// Window class for docking configuration
61#[derive(Debug, Clone)]
62pub struct WindowClass {
63    /// User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others.
64    pub class_id: sys::ImGuiID,
65    /// Hint for the platform backend. -1: use default. 0: request platform backend to not parent the platform. != 0: request platform backend to create a parent<>child relationship between the platform windows.
66    pub parent_viewport_id: sys::ImGuiID,
67    /// ID of parent window for shortcut focus route evaluation
68    pub focus_route_parent_window_id: sys::ImGuiID,
69    /// Set to true to enforce single floating windows of this class always having their own docking node
70    pub docking_always_tab_bar: bool,
71    /// Set to true to allow windows of this class to be docked/merged with an unclassed window
72    pub docking_allow_unclassed: bool,
73}
74
75impl Default for WindowClass {
76    fn default() -> Self {
77        Self {
78            class_id: 0,
79            parent_viewport_id: !0, // -1 as u32
80            focus_route_parent_window_id: 0,
81            docking_always_tab_bar: false,
82            docking_allow_unclassed: true,
83        }
84    }
85}
86
87impl WindowClass {
88    /// Creates a new window class with the specified class ID
89    pub fn new(class_id: sys::ImGuiID) -> Self {
90        Self {
91            class_id,
92            ..Default::default()
93        }
94    }
95
96    /// Sets the parent viewport ID
97    pub fn parent_viewport_id(mut self, id: sys::ImGuiID) -> Self {
98        self.parent_viewport_id = id;
99        self
100    }
101
102    /// Sets the focus route parent window ID
103    pub fn focus_route_parent_window_id(mut self, id: sys::ImGuiID) -> Self {
104        self.focus_route_parent_window_id = id;
105        self
106    }
107
108    /// Enables always showing tab bar for single floating windows
109    pub fn docking_always_tab_bar(mut self, enabled: bool) -> Self {
110        self.docking_always_tab_bar = enabled;
111        self
112    }
113
114    /// Allows docking with unclassed windows
115    pub fn docking_allow_unclassed(mut self, enabled: bool) -> Self {
116        self.docking_allow_unclassed = enabled;
117        self
118    }
119
120    /// Converts to ImGui's internal representation
121    fn to_imgui(&self) -> sys::ImGuiWindowClass {
122        sys::ImGuiWindowClass {
123            ClassId: self.class_id,
124            ParentViewportId: self.parent_viewport_id,
125            FocusRouteParentWindowId: self.focus_route_parent_window_id,
126            ViewportFlagsOverrideSet: 0,
127            ViewportFlagsOverrideClear: 0,
128            TabItemFlagsOverrideSet: 0,
129            DockNodeFlagsOverrideSet: 0,
130            DockingAlwaysTabBar: self.docking_always_tab_bar,
131            DockingAllowUnclassed: self.docking_allow_unclassed,
132        }
133    }
134}
135
136/// Docking-related functionality
137impl Ui {
138    /// Creates a dockspace over the main viewport
139    ///
140    /// This is a convenience function that creates a dockspace covering the entire main viewport.
141    /// It's equivalent to calling `dock_space` with the main viewport's ID and size.
142    ///
143    /// # Parameters
144    ///
145    /// * `dockspace_id` - The ID for the dockspace (use 0 to auto-generate)
146    /// * `flags` - Dock node flags
147    ///
148    /// # Returns
149    ///
150    /// The ID of the created dockspace
151    ///
152    /// # Example
153    ///
154    /// ```no_run
155    /// # use dear_imgui_rs::*;
156    /// # let mut ctx = Context::create();
157    /// # let ui = ctx.frame();
158    /// let dockspace_id = ui.dockspace_over_main_viewport_with_flags(
159    ///     0,
160    ///     DockNodeFlags::PASSTHRU_CENTRAL_NODE
161    /// );
162    /// ```
163    #[doc(alias = "DockSpaceOverViewport")]
164    pub fn dockspace_over_main_viewport_with_flags(
165        &self,
166        dockspace_id: Id,
167        flags: DockNodeFlags,
168    ) -> Id {
169        unsafe {
170            Id::from(sys::igDockSpaceOverViewport(
171                dockspace_id.into(),
172                sys::igGetMainViewport(),
173                flags.bits(),
174                ptr::null(),
175            ))
176        }
177    }
178
179    /// Creates a dockspace over the main viewport with default settings
180    ///
181    /// This is a convenience function that creates a dockspace covering the entire main viewport
182    /// with passthrough central node enabled.
183    ///
184    /// # Returns
185    ///
186    /// The ID of the created dockspace
187    ///
188    /// # Example
189    ///
190    /// ```no_run
191    /// # use dear_imgui_rs::*;
192    /// # let mut ctx = Context::create();
193    /// # let ui = ctx.frame();
194    /// let dockspace_id = ui.dockspace_over_main_viewport();
195    /// ```
196    #[doc(alias = "DockSpaceOverViewport")]
197    pub fn dockspace_over_main_viewport(&self) -> Id {
198        self.dockspace_over_main_viewport_with_flags(
199            Id::from(0u32),
200            DockNodeFlags::PASSTHRU_CENTRAL_NODE,
201        )
202    }
203
204    /// Creates a dockspace with the specified ID, size, and flags
205    ///
206    /// # Parameters
207    ///
208    /// * `id` - The ID for the dockspace (use 0 to auto-generate)
209    /// * `size` - The size of the dockspace in pixels
210    /// * `flags` - Dock node flags
211    /// * `window_class` - Optional window class for docking configuration
212    ///
213    /// # Returns
214    ///
215    /// The ID of the created dockspace
216    ///
217    /// # Example
218    ///
219    /// ```no_run
220    /// # use dear_imgui_rs::*;
221    /// # let mut ctx = Context::create();
222    /// # let ui = ctx.frame();
223    /// let dockspace_id = ui.dock_space_with_class(
224    ///     0,
225    ///     [800.0, 600.0],
226    ///     DockNodeFlags::NO_DOCKING_SPLIT,
227    ///     Some(&WindowClass::new(1))
228    /// );
229    /// ```
230    #[doc(alias = "DockSpace")]
231    pub fn dock_space_with_class(
232        &self,
233        id: Id,
234        size: [f32; 2],
235        flags: DockNodeFlags,
236        window_class: Option<&WindowClass>,
237    ) -> Id {
238        unsafe {
239            let size_vec = sys::ImVec2 {
240                x: size[0],
241                y: size[1],
242            };
243            let window_class_ptr = if let Some(wc) = window_class {
244                let imgui_wc = wc.to_imgui();
245                &imgui_wc as *const _
246            } else {
247                ptr::null()
248            };
249            Id::from(sys::igDockSpace(
250                id.into(),
251                size_vec,
252                flags.bits(),
253                window_class_ptr,
254            ))
255        }
256    }
257
258    /// Creates a dockspace with the specified ID and size
259    ///
260    /// # Parameters
261    ///
262    /// * `id` - The ID for the dockspace (use 0 to auto-generate)
263    /// * `size` - The size of the dockspace in pixels
264    ///
265    /// # Returns
266    ///
267    /// The ID of the created dockspace
268    ///
269    /// # Example
270    ///
271    /// ```no_run
272    /// # use dear_imgui_rs::*;
273    /// # let mut ctx = Context::create();
274    /// # let ui = ctx.frame();
275    /// let dockspace_id = ui.dock_space(0, [800.0, 600.0]);
276    /// ```
277    #[doc(alias = "DockSpace")]
278    pub fn dock_space(&self, id: Id, size: [f32; 2]) -> Id {
279        self.dock_space_with_class(id, size, DockNodeFlags::NONE, None)
280    }
281
282    /// Sets the dock ID for the next window with condition
283    ///
284    /// This function must be called before creating a window to dock it to a specific dock node.
285    ///
286    /// # Parameters
287    ///
288    /// * `dock_id` - The ID of the dock node to dock the next window to
289    /// * `cond` - Condition for when to apply the docking
290    ///
291    /// # Example
292    ///
293    /// ```no_run
294    /// # use dear_imgui_rs::*;
295    /// # let mut ctx = Context::create();
296    /// # let ui = ctx.frame();
297    /// let dockspace_id = ui.dockspace_over_main_viewport();
298    /// ui.set_next_window_dock_id_with_cond(dockspace_id, Condition::FirstUseEver);
299    /// ui.window("Docked Window").build(|| {
300    ///     ui.text("This window will be docked!");
301    /// });
302    /// ```
303    #[doc(alias = "SetNextWindowDockID")]
304    pub fn set_next_window_dock_id_with_cond(&self, dock_id: Id, cond: crate::Condition) {
305        unsafe {
306            sys::igSetNextWindowDockID(dock_id.into(), cond as i32);
307        }
308    }
309
310    /// Sets the dock ID for the next window
311    ///
312    /// This function must be called before creating a window to dock it to a specific dock node.
313    /// Uses `Condition::Always` by default.
314    ///
315    /// # Parameters
316    ///
317    /// * `dock_id` - The ID of the dock node to dock the next window to
318    ///
319    /// # Example
320    ///
321    /// ```no_run
322    /// # use dear_imgui_rs::*;
323    /// # let mut ctx = Context::create();
324    /// # let ui = ctx.frame();
325    /// let dockspace_id = ui.dockspace_over_main_viewport();
326    /// ui.set_next_window_dock_id(dockspace_id);
327    /// ui.window("Docked Window").build(|| {
328    ///     ui.text("This window will be docked!");
329    /// });
330    /// ```
331    #[doc(alias = "SetNextWindowDockID")]
332    pub fn set_next_window_dock_id(&self, dock_id: Id) {
333        self.set_next_window_dock_id_with_cond(dock_id, crate::Condition::Always)
334    }
335
336    /// Sets the window class for the next window
337    ///
338    /// This function must be called before creating a window to apply the window class configuration.
339    ///
340    /// # Parameters
341    ///
342    /// * `window_class` - The window class configuration
343    ///
344    /// # Example
345    ///
346    /// ```no_run
347    /// # use dear_imgui_rs::*;
348    /// # let mut ctx = Context::create();
349    /// # let ui = ctx.frame();
350    /// let window_class = WindowClass::new(1).docking_always_tab_bar(true);
351    /// ui.set_next_window_class(&window_class);
352    /// ui.window("Classed Window").build(|| {
353    ///     ui.text("This window has a custom class!");
354    /// });
355    /// ```
356    #[doc(alias = "SetNextWindowClass")]
357    pub fn set_next_window_class(&self, window_class: &WindowClass) {
358        unsafe {
359            let imgui_wc = window_class.to_imgui();
360            sys::igSetNextWindowClass(&imgui_wc as *const _);
361        }
362    }
363
364    /// Gets the dock ID of the current window
365    ///
366    /// # Returns
367    ///
368    /// The dock ID of the current window, or 0 if the window is not docked
369    ///
370    /// # Example
371    ///
372    /// ```no_run
373    /// # use dear_imgui_rs::*;
374    /// # let mut ctx = Context::create();
375    /// # let ui = ctx.frame();
376    /// ui.window("My Window").build(|| {
377    ///     let dock_id = ui.get_window_dock_id();
378    ///     if dock_id != 0 {
379    ///         ui.text(format!("This window is docked with ID: {}", dock_id));
380    ///     } else {
381    ///         ui.text("This window is not docked");
382    ///     }
383    /// });
384    /// ```
385    #[doc(alias = "GetWindowDockID")]
386    pub fn get_window_dock_id(&self) -> Id {
387        unsafe { Id::from(sys::igGetWindowDockID()) }
388    }
389
390    /// Checks if the current window is docked
391    ///
392    /// # Returns
393    ///
394    /// `true` if the current window is docked, `false` otherwise
395    ///
396    /// # Example
397    ///
398    /// ```no_run
399    /// # use dear_imgui_rs::*;
400    /// # let mut ctx = Context::create();
401    /// # let ui = ctx.frame();
402    /// ui.window("My Window").build(|| {
403    ///     if ui.is_window_docked() {
404    ///         ui.text("This window is docked!");
405    ///     } else {
406    ///         ui.text("This window is floating");
407    ///     }
408    /// });
409    /// ```
410    #[doc(alias = "IsWindowDocked")]
411    pub fn is_window_docked(&self) -> bool {
412        unsafe { sys::igIsWindowDocked() }
413    }
414}
415
416impl crate::Condition {
417    /// Converts the condition to ImGui's internal representation
418    pub(crate) fn as_imgui_cond(self) -> sys::ImGuiCond {
419        self as i32
420    }
421}
422
423// Re-export DockNodeFlags for convenience
424pub use DockNodeFlags as DockFlags;