typst_library/layout/
regions.rs

1use std::fmt::{self, Debug, Formatter};
2
3use crate::layout::{Abs, Axes, Size};
4
5/// A single region to layout into.
6#[derive(Debug, Copy, Clone, Hash)]
7pub struct Region {
8    /// The size of the region.
9    pub size: Size,
10    /// Whether elements should expand to fill the regions instead of shrinking
11    /// to fit the content.
12    pub expand: Axes<bool>,
13}
14
15impl Region {
16    /// Create a new region.
17    pub fn new(size: Size, expand: Axes<bool>) -> Self {
18        Self { size, expand }
19    }
20}
21
22impl From<Region> for Regions<'_> {
23    fn from(region: Region) -> Self {
24        Regions {
25            size: region.size,
26            expand: region.expand,
27            full: region.size.y,
28            backlog: &[],
29            last: None,
30        }
31    }
32}
33
34/// A sequence of regions to layout into.
35///
36/// A *region* is a contiguous rectangular space in which elements
37/// can be laid out. All regions within a `Regions` object have the
38/// same width, namely `self.size.x`. This means that it is not
39/// currently possible to, for instance, have content wrap to the
40/// side of a floating element.
41#[derive(Copy, Clone, Hash)]
42pub struct Regions<'a> {
43    /// The remaining size of the first region.
44    pub size: Size,
45    /// Whether elements should expand to fill the regions instead of shrinking
46    /// to fit the content.
47    pub expand: Axes<bool>,
48    /// The full height of the region for relative sizing.
49    pub full: Abs,
50    /// The height of followup regions. The width is the same for all regions.
51    pub backlog: &'a [Abs],
52    /// The height of the final region that is repeated once the backlog is
53    /// drained. The width is the same for all regions.
54    pub last: Option<Abs>,
55}
56
57impl Regions<'_> {
58    /// Create a new sequence of same-size regions that repeats indefinitely.
59    pub fn repeat(size: Size, expand: Axes<bool>) -> Self {
60        Self {
61            size,
62            full: size.y,
63            backlog: &[],
64            last: Some(size.y),
65            expand,
66        }
67    }
68
69    /// The base size, which doesn't take into account that the regions is
70    /// already partially used up.
71    ///
72    /// This is also used for relative sizing.
73    pub fn base(&self) -> Size {
74        Size::new(self.size.x, self.full)
75    }
76
77    /// Create new regions where all sizes are mapped with `f`.
78    ///
79    /// Note that since all regions must have the same width, the width returned
80    /// by `f` is ignored for the backlog and the final region.
81    pub fn map<'v, F>(&self, backlog: &'v mut Vec<Abs>, mut f: F) -> Regions<'v>
82    where
83        F: FnMut(Size) -> Size,
84    {
85        let x = self.size.x;
86        backlog.clear();
87        backlog.extend(self.backlog.iter().map(|&y| f(Size::new(x, y)).y));
88        Regions {
89            size: f(self.size),
90            full: f(Size::new(x, self.full)).y,
91            backlog,
92            last: self.last.map(|y| f(Size::new(x, y)).y),
93            expand: self.expand,
94        }
95    }
96
97    /// Whether the first region is full and a region break is called for.
98    pub fn is_full(&self) -> bool {
99        Abs::zero().fits(self.size.y) && self.may_progress()
100    }
101
102    /// Whether a region break is permitted.
103    pub fn may_break(&self) -> bool {
104        !self.backlog.is_empty() || self.last.is_some()
105    }
106
107    /// Whether calling `next()` may improve a situation where there is a lack
108    /// of space.
109    pub fn may_progress(&self) -> bool {
110        !self.backlog.is_empty() || self.last.is_some_and(|height| self.size.y != height)
111    }
112
113    /// Advance to the next region if there is any.
114    pub fn next(&mut self) {
115        if let Some(height) = self
116            .backlog
117            .split_first()
118            .map(|(first, tail)| {
119                self.backlog = tail;
120                *first
121            })
122            .or(self.last)
123        {
124            self.size.y = height;
125            self.full = height;
126        }
127    }
128
129    /// An iterator that returns the sizes of the first and all following
130    /// regions, equivalently to what would be produced by calling
131    /// [`next()`](Self::next) repeatedly until all regions are exhausted.
132    /// This iterator may be infinite.
133    pub fn iter(&self) -> impl Iterator<Item = Size> + '_ {
134        let first = std::iter::once(self.size);
135        let backlog = self.backlog.iter();
136        let last = self.last.iter().cycle();
137        first.chain(backlog.chain(last).map(|&h| Size::new(self.size.x, h)))
138    }
139}
140
141impl Debug for Regions<'_> {
142    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
143        f.write_str("Regions ")?;
144        let mut list = f.debug_list();
145        let mut prev = self.size.y;
146        list.entry(&self.size);
147        for &height in self.backlog {
148            list.entry(&Size::new(self.size.x, height));
149            prev = height;
150        }
151        if let Some(last) = self.last {
152            if last != prev {
153                list.entry(&Size::new(self.size.x, last));
154            }
155            list.entry(&(..));
156        }
157        list.finish()
158    }
159}