diff --git a/src/eval/template.rs b/src/eval/template.rs index 307bff2a2..e9cbbfcf0 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -6,7 +6,7 @@ use std::ops::{Add, AddAssign}; use std::rc::Rc; use crate::diag::StrResult; -use crate::geom::{Align, Dir, GenAxis, Length, Linear, Paint, Sides, Size}; +use crate::geom::{Align, Dir, Length, Linear, Paint, Sides, Size, SpecAxis}; use crate::layout::{Layout, PackedNode}; use crate::library::{ Decoration, DocumentNode, FlowChild, FlowNode, PageNode, ParChild, ParNode, @@ -33,7 +33,7 @@ enum TemplateNode { /// Plain text. Text(EcoString), /// Spacing. - Spacing(GenAxis, Spacing), + Spacing(SpecAxis, Spacing), /// A decorated template. Decorated(Decoration, Template), /// An inline node builder. @@ -108,7 +108,7 @@ impl Template { } /// Add spacing along an axis. - pub fn spacing(&mut self, axis: GenAxis, spacing: Spacing) { + pub fn spacing(&mut self, axis: SpecAxis, spacing: Spacing) { self.make_mut().push(TemplateNode::Spacing(axis, spacing)); } @@ -349,13 +349,13 @@ impl Builder { } /// Push spacing into the active paragraph or flow depending on the `axis`. - fn spacing(&mut self, axis: GenAxis, spacing: Spacing) { + fn spacing(&mut self, axis: SpecAxis, spacing: Spacing) { match axis { - GenAxis::Block => { + SpecAxis::Vertical => { self.flow.finish_par(&self.style); self.flow.push_hard(FlowChild::Spacing(spacing)); } - GenAxis::Inline => { + SpecAxis::Horizontal => { self.flow.par.push_hard(ParChild::Spacing(spacing)); } } diff --git a/src/frame.rs b/src/frame.rs index 5b1c36ce1..ec10fe964 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use serde::{Deserialize, Serialize}; use crate::font::FaceId; -use crate::geom::{Em, Length, Paint, Path, Point, Size, Transform}; +use crate::geom::{Align, Em, Length, Paint, Path, Point, Size, Spec, Transform}; use crate::image::ImageId; /// A finished layout with elements at fixed positions. @@ -23,9 +23,9 @@ pub struct Frame { impl Frame { /// Create a new, empty frame. #[track_caller] - pub fn new(size: Size, baseline: Length) -> Self { + pub fn new(size: Size) -> Self { assert!(size.is_finite()); - Self { size, baseline, elements: vec![] } + Self { size, baseline: size.h, elements: vec![] } } /// Add an element at a position in the background. @@ -55,12 +55,46 @@ impl Frame { } } - /// Move all elements in the frame by an offset. + /// Resize the frame to a new size, distributing new space according to the + /// given alignments. + pub fn resize(&mut self, new: Size, aligns: Spec) { + let offset = Point::new( + aligns.x.resolve(new.w - self.size.w), + aligns.y.resolve(new.h - self.size.h), + ); + self.size = new; + self.baseline += offset.y; + self.translate(offset); + } + + /// Move the contents of the frame by an offset. pub fn translate(&mut self, offset: Point) { for (point, _) in &mut self.elements { *point += offset; } } + + /// Arbitrarily transform the contents of the frame. + pub fn transform(&mut self, transform: Transform) { + self.group(|g| g.transform = transform); + } + + /// Clip the contents of a frame to its size. + pub fn clip(&mut self) { + self.group(|g| g.clips = true); + } + + /// Wrap the frame's contents in a group and modify that group with `f`. + pub fn group(&mut self, f: F) + where + F: FnOnce(&mut Group), + { + let mut wrapper = Frame { elements: vec![], ..*self }; + let mut group = Group::new(Rc::new(std::mem::take(self))); + f(&mut group); + wrapper.push(Point::zero(), Element::Group(group)); + *self = wrapper; + } } impl Debug for Frame { @@ -116,18 +150,6 @@ impl Group { clips: false, } } - - /// Set the group's transform. - pub fn transform(mut self, transform: Transform) -> Self { - self.transform = transform; - self - } - - /// Set whether the group should be a clipping boundary. - pub fn clips(mut self, clips: bool) -> Self { - self.clips = clips; - self - } } /// A run of shaped text. diff --git a/src/geom/gen.rs b/src/geom/gen.rs index e770f80de..5232139b1 100644 --- a/src/geom/gen.rs +++ b/src/geom/gen.rs @@ -1,18 +1,18 @@ use super::*; -/// A container with an inline and a block component. +/// A container with a main and cross component. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] pub struct Gen { - /// The inline component. - pub inline: T, - /// The block component. - pub block: T, + /// The main component. + pub cross: T, + /// The cross component. + pub main: T, } impl Gen { /// Create a new instance from the two components. - pub const fn new(inline: T, block: T) -> Self { - Self { inline, block } + pub const fn new(cross: T, main: T) -> Self { + Self { cross, main } } /// Create a new instance with two equal components. @@ -20,7 +20,7 @@ impl Gen { where T: Clone, { - Self { inline: value.clone(), block: value } + Self { cross: value.clone(), main: value } } /// Maps the individual fields with `f`. @@ -28,17 +28,14 @@ impl Gen { where F: FnMut(T) -> U, { - Gen { - inline: f(self.inline), - block: f(self.block), - } + Gen { cross: f(self.cross), main: f(self.main) } } /// Convert to the specific representation, given the current block axis. - pub fn to_spec(self, block: SpecAxis) -> Spec { - match block { - SpecAxis::Horizontal => Spec::new(self.block, self.inline), - SpecAxis::Vertical => Spec::new(self.inline, self.block), + pub fn to_spec(self, main: SpecAxis) -> Spec { + match main { + SpecAxis::Horizontal => Spec::new(self.main, self.cross), + SpecAxis::Vertical => Spec::new(self.cross, self.main), } } } @@ -47,19 +44,19 @@ impl Gen { /// The zero value. pub fn zero() -> Self { Self { - inline: Length::zero(), - block: Length::zero(), + cross: Length::zero(), + main: Length::zero(), } } /// Convert to a point. - pub fn to_point(self, block: SpecAxis) -> Point { - self.to_spec(block).to_point() + pub fn to_point(self, main: SpecAxis) -> Point { + self.to_spec(main).to_point() } /// Convert to a size. - pub fn to_size(self, block: SpecAxis) -> Size { - self.to_spec(block).to_size() + pub fn to_size(self, main: SpecAxis) -> Size { + self.to_spec(main).to_size() } } @@ -67,8 +64,8 @@ impl Gen> { /// Unwrap the individual fields. pub fn unwrap_or(self, other: Gen) -> Gen { Gen { - inline: self.inline.unwrap_or(other.inline), - block: self.block.unwrap_or(other.block), + cross: self.cross.unwrap_or(other.cross), + main: self.main.unwrap_or(other.main), } } } @@ -78,40 +75,40 @@ impl Get for Gen { fn get(self, axis: GenAxis) -> T { match axis { - GenAxis::Inline => self.inline, - GenAxis::Block => self.block, + GenAxis::Cross => self.cross, + GenAxis::Main => self.main, } } fn get_mut(&mut self, axis: GenAxis) -> &mut T { match axis { - GenAxis::Inline => &mut self.inline, - GenAxis::Block => &mut self.block, + GenAxis::Cross => &mut self.cross, + GenAxis::Main => &mut self.main, } } } impl Debug for Gen { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Gen({:?}, {:?})", self.inline, self.block) + write!(f, "Gen({:?}, {:?})", self.cross, self.main) } } -/// The two generic layouting axes. +/// Two generic axes of a container. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum GenAxis { - /// The axis words and lines are set along. - Inline, - /// The axis paragraphs and pages are set along. - Block, + /// The minor axis. + Cross, + /// The major axis. + Main, } impl GenAxis { /// The other axis. pub fn other(self) -> Self { match self { - Self::Inline => Self::Block, - Self::Block => Self::Inline, + Self::Cross => Self::Main, + Self::Main => Self::Cross, } } } diff --git a/src/geom/point.rs b/src/geom/point.rs index 49e3018a6..30e3f9b6b 100644 --- a/src/geom/point.rs +++ b/src/geom/point.rs @@ -36,8 +36,8 @@ impl Point { } /// Convert to the generic representation. - pub const fn to_gen(self, block: SpecAxis) -> Gen { - match block { + pub const fn to_gen(self, main: SpecAxis) -> Gen { + match main { SpecAxis::Horizontal => Gen::new(self.y, self.x), SpecAxis::Vertical => Gen::new(self.x, self.y), } diff --git a/src/geom/size.rs b/src/geom/size.rs index 12cb0ad21..1c0494255 100644 --- a/src/geom/size.rs +++ b/src/geom/size.rs @@ -51,8 +51,8 @@ impl Size { } /// Convert to the generic representation. - pub const fn to_gen(self, block: SpecAxis) -> Gen { - match block { + pub const fn to_gen(self, main: SpecAxis) -> Gen { + match main { SpecAxis::Horizontal => Gen::new(self.h, self.w), SpecAxis::Vertical => Gen::new(self.w, self.h), } diff --git a/src/geom/spec.rs b/src/geom/spec.rs index 02263481d..608643d82 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -57,8 +57,8 @@ impl Spec { } /// Convert to the generic representation. - pub fn to_gen(self, block: SpecAxis) -> Gen { - match block { + pub fn to_gen(self, main: SpecAxis) -> Gen { + match main { SpecAxis::Horizontal => Gen::new(self.y, self.x), SpecAxis::Vertical => Gen::new(self.x, self.y), } diff --git a/src/layout/constraints.rs b/src/layout/constraints.rs index 36cfa582c..b72254d7b 100644 --- a/src/layout/constraints.rs +++ b/src/layout/constraints.rs @@ -1,5 +1,6 @@ use std::rc::Rc; +use super::Regions; use crate::frame::Frame; use crate::geom::{Length, Linear, Size, Spec}; @@ -59,6 +60,17 @@ impl Constraints { } } + /// Create tight constraints for a region. + pub fn tight(regions: &Regions) -> Self { + Self { + min: Spec::default(), + max: Spec::default(), + exact: regions.current.to_spec().map(Some), + base: regions.base.to_spec().map(Some), + expand: regions.expand, + } + } + /// Check whether the constraints are fullfilled in a region with the given /// properties. pub fn check(&self, current: Size, base: Size, expand: Spec) -> bool { diff --git a/src/layout/regions.rs b/src/layout/regions.rs index 392979149..0ef925435 100644 --- a/src/layout/regions.rs +++ b/src/layout/regions.rs @@ -1,4 +1,4 @@ -use crate::geom::{Size, Spec}; +use crate::geom::{Length, Size, Spec}; /// A sequence of regions to layout into. #[derive(Debug, Clone)] @@ -13,9 +13,6 @@ pub struct Regions { pub last: Option, /// 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 expand: Spec, } @@ -52,13 +49,26 @@ impl Regions { regions } - /// Whether `current` is a fully sized (untouched) copy of the last region. + /// Whether the current region is full and a region break is called for. + pub fn is_full(&self) -> bool { + Length::zero().fits(self.current.h) && !self.in_last() + } + + /// Whether `current` is the last usable region. /// /// If this is true, calling `next()` will have no effect. - pub fn in_full_last(&self) -> bool { + pub fn in_last(&self) -> bool { self.backlog.len() == 0 && self.last.map_or(true, |size| self.current == size) } + /// Advance to the next region if there is any. + pub fn next(&mut self) { + if let Some(size) = self.backlog.next().or(self.last) { + self.current = size; + self.base = size; + } + } + /// An iterator that returns pairs of `(current, base)` that are equivalent /// to what would be produced by calling [`next()`](Self::next) repeatedly /// until all regions are exhausted. @@ -69,14 +79,6 @@ impl Regions { first.chain(backlog.chain(last).map(|&s| (s, s))) } - /// Advance to the next region if there is any. - pub fn next(&mut self) { - if let Some(size) = self.backlog.next().or(self.last) { - self.current = size; - self.base = size; - } - } - /// Mutate all contained sizes in place. pub fn mutate(&mut self, mut f: F) where diff --git a/src/library/align.rs b/src/library/align.rs index f788b325c..6f079b7bb 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -34,28 +34,29 @@ impl Layout for AlignNode { pod.expand.x &= self.aligns.x.is_none(); pod.expand.y &= self.aligns.y.is_none(); + // Layout the child. let mut frames = self.child.layout(ctx, &pod); - for (Constrained { item: frame, cts }, (current, _)) in + + for (Constrained { item: frame, cts }, (current, base)) in frames.iter_mut().zip(regions.iter()) { - let canvas = Size::new( + // The possibly larger size in which we align the frame. + let new = Size::new( if regions.expand.x { current.w } else { frame.size.w }, if regions.expand.y { current.h } else { frame.size.h }, ); let aligns = self.aligns.unwrap_or(Spec::new(Align::Left, Align::Top)); - let offset = Point::new( - aligns.x.resolve(canvas.w - frame.size.w), - aligns.y.resolve(canvas.h - frame.size.h), - ); - - let frame = Rc::make_mut(frame); - frame.size = canvas; - frame.baseline += offset.y; - frame.translate(offset); + Rc::make_mut(frame).resize(new, aligns); + // Set constraints. cts.expand = regions.expand; - cts.exact = current.to_spec().map(Some); + cts.base.x.and_set(Some(base.w)); + cts.base.y.and_set(Some(base.h)); + cts.exact = Spec::new( + regions.expand.x.then(|| current.w), + regions.expand.y.then(|| current.h), + ); } frames diff --git a/src/library/flow.rs b/src/library/flow.rs index 44ce63044..d30bce09c 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -52,7 +52,7 @@ impl Layout for FlowNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec>> { - FlowLayouter::new(self, regions.clone()).layout(ctx) + FlowLayouter::new(self, regions).layout(ctx) } } @@ -80,7 +80,7 @@ struct FlowLayouter<'a> { children: &'a [FlowChild], /// Whether the flow should expand to fill the region. expand: Spec, - /// The region to layout into. + /// The regions to layout children into. regions: Regions, /// The full size of `regions.current` that was available before we started /// subtracting. @@ -109,15 +109,18 @@ enum FlowItem { impl<'a> FlowLayouter<'a> { /// Create a new flow layouter. - fn new(flow: &'a FlowNode, mut regions: Regions) -> Self { - // Disable vertical expansion for children. + fn new(flow: &'a FlowNode, regions: &Regions) -> Self { let expand = regions.expand; + let full = regions.current; + + // Disable vertical expansion for children. + let mut regions = regions.clone(); regions.expand.y = false; Self { children: &flow.children, expand, - full: regions.current, + full, regions, used: Size::zero(), fr: Fractional::zero(), @@ -129,6 +132,10 @@ impl<'a> FlowLayouter<'a> { /// Layout all children. fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { for child in self.children { + if self.regions.is_full() { + self.finish_region(); + } + match *child { FlowChild::Spacing(Spacing::Linear(v)) => { self.layout_absolute(v); @@ -215,7 +222,7 @@ impl<'a> FlowLayouter<'a> { size.h = self.full.h; } - let mut output = Frame::new(size, size.h); + let mut output = Frame::new(size); let mut before = Length::zero(); let mut ruler = Align::Top; let mut first = true; diff --git a/src/library/grid.rs b/src/library/grid.rs index b48856eb9..09bb3b3ba 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -99,11 +99,11 @@ struct GridLayouter<'a> { cols: Vec, /// The row tracks including gutter tracks. rows: Vec, - /// The regions to layout into. + /// The regions to layout children into. regions: Regions, /// Resolved column sizes. rcols: Vec, - /// The full block size of the current region. + /// The full height of the current region. full: Length, /// The used-up size of the current region. The horizontal size is /// determined once after columns are resolved and not touched again. @@ -353,6 +353,12 @@ impl<'a> GridLayouter<'a> { /// Layout the grid row-by-row. fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { for y in 0 .. self.rows.len() { + // Skip to next region if current one is full, but only for content + // rows, not for gutter rows. + if y % 2 == 0 && self.regions.is_full() { + self.finish_region(ctx); + } + match self.rows[y] { TrackSizing::Auto => self.layout_auto_row(ctx, y), TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y), @@ -368,8 +374,8 @@ impl<'a> GridLayouter<'a> { self.finished } - /// Layout a row with automatic size along the block axis. Such a row may - /// break across multiple regions. + /// Layout a row with automatic height. Such a row may break across multiple + /// regions. fn layout_auto_row(&mut self, ctx: &mut LayoutContext, y: usize) { let mut resolved: Vec = vec![]; @@ -388,10 +394,14 @@ impl<'a> GridLayouter<'a> { let mut sizes = node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h); + // For each region, we want to know the maximum height any + // column requires. for (target, size) in resolved.iter_mut().zip(&mut sizes) { target.set_max(size); } + // New heights are maximal by virtue of being new. Note that + // this extend only uses the rest of the sizes iterator. resolved.extend(sizes); } } @@ -431,16 +441,16 @@ impl<'a> GridLayouter<'a> { } } - /// 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. + /// Layout a row with linear height. 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 resolved = v.resolve(self.regions.base.h); let frame = self.layout_single_row(ctx, resolved, y); // Skip to fitting region. - let length = frame.size.h; - while !self.regions.current.h.fits(length) && !self.regions.in_full_last() { - self.cts.max.y = Some(self.used.h + length); + let height = frame.size.h; + while !self.regions.current.h.fits(height) && !self.regions.in_last() { + self.cts.max.y = Some(self.used.h + height); self.finish_region(ctx); // Don't skip multiple regions for gutter and don't push a row. @@ -452,14 +462,14 @@ impl<'a> GridLayouter<'a> { self.push_row(frame); } - /// Layout a row with a fixed size along the block axis and return its frame. + /// Layout a row with fixed height and return its frame. fn layout_single_row( &self, ctx: &mut LayoutContext, height: Length, y: usize, ) -> Frame { - let mut output = Frame::new(Size::new(self.used.w, height), height); + let mut output = Frame::new(Size::new(self.used.w, height)); let mut pos = Point::zero(); for (x, &rcol) in self.rcols.iter().enumerate() { @@ -496,7 +506,7 @@ impl<'a> GridLayouter<'a> { // Prepare frames. let mut outputs: Vec<_> = heights .iter() - .map(|&h| Frame::new(Size::new(self.used.w, h), h)) + .map(|&h| Frame::new(Size::new(self.used.w, h))) .collect(); // Prepare regions. @@ -553,7 +563,7 @@ impl<'a> GridLayouter<'a> { } // The frame for the region. - let mut output = Frame::new(size, size.h); + let mut output = Frame::new(size); let mut pos = Point::zero(); // Place finished rows and layout fractional rows. diff --git a/src/library/image.rs b/src/library/image.rs index a421af606..185d033a9 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -43,12 +43,12 @@ impl Layout for ImageNode { let &Regions { current, expand, .. } = regions; let img = ctx.images.get(self.id); - let pixel_w = img.width() as f64; - let pixel_h = img.height() as f64; + let pxw = img.width() as f64; + let pxh = img.height() as f64; - let region_ratio = current.w / current.h; - let pixel_ratio = pixel_w / pixel_h; - let wide = region_ratio < pixel_ratio; + let pixel_ratio = pxw / pxh; + let current_ratio = current.w / current.h; + let wide = pixel_ratio > current_ratio; // The space into which the image will be placed according to its fit. let canvas = if expand.x && expand.y { @@ -58,7 +58,7 @@ impl Layout for ImageNode { } else if current.h.is_finite() { Size::new(current.w.min(current.h * pixel_ratio), current.h) } else { - Size::new(Length::pt(pixel_w), Length::pt(pixel_h)) + Size::new(Length::pt(pxw), Length::pt(pxh)) }; // The actual size of the fitted image. @@ -73,26 +73,19 @@ impl Layout for ImageNode { ImageFit::Stretch => canvas, }; - // Position the image so that it is centered in the canvas. - let mut frame = Frame::new(canvas, canvas.h); - frame.push( - (canvas - size).to_point() / 2.0, - Element::Image(self.id, size), - ); + // First, place the image in a frame of exactly its size and then resize + // the frame to the canvas size, center aligning the image in the + // process. + let mut frame = Frame::new(size); + frame.push(Point::zero(), Element::Image(self.id, size)); + frame.resize(canvas, Spec::new(Align::Center, Align::Horizon)); - // Create a clipping group if the image mode is `cover`. + // Create a clipping group if the fit mode is "cover". if self.fit == ImageFit::Cover { - let mut wrapper = Frame::new(canvas, canvas.h); - wrapper.push( - Point::zero(), - Element::Group(Group::new(Rc::new(frame)).clips(true)), - ); - frame = wrapper; + frame.clip(); } - let mut cts = Constraints::new(regions.expand); - cts.exact = regions.current.to_spec().map(Some); - vec![frame.constrain(cts)] + vec![frame.constrain(Constraints::tight(regions))] } } diff --git a/src/library/par.rs b/src/library/par.rs index 7f3f1088b..e09e4ad27 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -241,12 +241,17 @@ impl<'a> ParLayouter<'a> { starts.push((range.start, deco)); } ParChild::Undecorate => { - let (start, deco) = starts.pop().unwrap(); - decos.push((start .. range.end, deco)); + if let Some((start, deco)) = starts.pop() { + decos.push((start .. range.end, deco)); + } } } } + for (start, deco) in starts { + decos.push((start .. bidi.text.len(), deco)); + } + Self { align: par.align, leading: par.leading, @@ -307,7 +312,7 @@ impl<'a> ParLayouter<'a> { // If the line does not fit vertically, we start a new region. while !stack.regions.current.h.fits(line.size.h) { - if stack.regions.in_full_last() { + if stack.regions.in_last() { stack.overflowing = true; break; } @@ -487,8 +492,9 @@ impl<'a> LineLayout<'a> { let size = Size::new(self.size.w.max(width), self.size.h); let remaining = size.w - self.size.w; - let mut output = Frame::new(size, self.baseline); + let mut output = Frame::new(size); let mut offset = Length::zero(); + output.baseline = self.baseline; for (range, item) in self.reordered() { let mut position = |mut frame: Frame| { @@ -621,7 +627,7 @@ impl<'a> LineStack<'a> { self.cts.exact = self.full.to_spec().map(Some); } - let mut output = Frame::new(self.size, self.size.h); + let mut output = Frame::new(self.size); let mut offset = Length::zero(); let mut first = true; diff --git a/src/library/shape.rs b/src/library/shape.rs index 5d3504d04..36e25b3c3 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -165,12 +165,16 @@ impl Layout for ShapeNode { if regions.expand.y { regions.current.h } else { default }, ); + // Don't overflow the region. + size.w = size.w.min(regions.current.w); + size.h = size.h.min(regions.current.h); + if matches!(self.kind, ShapeKind::Square | ShapeKind::Circle) { size.w = size.w.min(size.h); size.h = size.w; } - Frame::new(size, size.h) + Frame::new(size) }; // Add fill and/or stroke. @@ -197,9 +201,6 @@ impl Layout for ShapeNode { ); // Return tight constraints for now. - let mut cts = Constraints::new(regions.expand); - cts.exact = regions.current.to_spec().map(Some); - cts.base = regions.base.to_spec().map(Some); - vec![frame.constrain(cts)] + vec![frame.constrain(Constraints::tight(regions))] } } diff --git a/src/library/sized.rs b/src/library/sized.rs index 3e27ff2f7..df150143d 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -35,10 +35,6 @@ impl Layout for SizedNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec>> { - // Resolve width and height relative to the region's base. - let width = self.sizing.x.map(|w| w.resolve(regions.base.w)); - let height = self.sizing.y.map(|h| h.resolve(regions.base.h)); - // Generate constraints. let mut cts = Constraints::new(regions.expand); cts.set_base_if_linear(regions.base, self.sizing); @@ -56,6 +52,10 @@ impl Layout for SizedNode { cts.base.y = Some(regions.base.h); } + // Resolve width and height relative to the region's base. + let width = self.sizing.x.map(|w| w.resolve(regions.base.w)); + let height = self.sizing.y.map(|h| h.resolve(regions.base.h)); + // The "pod" is the region into which the child will be layouted. let pod = { let size = Size::new( diff --git a/src/library/spacing.rs b/src/library/spacing.rs index 26f1bee11..59911dc72 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -3,14 +3,14 @@ use super::prelude::*; /// `h`: Horizontal spacing. pub fn h(_: &mut EvalContext, args: &mut Args) -> TypResult { let mut template = Template::new(); - template.spacing(GenAxis::Inline, args.expect("spacing")?); + template.spacing(SpecAxis::Horizontal, args.expect("spacing")?); Ok(Value::Template(template)) } /// `v`: Vertical spacing. pub fn v(_: &mut EvalContext, args: &mut Args) -> TypResult { let mut template = Template::new(); - template.spacing(GenAxis::Block, args.expect("spacing")?); + template.spacing(SpecAxis::Vertical, args.expect("spacing")?); Ok(Value::Template(template)) } diff --git a/src/library/stack.rs b/src/library/stack.rs index d0d5225ed..a6878bd68 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -66,7 +66,7 @@ impl Layout for StackNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec>> { - StackLayouter::new(self, regions.clone()).layout(ctx) + StackLayouter::new(self, regions).layout(ctx) } } @@ -96,7 +96,7 @@ struct StackLayouter<'a> { axis: SpecAxis, /// Whether the stack should expand to fill the region. expand: Spec, - /// The region to layout into. + /// The regions to layout children into. regions: Regions, /// The full size of `regions.current` that was available before we started /// subtracting. @@ -123,17 +123,21 @@ enum StackItem { impl<'a> StackLayouter<'a> { /// Create a new stack layouter. - fn new(stack: &'a StackNode, mut regions: Regions) -> Self { - // Disable expansion along the block axis for children. + fn new(stack: &'a StackNode, regions: &Regions) -> Self { let axis = stack.dir.axis(); let expand = regions.expand; + let full = regions.current; + + + // Disable expansion along the block axis for children. + let mut regions = regions.clone(); regions.expand.set(axis, false); Self { stack, axis, expand, - full: regions.current, + full, regions, used: Gen::zero(), fr: Fractional::zero(), @@ -145,6 +149,10 @@ impl<'a> StackLayouter<'a> { /// Layout all children. fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { for child in &self.stack.children { + if self.regions.is_full() { + self.finish_region(); + } + match *child { StackChild::Spacing(Spacing::Linear(v)) => { self.layout_absolute(v); @@ -167,10 +175,10 @@ impl<'a> StackLayouter<'a> { fn layout_absolute(&mut self, amount: Linear) { // Resolve the linear, limiting it to the remaining available space. let remaining = self.regions.current.get_mut(self.axis); - let resolved = amount.resolve(self.full.get(self.axis)); + let resolved = amount.resolve(self.regions.base.get(self.axis)); let limited = resolved.min(*remaining); *remaining -= limited; - self.used.block += limited; + self.used.main += limited; self.items.push(StackItem::Absolute(resolved)); } @@ -187,9 +195,9 @@ impl<'a> StackLayouter<'a> { for (i, frame) in frames.into_iter().enumerate() { // Grow our size, shrink the region and save the frame for later. let size = frame.item.size.to_gen(self.axis); - self.used.block += size.block; - self.used.inline.set_max(size.inline); - *self.regions.current.get_mut(self.axis) -= size.block; + self.used.main += size.main; + self.used.cross.set_max(size.cross); + *self.regions.current.get_mut(self.axis) -= size.main; self.items.push(StackItem::Frame(frame.item, align)); if i + 1 < len { @@ -210,13 +218,13 @@ impl<'a> StackLayouter<'a> { // Expand fully if there are fr spacings. let full = self.full.get(self.axis); - let remaining = full - self.used.block; + let remaining = full - self.used.main; if self.fr.get() > 0.0 && full.is_finite() { - self.used.block = full; + self.used.main = full; size.set(self.axis, full); } - let mut output = Frame::new(size, size.h); + let mut output = Frame::new(size); let mut before = Length::zero(); let mut ruler: Align = self.stack.dir.start().into(); @@ -239,11 +247,11 @@ impl<'a> StackLayouter<'a> { // Align along the block axis. let parent = size.get(self.axis); let child = frame.size.get(self.axis); - let block = ruler.resolve(parent - self.used.block) + let block = ruler.resolve(parent - self.used.main) + if self.stack.dir.is_positive() { before } else { - self.used.block - child - before + self.used.main - child - before }; let pos = Gen::new(Length::zero(), block).to_point(self.axis); diff --git a/src/library/text.rs b/src/library/text.rs index f1cd2b4d2..aac99e8a0 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -305,8 +305,9 @@ pub struct ShapedGlyph { impl<'a> ShapedText<'a> { /// Build the shaped text's frame. pub fn build(&self) -> Frame { - let mut frame = Frame::new(self.size, self.baseline); + let mut frame = Frame::new(self.size); let mut offset = Length::zero(); + frame.baseline = self.baseline; for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) { let pos = Point::new(offset, self.baseline); diff --git a/src/library/transform.rs b/src/library/transform.rs index 75df8067c..207a80988 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -62,13 +62,7 @@ impl Layout for TransformNode { let transform = Transform::translation(x, y) .pre_concat(self.transform) .pre_concat(Transform::translation(-x, -y)); - - let mut wrapper = Frame::new(frame.size, frame.baseline); - wrapper.push( - Point::zero(), - Element::Group(Group::new(std::mem::take(frame)).transform(transform)), - ); - *frame = Rc::new(wrapper); + Rc::make_mut(frame).transform(transform); } frames diff --git a/tests/typ/code/let.typ b/tests/typ/code/let.typ index cd7531b76..7fd6e0da7 100644 --- a/tests/typ/code/let.typ +++ b/tests/typ/code/let.typ @@ -11,7 +11,7 @@ // Syntax sugar for function definitions. #let fill = conifer -#let rect(body) = rect(width: 2cm, fill: fill, pad(5pt, body)) +#let rect(body) = rect(width: 2cm, fill: fill, padding: 5pt, body) #rect[Hi!] --- diff --git a/tests/typ/code/target.typ b/tests/typ/code/target.typ index 12a5ff8b5..735168173 100644 --- a/tests/typ/code/target.typ +++ b/tests/typ/code/target.typ @@ -7,6 +7,6 @@ #let d = 3 #let value = [hi] #let item(a, b) = a + b -#let fn(body) = rect(fill: conifer, pad(5pt, body)) +#let fn = rect with (fill: conifer, padding: 5pt) Some _includable_ text. diff --git a/tests/typ/elements/circle.typ b/tests/typ/elements/circle.typ index 2e97e985c..ad9d3a4e3 100644 --- a/tests/typ/elements/circle.typ +++ b/tests/typ/elements/circle.typ @@ -16,7 +16,7 @@ Auto-sized circle. \ Center-aligned rect in auto-sized circle. #circle(fill: forest, stroke: conifer, align(center + horizon, - rect(fill: conifer, pad(5pt)[But, soft!]) + rect(fill: conifer, padding: 5pt)[But, soft!] ) ) diff --git a/tests/typ/elements/rect.typ b/tests/typ/elements/rect.typ index bb666fb22..b3d4d2868 100644 --- a/tests/typ/elements/rect.typ +++ b/tests/typ/elements/rect.typ @@ -19,7 +19,7 @@ )) // Fixed width, text height. -#rect(width: 2cm, fill: rgb("9650d6"), pad(5pt)[Fixed and padded]) +#rect(width: 2cm, fill: rgb("9650d6"), padding: 5pt)[Fixed and padded] // Page width, fixed height. #rect(height: 1cm, width: 100%, fill: rgb("734ced"))[Topleft] diff --git a/tests/typ/layout/grid-1.typ b/tests/typ/layout/grid-1.typ index 2d13452b3..647e366fe 100644 --- a/tests/typ/layout/grid-1.typ +++ b/tests/typ/layout/grid-1.typ @@ -20,8 +20,6 @@ cell(100%, rgb("00ff00")), ) -#grid() - --- #grid( columns: (auto, auto, 40%),