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#[cfg(feature = "serde")]
51use serde::{Deserialize, Serialize};
52
53pub(crate) mod child_window;
54pub(crate) mod content_region;
55pub(crate) mod scroll;
56
57// Window-focused/hovered helpers are available via utils.rs variants.
58// Window hovered/focused flag helpers are provided by crate::utils::HoveredFlags.
59
60bitflags! {
61    /// Configuration flags for windows
62    #[repr(transparent)]
63    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64    pub struct WindowFlags: i32 {
65        /// Disable title-bar
66        const NO_TITLE_BAR = sys::ImGuiWindowFlags_NoTitleBar as i32;
67        /// Disable user resizing with the lower-right grip
68        const NO_RESIZE = sys::ImGuiWindowFlags_NoResize as i32;
69        /// Disable user moving the window
70        const NO_MOVE = sys::ImGuiWindowFlags_NoMove as i32;
71        /// Disable scrollbars (window can still scroll with mouse or programmatically)
72        const NO_SCROLLBAR = sys::ImGuiWindowFlags_NoScrollbar as i32;
73        /// Disable user vertically scrolling with mouse wheel
74        const NO_SCROLL_WITH_MOUSE = sys::ImGuiWindowFlags_NoScrollWithMouse as i32;
75        /// Disable user collapsing window by double-clicking on it
76        const NO_COLLAPSE = sys::ImGuiWindowFlags_NoCollapse as i32;
77        /// Resize every window to its content every frame
78        const ALWAYS_AUTO_RESIZE = sys::ImGuiWindowFlags_AlwaysAutoResize as i32;
79        /// Disable drawing background color (WindowBg, etc.) and outside border
80        const NO_BACKGROUND = sys::ImGuiWindowFlags_NoBackground as i32;
81        /// Never load/save settings in .ini file
82        const NO_SAVED_SETTINGS = sys::ImGuiWindowFlags_NoSavedSettings as i32;
83        /// Disable catching mouse, hovering test with pass through
84        const NO_MOUSE_INPUTS = sys::ImGuiWindowFlags_NoMouseInputs as i32;
85        /// Has a menu-bar
86        const MENU_BAR = sys::ImGuiWindowFlags_MenuBar as i32;
87        /// Allow horizontal scrollbar to appear (off by default)
88        const HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_HorizontalScrollbar as i32;
89        /// Disable taking focus when transitioning from hidden to visible state
90        const NO_FOCUS_ON_APPEARING = sys::ImGuiWindowFlags_NoFocusOnAppearing as i32;
91        /// Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)
92        const NO_BRING_TO_FRONT_ON_FOCUS = sys::ImGuiWindowFlags_NoBringToFrontOnFocus as i32;
93        /// Always show vertical scrollbar (even if ContentSize.y < Size.y)
94        const ALWAYS_VERTICAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysVerticalScrollbar as i32;
95        /// Always show horizontal scrollbar (even if ContentSize.x < Size.x)
96        const ALWAYS_HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysHorizontalScrollbar as i32;
97        /// No gamepad/keyboard navigation within the window
98        const NO_NAV_INPUTS = sys::ImGuiWindowFlags_NoNavInputs as i32;
99        /// No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB)
100        const NO_NAV_FOCUS = sys::ImGuiWindowFlags_NoNavFocus as i32;
101        /// 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.
102        const UNSAVED_DOCUMENT = sys::ImGuiWindowFlags_UnsavedDocument as i32;
103        // Docking related flags
104        /// 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)
105        const NO_DOCKING = sys::ImGuiWindowFlags_NoDocking as i32;
106        /// Indicate this window is a dock node host. Generally set by imgui internally when hosting a DockSpace.
107        const DOCK_NODE_HOST = sys::ImGuiWindowFlags_DockNodeHost as i32;
108        /// Disable gamepad/keyboard navigation and focusing
109        const NO_NAV = Self::NO_NAV_INPUTS.bits() | Self::NO_NAV_FOCUS.bits();
110        /// Disable all window decorations
111        const NO_DECORATION = Self::NO_TITLE_BAR.bits() | Self::NO_RESIZE.bits() | Self::NO_SCROLLBAR.bits() | Self::NO_COLLAPSE.bits();
112        /// Disable all user interactions
113        const NO_INPUTS = Self::NO_MOUSE_INPUTS.bits() | Self::NO_NAV_INPUTS.bits();
114    }
115}
116
117#[cfg(feature = "serde")]
118impl Serialize for WindowFlags {
119    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
120    where
121        S: serde::Serializer,
122    {
123        serializer.serialize_i32(self.bits())
124    }
125}
126
127#[cfg(feature = "serde")]
128impl<'de> Deserialize<'de> for WindowFlags {
129    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
130    where
131        D: serde::Deserializer<'de>,
132    {
133        let bits = i32::deserialize(deserializer)?;
134        Ok(WindowFlags::from_bits_truncate(bits))
135    }
136}
137
138/// Represents a window that can be built
139pub struct Window<'ui> {
140    ui: &'ui Ui,
141    name: String,
142    flags: WindowFlags,
143    size: Option<[f32; 2]>,
144    size_condition: Condition,
145    pos: Option<[f32; 2]>,
146    pos_condition: Condition,
147    content_size: Option<[f32; 2]>,
148    content_size_condition: Condition,
149    collapsed: Option<bool>,
150    collapsed_condition: Condition,
151    focused: Option<bool>,
152    bg_alpha: Option<f32>,
153}
154
155impl<'ui> Window<'ui> {
156    /// Creates a new window builder
157    pub fn new(ui: &'ui Ui, name: impl Into<String>) -> Self {
158        Self {
159            ui,
160            name: name.into(),
161            flags: WindowFlags::empty(),
162            size: None,
163            size_condition: Condition::Always,
164            pos: None,
165            pos_condition: Condition::Always,
166            content_size: None,
167            content_size_condition: Condition::Always,
168            collapsed: None,
169            collapsed_condition: Condition::Always,
170            focused: None,
171            bg_alpha: None,
172        }
173    }
174
175    /// Sets window flags
176    pub fn flags(mut self, flags: WindowFlags) -> Self {
177        self.flags = flags;
178        self
179    }
180
181    /// Sets window size
182    pub fn size(mut self, size: [f32; 2], condition: Condition) -> Self {
183        self.size = Some(size);
184        self.size_condition = condition;
185        self
186    }
187
188    /// Sets window position
189    pub fn position(mut self, pos: [f32; 2], condition: Condition) -> Self {
190        self.pos = Some(pos);
191        self.pos_condition = condition;
192        self
193    }
194
195    /// Sets window content size
196    pub fn content_size(mut self, size: [f32; 2], condition: Condition) -> Self {
197        self.content_size = Some(size);
198        self.content_size_condition = condition;
199        self
200    }
201
202    /// Sets window collapsed state
203    pub fn collapsed(mut self, collapsed: bool, condition: Condition) -> Self {
204        self.collapsed = Some(collapsed);
205        self.collapsed_condition = condition;
206        self
207    }
208
209    /// Sets window focused state
210    pub fn focused(mut self, focused: bool) -> Self {
211        self.focused = Some(focused);
212        self
213    }
214
215    /// Sets window background alpha
216    pub fn bg_alpha(mut self, alpha: f32) -> Self {
217        self.bg_alpha = Some(alpha);
218        self
219    }
220
221    /// Builds the window and calls the provided closure
222    pub fn build<F, R>(self, f: F) -> Option<R>
223    where
224        F: FnOnce() -> R,
225    {
226        let _token = self.begin()?;
227        Some(f())
228    }
229
230    /// Begins the window and returns a token
231    fn begin(self) -> Option<WindowToken<'ui>> {
232        use std::ffi::CString;
233
234        let name_cstr = CString::new(self.name).ok()?;
235
236        // Set window properties before beginning
237        if let Some(size) = self.size {
238            unsafe {
239                let size_vec = crate::sys::ImVec2 {
240                    x: size[0],
241                    y: size[1],
242                };
243                crate::sys::igSetNextWindowSize(size_vec, self.size_condition as i32);
244            }
245        }
246
247        if let Some(pos) = self.pos {
248            unsafe {
249                let pos_vec = crate::sys::ImVec2 {
250                    x: pos[0],
251                    y: pos[1],
252                };
253                let pivot_vec = crate::sys::ImVec2 { x: 0.0, y: 0.0 };
254                crate::sys::igSetNextWindowPos(pos_vec, self.pos_condition as i32, pivot_vec);
255            }
256        }
257
258        if let Some(content_size) = self.content_size {
259            unsafe {
260                let content_size_vec = crate::sys::ImVec2 {
261                    x: content_size[0],
262                    y: content_size[1],
263                };
264                crate::sys::igSetNextWindowContentSize(content_size_vec);
265            }
266        }
267
268        if let Some(collapsed) = self.collapsed {
269            unsafe {
270                crate::sys::igSetNextWindowCollapsed(collapsed, self.collapsed_condition as i32);
271            }
272        }
273
274        if let Some(focused) = self.focused
275            && focused
276        {
277            unsafe {
278                crate::sys::igSetNextWindowFocus();
279            }
280        }
281
282        if let Some(alpha) = self.bg_alpha {
283            unsafe {
284                crate::sys::igSetNextWindowBgAlpha(alpha);
285            }
286        }
287
288        // Begin the window
289        let mut open = true;
290        let result =
291            unsafe { crate::sys::igBegin(name_cstr.as_ptr(), &mut open, self.flags.bits()) };
292
293        // IMPORTANT: According to ImGui documentation, Begin/End calls must be balanced.
294        // If Begin returns false, we need to call End immediately and return None.
295        if result && open {
296            Some(WindowToken {
297                _phantom: std::marker::PhantomData,
298            })
299        } else {
300            // If Begin returns false, call End immediately and return None
301            unsafe {
302                crate::sys::igEnd();
303            }
304            None
305        }
306    }
307}
308
309/// Token representing an active window
310pub struct WindowToken<'ui> {
311    _phantom: std::marker::PhantomData<&'ui ()>,
312}
313
314impl<'ui> Drop for WindowToken<'ui> {
315    fn drop(&mut self) {
316        unsafe {
317            crate::sys::igEnd();
318        }
319    }
320}