use crate::{ensure_xml_tag_is, value::NumWithUnit, widgets::widget_node}; use anyhow::*; use derive_more::*; use serde::{Deserialize, Serialize}; use smart_default::SmartDefault; use std::collections::HashMap; use super::*; /// Full window-definition containing the fully expanded widget tree. /// **Use this** rather than `[RawEwwWindowDefinition]`. #[derive(Debug, Clone)] pub struct EwwWindowDefinition { pub name: WindowName, pub geometry: EwwWindowGeometry, pub stacking: WindowStacking, pub screen_number: Option, pub widget: Box, pub struts: StrutDefinition, pub focusable: bool, } impl EwwWindowDefinition { pub fn generate(defs: &HashMap, window: RawEwwWindowDefinition) -> Result { Ok(EwwWindowDefinition { name: window.name, geometry: window.geometry, stacking: window.stacking, screen_number: window.screen_number, widget: widget_node::generate_generic_widget_node(defs, &HashMap::new(), window.widget)?, struts: window.struts, focusable: window.focusable, }) } } /// Window-definition storing the raw WidgetUse, as received directly from parsing. #[derive(Debug, Clone, PartialEq)] pub struct RawEwwWindowDefinition { pub name: WindowName, pub geometry: EwwWindowGeometry, pub stacking: WindowStacking, pub screen_number: Option, pub widget: WidgetUse, pub struts: StrutDefinition, pub focusable: bool, } impl RawEwwWindowDefinition { pub fn from_xml_element(xml: &XmlElement) -> Result { ensure_xml_tag_is!(xml, "window"); let stacking: WindowStacking = xml.parse_optional_attr("stacking")?.unwrap_or_default(); // TODO maybe rename this to monitor? let focusable = xml.parse_optional_attr("focusable")?; let screen_number = xml.parse_optional_attr("screen")?; let struts: Option = xml.child("reserve").ok().map(StrutDefinition::from_xml_element).transpose().context("Failed to parse ")?; Ok(RawEwwWindowDefinition { name: WindowName(xml.attr("name")?.to_owned()), geometry: match xml.child("geometry") { Ok(node) => EwwWindowGeometry::from_xml_element(node)?, Err(_) => EwwWindowGeometry::default(), }, widget: WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?, stacking, screen_number, focusable: focusable.unwrap_or(false), struts: struts.unwrap_or_default(), }) } } #[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)] pub enum Side { #[default] Top, Left, Right, Bottom, } impl std::str::FromStr for Side { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "l" | "left" => Ok(Side::Left), "r" | "right" => Ok(Side::Right), "t" | "top" => Ok(Side::Top), "b" | "bottom" => Ok(Side::Bottom), _ => Err(anyhow!("Failed to parse {} as valid side. Must be one of \"left\", \"right\", \"top\", \"bottom\"", s)), } } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct StrutDefinition { pub side: Side, pub dist: NumWithUnit, } impl StrutDefinition { pub fn from_xml_element(xml: XmlElement) -> Result { Ok(StrutDefinition { side: xml.attr("side")?.parse()?, dist: xml.attr("distance")?.parse()? }) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, SmartDefault)] pub enum WindowStacking { #[default] Foreground, Background, } impl std::str::FromStr for WindowStacking { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let s = s.to_lowercase(); match s.as_str() { "foreground" | "fg" | "f" => Ok(WindowStacking::Foreground), "background" | "bg" | "b" => Ok(WindowStacking::Background), _ => Err(anyhow!("Couldn't parse '{}' as window stacking, must be either foreground, fg, background or bg", s)), } } } #[repr(transparent)] #[derive(Clone, Hash, PartialEq, Eq, AsRef, FromStr, Display, Serialize, Deserialize, Default, From, DebugCustom)] #[debug(fmt = "WindowName(\".0\")")] pub struct WindowName(String); impl std::borrow::Borrow for WindowName { fn borrow(&self) -> &str { &self.0 } }