theme.rs

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