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");
|
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() {
|
for mut child in self.children().iter() {
|
||||||
let outer = styles;
|
let outer = styles;
|
||||||
let mut styles = styles;
|
let mut styles = styles;
|
||||||
@ -70,8 +86,8 @@ impl LayoutMultiple for Packed<FlowElem> {
|
|||||||
layouter.layout_par(engine, elem, styles)?;
|
layouter.layout_par(engine, elem, styles)?;
|
||||||
} else if let Some(layoutable) = child.with::<dyn LayoutSingle>() {
|
} else if let Some(layoutable) = child.with::<dyn LayoutSingle>() {
|
||||||
layouter.layout_single(engine, layoutable, styles)?;
|
layouter.layout_single(engine, layoutable, styles)?;
|
||||||
} else if child.can::<dyn LayoutMultiple>() {
|
} else if let Some(layoutable) = child.with::<dyn LayoutMultiple>() {
|
||||||
layouter.layout_multiple(engine, child, styles)?;
|
layouter.layout_multiple(engine, child, layoutable, styles)?;
|
||||||
} else {
|
} else {
|
||||||
bail!(child.span(), "unexpected flow child");
|
bail!(child.span(), "unexpected flow child");
|
||||||
}
|
}
|
||||||
@ -179,11 +195,16 @@ impl FlowItem {
|
|||||||
|
|
||||||
impl<'a> FlowLayouter<'a> {
|
impl<'a> FlowLayouter<'a> {
|
||||||
/// Create a new flow layouter.
|
/// 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;
|
let expand = regions.expand;
|
||||||
|
|
||||||
// Disable vertical expansion & root for children.
|
// Disable vertical expansion when there are multiple or not directly
|
||||||
|
// layoutable children.
|
||||||
|
if !alone {
|
||||||
regions.expand.y = false;
|
regions.expand.y = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable root.
|
||||||
let root = std::mem::replace(&mut regions.root, false);
|
let root = std::mem::replace(&mut regions.root, false);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -340,6 +361,7 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
child: &Content,
|
child: &Content,
|
||||||
|
layoutable: &dyn LayoutMultiple,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
// Temporarily delegerate rootness to the columns.
|
// Temporarily delegerate rootness to the columns.
|
||||||
@ -368,7 +390,7 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
|
|
||||||
// Layout the block itself.
|
// Layout the block itself.
|
||||||
let sticky = BlockElem::sticky_in(styles);
|
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() {
|
for (i, mut frame) in fragment.into_iter().enumerate() {
|
||||||
// Find footnotes in the frame.
|
// Find footnotes in the frame.
|
||||||
|
@ -77,7 +77,7 @@ use crate::eval::Tracer;
|
|||||||
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
||||||
use crate::introspection::{Introspector, Locator};
|
use crate::introspection::{Introspector, Locator};
|
||||||
use crate::model::Document;
|
use crate::model::Document;
|
||||||
use crate::realize::{realize_block, realize_root, Arenas};
|
use crate::realize::{realize_doc, realize_flow, Arenas};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Arranging elements on the page in different ways.
|
/// Arranging elements on the page in different ways.
|
||||||
@ -207,7 +207,7 @@ impl LayoutRoot for Content {
|
|||||||
tracer,
|
tracer,
|
||||||
};
|
};
|
||||||
let arenas = Arenas::default();
|
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)
|
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 arenas = Arenas::default();
|
||||||
let (realized, styles) =
|
let (flow, styles) = realize_flow(&mut engine, &arenas, content, styles)?;
|
||||||
realize_block(&mut engine, &arenas, content, styles)?;
|
flow.layout(&mut engine, styles, regions)
|
||||||
realized.with::<dyn LayoutMultiple>().unwrap().layout(
|
|
||||||
&mut engine,
|
|
||||||
styles,
|
|
||||||
regions,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let fragment = cached(
|
let fragment = cached(
|
||||||
|
@ -12,9 +12,7 @@ mod process;
|
|||||||
|
|
||||||
pub use self::arenas::Arenas;
|
pub use self::arenas::Arenas;
|
||||||
pub use self::behaviour::{Behave, BehavedBuilder, Behaviour};
|
pub use self::behaviour::{Behave, BehavedBuilder, Behaviour};
|
||||||
pub use self::process::{process, processable};
|
pub use self::process::process;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
@ -36,9 +34,10 @@ use crate::model::{
|
|||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
|
use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
|
||||||
|
|
||||||
/// Realize into an element that is capable of root-level layout.
|
/// Realize into a `DocumentElem`, an element that is capable of root-level
|
||||||
#[typst_macros::time(name = "realize root")]
|
/// layout.
|
||||||
pub fn realize_root<'a>(
|
#[typst_macros::time(name = "realize doc")]
|
||||||
|
pub fn realize_doc<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
arenas: &'a Arenas<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
@ -47,30 +46,21 @@ pub fn realize_root<'a>(
|
|||||||
let mut builder = Builder::new(engine, arenas, true);
|
let mut builder = Builder::new(engine, arenas, true);
|
||||||
builder.accept(content, styles)?;
|
builder.accept(content, styles)?;
|
||||||
builder.interrupt_page(Some(styles), true)?;
|
builder.interrupt_page(Some(styles), true)?;
|
||||||
let (doc, trunk) = builder.doc.unwrap().finish();
|
Ok(builder.doc.unwrap().finish())
|
||||||
Ok((doc, trunk))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Realize into an element that is capable of block-level layout.
|
/// Realize into a `FlowElem`, an element that is capable of block-level layout.
|
||||||
#[typst_macros::time(name = "realize block")]
|
#[typst_macros::time(name = "realize flow")]
|
||||||
pub fn realize_block<'a>(
|
pub fn realize_flow<'a>(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
arenas: &'a Arenas<'a>,
|
arenas: &'a Arenas<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
|
) -> SourceResult<(Packed<FlowElem>, 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut builder = Builder::new(engine, arenas, false);
|
let mut builder = Builder::new(engine, arenas, false);
|
||||||
builder.accept(content, styles)?;
|
builder.accept(content, styles)?;
|
||||||
builder.interrupt_par()?;
|
builder.interrupt_par()?;
|
||||||
|
Ok(builder.flow.finish())
|
||||||
let (flow, trunk) = builder.flow.finish();
|
|
||||||
Ok((Cow::Owned(flow.pack()), trunk))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a document or a flow element from content.
|
/// Builds a document or a flow element from content.
|
||||||
|
@ -31,15 +31,6 @@ enum ShowStep<'a> {
|
|||||||
Builtin,
|
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.
|
/// Processes the given `target` element when encountering it during realization.
|
||||||
pub fn process(
|
pub fn process(
|
||||||
engine: &mut Engine,
|
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 |