Specify both axis with align function

This commit is contained in:
Laurenz 2019-11-22 15:54:39 +01:00
parent ebff8eb038
commit 4ab7ec6a9a
2 changed files with 103 additions and 16 deletions

View File

@ -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),

View File

@ -4,7 +4,21 @@ use crate::func::prelude::*;
#[derive(Debug, PartialEq)]
pub struct Align {
body: Option<SyntaxTree>,
alignment: Alignment,
primary: Option<AlignSpecifier>,
secondary: Option<AlignSpecifier>,
horizontal: Option<AlignSpecifier>,
vertical: Option<AlignSpecifier>,
}
#[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::<ArgIdent>()?;
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::<ArgIdent>()? {
align.primary = Some(parse_align_specifier(arg)?);
}
let mut parse_arg = |axis, target: &mut Option<AlignSpecifier>| {
Ok(if let Some(arg) = args.get_key_opt::<ArgIdent>(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<AlignSpecifier>| -> 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<AlignSpecifier> {
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<Alignment> {
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"))?,
})
}