use std::collections::HashMap; use simplexpr::{dynval::DynVal, SimplExpr}; use crate::{enum_parse, error::{AstError, AstResult}, parser::{ ast::{Ast, Span}, ast_iterator::AstIterator, from_ast::{FromAst, FromAstElementContent}, }, value::{AttrName, Coords, VarName}}; use super::{widget_use::WidgetUse, window_definition::EnumParseError}; use serde::{Serialize, Deserialize}; #[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, strum::Display)] pub enum AnchorAlignment { #[strum(serialize = "start")] #[default] START, #[strum(serialize = "center")] CENTER, #[strum(serialize = "end")] END, } impl AnchorAlignment { pub fn from_x_alignment(s: &str) -> Result { enum_parse! { "x-alignment", s, "l" | "left" => AnchorAlignment::START, "c" | "center" => AnchorAlignment::CENTER, "r" | "right" => AnchorAlignment::END, } } pub fn from_y_alignment(s: &str) -> Result { enum_parse! { "y-alignment", s, "t" | "top" => AnchorAlignment::START, "c" | "center" => AnchorAlignment::CENTER, "b" | "bottom" => AnchorAlignment::END, } } pub fn alignment_to_coordinate(&self, size_inner: i32, size_container: i32) -> i32 { match self { AnchorAlignment::START => 0, AnchorAlignment::CENTER => (size_container / 2) - (size_inner / 2), AnchorAlignment::END => size_container - size_inner, } } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)] pub struct AnchorPoint { pub x: AnchorAlignment, pub y: AnchorAlignment, } impl std::fmt::Display for AnchorPoint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use AnchorAlignment::*; match (self.x, self.y) { (CENTER, CENTER) => write!(f, "center"), (x, y) => write!( f, "{} {}", match x { START => "left", CENTER => "center", END => "right", }, match y { START => "top", CENTER => "center", END => "bottom", } ), } } } #[derive(Debug, thiserror::Error)] pub enum AnchorPointParseError { #[error("Could not parse anchor: Must either be \"center\" or be formatted like \"top left\"")] WrongFormat(String), #[error(transparent)] EnumParseError(#[from] EnumParseError), } impl std::str::FromStr for AnchorPoint { type Err = AnchorPointParseError; fn from_str(s: &str) -> Result { if s == "center" { Ok(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::CENTER }) } else { let (first, second) = s .split_once(' ') .ok_or_else(|| AnchorPointParseError::WrongFormat(s.to_string()))?; let x_y_result: Result<_, EnumParseError> = try { AnchorPoint { x: AnchorAlignment::from_x_alignment(first)?, y: AnchorAlignment::from_y_alignment(second)? } }; x_y_result.or_else(|_| { Ok(AnchorPoint { x: AnchorAlignment::from_x_alignment(second)?, y: AnchorAlignment::from_y_alignment(first)? }) }) } } } #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Serialize)] pub struct WindowGeometry { pub anchor_point: AnchorPoint, pub offset: Coords, pub size: Coords, } impl FromAstElementContent for WindowGeometry { fn get_element_name() -> &'static str { "geometry" } fn from_tail>(span: Span, mut iter: AstIterator) -> AstResult { let mut attrs = iter.expect_key_values()?; Ok(WindowGeometry { anchor_point: attrs.primitive_optional("anchor")?.unwrap_or_default(), size: Coords { x: attrs.primitive_optional("width")?.unwrap_or_default(), y: attrs.primitive_optional("height")?.unwrap_or_default(), }, offset: Coords { x: attrs.primitive_optional("x")?.unwrap_or_default(), y: attrs.primitive_optional("y")?.unwrap_or_default(), }, }) } } impl WindowGeometry { pub fn override_if_given(&self, anchor_point: Option, offset: Option, size: Option) -> Self { WindowGeometry { anchor_point: anchor_point.unwrap_or(self.anchor_point), offset: offset.unwrap_or(self.offset), size: size.unwrap_or(self.size), } } } impl std::fmt::Display for WindowGeometry { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}-{} ({})", self.offset, self.size, self.anchor_point) } }