only flush orphans outside of headers and footers

This commit is contained in:
PgBiel 2025-04-30 01:49:43 -03:00
parent 3ef2137619
commit 6c42f67b3d
4 changed files with 62 additions and 9 deletions

View File

@ -66,6 +66,16 @@ pub struct GridLayouter<'a> {
/// calculation. /// calculation.
/// TODO: consider refactoring this into something nicer. /// TODO: consider refactoring this into something nicer.
pub(super) current_row_height: Option<Abs>, pub(super) current_row_height: Option<Abs>,
/// This is `true` when laying out non-short lived headers and footers.
/// That is, headers and footers which are not immediately followed or
/// preceded (respectively) by conflicting headers and footers of same or
/// lower level, or the end or start of the table (respectively), which
/// would cause them to stop repeating.
///
/// If this is `false`, the next row to be laid out will remove an active
/// orphan snapshot and will flush pending headers, as there is no risk
/// that they will be orphans anymore.
pub(super) in_active_repeatable: bool,
/// The span of the grid element. /// The span of the grid element.
pub(super) span: Span, pub(super) span: Span,
} }
@ -211,6 +221,7 @@ impl<'a> GridLayouter<'a> {
upcoming_headers: &grid.headers, upcoming_headers: &grid.headers,
pending_headers: Default::default(), pending_headers: Default::default(),
current_row_height: None, current_row_height: None,
in_active_repeatable: false,
current: Current { current: Current {
initial: regions.size, initial: regions.size,
repeated_header_rows: 0, repeated_header_rows: 0,
@ -328,7 +339,9 @@ 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.flush_orphans(); if !self.in_active_repeatable {
self.flush_orphans();
}
self.lrows.push(Row::Fr(v, y, disambiguator)) self.lrows.push(Row::Fr(v, y, disambiguator))
} }
} }
@ -1441,9 +1454,11 @@ impl<'a> GridLayouter<'a> {
/// will be pushed for this particular row. It can be `false` for rows /// will be pushed for this particular row. It can be `false` for rows
/// spanning multiple regions. /// spanning multiple regions.
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, if !self.in_active_repeatable {
// so no need to remove them anymore. // There is now a row after the rows equipped with orphan
self.flush_orphans(); // prevention, so no need to keep moving them anymore.
self.flush_orphans();
}
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));
} }

View File

@ -57,11 +57,20 @@ impl<'a> GridLayouter<'a> {
y: usize, y: usize,
engine: &mut Engine, engine: &mut Engine,
disambiguator: usize, disambiguator: usize,
as_short_lived: bool,
) -> SourceResult<Option<Abs>> { ) -> SourceResult<Option<Abs>> {
let previous_row_height = let previous_row_height =
std::mem::replace(&mut self.current_row_height, Some(Abs::zero())); std::mem::replace(&mut self.current_row_height, Some(Abs::zero()));
let previous_in_active_repeatable =
std::mem::replace(&mut self.in_active_repeatable, !as_short_lived);
self.layout_row(y, engine, disambiguator)?; self.layout_row(y, engine, disambiguator)?;
_ = std::mem::replace(
&mut self.in_active_repeatable,
previous_in_active_repeatable,
);
Ok(std::mem::replace(&mut self.current_row_height, previous_row_height)) Ok(std::mem::replace(&mut self.current_row_height, previous_row_height))
} }
@ -73,11 +82,13 @@ impl<'a> GridLayouter<'a> {
header: &Header, header: &Header,
engine: &mut Engine, engine: &mut Engine,
disambiguator: usize, disambiguator: usize,
as_short_lived: bool,
) -> SourceResult<Abs> { ) -> SourceResult<Abs> {
let mut header_height = Abs::zero(); let mut header_height = Abs::zero();
for y in header.range() { for y in header.range() {
header_height += header_height += self
self.layout_header_row(y, engine, disambiguator)?.unwrap_or_default(); .layout_header_row(y, engine, disambiguator, as_short_lived)?
.unwrap_or_default();
} }
Ok(header_height) Ok(header_height)
} }
@ -270,7 +281,8 @@ impl<'a> GridLayouter<'a> {
// '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, false)?;
self.current.header_height += header_height; self.current.header_height += header_height;
self.current.repeating_header_height += header_height; self.current.repeating_header_height += header_height;
@ -307,7 +319,7 @@ impl<'a> GridLayouter<'a> {
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, false)?;
self.current.header_height += header_height; self.current.header_height += header_height;
if matches!(header, Repeatable::Repeated(_)) { if matches!(header, Repeatable::Repeated(_)) {
self.current.repeating_header_height += header_height; self.current.repeating_header_height += header_height;
@ -365,7 +377,8 @@ impl<'a> GridLayouter<'a> {
let initial_row_count = self.lrows.len(); let initial_row_count = self.lrows.len();
for header in headers { for header in headers {
let header_height = self.layout_header_rows(header.unwrap(), engine, 0)?; let header_height =
self.layout_header_rows(header.unwrap(), engine, 0, false)?;
// Only store this header height if it is actually going to // Only store this header height if it is actually going to
// become a pending header. Otherwise, pretend it's not a // become a pending header. Otherwise, pretend it's not a
@ -483,10 +496,24 @@ 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
.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;
for y in footer.start..self.grid.rows.len() { for y in footer.start..self.grid.rows.len() {
let previous_in_active_repeatable =
std::mem::replace(&mut self.in_active_repeatable, repeats);
self.layout_row(y, engine, disambiguator)?; self.layout_row(y, engine, disambiguator)?;
_ = std::mem::replace(
&mut self.in_active_repeatable,
previous_in_active_repeatable,
);
} }
Ok(()) Ok(())

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

View File

@ -684,6 +684,17 @@
grid.cell(x: 0)[end], grid.cell(x: 0)[end],
) )
--- grid-subheaders-non-repeating-header-before-multi-page-row ---
#set page(height: 6em)
#grid(
grid.header(
repeat: false,
[h]
),
[row #colbreak() row]
)
--- grid-subheaders-short-lived-no-orphan-prevention --- --- grid-subheaders-short-lived-no-orphan-prevention ---
// No orphan prevention for short-lived headers. // No orphan prevention for short-lived headers.
#set page(height: 8em) #set page(height: 8em)