1use gpui3::{Element, Hsla, Layout, LayoutId, 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 struct Themed<E> {
131 pub(crate) theme: Theme,
132 pub(crate) child: E,
133}
134
135impl<E: Element> Element for Themed<E> {
136 type State = E::State;
137 type FrameState = E::FrameState;
138
139 fn layout(
140 &mut self,
141 state: &mut E::State,
142 cx: &mut ViewContext<E::State>,
143 ) -> anyhow::Result<(LayoutId, Self::FrameState)>
144 where
145 Self: Sized,
146 {
147 // cx.push_theme(self.theme.clone());
148 let result = self.child.layout(state, cx);
149 // cx.pop_theme();
150 result
151 }
152
153 fn paint(
154 &mut self,
155 layout: Layout,
156 state: &mut Self::State,
157 frame_state: &mut Self::FrameState,
158 cx: &mut ViewContext<Self::State>,
159 ) where
160 Self: Sized,
161 {
162 // todo!
163 // cx.push_theme(self.theme.clone());
164 self.child.paint(layout, state, frame_state, cx);
165 // cx.pop_theme();
166 }
167}
168
169// fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
170// settings::get::<ThemeSettings>(cx)
171// .theme
172// .deserialized_base_theme
173// .lock()
174// .get_or_insert_with(|| {
175// let theme: Theme =
176// serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
177// .unwrap();
178// Box::new(theme)
179// })
180// .downcast_ref::<Theme>()
181// .unwrap()
182// .clone()
183// }
184
185pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
186 todo!()
187 // cx.theme::<Theme>()
188}