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}