summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-11-25 20:51:16 +0100
committerLaurenz <laurmaedje@gmail.com>2021-11-25 20:51:16 +0100
commit393d74f9bb0d4c71a69108d5be261103c39f47f3 (patch)
treea1d4219de2d8fbec1a16ac3760a95e0b7c9529c5 /src/library
parent304d9dd1107504f3925c2593dd279ea6616defab (diff)
Layout improvements
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs25
-rw-r--r--src/library/flow.rs19
-rw-r--r--src/library/grid.rs36
-rw-r--r--src/library/image.rs37
-rw-r--r--src/library/par.rs16
-rw-r--r--src/library/shape.rs11
-rw-r--r--src/library/sized.rs8
-rw-r--r--src/library/spacing.rs4
-rw-r--r--src/library/stack.rs38
-rw-r--r--src/library/text.rs3
-rw-r--r--src/library/transform.rs8
11 files changed, 113 insertions, 92 deletions
diff --git a/src/library/align.rs b/src/library/align.rs
index f788b325..6f079b7b 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 44ce6304..d30bce09 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<Constrained<Rc<Frame>>> {
- 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<bool>,
- /// 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<Constrained<Rc<Frame>>> {
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 b48856eb..09bb3b3b 100644
--- a/src/library/grid.rs
+++ b/src/library/grid.rs
@@ -99,11 +99,11 @@ struct GridLayouter<'a> {
cols: Vec<TrackSizing>,
/// The row tracks including gutter tracks.
rows: Vec<TrackSizing>,
- /// The regions to layout into.
+ /// The regions to layout children into.
regions: Regions,
/// Resolved column sizes.
rcols: Vec<Length>,
- /// 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<Constrained<Rc<Frame>>> {
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<Length> = 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 a421af60..185d033a 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 7f3f1088..e09e4ad2 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 5d3504d0..36e25b3c 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 3e27ff2f..df150143 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<Constrained<Rc<Frame>>> {
- // 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 26f1bee1..59911dc7 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<Value> {
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<Value> {
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 d0d5225e..a6878bd6 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<Constrained<Rc<Frame>>> {
- 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<bool>,
- /// 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<Constrained<Rc<Frame>>> {
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 f1cd2b4d..aac99e8a 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 75df8067..207a8098 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