use std::sync::{OnceLock, RwLock};
use crate::core::border::BorderType;
use crate::core::style::{Color, Style};
const DEFAULT_ACCENT: Color = Color::Rgb(137, 180, 250);
#[derive(Debug, Clone)]
pub struct Theme {
pub accent: Color,
pub title: Style,
pub heading: Style,
pub secondary: Style,
pub success: Style,
pub error: Style,
pub warning: Style,
pub info: Style,
pub debug: Style,
pub hint: Style,
pub selection: Style,
pub cursor: Style,
pub border: BorderType,
pub unicode: bool,
}
impl Default for Theme {
fn default() -> Self {
let accent = DEFAULT_ACCENT;
Self {
accent,
title: Style::new().fg(accent).bold(),
heading: Style::new().fg(accent).bold(),
secondary: Style::new().dim(),
success: Style::new().fg(Color::Green),
error: Style::new().fg(Color::Red),
warning: Style::new().fg(Color::Yellow),
info: Style::new().fg(accent),
debug: Style::new().fg(Color::Magenta),
hint: Style::new().dim(),
selection: Style::new().fg(accent).bold(),
cursor: Style::new().fg(Color::Black).bg(accent),
border: BorderType::Rounded,
unicode: true,
}
}
}
impl Theme {
pub fn bullet(&self) -> &'static str {
if self.unicode { "•" } else { "*" }
}
pub fn cursor_marker(&self) -> &'static str {
if self.unicode { "‣ " } else { "> " }
}
pub fn marker(&self) -> &'static str {
" "
}
pub fn checkbox_on(&self) -> &'static str {
if self.unicode { "◉ " } else { "[x] " }
}
pub fn checkbox_off(&self) -> &'static str {
if self.unicode { "◯ " } else { "[ ] " }
}
}
fn storage() -> &'static RwLock<Theme> {
static THEME: OnceLock<RwLock<Theme>> = OnceLock::new();
THEME.get_or_init(|| RwLock::new(Theme::default()))
}
pub fn theme() -> Theme {
match storage().read() {
Ok(guard) => guard.clone(),
Err(poisoned) => poisoned.into_inner().clone(),
}
}
pub fn set_theme(new_theme: Theme) {
let mut guard = match storage().write() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
*guard = new_theme;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_theme_uses_rounded_border() {
assert_eq!(Theme::default().border, BorderType::Rounded);
}
#[test]
fn ascii_mode_changes_glyphs() {
let theme = Theme {
unicode: false,
..Theme::default()
};
assert_eq!(theme.bullet(), "*");
assert_eq!(theme.cursor_marker(), "> ");
}
}