summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-07 17:07:44 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-07 17:07:44 +0200
commit537545e7f8351d7677c396456e46568f5a5e2a7a (patch)
treef4c7614293246db06c7fa7496458da01b15c3b84 /src/library
parentca1256c924f3672feb76dbc2bc2e309eb4fc4cf5 (diff)
Evaluation and node-based layouting 🚀
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs28
-rw-r--r--src/library/boxed.rs52
-rw-r--r--src/library/color.rs2
-rw-r--r--src/library/font.rs41
-rw-r--r--src/library/mod.rs2
-rw-r--r--src/library/page.rs43
-rw-r--r--src/library/spacing.rs29
7 files changed, 111 insertions, 86 deletions
diff --git a/src/library/align.rs b/src/library/align.rs
index f3280065..acd3a85c 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -14,7 +14,9 @@ use crate::prelude::*;
/// - `vertical`: Any of `top`, `bottom` or `center`.
///
/// There may not be two alignment specifications for the same axis.
-pub async fn align(mut args: Args, ctx: &mut LayoutContext) -> Value {
+pub fn align(mut args: Args, ctx: &mut EvalContext) -> Value {
+ let snapshot = ctx.state.clone();
+
let body = args.find::<SynTree>();
let first = args.get::<_, Spanned<SpecAlign>>(ctx, 0);
let second = args.get::<_, Spanned<SpecAlign>>(ctx, 1);
@@ -29,21 +31,25 @@ pub async fn align(mut args: Args, ctx: &mut LayoutContext) -> Value {
.chain(hor.into_iter().map(|align| (Some(SpecAxis::Horizontal), align)))
.chain(ver.into_iter().map(|align| (Some(SpecAxis::Vertical), align)));
- let aligns = dedup_aligns(ctx, iter);
+ let prev_main = ctx.state.aligns.main;
+ ctx.state.aligns = dedup_aligns(ctx, iter);
+
+ if prev_main != ctx.state.aligns.main {
+ ctx.end_par_group();
+ ctx.start_par_group();
+ }
+
+ if let Some(body) = body {
+ body.eval(ctx);
+ ctx.state = snapshot;
+ }
- Value::Commands(match body {
- Some(tree) => vec![
- SetAlignment(aligns),
- LayoutSyntaxTree(tree),
- SetAlignment(ctx.state.aligns),
- ],
- None => vec![SetAlignment(aligns)],
- })
+ Value::None
}
/// Deduplicate alignments and deduce to which axes they apply.
fn dedup_aligns(
- ctx: &mut LayoutContext,
+ ctx: &mut EvalContext,
iter: impl Iterator<Item = (Option<SpecAxis>, Spanned<SpecAlign>)>,
) -> Gen2<GenAlign> {
let mut aligns = ctx.state.aligns;
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index b88f5b7c..6edb3b17 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -1,4 +1,5 @@
use crate::geom::Linear;
+use crate::layout::nodes::{Fixed, Stack};
use crate::prelude::*;
/// `box`: Layouts its contents into a box.
@@ -6,34 +7,37 @@ use crate::prelude::*;
/// # Keyword arguments
/// - `width`: The width of the box (length or relative to parent's width).
/// - `height`: The height of the box (length or relative to parent's height).
-pub async fn boxed(mut args: Args, ctx: &mut LayoutContext) -> Value {
+pub fn boxed(mut args: Args, ctx: &mut EvalContext) -> Value {
let body = args.find::<SynTree>().unwrap_or_default();
let width = args.get::<_, Linear>(ctx, "width");
let height = args.get::<_, Linear>(ctx, "height");
args.done(ctx);
+ let dirs = ctx.state.dirs;
let aligns = ctx.state.aligns;
- let constraints = &mut ctx.constraints;
- constraints.base = constraints.spaces[0].size;
- constraints.spaces.truncate(1);
- constraints.repeat = false;
-
- if let Some(width) = width {
- let abs = width.eval(constraints.base.width);
- constraints.base.width = abs;
- constraints.spaces[0].size.width = abs;
- constraints.spaces[0].expansion.horizontal = true;
- }
-
- if let Some(height) = height {
- let abs = height.eval(constraints.base.height);
- constraints.base.height = abs;
- constraints.spaces[0].size.height = abs;
- constraints.spaces[0].expansion.vertical = true;
- }
-
- let layouted = layout_tree(&body, ctx).await;
- let layout = layouted.into_iter().next().unwrap();
-
- Value::Commands(vec![Add(layout, aligns)])
+
+ let snapshot = ctx.state.clone();
+
+ ctx.start_group(());
+ ctx.start_par_group();
+
+ body.eval(ctx);
+
+ ctx.end_par_group();
+ let ((), children) = ctx.end_group();
+
+ ctx.push(Fixed {
+ width,
+ height,
+ child: LayoutNode::dynamic(Stack {
+ dirs,
+ children,
+ aligns,
+ expand: Spec2::new(width.is_some(), height.is_some()),
+ }),
+ });
+
+ ctx.state = snapshot;
+
+ Value::None
}
diff --git a/src/library/color.rs b/src/library/color.rs
index 261352ba..17c33806 100644
--- a/src/library/color.rs
+++ b/src/library/color.rs
@@ -2,7 +2,7 @@ use crate::color::RgbaColor;
use crate::prelude::*;
/// `rgb`: Create an RGB(A) color.
-pub async fn rgb(mut args: Args, ctx: &mut LayoutContext) -> Value {
+pub fn rgb(mut args: Args, ctx: &mut EvalContext) -> Value {
let r = args.need::<_, Spanned<i64>>(ctx, 0, "red value");
let g = args.need::<_, Spanned<i64>>(ctx, 1, "green value");
let b = args.need::<_, Spanned<i64>>(ctx, 2, "blue value");
diff --git a/src/library/font.rs b/src/library/font.rs
index 21fb2d13..be6823c3 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -1,3 +1,5 @@
+use std::rc::Rc;
+
use fontdock::{FontStretch, FontStyle, FontWeight};
use crate::eval::StringLike;
@@ -49,37 +51,38 @@ use crate::prelude::*;
/// ```typst
/// [font: "My Serif", serif]
/// ```
-pub async fn font(mut args: Args, ctx: &mut LayoutContext) -> Value {
- let mut text = ctx.state.text.clone();
- let mut needs_flattening = false;
+pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value {
+ let snapshot = ctx.state.clone();
let body = args.find::<SynTree>();
if let Some(linear) = args.find::<Linear>() {
if linear.rel == 0.0 {
- text.font_size.base = linear.abs;
- text.font_size.scale = Linear::rel(1.0);
+ ctx.state.text.font_size.base = linear.abs;
+ ctx.state.text.font_size.scale = Linear::rel(1.0);
} else {
- text.font_size.scale = linear;
+ ctx.state.text.font_size.scale = linear;
}
}
+ let mut needs_flattening = false;
let list: Vec<_> = args.find_all::<StringLike>().map(|s| s.to_lowercase()).collect();
+
if !list.is_empty() {
- text.fallback.list = list;
+ Rc::make_mut(&mut ctx.state.text.fallback).list = list;
needs_flattening = true;
}
if let Some(style) = args.get::<_, FontStyle>(ctx, "style") {
- text.variant.style = style;
+ ctx.state.text.variant.style = style;
}
if let Some(weight) = args.get::<_, FontWeight>(ctx, "weight") {
- text.variant.weight = weight;
+ ctx.state.text.variant.weight = weight;
}
if let Some(stretch) = args.get::<_, FontStretch>(ctx, "stretch") {
- text.variant.stretch = stretch;
+ ctx.state.text.variant.stretch = stretch;
}
for (class, dict) in args.find_all_str::<Spanned<ValueDict>>() {
@@ -88,22 +91,20 @@ pub async fn font(mut args: Args, ctx: &mut LayoutContext) -> Value {
.map(|s| s.to_lowercase())
.collect();
- text.fallback.update_class_list(class, fallback);
+ Rc::make_mut(&mut ctx.state.text.fallback).update_class_list(class, fallback);
needs_flattening = true;
}
args.done(ctx);
if needs_flattening {
- text.fallback.flatten();
+ Rc::make_mut(&mut ctx.state.text.fallback).flatten();
+ }
+
+ if let Some(body) = body {
+ body.eval(ctx);
+ ctx.state = snapshot;
}
- Value::Commands(match body {
- Some(tree) => vec![
- SetTextState(text),
- LayoutSyntaxTree(tree),
- SetTextState(ctx.state.text.clone()),
- ],
- None => vec![SetTextState(text)],
- })
+ Value::None
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 191a3920..af23d050 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -21,7 +21,7 @@ macro_rules! std {
/// Create a scope with all standard library functions.
pub fn _std() -> Scope {
let mut std = Scope::new();
- $(std.set($name, ValueFunc::new(|args, ctx| Box::pin($func(args, ctx))));)*
+ $(std.set($name, ValueFunc::new($func));)*
std
}
};
diff --git a/src/library/page.rs b/src/library/page.rs
index fd33039c..448932a5 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -19,54 +19,61 @@ use crate::prelude::*;
/// - `top`: The top margin (length or relative to height).
/// - `bottom`: The bottom margin (length or relative to height).
/// - `flip`: Flips custom or paper-defined width and height (boolean).
-pub async fn page(mut args: Args, ctx: &mut LayoutContext) -> Value {
- let mut page = ctx.state.page.clone();
+pub fn page(mut args: Args, ctx: &mut EvalContext) -> Value {
+ let snapshot = ctx.state.clone();
if let Some(paper) = args.find::<Paper>() {
- page.class = paper.class;
- page.size = paper.size();
+ ctx.state.page.class = paper.class;
+ ctx.state.page.size = paper.size();
}
if let Some(Absolute(width)) = args.get::<_, Absolute>(ctx, "width") {
- page.class = PaperClass::Custom;
- page.size.width = width;
+ ctx.state.page.class = PaperClass::Custom;
+ ctx.state.page.size.width = width;
}
if let Some(Absolute(height)) = args.get::<_, Absolute>(ctx, "height") {
- page.class = PaperClass::Custom;
- page.size.height = height;
+ ctx.state.page.class = PaperClass::Custom;
+ ctx.state.page.size.height = height;
}
if let Some(margins) = args.get::<_, Linear>(ctx, "margins") {
- page.margins = Sides::uniform(Some(margins));
+ ctx.state.page.margins = Sides::uniform(Some(margins));
}
if let Some(left) = args.get::<_, Linear>(ctx, "left") {
- page.margins.left = Some(left);
+ ctx.state.page.margins.left = Some(left);
}
if let Some(top) = args.get::<_, Linear>(ctx, "top") {
- page.margins.top = Some(top);
+ ctx.state.page.margins.top = Some(top);
}
if let Some(right) = args.get::<_, Linear>(ctx, "right") {
- page.margins.right = Some(right);
+ ctx.state.page.margins.right = Some(right);
}
if let Some(bottom) = args.get::<_, Linear>(ctx, "bottom") {
- page.margins.bottom = Some(bottom);
+ ctx.state.page.margins.bottom = Some(bottom);
}
if args.get::<_, bool>(ctx, "flip").unwrap_or(false) {
- mem::swap(&mut page.size.width, &mut page.size.height);
+ let size = &mut ctx.state.page.size;
+ mem::swap(&mut size.width, &mut size.height);
}
args.done(ctx);
- Value::Commands(vec![SetPageState(page)])
+
+ ctx.end_page_group();
+ ctx.start_page_group(false);
+
+ Value::None
}
-/// `pagebreak`: Ends the current page.
-pub async fn pagebreak(args: Args, ctx: &mut LayoutContext) -> Value {
+/// `pagebreak`: Starts a new page.
+pub fn pagebreak(mut args: Args, ctx: &mut EvalContext) -> Value {
args.done(ctx);
- Value::Commands(vec![BreakPage])
+ ctx.end_page_group();
+ ctx.start_page_group(true);
+ Value::None
}
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index e254d5e4..6d00bd1c 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -1,12 +1,12 @@
use crate::geom::Linear;
-use crate::layout::SpacingKind;
+use crate::layout::nodes::{Softness, Spacing};
use crate::prelude::*;
/// `h`: Add horizontal spacing.
///
/// # Positional arguments
/// - The spacing (length or relative to font size).
-pub async fn h(args: Args, ctx: &mut LayoutContext) -> Value {
+pub fn h(args: Args, ctx: &mut EvalContext) -> Value {
spacing(args, ctx, SpecAxis::Horizontal)
}
@@ -14,19 +14,26 @@ pub async fn h(args: Args, ctx: &mut LayoutContext) -> Value {
///
/// # Positional arguments
/// - The spacing (length or relative to font size).
-pub async fn v(args: Args, ctx: &mut LayoutContext) -> Value {
+pub fn v(args: Args, ctx: &mut EvalContext) -> Value {
spacing(args, ctx, SpecAxis::Vertical)
}
-fn spacing(mut args: Args, ctx: &mut LayoutContext, axis: SpecAxis) -> Value {
+/// Apply spacing along a specific axis.
+fn spacing(mut args: Args, ctx: &mut EvalContext, axis: SpecAxis) -> Value {
let spacing = args.need::<_, Linear>(ctx, 0, "spacing");
args.done(ctx);
- Value::Commands(if let Some(spacing) = spacing {
- let spacing = spacing.eval(ctx.state.text.font_size());
- let axis = axis.switch(ctx.state.dirs);
- vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
- } else {
- vec![]
- })
+ if let Some(linear) = spacing {
+ let amount = linear.eval(ctx.state.text.font_size());
+ let spacing = Spacing { amount, softness: Softness::Hard };
+ if ctx.state.dirs.main.axis() == axis {
+ ctx.end_par_group();
+ ctx.push(spacing);
+ ctx.start_par_group();
+ } else {
+ ctx.push(spacing);
+ }
+ }
+
+ Value::None
}