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;