diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-03 11:44:53 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-03 13:35:39 +0100 |
| commit | 37a7afddfaffd44cb9bc013c9506599267e08983 (patch) | |
| tree | 20e7d62d3c5418baff01a21d0406b91bf3096214 /library/src/layout/pad.rs | |
| parent | 56342bd972a13ffe21beaf2b87ab7eb1597704b4 (diff) | |
Split crates
Diffstat (limited to 'library/src/layout/pad.rs')
| -rw-r--r-- | library/src/layout/pad.rs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs new file mode 100644 index 00000000..318d9f27 --- /dev/null +++ b/library/src/layout/pad.rs @@ -0,0 +1,83 @@ +use crate::prelude::*; + +/// Pad content at the sides. +#[derive(Debug, Hash)] +pub struct PadNode { + /// The amount of padding. + pub padding: Sides<Rel<Length>>, + /// The content whose sides to pad. + pub child: Content, +} + +#[node(LayoutBlock)] +impl PadNode { + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { + let all = args.named("rest")?.or(args.find()?); + let x = args.named("x")?; + let y = args.named("y")?; + let left = args.named("left")?.or(x).or(all).unwrap_or_default(); + let top = args.named("top")?.or(y).or(all).unwrap_or_default(); + let right = args.named("right")?.or(x).or(all).unwrap_or_default(); + let bottom = args.named("bottom")?.or(y).or(all).unwrap_or_default(); + let body = args.expect::<Content>("body")?; + let padding = Sides::new(left, top, right, bottom); + Ok(body.padded(padding)) + } +} + +impl LayoutBlock for PadNode { + fn layout_block( + &self, + world: Tracked<dyn World>, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult<Vec<Frame>> { + // Layout child into padded regions. + let padding = self.padding.resolve(styles); + let pod = regions.map(|size| shrink(size, padding)); + let mut frames = self.child.layout_block(world, &pod, styles)?; + + for frame in &mut frames { + // Apply the padding inversely such that the grown size padded + // yields the frame's size. + let padded = grow(frame.size(), padding); + let padding = padding.relative_to(padded); + let offset = Point::new(padding.left, padding.top); + + // Grow the frame and translate everything in the frame inwards. + frame.set_size(padded); + frame.translate(offset); + } + + Ok(frames) + } +} + +/// Shrink a size by padding relative to the size itself. +fn shrink(size: Size, padding: Sides<Rel<Abs>>) -> Size { + size - padding.relative_to(size).sum_by_axis() +} + +/// Grow a size by padding relative to the grown size. +/// This is the inverse operation to `shrink()`. +/// +/// For the horizontal axis the derivation looks as follows. +/// (Vertical axis is analogous.) +/// +/// Let w be the grown target width, +/// s be the given width, +/// l be the left padding, +/// r be the right padding, +/// p = l + r. +/// +/// We want that: w - l.resolve(w) - r.resolve(w) = s +/// +/// Thus: w - l.resolve(w) - r.resolve(w) = s +/// <=> w - p.resolve(w) = s +/// <=> w - p.rel * w - p.abs = s +/// <=> (1 - p.rel) * w = s + p.abs +/// <=> w = (s + p.abs) / (1 - p.rel) +fn grow(size: Size, padding: Sides<Rel<Abs>>) -> Size { + size.zip(padding.sum_by_axis()) + .map(|(s, p)| (s + p.abs).safe_div(1.0 - p.rel.get())) +} |
