Fix grid sizing

This commit is contained in:
Laurenz 2023-02-14 12:34:16 +01:00
parent cc9b52ddd4
commit b9c0fd87d3
6 changed files with 71 additions and 60 deletions

View File

@ -70,9 +70,9 @@ struct FlowLayouter<'a> {
regions: Regions<'a>, regions: Regions<'a>,
/// Whether the flow should expand to fill the region. /// Whether the flow should expand to fill the region.
expand: Axes<bool>, expand: Axes<bool>,
/// The full size of `regions.size` that was available before we started /// The intial size of `regions.size` that was available before we started
/// subtracting. /// subtracting.
full: Size, initial: Size,
/// Whether the last block was a paragraph. /// Whether the last block was a paragraph.
last_was_par: bool, last_was_par: bool,
/// Spacing and layouted blocks. /// Spacing and layouted blocks.
@ -98,7 +98,6 @@ impl<'a> FlowLayouter<'a> {
/// Create a new flow layouter. /// Create a new flow layouter.
fn new(mut regions: Regions<'a>) -> Self { fn new(mut regions: Regions<'a>) -> Self {
let expand = regions.expand; let expand = regions.expand;
let full = regions.size;
// Disable vertical expansion for children. // Disable vertical expansion for children.
regions.expand.y = false; regions.expand.y = false;
@ -106,7 +105,7 @@ impl<'a> FlowLayouter<'a> {
Self { Self {
regions, regions,
expand, expand,
full, initial: regions.size,
last_was_par: false, last_was_par: false,
items: vec![], items: vec![],
finished: vec![], finished: vec![],
@ -117,7 +116,7 @@ impl<'a> FlowLayouter<'a> {
fn layout_spacing(&mut self, node: VNode, styles: StyleChain) { fn layout_spacing(&mut self, node: VNode, styles: StyleChain) {
self.layout_item(match node.amount { self.layout_item(match node.amount {
Spacing::Rel(v) => FlowItem::Absolute( Spacing::Rel(v) => FlowItem::Absolute(
v.resolve(styles).relative_to(self.full.y), v.resolve(styles).relative_to(self.initial.y),
node.weakness > 0, node.weakness > 0,
), ),
Spacing::Fr(v) => FlowItem::Fractional(v), Spacing::Fr(v) => FlowItem::Fractional(v),
@ -256,10 +255,10 @@ impl<'a> FlowLayouter<'a> {
let mut fr = Fr::zero(); let mut fr = Fr::zero();
let mut used = Size::zero(); let mut used = Size::zero();
for item in &self.items { for item in &self.items {
match *item { match item {
FlowItem::Absolute(v, _) => used.y += v, FlowItem::Absolute(v, _) => used.y += *v,
FlowItem::Fractional(v) => fr += v, FlowItem::Fractional(v) => fr += *v,
FlowItem::Frame(ref frame, ..) => { FlowItem::Frame(frame, ..) => {
let size = frame.size(); let size = frame.size();
used.y += size.y; used.y += size.y;
used.x.set_max(size.x); used.x.set_max(size.x);
@ -269,14 +268,10 @@ impl<'a> FlowLayouter<'a> {
} }
// Determine the size of the flow in this region depending on whether // Determine the size of the flow in this region depending on whether
// the region expands. // the region expands. Also account for fractional spacing.
let mut size = self.expand.select(self.full, used).min(self.full); let mut size = self.expand.select(self.initial, used).min(self.initial);
if fr.get() > 0.0 && self.initial.y.is_finite() {
// Account for fractional spacing in the size calculation. size.y = self.initial.y;
let remaining = self.full.y - used.y;
if fr.get() > 0.0 && self.full.y.is_finite() {
used.y = self.full.y;
size.y = self.full.y;
} }
let mut output = Frame::new(size); let mut output = Frame::new(size);
@ -290,6 +285,7 @@ impl<'a> FlowLayouter<'a> {
offset += v; offset += v;
} }
FlowItem::Fractional(v) => { FlowItem::Fractional(v) => {
let remaining = self.initial.y - used.y;
offset += v.share(fr, remaining); offset += v.share(fr, remaining);
} }
FlowItem::Frame(frame, aligns, _) => { FlowItem::Frame(frame, aligns, _) => {
@ -309,7 +305,7 @@ impl<'a> FlowLayouter<'a> {
// Advance to the next region. // Advance to the next region.
self.finished.push(output); self.finished.push(output);
self.regions.next(); self.regions.next();
self.full = self.regions.size; self.initial = self.regions.size;
} }
/// Finish layouting and return the resulting fragment. /// Finish layouting and return the resulting fragment.

View File

@ -184,15 +184,14 @@ pub struct GridLayouter<'a, 'v> {
styles: StyleChain<'a>, styles: StyleChain<'a>,
/// Resolved column sizes. /// Resolved column sizes.
rcols: Vec<Abs>, rcols: Vec<Abs>,
/// The sum of `rcols`.
width: Abs,
/// Resolve row sizes, by region. /// Resolve row sizes, by region.
rrows: Vec<Vec<RowPiece>>, rrows: Vec<Vec<RowPiece>>,
/// Rows in the current region. /// Rows in the current region.
lrows: Vec<Row>, lrows: Vec<Row>,
/// The used-up size of the current region. The horizontal size is /// The initial size of the current region before we started subtracting.
/// determined once after columns are resolved and not touched again. initial: Size,
used: Size,
/// The sum of fractions in the current region.
fr: Fr,
/// Frames for finished regions. /// Frames for finished regions.
finished: Vec<Frame>, finished: Vec<Frame>,
} }
@ -306,10 +305,10 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
regions, regions,
styles, styles,
rcols, rcols,
width: Abs::zero(),
rrows: vec![], rrows: vec![],
lrows, lrows,
used: Size::zero(), initial: regions.size,
fr: Fr::zero(),
finished: vec![], finished: vec![],
} }
} }
@ -328,10 +327,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
match self.rows[y] { match self.rows[y] {
Sizing::Auto => self.layout_auto_row(y)?, Sizing::Auto => self.layout_auto_row(y)?,
Sizing::Rel(v) => self.layout_relative_row(v, y)?, Sizing::Rel(v) => self.layout_relative_row(v, y)?,
Sizing::Fr(v) => { Sizing::Fr(v) => self.lrows.push(Row::Fr(v, y)),
self.lrows.push(Row::Fr(v, y));
self.fr += v;
}
} }
} }
@ -384,7 +380,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
} }
// Sum up the resolved column sizes once here. // Sum up the resolved column sizes once here.
self.used.x = self.rcols.iter().sum(); self.width = self.rcols.iter().sum();
Ok(()) Ok(())
} }
@ -523,14 +519,17 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
resolved.remove(0); resolved.remove(0);
} }
// Expand all but the last region if the space is not // Expand all but the last region.
// eaten up by any fr rows. // Skip the first region if the space is eaten up by an fr row.
if self.fr.is_zero() {
let len = resolved.len(); let len = resolved.len();
for (region, target) in self.regions.iter().zip(&mut resolved[..len - 1]) { for (region, target) in self
.regions
.iter()
.zip(&mut resolved[..len - 1])
.skip(self.lrows.iter().any(|row| matches!(row, Row::Fr(..))) as usize)
{
target.set_max(region.y); target.set_max(region.y);
} }
}
// Layout into multiple regions. // Layout into multiple regions.
let fragment = self.layout_multi_row(&resolved, y)?; let fragment = self.layout_multi_row(&resolved, y)?;
@ -569,7 +568,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
/// Layout a row with fixed height and return its frame. /// Layout a row with fixed height and return its frame.
fn layout_single_row(&mut self, height: Abs, y: usize) -> SourceResult<Frame> { fn layout_single_row(&mut self, height: Abs, y: usize) -> SourceResult<Frame> {
let mut output = Frame::new(Size::new(self.used.x, height)); let mut output = Frame::new(Size::new(self.width, height));
let mut pos = Point::zero(); let mut pos = Point::zero();
for (x, &rcol) in self.rcols.iter().enumerate() { for (x, &rcol) in self.rcols.iter().enumerate() {
@ -594,11 +593,11 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
// Prepare frames. // Prepare frames.
let mut outputs: Vec<_> = heights let mut outputs: Vec<_> = heights
.iter() .iter()
.map(|&h| Frame::new(Size::new(self.used.x, h))) .map(|&h| Frame::new(Size::new(self.width, h)))
.collect(); .collect();
// Prepare regions. // Prepare regions.
let size = Size::new(self.used.x, heights[0]); let size = Size::new(self.width, heights[0]);
let mut pod = Regions::one(size, Axes::splat(true)); let mut pod = Regions::one(size, Axes::splat(true));
pod.full = self.regions.full; pod.full = self.regions.full;
pod.backlog = &heights[1..]; pod.backlog = &heights[1..];
@ -625,17 +624,26 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
/// Push a row frame into the current region. /// Push a row frame into the current region.
fn push_row(&mut self, frame: Frame, y: usize) { fn push_row(&mut self, frame: Frame, y: usize) {
self.regions.size.y -= frame.height(); self.regions.size.y -= frame.height();
self.used.y += frame.height();
self.lrows.push(Row::Frame(frame, y)); self.lrows.push(Row::Frame(frame, y));
} }
/// Finish rows for one region. /// Finish rows for one region.
fn finish_region(&mut self) -> SourceResult<()> { fn finish_region(&mut self) -> SourceResult<()> {
// Determine the height of existing rows in the region.
let mut used = Abs::zero();
let mut fr = Fr::zero();
for row in &self.lrows {
match row {
Row::Frame(frame, _) => used += frame.height(),
Row::Fr(v, _) => fr += *v,
}
}
// Determine the size of the grid in this region, expanding fully if // Determine the size of the grid in this region, expanding fully if
// there are fr rows. // there are fr rows.
let mut size = self.used; let mut size = Size::new(self.width, used).min(self.initial);
if self.fr.get() > 0.0 && self.regions.full.is_finite() { if fr.get() > 0.0 && self.initial.y.is_finite() {
size.y = self.regions.full; size.y = self.initial.y;
} }
// The frame for the region. // The frame for the region.
@ -648,8 +656,8 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
let (frame, y) = match row { let (frame, y) = match row {
Row::Frame(frame, y) => (frame, y), Row::Frame(frame, y) => (frame, y),
Row::Fr(v, y) => { Row::Fr(v, y) => {
let remaining = self.regions.full - self.used.y; let remaining = self.regions.full - used;
let height = v.share(self.fr, remaining); let height = v.share(fr, remaining);
(self.layout_single_row(height, y)?, y) (self.layout_single_row(height, y)?, y)
} }
}; };
@ -661,10 +669,9 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
} }
self.finished.push(output); self.finished.push(output);
self.regions.next();
self.rrows.push(rrows); self.rrows.push(rrows);
self.used.y = Abs::zero(); self.regions.next();
self.fr = Fr::zero(); self.initial = self.regions.size;
Ok(()) Ok(())
} }

View File

@ -129,8 +129,8 @@ struct StackLayouter<'a> {
styles: StyleChain<'a>, styles: StyleChain<'a>,
/// Whether the stack itself should expand to fill the region. /// Whether the stack itself should expand to fill the region.
expand: Axes<bool>, expand: Axes<bool>,
/// The full size of the current region that was available at the start. /// The initial size of the current region before we started subtracting.
full: Size, initial: Size,
/// The generic size used by the frames for the current region. /// The generic size used by the frames for the current region.
used: Gen<Abs>, used: Gen<Abs>,
/// The sum of fractions in the current region. /// The sum of fractions in the current region.
@ -154,13 +154,11 @@ enum StackItem {
impl<'a> StackLayouter<'a> { impl<'a> StackLayouter<'a> {
/// Create a new stack layouter. /// Create a new stack layouter.
fn new(dir: Dir, regions: Regions<'a>, styles: StyleChain<'a>) -> Self { fn new(dir: Dir, mut regions: Regions<'a>, styles: StyleChain<'a>) -> Self {
let axis = dir.axis(); let axis = dir.axis();
let expand = regions.expand; let expand = regions.expand;
let full = regions.size;
// Disable expansion along the block axis for children. // Disable expansion along the block axis for children.
let mut regions = regions.clone();
regions.expand.set(axis, false); regions.expand.set(axis, false);
Self { Self {
@ -169,7 +167,7 @@ impl<'a> StackLayouter<'a> {
regions, regions,
styles, styles,
expand, expand,
full, initial: regions.size,
used: Gen::zero(), used: Gen::zero(),
fr: Fr::zero(), fr: Fr::zero(),
items: vec![], items: vec![],
@ -251,11 +249,13 @@ impl<'a> StackLayouter<'a> {
fn finish_region(&mut self) { fn finish_region(&mut self) {
// Determine the size of the stack in this region dependening on whether // Determine the size of the stack in this region dependening on whether
// the region expands. // the region expands.
let used = self.used.to_axes(self.axis); let mut size = self
let mut size = self.expand.select(self.full, used); .expand
.select(self.initial, self.used.to_axes(self.axis))
.min(self.initial);
// Expand fully if there are fr spacings. // Expand fully if there are fr spacings.
let full = self.full.get(self.axis); let full = self.initial.get(self.axis);
let remaining = full - self.used.main; let remaining = full - self.used.main;
if self.fr.get() > 0.0 && full.is_finite() { if self.fr.get() > 0.0 && full.is_finite() {
self.used.main = full; self.used.main = full;
@ -303,7 +303,7 @@ impl<'a> StackLayouter<'a> {
// Advance to the next region. // Advance to the next region.
self.regions.next(); self.regions.next();
self.full = self.regions.size; self.initial = self.regions.size;
self.used = Gen::zero(); self.used = Gen::zero();
self.fr = Fr::zero(); self.fr = Fr::zero();
self.finished.push(output); self.finished.push(output);

View File

@ -34,7 +34,7 @@ impl Fr {
pub fn share(self, total: Self, remaining: Abs) -> Abs { pub fn share(self, total: Self, remaining: Abs) -> Abs {
let ratio = self / total; let ratio = self / total;
if ratio.is_finite() && remaining.is_finite() { if ratio.is_finite() && remaining.is_finite() {
ratio * remaining (ratio * remaining).max(Abs::zero())
} else { } else {
Abs::zero() Abs::zero()
} }

BIN
tests/ref/bugs/grid-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,8 @@
// Ensure that the list does not jump to the third page.
---
#set page(height: 70pt)
#v(40pt)
The following:
+ A
+ B