1use crate::console::{Console, ConsoleOptions};
10use crate::protocol::Renderable;
11use crate::segment::Segment;
12use crate::style::Style;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum HorizontalAlign {
18 Left,
19 #[default]
20 Center,
21 Right,
22}
23
24pub struct Align {
26 child: Box<dyn Renderable>,
27 align: HorizontalAlign,
28}
29
30impl Align {
31 pub fn left(child: Box<dyn Renderable>) -> Self {
33 Align {
34 child,
35 align: HorizontalAlign::Left,
36 }
37 }
38
39 pub fn center(child: Box<dyn Renderable>) -> Self {
41 Align {
42 child,
43 align: HorizontalAlign::Center,
44 }
45 }
46
47 pub fn right(child: Box<dyn Renderable>) -> Self {
49 Align {
50 child,
51 align: HorizontalAlign::Right,
52 }
53 }
54}
55
56impl Renderable for Align {
57 fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
58 let width = options.max_width;
59 let lines = console.render_lines(self.child.as_ref(), options, false);
60 let style = Some(Style::new());
61
62 let mut rows: Vec<Vec<Segment>> = Vec::with_capacity(lines.len());
63 for line in lines {
64 let line_width: usize = line.iter().map(Segment::cell_length).sum();
65 let excess = width.saturating_sub(line_width);
66 let (left_pad, right_pad) = match self.align {
67 HorizontalAlign::Left => (0, excess),
68 HorizontalAlign::Right => (excess, 0),
69 HorizontalAlign::Center => (excess / 2, excess - excess / 2),
70 };
71 let mut row = Vec::new();
72 if left_pad > 0 {
73 row.push(Segment::new(" ".repeat(left_pad), style.clone()));
74 }
75 row.extend(line);
76 if right_pad > 0 {
77 row.push(Segment::new(" ".repeat(right_pad), style.clone()));
78 }
79 rows.push(row);
80 }
81
82 let mut segments = Vec::new();
83 let last = rows.len().saturating_sub(1);
84 for (index, row) in rows.into_iter().enumerate() {
85 segments.extend(row);
86 if index != last {
87 segments.push(Segment::line());
88 }
89 }
90 segments
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::color::ColorSystem;
98 use crate::text::Text;
99
100 fn console(width: usize) -> Console {
101 Console::builder()
102 .force_terminal(true)
103 .color_system(Some(ColorSystem::Truecolor))
104 .width(width)
105 .build()
106 }
107
108 #[test]
109 fn center_pads_both_sides() {
110 let out = console(20).render_export(&Align::center(Box::new(Text::new("hi"))));
111 assert_eq!(out, " hi \n");
112 }
113
114 #[test]
115 fn right_pads_left() {
116 let out = console(20).render_export(&Align::right(Box::new(Text::new("hi"))));
117 assert_eq!(out, " hi\n");
118 }
119
120 #[test]
121 fn center_odd_remainder_floors_left() {
122 let out = console(21).render_export(&Align::center(Box::new(Text::new("hi"))));
123 assert_eq!(out, " hi \n");
124 }
125}