theme.rs

  1use gpui3::{Element, Hsla, Layout, LayoutId, Result, StackContext, ViewContext, WindowContext};
  2use serde::{de::Visitor, Deserialize, Deserializer};
  3use std::{collections::HashMap, fmt};
  4
  5#[derive(Deserialize, Clone, Default, Debug)]
  6pub struct Theme {
  7    pub name: String,
  8    pub is_light: bool,
  9    pub lowest: Layer,
 10    pub middle: Layer,
 11    pub highest: Layer,
 12    pub popover_shadow: Shadow,
 13    pub modal_shadow: Shadow,
 14    #[serde(deserialize_with = "deserialize_player_colors")]
 15    pub players: Vec<PlayerColors>,
 16    #[serde(deserialize_with = "deserialize_syntax_colors")]
 17    pub syntax: HashMap<String, Hsla>,
 18}
 19
 20#[derive(Deserialize, Clone, Default, Debug)]
 21pub struct Layer {
 22    pub base: StyleSet,
 23    pub variant: StyleSet,
 24    pub on: StyleSet,
 25    pub accent: StyleSet,
 26    pub positive: StyleSet,
 27    pub warning: StyleSet,
 28    pub negative: StyleSet,
 29}
 30
 31#[derive(Deserialize, Clone, Default, Debug)]
 32pub struct StyleSet {
 33    #[serde(rename = "default")]
 34    pub default: ContainerColors,
 35    pub hovered: ContainerColors,
 36    pub pressed: ContainerColors,
 37    pub active: ContainerColors,
 38    pub disabled: ContainerColors,
 39    pub inverted: ContainerColors,
 40}
 41
 42#[derive(Deserialize, Clone, Default, Debug)]
 43pub struct ContainerColors {
 44    pub background: Hsla,
 45    pub foreground: Hsla,
 46    pub border: Hsla,
 47}
 48
 49#[derive(Deserialize, Clone, Default, Debug)]
 50pub struct PlayerColors {
 51    pub selection: Hsla,
 52    pub cursor: Hsla,
 53}
 54
 55#[derive(Deserialize, Clone, Default, Debug)]
 56pub struct Shadow {
 57    pub blur: u8,
 58    pub color: Hsla,
 59    pub offset: Vec<u8>,
 60}
 61
 62fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
 63where
 64    D: Deserializer<'de>,
 65{
 66    struct PlayerArrayVisitor;
 67
 68    impl<'de> Visitor<'de> for PlayerArrayVisitor {
 69        type Value = Vec<PlayerColors>;
 70
 71        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
 72            formatter.write_str("an object with integer keys")
 73        }
 74
 75        fn visit_map<A: serde::de::MapAccess<'de>>(
 76            self,
 77            mut map: A,
 78        ) -> Result<Self::Value, A::Error> {
 79            let mut players = Vec::with_capacity(8);
 80            while let Some((key, value)) = map.next_entry::<usize, PlayerColors>()? {
 81                if key < 8 {
 82                    players.push(value);
 83                } else {
 84                    return Err(serde::de::Error::invalid_value(
 85                        serde::de::Unexpected::Unsigned(key as u64),
 86                        &"a key in range 0..7",
 87                    ));
 88                }
 89            }
 90            Ok(players)
 91        }
 92    }
 93
 94    deserializer.deserialize_map(PlayerArrayVisitor)
 95}
 96
 97fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result<HashMap<String, Hsla>, D::Error>
 98where
 99    D: serde::Deserializer<'de>,
100{
101    #[derive(Deserialize)]
102    struct ColorWrapper {
103        color: Hsla,
104    }
105
106    struct SyntaxVisitor;
107
108    impl<'de> Visitor<'de> for SyntaxVisitor {
109        type Value = HashMap<String, Hsla>;
110
111        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
112            formatter.write_str("a map with keys and objects with a single color field as values")
113        }
114
115        fn visit_map<M>(self, mut map: M) -> Result<HashMap<String, Hsla>, M::Error>
116        where
117            M: serde::de::MapAccess<'de>,
118        {
119            let mut result = HashMap::new();
120            while let Some(key) = map.next_key()? {
121                let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla
122                result.insert(key, wrapper.color);
123            }
124            Ok(result)
125        }
126    }
127    deserializer.deserialize_map(SyntaxVisitor)
128}
129
130pub fn themed<E, F>(theme: Theme, cx: &mut ViewContext<E::State>, build_child: F) -> Themed<E>
131where
132    E: Element,
133    F: FnOnce(&mut ViewContext<E::State>) -> E,
134{
135    let child = cx.with_state(theme.clone(), |cx| build_child(cx));
136    Themed { theme, child }
137}
138
139pub struct Themed<E> {
140    pub(crate) theme: Theme,
141    pub(crate) child: E,
142}
143
144impl<E: Element> Element for Themed<E> {
145    type State = E::State;
146    type FrameState = E::FrameState;
147
148    fn layout(
149        &mut self,
150        state: &mut E::State,
151        cx: &mut ViewContext<E::State>,
152    ) -> anyhow::Result<(LayoutId, Self::FrameState)>
153    where
154        Self: Sized,
155    {
156        cx.with_state(self.theme.clone(), |cx| self.child.layout(state, cx))
157    }
158
159    fn paint(
160        &mut self,
161        layout: Layout,
162        state: &mut Self::State,
163        frame_state: &mut Self::FrameState,
164        cx: &mut ViewContext<Self::State>,
165    ) -> Result<()>
166    where
167        Self: Sized,
168    {
169        cx.with_state(self.theme.clone(), |cx| {
170            self.child.paint(layout, state, frame_state, cx)
171        })
172    }
173}
174
175// fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
176//     settings::get::<ThemeSettings>(cx)
177//         .theme
178//         .deserialized_base_theme
179//         .lock()
180//         .get_or_insert_with(|| {
181//             let theme: Theme =
182//                 serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
183//                     .unwrap();
184//             Box::new(theme)
185//         })
186//         .downcast_ref::<Theme>()
187//         .unwrap()
188//         .clone()
189// }
190
191pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
192    cx.state()
193}