theme.rs

  1use gpui2::{AppContext, Hsla, Result, WindowContext};
  2use serde::{de::Visitor, Deserialize, Deserializer};
  3use std::collections::HashMap;
  4use std::fmt;
  5use std::sync::Arc;
  6
  7#[derive(Deserialize, Clone, Default, Debug)]
  8pub struct Theme {
  9    pub name: String,
 10    pub is_light: bool,
 11    pub lowest: Layer,
 12    pub middle: Layer,
 13    pub highest: Layer,
 14    pub popover_shadow: Shadow,
 15    pub modal_shadow: Shadow,
 16    #[serde(deserialize_with = "deserialize_player_colors")]
 17    pub players: Vec<PlayerColors>,
 18    #[serde(deserialize_with = "deserialize_syntax_colors")]
 19    pub syntax: HashMap<String, Hsla>,
 20}
 21
 22#[derive(Deserialize, Clone, Default, Debug)]
 23pub struct Layer {
 24    pub base: StyleSet,
 25    pub variant: StyleSet,
 26    pub on: StyleSet,
 27    pub accent: StyleSet,
 28    pub positive: StyleSet,
 29    pub warning: StyleSet,
 30    pub negative: StyleSet,
 31}
 32
 33#[derive(Deserialize, Clone, Default, Debug)]
 34pub struct StyleSet {
 35    #[serde(rename = "default")]
 36    pub default: ContainerColors,
 37    pub hovered: ContainerColors,
 38    pub pressed: ContainerColors,
 39    pub active: ContainerColors,
 40    pub disabled: ContainerColors,
 41    pub inverted: ContainerColors,
 42}
 43
 44#[derive(Deserialize, Clone, Default, Debug)]
 45pub struct ContainerColors {
 46    pub background: Hsla,
 47    pub foreground: Hsla,
 48    pub border: Hsla,
 49}
 50
 51#[derive(Deserialize, Clone, Default, Debug)]
 52pub struct PlayerColors {
 53    pub selection: Hsla,
 54    pub cursor: Hsla,
 55}
 56
 57#[derive(Deserialize, Clone, Default, Debug)]
 58pub struct Shadow {
 59    pub blur: u8,
 60    pub color: Hsla,
 61    pub offset: Vec<u8>,
 62}
 63
 64fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
 65where
 66    D: Deserializer<'de>,
 67{
 68    struct PlayerArrayVisitor;
 69
 70    impl<'de> Visitor<'de> for PlayerArrayVisitor {
 71        type Value = Vec<PlayerColors>;
 72
 73        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
 74            formatter.write_str("an object with integer keys")
 75        }
 76
 77        fn visit_map<A: serde::de::MapAccess<'de>>(
 78            self,
 79            mut map: A,
 80        ) -> Result<Self::Value, A::Error> {
 81            let mut players = Vec::with_capacity(8);
 82            while let Some((key, value)) = map.next_entry::<usize, PlayerColors>()? {
 83                if key < 8 {
 84                    players.push(value);
 85                } else {
 86                    return Err(serde::de::Error::invalid_value(
 87                        serde::de::Unexpected::Unsigned(key as u64),
 88                        &"a key in range 0..7",
 89                    ));
 90                }
 91            }
 92            Ok(players)
 93        }
 94    }
 95
 96    deserializer.deserialize_map(PlayerArrayVisitor)
 97}
 98
 99fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result<HashMap<String, Hsla>, D::Error>
100where
101    D: serde::Deserializer<'de>,
102{
103    #[derive(Deserialize)]
104    struct ColorWrapper {
105        color: Hsla,
106    }
107
108    struct SyntaxVisitor;
109
110    impl<'de> Visitor<'de> for SyntaxVisitor {
111        type Value = HashMap<String, Hsla>;
112
113        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
114            formatter.write_str("a map with keys and objects with a single color field as values")
115        }
116
117        fn visit_map<M>(self, mut map: M) -> Result<HashMap<String, Hsla>, M::Error>
118        where
119            M: serde::de::MapAccess<'de>,
120        {
121            let mut result = HashMap::new();
122            while let Some(key) = map.next_key()? {
123                let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla
124                result.insert(key, wrapper.color);
125            }
126            Ok(result)
127        }
128    }
129    deserializer.deserialize_map(SyntaxVisitor)
130}
131
132pub fn old_theme(cx: &WindowContext) -> Arc<Theme> {
133    Arc::new(cx.global::<Theme>().clone())
134}
135
136pub fn theme(cx: &AppContext) -> Arc<theme2::Theme> {
137    theme2::active_theme(cx).clone()
138}