use std::{ fmt::{Display, Write}, str::FromStr, }; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; use super::{ rule::{Condition, RuleOperator}, StringParseError, }; #[derive(Debug, Clone, Serialize, Deserialize, strum::EnumIter, PartialEq)] pub enum Window { Urgent, X11(i32), /// references the minimized window on the focused tag /// that has been minimized for the longest time LongestMinimized, /// references the minimized window on the focused tag /// that has been minimized most recently LastMinimized, } impl FromStr for Window { type Err = StringParseError; fn from_str(s: &str) -> Result { if let Ok(val) = i32::from_str(s) { Ok(Self::X11(val)) } else { Window::iter() .find(|w| w.to_string() == s) .ok_or(StringParseError::UnknownValue) } } } impl Default for Window { fn default() -> Self { Self::Urgent } } impl From for Window { fn from(value: i32) -> Self { Self::X11(value) } } impl std::fmt::Display for Window { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Window::Urgent => f.write_str("urgent"), Window::X11(id) => write!(f, "{id}"), Window::LongestMinimized => f.write_str("longest-minimized"), Window::LastMinimized => f.write_str("last-minimized"), } } } pub struct TagStatus { name: String, state: TagState, } #[allow(unused)] impl TagStatus { pub fn name(&self) -> &str { &self.name } pub fn state(&self) -> TagState { self.state } } impl FromStr for TagStatus { type Err = StringParseError; fn from_str(s: &str) -> Result { let mut parts = s.chars(); let state = parts .next() .ok_or(StringParseError::UnknownValue)? .try_into()?; let name = parts.collect(); Ok(Self { name, state }) } } #[derive(Debug, Clone, Copy, Serialize, Deserialize, strum::EnumIter)] pub enum TagState { Empty, NotEmpty, /// The tag contains an urgent window Urgent, /// The tag is viewed on the specified MONITOR and it is focused SameMonitorFocused, /// The tag is viewed on the specified MONITOR, but this monitor is not focused SameMonitor, /// The tag is viewed on a different MONITOR and it is focused DifferentMonitorFocused, /// The tag is viewed on a different MONITOR, but this monitor is not focused DifferentMonitor, } impl TryFrom for TagState { type Error = StringParseError; fn try_from(value: char) -> Result { Self::iter() .into_iter() .find(|i| char::from(i) == value) .ok_or(StringParseError::UnknownValue) } } impl FromStr for TagState { type Err = StringParseError; fn from_str(s: &str) -> Result { Self::iter() .into_iter() .find(|i| i.to_string() == s) .ok_or(StringParseError::UnknownValue) } } impl From<&TagState> for char { fn from(value: &TagState) -> Self { match value { TagState::SameMonitorFocused => '#', TagState::SameMonitor => '+', TagState::DifferentMonitorFocused => '%', TagState::DifferentMonitor => '-', TagState::Empty => '.', TagState::NotEmpty => ':', TagState::Urgent => '!', } } } impl Display for TagState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_char(self.into()) } } #[derive(Clone, Copy, strum::Display)] #[strum(serialize_all = "UPPERCASE")] pub enum WindowType { Dialog, Utility, Splash, Notification, Dock, Desktop, } impl WindowType { pub fn or>(types: I) -> Condition { Condition::WindowType { operator: RuleOperator::Regex, value: format!( "_NET_WM_WINDOW_TYPE_({})", types.map(|t| t.to_string()).collect::>().join("|") ), } } } #[macro_export] macro_rules! window_types { ($($ty:tt),+) => { crate::hlwm::window::WindowType::or([$( crate::hlwm::window::WindowType::$ty ),+].into_iter()) }; }