eww/src/config/window_geometry.rs
ElKowar 2e8b1af083
Lazy variables (#58)
* Implement lazy variables

* cleanup

* refactor
2020-11-08 15:08:01 +01:00

159 lines
5.2 KiB
Rust

use crate::value::Coords;
use anyhow::*;
use serde::{Deserialize, Serialize};
use smart_default::SmartDefault;
use std::fmt;
use super::xml_ext::XmlElement;
#[derive(Debug, derive_more::Display, Clone, Copy, Eq, PartialEq, SmartDefault, Serialize, Deserialize)]
pub enum AnchorAlignment {
#[display("start")]
#[default]
START,
#[display("center")]
CENTER,
#[display("end")]
END,
}
impl AnchorAlignment {
pub fn from_x_alignment(s: &str) -> Result<AnchorAlignment> {
match s {
"l" | "left" => Ok(AnchorAlignment::START),
"c" | "center" => Ok(AnchorAlignment::CENTER),
"r" | "right" => Ok(AnchorAlignment::END),
_ => bail!(
r#"couldn't parse '{}' as x-alignment. Must be one of "left", "center", "right""#,
s
),
}
}
pub fn from_y_alignment(s: &str) -> Result<AnchorAlignment> {
match s {
"t" | "top" => Ok(AnchorAlignment::START),
"c" | "center" => Ok(AnchorAlignment::CENTER),
"b" | "bottom" => Ok(AnchorAlignment::END),
_ => bail!(
r#"couldn't parse '{}' as y-alignment. Must be one of "top", "center", "bottom""#,
s
),
}
}
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 {
x: AnchorAlignment,
y: AnchorAlignment,
}
impl std::fmt::Display for AnchorPoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> 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",
}
),
}
}
}
impl std::str::FromStr for AnchorPoint {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "center" {
Ok(AnchorPoint {
x: AnchorAlignment::CENTER,
y: AnchorAlignment::CENTER,
})
} else {
let (first, second) = s
.split_once(' ')
.context("Failed to parse anchor: Must either be \"center\" or be formatted like \"top left\"")?;
let x_y_result: Result<_> = 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)]
pub struct EwwWindowGeometry {
pub anchor_point: AnchorPoint,
pub offset: Coords,
pub size: Coords,
}
impl EwwWindowGeometry {
pub fn from_xml_element(xml: XmlElement) -> Result<Self> {
Ok(EwwWindowGeometry {
anchor_point: xml.parse_optional_attr("anchor")?.unwrap_or_default(),
size: Coords {
x: xml.parse_optional_attr("width")?.unwrap_or_default(),
y: xml.parse_optional_attr("height")?.unwrap_or_default(),
},
offset: Coords {
x: xml.parse_optional_attr("x")?.unwrap_or_default(),
y: xml.parse_optional_attr("y")?.unwrap_or_default(),
},
})
}
pub fn override_if_given(&mut self, anchor_point: Option<AnchorPoint>, offset: Option<Coords>, size: Option<Coords>) -> Self {
EwwWindowGeometry {
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 EwwWindowGeometry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-{} ({})", self.offset, self.size, self.anchor_point)
}
}
impl EwwWindowGeometry {
/// Calculate the window rectangle given the configured window geometry
pub fn get_window_rectangle(&self, screen_rect: gdk::Rectangle) -> gdk::Rectangle {
let (offset_x, offset_y) = self.offset.relative_to(screen_rect.width, screen_rect.height);
let (width, height) = self.size.relative_to(screen_rect.width, screen_rect.height);
let x = screen_rect.x + offset_x + self.anchor_point.x.alignment_to_coordinate(width, screen_rect.width);
let y = screen_rect.y + offset_y + self.anchor_point.y.alignment_to_coordinate(height, screen_rect.height);
gdk::Rectangle { x, y, width, height }
}
}