summaryrefslogtreecommitdiff
path: root/src/library/layout/pad.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-02-28 15:50:48 +0100
committerLaurenz <laurmaedje@gmail.com>2022-02-28 23:54:34 +0100
commit3ca5b238238e1128aa7bbfbd5db9e632045d8600 (patch)
tree2471f4b340a15695b7f4d518c0b39fabaea676c4 /src/library/layout/pad.rs
parentb63c21c91d99a1554a019dc275f955d3e6a34271 (diff)
Reorganize library
Diffstat (limited to 'src/library/layout/pad.rs')
-rw-r--r--src/library/layout/pad.rs83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/library/layout/pad.rs b/src/library/layout/pad.rs
new file mode 100644
index 00000000..175a54f0
--- /dev/null
+++ b/src/library/layout/pad.rs
@@ -0,0 +1,83 @@
+use crate::library::prelude::*;
+
+/// Pad a node at the sides.
+#[derive(Debug, Hash)]
+pub struct PadNode {
+ /// The amount of padding.
+ pub padding: Sides<Linear>,
+ /// The child node whose sides to pad.
+ pub child: LayoutNode,
+}
+
+#[class]
+impl PadNode {
+ fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+ let all = args.find()?;
+ let hor = args.named("horizontal")?;
+ let ver = args.named("vertical")?;
+ let left = args.named("left")?.or(hor).or(all).unwrap_or_default();
+ let top = args.named("top")?.or(ver).or(all).unwrap_or_default();
+ let right = args.named("right")?.or(hor).or(all).unwrap_or_default();
+ let bottom = args.named("bottom")?.or(ver).or(all).unwrap_or_default();
+ let body: LayoutNode = args.expect("body")?;
+ let padding = Sides::new(left, top, right, bottom);
+ Ok(Template::block(body.padded(padding)))
+ }
+}
+
+impl Layout for PadNode {
+ fn layout(
+ &self,
+ ctx: &mut Context,
+ regions: &Regions,
+ styles: StyleChain,
+ ) -> TypResult<Vec<Arc<Frame>>> {
+ // Layout child into padded regions.
+ let pod = regions.map(|size| shrink(size, self.padding));
+ let mut frames = self.child.layout(ctx, &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, self.padding);
+ let padding = self.padding.resolve(padded);
+ let offset = Point::new(padding.left, padding.top);
+
+ // Grow the frame and translate everything in the frame inwards.
+ let frame = Arc::make_mut(frame);
+ frame.size = padded;
+ frame.translate(offset);
+ }
+
+ Ok(frames)
+ }
+}
+
+/// Shrink a size by padding relative to the size itself.
+fn shrink(size: Size, padding: Sides<Linear>) -> Size {
+ size - padding.resolve(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 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<Linear>) -> Size {
+ size.zip(padding.sum_by_axis())
+ .map(|(s, p)| (s + p.abs).safe_div(1.0 - p.rel.get()))
+}