change Repeatable to struct

This commit is contained in:
PgBiel 2025-06-10 02:31:53 -03:00
parent 9139baf263
commit 75a4cfb2e4
4 changed files with 54 additions and 59 deletions

View File

@ -274,7 +274,8 @@ impl<'a> GridLayouter<'a> {
pub fn layout(mut self, engine: &mut Engine) -> SourceResult<Fragment> { pub fn layout(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
self.measure_columns(engine)?; self.measure_columns(engine)?;
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer { if let Some(footer) = self.grid.footer.as_ref().and_then(Repeatable::as_repeated)
{
// 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)?;
@ -296,7 +297,9 @@ impl<'a> GridLayouter<'a> {
} }
} }
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer { if let Some(footer) =
self.grid.footer.as_ref().and_then(Repeatable::as_repeated)
{
if y >= footer.start { if y >= footer.start {
if y == footer.start { if y == footer.start {
self.layout_footer(footer, engine, self.finished.len())?; self.layout_footer(footer, engine, self.finished.len())?;
@ -1564,14 +1567,15 @@ impl<'a> GridLayouter<'a> {
// TODO(subfooters): explicitly check for short-lived footers. // TODO(subfooters): explicitly check for short-lived footers.
// TODO(subfooters): widow prevention for non-repeated footers with a // TODO(subfooters): widow prevention for non-repeated footers with a
// similar mechanism / when implementing multiple footers. // similar mechanism / when implementing multiple footers.
let footer_would_be_widow = let footer_would_be_widow = matches!(&self.grid.footer, Some(footer) if footer.repeated)
matches!(self.grid.footer, Some(Repeatable::Repeated(_))) && self.current.lrows.is_empty()
&& self.current.lrows.is_empty() && self.current.could_progress_at_top;
&& self.current.could_progress_at_top;
let mut laid_out_footer_start = None; let mut laid_out_footer_start = None;
if !footer_would_be_widow { if !footer_would_be_widow {
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer { if let Some(footer) =
self.grid.footer.as_ref().and_then(Repeatable::as_repeated)
{
// Don't layout the footer if it would be alone with the header // Don't layout the footer if it would be alone with the header
// in the page (hence the widow check), and don't layout it // in the page (hence the widow check), and don't layout it
// twice (check below). // twice (check below).
@ -1735,7 +1739,9 @@ impl<'a> GridLayouter<'a> {
self.current.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(footer) =
self.grid.footer.as_ref().and_then(Repeatable::as_repeated)
{
self.prepare_footer(footer, engine, disambiguator)?; self.prepare_footer(footer, engine, disambiguator)?;
} }

View File

@ -170,7 +170,7 @@ impl<'a> GridLayouter<'a> {
} }
for header in self.pending_headers { for header in self.pending_headers {
if let Repeatable::Repeated(header) = header { if header.repeated {
// Vector remains sorted by increasing levels: // Vector remains sorted by increasing levels:
// - 'pending_headers' themselves are sorted, since we only // - 'pending_headers' themselves are sorted, since we only
// push non-mutually-conflicting headers at a time. // push non-mutually-conflicting headers at a time.
@ -238,8 +238,8 @@ impl<'a> GridLayouter<'a> {
self.current.initial_after_repeats = self.regions.size.y; self.current.initial_after_repeats = self.regions.size.y;
} }
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer { if let Some(footer) = &self.grid.footer {
if skipped_region { if footer.repeated && 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.regions.size.y += self.current.footer_height; self.regions.size.y += self.current.footer_height;
@ -321,13 +321,13 @@ impl<'a> GridLayouter<'a> {
let mut has_non_repeated_pending_header = false; let mut has_non_repeated_pending_header = false;
for header in self.pending_headers { for header in self.pending_headers {
if matches!(header, Repeatable::NotRepeated(_)) { if !header.repeated {
self.current.initial_after_repeats = self.regions.size.y; self.current.initial_after_repeats = self.regions.size.y;
has_non_repeated_pending_header = true; has_non_repeated_pending_header = true;
} }
let header_height = let header_height =
self.layout_header_rows(header.unwrap(), engine, disambiguator, false)?; self.layout_header_rows(header.unwrap(), engine, disambiguator, false)?;
if matches!(header, Repeatable::Repeated(_)) { if header.repeated {
self.current.repeating_header_height += header_height; self.current.repeating_header_height += header_height;
self.current.repeating_header_heights.push(header_height); self.current.repeating_header_heights.push(header_height);
} }
@ -410,7 +410,7 @@ impl<'a> GridLayouter<'a> {
// it is guaranteed this header won't appear in a future // it is guaranteed this header won't appear in a future
// 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 && matches!(header, Repeatable::Repeated(_)) { if !short_lived && header.repeated {
self.current.repeating_header_height += header_height; self.current.repeating_header_height += header_height;
self.current.repeating_header_heights.push(header_height); self.current.repeating_header_heights.push(header_height);
if at_top { if at_top {
@ -519,11 +519,7 @@ impl<'a> GridLayouter<'a> {
// anyway, so this is mostly for correctness. // anyway, so this is mostly for correctness.
self.regions.size.y += self.current.footer_height; self.regions.size.y += self.current.footer_height;
let repeats = self let repeats = self.grid.footer.as_ref().is_some_and(|f| f.repeated);
.grid
.footer
.as_ref()
.is_some_and(|f| matches!(f, Repeatable::Repeated(_)));
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

@ -238,8 +238,8 @@ impl GridLayouter<'_> {
// current row is dynamic and depends on the amount of upcoming // current row is dynamic and depends on the amount of upcoming
// unbreakable cells (with or without a rowspan setting). // unbreakable cells (with or without a rowspan setting).
let mut amount_unbreakable_rows = None; let mut amount_unbreakable_rows = None;
if let Some(Repeatable::NotRepeated(footer)) = &self.grid.footer { if let Some(footer) = &self.grid.footer {
if current_row >= footer.start { if !footer.repeated && current_row >= footer.start {
// Non-repeated footer, so keep it unbreakable. // Non-repeated footer, so keep it unbreakable.
// //
// TODO(subfooters): This will become unnecessary // TODO(subfooters): This will become unnecessary
@ -400,7 +400,7 @@ impl GridLayouter<'_> {
if breakable if breakable
&& (!self.repeating_headers.is_empty() && (!self.repeating_headers.is_empty()
|| !self.pending_headers.is_empty() || !self.pending_headers.is_empty()
|| matches!(self.grid.footer, Some(Repeatable::Repeated(_)))) || matches!(&self.grid.footer, Some(footer) if footer.repeated))
{ {
// Subtract header and footer height from all upcoming regions // Subtract header and footer height from all upcoming regions
// when measuring the cell, including the last repeated region. // when measuring the cell, including the last repeated region.
@ -1176,14 +1176,15 @@ impl<'a> RowspanSimulator<'a> {
(None, Abs::zero()) (None, Abs::zero())
}; };
let footer_height = let footer_height = if let Some(footer) =
if let Some(Repeatable::Repeated(footer)) = &layouter.grid.footer { layouter.grid.footer.as_ref().and_then(Repeatable::as_repeated)
layouter {
.simulate_footer(footer, &self.regions, engine, disambiguator)? layouter
.height .simulate_footer(footer, &self.regions, engine, disambiguator)?
} else { .height
Abs::zero() } else {
}; Abs::zero()
};
let mut skipped_region = false; let mut skipped_region = false;
@ -1211,7 +1212,9 @@ impl<'a> RowspanSimulator<'a> {
}; };
} }
if let Some(Repeatable::Repeated(footer)) = &layouter.grid.footer { if let Some(footer) =
layouter.grid.footer.as_ref().and_then(Repeatable::as_repeated)
{
self.footer_height = if skipped_region { self.footer_height = if skipped_region {
// Simulate footers again, at the new region, as // Simulate footers again, at the new region, as
// the full region height may change. // the full region height may change.

View File

@ -465,23 +465,23 @@ impl Footer {
} }
} }
/// A possibly repeatable grid child. /// A possibly repeatable grid child (header or footer).
/// ///
/// It still exists even when not repeatable, but must not have additional /// It still exists even when not repeatable, but must not have additional
/// considerations by grid layout, other than for consistency (such as making /// considerations by grid layout, other than for consistency (such as making
/// a certain group of rows unbreakable). /// a certain group of rows unbreakable).
pub enum Repeatable<T> { pub struct Repeatable<T> {
/// The user asked this grid child to repeat. inner: T,
Repeated(T),
/// The user asked this grid child to not repeat. /// Whether the user requested the child to repeat.
NotRepeated(T), pub repeated: bool,
} }
impl<T> Deref for Repeatable<T> { impl<T> Deref for Repeatable<T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.unwrap() &self.inner
} }
} }
@ -490,28 +490,23 @@ impl<T> Repeatable<T> {
/// it repeats. /// it repeats.
#[inline] #[inline]
pub fn unwrap(&self) -> &T { pub fn unwrap(&self) -> &T {
match self { &self.inner
Self::Repeated(repeated) => repeated,
Self::NotRepeated(not_repeated) => not_repeated,
}
} }
/// Gets the value inside this repeatable, regardless of whether /// Gets the value inside this repeatable, regardless of whether
/// it repeats (mutably). /// it repeats (mutably).
#[inline] #[inline]
pub fn unwrap_mut(&mut self) -> &mut T { pub fn unwrap_mut(&mut self) -> &mut T {
match self { &mut self.inner
Self::Repeated(repeated) => repeated,
Self::NotRepeated(not_repeated) => not_repeated,
}
} }
/// Returns `Some` if the value is repeated, `None` otherwise. /// Returns `Some` if the value is repeated, `None` otherwise.
#[inline] #[inline]
pub fn as_repeated(&self) -> Option<&T> { pub fn as_repeated(&self) -> Option<&T> {
match self { if self.repeated {
Self::Repeated(repeated) => Some(repeated), Some(&self.inner)
Self::NotRepeated(_) => None, } else {
None
} }
} }
} }
@ -906,7 +901,7 @@ impl<'a> CellGrid<'a> {
#[inline] #[inline]
pub fn has_repeated_headers(&self) -> bool { pub fn has_repeated_headers(&self) -> bool {
self.headers.iter().any(|h| matches!(h, Repeatable::Repeated(_))) self.headers.iter().any(|h| h.repeated)
} }
} }
@ -1614,11 +1609,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
conflicting_header.unwrap_mut().short_lived = true; conflicting_header.unwrap_mut().short_lived = true;
} }
headers.push(if row_group.repeat { headers.push(Repeatable { inner: data, repeated: row_group.repeat });
Repeatable::Repeated(data)
} else {
Repeatable::NotRepeated(data)
});
} }
RowGroupKind::Footer => { RowGroupKind::Footer => {
@ -1920,10 +1911,9 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
// footers. // footers.
// TODO(subfooters): Switch this to marking the last N // TODO(subfooters): Switch this to marking the last N
// consecutive footers as short lived. // consecutive footers as short lived.
if repeat_footer && at_least_one_cell { Repeatable {
Repeatable::Repeated(footer) inner: footer,
} else { repeated: repeat_footer && at_least_one_cell,
Repeatable::NotRepeated(footer)
} }
}); });