mirror of
https://github.com/typst/typst
synced 2025-05-15 01:25:28 +08:00
145 lines
4.5 KiB
Rust
145 lines
4.5 KiB
Rust
use std::fmt::{self, Debug, Formatter};
|
|
|
|
use typst::geom::{Abs, Axes, Size};
|
|
|
|
/// A sequence of regions to layout into.
|
|
#[derive(Copy, Clone, Hash)]
|
|
pub struct Regions<'a> {
|
|
/// The remaining size of the first region.
|
|
pub size: Size,
|
|
/// The full height of the region for relative sizing.
|
|
pub full: Abs,
|
|
/// The height of followup regions. The width is the same for all regions.
|
|
pub backlog: &'a [Abs],
|
|
/// The height of the final region that is repeated once the backlog is
|
|
/// drained. The width is the same for all regions.
|
|
pub last: Option<Abs>,
|
|
/// Whether elements should expand to fill the regions instead of shrinking
|
|
/// to fit the content.
|
|
pub expand: Axes<bool>,
|
|
/// Whether these are the root regions or direct descendants.
|
|
///
|
|
/// True for the padded page regions and columns directly in the page,
|
|
/// false otherwise.
|
|
pub root: bool,
|
|
}
|
|
|
|
impl Regions<'_> {
|
|
/// Create a new region sequence with exactly one region.
|
|
pub fn one(size: Size, expand: Axes<bool>) -> Self {
|
|
Self {
|
|
size,
|
|
full: size.y,
|
|
backlog: &[],
|
|
last: None,
|
|
expand,
|
|
root: false,
|
|
}
|
|
}
|
|
|
|
/// Create a new sequence of same-size regions that repeats indefinitely.
|
|
pub fn repeat(size: Size, expand: Axes<bool>) -> Self {
|
|
Self {
|
|
size,
|
|
full: size.y,
|
|
backlog: &[],
|
|
last: Some(size.y),
|
|
expand,
|
|
root: false,
|
|
}
|
|
}
|
|
|
|
/// The base size, which doesn't take into account that the regions is
|
|
/// already partially used up.
|
|
///
|
|
/// This is also used for relative sizing.
|
|
pub fn base(&self) -> Size {
|
|
Size::new(self.size.x, self.full)
|
|
}
|
|
|
|
/// Create new regions where all sizes are mapped with `f`.
|
|
///
|
|
/// Note that since all regions must have the same width, the width returned
|
|
/// by `f` is ignored for the backlog and the final region.
|
|
pub fn map<'v, F>(&self, backlog: &'v mut Vec<Abs>, mut f: F) -> Regions<'v>
|
|
where
|
|
F: FnMut(Size) -> Size,
|
|
{
|
|
let x = self.size.x;
|
|
backlog.clear();
|
|
backlog.extend(self.backlog.iter().map(|&y| f(Size::new(x, y)).y));
|
|
Regions {
|
|
size: f(self.size),
|
|
full: f(Size::new(x, self.full)).y,
|
|
backlog,
|
|
last: self.last.map(|y| f(Size::new(x, y)).y),
|
|
expand: self.expand,
|
|
root: false,
|
|
}
|
|
}
|
|
|
|
/// Whether the first region is full and a region break is called for.
|
|
pub fn is_full(&self) -> bool {
|
|
Abs::zero().fits(self.size.y) && !self.in_last()
|
|
}
|
|
|
|
/// Whether the first region is the last usable region.
|
|
///
|
|
/// If this is true, calling `next()` will have no effect.
|
|
pub fn in_last(&self) -> bool {
|
|
self.backlog.is_empty() && self.last.map_or(true, |height| self.size.y == height)
|
|
}
|
|
|
|
/// The same regions, but with different `root` configuration.
|
|
pub fn with_root(self, root: bool) -> Self {
|
|
Self { root, ..self }
|
|
}
|
|
|
|
/// Advance to the next region if there is any.
|
|
pub fn next(&mut self) {
|
|
if let Some(height) = self
|
|
.backlog
|
|
.split_first()
|
|
.map(|(first, tail)| {
|
|
self.backlog = tail;
|
|
*first
|
|
})
|
|
.or(self.last)
|
|
{
|
|
self.size.y = height;
|
|
self.full = height;
|
|
}
|
|
}
|
|
|
|
/// An iterator that returns the sizes of the first and all following
|
|
/// regions, equivalently to what would be produced by calling
|
|
/// [`next()`](Self::next) repeatedly until all regions are exhausted.
|
|
/// This iterator may be infinite.
|
|
pub fn iter(&self) -> impl Iterator<Item = Size> + '_ {
|
|
let first = std::iter::once(self.size);
|
|
let backlog = self.backlog.iter();
|
|
let last = self.last.iter().cycle();
|
|
first.chain(backlog.chain(last).map(|&h| Size::new(self.size.x, h)))
|
|
}
|
|
}
|
|
|
|
impl Debug for Regions<'_> {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
f.write_str("Regions ")?;
|
|
let mut list = f.debug_list();
|
|
let mut prev = self.size.y;
|
|
list.entry(&self.size);
|
|
for &height in self.backlog {
|
|
list.entry(&Size::new(self.size.x, height));
|
|
prev = height;
|
|
}
|
|
if let Some(last) = self.last {
|
|
if last != prev {
|
|
list.entry(&Size::new(self.size.x, last));
|
|
}
|
|
list.entry(&(..));
|
|
}
|
|
list.finish()
|
|
}
|
|
}
|