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}