Skip to main content

laser_pdf/
flex.rs

1pub struct MeasureLayout {
2    width: f32,
3    gap: f32,
4    total_flex: u8,
5    no_expand_count: u8,
6    no_expand_width: f32,
7}
8
9impl MeasureLayout {
10    pub fn new(width: f32, gap: f32) -> Self {
11        MeasureLayout {
12            width,
13            gap,
14            total_flex: 0,
15            no_expand_count: 0,
16            no_expand_width: 0.,
17        }
18    }
19
20    pub fn add_fixed(&mut self, width: f32) {
21        self.no_expand_count += 1;
22        self.no_expand_width += width;
23    }
24
25    pub fn add_expand(&mut self, fraction: u8) {
26        // self.count += 1;
27        self.total_flex += fraction;
28    }
29
30    pub fn no_expand_width(&self) -> Option<f32> {
31        if self.no_expand_count == 0 {
32            None
33        } else {
34            Some(self.no_expand_width + self.gap * (self.no_expand_count - 1) as f32)
35        }
36    }
37
38    pub fn build(self) -> DrawLayout {
39        // The goal here is to divide the space in a way that, even with a gap, we divide the space
40        // such that for expample if you have a flex with 1fr + 1fr above a flex with 2fr + 1fr +
41        // 1fr the size of the first cell in each will actually be the same.
42        //
43        // Imagine each element has half a gap on either side. We can then divide the space
44        // including this and then subtract one gap for each element. Of course the input width
45        // needs to get one gap added to it at the beginning, otherwise the gaps wouldn't add up.
46        //
47        // For non-expanded elements we first subtract all of the non-expanded elements plus their
48        // gaps and then we do the math normally.
49
50        let remaining_width =
51            (self.width + self.gap - self.no_expand_width - self.gap * self.no_expand_count as f32)
52                .max(0.);
53
54        DrawLayout {
55            total_flex: self.total_flex,
56            gap: self.gap,
57            remaining_width,
58        }
59    }
60}
61
62#[derive(Copy, Clone)]
63pub struct DrawLayout {
64    total_flex: u8,
65    gap: f32,
66    remaining_width: f32,
67}
68
69impl DrawLayout {
70    pub fn expand_width(&self, fraction: u8) -> f32 {
71        (self.remaining_width * fraction as f32 / self.total_flex as f32 - self.gap).max(0.)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_alignment() {
81        let mut layout = MeasureLayout::new(15., 2.);
82        layout.add_expand(1);
83        layout.add_fixed(3.);
84        layout.add_expand(1);
85
86        let a = layout.build();
87
88        let mut layout = MeasureLayout::new(15., 2.);
89        layout.add_expand(1);
90        layout.add_expand(1);
91        layout.add_fixed(3.);
92        layout.add_expand(2);
93
94        let b = layout.build();
95
96        // the fixed element in the middle should stay in the same position in both cases
97        assert_eq!(
98            a.expand_width(1),
99            b.expand_width(1) + 2. + b.expand_width(1),
100        );
101        assert_eq!(a.expand_width(1), 4.,);
102
103        // while we're at it we can also test the total width
104        assert_eq!(a.expand_width(1) + 2. + 3. + 2. + a.expand_width(1), 15.);
105        assert_eq!(
106            b.expand_width(1) + 2. + b.expand_width(1) + 2. + 3. + 2. + b.expand_width(2),
107            15.
108        );
109    }
110
111    #[test]
112    fn test_total_width() {
113        {
114            let mut layout = MeasureLayout::new(100., 4.);
115            layout.add_expand(1);
116            layout.add_expand(1);
117            layout.add_expand(1);
118
119            let draw_layout = layout.build();
120
121            assert_eq!(
122                draw_layout.expand_width(1)
123                    + draw_layout.expand_width(1)
124                    + draw_layout.expand_width(1)
125                    + 2. * 4.,
126                100.,
127            );
128        }
129
130        {
131            let mut layout = MeasureLayout::new(100., 4.);
132            layout.add_expand(1);
133            layout.add_fixed(50.);
134            layout.add_expand(1);
135
136            let draw_layout = layout.build();
137
138            assert_eq!(
139                draw_layout.expand_width(1) + 50. + draw_layout.expand_width(1) + 2. * 4.,
140                100.,
141            );
142
143            assert_eq!(draw_layout.expand_width(1), 21.);
144        }
145
146        {
147            let mut layout = MeasureLayout::new(100., 4.);
148            layout.add_expand(1);
149            layout.add_fixed(25.);
150            layout.add_expand(1);
151            layout.add_fixed(25.);
152
153            let draw_layout = layout.build();
154
155            assert_eq!(
156                draw_layout.expand_width(1) + 25. + draw_layout.expand_width(1) + 25. + 3. * 4.,
157                100.,
158            );
159
160            assert_eq!(draw_layout.expand_width(1), 19.);
161        }
162
163        {
164            let mut layout = MeasureLayout::new(100., 4.);
165            layout.add_fixed(25.);
166            layout.add_expand(1);
167            layout.add_fixed(25.);
168            layout.add_expand(1);
169            layout.add_fixed(25.);
170
171            let draw_layout = layout.build();
172
173            assert_eq!(
174                25. + draw_layout.expand_width(1)
175                    + 25.
176                    + draw_layout.expand_width(1)
177                    + 25.
178                    + 4. * 4.,
179                100.,
180            );
181        }
182
183        {
184            let mut layout = MeasureLayout::new(100., 3.);
185            layout.add_fixed(25.);
186            layout.add_expand(2);
187            layout.add_fixed(25.);
188            layout.add_expand(1);
189            layout.add_fixed(25.);
190
191            let draw_layout = layout.build();
192
193            assert_eq!(
194                25. + draw_layout.expand_width(2)
195                    + 25.
196                    + draw_layout.expand_width(1)
197                    + 25.
198                    + 3. * 4.,
199                100.,
200            );
201        }
202
203        {
204            let mut layout = MeasureLayout::new(100., 4.);
205            layout.add_fixed(25.);
206            layout.add_expand(2);
207            layout.add_fixed(25.);
208            layout.add_expand(1);
209            layout.add_fixed(25.);
210
211            let draw_layout = layout.build();
212
213            assert_eq!(
214                (25. + draw_layout.expand_width(2)
215                    + 25.
216                    + draw_layout.expand_width(1)
217                    + 25.
218                    + 4. * 4.),
219                100.,
220            );
221        }
222
223        {
224            let mut layout = MeasureLayout::new(22., 2.);
225            layout.add_expand(2);
226            layout.add_fixed(14.);
227            layout.add_expand(1);
228
229            let draw_layout = layout.build();
230
231            assert_eq!(
232                draw_layout.expand_width(2) + 14. + draw_layout.expand_width(1) + 2. * 2.,
233                22.,
234            );
235        }
236    }
237}