Revise block node contract

Frames produced by block nodes are now always treated as exactly one per
given region and a frame must not be larger than its respective region.
Any overflow must be handled internally. This means that stack and grid
don't need to search for fitting regions anymore, since the child has
already does that for them. This commit further moves stack spacing into
a new `SpacingNode`.
This commit is contained in:
Laurenz 2021-10-16 11:39:26 +02:00
parent 1e74f7c407
commit 6690bc2354
15 changed files with 164 additions and 160 deletions

View File

@ -6,7 +6,7 @@ use std::rc::Rc;
use super::Str; use super::Str;
use crate::diag::StrResult; use crate::diag::StrResult;
use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size}; use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size, SpecAxis};
use crate::layout::{ use crate::layout::{
Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild,
StackNode, StackNode,
@ -309,7 +309,7 @@ impl Builder {
fn parbreak(&mut self) { fn parbreak(&mut self) {
let amount = self.style.par_spacing(); let amount = self.style.par_spacing();
self.stack.finish_par(&self.style); self.stack.finish_par(&self.style);
self.stack.push_soft(StackChild::Spacing(amount.into())); self.stack.push_soft(StackChild::spacing(amount, SpecAxis::Vertical));
} }
/// Apply a forced page break. /// Apply a forced page break.
@ -335,7 +335,7 @@ impl Builder {
/// Push a block node into the active stack, finishing the active paragraph. /// Push a block node into the active stack, finishing the active paragraph.
fn block(&mut self, node: impl Into<LayoutNode>) { fn block(&mut self, node: impl Into<LayoutNode>) {
self.parbreak(); self.parbreak();
self.stack.push(StackChild::Any(node.into(), self.style.aligns.block)); self.stack.push(StackChild::new(node, self.style.aligns.block));
self.parbreak(); self.parbreak();
} }
@ -344,7 +344,7 @@ impl Builder {
match axis { match axis {
GenAxis::Block => { GenAxis::Block => {
self.stack.finish_par(&self.style); self.stack.finish_par(&self.style);
self.stack.push_hard(StackChild::Spacing(amount)); self.stack.push_hard(StackChild::spacing(amount, SpecAxis::Vertical));
} }
GenAxis::Inline => { GenAxis::Inline => {
self.stack.par.push_hard(ParChild::Spacing(amount)); self.stack.par.push_hard(ParChild::Spacing(amount));
@ -508,10 +508,8 @@ impl ParBuilder {
fn build(self) -> Option<StackChild> { fn build(self) -> Option<StackChild> {
let Self { align, dir, line_spacing, children, .. } = self; let Self { align, dir, line_spacing, children, .. } = self;
(!children.is_empty()).then(|| { (!children.is_empty())
let node = ParNode { dir, line_spacing, children }; .then(|| StackChild::new(ParNode { dir, line_spacing, children }, align))
StackChild::Any(node.into(), align)
})
} }
} }

View File

@ -2,7 +2,7 @@ use std::rc::Rc;
use super::{Eval, EvalContext, Str, Template, Value}; use super::{Eval, EvalContext, Str, Template, Value};
use crate::diag::TypResult; use crate::diag::TypResult;
use crate::geom::Align; use crate::geom::{Align, SpecAxis};
use crate::layout::{ParChild, ParNode, StackChild, StackNode}; use crate::layout::{ParChild, ParNode, StackChild, StackNode};
use crate::syntax::*; use crate::syntax::*;
use crate::util::BoolExt; use crate::util::BoolExt;
@ -119,9 +119,9 @@ fn walk_item(ctx: &mut EvalContext, label: Str, body: Template) {
StackNode { StackNode {
dir: style.dir, dir: style.dir,
children: vec![ children: vec![
StackChild::Any(label.into(), Align::Start), StackChild::new(label, Align::Start),
StackChild::Spacing((style.text.size / 2.0).into()), StackChild::spacing(style.text.size / 2.0, SpecAxis::Horizontal),
StackChild::Any(body.to_stack(&style).into(), Align::Start), StackChild::new(body.to_stack(&style), Align::Start),
], ],
} }
}); });

View File

@ -98,7 +98,7 @@ impl<T: Debug> Debug for Gen<T> {
} }
/// The two generic layouting axes. /// The two generic layouting axes.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GenAxis { pub enum GenAxis {
/// The axis words and lines are set along. /// The axis words and lines are set along.
Inline, Inline,

View File

@ -76,7 +76,7 @@ impl<T> Get<Side> for Sides<T> {
} }
/// The four sides of objects. /// The four sides of objects.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Side { pub enum Side {
/// The left side. /// The left side.
Left, Left,

View File

@ -101,7 +101,7 @@ impl<T: Debug> Debug for Spec<T> {
} }
/// The two specific layouting axes. /// The two specific layouting axes.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum SpecAxis { pub enum SpecAxis {
/// The horizontal layouting axis. /// The horizontal layouting axis.
Horizontal, Horizontal,

View File

@ -330,18 +330,10 @@ impl<'a> GridLayouter<'a> {
/// Layout the grid row-by-row. /// Layout the grid row-by-row.
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> { fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
let base = self.regions.base.get(self.block);
for y in 0 .. self.rows.len() { for y in 0 .. self.rows.len() {
match self.rows[y] { match self.rows[y] {
TrackSizing::Auto => { TrackSizing::Auto => self.layout_auto_row(ctx, y),
self.layout_auto_row(ctx, y); TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y),
}
TrackSizing::Linear(v) => {
let resolved = v.resolve(base);
let frame = self.layout_single_row(ctx, resolved, y);
self.push_row(ctx, frame);
}
TrackSizing::Fractional(v) => { TrackSizing::Fractional(v) => {
self.fr += v; self.fr += v;
self.constraints.exact.set(self.block, Some(self.full)); self.constraints.exact.set(self.block, Some(self.full));
@ -395,7 +387,7 @@ impl<'a> GridLayouter<'a> {
// Layout into a single region. // Layout into a single region.
if let &[first] = resolved.as_slice() { if let &[first] = resolved.as_slice() {
let frame = self.layout_single_row(ctx, first, y); let frame = self.layout_single_row(ctx, first, y);
self.push_row(ctx, frame); self.push_row(frame);
return; return;
} }
@ -414,14 +406,39 @@ impl<'a> GridLayouter<'a> {
let frames = self.layout_multi_row(ctx, &resolved, y); let frames = self.layout_multi_row(ctx, &resolved, y);
let len = frames.len(); let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() { for (i, frame) in frames.into_iter().enumerate() {
self.push_row(frame);
if i + 1 < len { if i + 1 < len {
self.constraints.exact.set(self.block, Some(self.full)); self.constraints.exact.set(self.block, Some(self.full));
self.finish_region(ctx);
} }
self.push_row(ctx, frame);
} }
} }
/// Layout a row with a fixed size along the block axis. /// Layout a row with linear sizing along the block axis. Such a row cannot
/// break across multiple regions, but it may force a region break.
fn layout_linear_row(&mut self, ctx: &mut LayoutContext, v: Linear, y: usize) {
let base = self.regions.base.get(self.block);
let resolved = v.resolve(base);
let frame = self.layout_single_row(ctx, resolved, y);
// Skip to fitting region.
let length = frame.size.get(self.block);
while !self.regions.current.get(self.block).fits(length)
&& !self.regions.in_full_last()
{
self.constraints.max.set(self.block, Some(self.used.block + length));
self.finish_region(ctx);
// Don't skip multiple regions for gutter and don't push a row.
if y % 2 == 1 {
return;
}
}
self.push_row(frame);
}
/// Layout a row with a fixed size along the block axis and return its frame.
fn layout_single_row( fn layout_single_row(
&self, &self,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
@ -508,19 +525,9 @@ impl<'a> GridLayouter<'a> {
outputs outputs
} }
/// Push a row frame into the current or next fitting region, finishing /// Push a row frame into the current region.
/// regions (including layouting fractional rows) if necessary. fn push_row(&mut self, frame: Frame) {
fn push_row(&mut self, ctx: &mut LayoutContext, frame: Frame) {
let length = frame.size.get(self.block); let length = frame.size.get(self.block);
// Skip to fitting region.
while !self.regions.current.get(self.block).fits(length)
&& !self.regions.in_full_last()
{
self.constraints.max.set(self.block, Some(self.used.block + length));
self.finish_region(ctx);
}
*self.regions.current.get_mut(self.block) -= length; *self.regions.current.get_mut(self.block) -= length;
self.used.block += length; self.used.block += length;
self.lrows.push(Row::Frame(frame)); self.lrows.push(Row::Frame(frame));

View File

@ -23,18 +23,18 @@ impl Layout for ImageNode {
let pixel_size = Spec::new(img.width() as f64, img.height() as f64); let pixel_size = Spec::new(img.width() as f64, img.height() as f64);
let pixel_ratio = pixel_size.x / pixel_size.y; let pixel_ratio = pixel_size.x / pixel_size.y;
let mut constraints = Constraints::new(regions.expand);
constraints.set_base_if_linear(regions.base, Spec::new(self.width, self.height));
let width = self.width.map(|w| w.resolve(regions.base.w)); let width = self.width.map(|w| w.resolve(regions.base.w));
let height = self.height.map(|w| w.resolve(regions.base.h)); let height = self.height.map(|w| w.resolve(regions.base.h));
let mut cts = Constraints::new(regions.expand);
cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height));
let size = match (width, height) { let size = match (width, height) {
(Some(width), Some(height)) => Size::new(width, height), (Some(width), Some(height)) => Size::new(width, height),
(Some(width), None) => Size::new(width, width / pixel_ratio), (Some(width), None) => Size::new(width, width / pixel_ratio),
(None, Some(height)) => Size::new(height * pixel_ratio, height), (None, Some(height)) => Size::new(height * pixel_ratio, height),
(None, None) => { (None, None) => {
constraints.exact.x = Some(regions.current.w); cts.exact.x = Some(regions.current.w);
if regions.current.w.is_finite() { if regions.current.w.is_finite() {
// Fit to width. // Fit to width.
Size::new(regions.current.w, regions.current.w / pixel_ratio) Size::new(regions.current.w, regions.current.w / pixel_ratio)
@ -48,7 +48,7 @@ impl Layout for ImageNode {
let mut frame = Frame::new(size, size.h); let mut frame = Frame::new(size, size.h);
frame.push(Point::zero(), Element::Image(self.id, size)); frame.push(Point::zero(), Element::Image(self.id, size));
vec![frame.constrain(constraints)] vec![frame.constrain(cts)]
} }
} }

View File

@ -11,6 +11,7 @@ mod par;
mod regions; mod regions;
mod shape; mod shape;
mod shaping; mod shaping;
mod spacing;
mod stack; mod stack;
mod tree; mod tree;
@ -25,6 +26,7 @@ pub use par::*;
pub use regions::*; pub use regions::*;
pub use shape::*; pub use shape::*;
pub use shaping::*; pub use shaping::*;
pub use spacing::*;
pub use stack::*; pub use stack::*;
pub use tree::*; pub use tree::*;

View File

@ -46,7 +46,8 @@ impl Layout for ShapeNode {
let mut cts = Constraints::new(regions.expand); let mut cts = Constraints::new(regions.expand);
cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height)); cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height));
// Set exact and base constraint if child is automatically sized. // Set tight exact and base constraints if the child is
// automatically sized since we don't know what the child might do.
if self.width.is_none() { if self.width.is_none() {
cts.exact.x = Some(regions.current.w); cts.exact.x = Some(regions.current.w);
cts.base.x = Some(regions.base.w); cts.base.x = Some(regions.base.w);

View File

@ -48,6 +48,7 @@ pub fn shape<'a>(
/// This type contains owned or borrowed shaped text runs, which can be /// This type contains owned or borrowed shaped text runs, which can be
/// measured, used to reshape substrings more quickly and converted into a /// measured, used to reshape substrings more quickly and converted into a
/// frame. /// frame.
#[derive(Debug, Clone)]
pub struct ShapedText<'a> { pub struct ShapedText<'a> {
/// The text that was shaped. /// The text that was shaped.
pub text: &'a str, pub text: &'a str,
@ -64,7 +65,7 @@ pub struct ShapedText<'a> {
} }
/// A single glyph resulting from shaping. /// A single glyph resulting from shaping.
#[derive(Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ShapedGlyph { pub struct ShapedGlyph {
/// The font face the glyph is contained in. /// The font face the glyph is contained in.
pub face_id: FaceId, pub face_id: FaceId,

50
src/layout/spacing.rs Normal file
View File

@ -0,0 +1,50 @@
use super::*;
/// Spacing between other nodes.
#[derive(Debug)]
#[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct SpacingNode {
/// Which axis to space on.
pub axis: SpecAxis,
/// How much spacing to add.
pub amount: Linear,
}
impl Layout for SpacingNode {
fn layout(
&self,
_: &mut LayoutContext,
regions: &Regions,
) -> Vec<Constrained<Rc<Frame>>> {
let base = regions.base.get(self.axis);
let resolved = self.amount.resolve(base);
let limit = regions.current.get(self.axis);
// Generate constraints.
let mut cts = Constraints::new(regions.expand);
if self.amount.is_relative() {
cts.base.set(self.axis, Some(base));
}
// If the spacing fits into the region, any larger region would also do.
// If it was limited though, any change it region size might lead to
// different results.
if resolved < limit {
cts.min.set(self.axis, Some(resolved));
} else {
cts.exact.set(self.axis, Some(limit));
}
// Create frame with limited spacing size along spacing axis and zero
// extent along the other axis.
let mut size = Size::zero();
size.set(self.axis, resolved.min(limit));
vec![Frame::new(size, size.h).constrain(cts)]
}
}
impl From<SpacingNode> for LayoutNode {
fn from(spacing: SpacingNode) -> Self {
Self::new(spacing)
}
}

View File

@ -12,15 +12,6 @@ pub struct StackNode {
pub children: Vec<StackChild>, pub children: Vec<StackChild>,
} }
/// A child of a stack node.
#[cfg_attr(feature = "layout-cache", derive(Hash))]
pub enum StackChild {
/// Spacing between other nodes.
Spacing(Linear),
/// Any child node and how to align it in the stack.
Any(LayoutNode, Align),
}
impl Layout for StackNode { impl Layout for StackNode {
fn layout( fn layout(
&self, &self,
@ -37,12 +28,31 @@ impl From<StackNode> for LayoutNode {
} }
} }
/// A child of a stack node.
#[cfg_attr(feature = "layout-cache", derive(Hash))]
pub struct StackChild {
/// The node itself.
pub node: LayoutNode,
/// How to align the node along the block axis.
pub align: Align,
}
impl StackChild {
/// Create a new stack child.
pub fn new(node: impl Into<LayoutNode>, align: Align) -> Self {
Self { node: node.into(), align }
}
/// Create a spacing stack child.
pub fn spacing(amount: impl Into<Linear>, axis: SpecAxis) -> Self {
Self::new(SpacingNode { amount: amount.into(), axis }, Align::Start)
}
}
impl Debug for StackChild { impl Debug for StackChild {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { write!(f, "{:?}: ", self.align)?;
Self::Spacing(v) => write!(f, "Spacing({:?})", v), self.node.fmt(f)
Self::Any(node, _) => node.fmt(f),
}
} }
} }
@ -51,7 +61,7 @@ struct StackLayouter<'a> {
/// The stack node to layout. /// The stack node to layout.
stack: &'a StackNode, stack: &'a StackNode,
/// The axis of the block direction. /// The axis of the block direction.
block: SpecAxis, axis: SpecAxis,
/// Whether the stack should expand to fill the region. /// Whether the stack should expand to fill the region.
expand: Spec<bool>, expand: Spec<bool>,
/// The region to layout into. /// The region to layout into.
@ -63,10 +73,6 @@ struct StackLayouter<'a> {
used: Gen<Length>, used: Gen<Length>,
/// The alignment ruler for the current region. /// The alignment ruler for the current region.
ruler: Align, ruler: Align,
/// The constraints for the current region.
constraints: Constraints,
/// Whether the last region can fit all the remaining content.
overflowing: bool,
/// Offset, alignment and frame for all children that fit into the current /// Offset, alignment and frame for all children that fit into the current
/// region. The exact positions are not known yet. /// region. The exact positions are not known yet.
frames: Vec<(Length, Align, Rc<Frame>)>, frames: Vec<(Length, Align, Rc<Frame>)>,
@ -77,23 +83,21 @@ struct StackLayouter<'a> {
impl<'a> StackLayouter<'a> { impl<'a> StackLayouter<'a> {
/// Create a new stack layouter. /// Create a new stack layouter.
fn new(stack: &'a StackNode, mut regions: Regions) -> Self { fn new(stack: &'a StackNode, mut regions: Regions) -> Self {
let block = stack.dir.axis(); let axis = stack.dir.axis();
let full = regions.current; let full = regions.current;
let expand = regions.expand; let expand = regions.expand;
// Disable expansion along the block axis for children. // Disable expansion along the block axis for children.
regions.expand.set(block, false); regions.expand.set(axis, false);
Self { Self {
stack, stack,
block, axis,
expand, expand,
regions, regions,
full, full,
used: Gen::zero(), used: Gen::zero(),
ruler: Align::Start, ruler: Align::Start,
constraints: Constraints::new(expand),
overflowing: false,
frames: vec![], frames: vec![],
finished: vec![], finished: vec![],
} }
@ -102,17 +106,12 @@ impl<'a> StackLayouter<'a> {
/// Layout all children. /// Layout all children.
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> { fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
for child in &self.stack.children { for child in &self.stack.children {
match *child { let frames = child.node.layout(ctx, &self.regions);
StackChild::Spacing(amount) => self.space(amount), let len = frames.len();
StackChild::Any(ref node, align) => { for (i, frame) in frames.into_iter().enumerate() {
let nodes = node.layout(ctx, &self.regions); self.push_frame(frame.item, child.align);
let len = nodes.len(); if i + 1 < len {
for (i, frame) in nodes.into_iter().enumerate() { self.finish_region();
if i + 1 < len {
self.constraints.exact = self.full.to_spec().map(Some);
}
self.push_frame(frame.item, align);
}
} }
} }
} }
@ -121,96 +120,37 @@ impl<'a> StackLayouter<'a> {
self.finished self.finished
} }
/// Add block-axis spacing into the current region. /// Push a frame into the current region.
fn space(&mut self, amount: Linear) {
// Resolve the linear.
let full = self.full.get(self.block);
let resolved = amount.resolve(full);
// Cap the spacing to the remaining available space. This action does
// not directly affect the constraints because of the cap.
let remaining = self.regions.current.get_mut(self.block);
let capped = resolved.min(*remaining);
// Grow our size and shrink the available space in the region.
self.used.block += capped;
*remaining -= capped;
}
/// Push a frame into the current or next fitting region, finishing regions
/// if necessary.
fn push_frame(&mut self, frame: Rc<Frame>, align: Align) { fn push_frame(&mut self, frame: Rc<Frame>, align: Align) {
let size = frame.size.to_gen(self.block);
// Don't allow `Start` after `End` in the same region.
if align < self.ruler {
self.finish_region();
}
// Find a fitting region.
while !self.regions.current.get(self.block).fits(size.block) {
if self.regions.in_full_last() {
self.overflowing = true;
break;
}
self.constraints
.max
.get_mut(self.block)
.set_min(self.used.block + size.block);
self.finish_region();
}
// Shrink available space in the region.
*self.regions.current.get_mut(self.block) -= size.block;
// Grow our size. // Grow our size.
let offset = self.used.block; let offset = self.used.block;
let size = frame.size.to_gen(self.axis);
self.used.block += size.block; self.used.block += size.block;
self.used.inline.set_max(size.inline); self.used.inline.set_max(size.inline);
self.ruler = align; self.ruler = self.ruler.max(align);
// Remember the frame with offset and alignment. // Remember the frame and shrink available space in the region for the
self.frames.push((offset, align, frame)); // following children.
self.frames.push((offset, self.ruler, frame));
*self.regions.current.get_mut(self.axis) -= size.block;
} }
/// Finish the frame for one region. /// Finish the frame for one region.
fn finish_region(&mut self) { fn finish_region(&mut self) {
let expand = self.expand;
let used = self.used.to_size(self.block);
// Determine the stack's size dependening on whether the region expands. // Determine the stack's size dependening on whether the region expands.
let used = self.used.to_size(self.axis);
let size = Size::new( let size = Size::new(
if expand.x { if self.expand.x { self.full.w } else { used.w },
self.constraints.exact.x = Some(self.full.w); if self.expand.y { self.full.h } else { used.h },
self.full.w
} else {
self.constraints.min.x = Some(used.w.min(self.full.w));
used.w
},
if expand.y {
self.constraints.exact.y = Some(self.full.h);
self.full.h
} else {
self.constraints.min.y = Some(used.h.min(self.full.h));
used.h
},
); );
if self.overflowing {
self.constraints.min.y = None;
self.constraints.max.y = None;
self.constraints.exact = self.full.to_spec().map(Some);
}
let mut output = Frame::new(size, size.h); let mut output = Frame::new(size, size.h);
let mut first = true; let mut first = true;
// Place all frames. // Place all frames.
for (offset, align, frame) in self.frames.drain(..) { for (offset, align, frame) in self.frames.drain(..) {
let stack_size = size.to_gen(self.block); let stack_size = size.to_gen(self.axis);
let child_size = frame.size.to_gen(self.block); let child_size = frame.size.to_gen(self.axis);
// Align along the block axis. // Align along the block axis.
let block = align.resolve( let block = align.resolve(
@ -224,7 +164,7 @@ impl<'a> StackLayouter<'a> {
}, },
); );
let pos = Gen::new(Length::zero(), block).to_point(self.block); let pos = Gen::new(Length::zero(), block).to_point(self.axis);
// The baseline of the stack is that of the first frame. // The baseline of the stack is that of the first frame.
if first { if first {
@ -235,11 +175,15 @@ impl<'a> StackLayouter<'a> {
output.push_frame(pos, frame); output.push_frame(pos, frame);
} }
// Generate tight constraints for now.
let mut cts = Constraints::new(self.expand);
cts.exact = self.full.to_spec().map(Some);
cts.base = self.regions.base.to_spec().map(Some);
self.regions.next(); self.regions.next();
self.full = self.regions.current; self.full = self.regions.current;
self.used = Gen::zero(); self.used = Gen::zero();
self.ruler = Align::Start; self.ruler = Align::Start;
self.finished.push(output.constrain(self.constraints)); self.finished.push(output.constrain(cts));
self.constraints = Constraints::new(expand);
} }
} }

View File

@ -103,6 +103,7 @@ impl Layout for LayoutNode {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
if !entry.check(regions) { if !entry.check(regions) {
eprintln!("node: {:#?}", self.node);
eprintln!("regions: {:#?}", regions); eprintln!("regions: {:#?}", regions);
eprintln!( eprintln!(
"constraints: {:#?}", "constraints: {:#?}",

View File

@ -209,7 +209,7 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
} }
let dir = args.named("dir")?.unwrap_or(Dir::TTB); let dir = args.named("dir")?.unwrap_or(Dir::TTB);
let spacing = args.named("spacing")?; let spacing = args.named::<Linear>("spacing")?;
let list: Vec<Child> = args.all().collect(); let list: Vec<Child> = args.all().collect();
Ok(Value::Template(Template::from_block(move |style| { Ok(Value::Template(Template::from_block(move |style| {
@ -220,16 +220,16 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
for child in &list { for child in &list {
match child { match child {
Child::Spacing(v) => { Child::Spacing(v) => {
children.push(StackChild::Spacing(*v)); children.push(StackChild::spacing(*v, dir.axis()));
delayed = None; delayed = None;
} }
Child::Any(template) => { Child::Any(template) => {
if let Some(v) = delayed { if let Some(v) = delayed {
children.push(StackChild::Spacing(v)); children.push(StackChild::spacing(v, dir.axis()));
} }
let node = template.to_stack(style).into(); let node = template.to_stack(style);
children.push(StackChild::Any(node, style.aligns.block)); children.push(StackChild::new(node, style.aligns.block));
delayed = spacing; delayed = spacing;
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB