1mod highlight_map;
2mod resolution;
3mod theme_registry;
4
5use crate::editor::{EditorStyle, SelectionStyle};
6use anyhow::Result;
7use gpui::{
8 color::Color,
9 elements::{ContainerStyle, ImageStyle, LabelStyle},
10 fonts::{HighlightStyle, TextStyle},
11 Border,
12};
13use serde::Deserialize;
14use std::collections::HashMap;
15
16pub use highlight_map::*;
17pub use theme_registry::*;
18
19pub const DEFAULT_THEME_NAME: &'static str = "black";
20
21#[derive(Deserialize)]
22pub struct Theme {
23 #[serde(default)]
24 pub name: String,
25 pub workspace: Workspace,
26 pub chat_panel: ChatPanel,
27 pub people_panel: PeoplePanel,
28 pub selector: Selector,
29 pub editor: EditorStyle,
30 pub syntax: SyntaxTheme,
31}
32
33pub struct SyntaxTheme {
34 highlights: Vec<(String, HighlightStyle)>,
35}
36
37#[derive(Deserialize)]
38pub struct Workspace {
39 pub background: Color,
40 pub titlebar: Titlebar,
41 pub tab: Tab,
42 pub active_tab: Tab,
43 pub pane_divider: Border,
44 pub left_sidebar: Sidebar,
45 pub right_sidebar: Sidebar,
46}
47
48#[derive(Clone, Deserialize)]
49pub struct Titlebar {
50 #[serde(flatten)]
51 pub container: ContainerStyle,
52 pub title: TextStyle,
53 pub avatar_width: f32,
54 pub offline_icon: OfflineIcon,
55 pub icon_color: Color,
56 pub avatar: ImageStyle,
57 pub outdated_warning: ContainedText,
58}
59
60#[derive(Clone, Deserialize)]
61pub struct OfflineIcon {
62 #[serde(flatten)]
63 pub container: ContainerStyle,
64 pub width: f32,
65}
66
67#[derive(Clone, Deserialize)]
68pub struct Tab {
69 #[serde(flatten)]
70 pub container: ContainerStyle,
71 #[serde(flatten)]
72 pub label: LabelStyle,
73 pub spacing: f32,
74 pub icon_width: f32,
75 pub icon_close: Color,
76 pub icon_close_active: Color,
77 pub icon_dirty: Color,
78 pub icon_conflict: Color,
79}
80
81#[derive(Deserialize)]
82pub struct Sidebar {
83 #[serde(flatten)]
84 pub container: ContainerStyle,
85 pub width: f32,
86 pub icon: SidebarIcon,
87 pub active_icon: SidebarIcon,
88 pub resize_handle: ContainerStyle,
89}
90
91#[derive(Deserialize)]
92pub struct SidebarIcon {
93 pub color: Color,
94 pub height: f32,
95}
96
97#[derive(Deserialize)]
98pub struct ChatPanel {
99 #[serde(flatten)]
100 pub container: ContainerStyle,
101 pub message: ChatMessage,
102 pub pending_message: ChatMessage,
103 pub channel_select: ChannelSelect,
104 pub input_editor: InputEditorStyle,
105 pub sign_in_prompt: TextStyle,
106 pub hovered_sign_in_prompt: TextStyle,
107}
108
109#[derive(Deserialize)]
110pub struct PeoplePanel {
111 #[serde(flatten)]
112 pub container: ContainerStyle,
113 pub host_row_height: f32,
114 pub host_avatar: ImageStyle,
115 pub host_username: ContainedText,
116 pub tree_branch_width: f32,
117 pub tree_branch_color: Color,
118 pub shared_worktree: WorktreeRow,
119 pub hovered_shared_worktree: WorktreeRow,
120 pub unshared_worktree: WorktreeRow,
121 pub hovered_unshared_worktree: WorktreeRow,
122}
123
124#[derive(Deserialize)]
125pub struct WorktreeRow {
126 #[serde(flatten)]
127 pub container: ContainerStyle,
128 pub height: f32,
129 pub name: ContainedText,
130 pub guest_avatar: ImageStyle,
131 pub guest_avatar_spacing: f32,
132}
133
134#[derive(Deserialize)]
135pub struct ChatMessage {
136 #[serde(flatten)]
137 pub container: ContainerStyle,
138 pub body: TextStyle,
139 pub sender: ContainedText,
140 pub timestamp: ContainedText,
141}
142
143#[derive(Deserialize)]
144pub struct ChannelSelect {
145 #[serde(flatten)]
146 pub container: ContainerStyle,
147 pub header: ChannelName,
148 pub item: ChannelName,
149 pub active_item: ChannelName,
150 pub hovered_item: ChannelName,
151 pub hovered_active_item: ChannelName,
152 pub menu: ContainerStyle,
153}
154
155#[derive(Deserialize)]
156pub struct ChannelName {
157 #[serde(flatten)]
158 pub container: ContainerStyle,
159 pub hash: ContainedText,
160 pub name: TextStyle,
161}
162
163#[derive(Deserialize)]
164pub struct Selector {
165 #[serde(flatten)]
166 pub container: ContainerStyle,
167 pub empty: ContainedLabel,
168 pub input_editor: InputEditorStyle,
169 pub item: ContainedLabel,
170 pub active_item: ContainedLabel,
171}
172
173#[derive(Clone, Debug, Deserialize)]
174pub struct ContainedText {
175 #[serde(flatten)]
176 pub container: ContainerStyle,
177 #[serde(flatten)]
178 pub text: TextStyle,
179}
180
181#[derive(Deserialize)]
182pub struct ContainedLabel {
183 #[serde(flatten)]
184 pub container: ContainerStyle,
185 #[serde(flatten)]
186 pub label: LabelStyle,
187}
188
189#[derive(Clone, Deserialize)]
190pub struct InputEditorStyle {
191 #[serde(flatten)]
192 pub container: ContainerStyle,
193 pub text: TextStyle,
194 #[serde(default)]
195 pub placeholder_text: Option<TextStyle>,
196 pub selection: SelectionStyle,
197}
198
199impl SyntaxTheme {
200 pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
201 Self { highlights }
202 }
203
204 pub fn highlight_style(&self, id: HighlightId) -> Option<HighlightStyle> {
205 self.highlights
206 .get(id.0 as usize)
207 .map(|entry| entry.1.clone())
208 }
209
210 #[cfg(test)]
211 pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
212 self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
213 }
214}
215
216impl InputEditorStyle {
217 pub fn as_editor(&self) -> EditorStyle {
218 EditorStyle {
219 text: self.text.clone(),
220 placeholder_text: self.placeholder_text.clone(),
221 background: self
222 .container
223 .background_color
224 .unwrap_or(Color::transparent_black()),
225 selection: self.selection,
226 gutter_background: Default::default(),
227 active_line_background: Default::default(),
228 line_number: Default::default(),
229 line_number_active: Default::default(),
230 guest_selections: Default::default(),
231 }
232 }
233}
234
235impl<'de> Deserialize<'de> for SyntaxTheme {
236 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
237 where
238 D: serde::Deserializer<'de>,
239 {
240 let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
241
242 let mut result = Self::new(Vec::new());
243 for (key, style) in syntax_data {
244 match result
245 .highlights
246 .binary_search_by(|(needle, _)| needle.cmp(&key))
247 {
248 Ok(i) | Err(i) => {
249 result.highlights.insert(i, (key, style));
250 }
251 }
252 }
253
254 Ok(result)
255 }
256}