Skip to main content

dear_imgui_rs/
dock_space.rs

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