#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::as_conversions
)]
use bitflags::bitflags;
use std::borrow::Cow;
use std::f32;
use crate::sys;
use crate::{Condition, Ui};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
mod child_window;
pub(crate) mod content_region;
pub(crate) mod scroll;
pub use child_window::{ChildFlags, ChildWindow, ChildWindowToken};
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WindowFlags: i32 {
const NO_TITLE_BAR = sys::ImGuiWindowFlags_NoTitleBar as i32;
const NO_RESIZE = sys::ImGuiWindowFlags_NoResize as i32;
const NO_MOVE = sys::ImGuiWindowFlags_NoMove as i32;
const NO_SCROLLBAR = sys::ImGuiWindowFlags_NoScrollbar as i32;
const NO_SCROLL_WITH_MOUSE = sys::ImGuiWindowFlags_NoScrollWithMouse as i32;
const NO_COLLAPSE = sys::ImGuiWindowFlags_NoCollapse as i32;
const ALWAYS_AUTO_RESIZE = sys::ImGuiWindowFlags_AlwaysAutoResize as i32;
const NO_BACKGROUND = sys::ImGuiWindowFlags_NoBackground as i32;
const NO_SAVED_SETTINGS = sys::ImGuiWindowFlags_NoSavedSettings as i32;
const NO_MOUSE_INPUTS = sys::ImGuiWindowFlags_NoMouseInputs as i32;
const MENU_BAR = sys::ImGuiWindowFlags_MenuBar as i32;
const HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_HorizontalScrollbar as i32;
const NO_FOCUS_ON_APPEARING = sys::ImGuiWindowFlags_NoFocusOnAppearing as i32;
const NO_BRING_TO_FRONT_ON_FOCUS = sys::ImGuiWindowFlags_NoBringToFrontOnFocus as i32;
const ALWAYS_VERTICAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysVerticalScrollbar as i32;
const ALWAYS_HORIZONTAL_SCROLLBAR = sys::ImGuiWindowFlags_AlwaysHorizontalScrollbar as i32;
const NO_NAV_INPUTS = sys::ImGuiWindowFlags_NoNavInputs as i32;
const NO_NAV_FOCUS = sys::ImGuiWindowFlags_NoNavFocus as i32;
const UNSAVED_DOCUMENT = sys::ImGuiWindowFlags_UnsavedDocument as i32;
const NO_DOCKING = sys::ImGuiWindowFlags_NoDocking as i32;
const NO_NAV = Self::NO_NAV_INPUTS.bits() | Self::NO_NAV_FOCUS.bits();
const NO_DECORATION = Self::NO_TITLE_BAR.bits() | Self::NO_RESIZE.bits() | Self::NO_SCROLLBAR.bits() | Self::NO_COLLAPSE.bits();
const NO_INPUTS = Self::NO_MOUSE_INPUTS.bits() | Self::NO_NAV_INPUTS.bits();
}
}
pub(crate) fn validate_window_flags(caller: &str, flags: WindowFlags) {
let unsupported = flags.bits() & !WindowFlags::all().bits();
assert!(
unsupported == 0,
"{caller} received unsupported ImGuiWindowFlags bits: 0x{unsupported:X}"
);
}
fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
assert!(
value[0].is_finite() && value[1].is_finite(),
"{caller} {name} must contain finite values"
);
}
#[cfg(feature = "serde")]
impl Serialize for WindowFlags {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i32(self.bits())
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for WindowFlags {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let bits = i32::deserialize(deserializer)?;
Ok(WindowFlags::from_bits_truncate(bits))
}
}
pub struct Window<'ui> {
ui: &'ui Ui,
name: Cow<'ui, str>,
opened: Option<&'ui mut bool>,
flags: WindowFlags,
size: Option<[f32; 2]>,
size_condition: Condition,
size_constraints: Option<([f32; 2], [f32; 2])>,
pos: Option<[f32; 2]>,
pos_condition: Condition,
content_size: Option<[f32; 2]>,
collapsed: Option<bool>,
collapsed_condition: Condition,
focused: Option<bool>,
bg_alpha: Option<f32>,
scroll: Option<[f32; 2]>,
}
impl<'ui> Window<'ui> {
pub fn new(ui: &'ui Ui, name: impl Into<Cow<'ui, str>>) -> Self {
Self {
ui,
name: name.into(),
opened: None,
flags: WindowFlags::empty(),
size: None,
size_condition: Condition::Always,
size_constraints: None,
pos: None,
pos_condition: Condition::Always,
content_size: None,
collapsed: None,
collapsed_condition: Condition::Always,
focused: None,
bg_alpha: None,
scroll: None,
}
}
pub fn flags(mut self, flags: WindowFlags) -> Self {
self.flags = flags;
self
}
#[doc(alias = "Begin")]
pub fn opened(mut self, opened: &'ui mut bool) -> Self {
self.opened = Some(opened);
self
}
pub fn size(mut self, size: [f32; 2], condition: Condition) -> Self {
self.size = Some(size);
self.size_condition = condition;
self
}
#[doc(alias = "SetNextWindowSizeConstraints")]
pub fn size_constraints(mut self, min: [f32; 2], max: [f32; 2]) -> Self {
self.size_constraints = Some((min, max));
self
}
pub fn position(mut self, pos: [f32; 2], condition: Condition) -> Self {
self.pos = Some(pos);
self.pos_condition = condition;
self
}
pub fn content_size(mut self, size: [f32; 2]) -> Self {
self.content_size = Some(size);
self
}
pub fn collapsed(mut self, collapsed: bool, condition: Condition) -> Self {
self.collapsed = Some(collapsed);
self.collapsed_condition = condition;
self
}
pub fn focused(mut self, focused: bool) -> Self {
self.focused = Some(focused);
self
}
pub fn bg_alpha(mut self, alpha: f32) -> Self {
self.bg_alpha = Some(alpha);
self
}
#[doc(alias = "SetNextWindowScroll")]
pub fn scroll(mut self, scroll: [f32; 2]) -> Self {
self.scroll = Some(scroll);
self
}
pub fn build<F, R>(self, f: F) -> Option<R>
where
F: FnOnce() -> R,
{
let _token = self.begin()?;
Some(f())
}
fn begin(self) -> Option<WindowToken<'ui>> {
let name = self.name;
let name_ptr = self.ui.scratch_txt(name);
validate_window_flags("Window::begin()", self.flags);
if let Some(size) = self.size {
assert_finite_vec2("Window::begin()", "size", size);
unsafe {
let size_vec = crate::sys::ImVec2 {
x: size[0],
y: size[1],
};
crate::sys::igSetNextWindowSize(size_vec, self.size_condition as i32);
}
}
if let Some((min, max)) = self.size_constraints {
assert_finite_vec2("Window::begin()", "minimum size constraint", min);
assert_finite_vec2("Window::begin()", "maximum size constraint", max);
unsafe {
let min_vec = sys::ImVec2_c {
x: min[0],
y: min[1],
};
let max_vec = sys::ImVec2_c {
x: max[0],
y: max[1],
};
sys::igSetNextWindowSizeConstraints(min_vec, max_vec, None, std::ptr::null_mut());
}
}
if let Some(pos) = self.pos {
assert_finite_vec2("Window::begin()", "position", pos);
unsafe {
let pos_vec = crate::sys::ImVec2 {
x: pos[0],
y: pos[1],
};
let pivot_vec = crate::sys::ImVec2 { x: 0.0, y: 0.0 };
crate::sys::igSetNextWindowPos(pos_vec, self.pos_condition as i32, pivot_vec);
}
}
if let Some(content_size) = self.content_size {
assert_finite_vec2("Window::begin()", "content size", content_size);
unsafe {
let content_size_vec = crate::sys::ImVec2 {
x: content_size[0],
y: content_size[1],
};
crate::sys::igSetNextWindowContentSize(content_size_vec);
}
}
if let Some(collapsed) = self.collapsed {
unsafe {
crate::sys::igSetNextWindowCollapsed(collapsed, self.collapsed_condition as i32);
}
}
if let Some(focused) = self.focused
&& focused
{
unsafe {
crate::sys::igSetNextWindowFocus();
}
}
if let Some(alpha) = self.bg_alpha {
assert!(
alpha.is_finite(),
"Window::begin() background alpha must be finite"
);
unsafe {
crate::sys::igSetNextWindowBgAlpha(alpha);
}
}
if let Some(scroll) = self.scroll {
assert_finite_vec2("Window::begin()", "scroll", scroll);
unsafe {
let scroll_vec = sys::ImVec2_c {
x: scroll[0],
y: scroll[1],
};
sys::igSetNextWindowScroll(scroll_vec);
}
}
let mut opened = self.opened;
let opened_ptr: *mut bool = match opened.as_deref_mut() {
Some(opened) => opened as *mut bool,
None => std::ptr::null_mut(),
};
let result = unsafe { crate::sys::igBegin(name_ptr, opened_ptr, self.flags.bits()) };
let is_open = opened.is_none_or(|opened| *opened);
if result && is_open {
Some(WindowToken {
_phantom: std::marker::PhantomData,
})
} else {
unsafe {
crate::sys::igEnd();
}
None
}
}
}
pub struct WindowToken<'ui> {
_phantom: std::marker::PhantomData<&'ui ()>,
}
impl<'ui> Drop for WindowToken<'ui> {
fn drop(&mut self) {
unsafe {
crate::sys::igEnd();
}
}
}