dear_imgui_rs/window/child_window.rs
1//! Child windows
2//!
3//! Tools for building scrollable, optionally framed child regions within a
4//! parent window. Useful for panels, property views or nested layouts.
5//!
6//! Example:
7//! ```no_run
8//! # use dear_imgui_rs::*;
9//! # let mut ctx = Context::create();
10//! # let ui = ctx.frame();
11//! ui.window("Parent").build(|| {
12//! ui.child_window("pane")
13//! .size([200.0, 120.0])
14//! .border(true)
15//! .build(&ui, || {
16//! ui.text("Inside child window");
17//! });
18//! });
19//! ```
20//!
21#![allow(
22 clippy::cast_possible_truncation,
23 clippy::cast_sign_loss,
24 clippy::as_conversions
25)]
26use crate::sys;
27use crate::{Ui, WindowFlags};
28use std::ffi::CString;
29
30bitflags::bitflags! {
31 /// Configuration flags for child windows
32 #[repr(transparent)]
33 pub struct ChildFlags: u32 {
34 /// No flags
35 const NONE = 0;
36 /// Show an outer border and enable WindowPadding
37 const BORDERS = 1 << 0;
38 /// Pad with style.WindowPadding even if no border are drawn
39 const ALWAYS_USE_WINDOW_PADDING = 1 << 1;
40 /// Allow resize from right border
41 const RESIZE_X = 1 << 2;
42 /// Allow resize from bottom border
43 const RESIZE_Y = 1 << 3;
44 /// Enable auto-resizing width
45 const AUTO_RESIZE_X = 1 << 4;
46 /// Enable auto-resizing height
47 const AUTO_RESIZE_Y = 1 << 5;
48 /// Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden
49 const ALWAYS_AUTO_RESIZE = 1 << 6;
50 /// Style the child window like a framed item
51 const FRAME_STYLE = 1 << 7;
52 /// Share focus scope, allow gamepad/keyboard navigation to cross over parent border
53 const NAV_FLATTENED = 1 << 8;
54 }
55}
56
57/// Represents a child window that can be built
58pub struct ChildWindow<'ui> {
59 name: String,
60 size: [f32; 2],
61 child_flags: ChildFlags,
62 flags: WindowFlags,
63 _phantom: std::marker::PhantomData<&'ui ()>,
64}
65
66impl<'ui> ChildWindow<'ui> {
67 /// Creates a new child window builder
68 pub fn new(name: impl Into<String>) -> Self {
69 Self {
70 name: name.into(),
71 size: [0.0, 0.0],
72 child_flags: ChildFlags::NONE,
73 flags: WindowFlags::empty(),
74 _phantom: std::marker::PhantomData,
75 }
76 }
77
78 /// Sets the size of the child window
79 pub fn size(mut self, size: [f32; 2]) -> Self {
80 self.size = size;
81 self
82 }
83
84 /// Sets whether the child window has a border
85 pub fn border(mut self, border: bool) -> Self {
86 self.child_flags.set(ChildFlags::BORDERS, border);
87 self
88 }
89
90 /// Sets child flags for the child window
91 pub fn child_flags(mut self, child_flags: ChildFlags) -> Self {
92 self.child_flags = child_flags;
93 self
94 }
95
96 /// Sets window flags for the child window
97 pub fn flags(mut self, flags: WindowFlags) -> Self {
98 self.flags = flags;
99 self
100 }
101
102 /// Builds the child window and calls the provided closure
103 pub fn build<F, R>(self, ui: &'ui Ui, f: F) -> Option<R>
104 where
105 F: FnOnce() -> R,
106 {
107 let token = self.begin(ui)?;
108 let result = f();
109 drop(token); // Explicitly drop the token to call EndChild
110 Some(result)
111 }
112
113 /// Begins the child window and returns a token
114 fn begin(self, _ui: &'ui Ui) -> Option<ChildWindowToken<'ui>> {
115 let name_cstr = CString::new(self.name).ok()?;
116
117 let result = unsafe {
118 let size_vec = sys::ImVec2 {
119 x: self.size[0],
120 y: self.size[1],
121 };
122 sys::igBeginChild_Str(
123 name_cstr.as_ptr(),
124 size_vec,
125 self.child_flags.bits() as i32,
126 self.flags.bits(),
127 )
128 };
129
130 // IMPORTANT: According to ImGui documentation, BeginChild/EndChild are inconsistent
131 // with other Begin/End functions. EndChild() must ALWAYS be called regardless of
132 // what BeginChild() returns. However, if BeginChild returns false, EndChild must
133 // be called immediately and no content should be rendered.
134 if result {
135 Some(ChildWindowToken {
136 _phantom: std::marker::PhantomData,
137 })
138 } else {
139 // If BeginChild returns false, call EndChild immediately and return None
140 unsafe {
141 sys::igEndChild();
142 }
143 None
144 }
145 }
146}
147
148/// Token representing an active child window
149pub struct ChildWindowToken<'ui> {
150 _phantom: std::marker::PhantomData<&'ui ()>,
151}
152
153impl<'ui> Drop for ChildWindowToken<'ui> {
154 fn drop(&mut self) {
155 unsafe {
156 sys::igEndChild();
157 }
158 }
159}
160
161impl Ui {
162 /// Creates a child window builder
163 pub fn child_window(&self, name: impl Into<String>) -> ChildWindow<'_> {
164 ChildWindow::new(name)
165 }
166}