mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
create RowState abstraction
nicer to use than what that was before store temp values during row layout
This commit is contained in:
parent
6c42f67b3d
commit
09e7062b38
@ -61,21 +61,12 @@ pub struct GridLayouter<'a> {
|
||||
/// 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>,
|
||||
/// 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.
|
||||
/// State of the row being currently laid out.
|
||||
///
|
||||
/// 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,
|
||||
/// This is kept as a field to avoid passing down too many parameters from
|
||||
/// `layout_row` into called functions, which would then have to pass them
|
||||
/// down to `push_row`, which reads these values.
|
||||
pub(super) row_state: RowState,
|
||||
/// The span of the grid element.
|
||||
pub(super) span: Span,
|
||||
}
|
||||
@ -149,6 +140,25 @@ pub(super) struct Current {
|
||||
pub(super) footer_height: Abs,
|
||||
}
|
||||
|
||||
/// Data about the row being laid out right now.
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct RowState {
|
||||
/// 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.
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct FinishedHeaderRowInfo {
|
||||
/// The amount of repeated headers at the top of the region.
|
||||
@ -220,8 +230,7 @@ impl<'a> GridLayouter<'a> {
|
||||
repeating_headers: vec![],
|
||||
upcoming_headers: &grid.headers,
|
||||
pending_headers: Default::default(),
|
||||
current_row_height: None,
|
||||
in_active_repeatable: false,
|
||||
row_state: RowState::default(),
|
||||
current: Current {
|
||||
initial: regions.size,
|
||||
repeated_header_rows: 0,
|
||||
@ -310,12 +319,46 @@ impl<'a> GridLayouter<'a> {
|
||||
self.render_fills_strokes()
|
||||
}
|
||||
|
||||
/// Layout the given row.
|
||||
/// Layout a row with a certain initial state, returning the final state.
|
||||
#[inline]
|
||||
pub(super) fn layout_row_with_state(
|
||||
&mut self,
|
||||
y: usize,
|
||||
engine: &mut Engine,
|
||||
disambiguator: usize,
|
||||
initial_state: RowState,
|
||||
) -> SourceResult<RowState> {
|
||||
// Keep a copy of the previous value in the stack, as this function can
|
||||
// call itself recursively (e.g. if a region break is triggered and a
|
||||
// header is placed), so we shouldn't outright overwrite it, but rather
|
||||
// save and later restore the state when back to this call.
|
||||
let previous = std::mem::replace(&mut self.row_state, initial_state);
|
||||
|
||||
// Keep it as a separate function to allow inlining the return below,
|
||||
// as it's usually not needed.
|
||||
self.layout_row_internal(y, engine, disambiguator)?;
|
||||
|
||||
Ok(std::mem::replace(&mut self.row_state, previous))
|
||||
}
|
||||
|
||||
/// Layout the given row with the default row state.
|
||||
#[inline]
|
||||
pub(super) fn layout_row(
|
||||
&mut self,
|
||||
y: usize,
|
||||
engine: &mut Engine,
|
||||
disambiguator: usize,
|
||||
) -> SourceResult<()> {
|
||||
self.layout_row_with_state(y, engine, disambiguator, RowState::default())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Layout the given row using the current state.
|
||||
pub(super) fn layout_row_internal(
|
||||
&mut self,
|
||||
y: usize,
|
||||
engine: &mut Engine,
|
||||
disambiguator: usize,
|
||||
) -> SourceResult<()> {
|
||||
// Skip to next region if current one is full, but only for content
|
||||
// rows, not for gutter rows, and only if we aren't laying out an
|
||||
@ -339,7 +382,7 @@ impl<'a> GridLayouter<'a> {
|
||||
self.layout_relative_row(engine, disambiguator, v, y)?
|
||||
}
|
||||
Sizing::Fr(v) => {
|
||||
if !self.in_active_repeatable {
|
||||
if !self.row_state.in_active_repeatable {
|
||||
self.flush_orphans();
|
||||
}
|
||||
self.lrows.push(Row::Fr(v, y, disambiguator))
|
||||
@ -1067,7 +1110,7 @@ impl<'a> GridLayouter<'a> {
|
||||
let frame = self.layout_single_row(engine, disambiguator, first, y)?;
|
||||
self.push_row(frame, y, true);
|
||||
|
||||
if let Some(row_height) = &mut self.current_row_height {
|
||||
if let Some(row_height) = &mut self.row_state.current_row_height {
|
||||
// Add to header height, as we are in a header row.
|
||||
*row_height += first;
|
||||
}
|
||||
@ -1302,7 +1345,7 @@ impl<'a> GridLayouter<'a> {
|
||||
let resolved = v.resolve(self.styles).relative_to(self.regions.base().y);
|
||||
let frame = self.layout_single_row(engine, disambiguator, resolved, y)?;
|
||||
|
||||
if let Some(row_height) = &mut self.current_row_height {
|
||||
if let Some(row_height) = &mut self.row_state.current_row_height {
|
||||
// Add to header height, as we are in a header row.
|
||||
*row_height += resolved;
|
||||
}
|
||||
@ -1454,7 +1497,7 @@ impl<'a> GridLayouter<'a> {
|
||||
/// will be pushed for this particular row. It can be `false` for rows
|
||||
/// spanning multiple regions.
|
||||
fn push_row(&mut self, frame: Frame, y: usize, is_last: bool) {
|
||||
if !self.in_active_repeatable {
|
||||
if !self.row_state.in_active_repeatable {
|
||||
// There is now a row after the rows equipped with orphan
|
||||
// prevention, so no need to keep moving them anymore.
|
||||
self.flush_orphans();
|
||||
|
@ -3,7 +3,7 @@ use typst_library::engine::Engine;
|
||||
use typst_library::layout::grid::resolve::{Footer, Header, Repeatable};
|
||||
use typst_library::layout::{Abs, Axes, Frame, Regions};
|
||||
|
||||
use super::layouter::{may_progress_with_offset, GridLayouter};
|
||||
use super::layouter::{may_progress_with_offset, GridLayouter, RowState};
|
||||
use super::rowspans::UnbreakableRowGroup;
|
||||
|
||||
impl<'a> GridLayouter<'a> {
|
||||
@ -48,34 +48,10 @@ impl<'a> GridLayouter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lays out a row while indicating that it should store its persistent
|
||||
/// height as a header row, which will be its height if relative or auto,
|
||||
/// or zero otherwise (fractional).
|
||||
#[inline]
|
||||
fn layout_header_row(
|
||||
&mut self,
|
||||
y: usize,
|
||||
engine: &mut Engine,
|
||||
disambiguator: usize,
|
||||
as_short_lived: bool,
|
||||
) -> SourceResult<Option<Abs>> {
|
||||
let previous_row_height =
|
||||
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)?;
|
||||
|
||||
_ = std::mem::replace(
|
||||
&mut self.in_active_repeatable,
|
||||
previous_in_active_repeatable,
|
||||
);
|
||||
|
||||
Ok(std::mem::replace(&mut self.current_row_height, previous_row_height))
|
||||
}
|
||||
|
||||
/// Lays out rows belonging to a header, returning the calculated header
|
||||
/// height only for that header.
|
||||
/// height only for that header. Indicates to the laid out rows that they
|
||||
/// should inform their laid out heights if appropriate (auto or fixed
|
||||
/// size rows only).
|
||||
#[inline]
|
||||
fn layout_header_rows(
|
||||
&mut self,
|
||||
@ -87,7 +63,16 @@ impl<'a> GridLayouter<'a> {
|
||||
let mut header_height = Abs::zero();
|
||||
for y in header.range() {
|
||||
header_height += self
|
||||
.layout_header_row(y, engine, disambiguator, as_short_lived)?
|
||||
.layout_row_with_state(
|
||||
y,
|
||||
engine,
|
||||
disambiguator,
|
||||
RowState {
|
||||
current_row_height: Some(Abs::zero()),
|
||||
in_active_repeatable: !as_short_lived,
|
||||
},
|
||||
)?
|
||||
.current_row_height
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Ok(header_height)
|
||||
@ -505,15 +490,15 @@ impl<'a> GridLayouter<'a> {
|
||||
self.unbreakable_rows_left += footer_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)?;
|
||||
|
||||
_ = std::mem::replace(
|
||||
&mut self.in_active_repeatable,
|
||||
previous_in_active_repeatable,
|
||||
);
|
||||
self.layout_row_with_state(
|
||||
y,
|
||||
engine,
|
||||
disambiguator,
|
||||
RowState {
|
||||
in_active_repeatable: repeats,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
Loading…
x
Reference in New Issue
Block a user