1use crate::{skin, Config};
2use logger::Logger;
3use once_cell::sync::OnceCell;
4use ratatui::{style::Style, widgets::BorderType};
5
6pub static THEME: OnceCell<Theme> = OnceCell::new();
7
8#[derive(Debug, Default, Clone)]
9pub struct Theme {
10 pub base: Base,
11 pub highlight: Highlight,
12 pub border: Border,
13 pub title: Title,
14 pub hide_footer: bool,
15 pub hide_status: bool,
16}
17
18impl Theme {
19 pub(crate) fn update_from_skin(&mut self, skin: &skin::Skin) {
20 skin.apply_to(self);
21 }
22}
23
24#[derive(Debug, Clone, Default)]
25pub struct Base {
26 pub focused: Style,
27 pub unfocused: Style,
28}
29
30#[derive(Debug, Clone, Default)]
31pub struct Highlight {
32 pub focused: Style,
33 pub unfocused: Style,
34}
35
36#[derive(Debug, Clone, Default)]
37pub struct Title {
38 pub focused: Style,
39 pub unfocused: Style,
40}
41
42#[allow(clippy::struct_field_names)]
43#[derive(Debug, Clone)]
44pub struct Border {
45 pub focused: Style,
46 pub unfocused: Style,
47 pub border_type: BorderType,
48}
49
50impl Default for Border {
51 fn default() -> Self {
52 Self {
53 focused: Style::default(),
54 unfocused: Style::default(),
55 border_type: BorderType::Rounded,
56 }
57 }
58}
59
60impl Theme {
61 pub fn init(config: &Config) {
63 let skin = config
64 .skin
65 .as_deref()
66 .and_then(|skin_file| match skin::Skin::from_file(skin_file) {
67 Ok(skin) => Some(skin),
68 Err(err) => {
69 Logger::debug(format!(
70 "Failed to read skin from file {skin_file}, err: {err}"
71 ));
72 None
73 }
74 })
75 .unwrap_or_default();
76
77 let mut theme = Theme::default();
78 theme.update_from_skin(&skin);
79
80 let _ = THEME.set(theme.clone());
81 }
82
83 #[must_use]
85 pub fn global() -> &'static Theme {
86 THEME.get_or_init(|| {
87 Logger::debug("Theme was not initialized. Fallback to default.");
88 Theme::default()
89 })
90 }
91}