1use crate::*;
2
3pub struct Padding<E: Element> {
4 pub left: f32,
5 pub right: f32,
6 pub top: f32,
7 pub bottom: f32,
8 pub element: E,
9}
10
11impl<E: Element> Padding<E> {
12 pub fn left(left: f32, element: E) -> Self {
13 Padding {
14 left,
15 right: 0.,
16 top: 0.,
17 bottom: 0.,
18 element,
19 }
20 }
21
22 pub fn right(right: f32, element: E) -> Self {
23 Padding {
24 left: 0.,
25 right,
26 top: 0.,
27 bottom: 0.,
28 element,
29 }
30 }
31
32 pub fn top(top: f32, element: E) -> Self {
33 Padding {
34 left: 0.,
35 right: 0.,
36 top,
37 bottom: 0.,
38 element,
39 }
40 }
41
42 pub fn bottom(bottom: f32, element: E) -> Self {
43 Padding {
44 left: 0.,
45 right: 0.,
46 top: 0.,
47 bottom,
48 element,
49 }
50 }
51}
52
53impl<E: Element> Element for Padding<E> {
54 fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
55 self.element.first_location_usage(FirstLocationUsageCtx {
56 text_pieces_cache: ctx.text_pieces_cache,
57 width: self.width(ctx.width),
58 first_height: self.height(ctx.first_height),
59 full_height: self.height(ctx.full_height),
60 })
61 }
62
63 fn measure(&self, ctx: MeasureCtx) -> ElementSize {
64 let mut break_count = 0;
65 let mut extra_location_min_height = None;
66
67 let size = self.element.measure(MeasureCtx {
68 text_pieces_cache: ctx.text_pieces_cache,
69 width: self.width(ctx.width),
70 first_height: self.height(ctx.first_height),
71 breakable: ctx.breakable.as_ref().map(|b| BreakableMeasure {
72 break_count: &mut break_count,
73 extra_location_min_height: &mut extra_location_min_height,
74 full_height: self.height(b.full_height),
75 }),
76 });
77
78 if let Some(b) = ctx.breakable {
79 *b.break_count = break_count;
80
81 *b.extra_location_min_height = extra_location_min_height;
83 }
84
85 self.size(size)
86 }
87
88 fn draw(&self, ctx: DrawCtx) -> ElementSize {
89 let width = self.width(ctx.width);
90
91 let draw_ctx = DrawCtx {
92 pdf: ctx.pdf,
93 text_pieces_cache: ctx.text_pieces_cache,
94
95 location: Location {
96 pos: (
97 ctx.location.pos.0 + self.left,
98 ctx.location.pos.1 - self.top,
99 ),
100 ..ctx.location
101 },
102
103 preferred_height: ctx.preferred_height.map(|p| self.height(p)),
104
105 width,
106 first_height: self.height(ctx.first_height),
107 breakable: None,
108 };
109
110 let size = if let Some(breakable) = ctx.breakable {
111 self.element.draw(DrawCtx {
112 breakable: Some(BreakableDraw {
113 full_height: self.height(breakable.full_height),
114 preferred_height_break_count: breakable.preferred_height_break_count,
115 do_break: &mut |pdf, location_idx, height| {
116 let mut location = (breakable.do_break)(
117 pdf,
118 location_idx,
119 height.map(|h| h + self.top + self.bottom),
120 );
121
122 location.pos.0 += self.left;
123 location.pos.1 -= self.top;
124
125 location
126 },
127 }),
128 ..draw_ctx
129 })
130 } else {
131 self.element.draw(draw_ctx)
132 };
133
134 self.size(size)
135 }
136}
137
138impl<E: Element> Padding<E> {
139 fn width(&self, constraint: WidthConstraint) -> WidthConstraint {
140 WidthConstraint {
141 max: constraint.max - self.left - self.right,
142 expand: constraint.expand,
143 }
144 }
145
146 fn height(&self, input: f32) -> f32 {
147 input - self.top - self.bottom
148 }
149
150 fn size(&self, size: ElementSize) -> ElementSize {
151 ElementSize {
152 width: size.width.map(|w| w + self.left + self.right),
153 height: size.height.map(|h| h + self.top + self.bottom),
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::test_utils::*;
162
163 #[test]
164 fn test_padding() {
165 let element = BuildElement(|build_ctx, callback| {
166 let content = FakeText {
167 width: 10.,
168 line_height: 1.,
169 lines: 10,
170 };
171
172 let proxy = ElementProxy {
173 before_draw: &|ctx: &mut DrawCtx| {
174 assert_eq!(
175 ctx.width,
176 WidthConstraint {
177 max: 40. - 25.,
178 expand: build_ctx.width.expand,
179 }
180 );
181 assert_eq!(ctx.location.pos.0, 24.);
182 },
183 after_break: &|_location_idx: u32,
184 location: &Location,
185 _width: WidthConstraint,
186 _first_height| {
187 assert_eq!(location.pos.0, 24.);
188 },
189 ..ElementProxy::new(content)
190 };
191 callback.call(Padding {
192 left: 12.,
193 right: 13.,
194 top: 14.,
195 bottom: 15.,
196 element: proxy,
197 })
198 });
199
200 for output in (ElementTestParams {
201 first_height: 30.1,
202 full_height: 31.5,
203 width: 40.,
204 ..Default::default()
205 })
206 .run(&element)
207 {
208 output.assert_size(ElementSize {
209 width: Some(output.width.constrain(35.)),
210 height: Some(if output.breakable.is_none() {
211 10. + 29.
212 } else if output.first_height == 30.1 {
213 1. + 29.
214 } else {
215 2. + 29.
216 }),
217 });
218
219 if let Some(b) = output.breakable {
220 b.assert_break_count(if output.first_height == 30.1 { 5 } else { 4 })
221 .assert_extra_location_min_height(None);
222 }
223 }
224 }
225}