use retroglyph_core::{Color, Rect, Style};
use super::{Panel, Widget};
use crate::Surface;
use crate::layout::centered_rect;
use crate::{Align, Theme};
#[derive(Clone, Copy, Debug)]
pub struct Modal<'a> {
width: u16,
height: u16,
title: Option<&'a str>,
title_align: Align,
border_style: Style,
fill_style: Style,
}
impl<'a> Modal<'a> {
#[must_use]
pub fn new(width: u16, height: u16) -> Self {
Self {
width,
height,
title: None,
title_align: Align::Center,
border_style: Style::new(),
fill_style: Style::new(),
}
}
#[must_use]
pub const fn title(mut self, title: &'a str) -> Self {
self.title = Some(title);
self
}
#[must_use]
pub const fn title_align(mut self, align: Align) -> Self {
self.title_align = align;
self
}
#[must_use]
pub const fn border_style(mut self, style: Style) -> Self {
self.border_style = style;
self
}
#[must_use]
pub const fn fill_style(mut self, style: Style) -> Self {
self.fill_style = style;
self
}
#[must_use]
pub fn theme(self, theme: Theme) -> Self {
self.theme_on(theme, theme.panel_bg)
}
#[must_use]
pub fn theme_on(mut self, theme: Theme, bg: Color) -> Self {
self.border_style = Style::new().fg(theme.border).bg(theme.title_bg);
self.fill_style = Style::new().bg(bg);
self
}
pub fn render(self, screen: Rect, surface: &mut Surface<'_>) -> Rect {
let rect = centered_rect(screen, self.width, self.height);
let mut panel = Panel::new()
.border_style(self.border_style)
.fill_style(self.fill_style)
.title_align(self.title_align);
if let Some(title) = self.title {
panel = panel.title(title);
}
panel.render(rect, surface);
Rect::new(
rect.left() + 1,
rect.top() + 1,
rect.width().saturating_sub(2),
rect.height().saturating_sub(2),
)
}
}
#[cfg(test)]
mod tests {
use retroglyph_core::{Grid, Pos};
use super::*;
#[test]
fn centers_the_box_and_returns_the_inner_content_rect() {
let screen = Rect::new(0, 0, 20, 10);
let mut grid = Grid::new(20, 10);
let inner = Modal::new(10, 4).render(screen, &mut Surface::new(&mut grid, screen, 0));
assert_eq!(inner, Rect::new(6, 4, 8, 2));
assert_eq!(grid[Pos::new(5, 3)].glyph(), '┌');
assert_eq!(grid[Pos::new(14, 3)].glyph(), '┐');
}
#[test]
fn draws_only_the_box_leaving_the_rest_of_the_screen_untouched() {
let screen = Rect::new(0, 0, 20, 10);
let mut grid = Grid::new(20, 10);
Modal::new(10, 4).render(screen, &mut Surface::new(&mut grid, screen, 0));
assert_eq!(grid[Pos::new(0, 0)].glyph(), ' ');
}
#[test]
fn theme_maps_named_roles_onto_border_and_fill() {
let screen = Rect::new(0, 0, 20, 10);
let mut grid = Grid::new(20, 10);
Modal::new(10, 4)
.theme(Theme::DARK)
.render(screen, &mut Surface::new(&mut grid, screen, 0));
assert_eq!(
grid[Pos::new(5, 3)].style().foreground(),
Theme::DARK.border
);
assert_eq!(
grid[Pos::new(5, 3)].style().background(),
Theme::DARK.title_bg
);
assert_eq!(
grid[Pos::new(6, 4)].style().background(),
Theme::DARK.panel_bg
);
}
#[test]
fn theme_on_uses_the_given_backdrop_instead_of_panel_bg() {
let screen = Rect::new(0, 0, 20, 10);
let mut grid = Grid::new(20, 10);
Modal::new(10, 4)
.theme_on(Theme::DARK, Color::Default)
.render(screen, &mut Surface::new(&mut grid, screen, 0));
assert_eq!(
grid[Pos::new(5, 3)].style().foreground(),
Theme::DARK.border
);
assert_eq!(
grid[Pos::new(5, 3)].style().background(),
Theme::DARK.title_bg
);
assert_eq!(grid[Pos::new(6, 4)].style().background(), Color::Default);
}
}