summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
author+merlan #flirora <uruwi@protonmail.com>2024-05-30 11:40:01 -0400
committerGitHub <noreply@github.com>2024-05-30 15:40:01 +0000
commit23746ee18901e08852306f35639298ad234d3481 (patch)
tree7fb02376e4e621910720d7fff78d61c2453d7d46 /crates
parenta51bd3f0b6afacbab4529558d65144d303f9b804 (diff)
Add flush element (#4141)
Diffstat (limited to 'crates')
-rw-r--r--crates/typst/src/layout/flow.rs20
-rw-r--r--crates/typst/src/layout/mod.rs1
-rw-r--r--crates/typst/src/layout/place.rs51
-rw-r--r--crates/typst/src/realize/mod.rs5
4 files changed, 69 insertions, 8 deletions
diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs
index 2f985f28..84a395cc 100644
--- a/crates/typst/src/layout/flow.rs
+++ b/crates/typst/src/layout/flow.rs
@@ -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 {
diff --git a/crates/typst/src/layout/mod.rs b/crates/typst/src/layout/mod.rs
index 0fd8e6b1..444d5162 100644
--- a/crates/typst/src/layout/mod.rs
+++ b/crates/typst/src/layout/mod.rs
@@ -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>();
diff --git a/crates/typst/src/layout/place.rs b/crates/typst/src/layout/place.rs
index 7d41b037..f81af2b8 100644
--- a/crates/typst/src/layout/place.rs
+++ b/crates/typst/src/layout/place.rs
@@ -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> {}
diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs
index 4679a61c..600df1bb 100644
--- a/crates/typst/src/realize/mod.rs
+++ b/crates/typst/src/realize/mod.rs
@@ -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;