summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/layout/mod.rs3
-rw-r--r--src/library/axes.rs116
2 files changed, 103 insertions, 16 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 04039d07..e78e6067 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 65dfa800..09c2f7d2 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<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"))?,
+ })
+}