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}