dear_imgui/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::*;
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::*;
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::*;
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::*;
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::*;
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::*;
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::*;
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::*;
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::*;
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::*;
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;