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;