use ratatui::style::Color;
pub use tui_nuances::{
Theme as NuancesTheme, ThemeProvider, ThemeSwitcher as NuancesSwitcher, Themed,
};
use crate::palette::Theme;
impl Theme {
#[must_use]
pub fn from_nuances(t: &NuancesTheme) -> Self {
Self {
brand: t.title,
accent: t.border_focused,
success: t.success,
dim: t.muted,
fg: t.foreground,
sel_bg: t.selection_bg,
dir: t.primary,
match_file: t.highlight,
bg: t.background.unwrap_or(Color::Reset),
}
}
#[must_use]
pub fn from_provider(p: &impl ThemeProvider) -> Self {
Self::from_nuances(&p.nuance_theme())
}
}
impl From<&NuancesTheme> for Theme {
fn from(t: &NuancesTheme) -> Self {
Self::from_nuances(t)
}
}
impl From<NuancesTheme> for Theme {
fn from(t: NuancesTheme) -> Self {
Self::from_nuances(&t)
}
}
impl Themed for Theme {
fn apply_theme(self, theme: &NuancesTheme) -> Self {
Self::from_nuances(theme)
}
}
macro_rules! builder_fields {
($($f:ident),* $(,)?) => {
#[derive(Debug, Clone, Default)]
pub struct ThemeBuilder {
base: Option<Theme>,
$($f: Option<Color>,)*
}
impl ThemeBuilder {
$(
#[doc = concat!("Explicitly set `", stringify!($f), "` (wins over the shared theme).")]
#[must_use]
pub fn $f(mut self, color: Color) -> Self {
self.$f = Some(color);
self
}
)*
#[must_use]
pub fn build(&self) -> Theme {
let mut t = self.base.unwrap_or_default();
$(if let Some(c) = self.$f { t.$f = c; })*
t
}
}
};
}
builder_fields!(brand, accent, success, dim, fg, sel_bg, dir, match_file, bg);
impl ThemeBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn theme(mut self, theme: &NuancesTheme) -> Self {
self.base = Some(Theme::from_nuances(theme));
self
}
}
impl Themed for ThemeBuilder {
fn apply_theme(self, theme: &NuancesTheme) -> Self {
self.theme(theme)
}
}
impl From<ThemeBuilder> for Theme {
fn from(b: ThemeBuilder) -> Self {
b.build()
}
}
pub fn render_nuanced(
explorer: &mut crate::FileExplorer,
frame: &mut ratatui::Frame,
area: ratatui::layout::Rect,
theme: &NuancesTheme,
) {
crate::render_themed(explorer, frame, area, &Theme::from_nuances(theme));
}
pub fn render_dual_pane_nuanced(
dual: &mut crate::DualPane,
frame: &mut ratatui::Frame,
area: ratatui::layout::Rect,
theme: &NuancesTheme,
) {
crate::render_dual_pane_themed(dual, frame, area, &Theme::from_nuances(theme));
}
#[cfg(test)]
mod tests {
use super::*;
use ratatui::{Terminal, backend::TestBackend};
#[test]
fn mapping() {
let n = NuancesTheme::dracula();
let t = Theme::from_nuances(&n);
assert_eq!(t.brand, n.title);
assert_eq!(t.dim, n.muted);
assert_eq!(t.sel_bg, n.selection_bg);
assert_eq!(t.dir, n.primary);
assert_eq!(t.bg, n.background.unwrap_or(Color::Reset));
assert_eq!(Theme::from(&n), Theme::from(n.clone()));
assert_eq!(Theme::default().apply_theme(&n), t);
}
#[test]
fn no_theme_is_default() {
assert_eq!(ThemeBuilder::new().build(), Theme::default());
}
#[test]
fn explicit_wins_any_order() {
let n = NuancesTheme::nord();
let a = ThemeBuilder::new().brand(Color::Red).theme(&n).build();
let b = ThemeBuilder::new().theme(&n).brand(Color::Red).build();
assert_eq!(a, b);
assert_eq!(a.brand, Color::Red);
assert_eq!(a.accent, n.border_focused);
}
#[test]
fn every_builtin_renders() {
for n in NuancesTheme::all() {
let mut term = Terminal::new(TestBackend::new(60, 14)).unwrap();
let mut ex = crate::FileExplorer::new(std::env::temp_dir(), vec![]);
term.draw(|f| render_nuanced(&mut ex, f, f.area(), &n))
.unwrap();
let mut dual = crate::DualPane::builder(std::env::temp_dir()).build();
term.draw(|f| render_dual_pane_nuanced(&mut dual, f, f.area(), &n))
.unwrap();
}
}
}