use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
elem, Content, NativeElement, Packed, Resolve, Show, StyleChain,
};
use crate::introspection::Locator;
use crate::layout::{
layout_frame, Abs, AlignElem, Axes, BlockElem, Frame, Length, Point, Region, Size,
};
use crate::utils::Numeric;
#[elem(Show)]
pub struct RepeatElem {
#[required]
pub body: Content,
#[default]
pub gap: Length,
#[default(true)]
pub justify: bool,
}
impl Show for Packed<RepeatElem> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(BlockElem::single_layouter(self.clone(), layout_repeat)
.pack()
.spanned(self.span()))
}
}
#[typst_macros::time(span = elem.span())]
fn layout_repeat(
elem: &Packed<RepeatElem>,
engine: &mut Engine,
locator: Locator,
styles: StyleChain,
region: Region,
) -> SourceResult<Frame> {
let pod = Region::new(region.size, Axes::new(false, false));
let piece = layout_frame(engine, &elem.body, locator, styles, pod)?;
let size = Size::new(region.size.x, piece.height());
if !size.is_finite() {
bail!(elem.span(), "repeat with no size restrictions");
}
let mut frame = Frame::soft(size);
if piece.has_baseline() {
frame.set_baseline(piece.baseline());
}
let mut gap = elem.gap(styles).resolve(styles);
let fill = region.size.x;
let width = piece.width();
let count = ((fill + gap) / (width + gap)).floor();
let remaining = (fill + gap) % (width + gap);
let justify = elem.justify(styles);
if justify {
gap += remaining / (count - 1.0);
}
let align = AlignElem::alignment_in(styles).resolve(styles);
let mut offset = Abs::zero();
if count == 1.0 || !justify {
offset += align.x.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() + gap;
}
}
Ok(frame)
}