mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Set the correct base for grid cells
This commit is contained in:
parent
50a464488c
commit
b0b4607725
@ -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, ®ions).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, ®ions).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, ®ions)
|
||||
.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, ®ions).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, ®ions);
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
},
|
||||
);
|
||||
|
BIN
tests/ref/layout/grid-4.png
Normal file
BIN
tests/ref/layout/grid-4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 431 B |
33
tests/typ/layout/grid-4.typ
Normal file
33
tests/typ/layout/grid-4.typ
Normal file
@ -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),
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user