dear_imgui/window/
child_window.rs

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