From 2be6f9d17bfdc0687f0b227139f74bbbe7028897 Mon Sep 17 00:00:00 2001 From: Aleksei Gusev Date: Sat, 9 Aug 2025 00:17:19 +0300 Subject: [PATCH] theme: Add support for per-theme overrides (#30860) Closes #14050 Release Notes: - Added the ability to set theme-specific overrides via the `theme_overrides` setting. --------- Co-authored-by: Peter Tripp Co-authored-by: Marshall Bowers --- crates/theme/src/settings.rs | 69 +++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 6d19494f4009e92768e91dd2b7db0ba7a3880ecb..f5f1fd55475ca363c677ef623fd7a2cdde44d40c 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -4,6 +4,7 @@ use crate::{ ThemeNotFoundError, ThemeRegistry, ThemeStyleContent, }; use anyhow::Result; +use collections::HashMap; use derive_more::{Deref, DerefMut}; use gpui::{ App, Context, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels, @@ -117,7 +118,9 @@ pub struct ThemeSettings { /// Manual overrides for the active theme. /// /// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078) - pub theme_overrides: Option, + pub experimental_theme_overrides: Option, + /// Manual overrides per theme + pub theme_overrides: HashMap, /// The current icon theme selection. pub icon_theme_selection: Option, /// The active icon theme. @@ -425,7 +428,13 @@ pub struct ThemeSettingsContent { /// /// These values will override the ones on the current theme specified in `theme`. #[serde(rename = "experimental.theme_overrides", default)] - pub theme_overrides: Option, + pub experimental_theme_overrides: Option, + + /// Overrides per theme + /// + /// These values will override the ones on the specified theme + #[serde(default)] + pub theme_overrides: HashMap, } fn default_font_features() -> Option { @@ -647,30 +656,39 @@ impl ThemeSettings { /// Applies the theme overrides, if there are any, to the current theme. pub fn apply_theme_overrides(&mut self) { - if let Some(theme_overrides) = &self.theme_overrides { - let mut base_theme = (*self.active_theme).clone(); + // Apply the old overrides setting first, so that the new setting can override those. + if let Some(experimental_theme_overrides) = &self.experimental_theme_overrides { + let mut theme = (*self.active_theme).clone(); + ThemeSettings::modify_theme(&mut theme, experimental_theme_overrides); + self.active_theme = Arc::new(theme); + } - if let Some(window_background_appearance) = theme_overrides.window_background_appearance - { - base_theme.styles.window_background_appearance = - window_background_appearance.into(); - } + if let Some(theme_overrides) = self.theme_overrides.get(self.active_theme.name.as_ref()) { + let mut theme = (*self.active_theme).clone(); + ThemeSettings::modify_theme(&mut theme, theme_overrides); + self.active_theme = Arc::new(theme); + } + } - base_theme - .styles - .colors - .refine(&theme_overrides.theme_colors_refinement()); - base_theme - .styles - .status - .refine(&theme_overrides.status_colors_refinement()); - base_theme.styles.player.merge(&theme_overrides.players); - base_theme.styles.accents.merge(&theme_overrides.accents); - base_theme.styles.syntax = - SyntaxTheme::merge(base_theme.styles.syntax, theme_overrides.syntax_overrides()); - - self.active_theme = Arc::new(base_theme); + fn modify_theme(base_theme: &mut Theme, theme_overrides: &ThemeStyleContent) { + if let Some(window_background_appearance) = theme_overrides.window_background_appearance { + base_theme.styles.window_background_appearance = window_background_appearance.into(); } + + base_theme + .styles + .colors + .refine(&theme_overrides.theme_colors_refinement()); + base_theme + .styles + .status + .refine(&theme_overrides.status_colors_refinement()); + base_theme.styles.player.merge(&theme_overrides.players); + base_theme.styles.accents.merge(&theme_overrides.accents); + base_theme.styles.syntax = SyntaxTheme::merge( + base_theme.styles.syntax.clone(), + theme_overrides.syntax_overrides(), + ); } /// Switches to the icon theme with the given name, if it exists. @@ -848,7 +866,8 @@ impl settings::Settings for ThemeSettings { .get(defaults.theme.as_ref().unwrap().theme(*system_appearance)) .or(themes.get(&zed_default_dark().name)) .unwrap(), - theme_overrides: None, + experimental_theme_overrides: None, + theme_overrides: HashMap::default(), icon_theme_selection: defaults.icon_theme.clone(), active_icon_theme: defaults .icon_theme @@ -918,6 +937,8 @@ impl settings::Settings for ThemeSettings { } } + this.experimental_theme_overrides + .clone_from(&value.experimental_theme_overrides); this.theme_overrides.clone_from(&value.theme_overrides); this.apply_theme_overrides();