diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-05-23 11:55:00 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-23 09:55:00 +0000 |
| commit | 34f1a232463bafeb3d8d3fa9adeae33c4bb95b23 (patch) | |
| tree | 9ec2d0c4ed213008d8911f8119d4a480570e1512 /crates | |
| parent | a6cf584ee9c19cb27bf79410c0d1fec9e5012a27 (diff) | |
Do layout short-circuit in flow instead of realization (#4231)
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/typst/src/layout/flow.rs | 36 | ||||
| -rw-r--r-- | crates/typst/src/layout/mod.rs | 20 | ||||
| -rw-r--r-- | crates/typst/src/realize/mod.rs | 32 | ||||
| -rw-r--r-- | crates/typst/src/realize/process.rs | 9 |
4 files changed, 51 insertions, 46 deletions
diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs index c550ac28..2f985f28 100644 --- a/crates/typst/src/layout/flow.rs +++ b/crates/typst/src/layout/flow.rs @@ -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. diff --git a/crates/typst/src/layout/mod.rs b/crates/typst/src/layout/mod.rs index 23a08c5f..0fd8e6b1 100644 --- a/crates/typst/src/layout/mod.rs +++ b/crates/typst/src/layout/mod.rs @@ -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( diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs index 98b5c127..4679a61c 100644 --- a/crates/typst/src/realize/mod.rs +++ b/crates/typst/src/realize/mod.rs @@ -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. diff --git a/crates/typst/src/realize/process.rs b/crates/typst/src/realize/process.rs index 6ddb493d..4212ecb4 100644 --- a/crates/typst/src/realize/process.rs +++ b/crates/typst/src/realize/process.rs @@ -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, |
