use std::fmt;
use crate::arguments::{ArgumentScanner, ExpectArg, FromArgs};
use crate::keyword::FrameKeyword;
use crate::parse::UnrecognizedVariant;
use crate::screen::{Align, Dimension};
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum FrameAction {
#[default]
Open,
Close,
Redirect,
}
impl_parse_enum!(FrameAction, Open, Close, Redirect);
impl_display_enum!(FrameAction, Open, Close, Redirect);
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum FrameAlign {
#[default]
Top,
Bottom,
Left,
Right,
Middle,
Client,
}
impl_parse_enum!(FrameAlign, Top, Bottom, Left, Right, Middle, Client);
impl fmt::Display for FrameAlign {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl From<Align> for FrameAlign {
fn from(value: Align) -> Self {
match value {
Align::Top => Self::Top,
Align::Bottom => Self::Bottom,
Align::Left => Self::Left,
Align::Right => Self::Right,
Align::Middle => Self::Middle,
}
}
}
impl TryFrom<FrameAlign> for Align {
type Error = UnrecognizedVariant<Self>;
fn try_from(value: FrameAlign) -> Result<Self, Self::Error> {
match value {
FrameAlign::Top => Ok(Self::Top),
FrameAlign::Bottom => Ok(Self::Bottom),
FrameAlign::Left => Ok(Self::Left),
FrameAlign::Right => Ok(Self::Right),
FrameAlign::Middle => Ok(Self::Middle),
FrameAlign::Client => Err(Self::Error::new("Client")),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FrameLayout<S> {
External {
left: Dimension<i32>,
top: Dimension<i32>,
width: Option<Dimension<u32>>,
height: Option<Dimension<u32>>,
floating: bool,
},
Internal {
align: FrameAlign,
width: Option<Dimension<u32>>,
height: Option<Dimension<u32>>,
dock: Option<S>,
},
}
impl<S> Default for FrameLayout<S> {
fn default() -> Self {
Self::External {
left: Dimension::pixels(0),
top: Dimension::pixels(0),
width: None,
height: None,
floating: false,
}
}
}
impl<S> FrameLayout<S> {
pub fn map_text<T, F>(self, f: F) -> FrameLayout<T>
where
F: FnOnce(S) -> T,
{
match self {
Self::External {
left,
top,
width,
height,
floating,
} => FrameLayout::External {
left,
top,
width,
height,
floating,
},
Self::Internal {
align,
width,
height,
dock,
} => FrameLayout::Internal {
align,
width,
height,
dock: dock.map(f),
},
}
}
}
impl<S: AsRef<str>> FrameLayout<S> {
pub fn borrow_text(&self) -> FrameLayout<&str> {
match *self {
Self::External {
left,
top,
width,
height,
floating,
} => FrameLayout::External {
left,
top,
width,
height,
floating,
},
Self::Internal {
align,
width,
height,
ref dock,
} => FrameLayout::Internal {
align,
width,
height,
dock: dock.as_ref().map(AsRef::as_ref),
},
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Frame<S = String> {
pub name: S,
pub action: FrameAction,
pub title: S,
pub layout: FrameLayout<S>,
pub scrolling: bool,
pub persistent: bool,
}
impl<S> Frame<S> {
pub fn map_text<T, F>(self, mut f: F) -> Frame<T>
where
F: FnMut(S) -> T,
{
Frame {
name: f(self.name),
action: self.action,
title: f(self.title),
layout: self.layout.map_text(f),
scrolling: self.scrolling,
persistent: self.persistent,
}
}
}
impl_into_owned!(Frame);
impl<S: AsRef<str>> Frame<S> {
pub fn borrow_text(&self) -> Frame<&str> {
Frame {
name: self.name.as_ref(),
action: self.action,
title: self.title.as_ref(),
layout: self.layout.borrow_text(),
scrolling: self.scrolling,
persistent: self.persistent,
}
}
}
impl_partial_eq!(Frame);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum YesOrNo {
No,
Yes,
}
impl_parse_enum!(YesOrNo, No, Yes);
impl<'a, S: AsRef<str> + Clone> FromArgs<'a, S> for Frame<S> {
fn from_args<A: ArgumentScanner<'a, Decoded = S>>(scanner: A) -> crate::Result<Self> {
let mut scanner = scanner.with_keywords();
let name = scanner.get_next_or("name")?.expect_some("name")?;
let action = scanner
.get_next_or("action")?
.expect_variant()?
.unwrap_or_default();
let title = scanner
.get_next_or("title")?
.unwrap_or_else(|| name.clone());
let align = scanner
.get_next_or("align")?
.expect_variant()?
.unwrap_or_default();
let left = scanner
.get_next_or("left")?
.expect_number()?
.unwrap_or_default();
let top = scanner
.get_next_or("top")?
.expect_number()?
.unwrap_or_default();
let width = scanner.get_next_or("width")?.expect_number()?;
let height = scanner.get_next_or("height")?.expect_number()?;
let scrolling = scanner.get_next_or("scrolling")?.expect_variant()? == Some(YesOrNo::Yes);
let dock = scanner.get_next_or("dock")?;
let keywords = scanner.into_keywords()?;
let layout = if keywords.contains(FrameKeyword::Internal) || dock.is_some() {
FrameLayout::Internal {
dock,
align,
width,
height,
}
} else {
FrameLayout::External {
left,
top,
width,
height,
floating: keywords.contains(FrameKeyword::Floating),
}
};
Ok(Self {
name,
action,
title,
layout,
scrolling,
persistent: keywords.contains(FrameKeyword::Persistent),
})
}
}
impl_from_str!(Frame);
impl<S: AsRef<str>> fmt::Display for Frame<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Frame {
name,
action,
title,
layout,
scrolling,
persistent,
} = self.borrow_text().map_text(crate::display::Escape);
write!(f, "<FRAME NAME={name}")?;
match action {
FrameAction::Close => f.write_str(" CLOSE")?,
FrameAction::Open => (),
FrameAction::Redirect => f.write_str(" REDIRECT")?,
}
if title != name {
write!(f, " TITLE={title}")?;
}
match layout {
FrameLayout::External {
left,
top,
width,
height,
floating,
} => {
if left.amount != 0 {
write!(f, " LEFT={left}")?;
}
if top.amount != 0 {
write!(f, " TOP={top}")?;
}
if let Some(width) = width {
write!(f, " WIDTH={width}")?;
}
if let Some(height) = height {
write!(f, " HEIGHT={height}")?;
}
if floating {
f.write_str(" FLOATING")?;
}
}
FrameLayout::Internal {
align,
width,
height,
dock,
} => {
if align != FrameAlign::default() {
write!(f, " ALIGN={align}")?;
}
if let Some(width) = width {
write!(f, " WIDTH={width}")?;
}
if let Some(height) = height {
write!(f, " HEIGHT={height}")?;
}
match dock {
Some(dock) => write!(f, " DOCK={dock}")?,
None => write!(f, " INTERNAL")?,
}
}
}
if scrolling {
f.write_str(" SCROLLING=yes")?;
}
if persistent {
f.write_str(" PERSISTENT")?;
}
f.write_str(">")
}
}