mirror of
https://github.com/typst/typst
synced 2025-05-29 00:15:35 +08:00
Add flush element (#4141)
This commit is contained in:
parent
a51bd3f0b6
commit
23746ee189
@ -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 {
|
||||
|
@ -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>();
|
||||
|
@ -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> {}
|
||||
|
@ -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;
|
||||
|
BIN
tests/ref/place-figure-flush.png
Normal file
BIN
tests/ref/place-figure-flush.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
BIN
tests/ref/place-float-flush.png
Normal file
BIN
tests/ref/place-float-flush.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user