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