From ecf0ff4d054f11c79ec0ddbbdf45f3dfcf9fc8d7 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 23 Oct 2019 00:14:43 +0200 Subject: =?UTF-8?q?Introduce=20a=20set=20of=20macros=20for=20writing=20fun?= =?UTF-8?q?ctions=20more=20concisely=20=F0=9F=8E=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/library/structure.rs | 148 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/library/structure.rs (limited to 'src/library/structure.rs') diff --git a/src/library/structure.rs b/src/library/structure.rs new file mode 100644 index 00000000..ae05a12b --- /dev/null +++ b/src/library/structure.rs @@ -0,0 +1,148 @@ +use crate::func::prelude::*; + +/// Ends the current page. +#[derive(Debug, PartialEq)] +pub struct Pagebreak; + +function! { + data: Pagebreak, + parse: plain, + + layout(_, _) { + Ok(commands![Command::FinishLayout]) + } +} + +/// Ends the current line. +#[derive(Debug, PartialEq)] +pub struct Linebreak; + +function! { + data: Linebreak, + parse: plain, + + layout(_, _) { + Ok(commands![Command::FinishFlexRun]) + } +} + +/// Aligns content in different ways. +#[derive(Debug, PartialEq)] +pub struct Align { + body: Option, + alignment: Alignment, +} + +function! { + data: Align, + + parse(args, body, ctx) { + let body = parse!(optional: body, ctx); + let alignment = match args.get_ident()? { + "left" => Alignment::Left, + "right" => Alignment::Right, + "center" => Alignment::Center, + s => err!("invalid alignment specifier: {}", s), + }; + args.done()?; + + Ok(Align { + body, + alignment, + }) + } + + layout(this, ctx) { + Ok(commands![match &this.body { + Some(body) => { + Command::AddMany(layout_tree(body, LayoutContext { + alignment: this.alignment, + .. ctx + })?) + } + None => Command::SetAlignment(this.alignment) + }]) + } +} + +/// Layouts content into a box. +#[derive(Debug, PartialEq)] +pub struct Boxed { + body: SyntaxTree, + flow: Flow, +} + +function! { + data: Boxed, + + parse(args, body, ctx) { + let body = parse!(required: body, ctx); + + let mut flow = Flow::Vertical; + if let Some(ident) = args.get_ident_if_present()? { + flow = match ident { + "vertical" => Flow::Vertical, + "horizontal" => Flow::Horizontal, + f => err!("invalid flow specifier: {}", f), + }; + } + args.done()?; + + Ok(Boxed { + body, + flow, + }) + } + + layout(this, ctx) { + Ok(commands![ + Command::AddMany(layout_tree(&this.body, LayoutContext { + flow: this.flow, + .. ctx + })?) + ]) + } +} + +macro_rules! spacefunc { + ($ident:ident, $name:expr, $var:ident => $command:expr) => { + /// Adds whitespace. + #[derive(Debug, PartialEq)] + pub struct $ident(Spacing); + + function! { + data: $ident, + + parse(args, body, _ctx) { + parse!(forbidden: body); + + let spacing = match args.get_expr()? { + Expression::Size(s) => Spacing::Absolute(*s), + Expression::Number(f) => Spacing::Relative(*f as f32), + _ => err!("invalid spacing, expected size or number"), + }; + + Ok($ident(spacing)) + } + + layout(this, ctx) { + let $var = match this.0 { + Spacing::Absolute(s) => s, + Spacing::Relative(f) => Size::pt(f * ctx.style.font_size), + }; + + Ok(commands![$command]) + } + } + }; +} + +/// Absolute or font-relative spacing. +#[derive(Debug, PartialEq)] +enum Spacing { + Absolute(Size), + Relative(f32), +} + +spacefunc!(HorizontalSpace, "h", space => Command::AddFlex(Layout::empty(space, Size::zero()))); +spacefunc!(VerticalSpace, "v", space => Command::Add(Layout::empty(Size::zero(), space))); -- cgit v1.2.3