summaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/input/theme.rs
blob: bf0edf232c0eb8a246ca68fe44102704c4c31564 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use serde::{
    de::{Error, Visitor},
    Deserialize, Deserializer, Serialize, Serializer,
};
use std::{
    collections::{BTreeMap, HashMap},
    fmt,
};

use crate::data::Palette;

#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
pub struct UiConfig {
    pub pane_frames: FrameConfig,
}

impl UiConfig {
    pub fn merge(&self, other: UiConfig) -> Self {
        let mut merged = self.clone();
        merged.pane_frames = merged.pane_frames.merge(other.pane_frames);
        merged
    }
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)]
pub struct FrameConfig {
    pub rounded_corners: bool,
    pub hide_session_name: bool,
}

impl FrameConfig {
    pub fn merge(&self, other: FrameConfig) -> Self {
        let mut merged = self.clone();
        merged.rounded_corners = other.rounded_corners;
        merged.hide_session_name = other.hide_session_name;
        merged
    }
}

#[derive(Clone, PartialEq, Default)]
pub struct Themes(HashMap<String, Theme>);

impl fmt::Debug for Themes {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut stable_sorted = BTreeMap::new();
        for (theme_name, theme) in self.0.iter() {
            stable_sorted.insert(theme_name, theme);
        }
        write!(f, "{:#?}", stable_sorted)
    }
}

impl Themes {
    pub fn from_data(theme_data: HashMap<String, Theme>) -> Self {
        Themes(theme_data)
    }
    pub fn insert(&mut self, theme_name: String, theme: Theme) {
        self.0.insert(theme_name, theme);
    }
    pub fn merge(&self, mut other: Themes) -> Self {
        let mut merged = self.clone();
        for (name, theme) in other.0.drain() {
            merged.0.insert(name, theme);
        }
        merged
    }
    pub fn get_theme(&self, theme_name: &str) -> Option<&Theme> {
        self.0.get(theme_name)
    }
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Theme {
    #[serde(flatten)]
    pub palette: Palette,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct HexColor(u8, u8, u8);

impl From<HexColor> for (u8, u8, u8) {
    fn from(e: HexColor) -> (u8, u8, u8) {
        let HexColor(r, g, b) = e;
        (r, g, b)
    }
}

pub struct HexColorVisitor();

impl<'de> Visitor<'de> for HexColorVisitor {
    type Value = HexColor;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "a hex color in the format #RGB or #RRGGBB")
    }

    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
    where
        E: Error,
    {
        if let Some(stripped) = s.strip_prefix('#') {
            return self.visit_str(stripped);
        }

        if s.len() == 3 {
            Ok(HexColor(
                u8::from_str_radix(&s[0..1], 16).map_err(E::custom)? * 0x11,
                u8::from_str_radix(&s[1..2], 16).map_err(E::custom)? * 0x11,
                u8::from_str_radix(&s[2..3], 16).map_err(E::custom)? * 0x11,
            ))
        } else if s.len() == 6 {
            Ok(HexColor(
                u8::from_str_radix(&s[0..2], 16).map_err(E::custom)?,
                u8::from_str_radix(&s[2..4], 16).map_err(E::custom)?,
                u8::from_str_radix(&s[4..6], 16).map_err(E::custom)?,
            ))
        } else {
            Err(Error::custom(
                "Hex color must be of form \"#RGB\" or \"#RRGGBB\"",
            ))
        }
    }
}

impl<'de> Deserialize<'de> for HexColor {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(HexColorVisitor())
    }
}
impl Serialize for HexColor {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(format!("{:02X}{:02X}{:02X}", self.0, self.1, self.2).as_str())
    }
}

#[cfg(test)]
#[path = "./unit/theme_test.rs"]
mod theme_test;