use crate::console::RenderContext;
use crate::renderable::{BoxedRenderable, Renderable, Segment};
use crate::text::{Alignment, Span};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum VerticalAlignment {
#[default]
Top,
Middle,
Bottom,
}
pub struct Align {
child: BoxedRenderable,
align: Alignment,
vertical: VerticalAlignment,
height: Option<usize>,
pad: bool,
}
impl Align {
pub fn left(child: impl Renderable + Send + Sync + 'static) -> Self {
Align {
child: Box::new(child),
align: Alignment::Left,
vertical: VerticalAlignment::Top,
height: None,
pad: true,
}
}
pub fn center(child: impl Renderable + Send + Sync + 'static) -> Self {
Align {
child: Box::new(child),
align: Alignment::Center,
vertical: VerticalAlignment::Top,
height: None,
pad: true,
}
}
pub fn right(child: impl Renderable + Send + Sync + 'static) -> Self {
Align {
child: Box::new(child),
align: Alignment::Right,
vertical: VerticalAlignment::Top,
height: None,
pad: true,
}
}
pub fn vertical(mut self, vertical: VerticalAlignment) -> Self {
self.vertical = vertical;
self
}
pub fn height(mut self, height: usize) -> Self {
self.height = Some(height);
self
}
}
impl Renderable for Align {
fn render(&self, context: &RenderContext) -> Vec<Segment> {
let segments = self.child.render(context);
let width = context.width;
let mut aligned_segments = Vec::with_capacity(segments.len());
for segment in segments {
let mut line = segment.spans.clone();
let line_width: usize = line.iter().map(|s| s.width()).sum();
if line_width < width {
let padding = width - line_width;
match self.align {
Alignment::Left => {
if self.pad {
line.push(Span::raw(" ".repeat(padding)));
}
}
Alignment::Right => {
let mut new_line = vec![Span::raw(" ".repeat(padding))];
new_line.extend(line);
line = new_line;
}
Alignment::Center => {
let left_pad = padding / 2;
let right_pad = padding - left_pad;
let mut new_line = vec![Span::raw(" ".repeat(left_pad))];
new_line.extend(line);
if self.pad {
new_line.push(Span::raw(" ".repeat(right_pad)));
}
line = new_line;
}
}
}
if segment.newline {
aligned_segments.push(Segment::line(line));
} else {
aligned_segments.push(Segment::new(line));
}
}
if let Some(target_height) = self.height {
let current_height = aligned_segments.len();
if current_height < target_height {
let diff = target_height - current_height;
match self.vertical {
VerticalAlignment::Top => {
for _ in 0..diff {
aligned_segments
.push(Segment::line(vec![Span::raw(" ".repeat(width))]));
}
}
VerticalAlignment::Bottom => {
let mut new_segments = Vec::with_capacity(target_height);
for _ in 0..diff {
new_segments.push(Segment::line(vec![Span::raw(" ".repeat(width))]));
}
new_segments.extend(aligned_segments);
aligned_segments = new_segments;
}
VerticalAlignment::Middle => {
let top_pad = diff / 2;
let bottom_pad = diff - top_pad;
let mut new_segments = Vec::with_capacity(target_height);
for _ in 0..top_pad {
new_segments.push(Segment::line(vec![Span::raw(" ".repeat(width))]));
}
new_segments.extend(aligned_segments);
for _ in 0..bottom_pad {
new_segments.push(Segment::line(vec![Span::raw(" ".repeat(width))]));
}
aligned_segments = new_segments;
}
}
}
}
aligned_segments
}
}