Push some nodes directly into the stack
This commit is contained in:
parent
4dbd9285c9
commit
4017b5a9f6
@ -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();
|
||||
|
@ -74,17 +74,6 @@ 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 a word space into the active paragraph.
|
||||
pub fn push_word_space(&mut self) {
|
||||
self.stack.par.push_soft(self.make_text_node(" "));
|
||||
}
|
||||
|
||||
/// Push text into the active paragraph.
|
||||
///
|
||||
/// The text is split into lines at newlines.
|
||||
@ -92,11 +81,30 @@ impl ExecContext {
|
||||
self.stack.par.push(self.make_text_node(text));
|
||||
}
|
||||
|
||||
/// Push spacing into paragraph or stack depending on `axis`.
|
||||
/// Push a word space into the active paragraph.
|
||||
pub fn push_word_space(&mut self) {
|
||||
self.stack.par.push_soft(self.make_text_node(" "));
|
||||
}
|
||||
|
||||
/// 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 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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, ®ions)
|
||||
}
|
||||
}
|
||||
|
@ -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, ®ions)
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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()),
|
||||
|
@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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 });
|
||||
})
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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,
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 43 KiB |
@ -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]]
|
||||
|
@ -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]
|
||||
|
Loading…
x
Reference in New Issue
Block a user