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::layouter::GridLayouter;
|
||||||
use super::rowspans::UnbreakableRowGroup;
|
use super::rowspans::UnbreakableRowGroup;
|
||||||
|
|
||||||
|
pub enum HeadersToLayout<'a> {
|
||||||
|
RepeatingAndPending,
|
||||||
|
NewHeaders(&'a [Repeatable<Header>]),
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> GridLayouter<'a> {
|
impl<'a> GridLayouter<'a> {
|
||||||
pub fn place_new_headers(
|
pub fn place_new_headers(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -39,15 +44,16 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
self.layout_new_pending_headers(non_conflicting_headers, engine);
|
self.layout_new_pending_headers(non_conflicting_headers, engine);
|
||||||
|
|
||||||
self.layout_headers(
|
// Layout each conflicting header independently, without orphan
|
||||||
non_conflicting_headers.into_iter().map(Repeatable::unwrap),
|
// prevention (as they don't go into 'pending_headers').
|
||||||
true,
|
// These headers are short-lived as they are immediately followed by a
|
||||||
engine,
|
// header of the same or lower level, such that they never actually get
|
||||||
)?;
|
// to repeat.
|
||||||
for conflicting_header in conflicting_headers {
|
for conflicting_header in conflicting_headers.chunks_exact(1) {
|
||||||
self.layout_headers(
|
self.layout_headers(
|
||||||
std::iter::once(conflicting_header.unwrap()),
|
// Using 'chunks_exact", we pass a slice of length one instead
|
||||||
true,
|
// of a reference for type consistency.
|
||||||
|
HeadersToLayout::NewHeaders(conflicting_header),
|
||||||
engine,
|
engine,
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
@ -81,11 +87,12 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// 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
|
||||||
// to try to place old and new headers all over again, but that happens
|
// to try to place old and new headers all over again, but that happens
|
||||||
// for every new region anyway, so it's rather unavoidable.
|
// 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
|
// 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
|
// it's then confirmed the headers won't be moved due to orphan
|
||||||
// prevention anymore.
|
// prevention anymore.
|
||||||
|
self.pending_headers = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_pending_headers(&mut self) {
|
pub fn flush_pending_headers(&mut self) {
|
||||||
@ -156,22 +163,33 @@ impl<'a> GridLayouter<'a> {
|
|||||||
/// footers.
|
/// footers.
|
||||||
pub fn layout_headers(
|
pub fn layout_headers(
|
||||||
&mut self,
|
&mut self,
|
||||||
headers: impl Clone + IntoIterator<Item = &'a Header>,
|
headers: HeadersToLayout<'a>,
|
||||||
include_repeating: bool,
|
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
// Generate different locations for content in headers across its
|
// Generate different locations for content in headers across its
|
||||||
// repetitions by assigning a unique number for each one.
|
// repetitions by assigning a unique number for each one.
|
||||||
let disambiguator = self.finished.len();
|
let disambiguator = self.finished.len();
|
||||||
|
|
||||||
// At first, only consider the height of the given headers. However,
|
// At first, only consider the height of the given headers. However,
|
||||||
// for upcoming regions, we will have to consider repeating headers as
|
// for upcoming regions, we will have to consider repeating headers as
|
||||||
// well.
|
// well.
|
||||||
let mut header_height = self.simulate_header_height(
|
let mut header_height = match headers {
|
||||||
headers.clone(),
|
HeadersToLayout::RepeatingAndPending => self.simulate_header_height(
|
||||||
&self.regions,
|
self.repeating_headers
|
||||||
engine,
|
.iter()
|
||||||
disambiguator,
|
.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.
|
// We already take the footer into account below.
|
||||||
// While skipping regions, footer height won't be automatically
|
// While skipping regions, footer height won't be automatically
|
||||||
@ -187,16 +205,22 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
// TODO: re-calculate heights of headers and footers on each region
|
// TODO: re-calculate heights of headers and footers on each region
|
||||||
// if 'full'changes? (Assuming height doesn't change for now...)
|
// if 'full'changes? (Assuming height doesn't change for now...)
|
||||||
if include_repeating && !skipped_region {
|
if !skipped_region {
|
||||||
header_height =
|
if let HeadersToLayout::NewHeaders(headers) = headers {
|
||||||
// Laying out pending headers, so we have to consider the
|
header_height =
|
||||||
// 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.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,
|
&self.regions,
|
||||||
engine,
|
engine,
|
||||||
disambiguator,
|
disambiguator,
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skipped_region = true;
|
skipped_region = true;
|
||||||
@ -225,32 +249,47 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// Group of headers is unbreakable.
|
// Group of headers is unbreakable.
|
||||||
// Thus, no risk of 'finish_region' being recursively called from
|
// Thus, no risk of 'finish_region' being recursively called from
|
||||||
// within 'layout_row'.
|
// 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
|
// Need to relayout ALL headers if we skip a region, not only the
|
||||||
// provided headers.
|
// provided headers.
|
||||||
// TODO: maybe extract this into a function to share code with multiple
|
// TODO: maybe extract this into a function to share code with multiple
|
||||||
// footers.
|
// footers.
|
||||||
if include_repeating && skipped_region {
|
if matches!(headers, HeadersToLayout::RepeatingAndPending) || skipped_region {
|
||||||
self.unbreakable_rows_left +=
|
self.unbreakable_rows_left +=
|
||||||
total_header_row_count(self.repeating_headers.iter().map(Deref::deref));
|
total_header_row_count(self.repeating_headers.iter().map(Deref::deref));
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
for y in header.range() {
|
for y in header.range() {
|
||||||
self.layout_row(y, engine, disambiguator)?;
|
self.layout_row(y, engine, disambiguator)?;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for header in headers {
|
for header in self.pending_headers {
|
||||||
for y in header.range() {
|
for y in header.unwrap().range() {
|
||||||
self.layout_row(y, engine, disambiguator)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user