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)]
26// NOTE: Keep explicit `as u32` casts when using bindgen-generated flag constants.
27// The exact Rust type of `sys::ImGui*Flags_*` may vary across platforms/toolchains, while our
28// public wrapper APIs intentionally expose fixed underlying integer types.
29use crate::sys;
30use crate::{Ui, WindowFlags};
31use std::borrow::Cow;
32
33bitflags::bitflags! {
34 /// Configuration flags for child windows
35 #[repr(transparent)]
36 pub struct ChildFlags: u32 {
37 /// No flags
38 const NONE = 0;
39 /// Show an outer border and enable WindowPadding
40 const BORDERS = sys::ImGuiChildFlags_Borders as u32;
41 /// Pad with style.WindowPadding even if no border are drawn
42 const ALWAYS_USE_WINDOW_PADDING = sys::ImGuiChildFlags_AlwaysUseWindowPadding as u32;
43 /// Allow resize from right border
44 const RESIZE_X = sys::ImGuiChildFlags_ResizeX as u32;
45 /// Allow resize from bottom border
46 const RESIZE_Y = sys::ImGuiChildFlags_ResizeY as u32;
47 /// Enable auto-resizing width
48 const AUTO_RESIZE_X = sys::ImGuiChildFlags_AutoResizeX as u32;
49 /// Enable auto-resizing height
50 const AUTO_RESIZE_Y = sys::ImGuiChildFlags_AutoResizeY as u32;
51 /// Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden
52 const ALWAYS_AUTO_RESIZE = sys::ImGuiChildFlags_AlwaysAutoResize as u32;
53 /// Style the child window like a framed item
54 const FRAME_STYLE = sys::ImGuiChildFlags_FrameStyle as u32;
55 /// Share focus scope, allow gamepad/keyboard navigation to cross over parent border
56 const NAV_FLATTENED = sys::ImGuiChildFlags_NavFlattened as u32;
57 }
58}
59
60/// Represents a child window that can be built
61pub struct ChildWindow<'ui> {
62 name: Cow<'ui, str>,
63 size: [f32; 2],
64 child_flags: ChildFlags,
65 flags: WindowFlags,
66 _phantom: std::marker::PhantomData<&'ui ()>,
67}
68
69impl<'ui> ChildWindow<'ui> {
70 /// Creates a new child window builder
71 pub fn new(name: impl Into<Cow<'ui, str>>) -> Self {
72 Self {
73 name: name.into(),
74 size: [0.0, 0.0],
75 child_flags: ChildFlags::NONE,
76 flags: WindowFlags::empty(),
77 _phantom: std::marker::PhantomData,
78 }
79 }
80
81 /// Sets the size of the child window
82 pub fn size(mut self, size: [f32; 2]) -> Self {
83 self.size = size;
84 self
85 }
86
87 /// Sets whether the child window has a border
88 pub fn border(mut self, border: bool) -> Self {
89 self.child_flags.set(ChildFlags::BORDERS, border);
90 self
91 }
92
93 /// Sets child flags for the child window
94 pub fn child_flags(mut self, child_flags: ChildFlags) -> Self {
95 self.child_flags = child_flags;
96 self
97 }
98
99 /// Sets window flags for the child window
100 pub fn flags(mut self, flags: WindowFlags) -> Self {
101 self.flags = flags;
102 self
103 }
104
105 /// Builds the child window and calls the provided closure
106 pub fn build<F, R>(self, ui: &'ui Ui, f: F) -> Option<R>
107 where
108 F: FnOnce() -> R,
109 {
110 let token = self.begin(ui)?;
111 let result = f();
112 drop(token); // Explicitly drop the token to call EndChild
113 Some(result)
114 }
115
116 /// Begins the child window and returns a token
117 fn begin(self, ui: &'ui Ui) -> Option<ChildWindowToken<'ui>> {
118 let name_ptr = ui.scratch_txt(self.name);
119
120 let result = unsafe {
121 let size_vec = sys::ImVec2 {
122 x: self.size[0],
123 y: self.size[1],
124 };
125 sys::igBeginChild_Str(
126 name_ptr,
127 size_vec,
128 self.child_flags.bits() as i32,
129 self.flags.bits(),
130 )
131 };
132
133 // IMPORTANT: According to ImGui documentation, BeginChild/EndChild are inconsistent
134 // with other Begin/End functions. EndChild() must ALWAYS be called regardless of
135 // what BeginChild() returns. However, if BeginChild returns false, EndChild must
136 // be called immediately and no content should be rendered.
137 if result {
138 Some(ChildWindowToken {
139 _phantom: std::marker::PhantomData,
140 })
141 } else {
142 // If BeginChild returns false, call EndChild immediately and return None
143 unsafe {
144 sys::igEndChild();
145 }
146 None
147 }
148 }
149}
150
151/// Token representing an active child window
152pub struct ChildWindowToken<'ui> {
153 _phantom: std::marker::PhantomData<&'ui ()>,
154}
155
156impl<'ui> Drop for ChildWindowToken<'ui> {
157 fn drop(&mut self) {
158 unsafe {
159 sys::igEndChild();
160 }
161 }
162}
163
164impl Ui {
165 /// Creates a child window builder
166 pub fn child_window<'ui>(&'ui self, name: impl Into<Cow<'ui, str>>) -> ChildWindow<'ui> {
167 ChildWindow::new(name)
168 }
169}