diff --git a/crates/settings/src/base_keymap_setting.rs b/crates/settings/src/base_keymap_setting.rs index 599bdff9457c1b7eb947c28a1967fa2069ea11a5..f02ee49e7c778bfec604b5af9e7410de73eb9e01 100644 --- a/crates/settings/src/base_keymap_setting.rs +++ b/crates/settings/src/base_keymap_setting.rs @@ -4,6 +4,7 @@ use crate::{ self as settings, settings_content::{self, BaseKeymapContent, SettingsContent}, }; +use gpui::App; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, VsCodeSettings}; @@ -138,11 +139,11 @@ pub struct BaseKeymapSetting { } impl Settings for BaseKeymap { - fn from_file(s: &crate::settings_content::SettingsContent) -> Option { + fn from_file(s: &crate::settings_content::SettingsContent, _cx: &mut App) -> Option { s.base_keymap.map(Into::into) } - fn refine(&mut self, s: &settings_content::SettingsContent) { + fn refine(&mut self, s: &settings_content::SettingsContent, _cx: &mut App) { if let Some(base_keymap) = s.base_keymap { *self = base_keymap.into(); }; diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index e8201a4fa143a2e31300ecf955e1f9abb6af3158..b09991854dd1190ed9588960253bc685604b8b75 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -18,6 +18,9 @@ pub struct SettingsContent { #[serde(flatten)] pub project: ProjectSettingsContent, + #[serde(flatten)] + pub theme: ThemeSettingsContent, + pub base_keymap: Option, pub auto_update: Option, diff --git a/crates/settings/src/settings_content/theme.rs b/crates/settings/src/settings_content/theme.rs index e32f39469ef763448ba2da9ecbb88cab06d1ff95..a7469ec2893e05482ce16b16e71e192c81181c74 100644 --- a/crates/settings/src/settings_content/theme.rs +++ b/crates/settings/src/settings_content/theme.rs @@ -1,5 +1,5 @@ use collections::{HashMap, IndexMap}; -use gpui::{FontFallbacks, FontFeatures}; +use gpui::{FontFallbacks, FontFeatures, FontStyle, FontWeight}; use schemars::{JsonSchema, JsonSchema_repr, json_schema}; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; @@ -999,6 +999,16 @@ pub enum WindowBackgroundContent { Blurred, } +impl Into for WindowBackgroundContent { + fn into(self) -> gpui::WindowBackgroundAppearance { + match self { + WindowBackgroundContent::Opaque => gpui::WindowBackgroundAppearance::Opaque, + WindowBackgroundContent::Transparent => gpui::WindowBackgroundAppearance::Transparent, + WindowBackgroundContent::Blurred => gpui::WindowBackgroundAppearance::Blurred, + } + } +} + #[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)] #[serde(rename_all = "snake_case")] pub enum FontStyleContent { @@ -1007,6 +1017,16 @@ pub enum FontStyleContent { Oblique, } +impl From for FontStyle { + fn from(value: FontStyleContent) -> Self { + match value { + FontStyleContent::Normal => FontStyle::Normal, + FontStyleContent::Italic => FontStyle::Italic, + FontStyleContent::Oblique => FontStyle::Oblique, + } + } +} + #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, JsonSchema_repr, PartialEq)] #[repr(u16)] pub enum FontWeightContent { @@ -1020,3 +1040,70 @@ pub enum FontWeightContent { ExtraBold = 800, Black = 900, } + +impl From for FontWeight { + fn from(value: FontWeightContent) -> Self { + match value { + FontWeightContent::Thin => FontWeight::THIN, + FontWeightContent::ExtraLight => FontWeight::EXTRA_LIGHT, + FontWeightContent::Light => FontWeight::LIGHT, + FontWeightContent::Normal => FontWeight::NORMAL, + FontWeightContent::Medium => FontWeight::MEDIUM, + FontWeightContent::Semibold => FontWeight::SEMIBOLD, + FontWeightContent::Bold => FontWeight::BOLD, + FontWeightContent::ExtraBold => FontWeight::EXTRA_BOLD, + FontWeightContent::Black => FontWeight::BLACK, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_buffer_line_height_deserialize_valid() { + assert_eq!( + serde_json::from_value::(json!("comfortable")).unwrap(), + BufferLineHeight::Comfortable + ); + assert_eq!( + serde_json::from_value::(json!("standard")).unwrap(), + BufferLineHeight::Standard + ); + assert_eq!( + serde_json::from_value::(json!({"custom": 1.0})).unwrap(), + BufferLineHeight::Custom(1.0) + ); + assert_eq!( + serde_json::from_value::(json!({"custom": 1.5})).unwrap(), + BufferLineHeight::Custom(1.5) + ); + } + + #[test] + fn test_buffer_line_height_deserialize_invalid() { + assert!( + serde_json::from_value::(json!({"custom": 0.99})) + .err() + .unwrap() + .to_string() + .contains("buffer_line_height.custom must be at least 1.0") + ); + assert!( + serde_json::from_value::(json!({"custom": 0.0})) + .err() + .unwrap() + .to_string() + .contains("buffer_line_height.custom must be at least 1.0") + ); + assert!( + serde_json::from_value::(json!({"custom": -1.0})) + .err() + .unwrap() + .to_string() + .contains("buffer_line_height.custom must be at least 1.0") + ); + } +} diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 0bcd23ece66cd01dfe9f1fc4d5e5444f9ef7918b..d1b17103edea989363a9c5549a4243a0f7e968da 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -21,16 +21,13 @@ use std::{ str::{self, FromStr}, sync::Arc, }; -use util::{ - ResultExt as _, merge_non_null_json_value_into, -}; +use util::{ResultExt as _, merge_non_null_json_value_into}; pub type EditorconfigProperties = ec4rs::Properties; use crate::{ - ActiveSettingsProfileName, SettingsJsonSchemaParams, SettingsUiEntry, - VsCodeSettings, WorktreeId, parse_json_with_comments, - replace_value_in_json_text, + ActiveSettingsProfileName, SettingsJsonSchemaParams, SettingsUiEntry, VsCodeSettings, + WorktreeId, parse_json_with_comments, replace_value_in_json_text, settings_content::{ ExtensionsSettingsContent, ProjectSettingsContent, ServerSettingsContent, SettingsContent, UserSettingsContent, @@ -60,9 +57,9 @@ pub trait Settings: 'static + Send + Sync + Sized { /// user settings match the current version of the settings. const PRESERVED_KEYS: Option<&'static [&'static str]> = None; - fn from_file(content: &SettingsContent) -> Option; + fn from_file(content: &SettingsContent, cx: &mut App) -> Option; - fn refine(&mut self, content: &SettingsContent); + fn refine(&mut self, content: &SettingsContent, cx: &mut App); fn missing_default() -> anyhow::Error { anyhow::anyhow!("missing default for: {}", std::any::type_name::()) @@ -252,8 +249,8 @@ struct SettingValue { trait AnySettingValue: 'static + Send + Sync { fn setting_type_name(&self) -> &'static str; - fn from_file(&self, s: &SettingsContent) -> Option>; - fn refine(&self, value: &mut dyn Any, s: &[&SettingsContent]); + fn from_file(&self, s: &SettingsContent, cx: &mut App) -> Option>; + fn refine(&self, value: &mut dyn Any, s: &[&SettingsContent], cx: &mut App); fn value_for_path(&self, path: Option) -> &dyn Any; fn all_local_values(&self) -> Vec<(WorktreeId, Arc, &dyn Any)>; @@ -347,9 +344,9 @@ impl SettingsStore { refinements.push(server_settings) } // todo!() unwrap... - let mut value = T::from_file(&self.default_settings).unwrap(); + let mut value = T::from_file(&self.default_settings, cx).unwrap(); for refinement in refinements { - value.refine(refinement) + value.refine(refinement, cx) } setting_value.set_global_value(Box::new(value)); @@ -1114,8 +1111,8 @@ impl SettingsStore { for setting_value in self.setting_values.values_mut() { // If the global settings file changed, reload the global value for the field. if changed_local_path.is_none() { - let mut value = setting_value.from_file(&self.default_settings).unwrap(); - setting_value.refine(value.as_mut(), &refinements); + let mut value = setting_value.from_file(&self.default_settings, cx).unwrap(); + setting_value.refine(value.as_mut(), &refinements, cx); setting_value.set_global_value(value); } @@ -1137,7 +1134,7 @@ impl SettingsStore { // NOTE: this kind of condition existing in the old code too, // but is there a problem when a setting is removed from a file? - if setting_value.from_file(local_settings).is_some() { + if setting_value.from_file(local_settings, cx).is_some() { paths_stack.push(Some((*root_id, directory_path.as_ref()))); project_settings_stack.push(local_settings); @@ -1150,9 +1147,9 @@ impl SettingsStore { continue; } - let mut value = setting_value.from_file(&self.default_settings).unwrap(); - setting_value.refine(value.as_mut(), &refinements); - setting_value.refine(value.as_mut(), &project_settings_stack); + let mut value = setting_value.from_file(&self.default_settings, cx).unwrap(); + setting_value.refine(value.as_mut(), &refinements, cx); + setting_value.refine(value.as_mut(), &project_settings_stack, cx); setting_value.set_local_value(*root_id, directory_path.clone(), value); } } @@ -1235,16 +1232,16 @@ impl Debug for SettingsStore { } impl AnySettingValue for SettingValue { - fn from_file(&self, s: &SettingsContent) -> Option> { + fn from_file(&self, s: &SettingsContent, cx: &mut App) -> Option> { (type_name::(), TypeId::of::()); - T::from_file(s).map(|result| Box::new(result) as _) + T::from_file(s, cx).map(|result| Box::new(result) as _) } - fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent]) { + fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent], cx: &mut App) { (type_name::(), TypeId::of::()); let value = value.downcast_mut::().unwrap(); for refinement in refinements { - value.refine(refinement) + value.refine(refinement, cx) } } @@ -1338,7 +1335,7 @@ impl AnySettingValue for SettingValue { #[cfg(test)] mod tests { use crate::{ - TitleBarSettingsContent, TitleBarVisibilityContent, VsCodeSettingsSource, + TitleBarSettingsContent, TitleBarVisibilityContent, VsCodeSettingsSource, default_settings, settings_content::LanguageSettingsContent, test_settings, }; @@ -1348,7 +1345,7 @@ mod tests { use serde::Deserialize; use settings_ui_macros::{SettingsKey, SettingsUi}; use unindent::Unindent; - use util::Refine; + use util::MergeFrom; #[derive(Debug, PartialEq)] struct AutoUpdateSetting { @@ -1356,13 +1353,13 @@ mod tests { } impl Settings for AutoUpdateSetting { - fn from_file(content: &SettingsContent) -> Option { + fn from_file(content: &SettingsContent, _: &mut App) -> Option { content .auto_update .map(|auto_update| AutoUpdateSetting { auto_update }) } - fn refine(&mut self, content: &SettingsContent) { + fn refine(&mut self, content: &SettingsContent, _: &mut App) { if let Some(auto_update) = content.auto_update { self.auto_update = auto_update; } @@ -1377,18 +1374,18 @@ mod tests { } impl Settings for TitleBarSettings { - fn from_file(content: &SettingsContent) -> Option { + fn from_file(content: &SettingsContent, _: &mut App) -> Option { let content = content.title_bar?; Some(TitleBarSettings { show: content.show?, }) } - fn refine(&mut self, content: &SettingsContent) { + fn refine(&mut self, content: &SettingsContent, _: &mut App) { let Some(content) = content.title_bar else { return; }; - self.show.refine(&content.show) + self.show.merge_from(&content.show) } fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {} diff --git a/crates/theme/src/schema.rs b/crates/theme/src/schema.rs index 2e8445a7917aa52f8c99d7356bcc5d7623d8faa9..ae904dd5d6b10ad896118ef304ddc5b6fc4109cd 100644 --- a/crates/theme/src/schema.rs +++ b/crates/theme/src/schema.rs @@ -11,21 +11,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use crate::{StatusColorsRefinement, ThemeColorsRefinement}; -pub(crate) fn try_parse_color(color: &str) -> Result { - let rgba = gpui::Rgba::try_from(color)?; - let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a)); - let hsla = palette::Hsla::from_color(rgba); - - let hsla = gpui::hsla( - hsla.hue.into_positive_degrees() / 360., - hsla.saturation, - hsla.lightness, - hsla.alpha, - ); - - Ok(hsla) -} - fn ensure_non_opaque(color: Hsla) -> Hsla { const MAXIMUM_OPACITY: f32 = 0.7; if color.a <= MAXIMUM_OPACITY { @@ -81,7 +66,7 @@ pub struct ThemeFamilyContent { pub struct ThemeContent { pub name: String, pub appearance: AppearanceContent, - pub style: ThemeStyleContent, + pub style: settings::ThemeStyleContent, } /// The content of a serialized theme. @@ -95,909 +80,214 @@ pub struct ThemeStyleContent { pub accents: Vec, #[serde(flatten, default)] - pub colors: ThemeColorsContent, + pub colors: settings::ThemeColorsContent, #[serde(flatten, default)] - pub status: StatusColorsContent, + pub status: settings::StatusColorsContent, #[serde(default)] pub players: Vec, /// The styles for syntax nodes. #[serde(default)] - pub syntax: IndexMap, + pub syntax: IndexMap, } -impl ThemeStyleContent { - /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeContent`]. - #[inline(always)] - pub fn theme_colors_refinement(&self) -> ThemeColorsRefinement { - self.colors - .theme_colors_refinement(&self.status_colors_refinement()) - } - - /// Returns a [`StatusColorsRefinement`] based on the colors in the [`ThemeContent`]. - #[inline(always)] - pub fn status_colors_refinement(&self) -> StatusColorsRefinement { - self.status.status_colors_refinement() - } - - /// Returns the syntax style overrides in the [`ThemeContent`]. - pub fn syntax_overrides(&self) -> Vec<(String, HighlightStyle)> { - self.syntax - .iter() - .map(|(key, style)| { - ( - key.clone(), - HighlightStyle { - color: style - .color - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - background_color: style - .background_color - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - font_style: style.font_style.map(FontStyle::from), - font_weight: style.font_weight.map(FontWeight::from), - ..Default::default() - }, - ) - }) - .collect() - } +/// Returns the syntax style overrides in the [`ThemeContent`]. +pub fn syntax_overrides(this: &settings::ThemeStyleContent) -> Vec<(String, HighlightStyle)> { + this.syntax + .iter() + .map(|(key, style)| { + ( + key.clone(), + HighlightStyle { + color: style + .color + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + background_color: style + .background_color + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + font_style: style.font_style.map(FontStyle::from), + font_weight: style.font_weight.map(FontWeight::from), + ..Default::default() + }, + ) + }) + .collect() } -pub(crate) fn try_parse_color(color: &str) -> Result { - let rgba = gpui::Rgba::try_from(color)?; - let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a)); - let hsla = palette::Hsla::from_color(rgba); - - let hsla = gpui::hsla( - hsla.hue.into_positive_degrees() / 360., - hsla.saturation, - hsla.lightness, - hsla.alpha, - ); - - Ok(hsla) -} - -impl ThemeColorsContent { - /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeColorsContent`]. - pub fn theme_colors_refinement( - &self, - status_colors: &StatusColorsRefinement, - ) -> ThemeColorsRefinement { - let border = self - .border - .as_ref() - .and_then(|color| try_parse_color(color).ok()); - let editor_document_highlight_read_background = self - .editor_document_highlight_read_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()); - let scrollbar_thumb_background = self - .scrollbar_thumb_background +pub fn status_colors_refinement(colors: &settings::StatusColorsContent) -> StatusColorsRefinement { + StatusColorsRefinement { + conflict: colors + .conflict .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or_else(|| { - self.deprecated_scrollbar_thumb_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - }); - let scrollbar_thumb_hover_background = self - .scrollbar_thumb_hover_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()); - let scrollbar_thumb_active_background = self - .scrollbar_thumb_active_background + .and_then(|color| try_parse_color(color).ok()), + conflict_background: colors + .conflict_background .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(scrollbar_thumb_background); - let scrollbar_thumb_border = self - .scrollbar_thumb_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()); - let element_hover = self - .element_hover - .as_ref() - .and_then(|color| try_parse_color(color).ok()); - let panel_background = self - .panel_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()); - ThemeColorsRefinement { - border, - border_variant: self - .border_variant - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - border_focused: self - .border_focused - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - border_selected: self - .border_selected - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - border_transparent: self - .border_transparent - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - border_disabled: self - .border_disabled - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - elevated_surface_background: self - .elevated_surface_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - surface_background: self - .surface_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - background: self - .background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - element_background: self - .element_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - element_hover, - element_active: self - .element_active - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - element_selected: self - .element_selected - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - element_disabled: self - .element_disabled - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - element_selection_background: self - .element_selection_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - drop_target_background: self - .drop_target_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - drop_target_border: self - .drop_target_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ghost_element_background: self - .ghost_element_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ghost_element_hover: self - .ghost_element_hover - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ghost_element_active: self - .ghost_element_active - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ghost_element_selected: self - .ghost_element_selected - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ghost_element_disabled: self - .ghost_element_disabled - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - text: self - .text - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - text_muted: self - .text_muted - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - text_placeholder: self - .text_placeholder - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - text_disabled: self - .text_disabled - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - text_accent: self - .text_accent - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - icon: self - .icon - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - icon_muted: self - .icon_muted - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - icon_disabled: self - .icon_disabled - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - icon_placeholder: self - .icon_placeholder - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - icon_accent: self - .icon_accent - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - debugger_accent: self - .debugger_accent - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - status_bar_background: self - .status_bar_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - title_bar_background: self - .title_bar_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - title_bar_inactive_background: self - .title_bar_inactive_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - toolbar_background: self - .toolbar_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - tab_bar_background: self - .tab_bar_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - tab_inactive_background: self - .tab_inactive_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - tab_active_background: self - .tab_active_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - search_match_background: self - .search_match_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - panel_background, - panel_focused_border: self - .panel_focused_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - panel_indent_guide: self - .panel_indent_guide - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - panel_indent_guide_hover: self - .panel_indent_guide_hover - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - panel_indent_guide_active: self - .panel_indent_guide_active - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - panel_overlay_background: self - .panel_overlay_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(panel_background.map(ensure_opaque)), - panel_overlay_hover: self - .panel_overlay_hover - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(panel_background - .zip(element_hover) - .map(|(panel_bg, hover_bg)| panel_bg.blend(hover_bg)) - .map(ensure_opaque)), - pane_focused_border: self - .pane_focused_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - pane_group_border: self - .pane_group_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(border), - scrollbar_thumb_background, - scrollbar_thumb_hover_background, - scrollbar_thumb_active_background, - scrollbar_thumb_border, - scrollbar_track_background: self - .scrollbar_track_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - scrollbar_track_border: self - .scrollbar_track_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - minimap_thumb_background: self - .minimap_thumb_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(scrollbar_thumb_background.map(ensure_non_opaque)), - minimap_thumb_hover_background: self - .minimap_thumb_hover_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(scrollbar_thumb_hover_background.map(ensure_non_opaque)), - minimap_thumb_active_background: self - .minimap_thumb_active_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(scrollbar_thumb_active_background.map(ensure_non_opaque)), - minimap_thumb_border: self - .minimap_thumb_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - .or(scrollbar_thumb_border), - editor_foreground: self - .editor_foreground - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_background: self - .editor_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_gutter_background: self - .editor_gutter_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_subheader_background: self - .editor_subheader_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_active_line_background: self - .editor_active_line_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_highlighted_line_background: self - .editor_highlighted_line_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_debugger_active_line_background: self - .editor_debugger_active_line_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_line_number: self - .editor_line_number - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_hover_line_number: self - .editor_hover_line_number - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_active_line_number: self - .editor_active_line_number - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_invisible: self - .editor_invisible - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_wrap_guide: self - .editor_wrap_guide - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_active_wrap_guide: self - .editor_active_wrap_guide - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_indent_guide: self - .editor_indent_guide - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_indent_guide_active: self - .editor_indent_guide_active - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_document_highlight_read_background, - editor_document_highlight_write_background: self - .editor_document_highlight_write_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - editor_document_highlight_bracket_background: self - .editor_document_highlight_bracket_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - // Fall back to `editor.document_highlight.read_background`, for backwards compatibility. - .or(editor_document_highlight_read_background), - terminal_background: self - .terminal_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_background: self - .terminal_ansi_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_foreground: self - .terminal_foreground - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_bright_foreground: self - .terminal_bright_foreground - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_dim_foreground: self - .terminal_dim_foreground - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_black: self - .terminal_ansi_black - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_black: self - .terminal_ansi_bright_black - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_black: self - .terminal_ansi_dim_black - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_red: self - .terminal_ansi_red - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_red: self - .terminal_ansi_bright_red - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_red: self - .terminal_ansi_dim_red - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_green: self - .terminal_ansi_green - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_green: self - .terminal_ansi_bright_green - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_green: self - .terminal_ansi_dim_green - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_yellow: self - .terminal_ansi_yellow - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_yellow: self - .terminal_ansi_bright_yellow - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_yellow: self - .terminal_ansi_dim_yellow - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_blue: self - .terminal_ansi_blue - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_blue: self - .terminal_ansi_bright_blue - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_blue: self - .terminal_ansi_dim_blue - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_magenta: self - .terminal_ansi_magenta - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_magenta: self - .terminal_ansi_bright_magenta - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_magenta: self - .terminal_ansi_dim_magenta - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_cyan: self - .terminal_ansi_cyan - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_cyan: self - .terminal_ansi_bright_cyan - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_cyan: self - .terminal_ansi_dim_cyan - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_white: self - .terminal_ansi_white - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_bright_white: self - .terminal_ansi_bright_white - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - terminal_ansi_dim_white: self - .terminal_ansi_dim_white - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - link_text_hover: self - .link_text_hover - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - version_control_added: self - .version_control_added - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - // Fall back to `created`, for backwards compatibility. - .or(status_colors.created), - version_control_deleted: self - .version_control_deleted - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - // Fall back to `deleted`, for backwards compatibility. - .or(status_colors.deleted), - version_control_modified: self - .version_control_modified - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - // Fall back to `modified`, for backwards compatibility. - .or(status_colors.modified), - version_control_renamed: self - .version_control_renamed - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - // Fall back to `modified`, for backwards compatibility. - .or(status_colors.modified), - version_control_conflict: self - .version_control_conflict - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - // Fall back to `ignored`, for backwards compatibility. - .or(status_colors.ignored), - version_control_ignored: self - .version_control_ignored - .as_ref() - .and_then(|color| try_parse_color(color).ok()) - // Fall back to `conflict`, for backwards compatibility. - .or(status_colors.ignored), - #[allow(deprecated)] - version_control_conflict_marker_ours: self - .version_control_conflict_marker_ours - .as_ref() - .or(self.version_control_conflict_ours_background.as_ref()) - .and_then(|color| try_parse_color(color).ok()), - #[allow(deprecated)] - version_control_conflict_marker_theirs: self - .version_control_conflict_marker_theirs - .as_ref() - .or(self.version_control_conflict_theirs_background.as_ref()) - .and_then(|color| try_parse_color(color).ok()), - } - } -} - -#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)] -#[serde(default)] -pub struct StatusColorsContent { - /// Indicates some kind of conflict, like a file changed on disk while it was open, or - /// merge conflicts in a Git repository. - #[serde(rename = "conflict")] - pub conflict: Option, - - #[serde(rename = "conflict.background")] - pub conflict_background: Option, - - #[serde(rename = "conflict.border")] - pub conflict_border: Option, - - /// Indicates something new, like a new file added to a Git repository. - #[serde(rename = "created")] - pub created: Option, - - #[serde(rename = "created.background")] - pub created_background: Option, - - #[serde(rename = "created.border")] - pub created_border: Option, - - /// Indicates that something no longer exists, like a deleted file. - #[serde(rename = "deleted")] - pub deleted: Option, - - #[serde(rename = "deleted.background")] - pub deleted_background: Option, - - #[serde(rename = "deleted.border")] - pub deleted_border: Option, - - /// Indicates a system error, a failed operation or a diagnostic error. - #[serde(rename = "error")] - pub error: Option, - - #[serde(rename = "error.background")] - pub error_background: Option, - - #[serde(rename = "error.border")] - pub error_border: Option, - - /// Represents a hidden status, such as a file being hidden in a file tree. - #[serde(rename = "hidden")] - pub hidden: Option, - - #[serde(rename = "hidden.background")] - pub hidden_background: Option, - - #[serde(rename = "hidden.border")] - pub hidden_border: Option, - - /// Indicates a hint or some kind of additional information. - #[serde(rename = "hint")] - pub hint: Option, - - #[serde(rename = "hint.background")] - pub hint_background: Option, - - #[serde(rename = "hint.border")] - pub hint_border: Option, - - /// Indicates that something is deliberately ignored, such as a file or operation ignored by Git. - #[serde(rename = "ignored")] - pub ignored: Option, - - #[serde(rename = "ignored.background")] - pub ignored_background: Option, - - #[serde(rename = "ignored.border")] - pub ignored_border: Option, - - /// Represents informational status updates or messages. - #[serde(rename = "info")] - pub info: Option, - - #[serde(rename = "info.background")] - pub info_background: Option, - - #[serde(rename = "info.border")] - pub info_border: Option, - - /// Indicates a changed or altered status, like a file that has been edited. - #[serde(rename = "modified")] - pub modified: Option, - - #[serde(rename = "modified.background")] - pub modified_background: Option, - - #[serde(rename = "modified.border")] - pub modified_border: Option, - - /// Indicates something that is predicted, like automatic code completion, or generated code. - #[serde(rename = "predictive")] - pub predictive: Option, - - #[serde(rename = "predictive.background")] - pub predictive_background: Option, - - #[serde(rename = "predictive.border")] - pub predictive_border: Option, - - /// Represents a renamed status, such as a file that has been renamed. - #[serde(rename = "renamed")] - pub renamed: Option, - - #[serde(rename = "renamed.background")] - pub renamed_background: Option, - - #[serde(rename = "renamed.border")] - pub renamed_border: Option, - - /// Indicates a successful operation or task completion. - #[serde(rename = "success")] - pub success: Option, - - #[serde(rename = "success.background")] - pub success_background: Option, - - #[serde(rename = "success.border")] - pub success_border: Option, - - /// Indicates some kind of unreachable status, like a block of code that can never be reached. - #[serde(rename = "unreachable")] - pub unreachable: Option, - - #[serde(rename = "unreachable.background")] - pub unreachable_background: Option, - - #[serde(rename = "unreachable.border")] - pub unreachable_border: Option, - - /// Represents a warning status, like an operation that is about to fail. - #[serde(rename = "warning")] - pub warning: Option, - - #[serde(rename = "warning.background")] - pub warning_background: Option, - - #[serde(rename = "warning.border")] - pub warning_border: Option, -} - -impl StatusColorsContent { - /// Returns a [`StatusColorsRefinement`] based on the colors in the [`StatusColorsContent`]. - pub fn status_colors_refinement(&self) -> StatusColorsRefinement { - StatusColorsRefinement { - conflict: self - .conflict - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - conflict_background: self - .conflict_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - conflict_border: self - .conflict_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - created: self - .created - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - created_background: self - .created_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - created_border: self - .created_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - deleted: self - .deleted - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - deleted_background: self - .deleted_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - deleted_border: self - .deleted_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - error: self - .error - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - error_background: self - .error_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - error_border: self - .error_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - hidden: self - .hidden - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - hidden_background: self - .hidden_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - hidden_border: self - .hidden_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - hint: self - .hint - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - hint_background: self - .hint_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - hint_border: self - .hint_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ignored: self - .ignored - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ignored_background: self - .ignored_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - ignored_border: self - .ignored_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - info: self - .info - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - info_background: self - .info_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - info_border: self - .info_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - modified: self - .modified - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - modified_background: self - .modified_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - modified_border: self - .modified_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - predictive: self - .predictive - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - predictive_background: self - .predictive_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - predictive_border: self - .predictive_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - renamed: self - .renamed - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - renamed_background: self - .renamed_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - renamed_border: self - .renamed_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - success: self - .success - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - success_background: self - .success_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - success_border: self - .success_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - unreachable: self - .unreachable - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - unreachable_background: self - .unreachable_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - unreachable_border: self - .unreachable_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - warning: self - .warning - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - warning_background: self - .warning_background - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - warning_border: self - .warning_border - .as_ref() - .and_then(|color| try_parse_color(color).ok()), - } + .and_then(|color| try_parse_color(color).ok()), + conflict_border: colors + .conflict_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + created: colors + .created + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + created_background: colors + .created_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + created_border: colors + .created_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + deleted: colors + .deleted + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + deleted_background: colors + .deleted_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + deleted_border: colors + .deleted_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + error: colors + .error + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + error_background: colors + .error_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + error_border: colors + .error_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + hidden: colors + .hidden + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + hidden_background: colors + .hidden_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + hidden_border: colors + .hidden_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + hint: colors + .hint + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + hint_background: colors + .hint_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + hint_border: colors + .hint_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ignored: colors + .ignored + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ignored_background: colors + .ignored_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ignored_border: colors + .ignored_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + info: colors + .info + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + info_background: colors + .info_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + info_border: colors + .info_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + modified: colors + .modified + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + modified_background: colors + .modified_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + modified_border: colors + .modified_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + predictive: colors + .predictive + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + predictive_background: colors + .predictive_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + predictive_border: colors + .predictive_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + renamed: colors + .renamed + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + renamed_background: colors + .renamed_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + renamed_border: colors + .renamed_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + success: colors + .success + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + success_background: colors + .success_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + success_border: colors + .success_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + unreachable: colors + .unreachable + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + unreachable_background: colors + .unreachable_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + unreachable_border: colors + .unreachable_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + warning: colors + .warning + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + warning_background: colors + .warning_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + warning_border: colors + .warning_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), } } @@ -1011,24 +301,6 @@ pub struct PlayerColorContent { pub selection: Option, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum FontStyleContent { - Normal, - Italic, - Oblique, -} - -impl From for FontStyle { - fn from(value: FontStyleContent) -> Self { - match value { - FontStyleContent::Normal => FontStyle::Normal, - FontStyleContent::Italic => FontStyle::Italic, - FontStyleContent::Oblique => FontStyle::Oblique, - } - } -} - #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, JsonSchema_repr, PartialEq)] #[repr(u16)] pub enum FontWeightContent { @@ -1043,18 +315,531 @@ pub enum FontWeightContent { Black = 900, } -impl From for FontWeight { - fn from(value: FontWeightContent) -> Self { - match value { - FontWeightContent::Thin => FontWeight::THIN, - FontWeightContent::ExtraLight => FontWeight::EXTRA_LIGHT, - FontWeightContent::Light => FontWeight::LIGHT, - FontWeightContent::Normal => FontWeight::NORMAL, - FontWeightContent::Medium => FontWeight::MEDIUM, - FontWeightContent::Semibold => FontWeight::SEMIBOLD, - FontWeightContent::Bold => FontWeight::BOLD, - FontWeightContent::ExtraBold => FontWeight::EXTRA_BOLD, - FontWeightContent::Black => FontWeight::BLACK, - } +pub fn theme_colors_refinement( + this: &settings::ThemeColorsContent, + status_colors: &StatusColorsRefinement, +) -> ThemeColorsRefinement { + let border = this + .border + .as_ref() + .and_then(|color| try_parse_color(color).ok()); + let editor_document_highlight_read_background = this + .editor_document_highlight_read_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()); + let scrollbar_thumb_background = this + .scrollbar_thumb_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or_else(|| { + this.deprecated_scrollbar_thumb_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + }); + let scrollbar_thumb_hover_background = this + .scrollbar_thumb_hover_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()); + let scrollbar_thumb_active_background = this + .scrollbar_thumb_active_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(scrollbar_thumb_background); + let scrollbar_thumb_border = this + .scrollbar_thumb_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()); + let element_hover = this + .element_hover + .as_ref() + .and_then(|color| try_parse_color(color).ok()); + let panel_background = this + .panel_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()); + ThemeColorsRefinement { + border, + border_variant: this + .border_variant + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + border_focused: this + .border_focused + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + border_selected: this + .border_selected + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + border_transparent: this + .border_transparent + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + border_disabled: this + .border_disabled + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + elevated_surface_background: this + .elevated_surface_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + surface_background: this + .surface_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + background: this + .background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + element_background: this + .element_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + element_hover, + element_active: this + .element_active + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + element_selected: this + .element_selected + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + element_disabled: this + .element_disabled + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + element_selection_background: this + .element_selection_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + drop_target_background: this + .drop_target_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + drop_target_border: this + .drop_target_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ghost_element_background: this + .ghost_element_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ghost_element_hover: this + .ghost_element_hover + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ghost_element_active: this + .ghost_element_active + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ghost_element_selected: this + .ghost_element_selected + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + ghost_element_disabled: this + .ghost_element_disabled + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + text: this + .text + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + text_muted: this + .text_muted + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + text_placeholder: this + .text_placeholder + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + text_disabled: this + .text_disabled + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + text_accent: this + .text_accent + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + icon: this + .icon + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + icon_muted: this + .icon_muted + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + icon_disabled: this + .icon_disabled + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + icon_placeholder: this + .icon_placeholder + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + icon_accent: this + .icon_accent + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + debugger_accent: this + .debugger_accent + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + status_bar_background: this + .status_bar_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + title_bar_background: this + .title_bar_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + title_bar_inactive_background: this + .title_bar_inactive_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + toolbar_background: this + .toolbar_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + tab_bar_background: this + .tab_bar_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + tab_inactive_background: this + .tab_inactive_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + tab_active_background: this + .tab_active_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + search_match_background: this + .search_match_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + panel_background, + panel_focused_border: this + .panel_focused_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + panel_indent_guide: this + .panel_indent_guide + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + panel_indent_guide_hover: this + .panel_indent_guide_hover + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + panel_indent_guide_active: this + .panel_indent_guide_active + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + panel_overlay_background: this + .panel_overlay_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(panel_background.map(ensure_opaque)), + panel_overlay_hover: this + .panel_overlay_hover + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(panel_background + .zip(element_hover) + .map(|(panel_bg, hover_bg)| panel_bg.blend(hover_bg)) + .map(ensure_opaque)), + pane_focused_border: this + .pane_focused_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + pane_group_border: this + .pane_group_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(border), + scrollbar_thumb_background, + scrollbar_thumb_hover_background, + scrollbar_thumb_active_background, + scrollbar_thumb_border, + scrollbar_track_background: this + .scrollbar_track_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + scrollbar_track_border: this + .scrollbar_track_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + minimap_thumb_background: this + .minimap_thumb_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(scrollbar_thumb_background.map(ensure_non_opaque)), + minimap_thumb_hover_background: this + .minimap_thumb_hover_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(scrollbar_thumb_hover_background.map(ensure_non_opaque)), + minimap_thumb_active_background: this + .minimap_thumb_active_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(scrollbar_thumb_active_background.map(ensure_non_opaque)), + minimap_thumb_border: this + .minimap_thumb_border + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + .or(scrollbar_thumb_border), + editor_foreground: this + .editor_foreground + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_background: this + .editor_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_gutter_background: this + .editor_gutter_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_subheader_background: this + .editor_subheader_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_active_line_background: this + .editor_active_line_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_highlighted_line_background: this + .editor_highlighted_line_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_debugger_active_line_background: this + .editor_debugger_active_line_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_line_number: this + .editor_line_number + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_hover_line_number: this + .editor_hover_line_number + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_active_line_number: this + .editor_active_line_number + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_invisible: this + .editor_invisible + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_wrap_guide: this + .editor_wrap_guide + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_active_wrap_guide: this + .editor_active_wrap_guide + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_indent_guide: this + .editor_indent_guide + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_indent_guide_active: this + .editor_indent_guide_active + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_document_highlight_read_background, + editor_document_highlight_write_background: this + .editor_document_highlight_write_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + editor_document_highlight_bracket_background: this + .editor_document_highlight_bracket_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + // Fall back to `editor.document_highlight.read_background`, for backwards compatibility. + .or(editor_document_highlight_read_background), + terminal_background: this + .terminal_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_background: this + .terminal_ansi_background + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_foreground: this + .terminal_foreground + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_bright_foreground: this + .terminal_bright_foreground + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_dim_foreground: this + .terminal_dim_foreground + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_black: this + .terminal_ansi_black + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_black: this + .terminal_ansi_bright_black + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_black: this + .terminal_ansi_dim_black + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_red: this + .terminal_ansi_red + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_red: this + .terminal_ansi_bright_red + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_red: this + .terminal_ansi_dim_red + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_green: this + .terminal_ansi_green + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_green: this + .terminal_ansi_bright_green + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_green: this + .terminal_ansi_dim_green + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_yellow: this + .terminal_ansi_yellow + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_yellow: this + .terminal_ansi_bright_yellow + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_yellow: this + .terminal_ansi_dim_yellow + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_blue: this + .terminal_ansi_blue + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_blue: this + .terminal_ansi_bright_blue + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_blue: this + .terminal_ansi_dim_blue + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_magenta: this + .terminal_ansi_magenta + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_magenta: this + .terminal_ansi_bright_magenta + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_magenta: this + .terminal_ansi_dim_magenta + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_cyan: this + .terminal_ansi_cyan + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_cyan: this + .terminal_ansi_bright_cyan + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_cyan: this + .terminal_ansi_dim_cyan + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_white: this + .terminal_ansi_white + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_bright_white: this + .terminal_ansi_bright_white + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + terminal_ansi_dim_white: this + .terminal_ansi_dim_white + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + link_text_hover: this + .link_text_hover + .as_ref() + .and_then(|color| try_parse_color(color).ok()), + version_control_added: this + .version_control_added + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + // Fall back to `created`, for backwards compatibility. + .or(status_colors.created), + version_control_deleted: this + .version_control_deleted + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + // Fall back to `deleted`, for backwards compatibility. + .or(status_colors.deleted), + version_control_modified: this + .version_control_modified + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + // Fall back to `modified`, for backwards compatibility. + .or(status_colors.modified), + version_control_renamed: this + .version_control_renamed + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + // Fall back to `modified`, for backwards compatibility. + .or(status_colors.modified), + version_control_conflict: this + .version_control_conflict + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + // Fall back to `ignored`, for backwards compatibility. + .or(status_colors.ignored), + version_control_ignored: this + .version_control_ignored + .as_ref() + .and_then(|color| try_parse_color(color).ok()) + // Fall back to `conflict`, for backwards compatibility. + .or(status_colors.ignored), + #[allow(deprecated)] + version_control_conflict_marker_ours: this + .version_control_conflict_marker_ours + .as_ref() + .or(this.version_control_conflict_ours_background.as_ref()) + .and_then(|color| try_parse_color(color).ok()), + #[allow(deprecated)] + version_control_conflict_marker_theirs: this + .version_control_conflict_marker_theirs + .as_ref() + .or(this.version_control_conflict_theirs_background.as_ref()) + .and_then(|color| try_parse_color(color).ok()), } } + +pub(crate) fn try_parse_color(color: &str) -> anyhow::Result { + let rgba = gpui::Rgba::try_from(color)?; + let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a)); + let hsla = palette::Hsla::from_color(rgba); + + let hsla = gpui::hsla( + hsla.hue.into_positive_degrees() / 360., + hsla.saturation, + hsla.lightness, + hsla.alpha, + ); + + Ok(hsla) +} diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 500ab2c03cf7ee01b245ca2bc89bd2c218f66d1a..a6c99c8219fd4ff993e48d725e4142d16dcc2670 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -1,22 +1,25 @@ use crate::fallback_themes::zed_default_dark; use crate::{ Appearance, DEFAULT_ICON_THEME_NAME, IconTheme, IconThemeNotFoundError, SyntaxTheme, Theme, - ThemeNotFoundError, ThemeRegistry, ThemeStyleContent, + ThemeColorsRefinement, ThemeNotFoundError, ThemeRegistry, ThemeStyleContent, + status_colors_refinement, syntax_overrides, theme_colors_refinement, }; -use anyhow::Result; use collections::HashMap; use derive_more::{Deref, DerefMut}; use gpui::{ - App, Context, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels, - SharedString, Subscription, Window, px, + App, Context, Font, FontFallbacks, FontStyle, FontWeight, Global, Pixels, Subscription, Window, + px, }; use refineable::Refineable; use schemars::{JsonSchema, json_schema}; use serde::{Deserialize, Serialize}; -use settings::{ParameterizedJsonSchema, Settings, SettingsKey, SettingsSources, SettingsUi}; +use settings::{ + FontFamilyName, IconThemeName, ParameterizedJsonSchema, Settings, SettingsContent, ThemeMode, + ThemeName, +}; use std::sync::Arc; -use util::ResultExt as _; use util::schemars::replace_subschema; +use util::{MergeFrom, ResultExt as _}; const MIN_FONT_SIZE: Pixels = px(6.0); const MAX_FONT_SIZE: Pixels = px(100.0); @@ -119,16 +122,16 @@ 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 experimental_theme_overrides: Option, + pub experimental_theme_overrides: Option, /// Manual overrides per theme - pub theme_overrides: HashMap, + pub theme_overrides: HashMap, /// The current icon theme selection. pub icon_theme_selection: Option, /// The active icon theme. pub active_icon_theme: Arc, /// The density of the UI. /// Note: This setting is still experimental. See [this tracking issue]( - pub ui_density: UiDensity, + pub ui_density: settings::UiDensity, /// The amount of fading applied to unnecessary code. pub unnecessary_code_fade: f32, } @@ -277,24 +280,15 @@ pub enum ThemeSelection { }, } -// TODO: Rename ThemeMode -> ThemeAppearanceMode -/// The mode use to select a theme. -/// -/// `Light` and `Dark` will select their respective themes. -/// -/// `System` will select the theme based on the system's appearance. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ThemeMode { - /// Use the specified `light` theme. - Light, - - /// Use the specified `dark` theme. - Dark, - - /// Use the theme based on the system's appearance. - #[default] - System, +impl From for ThemeSelection { + fn from(selection: settings::ThemeSelection) -> Self { + match selection { + settings::ThemeSelection::Static(theme) => ThemeSelection::Static(theme), + settings::ThemeSelection::Dynamic { mode, light, dark } => { + ThemeSelection::Dynamic { mode, light, dark } + } + } + } } impl ThemeSelection { @@ -323,15 +317,13 @@ impl ThemeSelection { } /// Represents the selection of an icon theme, which can be either static or dynamic. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(untagged)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum IconThemeSelection { /// A static icon theme selection, represented by a single icon theme name. Static(IconThemeName), /// A dynamic icon theme selection, which can change based on the [`ThemeMode`]. Dynamic { /// The mode used to determine which theme to use. - #[serde(default)] mode: ThemeMode, /// The icon theme to use for light mode. light: IconThemeName, @@ -340,6 +332,17 @@ pub enum IconThemeSelection { }, } +impl From for IconThemeSelection { + fn from(selection: settings::IconThemeSelection) -> Self { + match selection { + settings::IconThemeSelection::Static(theme) => IconThemeSelection::Static(theme), + settings::IconThemeSelection::Dynamic { mode, light, dark } => { + IconThemeSelection::Dynamic { mode, light, dark } + } + } + } +} + impl IconThemeSelection { /// Returns the icon theme name based on the given [`Appearance`]. pub fn icon_theme(&self, system_appearance: Appearance) -> &str { @@ -365,189 +368,105 @@ impl IconThemeSelection { } } -/// Settings for rendering text in UI and text buffers. -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] -#[settings_key(None)] -pub struct ThemeSettingsContent { - /// The default font size for text in the UI. - #[serde(default)] - pub ui_font_size: Option, - /// The name of a font to use for rendering in the UI. - #[serde(default)] - pub ui_font_family: Option, - /// The font fallbacks to use for rendering in the UI. - #[serde(default)] - #[schemars(default = "default_font_fallbacks")] - #[schemars(extend("uniqueItems" = true))] - pub ui_font_fallbacks: Option>, - /// The OpenType features to enable for text in the UI. - #[serde(default)] - #[schemars(default = "default_font_features")] - pub ui_font_features: Option, - /// The weight of the UI font in CSS units from 100 to 900. - #[serde(default)] - pub ui_font_weight: Option, - /// The name of a font to use for rendering in text buffers. - #[serde(default)] - pub buffer_font_family: Option, - /// The font fallbacks to use for rendering in text buffers. - #[serde(default)] - #[schemars(extend("uniqueItems" = true))] - pub buffer_font_fallbacks: Option>, - /// The default font size for rendering in text buffers. - #[serde(default)] - pub buffer_font_size: Option, - /// The weight of the editor font in CSS units from 100 to 900. - #[serde(default)] - pub buffer_font_weight: Option, - /// The buffer's line height. - #[serde(default)] - pub buffer_line_height: Option, - /// The OpenType features to enable for rendering in text buffers. - #[serde(default)] - #[schemars(default = "default_font_features")] - pub buffer_font_features: Option, - /// The font size for the agent panel. Falls back to the UI font size if unset. - #[serde(default)] - pub agent_font_size: Option>, - /// The name of the Zed theme to use. - #[serde(default)] - pub theme: Option, - /// The name of the icon theme to use. - #[serde(default)] - pub icon_theme: Option, - - /// UNSTABLE: Expect many elements to be broken. - /// - // Controls the density of the UI. - #[serde(rename = "unstable.ui_density", default)] - pub ui_density: Option, - - /// How much to fade out unused code. - #[serde(default)] - pub unnecessary_code_fade: Option, - - /// EXPERIMENTAL: Overrides for the current theme. - /// - /// These values will override the ones on the current theme specified in `theme`. - #[serde(rename = "experimental.theme_overrides", default)] - 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 { - Some(FontFeatures::default()) -} - -fn default_font_fallbacks() -> Option { - Some(FontFallbacks::default()) -} - -impl ThemeSettingsContent { - /// Sets the theme for the given appearance to the theme with the specified name. - pub fn set_theme(&mut self, theme_name: impl Into>, appearance: Appearance) { - if let Some(selection) = self.theme.as_mut() { - let theme_to_update = match selection { - ThemeSelection::Static(theme) => theme, - ThemeSelection::Dynamic { mode, light, dark } => match mode { - ThemeMode::Light => light, - ThemeMode::Dark => dark, - ThemeMode::System => match appearance { - Appearance::Light => light, - Appearance::Dark => dark, - }, - }, - }; - - *theme_to_update = ThemeName(theme_name.into()); - } else { - self.theme = Some(ThemeSelection::Static(ThemeName(theme_name.into()))); - } - } - - /// Sets the icon theme for the given appearance to the icon theme with the specified name. - pub fn set_icon_theme(&mut self, icon_theme_name: String, appearance: Appearance) { - if let Some(selection) = self.icon_theme.as_mut() { - let icon_theme_to_update = match selection { - IconThemeSelection::Static(theme) => theme, - IconThemeSelection::Dynamic { mode, light, dark } => match mode { - ThemeMode::Light => light, - ThemeMode::Dark => dark, - ThemeMode::System => match appearance { - Appearance::Light => light, - Appearance::Dark => dark, - }, - }, - }; - - *icon_theme_to_update = IconThemeName(icon_theme_name.into()); - } else { - self.icon_theme = Some(IconThemeSelection::Static(IconThemeName( - icon_theme_name.into(), - ))); - } - } - - /// Sets the mode for the theme. - pub fn set_mode(&mut self, mode: ThemeMode) { - if let Some(selection) = self.theme.as_mut() { - match selection { - ThemeSelection::Static(theme) => { - // If the theme was previously set to a single static theme, - // we don't know whether it was a light or dark theme, so we - // just use it for both. - self.theme = Some(ThemeSelection::Dynamic { - mode, - light: theme.clone(), - dark: theme.clone(), - }); - } - ThemeSelection::Dynamic { - mode: mode_to_update, - .. - } => *mode_to_update = mode, - } - } else { - self.theme = Some(ThemeSelection::Dynamic { - mode, - light: ThemeName(ThemeSettings::DEFAULT_LIGHT_THEME.into()), - dark: ThemeName(ThemeSettings::DEFAULT_DARK_THEME.into()), - }); - } - - if let Some(selection) = self.icon_theme.as_mut() { - match selection { - IconThemeSelection::Static(icon_theme) => { - // If the icon theme was previously set to a single static - // theme, we don't know whether it was a light or dark - // theme, so we just use it for both. - self.icon_theme = Some(IconThemeSelection::Dynamic { - mode, - light: icon_theme.clone(), - dark: icon_theme.clone(), - }); - } - IconThemeSelection::Dynamic { - mode: mode_to_update, - .. - } => *mode_to_update = mode, - } - } else { - self.icon_theme = Some(IconThemeSelection::Static(IconThemeName( - DEFAULT_ICON_THEME_NAME.into(), - ))); - } - } -} +// impl ThemeSettingsContent { +// /// Sets the theme for the given appearance to the theme with the specified name. +// pub fn set_theme(&mut self, theme_name: impl Into>, appearance: Appearance) { +// if let Some(selection) = self.theme.as_mut() { +// let theme_to_update = match selection { +// ThemeSelection::Static(theme) => theme, +// ThemeSelection::Dynamic { mode, light, dark } => match mode { +// ThemeMode::Light => light, +// ThemeMode::Dark => dark, +// ThemeMode::System => match appearance { +// Appearance::Light => light, +// Appearance::Dark => dark, +// }, +// }, +// }; + +// *theme_to_update = ThemeName(theme_name.into()); +// } else { +// self.theme = Some(ThemeSelection::Static(ThemeName(theme_name.into()))); +// } +// } + +// /// Sets the icon theme for the given appearance to the icon theme with the specified name. +// pub fn set_icon_theme(&mut self, icon_theme_name: String, appearance: Appearance) { +// if let Some(selection) = self.icon_theme.as_mut() { +// let icon_theme_to_update = match selection { +// IconThemeSelection::Static(theme) => theme, +// IconThemeSelection::Dynamic { mode, light, dark } => match mode { +// ThemeMode::Light => light, +// ThemeMode::Dark => dark, +// ThemeMode::System => match appearance { +// Appearance::Light => light, +// Appearance::Dark => dark, +// }, +// }, +// }; + +// *icon_theme_to_update = IconThemeName(icon_theme_name.into()); +// } else { +// self.icon_theme = Some(IconThemeSelection::Static(IconThemeName( +// icon_theme_name.into(), +// ))); +// } +// } + +// /// Sets the mode for the theme. +// pub fn set_mode(&mut self, mode: ThemeMode) { +// if let Some(selection) = self.theme.as_mut() { +// match selection { +// ThemeSelection::Static(theme) => { +// // If the theme was previously set to a single static theme, +// // we don't know whether it was a light or dark theme, so we +// // just use it for both. +// self.theme = Some(ThemeSelection::Dynamic { +// mode, +// light: theme.clone(), +// dark: theme.clone(), +// }); +// } +// ThemeSelection::Dynamic { +// mode: mode_to_update, +// .. +// } => *mode_to_update = mode, +// } +// } else { +// self.theme = Some(ThemeSelection::Dynamic { +// mode, +// light: ThemeName(ThemeSettings::DEFAULT_LIGHT_THEME.into()), +// dark: ThemeName(ThemeSettings::DEFAULT_DARK_THEME.into()), +// }); +// } + +// if let Some(selection) = self.icon_theme.as_mut() { +// match selection { +// IconThemeSelection::Static(icon_theme) => { +// // If the icon theme was previously set to a single static +// // theme, we don't know whether it was a light or dark +// // theme, so we just use it for both. +// self.icon_theme = Some(IconThemeSelection::Dynamic { +// mode, +// light: icon_theme.clone(), +// dark: icon_theme.clone(), +// }); +// } +// IconThemeSelection::Dynamic { +// mode: mode_to_update, +// .. +// } => *mode_to_update = mode, +// } +// } else { +// self.icon_theme = Some(IconThemeSelection::Static(IconThemeName( +// DEFAULT_ICON_THEME_NAME.into(), +// ))); +// } +// } +// } /// The buffer's line height. -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)] -#[serde(rename_all = "snake_case")] +#[derive(Clone, Copy, Debug, PartialEq, Default)] pub enum BufferLineHeight { /// A less dense line height. #[default] @@ -555,21 +474,19 @@ pub enum BufferLineHeight { /// The default line height. Standard, /// A custom line height, where 1.0 is the font's height. Must be at least 1.0. - Custom(#[serde(deserialize_with = "deserialize_line_height")] f32), + Custom(f32), } -fn deserialize_line_height<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - let value = f32::deserialize(deserializer)?; - if value < 1.0 { - return Err(serde::de::Error::custom( - "buffer_line_height.custom must be at least 1.0", - )); +impl From for BufferLineHeight { + fn from(value: settings::BufferLineHeight) -> Self { + match value { + settings::BufferLineHeight::Comfortable => BufferLineHeight::Comfortable, + settings::BufferLineHeight::Standard => BufferLineHeight::Standard, + settings::BufferLineHeight::Custom(line_height) => { + BufferLineHeight::Custom(line_height) + } + } } - - Ok(value) } impl BufferLineHeight { @@ -681,24 +598,22 @@ impl ThemeSettings { } } - fn modify_theme(base_theme: &mut Theme, theme_overrides: &ThemeStyleContent) { + fn modify_theme(base_theme: &mut Theme, theme_overrides: &settings::ThemeStyleContent) { if let Some(window_background_appearance) = theme_overrides.window_background_appearance { base_theme.styles.window_background_appearance = window_background_appearance.into(); } + let status_color_refinement = status_colors_refinement(&theme_overrides.status); - base_theme - .styles - .colors - .refine(&theme_overrides.theme_colors_refinement()); - base_theme - .styles - .status - .refine(&theme_overrides.status_colors_refinement()); + base_theme.styles.colors.refine(&theme_colors_refinement( + &theme_overrides.colors, + &status_color_refinement, + )); + base_theme.styles.status.refine(&status_color_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(), + syntax_overrides(&theme_overrides), ); } @@ -818,292 +733,170 @@ fn clamp_font_weight(weight: f32) -> FontWeight { FontWeight(weight.clamp(100., 950.)) } +fn font_fallbacks_from_settings( + fallbacks: Option>, +) -> Option { + fallbacks.map(|fallbacks| { + FontFallbacks::from_fonts( + fallbacks + .into_iter() + .map(|font_family| font_family.0.to_string()) + .collect(), + ) + }) +} + impl settings::Settings for ThemeSettings { - fn load(sources: SettingsSources, cx: &mut App) -> Result { + fn from_file(content: &settings::SettingsContent, cx: &mut App) -> Option { + let content = &content.theme; let themes = ThemeRegistry::default_global(cx); let system_appearance = SystemAppearance::default_global(cx); - - fn font_fallbacks_from_settings( - fallbacks: Option>, - ) -> Option { - fallbacks.map(|fallbacks| { - FontFallbacks::from_fonts( - fallbacks - .into_iter() - .map(|font_family| font_family.0.to_string()) - .collect(), - ) - }) - } - - let defaults = sources.default; - let mut this = Self { - ui_font_size: defaults.ui_font_size.unwrap().into(), + let theme_selection: ThemeSelection = content.theme.clone()?.into(); + let icon_theme_selection: IconThemeSelection = content.icon_theme.clone()?.into(); + let this = Self { + ui_font_size: content.ui_font_size?.into(), ui_font: Font { - family: defaults.ui_font_family.as_ref().unwrap().0.clone().into(), - features: defaults.ui_font_features.clone().unwrap(), - fallbacks: font_fallbacks_from_settings(defaults.ui_font_fallbacks.clone()), - weight: defaults.ui_font_weight.map(FontWeight).unwrap(), + family: content.ui_font_family.as_ref()?.0.clone().into(), + features: content.ui_font_features.clone()?, + fallbacks: font_fallbacks_from_settings(content.ui_font_fallbacks.clone()), + weight: content.ui_font_weight.map(FontWeight)?, style: Default::default(), }, buffer_font: Font { - family: defaults - .buffer_font_family - .as_ref() - .unwrap() - .0 - .clone() - .into(), - features: defaults.buffer_font_features.clone().unwrap(), - fallbacks: font_fallbacks_from_settings(defaults.buffer_font_fallbacks.clone()), - weight: defaults.buffer_font_weight.map(FontWeight).unwrap(), + family: content.buffer_font_family.as_ref()?.0.clone().into(), + features: content.buffer_font_features.clone()?, + fallbacks: font_fallbacks_from_settings(content.buffer_font_fallbacks.clone()), + weight: content.buffer_font_weight.map(FontWeight)?, style: FontStyle::default(), }, - buffer_font_size: defaults.buffer_font_size.unwrap().into(), - buffer_line_height: defaults.buffer_line_height.unwrap(), - agent_font_size: defaults.agent_font_size.flatten().map(Into::into), - theme_selection: defaults.theme.clone(), + buffer_font_size: content.buffer_font_size?.into(), + buffer_line_height: content.buffer_line_height?.into(), + agent_font_size: content.agent_font_size.flatten().map(Into::into), active_theme: themes - .get(defaults.theme.as_ref().unwrap().theme(*system_appearance)) + .get(theme_selection.theme(*system_appearance)) .or(themes.get(&zed_default_dark().name)) .unwrap(), + theme_selection: Some(theme_selection), experimental_theme_overrides: None, theme_overrides: HashMap::default(), - icon_theme_selection: defaults.icon_theme.clone(), - active_icon_theme: defaults - .icon_theme - .as_ref() - .and_then(|selection| { - themes - .get_icon_theme(selection.icon_theme(*system_appearance)) - .ok() - }) - .unwrap_or_else(|| themes.get_icon_theme(DEFAULT_ICON_THEME_NAME).unwrap()), - ui_density: defaults.ui_density.unwrap_or(UiDensity::Default), - unnecessary_code_fade: defaults.unnecessary_code_fade.unwrap_or(0.0), + active_icon_theme: themes + .get_icon_theme(icon_theme_selection.icon_theme(*system_appearance)) + .ok()?, + icon_theme_selection: Some(icon_theme_selection), + ui_density: content.ui_density?, + unnecessary_code_fade: content.unnecessary_code_fade?, }; - for value in sources - .user - .into_iter() - .chain(sources.release_channel) - .chain(sources.operating_system) - .chain(sources.profile) - .chain(sources.server) - { - if let Some(value) = value.ui_density { - this.ui_density = value; - } - - if let Some(value) = value.buffer_font_family.clone() { - this.buffer_font.family = value.0.into(); - } - if let Some(value) = value.buffer_font_features.clone() { - this.buffer_font.features = value; - } - if let Some(value) = value.buffer_font_fallbacks.clone() { - this.buffer_font.fallbacks = font_fallbacks_from_settings(Some(value)); - } - if let Some(value) = value.buffer_font_weight { - this.buffer_font.weight = clamp_font_weight(value); - } + Some(this) + } - if let Some(value) = value.ui_font_family.clone() { - this.ui_font.family = value.0.into(); - } - if let Some(value) = value.ui_font_features.clone() { - this.ui_font.features = value; - } - if let Some(value) = value.ui_font_fallbacks.clone() { - this.ui_font.fallbacks = font_fallbacks_from_settings(Some(value)); - } - if let Some(value) = value.ui_font_weight { - this.ui_font.weight = clamp_font_weight(value); - } + fn refine(&mut self, content: &SettingsContent, cx: &mut App) { + let value = &content.theme; - if let Some(value) = &value.theme { - this.theme_selection = Some(value.clone()); + let themes = ThemeRegistry::default_global(cx); + let system_appearance = SystemAppearance::default_global(cx); - let theme_name = value.theme(*system_appearance); + self.ui_density.merge_from(&value.ui_density); - match themes.get(theme_name) { - Ok(theme) => { - this.active_theme = theme; - } - Err(err @ ThemeNotFoundError(_)) => { - if themes.extensions_loaded() { - log::error!("{err}"); - } - } - } - } + if let Some(value) = value.buffer_font_family.clone() { + self.buffer_font.family = value.0.into(); + } + if let Some(value) = value.buffer_font_features.clone() { + self.buffer_font.features = value; + } + if let Some(value) = value.buffer_font_fallbacks.clone() { + self.buffer_font.fallbacks = font_fallbacks_from_settings(Some(value)); + } + if let Some(value) = value.buffer_font_weight { + self.buffer_font.weight = clamp_font_weight(value); + } - this.experimental_theme_overrides - .clone_from(&value.experimental_theme_overrides); - this.theme_overrides.clone_from(&value.theme_overrides); - this.apply_theme_overrides(); + if let Some(value) = value.ui_font_family.clone() { + self.ui_font.family = value.0.into(); + } + if let Some(value) = value.ui_font_features.clone() { + self.ui_font.features = value; + } + if let Some(value) = value.ui_font_fallbacks.clone() { + self.ui_font.fallbacks = font_fallbacks_from_settings(Some(value)); + } + if let Some(value) = value.ui_font_weight { + self.ui_font.weight = clamp_font_weight(value); + } - if let Some(value) = &value.icon_theme { - this.icon_theme_selection = Some(value.clone()); + if let Some(value) = &value.theme { + self.theme_selection = Some(value.clone().into()); - let icon_theme_name = value.icon_theme(*system_appearance); + let theme_name = self + .theme_selection + .as_ref() + .unwrap() + .theme(*system_appearance); - match themes.get_icon_theme(icon_theme_name) { - Ok(icon_theme) => { - this.active_icon_theme = icon_theme; - } - Err(err @ IconThemeNotFoundError(_)) => { - if themes.extensions_loaded() { - log::error!("{err}"); - } + match themes.get(theme_name) { + Ok(theme) => { + self.active_theme = theme; + } + Err(err @ ThemeNotFoundError(_)) => { + if themes.extensions_loaded() { + log::error!("{err}"); } } } - - merge( - &mut this.ui_font_size, - value.ui_font_size.map(Into::into).map(clamp_font_size), - ); - merge( - &mut this.buffer_font_size, - value.buffer_font_size.map(Into::into).map(clamp_font_size), - ); - merge( - &mut this.agent_font_size, - value - .agent_font_size - .map(|value| value.map(Into::into).map(clamp_font_size)), - ); - - merge(&mut this.buffer_line_height, value.buffer_line_height); - - // Clamp the `unnecessary_code_fade` to ensure text can't disappear entirely. - merge(&mut this.unnecessary_code_fade, value.unnecessary_code_fade); - this.unnecessary_code_fade = this.unnecessary_code_fade.clamp(0.0, 0.9); } - Ok(this) - } + self.experimental_theme_overrides + .clone_from(&value.experimental_theme_overrides); + self.theme_overrides.clone_from(&value.theme_overrides); - fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) { - vscode.f32_setting("editor.fontWeight", &mut current.buffer_font_weight); - vscode.f32_setting("editor.fontSize", &mut current.buffer_font_size); - if let Some(font) = vscode.read_string("editor.font") { - current.buffer_font_family = Some(FontFamilyName(font.into())); - } - // TODO: possibly map editor.fontLigatures to buffer_font_features? - } -} + self.apply_theme_overrides(); -/// Newtype for a theme name. Its `ParameterizedJsonSchema` lists the theme names known at runtime. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(transparent)] -pub struct ThemeName(pub Arc); - -inventory::submit! { - ParameterizedJsonSchema { - add_and_get_ref: |generator, _params, cx| { - replace_subschema::(generator, || json_schema!({ - "type": "string", - "enum": ThemeRegistry::global(cx).list_names(), - })) - } - } -} + if let Some(value) = &value.icon_theme { + self.icon_theme_selection = Some(value.clone().into()); -/// Newtype for a icon theme name. Its `ParameterizedJsonSchema` lists the icon theme names known at -/// runtime. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(transparent)] -pub struct IconThemeName(pub Arc); - -inventory::submit! { - ParameterizedJsonSchema { - add_and_get_ref: |generator, _params, cx| { - replace_subschema::(generator, || json_schema!({ - "type": "string", - "enum": ThemeRegistry::global(cx) - .list_icon_themes() - .into_iter() - .map(|icon_theme| icon_theme.name) - .collect::>(), - })) - } - } -} + let icon_theme_name = self + .icon_theme_selection + .as_ref() + .unwrap() + .icon_theme(*system_appearance); -/// Newtype for font family name. Its `ParameterizedJsonSchema` lists the font families known at -/// runtime. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(transparent)] -pub struct FontFamilyName(pub Arc); - -inventory::submit! { - ParameterizedJsonSchema { - add_and_get_ref: |generator, params, _cx| { - replace_subschema::(generator, || { - json_schema!({ - "type": "string", - "enum": params.font_names, - }) - }) + match themes.get_icon_theme(icon_theme_name) { + Ok(icon_theme) => { + self.active_icon_theme = icon_theme; + } + Err(err @ IconThemeNotFoundError(_)) => { + if themes.extensions_loaded() { + log::error!("{err}"); + } + } + } } - } -} -fn merge(target: &mut T, value: Option) { - if let Some(value) = value { - *target = value; - } -} + self.ui_font_size + .merge_from(&value.ui_font_size.map(Into::into).map(clamp_font_size)); + self.buffer_font_size + .merge_from(&value.buffer_font_size.map(Into::into).map(clamp_font_size)); + self.agent_font_size.merge_from( + &value + .agent_font_size + .map(|value| value.map(Into::into).map(clamp_font_size)), + ); -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; + self.buffer_line_height + .merge_from(&value.buffer_line_height.map(Into::into)); - #[test] - fn test_buffer_line_height_deserialize_valid() { - assert_eq!( - serde_json::from_value::(json!("comfortable")).unwrap(), - BufferLineHeight::Comfortable - ); - assert_eq!( - serde_json::from_value::(json!("standard")).unwrap(), - BufferLineHeight::Standard - ); - assert_eq!( - serde_json::from_value::(json!({"custom": 1.0})).unwrap(), - BufferLineHeight::Custom(1.0) - ); - assert_eq!( - serde_json::from_value::(json!({"custom": 1.5})).unwrap(), - BufferLineHeight::Custom(1.5) - ); + // Clamp the `unnecessary_code_fade` to ensure text can't disappear entirely. + self.unnecessary_code_fade + .merge_from(&value.unnecessary_code_fade); + self.unnecessary_code_fade = self.unnecessary_code_fade.clamp(0.0, 0.9); } - #[test] - fn test_buffer_line_height_deserialize_invalid() { - assert!( - serde_json::from_value::(json!({"custom": 0.99})) - .err() - .unwrap() - .to_string() - .contains("buffer_line_height.custom must be at least 1.0") - ); - assert!( - serde_json::from_value::(json!({"custom": 0.0})) - .err() - .unwrap() - .to_string() - .contains("buffer_line_height.custom must be at least 1.0") - ); - assert!( - serde_json::from_value::(json!({"custom": -1.0})) - .err() - .unwrap() - .to_string() - .contains("buffer_line_height.custom must be at least 1.0") - ); + fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) { + vscode.f32_setting("editor.fontWeight", &mut current.theme.buffer_font_weight); + vscode.f32_setting("editor.fontSize", &mut current.theme.buffer_font_size); + if let Some(font) = vscode.read_string("editor.font") { + current.theme.buffer_font_family = Some(FontFamilyName(font.into())); + } + // TODO: possibly map editor.fontLigatures to buffer_font_features? } } diff --git a/crates/theme/src/styles/accents.rs b/crates/theme/src/styles/accents.rs index 52244fee46ce12edbbe361c3bec9bb80139f1167..5eda83637d2b6ebb736ba6bb2f3f80cf557bf847 100644 --- a/crates/theme/src/styles/accents.rs +++ b/crates/theme/src/styles/accents.rs @@ -66,7 +66,7 @@ impl AccentColors { } /// Merges the given accent colors into this [`AccentColors`] instance. - pub fn merge(&mut self, accent_colors: &[AccentContent]) { + pub fn merge(&mut self, accent_colors: &[settings::AccentContent]) { if accent_colors.is_empty() { return; } diff --git a/crates/theme/src/styles/players.rs b/crates/theme/src/styles/players.rs index d10ac61ba6bfeabbf0642d832e68841c39176546..db66fbe212cc407679f69acff4f27710835babd6 100644 --- a/crates/theme/src/styles/players.rs +++ b/crates/theme/src/styles/players.rs @@ -152,7 +152,7 @@ impl PlayerColors { } /// Merges the given player colors into this [`PlayerColors`] instance. - pub fn merge(&mut self, user_player_colors: &[PlayerColorContent]) { + pub fn merge(&mut self, user_player_colors: &[settings::PlayerColorContent]) { if user_player_colors.is_empty() { return; } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index c54010b4b0c6fe56168c3e15271d3423921b15da..c17761b7dfd828337132fb2c21b716fa0a24d8b6 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -178,7 +178,7 @@ impl ThemeFamily { AppearanceContent::Light => StatusColors::light(), AppearanceContent::Dark => StatusColors::dark(), }; - let mut status_colors_refinement = theme.style.status_colors_refinement(); + let mut status_colors_refinement = status_colors_refinement(&theme.style.status); apply_status_color_defaults(&mut status_colors_refinement); refined_status_colors.refine(&status_colors_refinement); @@ -192,7 +192,8 @@ impl ThemeFamily { AppearanceContent::Light => ThemeColors::light(), AppearanceContent::Dark => ThemeColors::dark(), }; - let mut theme_colors_refinement = theme.style.theme_colors_refinement(); + let mut theme_colors_refinement = + theme_colors_refinement(&theme.style.colors, &status_colors_refinement); apply_theme_color_defaults(&mut theme_colors_refinement, &refined_player_colors); refined_theme_colors.refine(&theme_colors_refinement); diff --git a/crates/title_bar/src/title_bar_settings.rs b/crates/title_bar/src/title_bar_settings.rs index 1af7c4547493e704c002bb08a2e4a862bd192931..01939211c4f3c40dbeb17bbd0b9dd62a2247e723 100644 --- a/crates/title_bar/src/title_bar_settings.rs +++ b/crates/title_bar/src/title_bar_settings.rs @@ -38,7 +38,7 @@ impl Settings for TitleBarSettings { } } - fn refine(&mut self, s: &SettingsContent) { + fn refine(&mut self, s: &SettingsContent, _: &mut App) { let Some(content) = s.title_bar else { return } diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 986b0c8ac822fe0ebb981a6bd0d168f915109f0a..298d7cc846dad8c3a0727af76d5d93d3be95079e 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -1388,12 +1388,12 @@ pub fn refine(dest: &mut T, src: &Option) { } } -pub trait Refine: Sized + Clone { - fn refine(&mut self, src: &Option); +pub trait MergeFrom: Sized + Clone { + fn merge_from(&mut self, src: &Option); } -impl Refine for T { - fn refine(&mut self, src: &Option) { +impl MergeFrom for T { + fn merge_from(&mut self, src: &Option) { if let Some(src) = src { *self = src.clone(); }