use crate::geom::{Rect, Size};
use crate::tree::OriginId;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct WindowId(pub u32);
impl WindowId {
pub const MAIN: WindowId = WindowId(0);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum WindowRole {
Drag,
Button(WindowButton),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum WindowButton {
Close,
Minimize,
Maximize,
}
impl WindowButton {
pub fn command(self, window: WindowId) -> WindowCommand {
match self {
WindowButton::Close => WindowCommand::Close(window),
WindowButton::Minimize => WindowCommand::Minimize(window),
WindowButton::Maximize => WindowCommand::ToggleMaximize(window),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum WindowKind {
#[default]
Normal,
Popup,
}
impl WindowKind {
pub const ALL: [WindowKind; 2] = [WindowKind::Normal, WindowKind::Popup];
pub fn name(self) -> &'static str {
match self {
WindowKind::Normal => "normal",
WindowKind::Popup => "popup",
}
}
pub fn from_name(name: &str) -> Option<WindowKind> {
Self::ALL.into_iter().find(|k| k.name() == name)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WindowConfig {
pub kind: WindowKind,
pub size: Size,
pub activates: bool,
pub anchor: Rect,
}
impl WindowConfig {
pub fn to_value(&self) -> crate::value::Value {
use crate::value::Value;
Value::map([
("kind", Value::str(self.kind.name())),
("width", Value::float(self.size.w)),
("height", Value::float(self.size.h)),
("activates", Value::Bool(self.activates)),
("anchor", self.anchor.to_value()),
])
}
pub const DEFAULT_SIZE: Size = Size { w: 640.0, h: 480.0 };
pub fn sized(w: f32, h: f32) -> Self {
Self {
size: Size::new(w, h),
..Self::default()
}
}
pub fn popup(anchor: Rect, w: f32, h: f32) -> Self {
Self {
size: Size::new(w, h),
anchor,
..Self::of_kind(WindowKind::Popup)
}
}
pub fn of_kind(kind: WindowKind) -> Self {
Self {
kind,
activates: kind == WindowKind::Normal,
..Self::default()
}
}
pub fn from_value(v: &crate::value::Value) -> Result<(String, Self), String> {
use crate::value::Value;
match v {
Value::Str(name) => Ok((name.clone(), Self::default())),
Value::Map(_) => {
let name = v
.get_str("name")
.ok_or("a windows entry needs a name")?
.to_string();
let kind = match v.get_str("kind") {
None => WindowKind::Normal,
Some(k) => WindowKind::from_name(k).ok_or_else(|| {
format!(
"windows entry `{name}` has kind {k:?}; the kinds are {}",
WindowKind::ALL
.iter()
.map(|k| format!("{:?}", k.name()))
.collect::<Vec<_>>()
.join(" and ")
)
})?,
};
let mut cfg = Self::of_kind(kind);
let num = |key: &str| v.get(key).and_then(Value::as_float).map(|n| n as f32);
if let (Some(w), Some(h)) = (num("width"), num("height"))
&& w > 0.0
&& h > 0.0
{
cfg.size = Size::new(w, h);
}
if let Some(a) = v.get_bool("activates") {
cfg.activates = a;
}
if let Some(a) = v.get("anchor") {
let f = |key: &str| a.get(key).and_then(Value::as_float).unwrap_or(0.0) as f32;
cfg.anchor = Rect::new(f("x"), f("y"), f("w"), f("h"));
}
Ok((name, cfg))
}
other => Err(format!(
"a windows entry is a name or a table, not {}",
other.type_name()
)),
}
}
}
impl Default for WindowConfig {
fn default() -> Self {
Self {
kind: WindowKind::Normal,
size: Self::DEFAULT_SIZE,
activates: true,
anchor: Rect::new(0.0, 0.0, 0.0, 0.0),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum WindowCommand {
StartDrag(WindowId),
Close(WindowId),
Minimize(WindowId),
ToggleMaximize(WindowId),
Open {
id: WindowId,
owner: WindowId,
origin: OriginId,
config: WindowConfig,
},
SetSize {
window: WindowId,
size: Size,
},
Focus(WindowId),
Redraw(WindowId),
}
impl WindowCommand {
pub fn kind_name(&self) -> &'static str {
match self {
WindowCommand::StartDrag(_) => "startDrag",
WindowCommand::Close(_) => "close",
WindowCommand::Minimize(_) => "minimize",
WindowCommand::ToggleMaximize(_) => "toggleMaximize",
WindowCommand::Open { .. } => "open",
WindowCommand::SetSize { .. } => "setSize",
WindowCommand::Focus(_) => "focus",
WindowCommand::Redraw(_) => "redraw",
}
}
pub fn to_value(&self) -> crate::value::Value {
use crate::value::Value;
let mut out = vec![
("kind".to_string(), Value::str(self.kind_name())),
("window".to_string(), Value::Int(self.window().0 as i64)),
];
match self {
WindowCommand::SetSize { size, .. } => {
out.push(("width".into(), Value::float(size.w)));
out.push(("height".into(), Value::float(size.h)));
}
WindowCommand::Open {
owner,
origin,
config,
..
} => {
out.push(("owner".into(), Value::Int(owner.0 as i64)));
out.push(("origin".into(), Value::Int(origin.0 as i64)));
out.push(("config".into(), config.to_value()));
}
_ => {}
}
Value::Map(out)
}
pub fn window(&self) -> WindowId {
match *self {
WindowCommand::StartDrag(w)
| WindowCommand::Close(w)
| WindowCommand::Minimize(w)
| WindowCommand::ToggleMaximize(w)
| WindowCommand::Focus(w)
| WindowCommand::Redraw(w) => w,
WindowCommand::Open { id, .. } => id,
WindowCommand::SetSize { window, .. } => window,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DismissReason {
Outside,
Escape,
}
impl DismissReason {
pub fn as_str(self) -> &'static str {
match self {
DismissReason::Outside => "outside",
DismissReason::Escape => "escape",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Backdrop {
#[default]
Opaque,
Transparent,
Blur,
Tinted,
}
impl Backdrop {
pub const ALL: [Backdrop; 4] = [
Backdrop::Opaque,
Backdrop::Transparent,
Backdrop::Blur,
Backdrop::Tinted,
];
pub fn name(self) -> &'static str {
match self {
Backdrop::Opaque => "opaque",
Backdrop::Transparent => "transparent",
Backdrop::Blur => "blur",
Backdrop::Tinted => "tinted",
}
}
pub fn from_name(name: &str) -> Option<Backdrop> {
Self::ALL.into_iter().find(|b| b.name() == name)
}
pub fn from_code(code: u32) -> Option<Backdrop> {
Self::ALL.get(code as usize).copied()
}
pub fn is_translucent(self) -> bool {
self != Backdrop::Opaque
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct WindowEnv {
pub id: WindowId,
pub custom_chrome: bool,
pub maximized: bool,
pub fullscreen: bool,
pub always_on_top: bool,
pub native_controls: Option<Rect>,
pub backdrop: Backdrop,
}