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}