typst/library/src/layout/repeat.rs
2023-05-17 14:38:03 +02:00

80 lines
2.1 KiB
Rust

use crate::prelude::*;
use super::AlignElem;
/// Repeats content to the available space.
///
/// This can be useful when implementing a custom index, reference, or outline.
///
/// Space may be inserted between the instances of the body parameter, so be
/// sure to include negative space if you need the instances to overlap.
///
/// Errors if there no bounds on the available space, as it would create
/// infinite content.
///
/// ## Example { #example }
/// ```example
/// Sign on the dotted line:
/// #box(width: 1fr, repeat[.])
///
/// #set text(10pt)
/// #v(8pt, weak: true)
/// #align(right)[
/// Berlin, the 22nd of December, 2022
/// ]
/// ```
///
/// Display: Repeat
/// Category: layout
#[element(Layout)]
pub struct RepeatElem {
/// The content to repeat.
#[required]
pub body: Content,
}
impl Layout for RepeatElem {
#[tracing::instrument(name = "RepeatElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.size, Axes::new(false, false));
let piece = self.body().layout(vt, styles, pod)?.into_frame();
let align = AlignElem::alignment_in(styles).x.resolve(styles);
let fill = regions.size.x;
let width = piece.width();
let count = (fill / width).floor();
let remaining = fill % width;
let apart = remaining / (count - 1.0);
let size = Size::new(regions.size.x, piece.height());
if !size.is_finite() {
bail!(self.span(), "repeat with no size restrictions");
}
let mut frame = Frame::new(size);
if piece.has_baseline() {
frame.set_baseline(piece.baseline());
}
let mut offset = Abs::zero();
if count == 1.0 {
offset += align.position(remaining);
}
if width > Abs::zero() {
for _ in 0..(count as usize).min(1000) {
frame.push_frame(Point::with_x(offset), piece.clone());
offset += piece.width() + apart;
}
}
Ok(Fragment::frame(frame))
}
}