theme.rs

  1mod theme_registry;
  2
  3use gpui::{
  4    color::Color,
  5    elements::{ContainerStyle, ImageStyle, LabelStyle},
  6    fonts::{HighlightStyle, TextStyle},
  7    Border,
  8};
  9use serde::Deserialize;
 10use std::{collections::HashMap, sync::Arc};
 11
 12pub use theme_registry::*;
 13
 14pub const DEFAULT_THEME_NAME: &'static str = "dark";
 15
 16#[derive(Deserialize, Default)]
 17pub struct Theme {
 18    #[serde(default)]
 19    pub name: String,
 20    pub workspace: Workspace,
 21    pub chat_panel: ChatPanel,
 22    pub contacts_panel: ContactsPanel,
 23    pub project_panel: ProjectPanel,
 24    pub command_palette: CommandPalette,
 25    pub selector: Selector,
 26    pub editor: Editor,
 27    pub search: Search,
 28    pub project_diagnostics: ProjectDiagnostics,
 29    pub breadcrumbs: ContainedText,
 30}
 31
 32#[derive(Deserialize, Default)]
 33pub struct Workspace {
 34    pub background: Color,
 35    pub titlebar: Titlebar,
 36    pub tab: Tab,
 37    pub active_tab: Tab,
 38    pub pane_divider: Border,
 39    pub leader_border_opacity: f32,
 40    pub leader_border_width: f32,
 41    pub sidebar_resize_handle: ContainerStyle,
 42    pub status_bar: StatusBar,
 43    pub toolbar: Toolbar,
 44    pub disconnected_overlay: ContainedText,
 45    pub modal: ContainerStyle,
 46}
 47
 48#[derive(Clone, Deserialize, Default)]
 49pub struct Titlebar {
 50    #[serde(flatten)]
 51    pub container: ContainerStyle,
 52    pub height: f32,
 53    pub title: TextStyle,
 54    pub avatar_width: f32,
 55    pub avatar_ribbon: AvatarRibbon,
 56    pub offline_icon: OfflineIcon,
 57    pub share_icon: ShareIcon,
 58    pub hovered_share_icon: ShareIcon,
 59    pub active_share_icon: ShareIcon,
 60    pub hovered_active_share_icon: ShareIcon,
 61    pub avatar: ImageStyle,
 62    pub sign_in_prompt: ContainedText,
 63    pub hovered_sign_in_prompt: ContainedText,
 64    pub outdated_warning: ContainedText,
 65}
 66
 67#[derive(Clone, Deserialize, Default)]
 68pub struct AvatarRibbon {
 69    #[serde(flatten)]
 70    pub container: ContainerStyle,
 71    pub width: f32,
 72    pub height: f32,
 73}
 74
 75#[derive(Clone, Deserialize, Default)]
 76pub struct OfflineIcon {
 77    #[serde(flatten)]
 78    pub container: ContainerStyle,
 79    pub width: f32,
 80    pub color: Color,
 81}
 82
 83#[derive(Clone, Deserialize, Default)]
 84pub struct ShareIcon {
 85    #[serde(flatten)]
 86    pub container: ContainerStyle,
 87    pub color: Color,
 88}
 89
 90#[derive(Clone, Deserialize, Default)]
 91pub struct Tab {
 92    pub height: f32,
 93    #[serde(flatten)]
 94    pub container: ContainerStyle,
 95    #[serde(flatten)]
 96    pub label: LabelStyle,
 97    pub spacing: f32,
 98    pub icon_width: f32,
 99    pub icon_close: Color,
100    pub icon_close_active: Color,
101    pub icon_dirty: Color,
102    pub icon_conflict: Color,
103}
104
105#[derive(Clone, Deserialize, Default)]
106pub struct Toolbar {
107    #[serde(flatten)]
108    pub container: ContainerStyle,
109    pub height: f32,
110    pub item_spacing: f32,
111}
112
113#[derive(Clone, Deserialize, Default)]
114pub struct Search {
115    #[serde(flatten)]
116    pub container: ContainerStyle,
117    pub editor: FindEditor,
118    pub invalid_editor: ContainerStyle,
119    pub option_button_group: ContainerStyle,
120    pub option_button: ContainedText,
121    pub active_option_button: ContainedText,
122    pub hovered_option_button: ContainedText,
123    pub active_hovered_option_button: ContainedText,
124    pub match_background: Color,
125    pub match_index: ContainedText,
126    pub results_status: TextStyle,
127    pub tab_icon_width: f32,
128    pub tab_icon_spacing: f32,
129}
130
131#[derive(Clone, Deserialize, Default)]
132pub struct FindEditor {
133    #[serde(flatten)]
134    pub input: FieldEditor,
135    pub min_width: f32,
136    pub max_width: f32,
137}
138
139#[derive(Deserialize, Default)]
140pub struct Sidebar {
141    pub resize_handle: ContainerStyle,
142}
143
144#[derive(Clone, Copy, Deserialize, Default)]
145pub struct SidebarItem {
146    #[serde(flatten)]
147    pub container: ContainerStyle,
148    pub icon_color: Color,
149    pub icon_size: f32,
150    pub height: f32,
151}
152
153#[derive(Deserialize, Default)]
154pub struct StatusBar {
155    #[serde(flatten)]
156    pub container: ContainerStyle,
157    pub height: f32,
158    pub item_spacing: f32,
159    pub cursor_position: TextStyle,
160    pub diagnostic_message: TextStyle,
161    pub lsp_message: TextStyle,
162    pub auto_update_progress_message: TextStyle,
163    pub auto_update_done_message: TextStyle,
164    pub sidebar_item: SidebarItem,
165    pub sidebar_item_active: SidebarItem,
166    pub sidebar_item_hover: SidebarItem,
167}
168
169#[derive(Deserialize, Default)]
170pub struct ChatPanel {
171    #[serde(flatten)]
172    pub container: ContainerStyle,
173    pub message: ChatMessage,
174    pub pending_message: ChatMessage,
175    pub channel_select: ChannelSelect,
176    pub input_editor: FieldEditor,
177    pub sign_in_prompt: TextStyle,
178    pub hovered_sign_in_prompt: TextStyle,
179}
180
181#[derive(Debug, Deserialize, Default)]
182pub struct ProjectPanel {
183    #[serde(flatten)]
184    pub container: ContainerStyle,
185    pub entry: ProjectPanelEntry,
186    pub hovered_entry: ProjectPanelEntry,
187    pub selected_entry: ProjectPanelEntry,
188    pub hovered_selected_entry: ProjectPanelEntry,
189}
190
191#[derive(Debug, Deserialize, Default)]
192pub struct ProjectPanelEntry {
193    pub height: f32,
194    #[serde(flatten)]
195    pub container: ContainerStyle,
196    pub text: TextStyle,
197    pub icon_color: Color,
198    pub icon_size: f32,
199    pub icon_spacing: f32,
200}
201
202#[derive(Debug, Deserialize, Default)]
203pub struct CommandPalette {
204    pub key: ContainedLabel,
205    pub keystroke_spacing: f32,
206}
207
208#[derive(Deserialize, Default)]
209pub struct ContactsPanel {
210    #[serde(flatten)]
211    pub container: ContainerStyle,
212    pub host_row_height: f32,
213    pub host_avatar: ImageStyle,
214    pub host_username: ContainedText,
215    pub tree_branch_width: f32,
216    pub tree_branch_color: Color,
217    pub shared_project: WorktreeRow,
218    pub hovered_shared_project: WorktreeRow,
219    pub unshared_project: WorktreeRow,
220    pub hovered_unshared_project: WorktreeRow,
221}
222
223#[derive(Deserialize, Default)]
224pub struct WorktreeRow {
225    #[serde(flatten)]
226    pub container: ContainerStyle,
227    pub height: f32,
228    pub name: ContainedText,
229    pub guest_avatar: ImageStyle,
230    pub guest_avatar_spacing: f32,
231}
232
233#[derive(Deserialize, Default)]
234pub struct ChatMessage {
235    #[serde(flatten)]
236    pub container: ContainerStyle,
237    pub body: TextStyle,
238    pub sender: ContainedText,
239    pub timestamp: ContainedText,
240}
241
242#[derive(Deserialize, Default)]
243pub struct ChannelSelect {
244    #[serde(flatten)]
245    pub container: ContainerStyle,
246    pub header: ChannelName,
247    pub item: ChannelName,
248    pub active_item: ChannelName,
249    pub hovered_item: ChannelName,
250    pub hovered_active_item: ChannelName,
251    pub menu: ContainerStyle,
252}
253
254#[derive(Deserialize, Default)]
255pub struct ChannelName {
256    #[serde(flatten)]
257    pub container: ContainerStyle,
258    pub hash: ContainedText,
259    pub name: TextStyle,
260}
261
262#[derive(Deserialize, Default)]
263pub struct Selector {
264    #[serde(flatten)]
265    pub container: ContainerStyle,
266    pub empty: ContainedLabel,
267    pub input_editor: FieldEditor,
268    pub item: ContainedLabel,
269    pub active_item: ContainedLabel,
270}
271
272#[derive(Clone, Debug, Deserialize, Default)]
273pub struct ContainedText {
274    #[serde(flatten)]
275    pub container: ContainerStyle,
276    #[serde(flatten)]
277    pub text: TextStyle,
278}
279
280#[derive(Clone, Debug, Deserialize, Default)]
281pub struct ContainedLabel {
282    #[serde(flatten)]
283    pub container: ContainerStyle,
284    #[serde(flatten)]
285    pub label: LabelStyle,
286}
287
288#[derive(Clone, Deserialize, Default)]
289pub struct ProjectDiagnostics {
290    #[serde(flatten)]
291    pub container: ContainerStyle,
292    pub empty_message: TextStyle,
293    pub status_bar_item: ContainedText,
294    pub tab_icon_width: f32,
295    pub tab_icon_spacing: f32,
296    pub tab_summary_spacing: f32,
297}
298
299#[derive(Clone, Deserialize, Default)]
300pub struct Editor {
301    pub text_color: Color,
302    #[serde(default)]
303    pub background: Color,
304    pub selection: SelectionStyle,
305    pub gutter_background: Color,
306    pub gutter_padding_factor: f32,
307    pub active_line_background: Color,
308    pub highlighted_line_background: Color,
309    pub rename_fade: f32,
310    pub document_highlight_read_background: Color,
311    pub document_highlight_write_background: Color,
312    pub diff_background_deleted: Color,
313    pub diff_background_inserted: Color,
314    pub line_number: Color,
315    pub line_number_active: Color,
316    pub guest_selections: Vec<SelectionStyle>,
317    pub syntax: Arc<SyntaxTheme>,
318    pub diagnostic_path_header: DiagnosticPathHeader,
319    pub diagnostic_header: DiagnosticHeader,
320    pub error_diagnostic: DiagnosticStyle,
321    pub invalid_error_diagnostic: DiagnosticStyle,
322    pub warning_diagnostic: DiagnosticStyle,
323    pub invalid_warning_diagnostic: DiagnosticStyle,
324    pub information_diagnostic: DiagnosticStyle,
325    pub invalid_information_diagnostic: DiagnosticStyle,
326    pub hint_diagnostic: DiagnosticStyle,
327    pub invalid_hint_diagnostic: DiagnosticStyle,
328    pub autocomplete: AutocompleteStyle,
329    pub code_actions_indicator: Color,
330    pub unnecessary_code_fade: f32,
331}
332
333#[derive(Clone, Deserialize, Default)]
334pub struct DiagnosticPathHeader {
335    #[serde(flatten)]
336    pub container: ContainerStyle,
337    pub filename: ContainedText,
338    pub path: ContainedText,
339    pub text_scale_factor: f32,
340}
341
342#[derive(Clone, Deserialize, Default)]
343pub struct DiagnosticHeader {
344    #[serde(flatten)]
345    pub container: ContainerStyle,
346    pub message: ContainedLabel,
347    pub code: ContainedText,
348    pub text_scale_factor: f32,
349    pub icon_width_factor: f32,
350}
351
352#[derive(Clone, Deserialize, Default)]
353pub struct DiagnosticStyle {
354    pub message: LabelStyle,
355    #[serde(default)]
356    pub header: ContainerStyle,
357    pub text_scale_factor: f32,
358}
359
360#[derive(Clone, Deserialize, Default)]
361pub struct AutocompleteStyle {
362    #[serde(flatten)]
363    pub container: ContainerStyle,
364    pub item: ContainerStyle,
365    pub selected_item: ContainerStyle,
366    pub hovered_item: ContainerStyle,
367    pub match_highlight: HighlightStyle,
368}
369
370#[derive(Clone, Copy, Default, Deserialize)]
371pub struct SelectionStyle {
372    pub cursor: Color,
373    pub selection: Color,
374}
375
376#[derive(Clone, Deserialize, Default)]
377pub struct FieldEditor {
378    #[serde(flatten)]
379    pub container: ContainerStyle,
380    pub text: TextStyle,
381    #[serde(default)]
382    pub placeholder_text: Option<TextStyle>,
383    pub selection: SelectionStyle,
384}
385
386impl Editor {
387    pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle {
388        let style_ix = replica_id as usize % (self.guest_selections.len() + 1);
389        if style_ix == 0 {
390            &self.selection
391        } else {
392            &self.guest_selections[style_ix - 1]
393        }
394    }
395}
396
397#[derive(Default)]
398pub struct SyntaxTheme {
399    pub highlights: Vec<(String, HighlightStyle)>,
400}
401
402impl SyntaxTheme {
403    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
404        Self { highlights }
405    }
406}
407
408impl<'de> Deserialize<'de> for SyntaxTheme {
409    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
410    where
411        D: serde::Deserializer<'de>,
412    {
413        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
414
415        let mut result = Self::new(Vec::new());
416        for (key, style) in syntax_data {
417            match result
418                .highlights
419                .binary_search_by(|(needle, _)| needle.cmp(&key))
420            {
421                Ok(i) | Err(i) => {
422                    result.highlights.insert(i, (key, style));
423                }
424            }
425        }
426
427        Ok(result)
428    }
429}