1#![allow(
41 clippy::cast_possible_truncation,
42 clippy::cast_sign_loss,
43 clippy::as_conversions
44)]
45use bitflags::bitflags;
46use std::borrow::Cow;
47use std::f32;
48
49use crate::sys;
50use crate::{Condition, Ui};
51#[cfg(feature = "serde")]
52use serde::{Deserialize, Serialize};
53
54mod child_window;
55pub(crate) mod content_region;
56pub(crate) mod scroll;
57
58pub use child_window::{ChildFlags, ChildWindow, ChildWindowToken};
59
60bitflags! {
64 #[repr(transparent)]
66 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67 pub struct WindowFlags: i32 {
68 const NO_TITLE_BAR = sys::ImGuiWindowFlags_NoTitleBar as i32;
70 const NO_RESIZE = sys::ImGuiWindowFlags_NoResize as i32;
72 const NO_MOVE = sys::ImGuiWindowFlags_NoMove as i32;
74 const NO_SCROLLBAR = sys::ImGuiWindowFlags_NoScrollbar as i32;
76 const NO_SCROLL_WITH_MOUSE = sys::ImGuiWindowFlags_NoScrollWithMouse as i32;
78 const NO_COLLAPSE = sys::ImGuiWindowFlags_NoCollapse as i32;
80 const ALWAYS_AUTO_RESIZE = sys::ImGuiWindowFlags_AlwaysAutoResize as i32;
82 const NO_BACKGROUND = sys::ImGuiWindowFlags_NoBackground as i32;
84 const NO_SAVED_SETTINGS = sys::ImGuiWindowFlags_NoSavedSettings as i32;
86 const NO_MOUSE_INPUTS = sys::ImGuiWindowFlags_NoMouseInputs as i32;
88 const MENU_BAR = sys::ImGuiWindowFlags_MenuBar as i32;
90 const HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_HorizontalScrollbar as i32;
92 const NO_FOCUS_ON_APPEARING = sys::ImGuiWindowFlags_NoFocusOnAppearing as i32;
94 const NO_BRING_TO_FRONT_ON_FOCUS = sys::ImGuiWindowFlags_NoBringToFrontOnFocus as i32;
96 const ALWAYS_VERTICAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysVerticalScrollbar as i32;
98 const ALWAYS_HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysHorizontalScrollbar as i32;
100 const NO_NAV_INPUTS = sys::ImGuiWindowFlags_NoNavInputs as i32;
102 const NO_NAV_FOCUS = sys::ImGuiWindowFlags_NoNavFocus as i32;
104 const UNSAVED_DOCUMENT = sys::ImGuiWindowFlags_UnsavedDocument as i32;
106 const NO_DOCKING = sys::ImGuiWindowFlags_NoDocking as i32;
109 const NO_NAV = Self::NO_NAV_INPUTS.bits() | Self::NO_NAV_FOCUS.bits();
111 const NO_DECORATION = Self::NO_TITLE_BAR.bits() | Self::NO_RESIZE.bits() | Self::NO_SCROLLBAR.bits() | Self::NO_COLLAPSE.bits();
113 const NO_INPUTS = Self::NO_MOUSE_INPUTS.bits() | Self::NO_NAV_INPUTS.bits();
115 }
116}
117
118pub(crate) fn validate_window_flags(caller: &str, flags: WindowFlags) {
119 let unsupported = flags.bits() & !WindowFlags::all().bits();
120 assert!(
121 unsupported == 0,
122 "{caller} received unsupported ImGuiWindowFlags bits: 0x{unsupported:X}"
123 );
124}
125
126fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
127 assert!(
128 value[0].is_finite() && value[1].is_finite(),
129 "{caller} {name} must contain finite values"
130 );
131}
132
133#[cfg(feature = "serde")]
134impl Serialize for WindowFlags {
135 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136 where
137 S: serde::Serializer,
138 {
139 serializer.serialize_i32(self.bits())
140 }
141}
142
143#[cfg(feature = "serde")]
144impl<'de> Deserialize<'de> for WindowFlags {
145 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
146 where
147 D: serde::Deserializer<'de>,
148 {
149 let bits = i32::deserialize(deserializer)?;
150 Ok(WindowFlags::from_bits_retain(bits))
151 }
152}
153
154pub struct Window<'ui> {
156 ui: &'ui Ui,
157 name: Cow<'ui, str>,
158 opened: Option<&'ui mut bool>,
159 flags: WindowFlags,
160 size: Option<[f32; 2]>,
161 size_condition: Condition,
162 size_constraints: Option<([f32; 2], [f32; 2])>,
163 pos: Option<[f32; 2]>,
164 pos_condition: Condition,
165 content_size: Option<[f32; 2]>,
166 collapsed: Option<bool>,
167 collapsed_condition: Condition,
168 focused: Option<bool>,
169 bg_alpha: Option<f32>,
170 scroll: Option<[f32; 2]>,
171}
172
173impl<'ui> Window<'ui> {
174 pub fn new(ui: &'ui Ui, name: impl Into<Cow<'ui, str>>) -> Self {
176 Self {
177 ui,
178 name: name.into(),
179 opened: None,
180 flags: WindowFlags::empty(),
181 size: None,
182 size_condition: Condition::Always,
183 size_constraints: None,
184 pos: None,
185 pos_condition: Condition::Always,
186 content_size: None,
187 collapsed: None,
188 collapsed_condition: Condition::Always,
189 focused: None,
190 bg_alpha: None,
191 scroll: None,
192 }
193 }
194
195 pub fn flags(mut self, flags: WindowFlags) -> Self {
197 self.flags = flags;
198 self
199 }
200
201 #[doc(alias = "Begin")]
209 pub fn opened(mut self, opened: &'ui mut bool) -> Self {
210 self.opened = Some(opened);
211 self
212 }
213
214 pub fn size(mut self, size: [f32; 2], condition: Condition) -> Self {
216 self.size = Some(size);
217 self.size_condition = condition;
218 self
219 }
220
221 #[doc(alias = "SetNextWindowSizeConstraints")]
226 pub fn size_constraints(mut self, min: [f32; 2], max: [f32; 2]) -> Self {
227 self.size_constraints = Some((min, max));
228 self
229 }
230
231 pub fn position(mut self, pos: [f32; 2], condition: Condition) -> Self {
233 self.pos = Some(pos);
234 self.pos_condition = condition;
235 self
236 }
237
238 pub fn content_size(mut self, size: [f32; 2]) -> Self {
240 self.content_size = Some(size);
241 self
242 }
243
244 pub fn collapsed(mut self, collapsed: bool, condition: Condition) -> Self {
246 self.collapsed = Some(collapsed);
247 self.collapsed_condition = condition;
248 self
249 }
250
251 pub fn focused(mut self, focused: bool) -> Self {
253 self.focused = Some(focused);
254 self
255 }
256
257 pub fn bg_alpha(mut self, alpha: f32) -> Self {
259 self.bg_alpha = Some(alpha);
260 self
261 }
262
263 #[doc(alias = "SetNextWindowScroll")]
265 pub fn scroll(mut self, scroll: [f32; 2]) -> Self {
266 self.scroll = Some(scroll);
267 self
268 }
269
270 pub fn build<F, R>(self, f: F) -> Option<R>
272 where
273 F: FnOnce() -> R,
274 {
275 let _token = self.begin()?;
276 Some(f())
277 }
278
279 fn begin(self) -> Option<WindowToken<'ui>> {
281 let name = self.name;
282 let name_ptr = self.ui.scratch_txt(name);
283 validate_window_flags("Window::begin()", self.flags);
284
285 self.ui.run_with_bound_context(|| {
287 if let Some(size) = self.size {
288 assert_finite_vec2("Window::begin()", "size", size);
289 unsafe {
290 let size_vec = crate::sys::ImVec2 {
291 x: size[0],
292 y: size[1],
293 };
294 crate::sys::igSetNextWindowSize(size_vec, self.size_condition as i32);
295 }
296 }
297
298 if let Some((min, max)) = self.size_constraints {
299 assert_finite_vec2("Window::begin()", "minimum size constraint", min);
300 assert_finite_vec2("Window::begin()", "maximum size constraint", max);
301 unsafe {
302 let min_vec = sys::ImVec2_c {
303 x: min[0],
304 y: min[1],
305 };
306 let max_vec = sys::ImVec2_c {
307 x: max[0],
308 y: max[1],
309 };
310 sys::igSetNextWindowSizeConstraints(
311 min_vec,
312 max_vec,
313 None,
314 std::ptr::null_mut(),
315 );
316 }
317 }
318
319 if let Some(pos) = self.pos {
320 assert_finite_vec2("Window::begin()", "position", pos);
321 unsafe {
322 let pos_vec = crate::sys::ImVec2 {
323 x: pos[0],
324 y: pos[1],
325 };
326 let pivot_vec = crate::sys::ImVec2 { x: 0.0, y: 0.0 };
327 crate::sys::igSetNextWindowPos(pos_vec, self.pos_condition as i32, pivot_vec);
328 }
329 }
330
331 if let Some(content_size) = self.content_size {
332 assert_finite_vec2("Window::begin()", "content size", content_size);
333 unsafe {
334 let content_size_vec = crate::sys::ImVec2 {
335 x: content_size[0],
336 y: content_size[1],
337 };
338 crate::sys::igSetNextWindowContentSize(content_size_vec);
339 }
340 }
341
342 if let Some(collapsed) = self.collapsed {
343 unsafe {
344 crate::sys::igSetNextWindowCollapsed(
345 collapsed,
346 self.collapsed_condition as i32,
347 );
348 }
349 }
350
351 if let Some(focused) = self.focused
352 && focused
353 {
354 unsafe {
355 crate::sys::igSetNextWindowFocus();
356 }
357 }
358
359 if let Some(alpha) = self.bg_alpha {
360 assert!(
361 alpha.is_finite(),
362 "Window::begin() background alpha must be finite"
363 );
364 unsafe {
365 crate::sys::igSetNextWindowBgAlpha(alpha);
366 }
367 }
368
369 if let Some(scroll) = self.scroll {
370 assert_finite_vec2("Window::begin()", "scroll", scroll);
371 unsafe {
372 let scroll_vec = sys::ImVec2_c {
373 x: scroll[0],
374 y: scroll[1],
375 };
376 sys::igSetNextWindowScroll(scroll_vec);
377 }
378 }
379
380 let mut opened = self.opened;
382 let opened_ptr: *mut bool = match opened.as_deref_mut() {
383 Some(opened) => opened as *mut bool,
384 None => std::ptr::null_mut(),
385 };
386 let result = unsafe { crate::sys::igBegin(name_ptr, opened_ptr, self.flags.bits()) };
387 let is_open = opened.is_none_or(|opened| *opened);
388
389 if result && is_open {
392 Some(WindowToken { ui: self.ui })
393 } else {
394 unsafe {
396 crate::sys::igEnd();
397 }
398 None
399 }
400 })
401 }
402}
403
404pub struct WindowToken<'ui> {
406 ui: &'ui Ui,
407}
408
409impl<'ui> Drop for WindowToken<'ui> {
410 fn drop(&mut self) {
411 self.ui.run_with_bound_context(|| unsafe {
412 crate::sys::igEnd();
413 });
414 }
415}