summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-06-11 14:42:20 +0200
committerLaurenz <laurmaedje@gmail.com>2021-06-11 14:42:20 +0200
commit4017b5a9f67e06145129d75de452c8a42e2d2f5a (patch)
tree8ce9c6f80faa75ed62d4f7fbe31d3ceee6e8d4ba
parent4dbd9285c91d59d527f4324df4aaf239ecb007ca (diff)
Push some nodes directly into the stack
-rw-r--r--bench/src/bench.rs2
-rw-r--r--src/exec/context.rs46
-rw-r--r--src/exec/mod.rs9
-rw-r--r--src/layout/fixed.rs4
-rw-r--r--src/layout/mod.rs20
-rw-r--r--src/layout/par.rs4
-rw-r--r--src/layout/stack.rs22
-rw-r--r--src/library/grid.rs2
-rw-r--r--src/library/image.rs2
-rw-r--r--src/library/pad.rs2
-rw-r--r--src/library/shapes.rs8
-rw-r--r--src/library/stack.rs2
-rw-r--r--tests/ref/layout/fixed.pngbin1372 -> 1389 bytes
-rw-r--r--tests/ref/layout/pad.pngbin1226 -> 44396 bytes
-rw-r--r--tests/typ/layout/fixed.typ6
-rw-r--r--tests/typ/layout/pad.typ16
16 files changed, 86 insertions, 59 deletions
diff --git a/bench/src/bench.rs b/bench/src/bench.rs
index d4e297bf..832d538f 100644
--- a/bench/src/bench.rs
+++ b/bench/src/bench.rs
@@ -13,7 +13,7 @@ use typst::typeset;
const FONT_DIR: &str = "../fonts";
const TYP_DIR: &str = "../tests/typ";
-const CASES: &[&str] = &["full/coma.typ", "text/basic.typ"];
+const CASES: &[&str] = &["coma.typ", "text/basic.typ"];
fn benchmarks(c: &mut Criterion) {
let mut loader = FsLoader::new();
diff --git a/src/exec/context.rs b/src/exec/context.rs
index e03a7c67..3994fdb9 100644
--- a/src/exec/context.rs
+++ b/src/exec/context.rs
@@ -74,10 +74,11 @@ impl ExecContext {
mem::replace(&mut self.stack, stack).build()
}
- /// Push any node into the active paragraph.
- pub fn push(&mut self, node: impl Into<AnyNode>) {
- let align = self.state.aligns.cross;
- self.stack.par.push(ParChild::Any(node.into(), align));
+ /// Push text into the active paragraph.
+ ///
+ /// The text is split into lines at newlines.
+ pub fn push_text(&mut self, text: impl Into<String>) {
+ self.stack.par.push(self.make_text_node(text));
}
/// Push a word space into the active paragraph.
@@ -85,18 +86,25 @@ impl ExecContext {
self.stack.par.push_soft(self.make_text_node(" "));
}
- /// Push text into the active paragraph.
- ///
- /// The text is split into lines at newlines.
- pub fn push_text(&mut self, text: impl Into<String>) {
- self.stack.par.push(self.make_text_node(text));
+ /// Push any node into the active paragraph.
+ pub fn push_into_par(&mut self, node: impl Into<AnyNode>) {
+ let align = self.state.aligns.cross;
+ self.stack.par.push(ParChild::Any(node.into(), align));
}
- /// Push spacing into paragraph or stack depending on `axis`.
+ /// Push any node into the active stack.
+ pub fn push_into_stack(&mut self, node: impl Into<AnyNode>) {
+ self.parbreak();
+ let aligns = self.state.aligns;
+ self.stack.push(StackChild::Any(node.into(), aligns));
+ self.parbreak();
+ }
+
+ /// Push spacing into the active paragraph or stack depending on the `axis`.
pub fn push_spacing(&mut self, axis: GenAxis, amount: Length) {
match axis {
GenAxis::Main => {
- self.stack.parbreak(&self.state);
+ self.stack.finish_par(&self.state);
self.stack.push_hard(StackChild::Spacing(amount));
}
GenAxis::Cross => {
@@ -113,18 +121,18 @@ impl ExecContext {
/// Apply a forced paragraph break.
pub fn parbreak(&mut self) {
let amount = self.state.par.spacing.resolve(self.state.font.size);
- self.stack.parbreak(&self.state);
+ self.stack.finish_par(&self.state);
self.stack.push_soft(StackChild::Spacing(amount));
}
/// Apply a forced page break.
- pub fn pagebreak(&mut self, keep: bool, hard: bool, source: Span) {
+ pub fn pagebreak(&mut self, keep: bool, hard: bool, span: Span) {
if let Some(builder) = &mut self.page {
let page = mem::replace(builder, PageBuilder::new(&self.state, hard));
let stack = mem::replace(&mut self.stack, StackBuilder::new(&self.state));
self.tree.runs.extend(page.build(stack.build(), keep));
} else {
- self.diag(error!(source, "cannot modify page from here"));
+ self.diag(error!(span, "cannot modify page from here"));
}
}
@@ -185,6 +193,11 @@ impl StackBuilder {
}
}
+ fn push(&mut self, child: StackChild) {
+ self.children.extend(self.last.any());
+ self.children.push(child);
+ }
+
fn push_soft(&mut self, child: StackChild) {
self.last.soft(child);
}
@@ -194,11 +207,10 @@ impl StackBuilder {
self.children.push(child);
}
- fn parbreak(&mut self, state: &State) {
+ fn finish_par(&mut self, state: &State) {
let par = mem::replace(&mut self.par, ParBuilder::new(state));
if let Some(par) = par.build() {
- self.children.extend(self.last.any());
- self.children.push(par);
+ self.push(par);
}
}
diff --git a/src/exec/mod.rs b/src/exec/mod.rs
index 8b088f85..fea6de33 100644
--- a/src/exec/mod.rs
+++ b/src/exec/mod.rs
@@ -11,7 +11,7 @@ use std::rc::Rc;
use crate::diag::Pass;
use crate::eval::{ExprMap, TemplateFunc, TemplateNode, TemplateValue, Value};
use crate::geom::{Dir, Gen};
-use crate::layout::{self, FixedNode, StackChild, StackNode};
+use crate::layout::{self, StackChild, StackNode};
use crate::pretty::pretty;
use crate::syntax;
@@ -127,12 +127,7 @@ fn exec_item(ctx: &mut ExecContext, label: String, body: &syntax::Tree, map: &Ex
],
};
- ctx.push(FixedNode {
- width: None,
- height: None,
- child: stack.into(),
- });
-
+ ctx.push_into_stack(stack);
ctx.parbreak();
}
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs
index 7c28e8e5..233c27f3 100644
--- a/src/layout/fixed.rs
+++ b/src/layout/fixed.rs
@@ -19,8 +19,8 @@ impl Layout for FixedNode {
self.height.map_or(current.height, |h| h.resolve(base.height)),
);
- let fixed = Spec::new(self.width.is_some(), self.height.is_some());
- let regions = Regions::one(size, fixed);
+ let expand = Spec::new(self.width.is_some(), self.height.is_some());
+ let regions = Regions::one(size, expand);
self.child.layout(ctx, &regions)
}
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 9d8549e6..4153fc3c 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -64,8 +64,8 @@ impl PageRun {
// When one of the lengths is infinite the page fits its content along
// that axis.
let Size { width, height } = self.size;
- let fixed = Spec::new(width.is_finite(), height.is_finite());
- let regions = Regions::repeat(self.size, fixed);
+ let expand = Spec::new(width.is_finite(), height.is_finite());
+ let regions = Regions::repeat(self.size, expand);
self.child.layout(ctx, &regions)
}
}
@@ -214,34 +214,34 @@ pub struct Regions {
pub backlog: Vec<Size>,
/// The final region that is repeated once the backlog is drained.
pub last: Option<Size>,
- /// Whether layouting into these regions should produce frames of the exact
- /// size of `current` instead of shrinking to fit the content.
+ /// Whether nodes should expand to fill the regions instead of shrinking to
+ /// fit the content.
///
/// This property is only handled by nodes that have the ability to control
/// their own size.
- pub fixed: Spec<bool>,
+ pub expand: Spec<bool>,
}
impl Regions {
/// Create a new region sequence with exactly one region.
- pub fn one(size: Size, fixed: Spec<bool>) -> Self {
+ pub fn one(size: Size, expand: Spec<bool>) -> Self {
Self {
current: size,
base: size,
backlog: vec![],
last: None,
- fixed,
+ expand,
}
}
/// Create a new sequence of same-size regions that repeats indefinitely.
- pub fn repeat(size: Size, fixed: Spec<bool>) -> Self {
+ pub fn repeat(size: Size, expand: Spec<bool>) -> Self {
Self {
current: size,
base: size,
backlog: vec![],
last: Some(size),
- fixed,
+ expand,
}
}
@@ -255,7 +255,7 @@ impl Regions {
base: f(self.base),
backlog: self.backlog.iter().copied().map(|s| f(s)).collect(),
last: self.last.map(f),
- fixed: self.fixed,
+ expand: self.expand,
}
}
diff --git a/src/layout/par.rs b/src/layout/par.rs
index d51bad3e..24185458 100644
--- a/src/layout/par.rs
+++ b/src/layout/par.rs
@@ -92,7 +92,7 @@ impl Debug for ParChild {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Spacing(amount) => write!(f, "Spacing({:?})", amount),
- Self::Text(text, _, align) => write!(f, "Text({:?}, {:?})", text, align),
+ Self::Text(text, align, _) => write!(f, "Text({:?}, {:?})", text, align),
Self::Any(any, align) => {
f.debug_tuple("Any").field(any).field(align).finish()
}
@@ -304,7 +304,7 @@ impl<'a> LineStack<'a> {
}
fn finish_region(&mut self, ctx: &LayoutContext) {
- if self.regions.fixed.horizontal {
+ if self.regions.expand.horizontal {
self.size.width = self.regions.current.width;
}
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index e4c0708d..7049f60c 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -44,6 +44,8 @@ struct StackLayouter<'a> {
stack: &'a StackNode,
/// The axis of the main direction.
main: SpecAxis,
+ /// Whether the stack should expand to fill the region.
+ expand: Spec<bool>,
/// The region to layout into.
regions: Regions,
/// Offset, alignment and frame for all children that fit into the current
@@ -62,19 +64,27 @@ struct StackLayouter<'a> {
impl<'a> StackLayouter<'a> {
fn new(stack: &'a StackNode, mut regions: Regions) -> Self {
+ let main = stack.dirs.main.axis();
+ let full = regions.current;
+ let expand = regions.expand;
+
+ // Disable expansion on the main axis for children.
+ *regions.expand.get_mut(main) = false;
+
if let Some(aspect) = stack.aspect {
regions.apply_aspect_ratio(aspect);
}
Self {
stack,
- main: stack.dirs.main.axis(),
+ main,
+ expand,
+ regions,
finished: vec![],
frames: vec![],
- full: regions.current,
+ full,
used: Gen::zero(),
ruler: Align::Start,
- regions,
}
}
@@ -138,13 +148,13 @@ impl<'a> StackLayouter<'a> {
fn finish_region(&mut self) {
let used = self.used.to_size(self.main);
- let fixed = self.regions.fixed;
+ let expand = self.expand;
// Determine the stack's size dependening on whether the region is
// fixed.
let mut stack_size = Size::new(
- if fixed.horizontal { self.full.width } else { used.width },
- if fixed.vertical { self.full.height } else { used.height },
+ if expand.horizontal { self.full.width } else { used.width },
+ if expand.vertical { self.full.height } else { used.height },
);
// Make sure the stack's size satisfies the aspect ratio.
diff --git a/src/library/grid.rs b/src/library/grid.rs
index cfe46bf4..79070e79 100644
--- a/src/library/grid.rs
+++ b/src/library/grid.rs
@@ -47,7 +47,7 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
.map(|child| ctx.exec_template_stack(child).into())
.collect();
- ctx.push(GridNode {
+ ctx.push_into_stack(GridNode {
column_dir: column_dir.unwrap_or(ctx.state.lang.dir),
children,
tracks: Gen::new(columns.clone(), rows.clone()),
diff --git a/src/library/image.rs b/src/library/image.rs
index 7fabfe35..e926b955 100644
--- a/src/library/image.rs
+++ b/src/library/image.rs
@@ -33,7 +33,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
Value::template("image", move |ctx| {
if let Some(node) = node {
- ctx.push(node);
+ ctx.push_into_par(node);
}
})
}
diff --git a/src/library/pad.rs b/src/library/pad.rs
index 6b944c9c..5fa56567 100644
--- a/src/library/pad.rs
+++ b/src/library/pad.rs
@@ -32,6 +32,6 @@ pub fn pad(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
Value::template("pad", move |ctx| {
let child = ctx.exec_template_stack(&body).into();
- ctx.push(PadNode { padding, child });
+ ctx.push_into_stack(PadNode { padding, child });
})
}
diff --git a/src/library/shapes.rs b/src/library/shapes.rs
index 287b1c10..3c6f801d 100644
--- a/src/library/shapes.rs
+++ b/src/library/shapes.rs
@@ -67,13 +67,13 @@ fn rect_impl(
let fixed = FixedNode { width, height, child: stack.into() };
if let Some(color) = fill {
- ctx.push(BackgroundNode {
+ ctx.push_into_par(BackgroundNode {
shape: BackgroundShape::Rect,
fill: Fill::Color(color),
child: fixed.into(),
});
} else {
- ctx.push(fixed);
+ ctx.push_into_par(fixed);
}
})
}
@@ -151,13 +151,13 @@ fn ellipse_impl(
};
if let Some(color) = fill {
- ctx.push(BackgroundNode {
+ ctx.push_into_par(BackgroundNode {
shape: BackgroundShape::Ellipse,
fill: Fill::Color(color),
child: fixed.into(),
});
} else {
- ctx.push(fixed);
+ ctx.push_into_par(fixed);
}
})
}
diff --git a/src/library/stack.rs b/src/library/stack.rs
index 03bdc6a1..672cfbef 100644
--- a/src/library/stack.rs
+++ b/src/library/stack.rs
@@ -31,7 +31,7 @@ pub fn stack(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
})
.collect();
- ctx.push(StackNode {
+ ctx.push_into_stack(StackNode {
dirs: Gen::new(ctx.state.lang.dir, dir),
aspect: None,
children,
diff --git a/tests/ref/layout/fixed.png b/tests/ref/layout/fixed.png
index e07e15e8..5ee9c4ff 100644
--- a/tests/ref/layout/fixed.png
+++ b/tests/ref/layout/fixed.png
Binary files differ
diff --git a/tests/ref/layout/pad.png b/tests/ref/layout/pad.png
index 0bf0adde..645c8db4 100644
--- a/tests/ref/layout/pad.png
+++ b/tests/ref/layout/pad.png
Binary files differ
diff --git a/tests/typ/layout/fixed.typ b/tests/typ/layout/fixed.typ
index bcb88fe1..51646eaa 100644
--- a/tests/typ/layout/fixed.typ
+++ b/tests/typ/layout/fixed.typ
@@ -1,14 +1,14 @@
-// Test shrink-to-fit vs fixed.
+// Test shrink-to-fit vs expand.
---
#let right(body) = align(right, body)
#let pad(body) = pad(left: 10pt, right: 10pt, body)
-// Top-level paragraph fills page, boxed paragraph only when width is fixed.
+// Top-level paragraph fills page, boxed paragraph only when the width is set.
L #right[R] \
#rect(width: 50pt)[L #right[R]] \
#rect[L #right[R]]
// Pad inherits expansion behaviour.
-#pad[PL #right[PR]] \
#rect(pad[PL #right[PR]])
+#pad[PL #right[PR]]
diff --git a/tests/typ/layout/pad.typ b/tests/typ/layout/pad.typ
index 3726ce53..8c23549a 100644
--- a/tests/typ/layout/pad.typ
+++ b/tests/typ/layout/pad.typ
@@ -5,9 +5,19 @@
#pad(left: 10pt, [Indented!])
// All sides together.
-#rect(fill: conifer,
- pad(10pt, right: 20pt,
- rect(width: 20pt, height: 20pt, fill: #eb5278)))
+#rect(fill: conifer)[
+ #pad(10pt, right: 20pt)[
+ #rect(width: 20pt, height: 20pt, fill: #eb5278)
+ ]
+]
// Error: 14-24 missing argument: body
Hi #rect(pad(left: 10pt)) there
+
+---
+// Test that the pad node doesn't consume the whole region.
+
+#page(width: 4cm, height: 5cm)
+#align(left)[Before]
+#pad(10pt, image("../../res/tiger.jpg"))
+#align(right)[After]