use ratatui::style::{Color, Modifier, Style};
use crate::config::Theme as ThemeConfig;
#[derive(Debug, Clone)]
pub struct Theme {
header_bg: Color,
header_fg: Color,
filter_fg: Color,
filter_placeholder_fg: Color,
number_fg: Color,
script_fg: Color,
selected_bg: Color,
selected_fg: Color,
cursor_fg: Color,
multiselect_fg: Color,
description_fg: Color,
command_fg: Color,
separator_fg: Color,
footer_fg: Color,
key_fg: Color,
error_fg: Color,
success_fg: Color,
warning_fg: Color,
}
impl Default for Theme {
fn default() -> Self {
Self::new(&ThemeConfig::Default)
}
}
impl Theme {
pub fn new(config: &ThemeConfig) -> Self {
match config {
ThemeConfig::Default => Self::default_theme(),
ThemeConfig::Minimal => Self::minimal_theme(),
ThemeConfig::None => Self::no_color_theme(),
}
}
fn default_theme() -> Self {
Self {
header_bg: Color::Blue,
header_fg: Color::White,
filter_fg: Color::Yellow,
filter_placeholder_fg: Color::DarkGray,
number_fg: Color::Cyan,
script_fg: Color::White,
selected_bg: Color::Blue,
selected_fg: Color::White,
cursor_fg: Color::Green,
multiselect_fg: Color::Magenta,
description_fg: Color::Gray,
command_fg: Color::DarkGray,
separator_fg: Color::DarkGray,
footer_fg: Color::DarkGray,
key_fg: Color::Cyan,
error_fg: Color::Red,
success_fg: Color::Green,
warning_fg: Color::Yellow,
}
}
fn minimal_theme() -> Self {
Self {
header_bg: Color::Reset,
header_fg: Color::White,
filter_fg: Color::White,
filter_placeholder_fg: Color::DarkGray,
number_fg: Color::Gray,
script_fg: Color::White,
selected_bg: Color::Reset,
selected_fg: Color::Cyan,
cursor_fg: Color::White,
multiselect_fg: Color::White,
description_fg: Color::Gray,
command_fg: Color::DarkGray,
separator_fg: Color::DarkGray,
footer_fg: Color::DarkGray,
key_fg: Color::Gray,
error_fg: Color::Red,
success_fg: Color::Green,
warning_fg: Color::Yellow,
}
}
fn no_color_theme() -> Self {
Self {
header_bg: Color::Reset,
header_fg: Color::Reset,
filter_fg: Color::Reset,
filter_placeholder_fg: Color::Reset,
number_fg: Color::Reset,
script_fg: Color::Reset,
selected_bg: Color::Reset,
selected_fg: Color::Reset,
cursor_fg: Color::Reset,
multiselect_fg: Color::Reset,
description_fg: Color::Reset,
command_fg: Color::Reset,
separator_fg: Color::Reset,
footer_fg: Color::Reset,
key_fg: Color::Reset,
error_fg: Color::Reset,
success_fg: Color::Reset,
warning_fg: Color::Reset,
}
}
pub fn header(&self) -> Style {
Style::default()
.fg(self.header_fg)
.bg(self.header_bg)
.add_modifier(Modifier::BOLD)
}
pub fn header_project(&self) -> Style {
Style::default()
.fg(self.header_fg)
.bg(self.header_bg)
.add_modifier(Modifier::BOLD)
}
pub fn header_runner(&self) -> Style {
Style::default().fg(self.header_fg).bg(self.header_bg)
}
pub fn filter(&self) -> Style {
Style::default().fg(self.filter_fg)
}
pub fn filter_active(&self) -> Style {
Style::default()
.fg(self.filter_fg)
.add_modifier(Modifier::BOLD)
}
pub fn filter_placeholder(&self) -> Style {
Style::default()
.fg(self.filter_placeholder_fg)
.add_modifier(Modifier::ITALIC)
}
pub fn number(&self) -> Style {
Style::default()
.fg(self.number_fg)
.add_modifier(Modifier::DIM)
}
pub fn script(&self) -> Style {
Style::default().fg(self.script_fg)
}
pub fn selected(&self) -> Style {
if self.selected_bg == Color::Reset {
Style::default()
.fg(self.selected_fg)
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED)
} else {
Style::default()
.fg(self.selected_fg)
.bg(self.selected_bg)
.add_modifier(Modifier::BOLD)
}
}
pub fn cursor(&self) -> Style {
Style::default()
.fg(self.cursor_fg)
.add_modifier(Modifier::BOLD)
}
pub fn multiselect(&self) -> Style {
Style::default()
.fg(self.multiselect_fg)
.add_modifier(Modifier::BOLD)
}
pub fn description(&self) -> Style {
Style::default().fg(self.description_fg)
}
pub fn command(&self) -> Style {
Style::default()
.fg(self.command_fg)
.add_modifier(Modifier::ITALIC)
}
pub fn separator(&self) -> Style {
Style::default().fg(self.separator_fg)
}
pub fn footer(&self) -> Style {
Style::default().fg(self.footer_fg)
}
pub fn key(&self) -> Style {
Style::default()
.fg(self.key_fg)
.add_modifier(Modifier::BOLD)
}
pub fn error(&self) -> Style {
Style::default()
.fg(self.error_fg)
.add_modifier(Modifier::BOLD)
}
pub fn success(&self) -> Style {
Style::default().fg(self.success_fg)
}
pub fn warning(&self) -> Style {
Style::default().fg(self.warning_fg)
}
pub fn dim(&self) -> Style {
Style::default().add_modifier(Modifier::DIM)
}
pub fn bold(&self) -> Style {
Style::default().add_modifier(Modifier::BOLD)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_theme() {
let theme = Theme::default();
assert_eq!(theme.header_bg, Color::Blue);
assert_eq!(theme.header_fg, Color::White);
}
#[test]
fn test_theme_from_config() {
let theme = Theme::new(&ThemeConfig::Default);
assert_eq!(theme.header_bg, Color::Blue);
let theme = Theme::new(&ThemeConfig::Minimal);
assert_eq!(theme.header_bg, Color::Reset);
let theme = Theme::new(&ThemeConfig::None);
assert_eq!(theme.header_fg, Color::Reset);
}
#[test]
fn test_header_style() {
let theme = Theme::default();
let style = theme.header();
assert!(style.add_modifier.contains(Modifier::BOLD));
}
#[test]
fn test_selected_style_minimal() {
let theme = Theme::new(&ThemeConfig::Minimal);
let style = theme.selected();
assert!(style.add_modifier.contains(Modifier::UNDERLINED));
}
#[test]
fn test_no_color_theme() {
let theme = Theme::new(&ThemeConfig::None);
assert_eq!(theme.header_bg, Color::Reset);
assert_eq!(theme.filter_fg, Color::Reset);
assert_eq!(theme.number_fg, Color::Reset);
}
}