theme.rs

  1use gpui3::{
  2    BorrowAppContext, Bounds, Element, Hsla, LayoutId, Pixels, Result, ViewContext, WindowContext,
  3};
  4use serde::{de::Visitor, Deserialize, Deserializer};
  5use std::{collections::HashMap, fmt};
  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 themed<E, F>(theme: Theme, cx: &mut ViewContext<E::State>, build_child: F) -> Themed<E>
133where
134    E: Element,
135    F: FnOnce(&mut ViewContext<E::State>) -> E,
136{
137    let child = cx.with_state(theme.clone(), |cx| build_child(cx));
138    Themed { theme, child }
139}
140
141pub struct Themed<E> {
142    pub(crate) theme: Theme,
143    pub(crate) child: E,
144}
145
146impl<E: Element> Element for Themed<E> {
147    type State = E::State;
148    type FrameState = E::FrameState;
149
150    fn layout(
151        &mut self,
152        state: &mut E::State,
153        cx: &mut ViewContext<E::State>,
154    ) -> anyhow::Result<(LayoutId, Self::FrameState)>
155    where
156        Self: Sized,
157    {
158        cx.with_state(self.theme.clone(), |cx| self.child.layout(state, cx))
159    }
160
161    fn paint(
162        &mut self,
163        bounds: Bounds<Pixels>,
164        state: &mut Self::State,
165        frame_state: &mut Self::FrameState,
166        cx: &mut ViewContext<Self::State>,
167    ) -> Result<()>
168    where
169        Self: Sized,
170    {
171        cx.with_state(self.theme.clone(), |cx| {
172            self.child.paint(bounds, state, frame_state, cx)
173        })
174    }
175}
176
177// fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
178//     settings::get::<ThemeSettings>(cx)
179//         .theme
180//         .deserialized_base_theme
181//         .lock()
182//         .get_or_insert_with(|| {
183//             let theme: Theme =
184//                 serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
185//                     .unwrap();
186//             Box::new(theme)
187//         })
188//         .downcast_ref::<Theme>()
189//         .unwrap()
190//         .clone()
191// }
192
193pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
194    cx.state()
195}