typst/src/library/layout.rs

142 lines
4.2 KiB
Rust

use crate::length::ScaleLength;
use super::*;
function! {
/// `align`: Aligns content along the layouting axes.
#[derive(Debug, Clone, PartialEq)]
pub struct AlignFunc {
body: Option<SyntaxModel>,
map: PosAxisMap<AlignmentValue>,
}
parse(header, body, ctx, f) {
AlignFunc {
body: body!(opt: body, ctx, f),
map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
}
}
layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions;
let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |alignment| {
alignment.axis().map(|s| s.to_generic(ctx.axes))
});
for &axis in &[Primary, Secondary] {
if let Some(Spanned { v: alignment, span }) = map.get_spanned(axis) {
if let Some(generic) = alignment.to_generic(ctx.axes, axis) {
*ctx.alignment.get_mut(axis) = generic;
} else {
error!(
@f, span,
"invalid alignment `{}` for {} axis", alignment, axis,
);
}
}
}
match &self.body {
Some(body) => {
let layouted = layout(body, ctx).await;
f.extend(layouted.feedback);
vec![AddMultiple(layouted.output)]
}
None => vec![SetAlignment(ctx.alignment)],
}
}
}
function! {
/// `direction`: Sets the directions of the layouting axes.
#[derive(Debug, Clone, PartialEq)]
pub struct DirectionFunc {
name_span: Span,
body: Option<SyntaxModel>,
map: PosAxisMap<Direction>,
}
parse(header, body, ctx, f) {
DirectionFunc {
name_span: header.name.span,
body: body!(opt: body, ctx, f),
map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
}
}
layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions;
let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |direction| {
Some(direction.axis().to_generic(ctx.axes))
});
let mut axes = ctx.axes;
map.with(Primary, |&dir| axes.primary = dir);
map.with(Secondary, |&dir| axes.secondary = dir);
if axes.primary.axis() == axes.secondary.axis() {
error!(
@f, self.name_span,
"invalid aligned primary and secondary axes: `{}`, `{}`",
ctx.axes.primary, ctx.axes.secondary,
);
} else {
ctx.axes = axes;
}
match &self.body {
Some(body) => {
let layouted = layout(body, ctx).await;
f.extend(layouted.feedback);
vec![AddMultiple(layouted.output)]
}
None => vec![SetAxes(ctx.axes)],
}
}
}
function! {
/// `box`: Layouts content into a box.
#[derive(Debug, Clone, PartialEq)]
pub struct BoxFunc {
body: SyntaxModel,
extents: AxisMap<ScaleLength>,
debug: Option<bool>,
}
parse(header, body, ctx, f) {
BoxFunc {
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
debug: header.args.key.get::<bool>(&mut f.diagnostics, "debug"),
}
}
layout(self, ctx, f) {
ctx.repeat = false;
ctx.spaces.truncate(1);
if let Some(debug) = self.debug {
ctx.debug = debug;
}
let map = self.extents.dedup(&mut f.diagnostics, ctx.axes);
for &axis in &[Horizontal, Vertical] {
if let Some(scale) = map.get(axis) {
let length = scale.scaled(ctx.base.get(axis));
*ctx.base.get_mut(axis) = length;
*ctx.spaces[0].dimensions.get_mut(axis) = length;
*ctx.spaces[0].expansion.get_mut(axis) = true;
}
}
let layouted = layout(&self.body, ctx).await;
let layout = layouted.output.into_iter().next().unwrap();
f.extend(layouted.feedback);
vec![Add(layout)]
}
}