dear_imgui_rs/window/
mod.rs

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