summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/layout/grid.rs99
-rw-r--r--src/layout/stack.rs4
-rw-r--r--tests/ref/layout/grid-4.pngbin0 -> 431 bytes
-rw-r--r--tests/typ/layout/grid-4.typ33
4 files changed, 107 insertions, 29 deletions
diff --git a/src/layout/grid.rs b/src/layout/grid.rs
index 6f8825da..606821e2 100644
--- a/src/layout/grid.rs
+++ b/src/layout/grid.rs
@@ -166,8 +166,8 @@ impl<'a> GridLayouter<'a> {
}
// Generic version of current and base size.
- let current = self.regions.current.to_gen(self.block);
- let base = self.regions.base.to_gen(self.block);
+ let current = self.regions.current.get(self.inline);
+ let base = self.regions.base.get(self.inline);
// The different cases affecting constraints.
let mut case = Case::PurelyLinear;
@@ -178,6 +178,8 @@ impl<'a> GridLayouter<'a> {
// Sum of fractions of all fractional tracks.
let mut fr = Fractional::zero();
+
+
// Resolve the size of all linear columns and compute the sum of all
// fractional tracks.
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
@@ -186,8 +188,7 @@ impl<'a> GridLayouter<'a> {
case = Case::Fitting;
}
TrackSizing::Linear(v) => {
- self.constraints.base.set(self.inline, Some(base.inline));
- let resolved = v.resolve(base.inline);
+ let resolved = v.resolve(base);
*rcol = resolved;
linear += resolved;
}
@@ -199,7 +200,7 @@ impl<'a> GridLayouter<'a> {
}
// Size that is not used by fixed-size columns.
- let available = current.inline - linear;
+ let available = current - linear;
if available >= Length::zero() {
// Determine size of auto columns.
let (auto, count) = self.measure_auto_columns(ctx, available);
@@ -220,6 +221,9 @@ impl<'a> GridLayouter<'a> {
case = Case::Overflowing;
}
+ // Children could depend on base.
+ self.constraints.base = self.regions.base.to_spec().map(Some);
+
// Set constraints depending on the case we hit.
match case {
Case::PurelyLinear => {}
@@ -227,7 +231,7 @@ impl<'a> GridLayouter<'a> {
self.constraints.min.set(self.inline, Some(self.used.inline));
}
Case::Exact => {
- self.constraints.exact.set(self.inline, Some(current.inline));
+ self.constraints.exact.set(self.inline, Some(current));
}
Case::Overflowing => {
self.constraints.max.set(self.inline, Some(linear));
@@ -244,6 +248,8 @@ impl<'a> GridLayouter<'a> {
ctx: &mut LayoutContext,
available: Length,
) -> (Length, usize) {
+ let base = self.regions.base.get(self.block);
+
let mut auto = Length::zero();
let mut count = 0;
@@ -255,11 +261,22 @@ impl<'a> GridLayouter<'a> {
}
let mut resolved = Length::zero();
- for node in (0 .. self.rows.len()).filter_map(|y| self.cell(x, y)) {
- let size = Gen::new(available, Length::inf()).to_size(self.block);
- let regions = Regions::one(size, size, Spec::splat(false));
- let frame = node.layout(ctx, &regions).remove(0).item;
- resolved.set_max(frame.size.get(self.inline));
+ for y in 0 .. self.rows.len() {
+ if let Some(node) = self.cell(x, y) {
+ let size = Gen::new(available, Length::inf()).to_size(self.block);
+ let mut regions =
+ Regions::one(size, self.regions.base, Spec::splat(false));
+
+ // For fractional rows, we can already resolve the correct
+ // base, for auto it's already correct and for fr we could
+ // only guess anyway.
+ if let TrackSizing::Linear(v) = self.rows[y] {
+ regions.base.set(self.block, v.resolve(base));
+ }
+
+ let frame = node.layout(ctx, &regions).remove(0).item;
+ resolved.set_max(frame.size.get(self.inline));
+ }
}
self.rcols[x] = resolved;
@@ -314,16 +331,14 @@ impl<'a> GridLayouter<'a> {
/// Layout the grid row-by-row.
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() {
match self.rows[y] {
TrackSizing::Auto => {
self.layout_auto_row(ctx, y);
}
TrackSizing::Linear(v) => {
- let base = self.regions.base.get(self.block);
- if v.is_relative() {
- self.constraints.base.set(self.block, Some(base));
- }
let resolved = v.resolve(base);
let frame = self.layout_single_row(ctx, resolved, y);
self.push_row(ctx, frame);
@@ -343,6 +358,8 @@ impl<'a> GridLayouter<'a> {
/// Layout a row with automatic size along the block axis. Such a row may
/// break across multiple regions.
fn layout_auto_row(&mut self, ctx: &mut LayoutContext, y: usize) {
+ let base = self.regions.base.get(self.inline);
+
let mut first = Length::zero();
let mut rest: Vec<Length> = vec![];
@@ -350,10 +367,18 @@ impl<'a> GridLayouter<'a> {
for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) {
let inline = self.inline;
- self.regions.mutate(|size| size.set(inline, rcol));
+
+ let mut regions = self.regions.clone();
+ regions.mutate(|size| size.set(inline, rcol));
+
+ // Set the inline base back to the parent region's inline base
+ // for auto columns.
+ if self.cols[x] == TrackSizing::Auto {
+ regions.base.set(self.inline, base);
+ }
let mut sizes = node
- .layout(ctx, &self.regions)
+ .layout(ctx, &regions)
.into_iter()
.map(|frame| frame.item.size.get(self.block));
@@ -389,17 +414,29 @@ impl<'a> GridLayouter<'a> {
fn layout_single_row(
&self,
ctx: &mut LayoutContext,
- length: Length,
+ block: Length,
y: usize,
) -> Frame {
- let size = self.to_size(length);
+ let size = self.complete(block);
+
let mut output = Frame::new(size, size.h);
let mut pos = Gen::zero();
for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) {
- let size = Gen::new(rcol, length).to_size(self.block);
- let regions = Regions::one(size, size, Spec::splat(true));
+ let size = Gen::new(rcol, block).to_size(self.block);
+ let mut base = self.regions.base;
+
+ // Set the base to the size for non-auto rows.
+ let sizing = Gen::new(self.cols[x], self.rows[y]).to_spec(self.block);
+ if sizing.x != TrackSizing::Auto {
+ base.w = size.w;
+ }
+ if sizing.y != TrackSizing::Auto {
+ base.h = size.h;
+ }
+
+ let regions = Regions::one(size, base, Spec::splat(true));
let frame = node.layout(ctx, &regions).remove(0);
output.push_frame(pos.to_point(self.block), frame.item);
}
@@ -418,18 +455,20 @@ impl<'a> GridLayouter<'a> {
rest: &[Length],
y: usize,
) -> Vec<Frame> {
+ let base = self.regions.base.get(self.inline);
+
// Prepare frames.
let mut outputs: Vec<_> = std::iter::once(first)
.chain(rest.iter().copied())
- .map(|v| self.to_size(v))
+ .map(|v| self.complete(v))
.map(|size| Frame::new(size, size.h))
.collect();
// Prepare regions.
- let size = self.to_size(first);
- let mut regions = Regions::one(size, size, Spec::splat(true));
+ let size = self.complete(first);
+ let mut regions = Regions::one(size, self.regions.base, Spec::splat(true));
regions.backlog =
- rest.iter().map(|&v| self.to_size(v)).collect::<Vec<_>>().into_iter();
+ rest.iter().map(|&v| self.complete(v)).collect::<Vec<_>>().into_iter();
// Layout the row.
let mut pos = Gen::zero();
@@ -437,6 +476,12 @@ impl<'a> GridLayouter<'a> {
if let Some(node) = self.cell(x, y) {
regions.mutate(|size| size.set(self.inline, rcol));
+ // Set the inline base back to the parent region's inline base
+ // for auto columns.
+ if self.cols[x] == TrackSizing::Auto {
+ regions.base.set(self.inline, base);
+ }
+
// Push the layouted frames into the individual output frames.
let frames = node.layout(ctx, &regions);
for (output, frame) in outputs.iter_mut().zip(frames) {
@@ -477,7 +522,7 @@ impl<'a> GridLayouter<'a> {
self.full
};
- let size = self.to_size(block);
+ let size = self.complete(block);
self.constraints.min.set(self.block, Some(block));
// The frame for the region.
@@ -537,7 +582,7 @@ impl<'a> GridLayouter<'a> {
/// Return a size where the inline axis spans the whole grid and the block
/// axis the given length.
- fn to_size(&self, block: Length) -> Size {
+ fn complete(&self, block: Length) -> Size {
Gen::new(self.used.inline, block).to_size(self.block)
}
}
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index d31012f0..211c8a7e 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -178,14 +178,14 @@ impl<'a> StackLayouter<'a> {
self.constraints.exact.x = Some(self.full.w);
self.full.w
} else {
- self.constraints.min.x = Some(used.w);
+ 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);
+ self.constraints.min.y = Some(used.h.min(self.full.h));
used.h
},
);
diff --git a/tests/ref/layout/grid-4.png b/tests/ref/layout/grid-4.png
new file mode 100644
index 00000000..a291658f
--- /dev/null
+++ b/tests/ref/layout/grid-4.png
Binary files differ
diff --git a/tests/typ/layout/grid-4.typ b/tests/typ/layout/grid-4.typ
new file mode 100644
index 00000000..02ac0486
--- /dev/null
+++ b/tests/typ/layout/grid-4.typ
@@ -0,0 +1,33 @@
+// Test relative sizing inside grids.
+
+---
+// Test that auto and linear columns use the correct base.
+#grid(
+ columns: (auto, 60%),
+ rows: (auto, auto),
+ rect(width: 50%, height: 0.5cm, fill: conifer),
+ rect(width: 100%, height: 0.5cm, fill: eastern),
+ rect(width: 50%, height: 0.5cm, fill: forest),
+)
+
+---
+// Test that fr columns use the correct base.
+#grid(
+ columns: (1fr,) * 4,
+ rows: (1cm,),
+ rect(width: 50%, height: 100%, fill: conifer),
+ rect(width: 50%, height: 100%, fill: forest),
+ rect(width: 50%, height: 100%, fill: conifer),
+ rect(width: 50%, height: 100%, fill: forest),
+)
+
+---
+// Test that all three kinds of rows use the correct bases.
+#page(height: 4cm, margins: 0cm)
+#grid(
+ rows: (1cm, 1fr, 1fr, auto),
+ rect(height: 50%, width: 100%, fill: conifer),
+ rect(height: 50%, width: 100%, fill: forest),
+ rect(height: 50%, width: 100%, fill: conifer),
+ rect(height: 25%, width: 100%, fill: forest),
+)