mirror of
https://github.com/typst/typst
synced 2025-05-15 17:45:27 +08:00
use headers to layout enum on layout_headers
- This will allow us to tell layout_headers to just layout headers that are already repeating, plus pending headers which are waiting for layout.
This commit is contained in:
parent
af35e287af
commit
63055c8c83
@ -8,6 +8,11 @@ use typst_library::layout::{Abs, Axes, Frame, Regions};
|
||||
use super::layouter::GridLayouter;
|
||||
use super::rowspans::UnbreakableRowGroup;
|
||||
|
||||
pub enum HeadersToLayout<'a> {
|
||||
RepeatingAndPending,
|
||||
NewHeaders(&'a [Repeatable<Header>]),
|
||||
}
|
||||
|
||||
impl<'a> GridLayouter<'a> {
|
||||
pub fn place_new_headers(
|
||||
&mut self,
|
||||
@ -39,15 +44,16 @@ impl<'a> GridLayouter<'a> {
|
||||
|
||||
self.layout_new_pending_headers(non_conflicting_headers, engine);
|
||||
|
||||
// Layout each conflicting header independently, without orphan
|
||||
// prevention (as they don't go into 'pending_headers').
|
||||
// These headers are short-lived as they are immediately followed by a
|
||||
// header of the same or lower level, such that they never actually get
|
||||
// to repeat.
|
||||
for conflicting_header in conflicting_headers.chunks_exact(1) {
|
||||
self.layout_headers(
|
||||
non_conflicting_headers.into_iter().map(Repeatable::unwrap),
|
||||
true,
|
||||
engine,
|
||||
)?;
|
||||
for conflicting_header in conflicting_headers {
|
||||
self.layout_headers(
|
||||
std::iter::once(conflicting_header.unwrap()),
|
||||
true,
|
||||
// Using 'chunks_exact", we pass a slice of length one instead
|
||||
// of a reference for type consistency.
|
||||
HeadersToLayout::NewHeaders(conflicting_header),
|
||||
engine,
|
||||
)?
|
||||
}
|
||||
@ -81,11 +87,12 @@ impl<'a> GridLayouter<'a> {
|
||||
// This might be a waste as we could generate an orphan and thus have
|
||||
// to try to place old and new headers all over again, but that happens
|
||||
// for every new region anyway, so it's rather unavoidable.
|
||||
self.layout_headers(headers.iter().map(Repeatable::unwrap), true, engine);
|
||||
self.layout_headers(HeadersToLayout::NewHeaders(headers), engine);
|
||||
|
||||
// After the first subsequent row is laid out, move to repeating, as
|
||||
// it's then confirmed the headers won't be moved due to orphan
|
||||
// prevention anymore.
|
||||
self.pending_headers = headers;
|
||||
}
|
||||
|
||||
pub fn flush_pending_headers(&mut self) {
|
||||
@ -156,22 +163,33 @@ impl<'a> GridLayouter<'a> {
|
||||
/// footers.
|
||||
pub fn layout_headers(
|
||||
&mut self,
|
||||
headers: impl Clone + IntoIterator<Item = &'a Header>,
|
||||
include_repeating: bool,
|
||||
headers: HeadersToLayout<'a>,
|
||||
engine: &mut Engine,
|
||||
) -> SourceResult<()> {
|
||||
// Generate different locations for content in headers across its
|
||||
// repetitions by assigning a unique number for each one.
|
||||
let disambiguator = self.finished.len();
|
||||
|
||||
// At first, only consider the height of the given headers. However,
|
||||
// for upcoming regions, we will have to consider repeating headers as
|
||||
// well.
|
||||
let mut header_height = self.simulate_header_height(
|
||||
headers.clone(),
|
||||
let mut header_height = match headers {
|
||||
HeadersToLayout::RepeatingAndPending => self.simulate_header_height(
|
||||
self.repeating_headers
|
||||
.iter()
|
||||
.map(Deref::deref)
|
||||
.chain(self.pending_headers.into_iter().map(Repeatable::unwrap)),
|
||||
&self.regions,
|
||||
engine,
|
||||
disambiguator,
|
||||
)?;
|
||||
)?,
|
||||
HeadersToLayout::NewHeaders(headers) => self.simulate_header_height(
|
||||
headers.into_iter().map(Repeatable::unwrap),
|
||||
&self.regions,
|
||||
engine,
|
||||
disambiguator,
|
||||
)?,
|
||||
};
|
||||
|
||||
// We already take the footer into account below.
|
||||
// While skipping regions, footer height won't be automatically
|
||||
@ -187,17 +205,23 @@ impl<'a> GridLayouter<'a> {
|
||||
|
||||
// TODO: re-calculate heights of headers and footers on each region
|
||||
// if 'full'changes? (Assuming height doesn't change for now...)
|
||||
if include_repeating && !skipped_region {
|
||||
if !skipped_region {
|
||||
if let HeadersToLayout::NewHeaders(headers) = headers {
|
||||
header_height =
|
||||
// Laying out pending headers, so we have to consider the
|
||||
// combined height of already repeating headers as well.
|
||||
// Laying out new headers, so we have to consider the
|
||||
// combined height of already repeating headers as well
|
||||
// when beginning a new region.
|
||||
self.simulate_header_height(
|
||||
self.repeating_headers.iter().map(|h| *h).chain(headers.clone()),
|
||||
self.repeating_headers
|
||||
.iter()
|
||||
.map(|h| *h)
|
||||
.chain(self.pending_headers.into_iter().chain(headers).map(Repeatable::unwrap)),
|
||||
&self.regions,
|
||||
engine,
|
||||
disambiguator,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
skipped_region = true;
|
||||
|
||||
@ -225,16 +249,21 @@ impl<'a> GridLayouter<'a> {
|
||||
// Group of headers is unbreakable.
|
||||
// Thus, no risk of 'finish_region' being recursively called from
|
||||
// within 'layout_row'.
|
||||
self.unbreakable_rows_left += total_header_row_count(headers.clone());
|
||||
if let HeadersToLayout::NewHeaders(headers) = headers {
|
||||
// Do this before laying out repeating and pending headers from a
|
||||
// new region to make sure row code is aware that all of those
|
||||
// headers should stay together!
|
||||
self.unbreakable_rows_left +=
|
||||
total_header_row_count(headers.into_iter().map(Repeatable::unwrap));
|
||||
}
|
||||
|
||||
// Need to relayout ALL headers if we skip a region, not only the
|
||||
// provided headers.
|
||||
// TODO: maybe extract this into a function to share code with multiple
|
||||
// footers.
|
||||
if include_repeating && skipped_region {
|
||||
if matches!(headers, HeadersToLayout::RepeatingAndPending) || skipped_region {
|
||||
self.unbreakable_rows_left +=
|
||||
total_header_row_count(self.repeating_headers.iter().map(Deref::deref));
|
||||
}
|
||||
|
||||
// Use indices to avoid double borrow. We don't mutate headers in
|
||||
// 'layout_row' so this is fine.
|
||||
@ -246,11 +275,21 @@ impl<'a> GridLayouter<'a> {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
for header in headers {
|
||||
for y in header.range() {
|
||||
for header in self.pending_headers {
|
||||
for y in header.unwrap().range() {
|
||||
self.layout_row(y, engine, disambiguator)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let HeadersToLayout::NewHeaders(headers) = headers {
|
||||
for header in headers {
|
||||
for y in header.unwrap().range() {
|
||||
self.layout_row(y, engine, disambiguator)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user