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_finite_vec2, assert_nonzero_id, claim_dockspace_submission,
4    main_viewport_dockspace_host_name, window_skips_items,
5};
6use super::window_class::WindowClass;
7use crate::ui::Ui;
8use crate::{
9    DockLayout, DockLayoutApply, DockLayoutError, DockspaceTarget, Id,
10    dock_layout::{DockspaceSubmission, submit_and_apply},
11    sys,
12};
13use std::ptr;
14
15fn resolve_main_viewport_dockspace_id(requested: Id, host_name: &std::ffi::CStr) -> Id {
16    if requested.raw() != 0 {
17        return requested;
18    }
19
20    unsafe {
21        let host_id = sys::igGetIDWithSeed_Str(host_name.as_ptr(), ptr::null(), 0);
22        Id::from(sys::igGetIDWithSeed_Str(
23            c"DockSpace".as_ptr(),
24            ptr::null(),
25            host_id,
26        ))
27    }
28}
29
30/// Docking-related functionality
31impl Ui {
32    /// Submit a dockspace at the current cursor and apply a complete declarative layout.
33    ///
34    /// The target's initial position is ignored for this submission mode; the dock node follows
35    /// the host window's current cursor position. Initial size, flags, and window class still come
36    /// from the target.
37    pub fn dock_space_with_layout(
38        &self,
39        target: &DockspaceTarget,
40        layout: &DockLayout,
41        apply: DockLayoutApply,
42    ) -> Result<Id, DockLayoutError> {
43        submit_and_apply(
44            self,
45            target,
46            layout,
47            apply,
48            DockspaceSubmission::CurrentWindow,
49        )
50    }
51
52    /// Submit a dockspace over the main viewport and apply a complete declarative layout.
53    pub fn dockspace_over_main_viewport_with_layout(
54        &self,
55        target: &DockspaceTarget,
56        layout: &DockLayout,
57        apply: DockLayoutApply,
58    ) -> Result<Id, DockLayoutError> {
59        submit_and_apply(
60            self,
61            target,
62            layout,
63            apply,
64            DockspaceSubmission::MainViewport,
65        )
66    }
67
68    /// Creates a dockspace over the main viewport
69    ///
70    /// This is a convenience function that creates a dockspace covering the entire main viewport.
71    /// It's equivalent to calling `dock_space` with the main viewport's ID and size.
72    ///
73    /// # Parameters
74    ///
75    /// * `dockspace_id` - The ID for the dockspace (use 0 to auto-generate)
76    /// * `flags` - Dock node flags
77    ///
78    /// # Returns
79    ///
80    /// The ID of the created dockspace
81    ///
82    /// # Panics
83    ///
84    /// Panics when docking was not enabled before the first frame, or when the effective
85    /// dockspace ID was already submitted without `KEEP_ALIVE_ONLY` during this frame.
86    ///
87    /// # Example
88    ///
89    /// ```no_run
90    /// # use dear_imgui_rs::*;
91    /// # let mut ctx = Context::create();
92    /// # let ui = ctx.frame();
93    /// let dockspace_id = ui.dockspace_over_main_viewport_with_flags(
94    ///     0.into(),
95    ///     DockNodeFlags::PASSTHRU_CENTRAL_NODE
96    /// );
97    /// ```
98    #[doc(alias = "DockSpaceOverViewport")]
99    pub fn dockspace_over_main_viewport_with_flags(
100        &self,
101        dockspace_id: Id,
102        flags: DockNodeFlags,
103    ) -> Id {
104        const CALLER: &str = "Ui::dockspace_over_main_viewport_with_flags()";
105        validate_dock_node_flags(CALLER, flags);
106        self.run_with_bound_context(|| {
107            assert_docking_available(CALLER);
108            let host_name = main_viewport_dockspace_host_name(CALLER);
109            let effective_id = resolve_main_viewport_dockspace_id(dockspace_id, &host_name);
110            let claim = claim_dockspace_submission(self, CALLER, effective_id, flags, false)
111                .unwrap_or_else(|_| {
112                    panic!("{CALLER} cannot submit dockspace {effective_id:?} twice in one frame")
113                });
114            let submitted = unsafe {
115                Id::from(sys::igDockSpaceOverViewport(
116                    dockspace_id.into(),
117                    sys::igGetMainViewport(),
118                    flags.bits(),
119                    ptr::null(),
120                ))
121            };
122            if let Some(claim) = claim {
123                if window_skips_items(&host_name) {
124                    drop(claim);
125                } else {
126                    claim.commit();
127                }
128            }
129            assert_eq!(
130                submitted, effective_id,
131                "{CALLER} native auto-generated dockspace ID changed unexpectedly"
132            );
133            submitted
134        })
135    }
136
137    /// Creates a dockspace over the main viewport with default settings
138    ///
139    /// This is a convenience function that creates a dockspace covering the entire main viewport
140    /// with passthrough central node enabled.
141    ///
142    /// # Returns
143    ///
144    /// The ID of the created dockspace
145    ///
146    /// # Panics
147    ///
148    /// Panics when docking was not enabled before the first frame, or when the effective
149    /// dockspace ID was already submitted during this frame.
150    ///
151    /// # Example
152    ///
153    /// ```no_run
154    /// # use dear_imgui_rs::*;
155    /// # let mut ctx = Context::create();
156    /// # let ui = ctx.frame();
157    /// let dockspace_id = ui.dockspace_over_main_viewport();
158    /// ```
159    #[doc(alias = "DockSpaceOverViewport")]
160    pub fn dockspace_over_main_viewport(&self) -> Id {
161        self.dockspace_over_main_viewport_with_flags(
162            Id::from(0u32),
163            DockNodeFlags::PASSTHRU_CENTRAL_NODE,
164        )
165    }
166
167    /// Creates a dockspace with the specified ID, size, and flags
168    ///
169    /// # Parameters
170    ///
171    /// * `id` - The non-zero ID for the dockspace. Use [`Ui::get_id`] to create one.
172    /// * `size` - The size of the dockspace in pixels
173    /// * `flags` - Dock node flags
174    /// * `window_class` - Optional window class for docking configuration
175    ///
176    /// # Returns
177    ///
178    /// The ID of the created dockspace
179    ///
180    /// # Panics
181    ///
182    /// Panics when docking was not enabled before the first frame, or when `id` was already
183    /// submitted without `KEEP_ALIVE_ONLY` during this frame.
184    ///
185    /// # Example
186    ///
187    /// ```no_run
188    /// # use dear_imgui_rs::*;
189    /// # let mut ctx = Context::create();
190    /// # let ui = ctx.frame();
191    /// let dockspace_id = ui.get_id("MyDockspace");
192    /// let dockspace_id = ui.dock_space_with_class(
193    ///     dockspace_id,
194    ///     [800.0, 600.0],
195    ///     DockNodeFlags::NO_DOCKING_SPLIT,
196    ///     Some(&WindowClass::new(Id::from(1u32)))
197    /// );
198    /// ```
199    #[doc(alias = "DockSpace")]
200    pub fn dock_space_with_class(
201        &self,
202        id: Id,
203        size: [f32; 2],
204        flags: DockNodeFlags,
205        window_class: Option<&WindowClass>,
206    ) -> Id {
207        const CALLER: &str = "Ui::dock_space_with_class()";
208        validate_dock_node_flags(CALLER, flags);
209        assert_nonzero_id(CALLER, "id", id);
210        assert_finite_vec2(CALLER, "size", size);
211        let size_vec = sys::ImVec2 {
212            x: size[0],
213            y: size[1],
214        };
215        let imgui_window_class = window_class.map(|class| class.to_imgui(CALLER));
216        let window_class_ptr = imgui_window_class
217            .as_ref()
218            .map_or(ptr::null(), |wc| wc as *const _);
219        self.run_with_bound_context(|| {
220            let claim =
221                claim_dockspace_submission(self, CALLER, id, flags, true).unwrap_or_else(|_| {
222                    panic!("{CALLER} cannot submit dockspace {id:?} twice in one frame")
223                });
224            let submitted = unsafe {
225                Id::from(sys::igDockSpace(
226                    id.into(),
227                    size_vec,
228                    flags.bits(),
229                    window_class_ptr,
230                ))
231            };
232            if let Some(claim) = claim {
233                claim.commit();
234            }
235            assert_eq!(
236                submitted, id,
237                "{CALLER} native submission returned an unexpected dockspace ID"
238            );
239            submitted
240        })
241    }
242
243    /// Creates a dockspace with the specified ID and size
244    ///
245    /// # Parameters
246    ///
247    /// * `id` - The non-zero ID for the dockspace
248    /// * `size` - The size of the dockspace in pixels
249    ///
250    /// # Returns
251    ///
252    /// The ID of the created dockspace
253    ///
254    /// # Example
255    ///
256    /// ```no_run
257    /// # use dear_imgui_rs::*;
258    /// # let mut ctx = Context::create();
259    /// # let ui = ctx.frame();
260    /// let dockspace_id = ui.get_id("MyDockspace");
261    /// let dockspace_id = ui.dock_space(dockspace_id, [800.0, 600.0]);
262    /// ```
263    #[doc(alias = "DockSpace")]
264    pub fn dock_space(&self, id: Id, size: [f32; 2]) -> Id {
265        self.dock_space_with_class(id, size, DockNodeFlags::NONE, None)
266    }
267
268    /// Sets the dock ID for the next window with condition
269    ///
270    /// This function must be called before creating a window to dock it to a specific dock node.
271    ///
272    /// # Panics
273    ///
274    /// Panics when docking was not enabled before the first frame.
275    ///
276    /// # Parameters
277    ///
278    /// * `dock_id` - The ID of the dock node to dock the next window to
279    /// * `cond` - Condition for when to apply the docking
280    ///
281    /// # Example
282    ///
283    /// ```no_run
284    /// # use dear_imgui_rs::*;
285    /// # let mut ctx = Context::create();
286    /// # let ui = ctx.frame();
287    /// let dockspace_id = ui.dockspace_over_main_viewport();
288    /// ui.set_next_window_dock_id_with_cond(dockspace_id, Condition::FirstUseEver);
289    /// ui.window("Docked Window").build(|| {
290    ///     ui.text("This window will be docked!");
291    /// });
292    /// ```
293    #[doc(alias = "SetNextWindowDockID")]
294    pub fn set_next_window_dock_id_with_cond(&self, dock_id: Id, cond: crate::Condition) {
295        const CALLER: &str = "Ui::set_next_window_dock_id_with_cond()";
296        self.run_with_bound_context(|| {
297            assert_docking_available(CALLER);
298            unsafe {
299                sys::igSetNextWindowDockID(dock_id.into(), cond as i32);
300            }
301        });
302    }
303
304    /// Sets the dock ID for the next window
305    ///
306    /// This function must be called before creating a window to dock it to a specific dock node.
307    /// Uses `Condition::Always` by default.
308    ///
309    /// # Panics
310    ///
311    /// Panics when docking was not enabled before the first frame.
312    ///
313    /// # Parameters
314    ///
315    /// * `dock_id` - The ID of the dock node to dock the next window to
316    ///
317    /// # Example
318    ///
319    /// ```no_run
320    /// # use dear_imgui_rs::*;
321    /// # let mut ctx = Context::create();
322    /// # let ui = ctx.frame();
323    /// let dockspace_id = ui.dockspace_over_main_viewport();
324    /// ui.set_next_window_dock_id(dockspace_id);
325    /// ui.window("Docked Window").build(|| {
326    ///     ui.text("This window will be docked!");
327    /// });
328    /// ```
329    #[doc(alias = "SetNextWindowDockID")]
330    pub fn set_next_window_dock_id(&self, dock_id: Id) {
331        self.set_next_window_dock_id_with_cond(dock_id, crate::Condition::Always)
332    }
333
334    /// Sets the window class for the next window
335    ///
336    /// This function must be called before creating a window to apply the window class configuration.
337    ///
338    /// # Parameters
339    ///
340    /// * `window_class` - The window class configuration
341    ///
342    /// # Example
343    ///
344    /// ```no_run
345    /// # use dear_imgui_rs::*;
346    /// # let mut ctx = Context::create();
347    /// # let ui = ctx.frame();
348    /// let window_class = WindowClass::new(Id::from(1u32)).docking_always_tab_bar(true);
349    /// ui.set_next_window_class(&window_class);
350    /// ui.window("Classed Window").build(|| {
351    ///     ui.text("This window has a custom class!");
352    /// });
353    /// ```
354    #[doc(alias = "SetNextWindowClass")]
355    pub fn set_next_window_class(&self, window_class: &WindowClass) {
356        let imgui_wc = window_class.to_imgui("Ui::set_next_window_class()");
357        self.run_with_bound_context(|| unsafe {
358            sys::igSetNextWindowClass(&imgui_wc as *const _);
359        });
360    }
361
362    /// Gets the dock ID of the current window
363    ///
364    /// # Returns
365    ///
366    /// The dock ID of the current window, or 0 if the window is not docked
367    ///
368    /// # Example
369    ///
370    /// ```no_run
371    /// # use dear_imgui_rs::*;
372    /// # let mut ctx = Context::create();
373    /// # let ui = ctx.frame();
374    /// ui.window("My Window").build(|| {
375    ///     let dock_id = ui.get_window_dock_id();
376    ///     if dock_id != 0.into() {
377    ///         ui.text(format!("This window is docked with ID: {}", dock_id.raw()));
378    ///     } else {
379    ///         ui.text("This window is not docked");
380    ///     }
381    /// });
382    /// ```
383    #[doc(alias = "GetWindowDockID")]
384    pub fn get_window_dock_id(&self) -> Id {
385        self.run_with_bound_context(|| unsafe { Id::from(sys::igGetWindowDockID()) })
386    }
387
388    /// Checks if the current window is docked
389    ///
390    /// # Returns
391    ///
392    /// `true` if the current window is docked, `false` otherwise
393    ///
394    /// # Example
395    ///
396    /// ```no_run
397    /// # use dear_imgui_rs::*;
398    /// # let mut ctx = Context::create();
399    /// # let ui = ctx.frame();
400    /// ui.window("My Window").build(|| {
401    ///     if ui.is_window_docked() {
402    ///         ui.text("This window is docked!");
403    ///     } else {
404    ///         ui.text("This window is floating");
405    ///     }
406    /// });
407    /// ```
408    #[doc(alias = "IsWindowDocked")]
409    pub fn is_window_docked(&self) -> bool {
410        self.run_with_bound_context(|| unsafe { sys::igIsWindowDocked() })
411    }
412}