summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-03-11 10:48:29 +0100
committerLaurenz <laurmaedje@gmail.com>2021-03-11 10:48:29 +0100
commitc1b1dbcc0925ba1730fabbfbca3c8b99831c5561 (patch)
tree6e4cb30753729c699bd899a7f2ec352e276beee8 /src
parent4e5f85aa4ac0d6b51323bb2a6e1fbd3f4f46babb (diff)
Better expansion behaviour 🐪
This makes expansion behaviour inheritable by placing it into the area and passing it down during layouting instead of computing some approximation of what we want during execution.
Diffstat (limited to 'src')
-rw-r--r--src/exec/context.rs20
-rw-r--r--src/exec/mod.rs19
-rw-r--r--src/exec/state.rs6
-rw-r--r--src/layout/fixed.rs8
-rw-r--r--src/layout/mod.rs15
-rw-r--r--src/layout/pad.rs1
-rw-r--r--src/layout/par.rs21
-rw-r--r--src/layout/stack.rs12
-rw-r--r--src/library/pad.rs3
-rw-r--r--src/library/page.rs4
-rw-r--r--src/library/shapes.rs6
11 files changed, 51 insertions, 64 deletions
diff --git a/src/exec/context.rs b/src/exec/context.rs
index d491a251..4d2047a6 100644
--- a/src/exec/context.rs
+++ b/src/exec/context.rs
@@ -7,7 +7,7 @@ use super::*;
use crate::diag::{Diag, DiagSet};
use crate::geom::{ChildAlign, Dir, Gen, LayoutDirs, Length, Linear, Sides, Size};
use crate::layout::{
- Expansion, Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree,
+ Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree,
};
use crate::parse::is_newline;
@@ -105,18 +105,18 @@ impl<'a> ExecContext<'a> {
}
}
- /// Execute the body of a function and return the result as a stack node.
- pub fn exec_body(&mut self, body: &ValueTemplate, expand: Spec<Expansion>) -> Node {
+ /// Execute a template and return the result as a stack node.
+ pub fn exec(&mut self, template: &ValueTemplate) -> Node {
let dirs = self.state.dirs;
let align = self.state.align;
self.start_group(ContentGroup);
self.start_par_group();
- body.exec(self);
+ template.exec(self);
self.end_par_group();
let children = self.end_group::<ContentGroup>().1;
- NodeStack { dirs, align, expand, children }.into()
+ NodeStack { dirs, align, children }.into()
}
/// Start a page group based on the active page state.
@@ -128,7 +128,6 @@ impl<'a> ExecContext<'a> {
pub fn start_page_group(&mut self, softness: Softness) {
self.start_group(PageGroup {
size: self.state.page.size,
- expand: self.state.page.expand,
padding: self.state.page.margins(),
dirs: self.state.dirs,
align: self.state.align,
@@ -158,7 +157,6 @@ impl<'a> ExecContext<'a> {
child: NodeStack {
dirs: group.dirs,
align: group.align,
- expand: group.expand,
children,
}
.into(),
@@ -186,13 +184,6 @@ impl<'a> ExecContext<'a> {
self.push(NodePar {
dirs: group.dirs,
align: group.align,
- // FIXME: This is a hack and should be superseded by something
- // better.
- cross_expansion: if self.groups.len() <= 1 {
- Expansion::Fill
- } else {
- Expansion::Fit
- },
line_spacing: group.line_spacing,
children,
});
@@ -306,7 +297,6 @@ pub enum Softness {
#[derive(Debug)]
struct PageGroup {
size: Size,
- expand: Spec<Expansion>,
padding: Sides<Linear>,
dirs: LayoutDirs,
align: ChildAlign,
diff --git a/src/exec/mod.rs b/src/exec/mod.rs
index ea2c90f4..79ad81e7 100644
--- a/src/exec/mod.rs
+++ b/src/exec/mod.rs
@@ -11,8 +11,7 @@ use std::rc::Rc;
use crate::diag::Pass;
use crate::env::Env;
use crate::eval::{ExprMap, TemplateFunc, TemplateNode, Value, ValueTemplate};
-use crate::geom::Spec;
-use crate::layout::{self, Expansion, NodeSpacing, NodeStack};
+use crate::layout::{self, NodeFixed, NodeSpacing, NodeStack};
use crate::pretty::pretty;
use crate::syntax::*;
@@ -120,11 +119,17 @@ impl Exec for NodeRaw {
ctx.apply_parbreak();
}
- ctx.push(NodeStack {
- dirs: ctx.state.dirs,
- align: ctx.state.align,
- expand: Spec::uniform(Expansion::Fit),
- children,
+ // This is wrapped in a fixed node to make sure the stack fits to its
+ // content instead of filling the available area.
+ ctx.push(NodeFixed {
+ width: None,
+ height: None,
+ child: NodeStack {
+ dirs: ctx.state.dirs,
+ align: ctx.state.align,
+ children,
+ }
+ .into(),
});
if self.block {
diff --git a/src/exec/state.rs b/src/exec/state.rs
index 416b5d08..3293662a 100644
--- a/src/exec/state.rs
+++ b/src/exec/state.rs
@@ -3,9 +3,8 @@ use std::rc::Rc;
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
use crate::geom::{
- Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size, Spec,
+ Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size,
};
-use crate::layout::Expansion;
use crate::paper::{Paper, PaperClass, PAPER_A4};
/// The evaluation state.
@@ -42,8 +41,6 @@ pub struct PageState {
pub class: PaperClass,
/// The width and height of the page.
pub size: Size,
- /// Whether the expand the pages to the `size` or to fit the content.
- pub expand: Spec<Expansion>,
/// The amount of white space on each side of the page. If a side is set to
/// `None`, the default for the paper class is used.
pub margins: Sides<Option<Linear>>,
@@ -55,7 +52,6 @@ impl PageState {
Self {
class: paper.class,
size: paper.size(),
- expand: Spec::uniform(Expansion::Fill),
margins: Sides::uniform(None),
}
}
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs
index 60dbe4d6..a9554a07 100644
--- a/src/layout/fixed.rs
+++ b/src/layout/fixed.rs
@@ -20,7 +20,13 @@ impl Layout for NodeFixed {
self.height.map(|h| h.resolve(full.height)).unwrap_or(current.height),
);
- let areas = Areas::once(size);
+ let fill_if = |cond| if cond { Expand::Fill } else { Expand::Fit };
+ let expand = Spec::new(
+ fill_if(self.width.is_some()),
+ fill_if(self.height.is_some()),
+ );
+
+ let areas = Areas::once(size, expand);
self.child.layout(ctx, &areas)
}
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 622b0363..2e6a39ff 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -55,7 +55,7 @@ pub struct NodePages {
impl NodePages {
/// Layout the page run.
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
- let areas = Areas::repeat(self.size);
+ let areas = Areas::repeat(self.size, Spec::uniform(Expand::Fill));
let layouted = self.child.layout(ctx, &areas);
layouted.into_frames()
}
@@ -85,26 +85,31 @@ pub struct Areas {
pub backlog: Vec<Size>,
/// The final area that is repeated when the backlog is empty.
pub last: Option<Size>,
+ /// Whether the frames resulting from layouting into this areas should be
+ /// shrunk to fit their content or expanded to fill the area.
+ pub expand: Spec<Expand>,
}
impl Areas {
/// Create a new length-1 sequence of areas with just one `area`.
- pub fn once(size: Size) -> Self {
+ pub fn once(size: Size, expand: Spec<Expand>) -> Self {
Self {
current: size,
full: size,
backlog: vec![],
last: None,
+ expand,
}
}
/// Create a new sequence of areas that repeats `area` indefinitely.
- pub fn repeat(size: Size) -> Self {
+ pub fn repeat(size: Size, expand: Spec<Expand>) -> Self {
Self {
current: size,
full: size,
backlog: vec![],
last: Some(size),
+ expand,
}
}
@@ -129,14 +134,14 @@ impl Areas {
/// Whether to expand or shrink a node along an axis.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum Expansion {
+pub enum Expand {
/// Fit the content.
Fit,
/// Fill the available space.
Fill,
}
-impl Expansion {
+impl Expand {
/// Resolve the expansion to either the `fit` or `fill` length.
///
/// Prefers `fit` if `fill` is infinite.
diff --git a/src/layout/pad.rs b/src/layout/pad.rs
index 425fa41b..02adebeb 100644
--- a/src/layout/pad.rs
+++ b/src/layout/pad.rs
@@ -37,6 +37,7 @@ fn shrink(areas: &Areas, padding: Sides<Linear>) -> Areas {
full: shrink(areas.full),
backlog: areas.backlog.iter().copied().map(shrink).collect(),
last: areas.last.map(shrink),
+ expand: areas.expand,
}
}
diff --git a/src/layout/par.rs b/src/layout/par.rs
index 45494dec..9a5d26dd 100644
--- a/src/layout/par.rs
+++ b/src/layout/par.rs
@@ -8,8 +8,6 @@ pub struct NodePar {
/// The children are placed in lines along the `cross` direction. The lines
/// are stacked along the `main` direction.
pub dirs: LayoutDirs,
- /// Whether to expand the cross axis to fill the area or to fit the content.
- pub cross_expansion: Expansion,
/// The spacing to insert after each line.
pub line_spacing: Length,
/// The nodes to be arranged in a paragraph.
@@ -42,11 +40,11 @@ impl From<NodePar> for NodeAny {
}
}
-struct ParLayouter<'a> {
- par: &'a NodePar,
+struct ParLayouter {
main: SpecAxis,
cross: SpecAxis,
dirs: LayoutDirs,
+ line_spacing: Length,
areas: Areas,
finished: Vec<Frame>,
lines: Vec<(Length, Frame, Align)>,
@@ -56,13 +54,13 @@ struct ParLayouter<'a> {
line_ruler: Align,
}
-impl<'a> ParLayouter<'a> {
- fn new(par: &'a NodePar, areas: Areas) -> Self {
+impl ParLayouter {
+ fn new(par: &NodePar, areas: Areas) -> Self {
Self {
- par,
main: par.dirs.main.axis(),
cross: par.dirs.cross.axis(),
dirs: par.dirs,
+ line_spacing: par.line_spacing,
areas,
finished: vec![],
lines: vec![],
@@ -134,13 +132,12 @@ impl<'a> ParLayouter<'a> {
}
fn finish_line(&mut self) {
+ let expand = self.areas.expand.switch(self.dirs);
let full_size = {
let full = self.areas.full.switch(self.dirs);
Gen::new(
self.line_size.main,
- self.par
- .cross_expansion
- .resolve(self.line_size.cross.min(full.cross), full.cross),
+ expand.cross.resolve(self.line_size.cross.min(full.cross), full.cross),
)
};
@@ -165,8 +162,8 @@ impl<'a> ParLayouter<'a> {
// Add line spacing, but only between lines.
if !self.lines.is_empty() {
- self.lines_size.main += self.par.line_spacing;
- *self.areas.current.get_mut(self.main) -= self.par.line_spacing;
+ self.lines_size.main += self.line_spacing;
+ *self.areas.current.get_mut(self.main) -= self.line_spacing;
}
// Update metrics of the whole paragraph.
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 7d1d7a12..73bd49c1 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -10,8 +10,6 @@ pub struct NodeStack {
pub dirs: LayoutDirs,
/// How to align this stack in _its_ parent.
pub align: ChildAlign,
- /// Whether to expand the axes to fill the area or to fit the content.
- pub expand: Spec<Expansion>,
/// The nodes to be stacked.
pub children: Vec<Node>,
}
@@ -40,8 +38,7 @@ impl From<NodeStack> for NodeAny {
}
}
-struct StackLayouter<'a> {
- stack: &'a NodeStack,
+struct StackLayouter {
main: SpecAxis,
dirs: LayoutDirs,
areas: Areas,
@@ -51,10 +48,9 @@ struct StackLayouter<'a> {
ruler: Align,
}
-impl<'a> StackLayouter<'a> {
- fn new(stack: &'a NodeStack, areas: Areas) -> Self {
+impl StackLayouter {
+ fn new(stack: &NodeStack, areas: Areas) -> Self {
Self {
- stack,
main: stack.dirs.main.axis(),
dirs: stack.dirs,
areas,
@@ -97,7 +93,7 @@ impl<'a> StackLayouter<'a> {
fn finish_area(&mut self) {
let full_size = {
- let expand = self.stack.expand.switch(self.dirs);
+ let expand = self.areas.expand.switch(self.dirs);
let full = self.areas.full.switch(self.dirs);
Gen::new(
expand.main.resolve(self.used.main.min(full.main), full.main),
diff --git a/src/library/pad.rs b/src/library/pad.rs
index f9e4386d..0bf93093 100644
--- a/src/library/pad.rs
+++ b/src/library/pad.rs
@@ -29,8 +29,7 @@ pub fn pad(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
Value::template("pad", move |ctx| {
let snapshot = ctx.state.clone();
- let expand = Spec::uniform(Expansion::Fit);
- let child = ctx.exec_body(&body, expand);
+ let child = ctx.exec(&body);
ctx.push(NodePad { padding, child });
ctx.state = snapshot;
diff --git a/src/library/page.rs b/src/library/page.rs
index 1fdf4d3f..4ab92b31 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -45,19 +45,16 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
if let Some(paper) = paper {
ctx.state.page.class = paper.class;
ctx.state.page.size = paper.size();
- ctx.state.page.expand = Spec::uniform(Expansion::Fill);
}
if let Some(width) = width {
ctx.state.page.class = PaperClass::Custom;
ctx.state.page.size.width = width;
- ctx.state.page.expand.horizontal = Expansion::Fill;
}
if let Some(height) = height {
ctx.state.page.class = PaperClass::Custom;
ctx.state.page.size.height = height;
- ctx.state.page.expand.vertical = Expansion::Fill;
}
if let Some(margins) = margins {
@@ -83,7 +80,6 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
if flip.unwrap_or(false) {
let page = &mut ctx.state.page;
std::mem::swap(&mut page.size.width, &mut page.size.height);
- std::mem::swap(&mut page.expand.horizontal, &mut page.expand.vertical);
}
ctx.set_dirs(Gen::new(main, cross));
diff --git a/src/library/shapes.rs b/src/library/shapes.rs
index 5e638c01..9c466318 100644
--- a/src/library/shapes.rs
+++ b/src/library/shapes.rs
@@ -26,17 +26,13 @@ pub fn box_(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
let color = args.get(ctx, "color");
let body = args.find::<ValueTemplate>(ctx).unwrap_or_default();
- let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
- let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
-
Value::template("box", move |ctx| {
let snapshot = ctx.state.clone();
ctx.set_dirs(Gen::new(main, cross));
- let child = ctx.exec_body(&body, expand);
+ let child = ctx.exec(&body);
let fixed = NodeFixed { width, height, child };
-
if let Some(color) = color {
ctx.push(NodeBackground {
fill: Fill::Color(color),