wireman_theme/
theme.rs

1use crate::{skin, Config};
2use logger::Logger;
3use once_cell::sync::OnceCell;
4use ratatui::{layout::Direction, style::Style, widgets::BorderType};
5use serde::{Deserialize, Serialize};
6
7pub static THEME: OnceCell<Theme> = OnceCell::new();
8
9/// Configuration for line numbers in the editor.
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum LineNumbers {
13    /// Line numbers are disabled (default).
14    #[default]
15    None,
16    /// Display absolute line numbers.
17    Absolute,
18    /// Display relative line numbers.
19    Relative,
20}
21
22#[derive(Debug, Default, Clone)]
23pub struct Theme {
24    pub layout: Layout,
25    pub base: Base,
26    pub highlight: Highlight,
27    pub border: Border,
28    pub title: Title,
29    pub editor: Editor,
30    pub hide_footer: bool,
31    pub hide_status: bool,
32}
33
34impl Theme {
35    pub(crate) fn update_from_skin(&mut self, skin: &skin::Skin) {
36        skin.apply_to(self);
37    }
38}
39
40#[derive(Debug, Clone, Default)]
41pub struct Layout {
42    pub main_split: Direction,
43}
44
45#[derive(Debug, Clone, Default)]
46pub struct Base {
47    pub focused: Style,
48    pub unfocused: Style,
49}
50
51#[derive(Debug, Clone, Default)]
52pub struct Highlight {
53    pub focused: Style,
54    pub unfocused: Style,
55}
56
57#[derive(Debug, Clone, Default)]
58pub struct Title {
59    pub focused: Style,
60    pub unfocused: Style,
61}
62
63#[allow(clippy::struct_field_names)]
64#[derive(Debug, Clone)]
65pub struct Border {
66    pub focused: Style,
67    pub unfocused: Style,
68    pub border_type_focused: BorderType,
69    pub border_type_unfocused: BorderType,
70}
71
72impl Default for Border {
73    fn default() -> Self {
74        Self {
75            focused: Style::default(),
76            unfocused: Style::default(),
77            border_type_focused: BorderType::Rounded,
78            border_type_unfocused: BorderType::Rounded,
79        }
80    }
81}
82
83#[derive(Debug, Clone)]
84pub struct Editor {
85    pub line_numbers: LineNumbers,
86}
87
88impl Default for Editor {
89    fn default() -> Self {
90        Self {
91            line_numbers: LineNumbers::Absolute,
92        }
93    }
94}
95
96impl Theme {
97    /// Initializes the `Theme` from a config.
98    pub fn init(config: &Config) {
99        let skin = config
100            .skin
101            .as_deref()
102            .and_then(|skin_file| match skin::Skin::from_file(skin_file) {
103                Ok(skin) => Some(skin),
104                Err(err) => {
105                    Logger::debug(format!(
106                        "Failed to read skin from file {skin_file}, err: {err}"
107                    ));
108                    None
109                }
110            })
111            .unwrap_or_default();
112
113        let mut theme = Theme::default();
114        theme.update_from_skin(&skin);
115
116        let _ = THEME.set(theme.clone());
117    }
118
119    /// Gets the globally shared theme data
120    #[must_use]
121    pub fn global() -> &'static Theme {
122        THEME.get_or_init(|| {
123            Logger::debug("Theme was not initialized. Fallback to default.");
124            Theme::default()
125        })
126    }
127}