Add flush element (#4141)

This commit is contained in:
+merlan #flirora 2024-05-30 11:40:01 -04:00 committed by GitHub
parent a51bd3f0b6
commit 23746ee189
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 96 additions and 8 deletions

View File

@ -13,9 +13,9 @@ use crate::foundations::{
}; };
use crate::introspection::TagElem; use crate::introspection::TagElem;
use crate::layout::{ use crate::layout::{
Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlignment, Fr, Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlignment,
Fragment, Frame, FrameItem, LayoutMultiple, LayoutSingle, PlaceElem, Point, Regions, FlushElem, Fr, Fragment, Frame, FrameItem, LayoutMultiple, LayoutSingle, PlaceElem,
Rel, Size, Spacing, VElem, Point, Regions, Rel, Size, Spacing, VElem,
}; };
use crate::model::{FootnoteElem, FootnoteEntry, ParElem}; use crate::model::{FootnoteElem, FootnoteEntry, ParElem};
use crate::utils::Numeric; use crate::utils::Numeric;
@ -73,6 +73,8 @@ impl LayoutMultiple for Packed<FlowElem> {
if let Some(elem) = child.to_packed::<TagElem>() { if let Some(elem) = child.to_packed::<TagElem>() {
layouter.layout_tag(elem); layouter.layout_tag(elem);
} else if child.is::<FlushElem>() {
layouter.flush(engine)?;
} else if let Some(elem) = child.to_packed::<VElem>() { } else if let Some(elem) = child.to_packed::<VElem>() {
layouter.layout_spacing(engine, elem, styles)?; layouter.layout_spacing(engine, elem, styles)?;
} else if let Some(placed) = child.to_packed::<PlaceElem>() { } else if let Some(placed) = child.to_packed::<PlaceElem>() {
@ -683,6 +685,18 @@ impl<'a> FlowLayouter<'a> {
Ok(()) Ok(())
} }
/// Lays out all floating elements before continuing with other content.
fn flush(&mut self, engine: &mut Engine) -> SourceResult<()> {
for item in std::mem::take(&mut self.pending_floats) {
self.layout_item(engine, item)?;
}
while !self.pending_floats.is_empty() {
self.finish_region(engine, false)?;
}
Ok(())
}
/// Finish layouting and return the resulting fragment. /// Finish layouting and return the resulting fragment.
fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> { fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
if self.expand.y { if self.expand.y {

View File

@ -107,6 +107,7 @@ pub fn define(global: &mut Scope) {
global.define_elem::<ColumnsElem>(); global.define_elem::<ColumnsElem>();
global.define_elem::<ColbreakElem>(); global.define_elem::<ColbreakElem>();
global.define_elem::<PlaceElem>(); global.define_elem::<PlaceElem>();
global.define_elem::<FlushElem>();
global.define_elem::<AlignElem>(); global.define_elem::<AlignElem>();
global.define_elem::<PadElem>(); global.define_elem::<PadElem>();
global.define_elem::<RepeatElem>(); global.define_elem::<RepeatElem>();

View File

@ -1,6 +1,6 @@
use crate::diag::{bail, At, Hint, SourceResult}; use crate::diag::{bail, At, Hint, SourceResult};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{elem, Content, Packed, Smart, StyleChain}; use crate::foundations::{elem, scope, Content, Packed, Smart, StyleChain, Unlabellable};
use crate::layout::{ use crate::layout::{
Alignment, Axes, Em, Fragment, LayoutMultiple, Length, Regions, Rel, Size, VAlignment, Alignment, Axes, Em, Fragment, LayoutMultiple, Length, Regions, Rel, Size, VAlignment,
}; };
@ -26,7 +26,7 @@ use crate::realize::{Behave, Behaviour};
/// ), /// ),
/// ) /// )
/// ``` /// ```
#[elem(Behave)] #[elem(scope, Behave)]
pub struct PlaceElem { pub struct PlaceElem {
/// Relative to which position in the parent container to place the content. /// Relative to which position in the parent container to place the content.
/// ///
@ -43,7 +43,9 @@ pub struct PlaceElem {
/// Whether the placed element has floating layout. /// Whether the placed element has floating layout.
/// ///
/// Floating elements are positioned at the top or bottom of the page, /// Floating elements are positioned at the top or bottom of the page,
/// displacing in-flow content. /// displacing in-flow content. They are always placed in the in-flow
/// order relative to each other, as well as before any content following
/// a later [`flush`] element.
/// ///
/// ```example /// ```example
/// #set page(height: 150pt) /// #set page(height: 150pt)
@ -95,6 +97,12 @@ pub struct PlaceElem {
pub body: Content, pub body: Content,
} }
#[scope]
impl PlaceElem {
#[elem]
type FlushElem;
}
impl Packed<PlaceElem> { impl Packed<PlaceElem> {
#[typst_macros::time(name = "place", span = self.span())] #[typst_macros::time(name = "place", span = self.span())]
pub fn layout( pub fn layout(
@ -136,3 +144,40 @@ impl Behave for Packed<PlaceElem> {
Behaviour::Ignorant Behaviour::Ignorant
} }
} }
/// Asks the layout algorithm to place pending floating elements before
/// continuing with the content.
///
/// This is useful for preventing floating figures from spilling
/// into the next section.
///
/// ```example
/// #set page(height: 165pt, width: 150pt)
///
/// Some introductory text: #lorem(15)
///
/// #figure(
/// rect(
/// width: 100%,
/// height: 64pt,
/// [I float with a caption!],
/// ),
/// placement: auto,
/// caption: [A self-describing figure],
/// )
///
/// #place.flush()
///
/// Some conclusive text that must occur
/// after the figure.
/// ```
#[elem(Behave, Unlabellable)]
pub struct FlushElem {}
impl Behave for Packed<FlushElem> {
fn behaviour(&self) -> Behaviour {
Behaviour::Invisible
}
}
impl Unlabellable for Packed<FlushElem> {}

View File

@ -23,8 +23,8 @@ use crate::foundations::{
}; };
use crate::introspection::TagElem; use crate::introspection::TagElem;
use crate::layout::{ use crate::layout::{
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, HElem, LayoutMultiple, AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, FlushElem, HElem,
LayoutSingle, PageElem, PagebreakElem, Parity, PlaceElem, VElem, LayoutMultiple, LayoutSingle, PageElem, PagebreakElem, Parity, PlaceElem, VElem,
}; };
use crate::math::{EquationElem, LayoutMath}; use crate::math::{EquationElem, LayoutMath};
use crate::model::{ use crate::model::{
@ -381,6 +381,7 @@ impl<'a> FlowBuilder<'a> {
|| content.is::<ColbreakElem>() || content.is::<ColbreakElem>()
|| content.is::<TagElem>() || content.is::<TagElem>()
|| content.is::<PlaceElem>() || content.is::<PlaceElem>()
|| content.is::<FlushElem>()
{ {
self.0.push(content, styles); self.0.push(content, styles);
return true; return true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -121,6 +121,33 @@ Second
image("/assets/images/diagram.svg", width: 80%), image("/assets/images/diagram.svg", width: 80%),
) )
--- place-float-flush ---
#set page(height: 150pt, width: 150pt)
#let floater = place(auto, float: true, rect(width: 100%, height: 90pt, text(size: 24pt)[I float!]))
Some introductory text.
#floater #floater #floater #floater
Some additional text.
#place.flush()
Some conclusive text. // Should appear after all the floating figures
--- place-figure-flush ---
#set page(height: 165pt, width: 150pt)
Some introductory text: #lorem(15)
#figure(placement: auto, caption: [A self-describing figure], rect(width: 100%, height: 64pt, [I float with a caption!]))
#place.flush()
Some conclusive text that must occur after the figure.
--- place-bottom-in-box --- --- place-bottom-in-box ---
#box( #box(
fill: aqua, fill: aqua,