From a5f52f0f04b06b9b09aa4b97aac6aaae19e0440c Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 29 Oct 2024 22:30:58 -0400 Subject: [PATCH] Use theme families to refine user themes (#19936) This PR changes the way we load user themes into the ThemeRegistry. Rather than directly pass a theme family's themes to `insert_user_themes`, instead we use the new `refine_theme_family ` and `ThemeFamily::refine_theme`. This PR should have net zero change to themes today, but sets up enabling theme variables. We need to do it this way so each theme has access to it's family when it is refined. Release Notes: - N/A --- crates/theme/src/registry.rs | 92 ++-------------------------- crates/theme/src/theme.rs | 112 ++++++++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 90 deletions(-) diff --git a/crates/theme/src/registry.rs b/crates/theme/src/registry.rs index 9f95d19937..73e8fe8c66 100644 --- a/crates/theme/src/registry.rs +++ b/crates/theme/src/registry.rs @@ -6,16 +6,11 @@ use collections::HashMap; use derive_more::{Deref, DerefMut}; use fs::Fs; use futures::StreamExt; -use gpui::{AppContext, AssetSource, Global, HighlightStyle, SharedString}; +use gpui::{AppContext, AssetSource, Global, SharedString}; use parking_lot::RwLock; -use refineable::Refineable; use util::ResultExt; -use crate::{ - try_parse_color, AccentColors, Appearance, AppearanceContent, PlayerColors, StatusColors, - SyntaxTheme, SystemColors, Theme, ThemeColors, ThemeContent, ThemeFamily, ThemeFamilyContent, - ThemeStyles, -}; +use crate::{refine_theme_family, Appearance, Theme, ThemeFamily, ThemeFamilyContent}; /// The metadata for a theme. #[derive(Debug, Clone)] @@ -97,89 +92,12 @@ impl ThemeRegistry { #[allow(unused)] fn insert_user_theme_families(&self, families: impl IntoIterator) { for family in families.into_iter() { - self.insert_user_themes(family.themes); + let refined_family = refine_theme_family(family); + + self.insert_themes(refined_family.themes); } } - /// Inserts user themes into the registry. - pub fn insert_user_themes(&self, themes: impl IntoIterator) { - self.insert_themes(themes.into_iter().map(|user_theme| { - let mut theme_colors = match user_theme.appearance { - AppearanceContent::Light => ThemeColors::light(), - AppearanceContent::Dark => ThemeColors::dark(), - }; - theme_colors.refine(&user_theme.style.theme_colors_refinement()); - - let mut status_colors = match user_theme.appearance { - AppearanceContent::Light => StatusColors::light(), - AppearanceContent::Dark => StatusColors::dark(), - }; - status_colors.refine(&user_theme.style.status_colors_refinement()); - - let mut player_colors = match user_theme.appearance { - AppearanceContent::Light => PlayerColors::light(), - AppearanceContent::Dark => PlayerColors::dark(), - }; - player_colors.merge(&user_theme.style.players); - - let mut accent_colors = match user_theme.appearance { - AppearanceContent::Light => AccentColors::light(), - AppearanceContent::Dark => AccentColors::dark(), - }; - accent_colors.merge(&user_theme.style.accents); - - let syntax_highlights = user_theme - .style - .syntax - .iter() - .map(|(syntax_token, highlight)| { - ( - syntax_token.clone(), - HighlightStyle { - color: highlight - .color - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - background_color: highlight - .background_color - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - font_style: highlight.font_style.map(Into::into), - font_weight: highlight.font_weight.map(Into::into), - ..Default::default() - }, - ) - }) - .collect::>(); - let syntax_theme = - SyntaxTheme::merge(Arc::new(SyntaxTheme::default()), syntax_highlights); - - let window_background_appearance = user_theme - .style - .window_background_appearance - .map(Into::into) - .unwrap_or_default(); - - Theme { - id: uuid::Uuid::new_v4().to_string(), - name: user_theme.name.into(), - appearance: match user_theme.appearance { - AppearanceContent::Light => Appearance::Light, - AppearanceContent::Dark => Appearance::Dark, - }, - styles: ThemeStyles { - system: SystemColors::default(), - window_background_appearance, - accents: accent_colors, - colors: theme_colors, - status: status_colors, - player: player_colors, - syntax: syntax_theme, - }, - } - })); - } - /// Removes the themes with the given names from the registry. pub fn remove_user_themes(&self, themes_to_remove: &[SharedString]) { self.state diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index c62359242d..307ea6b287 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -29,10 +29,11 @@ pub use settings::*; pub use styles::*; use gpui::{ - px, AppContext, AssetSource, Hsla, Pixels, SharedString, WindowAppearance, - WindowBackgroundAppearance, + px, AppContext, AssetSource, HighlightStyle, Hsla, Pixels, Refineable, SharedString, + WindowAppearance, WindowBackgroundAppearance, }; use serde::Deserialize; +use uuid::Uuid; /// Defines window border radius for platforms that use client side decorations. pub const CLIENT_SIDE_DECORATION_ROUNDING: Pixels = px(10.0); @@ -137,7 +138,112 @@ pub struct ThemeFamily { pub scales: ColorScales, } -impl ThemeFamily {} +impl ThemeFamily { + // This is on ThemeFamily because we will have variables here we will need + // in the future to resolve @references. + /// Refines ThemeContent into a theme, merging it's contents with the base theme. + pub fn refine_theme(&self, theme: &ThemeContent) -> Theme { + let appearance = match theme.appearance { + AppearanceContent::Light => Appearance::Light, + AppearanceContent::Dark => Appearance::Dark, + }; + + let mut refined_theme_colors = match theme.appearance { + AppearanceContent::Light => ThemeColors::light(), + AppearanceContent::Dark => ThemeColors::dark(), + }; + refined_theme_colors.refine(&theme.style.theme_colors_refinement()); + + let mut refined_status_colors = match theme.appearance { + AppearanceContent::Light => StatusColors::light(), + AppearanceContent::Dark => StatusColors::dark(), + }; + refined_status_colors.refine(&theme.style.status_colors_refinement()); + + let mut refined_player_colors = match theme.appearance { + AppearanceContent::Light => PlayerColors::light(), + AppearanceContent::Dark => PlayerColors::dark(), + }; + refined_player_colors.merge(&theme.style.players); + + let mut refined_accent_colors = match theme.appearance { + AppearanceContent::Light => AccentColors::light(), + AppearanceContent::Dark => AccentColors::dark(), + }; + refined_accent_colors.merge(&theme.style.accents); + + let syntax_highlights = theme + .style + .syntax + .iter() + .map(|(syntax_token, highlight)| { + ( + syntax_token.clone(), + HighlightStyle { + color: highlight + .color + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + background_color: highlight + .background_color + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + font_style: highlight.font_style.map(Into::into), + font_weight: highlight.font_weight.map(Into::into), + ..Default::default() + }, + ) + }) + .collect::>(); + let syntax_theme = SyntaxTheme::merge(Arc::new(SyntaxTheme::default()), syntax_highlights); + + let window_background_appearance = theme + .style + .window_background_appearance + .map(Into::into) + .unwrap_or_default(); + + Theme { + id: uuid::Uuid::new_v4().to_string(), + name: theme.name.clone().into(), + appearance, + styles: ThemeStyles { + system: SystemColors::default(), + window_background_appearance, + accents: refined_accent_colors, + colors: refined_theme_colors, + status: refined_status_colors, + player: refined_player_colors, + syntax: syntax_theme, + }, + } + } +} + +/// Refines a [ThemeFamilyContent] and it's [ThemeContent]s into a [ThemeFamily]. +pub fn refine_theme_family(theme_family_content: ThemeFamilyContent) -> ThemeFamily { + let id = Uuid::new_v4().to_string(); + let name = theme_family_content.name.clone(); + let author = theme_family_content.author.clone(); + + let mut theme_family = ThemeFamily { + id: id.clone(), + name: name.clone().into(), + author: author.clone().into(), + themes: vec![], + scales: default_color_scales(), + }; + + let refined_themes = theme_family_content + .themes + .iter() + .map(|theme_content| theme_family.refine_theme(theme_content)) + .collect(); + + theme_family.themes = refined_themes; + + theme_family +} /// A theme is the primary mechanism for defining the appearance of the UI. #[derive(Clone, PartialEq)]