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
This commit is contained in:
Nate Butler 2024-10-29 22:30:58 -04:00 committed by GitHub
parent 63524a2354
commit a5f52f0f04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 114 additions and 90 deletions

View file

@ -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<Item = ThemeFamilyContent>) {
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<Item = ThemeContent>) {
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::<Vec<_>>();
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

View file

@ -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::<Vec<_>>();
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)]