Do layout short-circuit in flow instead of realization (#4231)

This commit is contained in:
Laurenz 2024-05-23 11:55:00 +02:00 committed by GitHub
parent a6cf584ee9
commit 34f1a23246
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 51 additions and 46 deletions

View File

@ -46,7 +46,23 @@ impl LayoutMultiple for Packed<FlowElem> {
bail!(self.span(), "cannot expand into infinite height");
}
let mut layouter = FlowLayouter::new(regions, styles);
// Check whether we have just a single multiple-layoutable element. In
// that case, we do not set `expand.y` to `false`, but rather keep it at
// its original value (since that element can take the full space).
//
// Consider the following code: `block(height: 5cm, pad(10pt, align(bottom, ..)))`
// Thanks to the code below, the expansion will be passed all the way
// through the block & pad and reach the innermost flow, so that things
// are properly bottom-aligned.
let mut alone = false;
if let [child] = self.children().as_slice() {
alone = child
.to_packed::<StyledElem>()
.map_or(child, |styled| &styled.child)
.can::<dyn LayoutMultiple>();
}
let mut layouter = FlowLayouter::new(regions, styles, alone);
for mut child in self.children().iter() {
let outer = styles;
let mut styles = styles;
@ -70,8 +86,8 @@ impl LayoutMultiple for Packed<FlowElem> {
layouter.layout_par(engine, elem, styles)?;
} else if let Some(layoutable) = child.with::<dyn LayoutSingle>() {
layouter.layout_single(engine, layoutable, styles)?;
} else if child.can::<dyn LayoutMultiple>() {
layouter.layout_multiple(engine, child, styles)?;
} else if let Some(layoutable) = child.with::<dyn LayoutMultiple>() {
layouter.layout_multiple(engine, child, layoutable, styles)?;
} else {
bail!(child.span(), "unexpected flow child");
}
@ -179,11 +195,16 @@ impl FlowItem {
impl<'a> FlowLayouter<'a> {
/// Create a new flow layouter.
fn new(mut regions: Regions<'a>, styles: StyleChain<'a>) -> Self {
fn new(mut regions: Regions<'a>, styles: StyleChain<'a>, alone: bool) -> Self {
let expand = regions.expand;
// Disable vertical expansion & root for children.
regions.expand.y = false;
// Disable vertical expansion when there are multiple or not directly
// layoutable children.
if !alone {
regions.expand.y = false;
}
// Disable root.
let root = std::mem::replace(&mut regions.root, false);
Self {
@ -340,6 +361,7 @@ impl<'a> FlowLayouter<'a> {
&mut self,
engine: &mut Engine,
child: &Content,
layoutable: &dyn LayoutMultiple,
styles: StyleChain,
) -> SourceResult<()> {
// Temporarily delegerate rootness to the columns.
@ -368,7 +390,7 @@ impl<'a> FlowLayouter<'a> {
// Layout the block itself.
let sticky = BlockElem::sticky_in(styles);
let fragment = child.layout(engine, styles, self.regions)?;
let fragment = layoutable.layout(engine, styles, self.regions)?;
for (i, mut frame) in fragment.into_iter().enumerate() {
// Find footnotes in the frame.

View File

@ -77,7 +77,7 @@ use crate::eval::Tracer;
use crate::foundations::{category, Category, Content, Scope, StyleChain};
use crate::introspection::{Introspector, Locator};
use crate::model::Document;
use crate::realize::{realize_block, realize_root, Arenas};
use crate::realize::{realize_doc, realize_flow, Arenas};
use crate::World;
/// Arranging elements on the page in different ways.
@ -207,7 +207,7 @@ impl LayoutRoot for Content {
tracer,
};
let arenas = Arenas::default();
let (document, styles) = realize_root(&mut engine, &arenas, content, styles)?;
let (document, styles) = realize_doc(&mut engine, &arenas, content, styles)?;
document.layout_root(&mut engine, styles)
}
@ -258,14 +258,16 @@ impl LayoutMultiple for Content {
);
}
// If we are in a `PageElem`, this might already be a realized flow.
if let Some(flow) = content.to_packed::<FlowElem>() {
return flow.layout(&mut engine, styles, regions);
}
// Layout the content by first turning it into a `FlowElem` and then
// layouting that.
let arenas = Arenas::default();
let (realized, styles) =
realize_block(&mut engine, &arenas, content, styles)?;
realized.with::<dyn LayoutMultiple>().unwrap().layout(
&mut engine,
styles,
regions,
)
let (flow, styles) = realize_flow(&mut engine, &arenas, content, styles)?;
flow.layout(&mut engine, styles, regions)
}
let fragment = cached(

View File

@ -12,9 +12,7 @@ mod process;
pub use self::arenas::Arenas;
pub use self::behaviour::{Behave, BehavedBuilder, Behaviour};
pub use self::process::{process, processable};
use std::borrow::Cow;
pub use self::process::process;
use std::mem;
@ -36,9 +34,10 @@ use crate::model::{
use crate::syntax::Span;
use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
/// Realize into an element that is capable of root-level layout.
#[typst_macros::time(name = "realize root")]
pub fn realize_root<'a>(
/// Realize into a `DocumentElem`, an element that is capable of root-level
/// layout.
#[typst_macros::time(name = "realize doc")]
pub fn realize_doc<'a>(
engine: &mut Engine,
arenas: &'a Arenas<'a>,
content: &'a Content,
@ -47,30 +46,21 @@ pub fn realize_root<'a>(
let mut builder = Builder::new(engine, arenas, true);
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles), true)?;
let (doc, trunk) = builder.doc.unwrap().finish();
Ok((doc, trunk))
Ok(builder.doc.unwrap().finish())
}
/// Realize into an element that is capable of block-level layout.
#[typst_macros::time(name = "realize block")]
pub fn realize_block<'a>(
/// Realize into a `FlowElem`, an element that is capable of block-level layout.
#[typst_macros::time(name = "realize flow")]
pub fn realize_flow<'a>(
engine: &mut Engine,
arenas: &'a Arenas<'a>,
content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
// These elements implement `Layout` but still require a flow for
// proper layout.
if content.can::<dyn LayoutMultiple>() && !processable(engine, content, styles) {
return Ok((Cow::Borrowed(content), styles));
}
) -> SourceResult<(Packed<FlowElem>, StyleChain<'a>)> {
let mut builder = Builder::new(engine, arenas, false);
builder.accept(content, styles)?;
builder.interrupt_par()?;
let (flow, trunk) = builder.flow.finish();
Ok((Cow::Owned(flow.pack()), trunk))
Ok(builder.flow.finish())
}
/// Builds a document or a flow element from content.

View File

@ -31,15 +31,6 @@ enum ShowStep<'a> {
Builtin,
}
/// Returns whether the `target` element needs processing.
pub fn processable<'a>(
engine: &mut Engine,
target: &'a Content,
styles: StyleChain<'a>,
) -> bool {
verdict(engine, target, styles).is_some()
}
/// Processes the given `target` element when encountering it during realization.
pub fn process(
engine: &mut Engine,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 B

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB