dear_imgui/window/
mod.rs

1#![allow(
2    clippy::cast_possible_truncation,
3    clippy::cast_sign_loss,
4    clippy::as_conversions
5)]
6use bitflags::bitflags;
7use std::f32;
8
9use crate::sys;
10use crate::{Condition, Ui};
11
12pub(crate) mod child_window;
13pub(crate) mod content_region;
14pub(crate) mod scroll;
15
16// Window-focused/hovered helpers are available via utils.rs variants.
17// Window hovered/focused flag helpers are provided by crate::utils::HoveredFlags.
18
19bitflags! {
20    /// Configuration flags for windows
21    #[repr(transparent)]
22    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23    pub struct WindowFlags: i32 {
24        /// Disable title-bar
25        const NO_TITLE_BAR = sys::ImGuiWindowFlags_NoTitleBar as i32;
26        /// Disable user resizing with the lower-right grip
27        const NO_RESIZE = sys::ImGuiWindowFlags_NoResize as i32;
28        /// Disable user moving the window
29        const NO_MOVE = sys::ImGuiWindowFlags_NoMove as i32;
30        /// Disable scrollbars (window can still scroll with mouse or programmatically)
31        const NO_SCROLLBAR = sys::ImGuiWindowFlags_NoScrollbar as i32;
32        /// Disable user vertically scrolling with mouse wheel
33        const NO_SCROLL_WITH_MOUSE = sys::ImGuiWindowFlags_NoScrollWithMouse as i32;
34        /// Disable user collapsing window by double-clicking on it
35        const NO_COLLAPSE = sys::ImGuiWindowFlags_NoCollapse as i32;
36        /// Resize every window to its content every frame
37        const ALWAYS_AUTO_RESIZE = sys::ImGuiWindowFlags_AlwaysAutoResize as i32;
38        /// Disable drawing background color (WindowBg, etc.) and outside border
39        const NO_BACKGROUND = sys::ImGuiWindowFlags_NoBackground as i32;
40        /// Never load/save settings in .ini file
41        const NO_SAVED_SETTINGS = sys::ImGuiWindowFlags_NoSavedSettings as i32;
42        /// Disable catching mouse, hovering test with pass through
43        const NO_MOUSE_INPUTS = sys::ImGuiWindowFlags_NoMouseInputs as i32;
44        /// Has a menu-bar
45        const MENU_BAR = sys::ImGuiWindowFlags_MenuBar as i32;
46        /// Allow horizontal scrollbar to appear (off by default)
47        const HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_HorizontalScrollbar as i32;
48        /// Disable taking focus when transitioning from hidden to visible state
49        const NO_FOCUS_ON_APPEARING = sys::ImGuiWindowFlags_NoFocusOnAppearing as i32;
50        /// Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)
51        const NO_BRING_TO_FRONT_ON_FOCUS = sys::ImGuiWindowFlags_NoBringToFrontOnFocus as i32;
52        /// Always show vertical scrollbar (even if ContentSize.y < Size.y)
53        const ALWAYS_VERTICAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysVerticalScrollbar as i32;
54        /// Always show horizontal scrollbar (even if ContentSize.x < Size.x)
55        const ALWAYS_HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysHorizontalScrollbar as i32;
56        /// No gamepad/keyboard navigation within the window
57        const NO_NAV_INPUTS = sys::ImGuiWindowFlags_NoNavInputs as i32;
58        /// No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB)
59        const NO_NAV_FOCUS = sys::ImGuiWindowFlags_NoNavFocus as i32;
60        /// Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
61        const UNSAVED_DOCUMENT = sys::ImGuiWindowFlags_UnsavedDocument as i32;
62        /// Disable gamepad/keyboard navigation and focusing
63        const NO_NAV = Self::NO_NAV_INPUTS.bits() | Self::NO_NAV_FOCUS.bits();
64        /// Disable all window decorations
65        const NO_DECORATION = Self::NO_TITLE_BAR.bits() | Self::NO_RESIZE.bits() | Self::NO_SCROLLBAR.bits() | Self::NO_COLLAPSE.bits();
66        /// Disable all user interactions
67        const NO_INPUTS = Self::NO_MOUSE_INPUTS.bits() | Self::NO_NAV_INPUTS.bits();
68    }
69}
70
71/// Represents a window that can be built
72pub struct Window<'ui> {
73    ui: &'ui Ui,
74    name: String,
75    flags: WindowFlags,
76    size: Option<[f32; 2]>,
77    size_condition: Condition,
78    pos: Option<[f32; 2]>,
79    pos_condition: Condition,
80    content_size: Option<[f32; 2]>,
81    content_size_condition: Condition,
82    collapsed: Option<bool>,
83    collapsed_condition: Condition,
84    focused: Option<bool>,
85    bg_alpha: Option<f32>,
86}
87
88impl<'ui> Window<'ui> {
89    /// Creates a new window builder
90    pub fn new(ui: &'ui Ui, name: impl Into<String>) -> Self {
91        Self {
92            ui,
93            name: name.into(),
94            flags: WindowFlags::empty(),
95            size: None,
96            size_condition: Condition::Always,
97            pos: None,
98            pos_condition: Condition::Always,
99            content_size: None,
100            content_size_condition: Condition::Always,
101            collapsed: None,
102            collapsed_condition: Condition::Always,
103            focused: None,
104            bg_alpha: None,
105        }
106    }
107
108    /// Sets window flags
109    pub fn flags(mut self, flags: WindowFlags) -> Self {
110        self.flags = flags;
111        self
112    }
113
114    /// Sets window size
115    pub fn size(mut self, size: [f32; 2], condition: Condition) -> Self {
116        self.size = Some(size);
117        self.size_condition = condition;
118        self
119    }
120
121    /// Sets window position
122    pub fn position(mut self, pos: [f32; 2], condition: Condition) -> Self {
123        self.pos = Some(pos);
124        self.pos_condition = condition;
125        self
126    }
127
128    /// Sets window content size
129    pub fn content_size(mut self, size: [f32; 2], condition: Condition) -> Self {
130        self.content_size = Some(size);
131        self.content_size_condition = condition;
132        self
133    }
134
135    /// Sets window collapsed state
136    pub fn collapsed(mut self, collapsed: bool, condition: Condition) -> Self {
137        self.collapsed = Some(collapsed);
138        self.collapsed_condition = condition;
139        self
140    }
141
142    /// Sets window focused state
143    pub fn focused(mut self, focused: bool) -> Self {
144        self.focused = Some(focused);
145        self
146    }
147
148    /// Sets window background alpha
149    pub fn bg_alpha(mut self, alpha: f32) -> Self {
150        self.bg_alpha = Some(alpha);
151        self
152    }
153
154    /// Builds the window and calls the provided closure
155    pub fn build<F, R>(self, f: F) -> Option<R>
156    where
157        F: FnOnce() -> R,
158    {
159        let _token = self.begin()?;
160        Some(f())
161    }
162
163    /// Begins the window and returns a token
164    fn begin(self) -> Option<WindowToken<'ui>> {
165        use std::ffi::CString;
166
167        let name_cstr = CString::new(self.name).ok()?;
168
169        // Set window properties before beginning
170        if let Some(size) = self.size {
171            unsafe {
172                let size_vec = crate::sys::ImVec2 {
173                    x: size[0],
174                    y: size[1],
175                };
176                crate::sys::igSetNextWindowSize(size_vec, self.size_condition as i32);
177            }
178        }
179
180        if let Some(pos) = self.pos {
181            unsafe {
182                let pos_vec = crate::sys::ImVec2 {
183                    x: pos[0],
184                    y: pos[1],
185                };
186                let pivot_vec = crate::sys::ImVec2 { x: 0.0, y: 0.0 };
187                crate::sys::igSetNextWindowPos(pos_vec, self.pos_condition as i32, pivot_vec);
188            }
189        }
190
191        if let Some(content_size) = self.content_size {
192            unsafe {
193                let content_size_vec = crate::sys::ImVec2 {
194                    x: content_size[0],
195                    y: content_size[1],
196                };
197                crate::sys::igSetNextWindowContentSize(content_size_vec);
198            }
199        }
200
201        if let Some(collapsed) = self.collapsed {
202            unsafe {
203                crate::sys::igSetNextWindowCollapsed(collapsed, self.collapsed_condition as i32);
204            }
205        }
206
207        if let Some(focused) = self.focused
208            && focused
209        {
210            unsafe {
211                crate::sys::igSetNextWindowFocus();
212            }
213        }
214
215        if let Some(alpha) = self.bg_alpha {
216            unsafe {
217                crate::sys::igSetNextWindowBgAlpha(alpha);
218            }
219        }
220
221        // Begin the window
222        let mut open = true;
223        let result =
224            unsafe { crate::sys::igBegin(name_cstr.as_ptr(), &mut open, self.flags.bits()) };
225
226        // IMPORTANT: According to ImGui documentation, Begin/End calls must be balanced.
227        // If Begin returns false, we need to call End immediately and return None.
228        if result && open {
229            Some(WindowToken {
230                _phantom: std::marker::PhantomData,
231            })
232        } else {
233            // If Begin returns false, call End immediately and return None
234            unsafe {
235                crate::sys::igEnd();
236            }
237            None
238        }
239    }
240}
241
242/// Token representing an active window
243pub struct WindowToken<'ui> {
244    _phantom: std::marker::PhantomData<&'ui ()>,
245}
246
247impl<'ui> Drop for WindowToken<'ui> {
248    fn drop(&mut self) {
249        unsafe {
250            crate::sys::igEnd();
251        }
252    }
253}