Skip to main content

gpui_component/theme/
schema.rs

1use std::{rc::Rc, sync::Arc};
2
3use anyhow::Result;
4use gpui::{Hsla, SharedString, px};
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::{
9    Colorize, Theme, ThemeColor, ThemeMode,
10    highlighter::{HighlightTheme, HighlightThemeStyle},
11};
12
13/// Represents a theme configuration.
14#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
15#[serde(default)]
16pub struct ThemeSet {
17    /// The name of the theme set.
18    pub name: SharedString,
19    /// The author of the theme.
20    pub author: Option<SharedString>,
21    /// The URL of the theme.
22    pub url: Option<SharedString>,
23    /// The theme list of the theme set.
24    #[serde(rename = "themes")]
25    pub themes: Vec<ThemeConfig>,
26}
27
28#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
29#[serde(default)]
30pub struct ThemeConfig {
31    /// Whether this theme is the default theme.
32    pub is_default: bool,
33    /// The name of the theme.
34    pub name: SharedString,
35    /// The mode of the theme, default is light.
36    pub mode: ThemeMode,
37
38    /// The base font size, default is 16.
39    #[serde(rename = "font.size")]
40    pub font_size: Option<f32>,
41    /// The base font family, default is system font: `.SystemUIFont`.
42    #[serde(rename = "font.family")]
43    pub font_family: Option<SharedString>,
44    /// The monospace font family, default is platform specific:
45    /// - macOS: `Menlo`
46    /// - Windows: `Consolas`
47    /// - Linux: `DejaVu Sans Mono`
48    #[serde(rename = "mono_font.family")]
49    pub mono_font_family: Option<SharedString>,
50    /// The monospace font size, default is 13.
51    #[serde(rename = "mono_font.size")]
52    pub mono_font_size: Option<f32>,
53
54    /// The border radius for general elements, default is 6.
55    #[serde(rename = "radius")]
56    pub radius: Option<usize>,
57    /// The border radius for large elements like Dialogs and Notifications, default is 8.
58    #[serde(rename = "radius.lg")]
59    pub radius_lg: Option<usize>,
60    /// Set shadows in the theme, for example the Input and Button, default is true.
61    #[serde(rename = "shadow")]
62    pub shadow: Option<bool>,
63
64    /// The colors of the theme.
65    pub colors: ThemeConfigColors,
66    /// The highlight theme, this part is combilbility with `style` section in Zed theme.
67    ///
68    /// https://github.com/zed-industries/zed/blob/f50041779dcfd7a76c8aec293361c60c53f02d51/assets/themes/ayu/ayu.json#L9
69    pub highlight: Option<HighlightThemeStyle>,
70}
71
72#[derive(Debug, Default, Clone, JsonSchema, Serialize, Deserialize)]
73pub struct ThemeConfigColors {
74    /// Used for accents such as hover background on MenuItem, ListItem, etc.
75    #[serde(rename = "accent.background")]
76    pub accent: Option<SharedString>,
77    /// Used for accent text color.
78    #[serde(rename = "accent.foreground")]
79    pub accent_foreground: Option<SharedString>,
80    /// Accordion background color.
81    #[serde(rename = "accordion.background")]
82    pub accordion: Option<SharedString>,
83    /// Accordion hover background color.
84    #[serde(rename = "accordion.hover.background")]
85    pub accordion_hover: Option<SharedString>,
86    /// Default background color.
87    #[serde(rename = "background")]
88    pub background: Option<SharedString>,
89    /// Default border color
90    #[serde(rename = "border")]
91    pub border: Option<SharedString>,
92    /// Background color for GroupBox.
93    #[serde(rename = "group_box.background")]
94    pub group_box: Option<SharedString>,
95    /// Text color for GroupBox.
96    #[serde(rename = "group_box.foreground")]
97    pub group_box_foreground: Option<SharedString>,
98    /// Title text color for GroupBox.
99    #[serde(rename = "group_box.title.foreground")]
100    pub group_box_title_foreground: Option<SharedString>,
101    /// Input caret color (Blinking cursor).
102    #[serde(rename = "caret")]
103    pub caret: Option<SharedString>,
104    /// Chart 1 color.
105    #[serde(rename = "chart.1")]
106    pub chart_1: Option<SharedString>,
107    /// Chart 2 color.
108    #[serde(rename = "chart.2")]
109    pub chart_2: Option<SharedString>,
110    /// Chart 3 color.
111    #[serde(rename = "chart.3")]
112    pub chart_3: Option<SharedString>,
113    /// Chart 4 color.
114    #[serde(rename = "chart.4")]
115    pub chart_4: Option<SharedString>,
116    /// Chart 5 color.
117    #[serde(rename = "chart.5")]
118    pub chart_5: Option<SharedString>,
119    /// Danger background color.
120    #[serde(rename = "danger.background")]
121    pub danger: Option<SharedString>,
122    /// Danger active background color.
123    #[serde(rename = "danger.active.background")]
124    pub danger_active: Option<SharedString>,
125    /// Danger text color.
126    #[serde(rename = "danger.foreground")]
127    pub danger_foreground: Option<SharedString>,
128    /// Danger hover background color.
129    #[serde(rename = "danger.hover.background")]
130    pub danger_hover: Option<SharedString>,
131    /// Description List label background color.
132    #[serde(rename = "description_list.label.background")]
133    pub description_list_label: Option<SharedString>,
134    /// Description List label foreground color.
135    #[serde(rename = "description_list.label.foreground")]
136    pub description_list_label_foreground: Option<SharedString>,
137    /// Drag border color.
138    #[serde(rename = "drag.border")]
139    pub drag_border: Option<SharedString>,
140    /// Drop target background color.
141    #[serde(rename = "drop_target.background")]
142    pub drop_target: Option<SharedString>,
143    /// Default text color.
144    #[serde(rename = "foreground")]
145    pub foreground: Option<SharedString>,
146    /// Info background color.
147    #[serde(rename = "info.background")]
148    pub info: Option<SharedString>,
149    /// Info active background color.
150    #[serde(rename = "info.active.background")]
151    pub info_active: Option<SharedString>,
152    /// Info text color.
153    #[serde(rename = "info.foreground")]
154    pub info_foreground: Option<SharedString>,
155    /// Info hover background color.
156    #[serde(rename = "info.hover.background")]
157    pub info_hover: Option<SharedString>,
158    /// Border color for inputs such as Input, Select, etc.
159    #[serde(rename = "input.border")]
160    pub input: Option<SharedString>,
161    /// Link text color.
162    #[serde(rename = "link")]
163    pub link: Option<SharedString>,
164    /// Active link text color.
165    #[serde(rename = "link.active")]
166    pub link_active: Option<SharedString>,
167    /// Hover link text color.
168    #[serde(rename = "link.hover")]
169    pub link_hover: Option<SharedString>,
170    /// Background color for List and ListItem.
171    #[serde(rename = "list.background")]
172    pub list: Option<SharedString>,
173    /// Background color for active ListItem.
174    #[serde(rename = "list.active.background")]
175    pub list_active: Option<SharedString>,
176    /// Border color for active ListItem.
177    #[serde(rename = "list.active.border")]
178    pub list_active_border: Option<SharedString>,
179    /// Stripe background color for even ListItem.
180    #[serde(rename = "list.even.background")]
181    pub list_even: Option<SharedString>,
182    /// Background color for List header.
183    #[serde(rename = "list.head.background")]
184    pub list_head: Option<SharedString>,
185    /// Hover background color for ListItem.
186    #[serde(rename = "list.hover.background")]
187    pub list_hover: Option<SharedString>,
188    /// Muted backgrounds such as Skeleton and Switch.
189    #[serde(rename = "muted.background")]
190    pub muted: Option<SharedString>,
191    /// Muted text color, as used in disabled text.
192    #[serde(rename = "muted.foreground")]
193    pub muted_foreground: Option<SharedString>,
194    /// Background color for Popover.
195    #[serde(rename = "popover.background")]
196    pub popover: Option<SharedString>,
197    /// Text color for Popover.
198    #[serde(rename = "popover.foreground")]
199    pub popover_foreground: Option<SharedString>,
200    /// Primary background color.
201    #[serde(rename = "primary.background")]
202    pub primary: Option<SharedString>,
203    /// Active primary background color.
204    #[serde(rename = "primary.active.background")]
205    pub primary_active: Option<SharedString>,
206    /// Primary text color.
207    #[serde(rename = "primary.foreground")]
208    pub primary_foreground: Option<SharedString>,
209    /// Hover primary background color.
210    #[serde(rename = "primary.hover.background")]
211    pub primary_hover: Option<SharedString>,
212    /// Progress bar background color.
213    #[serde(rename = "progress.bar.background")]
214    pub progress_bar: Option<SharedString>,
215    /// Used for focus ring.
216    #[serde(rename = "ring")]
217    pub ring: Option<SharedString>,
218    /// Scrollbar background color.
219    #[serde(rename = "scrollbar.background")]
220    pub scrollbar: Option<SharedString>,
221    /// Scrollbar thumb background color.
222    #[serde(rename = "scrollbar.thumb.background")]
223    pub scrollbar_thumb: Option<SharedString>,
224    /// Scrollbar thumb hover background color.
225    #[serde(rename = "scrollbar.thumb.hover.background")]
226    pub scrollbar_thumb_hover: Option<SharedString>,
227    /// Secondary background color.
228    #[serde(rename = "secondary.background")]
229    pub secondary: Option<SharedString>,
230    /// Active secondary background color.
231    #[serde(rename = "secondary.active.background")]
232    pub secondary_active: Option<SharedString>,
233    /// Secondary text color, used for secondary Button text color or secondary text.
234    #[serde(rename = "secondary.foreground")]
235    pub secondary_foreground: Option<SharedString>,
236    /// Hover secondary background color.
237    #[serde(rename = "secondary.hover.background")]
238    pub secondary_hover: Option<SharedString>,
239    /// Input selection background color.
240    #[serde(rename = "selection.background")]
241    pub selection: Option<SharedString>,
242    /// Sidebar background color.
243    #[serde(rename = "sidebar.background")]
244    pub sidebar: Option<SharedString>,
245    /// Sidebar accent background color.
246    #[serde(rename = "sidebar.accent.background")]
247    pub sidebar_accent: Option<SharedString>,
248    /// Sidebar accent text color.
249    #[serde(rename = "sidebar.accent.foreground")]
250    pub sidebar_accent_foreground: Option<SharedString>,
251    /// Sidebar border color.
252    #[serde(rename = "sidebar.border")]
253    pub sidebar_border: Option<SharedString>,
254    /// Sidebar text color.
255    #[serde(rename = "sidebar.foreground")]
256    pub sidebar_foreground: Option<SharedString>,
257    /// Sidebar primary background color.
258    #[serde(rename = "sidebar.primary.background")]
259    pub sidebar_primary: Option<SharedString>,
260    /// Sidebar primary text color.
261    #[serde(rename = "sidebar.primary.foreground")]
262    pub sidebar_primary_foreground: Option<SharedString>,
263    /// Skeleton background color.
264    #[serde(rename = "skeleton.background")]
265    pub skeleton: Option<SharedString>,
266    /// Slider bar background color.
267    #[serde(rename = "slider.background")]
268    pub slider_bar: Option<SharedString>,
269    /// Slider thumb background color.
270    #[serde(rename = "slider.thumb.background")]
271    pub slider_thumb: Option<SharedString>,
272    /// Success background color.
273    #[serde(rename = "success.background")]
274    pub success: Option<SharedString>,
275    /// Success text color.
276    #[serde(rename = "success.foreground")]
277    pub success_foreground: Option<SharedString>,
278    /// Success hover background color.
279    #[serde(rename = "success.hover.background")]
280    pub success_hover: Option<SharedString>,
281    /// Success active background color.
282    #[serde(rename = "success.active.background")]
283    pub success_active: Option<SharedString>,
284    /// Bullish color for candlestick charts (upward price movement).
285    #[serde(rename = "bullish.background")]
286    pub bullish: Option<SharedString>,
287    /// Bearish color for candlestick charts (downward price movement).
288    #[serde(rename = "bearish.background")]
289    pub bearish: Option<SharedString>,
290    /// Switch background color.
291    #[serde(rename = "switch.background")]
292    pub switch: Option<SharedString>,
293    /// Switch thumb background color.
294    #[serde(rename = "switch.thumb.background")]
295    pub switch_thumb: Option<SharedString>,
296    /// Tab background color.
297    #[serde(rename = "tab.background")]
298    pub tab: Option<SharedString>,
299    /// Tab active background color.
300    #[serde(rename = "tab.active.background")]
301    pub tab_active: Option<SharedString>,
302    /// Tab active text color.
303    #[serde(rename = "tab.active.foreground")]
304    pub tab_active_foreground: Option<SharedString>,
305    /// TabBar background color.
306    #[serde(rename = "tab_bar.background")]
307    pub tab_bar: Option<SharedString>,
308    /// TabBar segmented background color.
309    #[serde(rename = "tab_bar.segmented.background")]
310    pub tab_bar_segmented: Option<SharedString>,
311    /// Tab text color.
312    #[serde(rename = "tab.foreground")]
313    pub tab_foreground: Option<SharedString>,
314    /// Table background color.
315    #[serde(rename = "table.background")]
316    pub table: Option<SharedString>,
317    /// Table active item background color.
318    #[serde(rename = "table.active.background")]
319    pub table_active: Option<SharedString>,
320    /// Table active item border color.
321    #[serde(rename = "table.active.border")]
322    pub table_active_border: Option<SharedString>,
323    /// Stripe background color for even TableRow.
324    #[serde(rename = "table.even.background")]
325    pub table_even: Option<SharedString>,
326    /// Table head background color.
327    #[serde(rename = "table.head.background")]
328    pub table_head: Option<SharedString>,
329    /// Table head text color.
330    #[serde(rename = "table.head.foreground")]
331    pub table_head_foreground: Option<SharedString>,
332    /// Table item hover background color.
333    #[serde(rename = "table.hover.background")]
334    pub table_hover: Option<SharedString>,
335    /// Table row border color.
336    #[serde(rename = "table.row.border")]
337    pub table_row_border: Option<SharedString>,
338    /// TitleBar background color, use for Window title bar.
339    #[serde(rename = "title_bar.background")]
340    pub title_bar: Option<SharedString>,
341    /// TitleBar border color.
342    #[serde(rename = "title_bar.border")]
343    pub title_bar_border: Option<SharedString>,
344    /// Background color for Tiles.
345    #[serde(rename = "tiles.background")]
346    pub tiles: Option<SharedString>,
347    /// Warning background color.
348    #[serde(rename = "warning.background")]
349    pub warning: Option<SharedString>,
350    /// Warning active background color.
351    #[serde(rename = "warning.active.background")]
352    pub warning_active: Option<SharedString>,
353    /// Warning hover background color.
354    #[serde(rename = "warning.hover.background")]
355    pub warning_hover: Option<SharedString>,
356    /// Warning foreground color.
357    #[serde(rename = "warning.foreground")]
358    pub warning_foreground: Option<SharedString>,
359    /// Overlay background color.
360    #[serde(rename = "overlay")]
361    pub overlay: Option<SharedString>,
362    /// Window border color.
363    ///
364    /// # Platform specific:
365    ///
366    /// This is only works on Linux, other platforms we can't change the window border color.
367    #[serde(rename = "window.border")]
368    pub window_border: Option<SharedString>,
369
370    /// Base blue color.
371    #[serde(rename = "base.blue")]
372    blue: Option<String>,
373    /// Base light blue color.
374    #[serde(rename = "base.blue.light")]
375    blue_light: Option<String>,
376    /// Base cyan color.
377    #[serde(rename = "base.cyan")]
378    cyan: Option<String>,
379    /// Base light cyan color.
380    #[serde(rename = "base.cyan.light")]
381    cyan_light: Option<String>,
382    /// Base green color.
383    #[serde(rename = "base.green")]
384    green: Option<String>,
385    /// Base light green color.
386    #[serde(rename = "base.green.light")]
387    green_light: Option<String>,
388    /// Base magenta color.
389    #[serde(rename = "base.magenta")]
390    magenta: Option<String>,
391    #[serde(rename = "base.magenta.light")]
392    magenta_light: Option<String>,
393    /// Base red color.
394    #[serde(rename = "base.red")]
395    red: Option<String>,
396    /// Base light red color.
397    #[serde(rename = "base.red.light")]
398    red_light: Option<String>,
399    /// Base yellow color.
400    #[serde(rename = "base.yellow")]
401    yellow: Option<String>,
402    /// Base light yellow color.
403    #[serde(rename = "base.yellow.light")]
404    yellow_light: Option<String>,
405}
406
407/// Try to parse HEX color, `#RRGGBB` or `#RRGGBBAA`
408fn try_parse_color(color: &str) -> Result<Hsla> {
409    let rgba = gpui::Rgba::try_from(color)?;
410    Ok(rgba.into())
411}
412
413impl ThemeColor {
414    /// Create a new `ThemeColor` from a `ThemeConfig`.
415    pub(crate) fn apply_config(&mut self, config: &ThemeConfig, default_theme: &ThemeColor) {
416        let colors = config.colors.clone();
417
418        macro_rules! apply_color {
419            ($config_field:ident) => {
420                if let Some(value) = colors.$config_field {
421                    if let Ok(color) = try_parse_color(&value) {
422                        self.$config_field = color;
423                    } else {
424                        self.$config_field = default_theme.$config_field;
425                    }
426                } else {
427                    self.$config_field = default_theme.$config_field;
428                }
429            };
430            // With fallback
431            ($config_field:ident, fallback = $fallback:expr) => {
432                if let Some(value) = colors.$config_field {
433                    if let Ok(color) = try_parse_color(&value) {
434                        self.$config_field = color;
435                    }
436                } else {
437                    self.$config_field = $fallback;
438                }
439            };
440        }
441
442        apply_color!(background);
443
444        // Base colors for fallback
445        apply_color!(red);
446        apply_color!(
447            red_light,
448            fallback = self.background.blend(self.red.opacity(0.8))
449        );
450        apply_color!(green);
451        apply_color!(
452            green_light,
453            fallback = self.background.blend(self.green.opacity(0.8))
454        );
455        apply_color!(blue);
456        apply_color!(
457            blue_light,
458            fallback = self.background.blend(self.blue.opacity(0.8))
459        );
460        apply_color!(magenta);
461        apply_color!(
462            magenta_light,
463            fallback = self.background.blend(self.magenta.opacity(0.8))
464        );
465        apply_color!(yellow);
466        apply_color!(
467            yellow_light,
468            fallback = self.background.blend(self.yellow.opacity(0.8))
469        );
470        apply_color!(cyan);
471        apply_color!(
472            cyan_light,
473            fallback = self.background.blend(self.cyan.opacity(0.8))
474        );
475
476        apply_color!(border);
477        apply_color!(foreground);
478        apply_color!(muted);
479        apply_color!(
480            muted_foreground,
481            fallback = self.muted.blend(self.foreground.opacity(0.7))
482        );
483
484        // Button colors
485        let active_darken = if config.mode.is_dark() { 0.2 } else { 0.1 };
486        let hover_opacity = 0.9;
487        apply_color!(primary);
488        apply_color!(primary_foreground, fallback = self.foreground);
489        apply_color!(
490            primary_hover,
491            fallback = self.background.blend(self.primary.opacity(hover_opacity))
492        );
493        apply_color!(
494            primary_active,
495            fallback = self.primary.darken(active_darken)
496        );
497        apply_color!(secondary);
498        apply_color!(secondary_foreground, fallback = self.foreground);
499        apply_color!(
500            secondary_hover,
501            fallback = self.background.blend(self.secondary.opacity(hover_opacity))
502        );
503        apply_color!(
504            secondary_active,
505            fallback = self.secondary.darken(active_darken)
506        );
507        apply_color!(success, fallback = self.green);
508        apply_color!(success_foreground, fallback = self.primary_foreground);
509        apply_color!(
510            success_hover,
511            fallback = self.background.blend(self.success.opacity(hover_opacity))
512        );
513        apply_color!(
514            success_active,
515            fallback = self.success.darken(active_darken)
516        );
517        apply_color!(bullish, fallback = self.green);
518        apply_color!(bearish, fallback = self.red);
519        apply_color!(info, fallback = self.cyan);
520        apply_color!(info_foreground, fallback = self.primary_foreground);
521        apply_color!(
522            info_hover,
523            fallback = self.background.blend(self.info.opacity(hover_opacity))
524        );
525        apply_color!(info_active, fallback = self.info.darken(active_darken));
526        apply_color!(warning, fallback = self.yellow);
527        apply_color!(warning_foreground, fallback = self.primary_foreground);
528        apply_color!(
529            warning_hover,
530            fallback = self.background.blend(self.warning.opacity(0.9))
531        );
532        apply_color!(
533            warning_active,
534            fallback = self.background.blend(self.warning.darken(active_darken))
535        );
536
537        // Other colors
538        apply_color!(accent, fallback = self.secondary);
539        apply_color!(accent_foreground, fallback = self.foreground);
540        apply_color!(accordion, fallback = self.background);
541        apply_color!(accordion_hover, fallback = self.accent.opacity(0.8));
542        apply_color!(
543            group_box,
544            fallback = self
545                .background
546                .blend(
547                    self.secondary
548                        .opacity(if config.mode.is_dark() { 0.3 } else { 0.4 })
549                )
550        );
551        apply_color!(group_box_foreground, fallback = self.foreground);
552        apply_color!(caret, fallback = self.primary);
553        apply_color!(chart_1, fallback = self.blue.lighten(0.4));
554        apply_color!(chart_2, fallback = self.blue.lighten(0.2));
555        apply_color!(chart_3, fallback = self.blue);
556        apply_color!(chart_4, fallback = self.blue.darken(0.2));
557        apply_color!(chart_5, fallback = self.blue.darken(0.4));
558        apply_color!(danger, fallback = self.red);
559        apply_color!(danger_active, fallback = self.danger.darken(active_darken));
560        apply_color!(danger_foreground, fallback = self.primary_foreground);
561        apply_color!(
562            danger_hover,
563            fallback = self.background.blend(self.danger.opacity(0.9))
564        );
565        apply_color!(
566            description_list_label,
567            fallback = self.background.blend(self.border.opacity(0.2))
568        );
569        apply_color!(
570            description_list_label_foreground,
571            fallback = self.muted_foreground
572        );
573        apply_color!(drag_border, fallback = self.primary.opacity(0.65));
574        apply_color!(drop_target, fallback = self.primary.opacity(0.2));
575        apply_color!(input, fallback = self.border);
576        apply_color!(link, fallback = self.primary);
577        apply_color!(link_active, fallback = self.link);
578        apply_color!(link_hover, fallback = self.link);
579        apply_color!(list, fallback = self.background);
580        apply_color!(
581            list_active,
582            fallback = self.background.blend(self.primary.opacity(0.1))
583        );
584        apply_color!(
585            list_active_border,
586            fallback = self.background.blend(self.primary.opacity(0.6))
587        );
588        apply_color!(list_even, fallback = self.list);
589        apply_color!(list_head, fallback = self.list);
590        apply_color!(list_hover, fallback = self.secondary_hover);
591        apply_color!(popover, fallback = self.background);
592        apply_color!(popover_foreground, fallback = self.foreground);
593        apply_color!(progress_bar, fallback = self.primary);
594        apply_color!(ring, fallback = self.blue);
595        apply_color!(scrollbar, fallback = self.background);
596        apply_color!(scrollbar_thumb, fallback = self.accent);
597        apply_color!(scrollbar_thumb_hover, fallback = self.scrollbar_thumb);
598        apply_color!(selection, fallback = self.primary);
599        apply_color!(sidebar, fallback = self.background);
600        apply_color!(sidebar_accent, fallback = self.accent);
601        apply_color!(sidebar_accent_foreground, fallback = self.accent_foreground);
602        apply_color!(sidebar_border, fallback = self.border);
603        apply_color!(sidebar_foreground, fallback = self.foreground);
604        apply_color!(sidebar_primary, fallback = self.primary);
605        apply_color!(
606            sidebar_primary_foreground,
607            fallback = self.primary_foreground
608        );
609        apply_color!(skeleton, fallback = self.secondary);
610        apply_color!(slider_bar, fallback = self.primary);
611        apply_color!(slider_thumb, fallback = self.primary_foreground);
612        apply_color!(switch, fallback = self.secondary);
613        apply_color!(switch_thumb, fallback = self.background);
614        apply_color!(tab, fallback = self.background);
615        apply_color!(tab_active, fallback = self.background);
616        apply_color!(tab_active_foreground, fallback = self.foreground);
617        apply_color!(tab_bar, fallback = self.background);
618        apply_color!(tab_bar_segmented, fallback = self.secondary);
619        apply_color!(tab_foreground, fallback = self.foreground);
620        apply_color!(table, fallback = self.list);
621        apply_color!(table_active, fallback = self.list_active);
622        apply_color!(table_active_border, fallback = self.list_active_border);
623        apply_color!(table_even, fallback = self.list_even);
624        apply_color!(table_head, fallback = self.list_head);
625        apply_color!(table_head_foreground, fallback = self.muted_foreground);
626        apply_color!(table_hover, fallback = self.list_hover);
627        apply_color!(table_row_border, fallback = self.border);
628        apply_color!(title_bar, fallback = self.background);
629        apply_color!(title_bar_border, fallback = self.border);
630        apply_color!(tiles, fallback = self.background);
631        apply_color!(overlay);
632        apply_color!(window_border, fallback = self.border);
633
634        // TODO: Apply default fallback colors to highlight.
635
636        // Ensure opacity for list_active, table_active
637        self.list_active = self.list_active.alpha(self.list_active.a.min(0.2));
638        self.table_active = self.table_active.alpha(self.table_active.a.min(0.2));
639        self.selection = self.selection.alpha(self.selection.a.min(0.3));
640    }
641}
642
643impl Theme {
644    /// Apply the given theme configuration to the current theme.
645    pub fn apply_config(&mut self, config: &Rc<ThemeConfig>) {
646        if config.mode.is_dark() {
647            self.dark_theme = config.clone();
648        } else {
649            self.light_theme = config.clone();
650        }
651        if let Some(style) = &config.highlight {
652            let highlight_theme = Arc::new(HighlightTheme {
653                name: config.name.to_string(),
654                appearance: config.mode,
655                style: style.clone(),
656            });
657            self.highlight_theme = highlight_theme.clone();
658        }
659
660        let default_theme = if config.mode.is_dark() {
661            Self::from(ThemeColor::dark().as_ref())
662        } else {
663            Self::from(ThemeColor::light().as_ref())
664        };
665
666        if let Some(font_size) = config.font_size {
667            self.font_size = px(font_size);
668        } else {
669            self.font_size = default_theme.font_size;
670        }
671        if let Some(font_family) = &config.font_family {
672            self.font_family = font_family.clone();
673        } else {
674            self.font_family = default_theme.font_family.clone();
675        }
676        if let Some(mono_font_family) = &config.mono_font_family {
677            self.mono_font_family = mono_font_family.clone();
678        } else {
679            self.mono_font_family = default_theme.mono_font_family.clone();
680        }
681        if let Some(mono_font_size) = config.mono_font_size {
682            self.mono_font_size = px(mono_font_size);
683        } else {
684            self.mono_font_size = default_theme.mono_font_size;
685        }
686        if let Some(radius) = config.radius {
687            self.radius = px(radius as f32);
688        } else {
689            self.radius = default_theme.radius;
690        }
691        if let Some(radius_lg) = config.radius_lg {
692            self.radius_lg = px(radius_lg as f32);
693        } else {
694            self.radius_lg = default_theme.radius_lg;
695        }
696        if let Some(shadow) = config.shadow {
697            self.shadow = shadow;
698        } else {
699            self.shadow = default_theme.shadow;
700        }
701
702        self.colors.apply_config(&config, &default_theme.colors);
703        self.mode = config.mode;
704    }
705}
706
707#[cfg(test)]
708mod tests {
709    use super::try_parse_color;
710    use gpui::hsla;
711
712    #[test]
713    fn test_try_parse_color() {
714        assert_eq!(
715            try_parse_color("#F2F200").ok(),
716            Some(hsla(0.16666667, 1., 0.4745098, 1.0))
717        );
718        assert_eq!(
719            try_parse_color("#00f21888").ok(),
720            Some(hsla(0.34986225, 1.0, 0.4745098, 0.53333336))
721        );
722    }
723}