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::layout::{
Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlignment, Fr,
Fragment, Frame, FrameItem, LayoutMultiple, LayoutSingle, PlaceElem, Point, Regions,
Rel, Size, Spacing, VElem,
Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlignment,
FlushElem, Fr, Fragment, Frame, FrameItem, LayoutMultiple, LayoutSingle, PlaceElem,
Point, Regions, Rel, Size, Spacing, VElem,
};
use crate::model::{FootnoteElem, FootnoteEntry, ParElem};
use crate::utils::Numeric;
@ -73,6 +73,8 @@ impl LayoutMultiple for Packed<FlowElem> {
if let Some(elem) = child.to_packed::<TagElem>() {
layouter.layout_tag(elem);
} else if child.is::<FlushElem>() {
layouter.flush(engine)?;
} else if let Some(elem) = child.to_packed::<VElem>() {
layouter.layout_spacing(engine, elem, styles)?;
} else if let Some(placed) = child.to_packed::<PlaceElem>() {
@ -683,6 +685,18 @@ impl<'a> FlowLayouter<'a> {
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.
fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
if self.expand.y {

View File

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

View File

@ -1,6 +1,6 @@
use crate::diag::{bail, At, Hint, SourceResult};
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::{
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 {
/// 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.
///
/// 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
/// #set page(height: 150pt)
@ -95,6 +97,12 @@ pub struct PlaceElem {
pub body: Content,
}
#[scope]
impl PlaceElem {
#[elem]
type FlushElem;
}
impl Packed<PlaceElem> {
#[typst_macros::time(name = "place", span = self.span())]
pub fn layout(
@ -136,3 +144,40 @@ impl Behave for Packed<PlaceElem> {
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::layout::{
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, HElem, LayoutMultiple,
LayoutSingle, PageElem, PagebreakElem, Parity, PlaceElem, VElem,
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, FlushElem, HElem,
LayoutMultiple, LayoutSingle, PageElem, PagebreakElem, Parity, PlaceElem, VElem,
};
use crate::math::{EquationElem, LayoutMath};
use crate::model::{
@ -381,6 +381,7 @@ impl<'a> FlowBuilder<'a> {
|| content.is::<ColbreakElem>()
|| content.is::<TagElem>()
|| content.is::<PlaceElem>()
|| content.is::<FlushElem>()
{
self.0.push(content, styles);
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%),
)
--- 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 ---
#box(
fill: aqua,