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