Skip to main content

dear_imgui_rs/dock_space/
ui.rs

1use super::flags::{DockNodeFlags, validate_dock_node_flags};
2use super::validation::{
3    assert_docking_available, assert_dockspace_has_no_active_content,
4    assert_dockspace_host_name_supported, assert_dockspace_size,
5    assert_existing_dockspace_node_is_root, assert_nonzero_id, claim_dockspace_submission,
6    current_window_skips_items, main_viewport_dockspace_id,
7};
8use super::window_class::WindowClass;
9use crate::ui::Ui;
10use crate::{Id, sys};
11use std::ptr;
12
13/// Docking-related functionality
14impl Ui {
15    /// Configure and submit a dockspace through the canonical builder.
16    pub fn dockspace(&self) -> crate::DockspaceBuilder<'_, 'static> {
17        crate::DockspaceBuilder::new(self)
18    }
19
20    /// Submit Dear ImGui's low-level dockspace-over-viewport operation for the main viewport.
21    ///
22    /// This creates Dear ImGui's hidden main-viewport host window, applies the viewport work
23    /// rectangle and platform ownership, and submits a dockspace within that host.
24    /// Submit it before every window that can be hosted by this dockspace. A
25    /// `KEEP_ALIVE_ONLY` submission may be made later because it does not create a visible host.
26    /// Without an earlier submission, Dear ImGui may already undock a window when that window is
27    /// begun, before this method can diagnose the ordering error.
28    ///
29    /// # Parameters
30    ///
31    /// * `dockspace_id` - The ID for the dockspace (use 0 to auto-generate)
32    /// * `flags` - Dock node flags
33    ///
34    /// # Returns
35    ///
36    /// The ID of the created dockspace
37    ///
38    /// # Panics
39    ///
40    /// Panics when docking was not enabled before the first frame, when the effective dockspace
41    /// ID names a child of another dock tree, when the dockspace was already submitted without
42    /// `KEEP_ALIVE_ONLY` during this frame, or when a hosted window was submitted before a visible
43    /// dockspace submission while that window is still attached. `KEEP_ALIVE_ONLY` remains valid
44    /// after hosted windows.
45    ///
46    /// # Example
47    ///
48    /// ```no_run
49    /// # use dear_imgui_rs::*;
50    /// # let mut ctx = Context::create();
51    /// # let ui = ctx.frame();
52    /// let dockspace_id = ui.dock_space_over_main_viewport_raw(
53    ///     0.into(),
54    ///     DockNodeFlags::PASSTHRU_CENTRAL_NODE
55    /// );
56    /// ```
57    #[doc(alias = "DockSpaceOverViewport")]
58    pub fn dock_space_over_main_viewport_raw(&self, dockspace_id: Id, flags: DockNodeFlags) -> Id {
59        const CALLER: &str = "Ui::dock_space_over_main_viewport_raw()";
60        validate_dock_node_flags(CALLER, flags);
61        self.run_with_bound_context(|| {
62            assert_docking_available(CALLER);
63            let requested = (dockspace_id.raw() != 0).then_some(dockspace_id);
64            let effective_id = main_viewport_dockspace_id(CALLER, requested);
65            assert_existing_dockspace_node_is_root(CALLER, effective_id);
66            let claim = claim_dockspace_submission(self, CALLER, effective_id, flags, false)
67                .unwrap_or_else(|_| {
68                    panic!("{CALLER} cannot submit dockspace {effective_id:?} twice in one frame")
69                });
70            if !flags.contains(DockNodeFlags::KEEP_ALIVE_ONLY) {
71                assert_dockspace_has_no_active_content(CALLER, effective_id);
72            }
73            let submitted = unsafe {
74                Id::from(sys::igDockSpaceOverViewport(
75                    effective_id.into(),
76                    sys::igGetMainViewport(),
77                    flags.bits(),
78                    ptr::null(),
79                ))
80            };
81            if let Some(claim) = claim {
82                claim.commit();
83            }
84            assert_eq!(
85                submitted, effective_id,
86                "{CALLER} native submission returned an unexpected dockspace ID"
87            );
88            submitted
89        })
90    }
91
92    /// Submit Dear ImGui's low-level dockspace operation in the current window.
93    ///
94    /// Submit it before every window that can be hosted by this dockspace. A
95    /// `KEEP_ALIVE_ONLY` submission may be made later because it does not create a visible host.
96    /// Without an earlier submission, Dear ImGui may already undock a window when that window is
97    /// begun, before this method can diagnose the ordering error.
98    ///
99    /// # Parameters
100    ///
101    /// * `id` - The non-zero ID for the dockspace. Use [`Ui::get_id`] to create one.
102    /// * `size` - The size of the dockspace in pixels
103    /// * `flags` - Dock node flags
104    /// * `window_class` - Optional window class for docking configuration
105    ///
106    /// # Returns
107    ///
108    /// The ID of the created dockspace
109    ///
110    /// # Panics
111    ///
112    /// Panics when docking was not enabled before the first frame, when `id` names a child of
113    /// another dock tree, when `id` was already submitted without `KEEP_ALIVE_ONLY` during this
114    /// frame, or when a hosted window was submitted before a visible dockspace submission while
115    /// that window is still attached. `KEEP_ALIVE_ONLY` remains valid after hosted windows.
116    ///
117    /// # Example
118    ///
119    /// ```no_run
120    /// # use dear_imgui_rs::*;
121    /// # let mut ctx = Context::create();
122    /// # let ui = ctx.frame();
123    /// let dockspace_id = ui.get_id("MyDockspace");
124    /// let dockspace_id = ui.dock_space_raw(
125    ///     dockspace_id,
126    ///     [800.0, 600.0],
127    ///     DockNodeFlags::NO_DOCKING_SPLIT,
128    ///     Some(&WindowClass::new(Id::from(1u32)))
129    /// );
130    /// ```
131    #[doc(alias = "DockSpace")]
132    pub fn dock_space_raw(
133        &self,
134        id: Id,
135        size: [f32; 2],
136        flags: DockNodeFlags,
137        window_class: Option<&WindowClass>,
138    ) -> Id {
139        const CALLER: &str = "Ui::dock_space_raw()";
140        validate_dock_node_flags(CALLER, flags);
141        assert_nonzero_id(CALLER, "id", id);
142        assert_dockspace_size(CALLER, "size", size);
143        let size_vec = sys::ImVec2 {
144            x: size[0],
145            y: size[1],
146        };
147        let imgui_window_class = window_class.map(|class| class.to_imgui(CALLER));
148        let window_class_ptr = imgui_window_class
149            .as_ref()
150            .map_or(ptr::null(), |wc| wc as *const _);
151        self.run_with_bound_context(|| {
152            assert_dockspace_host_name_supported(CALLER);
153            assert_existing_dockspace_node_is_root(CALLER, id);
154            let host_skipped = current_window_skips_items(CALLER);
155            let claim =
156                claim_dockspace_submission(self, CALLER, id, flags, true).unwrap_or_else(|_| {
157                    panic!("{CALLER} cannot submit dockspace {id:?} twice in one frame")
158                });
159            if !flags.contains(DockNodeFlags::KEEP_ALIVE_ONLY) && !host_skipped {
160                assert_dockspace_has_no_active_content(CALLER, id);
161            }
162            let submitted = unsafe {
163                Id::from(sys::igDockSpace(
164                    id.into(),
165                    size_vec,
166                    flags.bits(),
167                    window_class_ptr,
168                ))
169            };
170            if let Some(claim) = claim {
171                claim.commit();
172            }
173            assert_eq!(
174                submitted, id,
175                "{CALLER} native submission returned an unexpected dockspace ID"
176            );
177            submitted
178        })
179    }
180
181    /// Sets the dock ID for the next window with condition
182    ///
183    /// This function must be called before creating a window to dock it to a specific dock node.
184    ///
185    /// # Panics
186    ///
187    /// Panics when docking was not enabled before the first frame.
188    ///
189    /// # Parameters
190    ///
191    /// * `dock_id` - The ID of the dock node to dock the next window to
192    /// * `cond` - Condition for when to apply the docking
193    ///
194    /// # Example
195    ///
196    /// ```no_run
197    /// # use dear_imgui_rs::*;
198    /// # let mut ctx = Context::create();
199    /// # let ui = ctx.frame();
200    /// let dockspace_id = ui.dockspace().build()?;
201    /// ui.set_next_window_dock_id_with_cond(dockspace_id, Condition::FirstUseEver);
202    /// ui.window("Docked Window").build(|| {
203    ///     ui.text("This window will be docked!");
204    /// });
205    /// # Ok::<(), DockspaceError>(())
206    /// ```
207    #[doc(alias = "SetNextWindowDockID")]
208    pub fn set_next_window_dock_id_with_cond(&self, dock_id: Id, cond: crate::Condition) {
209        const CALLER: &str = "Ui::set_next_window_dock_id_with_cond()";
210        self.run_with_bound_context(|| {
211            assert_docking_available(CALLER);
212            unsafe {
213                sys::igSetNextWindowDockID(dock_id.into(), cond as i32);
214            }
215        });
216    }
217
218    /// Sets the dock ID for the next window
219    ///
220    /// This function must be called before creating a window to dock it to a specific dock node.
221    /// Uses `Condition::Always` by default.
222    ///
223    /// # Panics
224    ///
225    /// Panics when docking was not enabled before the first frame.
226    ///
227    /// # Parameters
228    ///
229    /// * `dock_id` - The ID of the dock node to dock the next window to
230    ///
231    /// # Example
232    ///
233    /// ```no_run
234    /// # use dear_imgui_rs::*;
235    /// # let mut ctx = Context::create();
236    /// # let ui = ctx.frame();
237    /// let dockspace_id = ui.dockspace().build()?;
238    /// ui.set_next_window_dock_id(dockspace_id);
239    /// ui.window("Docked Window").build(|| {
240    ///     ui.text("This window will be docked!");
241    /// });
242    /// # Ok::<(), DockspaceError>(())
243    /// ```
244    #[doc(alias = "SetNextWindowDockID")]
245    pub fn set_next_window_dock_id(&self, dock_id: Id) {
246        self.set_next_window_dock_id_with_cond(dock_id, crate::Condition::Always)
247    }
248
249    /// Sets the window class for the next window
250    ///
251    /// This function must be called before creating a window to apply the window class configuration.
252    ///
253    /// # Parameters
254    ///
255    /// * `window_class` - The window class configuration
256    ///
257    /// # Example
258    ///
259    /// ```no_run
260    /// # use dear_imgui_rs::*;
261    /// # let mut ctx = Context::create();
262    /// # let ui = ctx.frame();
263    /// let window_class = WindowClass::new(Id::from(1u32)).docking_always_tab_bar(true);
264    /// ui.set_next_window_class(&window_class);
265    /// ui.window("Classed Window").build(|| {
266    ///     ui.text("This window has a custom class!");
267    /// });
268    /// ```
269    #[doc(alias = "SetNextWindowClass")]
270    pub fn set_next_window_class(&self, window_class: &WindowClass) {
271        let imgui_wc = window_class.to_imgui("Ui::set_next_window_class()");
272        self.run_with_bound_context(|| unsafe {
273            sys::igSetNextWindowClass(&imgui_wc as *const _);
274        });
275    }
276
277    /// Gets the dock ID of the current window
278    ///
279    /// # Returns
280    ///
281    /// The dock ID of the current window, or 0 if the window is not docked
282    ///
283    /// # Example
284    ///
285    /// ```no_run
286    /// # use dear_imgui_rs::*;
287    /// # let mut ctx = Context::create();
288    /// # let ui = ctx.frame();
289    /// ui.window("My Window").build(|| {
290    ///     let dock_id = ui.get_window_dock_id();
291    ///     if dock_id != 0.into() {
292    ///         ui.text(format!("This window is docked with ID: {}", dock_id.raw()));
293    ///     } else {
294    ///         ui.text("This window is not docked");
295    ///     }
296    /// });
297    /// ```
298    #[doc(alias = "GetWindowDockID")]
299    pub fn get_window_dock_id(&self) -> Id {
300        self.run_with_bound_context(|| unsafe { Id::from(sys::igGetWindowDockID()) })
301    }
302
303    /// Checks if the current window is docked
304    ///
305    /// # Returns
306    ///
307    /// `true` if the current window is docked, `false` otherwise
308    ///
309    /// # Example
310    ///
311    /// ```no_run
312    /// # use dear_imgui_rs::*;
313    /// # let mut ctx = Context::create();
314    /// # let ui = ctx.frame();
315    /// ui.window("My Window").build(|| {
316    ///     if ui.is_window_docked() {
317    ///         ui.text("This window is docked!");
318    ///     } else {
319    ///         ui.text("This window is floating");
320    ///     }
321    /// });
322    /// ```
323    #[doc(alias = "IsWindowDocked")]
324    pub fn is_window_docked(&self) -> bool {
325        self.run_with_bound_context(|| unsafe { sys::igIsWindowDocked() })
326    }
327}