move per-region state to Current

This commit is contained in:
PgBiel 2025-04-19 20:07:15 -03:00
parent 1e3719a9ba
commit 9b5c77a3a3
3 changed files with 122 additions and 105 deletions

View File

@ -43,6 +43,36 @@ pub struct GridLayouter<'a> {
/// Rowspans not yet laid out because not all of their spanned rows were /// Rowspans not yet laid out because not all of their spanned rows were
/// laid out yet. /// laid out yet.
pub(super) rowspans: Vec<Rowspan>, pub(super) rowspans: Vec<Rowspan>,
/// Grid layout state for the current region.
pub(super) current: Current,
/// Frames for finished regions.
pub(super) finished: Vec<Frame>,
/// The amount and height of header rows on each finished region.
pub(super) finished_header_rows: Vec<FinishedHeaderRowInfo>,
/// Whether this is an RTL grid.
pub(super) is_rtl: bool,
/// Currently repeating headers, one per level.
/// Sorted by increasing levels.
///
/// Note that some levels may be absent, in particular level 0, which does
/// not exist (so the first level is >= 1).
pub(super) repeating_headers: Vec<&'a Header>,
/// Headers, repeating or not, awaiting their first successful layout.
/// Sorted by increasing levels.
pub(super) pending_headers: &'a [Repeatable<Header>],
pub(super) upcoming_headers: &'a [Repeatable<Header>],
/// If this is `Some`, this will receive the currently laid out row's
/// height if it is auto or relative. This is used for header height
/// calculation.
/// TODO: consider refactoring this into something nicer.
pub(super) current_row_height: Option<Abs>,
/// The span of the grid element.
pub(super) span: Span,
}
/// Grid layout state for the current region. This should be reset or updated
/// on each region break.
pub struct Current {
/// The initial size of the current region before we started subtracting. /// The initial size of the current region before we started subtracting.
pub(super) initial: Size, pub(super) initial: Size,
/// The amount of repeated header rows at the start of the current region. /// The amount of repeated header rows at the start of the current region.
@ -68,32 +98,6 @@ pub struct GridLayouter<'a> {
/// ///
/// A value of zero indicates no headers were placed. /// A value of zero indicates no headers were placed.
pub(super) current_last_repeated_header_end: usize, pub(super) current_last_repeated_header_end: usize,
/// Frames for finished regions.
pub(super) finished: Vec<Frame>,
/// The amount and height of header rows on each finished region.
pub(super) finished_header_rows: Vec<FinishedHeaderRowInfo>,
/// Whether this is an RTL grid.
pub(super) is_rtl: bool,
/// Currently repeating headers, one per level.
/// Sorted by increasing levels.
///
/// Note that some levels may be absent, in particular level 0, which does
/// not exist (so the first level is >= 1).
pub(super) repeating_headers: Vec<&'a Header>,
/// Headers, repeating or not, awaiting their first successful layout.
/// Sorted by increasing levels.
pub(super) pending_headers: &'a [Repeatable<Header>],
pub(super) upcoming_headers: &'a [Repeatable<Header>],
/// The height for each repeating header that was placed in this region.
/// Note that this includes headers not at the top of the region (pending
/// headers), and excludes headers removed by virtue of a new, conflicting
/// header being found.
pub(super) repeating_header_heights: Vec<Abs>,
/// If this is `Some`, this will receive the currently laid out row's
/// height if it is auto or relative. This is used for header height
/// calculation.
/// TODO: consider refactoring this into something nicer.
pub(super) current_row_height: Option<Abs>,
/// Stores the length of `lrows` before a sequence of trailing rows /// Stores the length of `lrows` before a sequence of trailing rows
/// equipped with orphan prevention were laid out. In this case, if no more /// equipped with orphan prevention were laid out. In this case, if no more
/// rows are laid out after those rows before the region ends, the rows /// rows are laid out after those rows before the region ends, the rows
@ -116,11 +120,14 @@ pub struct GridLayouter<'a> {
/// In particular, non-repeating headers only occupy the initial region, /// In particular, non-repeating headers only occupy the initial region,
/// but disappear on new regions, so they can be ignored. /// but disappear on new regions, so they can be ignored.
pub(super) repeating_header_height: Abs, pub(super) repeating_header_height: Abs,
/// The height for each repeating header that was placed in this region.
/// Note that this includes headers not at the top of the region (pending
/// headers), and excludes headers removed by virtue of a new, conflicting
/// header being found.
pub(super) repeating_header_heights: Vec<Abs>,
/// The simulated footer height for this region. /// The simulated footer height for this region.
/// The simulation occurs before any rows are laid out for a region. /// The simulation occurs before any rows are laid out for a region.
pub(super) footer_height: Abs, pub(super) footer_height: Abs,
/// The span of the grid element.
pub(super) span: Span,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -188,21 +195,23 @@ impl<'a> GridLayouter<'a> {
lrows: vec![], lrows: vec![],
unbreakable_rows_left: 0, unbreakable_rows_left: 0,
rowspans: vec![], rowspans: vec![],
initial: regions.size,
current_repeating_header_rows: 0,
current_last_repeated_header_end: 0,
finished: vec![], finished: vec![],
finished_header_rows: vec![], finished_header_rows: vec![],
is_rtl: TextElem::dir_in(styles) == Dir::RTL, is_rtl: TextElem::dir_in(styles) == Dir::RTL,
repeating_headers: vec![], repeating_headers: vec![],
upcoming_headers: &grid.headers, upcoming_headers: &grid.headers,
repeating_header_heights: vec![],
pending_headers: Default::default(), pending_headers: Default::default(),
lrows_orphan_snapshot: None,
current_row_height: None, current_row_height: None,
header_height: Abs::zero(), current: Current {
repeating_header_height: Abs::zero(), initial: regions.size,
footer_height: Abs::zero(), current_repeating_header_rows: 0,
current_last_repeated_header_end: 0,
lrows_orphan_snapshot: None,
header_height: Abs::zero(),
repeating_header_height: Abs::zero(),
repeating_header_heights: vec![],
footer_height: Abs::zero(),
},
span, span,
} }
} }
@ -215,7 +224,7 @@ impl<'a> GridLayouter<'a> {
// Ensure rows in the first region will be aware of the possible // Ensure rows in the first region will be aware of the possible
// presence of the footer. // presence of the footer.
self.prepare_footer(footer, engine, 0)?; self.prepare_footer(footer, engine, 0)?;
self.regions.size.y -= self.footer_height; self.regions.size.y -= self.current.footer_height;
} }
let mut y = 0; let mut y = 0;
@ -344,7 +353,7 @@ impl<'a> GridLayouter<'a> {
self.layout_relative_row(engine, disambiguator, v, y)? self.layout_relative_row(engine, disambiguator, v, y)?
} }
Sizing::Fr(v) => { Sizing::Fr(v) => {
self.lrows_orphan_snapshot = None; self.current.lrows_orphan_snapshot = None;
self.lrows.push(Row::Fr(v, y, disambiguator)) self.lrows.push(Row::Fr(v, y, disambiguator))
} }
} }
@ -1094,7 +1103,7 @@ impl<'a> GridLayouter<'a> {
target.set_max( target.set_max(
region.y region.y
- if i > 0 { - if i > 0 {
self.repeating_header_height + self.footer_height self.current.repeating_header_height + self.current.footer_height
} else { } else {
Abs::zero() Abs::zero()
}, },
@ -1325,7 +1334,7 @@ impl<'a> GridLayouter<'a> {
&& !self.regions.size.y.fits(height) && !self.regions.size.y.fits(height)
&& may_progress_with_offset( && may_progress_with_offset(
self.regions, self.regions,
self.header_height + self.footer_height, self.current.header_height + self.current.footer_height,
) )
{ {
self.finish_region(engine, false)?; self.finish_region(engine, false)?;
@ -1459,7 +1468,7 @@ impl<'a> GridLayouter<'a> {
fn push_row(&mut self, frame: Frame, y: usize, is_last: bool) { fn push_row(&mut self, frame: Frame, y: usize, is_last: bool) {
// There is now a row after the rows equipped with orphan prevention, // There is now a row after the rows equipped with orphan prevention,
// so no need to remove them anymore. // so no need to remove them anymore.
self.lrows_orphan_snapshot = None; self.current.lrows_orphan_snapshot = None;
self.regions.size.y -= frame.height(); self.regions.size.y -= frame.height();
self.lrows.push(Row::Frame(frame, y, is_last)); self.lrows.push(Row::Frame(frame, y, is_last));
} }
@ -1470,11 +1479,11 @@ impl<'a> GridLayouter<'a> {
engine: &mut Engine, engine: &mut Engine,
last: bool, last: bool,
) -> SourceResult<()> { ) -> SourceResult<()> {
if let Some(orphan_snapshot) = self.lrows_orphan_snapshot.take() { if let Some(orphan_snapshot) = self.current.lrows_orphan_snapshot.take() {
if !last { if !last {
self.lrows.truncate(orphan_snapshot); self.lrows.truncate(orphan_snapshot);
self.current_repeating_header_rows = self.current.current_repeating_header_rows =
self.current_repeating_header_rows.min(orphan_snapshot); self.current.current_repeating_header_rows.min(orphan_snapshot);
} }
} }
@ -1485,11 +1494,12 @@ impl<'a> GridLayouter<'a> {
{ {
// Remove the last row in the region if it is a gutter row. // Remove the last row in the region if it is a gutter row.
self.lrows.pop().unwrap(); self.lrows.pop().unwrap();
self.current_repeating_header_rows = self.current.current_repeating_header_rows =
self.current_repeating_header_rows.min(self.lrows.len()); self.current.current_repeating_header_rows.min(self.lrows.len());
} }
let footer_would_be_widow = if let Some(last_header_row) = self let footer_would_be_widow = if let Some(last_header_row) = self
.current
.current_repeating_header_rows .current_repeating_header_rows
.checked_sub(1) .checked_sub(1)
.and_then(|last_header_index| self.lrows.get(last_header_index)) .and_then(|last_header_index| self.lrows.get(last_header_index))
@ -1502,21 +1512,21 @@ impl<'a> GridLayouter<'a> {
.as_ref() .as_ref()
.and_then(Repeatable::as_repeated) .and_then(Repeatable::as_repeated)
.is_none_or(|footer| footer.start != last_header_end) .is_none_or(|footer| footer.start != last_header_end)
&& self.lrows.len() == self.current_repeating_header_rows && self.lrows.len() == self.current.current_repeating_header_rows
&& may_progress_with_offset( && may_progress_with_offset(
self.regions, self.regions,
// Since we're trying to find a region where to place all // Since we're trying to find a region where to place all
// repeating + pending headers, it makes sense to use // repeating + pending headers, it makes sense to use
// 'header_height' and include even non-repeating pending // 'header_height' and include even non-repeating pending
// headers for this check. // headers for this check.
self.header_height + self.footer_height, self.current.header_height + self.current.footer_height,
) )
{ {
// Header and footer would be alone in this region, but there are more // Header and footer would be alone in this region, but there are more
// rows beyond the header and the footer. Push an empty region. // rows beyond the header and the footer. Push an empty region.
self.lrows.clear(); self.lrows.clear();
self.current_last_repeated_header_end = 0; self.current.current_last_repeated_header_end = 0;
self.current_repeating_header_rows = 0; self.current.current_repeating_header_rows = 0;
true true
} else { } else {
false false
@ -1533,7 +1543,7 @@ impl<'a> GridLayouter<'a> {
// This header height isn't doing much as we just confirmed // This header height isn't doing much as we just confirmed
// that there are no headers in this region, but let's keep // that there are no headers in this region, but let's keep
// it here for correctness. It will add zero anyway. // it here for correctness. It will add zero anyway.
self.header_height + self.footer_height, self.current.header_height + self.current.footer_height,
) )
&& footer.start != 0 && footer.start != 0
} else { } else {
@ -1564,9 +1574,9 @@ impl<'a> GridLayouter<'a> {
// 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 = Size::new(self.width, used).min(self.initial); let mut size = Size::new(self.width, used).min(self.current.initial);
if fr.get() > 0.0 && self.initial.y.is_finite() { if fr.get() > 0.0 && self.current.initial.y.is_finite() {
size.y = self.initial.y; size.y = self.current.initial.y;
} }
// The frame for the region. // The frame for the region.
@ -1588,7 +1598,7 @@ impl<'a> GridLayouter<'a> {
}; };
let height = frame.height(); let height = frame.height();
if i < self.current_repeating_header_rows { if i < self.current.current_repeating_header_rows {
header_row_height += height; header_row_height += height;
} }
@ -1688,18 +1698,18 @@ impl<'a> GridLayouter<'a> {
output, output,
rrows, rrows,
FinishedHeaderRowInfo { FinishedHeaderRowInfo {
repeated: self.current_repeating_header_rows, repeated: self.current.current_repeating_header_rows,
last_repeated_header_end: self.current_last_repeated_header_end, last_repeated_header_end: self.current.current_last_repeated_header_end,
height: header_row_height, height: header_row_height,
}, },
); );
if !last { if !last {
self.current_repeating_header_rows = 0; self.current.current_repeating_header_rows = 0;
self.current_last_repeated_header_end = 0; self.current.current_last_repeated_header_end = 0;
self.header_height = Abs::zero(); self.current.header_height = Abs::zero();
self.repeating_header_height = Abs::zero(); self.current.repeating_header_height = Abs::zero();
self.repeating_header_heights.clear(); self.current.repeating_header_heights.clear();
let disambiguator = self.finished.len(); let disambiguator = self.finished.len();
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer { if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
@ -1710,7 +1720,7 @@ impl<'a> GridLayouter<'a> {
// Note that header layout will only subtract this again if it has // Note that header layout will only subtract this again if it has
// to skip regions to fit headers, so there is no risk of // to skip regions to fit headers, so there is no risk of
// subtracting this twice. // subtracting this twice.
self.regions.size.y -= self.footer_height; self.regions.size.y -= self.current.footer_height;
if !self.repeating_headers.is_empty() || !self.pending_headers.is_empty() { if !self.repeating_headers.is_empty() || !self.pending_headers.is_empty() {
// Add headers to the new region. // Add headers to the new region.
@ -1732,14 +1742,14 @@ impl<'a> GridLayouter<'a> {
self.finished.push(output); self.finished.push(output);
self.rrows.push(resolved_rows); self.rrows.push(resolved_rows);
self.regions.next(); self.regions.next();
self.initial = self.regions.size; self.current.initial = self.regions.size;
if !self.grid.headers.is_empty() { if !self.grid.headers.is_empty() {
self.finished_header_rows.push(header_row_info); self.finished_header_rows.push(header_row_info);
} }
// Ensure orphan prevention is handled before resolving rows. // Ensure orphan prevention is handled before resolving rows.
debug_assert!(self.lrows_orphan_snapshot.is_none()); debug_assert!(self.current.lrows_orphan_snapshot.is_none());
} }
} }

View File

@ -142,15 +142,16 @@ impl<'a> GridLayouter<'a> {
// Ensure upcoming rows won't see that these headers will occupy any // Ensure upcoming rows won't see that these headers will occupy any
// space in future regions anymore. // space in future regions anymore.
for removed_height in self.repeating_header_heights.drain(first_conflicting_pos..) for removed_height in
self.current.repeating_header_heights.drain(first_conflicting_pos..)
{ {
self.repeating_header_height -= removed_height; self.current.repeating_header_height -= removed_height;
} }
// Non-repeating headers stop at the pending stage for orphan // Non-repeating headers stop at the pending stage for orphan
// prevention only. Flushing pending headers, so those will no longer // prevention only. Flushing pending headers, so those will no longer
// appear in a future region. // appear in a future region.
self.header_height = self.repeating_header_height; self.current.header_height = self.current.repeating_header_height;
// Let's try to place them at least once. // Let's try to place them at least once.
// This might be a waste as we could generate an orphan and thus have // This might be a waste as we could generate an orphan and thus have
@ -223,7 +224,7 @@ impl<'a> GridLayouter<'a> {
// available size for consistency with the first region, so we // available size for consistency with the first region, so we
// need to consider the footer when evaluating if skipping yet // need to consider the footer when evaluating if skipping yet
// another region would make a difference. // another region would make a difference.
self.footer_height, self.current.footer_height,
) )
{ {
// Advance regions without any output until we can place the // Advance regions without any output until we can place the
@ -238,7 +239,7 @@ impl<'a> GridLayouter<'a> {
// if 'full' changes? (Assuming height doesn't change for now...) // if 'full' changes? (Assuming height doesn't change for now...)
skipped_region = true; skipped_region = true;
self.regions.size.y -= self.footer_height; self.regions.size.y -= self.current.footer_height;
} }
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer { if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
@ -246,11 +247,11 @@ impl<'a> GridLayouter<'a> {
// Simulate the footer again; the region's 'full' might have // Simulate the footer again; the region's 'full' might have
// changed. // changed.
// TODO: maybe this should go in the loop, a bit hacky as is... // TODO: maybe this should go in the loop, a bit hacky as is...
self.regions.size.y += self.footer_height; self.regions.size.y += self.current.footer_height;
self.footer_height = self self.current.footer_height = self
.simulate_footer(footer, &self.regions, engine, disambiguator)? .simulate_footer(footer, &self.regions, engine, disambiguator)?
.height; .height;
self.regions.size.y -= self.footer_height; self.regions.size.y -= self.current.footer_height;
} }
} }
@ -265,22 +266,22 @@ impl<'a> GridLayouter<'a> {
// within 'layout_row'. // within 'layout_row'.
self.unbreakable_rows_left += repeating_header_rows + pending_header_rows; self.unbreakable_rows_left += repeating_header_rows + pending_header_rows;
self.current_last_repeated_header_end = self.current.current_last_repeated_header_end =
self.repeating_headers.last().map(|h| h.end).unwrap_or_default(); self.repeating_headers.last().map(|h| h.end).unwrap_or_default();
// Reset the header height for this region. // Reset the header height for this region.
// It will be re-calculated when laying out each header row. // It will be re-calculated when laying out each header row.
self.header_height = Abs::zero(); self.current.header_height = Abs::zero();
self.repeating_header_height = Abs::zero(); self.current.repeating_header_height = Abs::zero();
self.repeating_header_heights.clear(); self.current.repeating_header_heights.clear();
// Use indices to avoid double borrow. We don't mutate headers in // Use indices to avoid double borrow. We don't mutate headers in
// 'layout_row' so this is fine. // 'layout_row' so this is fine.
let mut i = 0; let mut i = 0;
while let Some(&header) = self.repeating_headers.get(i) { while let Some(&header) = self.repeating_headers.get(i) {
let header_height = self.layout_header_rows(header, engine, disambiguator)?; let header_height = self.layout_header_rows(header, engine, disambiguator)?;
self.header_height += header_height; self.current.header_height += header_height;
self.repeating_header_height += header_height; self.current.repeating_header_height += header_height;
// We assume that this vector will be sorted according // We assume that this vector will be sorted according
// to increasing levels like 'repeating_headers' and // to increasing levels like 'repeating_headers' and
@ -300,26 +301,26 @@ impl<'a> GridLayouter<'a> {
// headers which have now stopped repeating. They are always at // headers which have now stopped repeating. They are always at
// the end and new pending headers respect the existing sort, // the end and new pending headers respect the existing sort,
// so the vector will remain sorted. // so the vector will remain sorted.
self.repeating_header_heights.push(header_height); self.current.repeating_header_heights.push(header_height);
i += 1; i += 1;
} }
self.current_repeating_header_rows = self.lrows.len(); self.current.current_repeating_header_rows = self.lrows.len();
if !self.pending_headers.is_empty() { if !self.pending_headers.is_empty() {
// Restore snapshot: if pending headers placed again turn out to be // Restore snapshot: if pending headers placed again turn out to be
// orphans, remove their rows again. // orphans, remove their rows again.
self.lrows_orphan_snapshot = Some(self.lrows.len()); self.current.lrows_orphan_snapshot = Some(self.lrows.len());
} }
for header in self.pending_headers { for header in self.pending_headers {
let header_height = let header_height =
self.layout_header_rows(header.unwrap(), engine, disambiguator)?; self.layout_header_rows(header.unwrap(), engine, disambiguator)?;
self.header_height += header_height; self.current.header_height += header_height;
if matches!(header, Repeatable::Repeated(_)) { if matches!(header, Repeatable::Repeated(_)) {
self.repeating_header_height += header_height; self.current.repeating_header_height += header_height;
self.repeating_header_heights.push(header_height); self.current.repeating_header_heights.push(header_height);
} }
} }
@ -359,7 +360,7 @@ impl<'a> GridLayouter<'a> {
// 'header_height == repeating_header_height' here // 'header_height == repeating_header_height' here
// (there won't be any pending headers at this point, other // (there won't be any pending headers at this point, other
// than the ones we are about to place). // than the ones we are about to place).
self.header_height + self.footer_height, self.current.header_height + self.current.footer_height,
) )
{ {
// Note that, after the first region skip, the new headers will go // Note that, after the first region skip, the new headers will go
@ -382,10 +383,10 @@ impl<'a> GridLayouter<'a> {
// region, so multi-page rows and cells can effectively ignore // region, so multi-page rows and cells can effectively ignore
// this header. // this header.
if !short_lived { if !short_lived {
self.header_height += header_height; self.current.header_height += header_height;
if matches!(header, Repeatable::Repeated(_)) { if matches!(header, Repeatable::Repeated(_)) {
self.repeating_header_height += header_height; self.current.repeating_header_height += header_height;
self.repeating_header_heights.push(header_height); self.current.repeating_header_heights.push(header_height);
} }
} }
} }
@ -393,7 +394,7 @@ impl<'a> GridLayouter<'a> {
// Remove new headers at the end of the region if upcoming child doesn't fit. // Remove new headers at the end of the region if upcoming child doesn't fit.
// TODO: Short lived if footer comes afterwards // TODO: Short lived if footer comes afterwards
if !short_lived { if !short_lived {
self.lrows_orphan_snapshot = Some(initial_row_count); self.current.lrows_orphan_snapshot = Some(initial_row_count);
} }
Ok(()) Ok(())
@ -466,7 +467,7 @@ impl<'a> GridLayouter<'a> {
// That is unnecessary at the moment as 'prepare_footers' is only // That is unnecessary at the moment as 'prepare_footers' is only
// called at the start of the region, but what about when we can have // called at the start of the region, but what about when we can have
// footers in the middle of the region? Let's think about this then. // footers in the middle of the region? Let's think about this then.
self.footer_height = if skipped_region { self.current.footer_height = if skipped_region {
// Simulate the footer again; the region's 'full' might have // Simulate the footer again; the region's 'full' might have
// changed. // changed.
self.simulate_footer(footer, &self.regions, engine, disambiguator)? self.simulate_footer(footer, &self.regions, engine, disambiguator)?
@ -489,7 +490,7 @@ impl<'a> GridLayouter<'a> {
// Ensure footer rows have their own height available. // Ensure footer rows have their own height available.
// Won't change much as we're creating an unbreakable row group // Won't change much as we're creating an unbreakable row group
// anyway, so this is mostly for correctness. // anyway, so this is mostly for correctness.
self.regions.size.y += self.footer_height; self.regions.size.y += self.current.footer_height;
let footer_len = self.grid.rows.len() - footer.start; let footer_len = self.grid.rows.len() - footer.start;
self.unbreakable_rows_left += footer_len; self.unbreakable_rows_left += footer_len;

View File

@ -263,7 +263,7 @@ impl GridLayouter<'_> {
// due to orphan/widow prevention, which explains the usage of // due to orphan/widow prevention, which explains the usage of
// 'header_height' (include non-repeating but pending headers) rather // 'header_height' (include non-repeating but pending headers) rather
// than 'repeating_header_height'. // than 'repeating_header_height'.
self.header_height + self.footer_height, self.current.header_height + self.current.footer_height,
) )
{ {
self.finish_region(engine, false)?; self.finish_region(engine, false)?;
@ -422,7 +422,9 @@ impl GridLayouter<'_> {
let mapped_regions = self.regions.map(&mut custom_backlog, |size| { let mapped_regions = self.regions.map(&mut custom_backlog, |size| {
Size::new( Size::new(
size.x, size.x,
size.y - self.repeating_header_height - self.footer_height, size.y
- self.current.repeating_header_height
- self.current.footer_height,
) )
}); });
@ -535,7 +537,7 @@ impl GridLayouter<'_> {
// and unbreakable rows in general, so there is no risk // and unbreakable rows in general, so there is no risk
// of accessing an incomplete list of rows. // of accessing an incomplete list of rows.
let initial_header_height = self.lrows let initial_header_height = self.lrows
[..self.current_repeating_header_rows] [..self.current.current_repeating_header_rows]
.iter() .iter()
.map(|row| match row { .map(|row| match row {
Row::Frame(frame, _, _) => frame.height(), Row::Frame(frame, _, _) => frame.height(),
@ -543,7 +545,9 @@ impl GridLayouter<'_> {
}) })
.sum(); .sum();
self.initial.y - initial_header_height - self.footer_height self.current.initial.y
- initial_header_height
- self.current.footer_height
} else { } else {
// When measuring unbreakable auto rows, infinite // When measuring unbreakable auto rows, infinite
// height is available for content to expand. // height is available for content to expand.
@ -559,7 +563,8 @@ impl GridLayouter<'_> {
// Assume only repeating headers will survive starting at // Assume only repeating headers will survive starting at
// the next region. // the next region.
let backlog = self.regions.backlog.iter().map(|&size| { let backlog = self.regions.backlog.iter().map(|&size| {
size - self.repeating_header_height - self.footer_height size - self.current.repeating_header_height
- self.current.footer_height
}); });
heights_up_to_current_region.chain(backlog).collect::<Vec<_>>() heights_up_to_current_region.chain(backlog).collect::<Vec<_>>()
@ -574,10 +579,10 @@ impl GridLayouter<'_> {
height = *rowspan_height; height = *rowspan_height;
backlog = None; backlog = None;
full = rowspan_full; full = rowspan_full;
last = self last = self.regions.last.map(|size| {
.regions size - self.current.repeating_header_height
.last - self.current.footer_height
.map(|size| size - self.repeating_header_height - self.footer_height); });
} else { } else {
// The rowspan started in the current region, as its vector // The rowspan started in the current region, as its vector
// of heights in regions is currently empty. // of heights in regions is currently empty.
@ -782,7 +787,8 @@ impl GridLayouter<'_> {
// Subtract the repeating header and footer height, since that's // Subtract the repeating header and footer height, since that's
// the height we used when subtracting from the region backlog's // the height we used when subtracting from the region backlog's
// heights while measuring cells. // heights while measuring cells.
simulated_regions.size.y -= self.repeating_header_height + self.footer_height; simulated_regions.size.y -=
self.current.repeating_header_height + self.current.footer_height;
} }
if let Some(original_last_resolved_size) = last_resolved_size { if let Some(original_last_resolved_size) = last_resolved_size {
@ -921,8 +927,8 @@ impl GridLayouter<'_> {
// rowspan, since headers and footers are unbreakable, so // rowspan, since headers and footers are unbreakable, so
// assuming the repeating header height and footer height // assuming the repeating header height and footer height
// won't change is safe. // won't change is safe.
self.repeating_header_height, self.current.repeating_header_height,
self.footer_height, self.current.footer_height,
); );
let total_spanned_height = rowspan_simulator.simulate_rowspan_layout( let total_spanned_height = rowspan_simulator.simulate_rowspan_layout(
@ -1006,7 +1012,7 @@ impl GridLayouter<'_> {
extra_amount_to_grow -= simulated_regions.size.y.max(Abs::zero()); extra_amount_to_grow -= simulated_regions.size.y.max(Abs::zero());
simulated_regions.next(); simulated_regions.next();
simulated_regions.size.y -= simulated_regions.size.y -=
self.repeating_header_height + self.footer_height; self.current.repeating_header_height + self.current.footer_height;
disambiguator += 1; disambiguator += 1;
} }
simulated_regions.size.y -= extra_amount_to_grow; simulated_regions.size.y -= extra_amount_to_grow;