fast_rich/
padding.rs

1//! Padding wrapper for renderables.
2//!
3//! Adds configurable padding (spaces) around any renderable content.
4
5use crate::console::RenderContext;
6use crate::renderable::{BoxedRenderable, Renderable, Segment};
7use crate::text::Span;
8
9/// Padding specification.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct PaddingSpec {
12    /// Top padding (lines)
13    pub top: usize,
14    /// Right padding (spaces)
15    pub right: usize,
16    /// Bottom padding (lines)
17    pub bottom: usize,
18    /// Left padding (spaces)
19    pub left: usize,
20}
21
22impl PaddingSpec {
23    /// Create padding with all sides equal.
24    pub fn all(size: usize) -> Self {
25        PaddingSpec {
26            top: size,
27            right: size,
28            bottom: size,
29            left: size,
30        }
31    }
32
33    /// Create padding with vertical and horizontal values.
34    pub fn symmetric(vertical: usize, horizontal: usize) -> Self {
35        PaddingSpec {
36            top: vertical,
37            right: horizontal,
38            bottom: vertical,
39            left: horizontal,
40        }
41    }
42
43    /// Create padding for each side individually.
44    pub fn new(top: usize, right: usize, bottom: usize, left: usize) -> Self {
45        PaddingSpec {
46            top,
47            right,
48            bottom,
49            left,
50        }
51    }
52
53    /// No padding.
54    pub fn none() -> Self {
55        PaddingSpec::all(0)
56    }
57}
58
59/// A renderable that adds padding around its child.
60pub struct Padding {
61    child: BoxedRenderable,
62    spec: PaddingSpec,
63}
64
65impl Padding {
66    /// Create a new padding wrapper.
67    pub fn new(child: impl Renderable + Send + Sync + 'static, spec: PaddingSpec) -> Self {
68        Padding {
69            child: Box::new(child),
70            spec,
71        }
72    }
73
74    /// Create padding with all sides equal.
75    pub fn all(child: impl Renderable + Send + Sync + 'static, size: usize) -> Self {
76        Padding::new(child, PaddingSpec::all(size))
77    }
78
79    /// Create padding with vertical and horizontal values.
80    pub fn symmetric(
81        child: impl Renderable + Send + Sync + 'static,
82        vertical: usize,
83        horizontal: usize,
84    ) -> Self {
85        Padding::new(child, PaddingSpec::symmetric(vertical, horizontal))
86    }
87}
88
89impl Renderable for Padding {
90    fn render(&self, context: &RenderContext) -> Vec<Segment> {
91        // Calculate available width for child after horizontal padding
92        let child_width = context
93            .width
94            .saturating_sub(self.spec.left + self.spec.right);
95        let child_context = RenderContext { width: child_width, height: None };
96
97        // Render child
98        let child_segments = self.child.render(&child_context);
99
100        let mut result = Vec::new();
101
102        // Top padding (empty lines)
103        for _ in 0..self.spec.top {
104            result.push(Segment::line(vec![Span::raw(" ".repeat(context.width))]));
105        }
106
107        // Add left/right padding to each child segment
108        for segment in child_segments {
109            let mut padded_spans = Vec::new();
110
111            // Left padding
112            if self.spec.left > 0 {
113                padded_spans.push(Span::raw(" ".repeat(self.spec.left)));
114            }
115
116            // Child content
117            padded_spans.extend(segment.spans);
118
119            // Right padding
120            if self.spec.right > 0 {
121                padded_spans.push(Span::raw(" ".repeat(self.spec.right)));
122            }
123
124            if segment.newline {
125                result.push(Segment::line(padded_spans));
126            } else {
127                result.push(Segment::new(padded_spans));
128            }
129        }
130
131        // Bottom padding (empty lines)
132        for _ in 0..self.spec.bottom {
133            result.push(Segment::line(vec![Span::raw(" ".repeat(context.width))]));
134        }
135
136        result
137    }
138}