From 4ab7ec6a9a1159bdf1e22eccb56d6d827aaf5b23 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 22 Nov 2019 15:54:39 +0100 Subject: [PATCH] =?UTF-8?q?Specify=20both=20axis=20with=20align=20function?= =?UTF-8?q?=20=E2=9E=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layout/mod.rs | 3 ++ src/library/axes.rs | 116 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 103 insertions(+), 16 deletions(-) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 04039d07f..e78e60673 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -324,6 +324,8 @@ impl SpaceState { pub enum LayoutError { /// An action is unallowed in the active context. Unallowed(&'static str), + /// A specifier or operation is invalid for the given axis. + UnalignedAxis(&'static str), /// There is not enough space to add an item. NotEnoughSpace(&'static str), /// There was no suitable font for the given character. @@ -339,6 +341,7 @@ error_type! { err: LayoutError, show: f => match err { LayoutError::Unallowed(desc) => write!(f, "unallowed: {}", desc), + LayoutError::UnalignedAxis(desc) => write!(f, "unaligned axis: {}", desc), LayoutError::NotEnoughSpace(desc) => write!(f, "not enough space: {}", desc), LayoutError::NoSuitableFont(c) => write!(f, "no suitable font for '{}'", c), LayoutError::Font(err) => write!(f, "font error: {}", err), diff --git a/src/library/axes.rs b/src/library/axes.rs index 65dfa800f..09c2f7d29 100644 --- a/src/library/axes.rs +++ b/src/library/axes.rs @@ -4,7 +4,21 @@ use crate::func::prelude::*; #[derive(Debug, PartialEq)] pub struct Align { body: Option, - alignment: Alignment, + primary: Option, + secondary: Option, + horizontal: Option, + vertical: Option, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum AlignSpecifier { + Origin, + Center, + End, + Left, + Right, + Top, + Bottom, } function! { @@ -12,29 +26,76 @@ function! { parse(args, body, ctx) { let body = parse!(optional: body, ctx); - let arg = args.get_pos::()?; - let alignment = match arg.val { - "left" | "origin" => Alignment::Origin, - "center" => Alignment::Center, - "right" | "end" => Alignment::End, - s => err!("invalid alignment specifier: {}", s), + + let mut align = Align { + body, + primary: None, + secondary: None, + horizontal: None, + vertical: None, }; + + if let Some(arg) = args.get_pos_opt::()? { + align.primary = Some(parse_align_specifier(arg)?); + } + + let mut parse_arg = |axis, target: &mut Option| { + Ok(if let Some(arg) = args.get_key_opt::(axis)? { + if target.is_none() { + *target = Some(parse_align_specifier(arg)?); + } else { + err!("duplicate alignment specification for {} axis", axis); + } + }) + }; + + parse_arg("primary", &mut align.primary)?; + parse_arg("secondary", &mut align.secondary)?; + parse_arg("horizontal", &mut align.horizontal)?; + parse_arg("vertical", &mut align.vertical)?; + args.done()?; - Ok(Align { - body, - alignment, - }) + Ok(align) } layout(this, ctx) { let mut axes = ctx.axes; - axes.primary.alignment = this.alignment; + let primary_horizontal = axes.primary.axis.is_horizontal(); - if ctx.axes.primary.alignment == Alignment::End - && this.alignment == Alignment::Origin { - axes.primary.expand = true; - } + let mut primary = false; + let mut secondary = false; + + let mut set_axis = |is_primary: bool, spec: Option| -> LayoutResult<()> { + if let Some(spec) = spec { + let (axis, was_set, name) = match is_primary { + true => (&mut axes.primary, &mut primary, "primary"), + false => (&mut axes.secondary, &mut secondary, "secondary"), + }; + + if *was_set { + panic!("duplicate alignment for {} axis", name); + } + + *was_set = true; + + let horizontal = axis.axis.is_horizontal(); + let alignment = generic_alignment(spec, horizontal)?; + + if axis.alignment == Alignment::End && alignment == Alignment::Origin { + axis.expand = true; + } + + axis.alignment = alignment; + } + + Ok(()) + }; + + set_axis(true, this.primary)?; + set_axis(false, this.secondary)?; + set_axis(primary_horizontal, this.horizontal)?; + set_axis(!primary_horizontal, this.vertical)?; Ok(match &this.body { Some(body) => commands![AddMultiple( @@ -47,3 +108,26 @@ function! { }) } } + +fn parse_align_specifier(arg: Spanned<&str>) -> ParseResult { + Ok(match arg.val { + "origin" => AlignSpecifier::Origin, + "center" => AlignSpecifier::Center, + "end" => AlignSpecifier::End, + "left" => AlignSpecifier::Left, + "right" => AlignSpecifier::Right, + "top" => AlignSpecifier::Top, + "bottom" => AlignSpecifier::Bottom, + s => err!("invalid alignment specifier: {}", s), + }) +} + +fn generic_alignment(spec: AlignSpecifier, horizontal: bool) -> LayoutResult { + use AlignSpecifier::*; + Ok(match (spec, horizontal) { + (Origin, _) | (Left, true) | (Top, false) => Alignment::Origin, + (Center, _) => Alignment::Center, + (End, _) | (Right, true) | (Bottom, false) => Alignment::End, + _ => Err(LayoutError::UnalignedAxis("invalid alignment"))?, + }) +}