Do layout short-circuit in flow instead of realization (#4231)
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 966 B |
Before Width: | Height: | Size: 1012 B After Width: | Height: | Size: 978 B |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |