use crate::{ Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme, status_colors_refinement, syntax_overrides, theme_colors_refinement,};use collections::HashMap;use derive_more::{Deref, DerefMut};use gpui::{ App, Context, Font, FontFallbacks, FontStyle, Global, Pixels, Subscription, Window, px,};use refineable::Refineable;use schemars::JsonSchema;use serde::{Deserialize, Serialize};pub use settings::{FontFamilyName, IconThemeName, ThemeAppearanceMode, ThemeName};use settings::{IntoGpui, RegisterSetting, Settings, SettingsContent};use std::sync::Arc; const MIN_FONT_SIZE: Pixels = px(6.0);const MAX_FONT_SIZE: Pixels = px(100.0);const MIN_LINE_HEIGHT: f32 = 1.0; #[derive( Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize, JsonSchema,)] /// Specifies the density of the UI./// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)#[serde(rename_all = "snake_case")]pub enum UiDensity { /// A denser UI with tighter spacing and smaller elements. #[serde(alias = "compact")] Compact, #[default] #[serde(alias = "default")] /// The default UI density. Default, #[serde(alias = "comfortable")] /// A looser UI with more spacing and larger elements. Comfortable,} impl UiDensity { /// The spacing ratio of a given density. /// TODO: Standardize usage throughout the app or remove pub fn spacing_ratio(self) -> f32 { match self { UiDensity::Compact => 0.75, UiDensity::Default => 1.0, UiDensity::Comfortable => 1.25, } }} impl From<String> for UiDensity { fn from(s: String) -> Self { match s.as_str() { "compact" => Self::Compact, "default" => Self::Default, "comfortable" => Self::Comfortable, _ => Self::default(), } }} impl From<UiDensity> for String { fn from(val: UiDensity) -> Self { match val { UiDensity::Compact => "compact".to_string(), UiDensity::Default => "default".to_string(), UiDensity::Comfortable => "comfortable".to_string(), } }} impl From<settings::UiDensity> for UiDensity { fn from(val: settings::UiDensity) -> Self { match val { settings::UiDensity::Compact => Self::Compact, settings::UiDensity::Default => Self::Default, settings::UiDensity::Comfortable => Self::Comfortable, } }} /// Customizable settings for the UI and theme system.#[derive(Clone, PartialEq, RegisterSetting)]pub struct ThemeSettings { /// The UI font size. Determines the size of text in the UI, /// as well as the size of a [gpui::Rems] unit. /// /// Changing this will impact the size of all UI elements. ui_font_size: Pixels, /// The font used for UI elements. pub ui_font: Font, /// The font size used for buffers, and the terminal. /// /// The terminal font size can be overridden using it's own setting. buffer_font_size: Pixels, /// The font used for buffers, and the terminal. /// /// The terminal font family can be overridden using it's own setting. pub buffer_font: Font, /// The agent font size. Determines the size of text in the agent panel. Falls back to the UI font size if unset. agent_ui_font_size: Option<Pixels>, /// The agent buffer font size. Determines the size of user messages in the agent panel. agent_buffer_font_size: Option<Pixels>, /// The line height for buffers, and the terminal. /// /// Changing this may affect the spacing of some UI elements. /// /// The terminal font family can be overridden using it's own setting. pub buffer_line_height: BufferLineHeight, /// The current theme selection. pub theme: ThemeSelection, /// 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<settings::ThemeStyleContent>, /// Manual overrides per theme pub theme_overrides: HashMap<String, settings::ThemeStyleContent>, /// The current icon theme selection. pub icon_theme: IconThemeSelection, /// The density of the UI. /// Note: This setting is still experimental. See [this tracking issue]( pub ui_density: UiDensity, /// The amount of fading applied to unnecessary code. pub unnecessary_code_fade: f32,} /// Returns the name of the default theme for the given [`Appearance`].pub fn default_theme(appearance: Appearance) -> &'static str { match appearance { Appearance::Light => settings::DEFAULT_LIGHT_THEME, Appearance::Dark => settings::DEFAULT_DARK_THEME, }} /// The appearance of the system.#[derive(Debug, Clone, Copy, Deref)]pub struct SystemAppearance(pub Appearance); impl Default for SystemAppearance { fn default() -> Self { Self(Appearance::Dark) }} #[derive(Deref, DerefMut, Default)]struct GlobalSystemAppearance(SystemAppearance); impl Global for GlobalSystemAppearance {} impl SystemAppearance { /// Initializes the [`SystemAppearance`] for the application. pub fn init(cx: &mut App) { *cx.default_global::<GlobalSystemAppearance>() = GlobalSystemAppearance(SystemAppearance(cx.window_appearance().into())); } /// Returns the global [`SystemAppearance`]. pub fn global(cx: &App) -> Self { cx.global::<GlobalSystemAppearance>().0 } /// Returns a mutable reference to the global [`SystemAppearance`]. pub fn global_mut(cx: &mut App) -> &mut Self { cx.global_mut::<GlobalSystemAppearance>() }} #[derive(Default)]struct BufferFontSize(Pixels); impl Global for BufferFontSize {} #[derive(Default)]pub(crate) struct UiFontSize(Pixels); impl Global for UiFontSize {} /// In-memory override for the font size in the agent panel.#[derive(Default)]pub struct AgentFontSize(Pixels); impl Global for AgentFontSize {} /// Represents the selection of a theme, which can be either static or dynamic.#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]#[serde(untagged)]pub enum ThemeSelection { /// A static theme selection, represented by a single theme name. Static(ThemeName), /// A dynamic theme selection, which can change based the [ThemeMode]. Dynamic { /// The mode used to determine which theme to use. #[serde(default)] mode: ThemeAppearanceMode, /// The theme to use for light mode. light: ThemeName, /// The theme to use for dark mode. dark: ThemeName, },} impl From<settings::ThemeSelection> 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 { /// Returns the theme name for the selected [ThemeMode]. pub fn name(&self, system_appearance: Appearance) -> ThemeName { match self { Self::Static(theme) => theme.clone(), Self::Dynamic { mode, light, dark } => match mode { ThemeAppearanceMode::Light => light.clone(), ThemeAppearanceMode::Dark => dark.clone(), ThemeAppearanceMode::System => match system_appearance { Appearance::Light => light.clone(), Appearance::Dark => dark.clone(), }, }, } } /// Returns the [ThemeMode] for the [ThemeSelection]. pub fn mode(&self) -> Option<ThemeAppearanceMode> { match self { ThemeSelection::Static(_) => None, ThemeSelection::Dynamic { mode, .. } => Some(*mode), } }} /// Represents the selection of an icon theme, which can be either static or dynamic.#[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. mode: ThemeAppearanceMode, /// The icon theme to use for light mode. light: IconThemeName, /// The icon theme to use for dark mode. dark: IconThemeName, },} impl From<settings::IconThemeSelection> 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 name(&self, system_appearance: Appearance) -> IconThemeName { match self { Self::Static(theme) => theme.clone(), Self::Dynamic { mode, light, dark } => match mode { ThemeAppearanceMode::Light => light.clone(), ThemeAppearanceMode::Dark => dark.clone(), ThemeAppearanceMode::System => match system_appearance { Appearance::Light => light.clone(), Appearance::Dark => dark.clone(), }, }, } } /// Returns the [`ThemeMode`] for the [`IconThemeSelection`]. pub fn mode(&self) -> Option<ThemeAppearanceMode> { match self { IconThemeSelection::Static(_) => None, IconThemeSelection::Dynamic { mode, .. } => Some(*mode), } }} /// Sets the theme for the given appearance to the theme with the specified name.////// The caller should make sure that the [`Appearance`] matches the theme associated with the name.////// If the current [`ThemeAppearanceMode`] is set to [`System`] and the user's system [`Appearance`]/// is different than the new theme's [`Appearance`], this function will update the/// [`ThemeAppearanceMode`] to the new theme's appearance in order to display the new theme.////// [`System`]: ThemeAppearanceMode::Systempub fn set_theme( current: &mut SettingsContent, theme_name: impl Into<Arc<str>>, theme_appearance: Appearance, system_appearance: Appearance,) { let theme_name = ThemeName(theme_name.into()); let Some(selection) = current.theme.theme.as_mut() else { current.theme.theme = Some(settings::ThemeSelection::Static(theme_name)); return; }; match selection { settings::ThemeSelection::Static(theme) => { *theme = theme_name; } settings::ThemeSelection::Dynamic { mode, light, dark } => { // Update the appropriate theme slot based on appearance. match theme_appearance { Appearance::Light => *light = theme_name, Appearance::Dark => *dark = theme_name, } // Don't update the theme mode if it is set to system and the new theme has the same // appearance. let should_update_mode = !(mode == &ThemeAppearanceMode::System && theme_appearance == system_appearance); if should_update_mode { // Update the mode to the specified appearance (otherwise we might set the theme and // nothing gets updated because the system specified the other mode appearance). *mode = ThemeAppearanceMode::from(theme_appearance); } } }} /// Sets the icon theme for the given appearance to the icon theme with the specified name.pub fn set_icon_theme( current: &mut SettingsContent, icon_theme_name: IconThemeName, appearance: Appearance,) { if let Some(selection) = current.theme.icon_theme.as_mut() { let icon_theme_to_update = match selection { settings::IconThemeSelection::Static(theme) => theme, settings::IconThemeSelection::Dynamic { mode, light, dark } => match mode { ThemeAppearanceMode::Light => light, ThemeAppearanceMode::Dark => dark, ThemeAppearanceMode::System => match appearance { Appearance::Light => light, Appearance::Dark => dark, }, }, }; *icon_theme_to_update = icon_theme_name; } else { current.theme.icon_theme = Some(settings::IconThemeSelection::Static(icon_theme_name)); }} /// Sets the mode for the theme.pub fn set_mode(content: &mut SettingsContent, mode: ThemeAppearanceMode) { let theme = content.theme.as_mut(); if let Some(selection) = theme.theme.as_mut() { match selection { settings::ThemeSelection::Static(_) => { // If the theme was previously set to a single static theme, // reset to the default dynamic light/dark pair and let users // customize light/dark themes explicitly afterward. *selection = settings::ThemeSelection::Dynamic { mode: ThemeAppearanceMode::System, light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()), dark: ThemeName(settings::DEFAULT_DARK_THEME.into()), }; } settings::ThemeSelection::Dynamic { mode: mode_to_update, .. } => *mode_to_update = mode, } } else { theme.theme = Some(settings::ThemeSelection::Dynamic { mode, light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()), dark: ThemeName(settings::DEFAULT_DARK_THEME.into()), }); } if let Some(selection) = theme.icon_theme.as_mut() { match selection { settings::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. *selection = settings::IconThemeSelection::Dynamic { mode, light: icon_theme.clone(), dark: icon_theme.clone(), }; } settings::IconThemeSelection::Dynamic { mode: mode_to_update, .. } => *mode_to_update = mode, } } else { theme.icon_theme = Some(settings::IconThemeSelection::Static(IconThemeName( DEFAULT_ICON_THEME_NAME.into(), ))); }}// } /// The buffer's line height.#[derive(Clone, Copy, Debug, PartialEq, Default)]pub enum BufferLineHeight { /// A less dense line height. #[default] Comfortable, /// The default line height. Standard, /// A custom line height, where 1.0 is the font's height. Must be at least 1.0. Custom(f32),} impl From<settings::BufferLineHeight> 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) } } }} impl BufferLineHeight { /// Returns the value of the line height. pub fn value(&self) -> f32 { match self { BufferLineHeight::Comfortable => 1.618, BufferLineHeight::Standard => 1.3, BufferLineHeight::Custom(line_height) => *line_height, } }} impl ThemeSettings { /// Returns the buffer font size. pub fn buffer_font_size(&self, cx: &App) -> Pixels { let font_size = cx .try_global::<BufferFontSize>() .map(|size| size.0) .unwrap_or(self.buffer_font_size); clamp_font_size(font_size) } /// Returns the UI font size. pub fn ui_font_size(&self, cx: &App) -> Pixels { let font_size = cx .try_global::<UiFontSize>() .map(|size| size.0) .unwrap_or(self.ui_font_size); clamp_font_size(font_size) } /// Returns the agent panel font size. Falls back to the UI font size if unset. pub fn agent_ui_font_size(&self, cx: &App) -> Pixels { cx.try_global::<AgentFontSize>() .map(|size| size.0) .or(self.agent_ui_font_size) .map(clamp_font_size) .unwrap_or_else(|| self.ui_font_size(cx)) } /// Returns the agent panel buffer font size. pub fn agent_buffer_font_size(&self, cx: &App) -> Pixels { cx.try_global::<AgentFontSize>() .map(|size| size.0) .or(self.agent_buffer_font_size) .map(clamp_font_size) .unwrap_or_else(|| self.buffer_font_size(cx)) } Lines 1–500 of 741. Load more
use crate::{ Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme, status_colors_refinement, syntax_overrides, theme_colors_refinement, }; use collections::HashMap; use derive_more::{Deref, DerefMut}; use gpui::{ App, Context, Font, FontFallbacks, FontStyle, Global, Pixels, Subscription, Window, px, }; use refineable::Refineable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub use settings::{FontFamilyName, IconThemeName, ThemeAppearanceMode, ThemeName}; use settings::{IntoGpui, RegisterSetting, Settings, SettingsContent}; use std::sync::Arc; const MIN_FONT_SIZE: Pixels = px(6.0); const MAX_FONT_SIZE: Pixels = px(100.0); const MIN_LINE_HEIGHT: f32 = 1.0; #[derive( Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize, JsonSchema, )] /// Specifies the density of the UI. /// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078) #[serde(rename_all = "snake_case")] pub enum UiDensity { /// A denser UI with tighter spacing and smaller elements. #[serde(alias = "compact")] Compact, #[default] #[serde(alias = "default")] /// The default UI density. Default, #[serde(alias = "comfortable")] /// A looser UI with more spacing and larger elements. Comfortable, } impl UiDensity { /// The spacing ratio of a given density. /// TODO: Standardize usage throughout the app or remove pub fn spacing_ratio(self) -> f32 { match self { UiDensity::Compact => 0.75, UiDensity::Default => 1.0, UiDensity::Comfortable => 1.25, } } } impl From<String> for UiDensity { fn from(s: String) -> Self { match s.as_str() { "compact" => Self::Compact, "default" => Self::Default, "comfortable" => Self::Comfortable, _ => Self::default(), } } } impl From<UiDensity> for String { fn from(val: UiDensity) -> Self { match val { UiDensity::Compact => "compact".to_string(), UiDensity::Default => "default".to_string(), UiDensity::Comfortable => "comfortable".to_string(), } } } impl From<settings::UiDensity> for UiDensity { fn from(val: settings::UiDensity) -> Self { match val { settings::UiDensity::Compact => Self::Compact, settings::UiDensity::Default => Self::Default, settings::UiDensity::Comfortable => Self::Comfortable, } } } /// Customizable settings for the UI and theme system. #[derive(Clone, PartialEq, RegisterSetting)] pub struct ThemeSettings { /// The UI font size. Determines the size of text in the UI, /// as well as the size of a [gpui::Rems] unit. /// /// Changing this will impact the size of all UI elements. ui_font_size: Pixels, /// The font used for UI elements. pub ui_font: Font, /// The font size used for buffers, and the terminal. /// /// The terminal font size can be overridden using it's own setting. buffer_font_size: Pixels, /// The font used for buffers, and the terminal. /// /// The terminal font family can be overridden using it's own setting. pub buffer_font: Font, /// The agent font size. Determines the size of text in the agent panel. Falls back to the UI font size if unset. agent_ui_font_size: Option<Pixels>, /// The agent buffer font size. Determines the size of user messages in the agent panel. agent_buffer_font_size: Option<Pixels>, /// The line height for buffers, and the terminal. /// /// Changing this may affect the spacing of some UI elements. /// /// The terminal font family can be overridden using it's own setting. pub buffer_line_height: BufferLineHeight, /// The current theme selection. pub theme: ThemeSelection, /// 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<settings::ThemeStyleContent>, /// Manual overrides per theme pub theme_overrides: HashMap<String, settings::ThemeStyleContent>, /// The current icon theme selection. pub icon_theme: IconThemeSelection, /// The density of the UI. /// Note: This setting is still experimental. See [this tracking issue]( pub ui_density: UiDensity, /// The amount of fading applied to unnecessary code. pub unnecessary_code_fade: f32, } /// Returns the name of the default theme for the given [`Appearance`]. pub fn default_theme(appearance: Appearance) -> &'static str { match appearance { Appearance::Light => settings::DEFAULT_LIGHT_THEME, Appearance::Dark => settings::DEFAULT_DARK_THEME, } } /// The appearance of the system. #[derive(Debug, Clone, Copy, Deref)] pub struct SystemAppearance(pub Appearance); impl Default for SystemAppearance { fn default() -> Self { Self(Appearance::Dark) } } #[derive(Deref, DerefMut, Default)] struct GlobalSystemAppearance(SystemAppearance); impl Global for GlobalSystemAppearance {} impl SystemAppearance { /// Initializes the [`SystemAppearance`] for the application. pub fn init(cx: &mut App) { *cx.default_global::<GlobalSystemAppearance>() = GlobalSystemAppearance(SystemAppearance(cx.window_appearance().into())); } /// Returns the global [`SystemAppearance`]. pub fn global(cx: &App) -> Self { cx.global::<GlobalSystemAppearance>().0 } /// Returns a mutable reference to the global [`SystemAppearance`]. pub fn global_mut(cx: &mut App) -> &mut Self { cx.global_mut::<GlobalSystemAppearance>() } } #[derive(Default)] struct BufferFontSize(Pixels); impl Global for BufferFontSize {} #[derive(Default)] pub(crate) struct UiFontSize(Pixels); impl Global for UiFontSize {} /// In-memory override for the font size in the agent panel. #[derive(Default)] pub struct AgentFontSize(Pixels); impl Global for AgentFontSize {} /// Represents the selection of a theme, which can be either static or dynamic. #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(untagged)] pub enum ThemeSelection { /// A static theme selection, represented by a single theme name. Static(ThemeName), /// A dynamic theme selection, which can change based the [ThemeMode]. Dynamic { /// The mode used to determine which theme to use. #[serde(default)] mode: ThemeAppearanceMode, /// The theme to use for light mode. light: ThemeName, /// The theme to use for dark mode. dark: ThemeName, }, } impl From<settings::ThemeSelection> 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 { /// Returns the theme name for the selected [ThemeMode]. pub fn name(&self, system_appearance: Appearance) -> ThemeName { match self { Self::Static(theme) => theme.clone(), Self::Dynamic { mode, light, dark } => match mode { ThemeAppearanceMode::Light => light.clone(), ThemeAppearanceMode::Dark => dark.clone(), ThemeAppearanceMode::System => match system_appearance { Appearance::Light => light.clone(), Appearance::Dark => dark.clone(), }, }, } } /// Returns the [ThemeMode] for the [ThemeSelection]. pub fn mode(&self) -> Option<ThemeAppearanceMode> { match self { ThemeSelection::Static(_) => None, ThemeSelection::Dynamic { mode, .. } => Some(*mode), } } } /// Represents the selection of an icon theme, which can be either static or dynamic. #[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. mode: ThemeAppearanceMode, /// The icon theme to use for light mode. light: IconThemeName, /// The icon theme to use for dark mode. dark: IconThemeName, }, } impl From<settings::IconThemeSelection> 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 name(&self, system_appearance: Appearance) -> IconThemeName { match self { Self::Static(theme) => theme.clone(), Self::Dynamic { mode, light, dark } => match mode { ThemeAppearanceMode::Light => light.clone(), ThemeAppearanceMode::Dark => dark.clone(), ThemeAppearanceMode::System => match system_appearance { Appearance::Light => light.clone(), Appearance::Dark => dark.clone(), }, }, } } /// Returns the [`ThemeMode`] for the [`IconThemeSelection`]. pub fn mode(&self) -> Option<ThemeAppearanceMode> { match self { IconThemeSelection::Static(_) => None, IconThemeSelection::Dynamic { mode, .. } => Some(*mode), } } } /// Sets the theme for the given appearance to the theme with the specified name. /// /// The caller should make sure that the [`Appearance`] matches the theme associated with the name. /// /// If the current [`ThemeAppearanceMode`] is set to [`System`] and the user's system [`Appearance`] /// is different than the new theme's [`Appearance`], this function will update the /// [`ThemeAppearanceMode`] to the new theme's appearance in order to display the new theme. /// /// [`System`]: ThemeAppearanceMode::System pub fn set_theme( current: &mut SettingsContent, theme_name: impl Into<Arc<str>>, theme_appearance: Appearance, system_appearance: Appearance, ) { let theme_name = ThemeName(theme_name.into()); let Some(selection) = current.theme.theme.as_mut() else { current.theme.theme = Some(settings::ThemeSelection::Static(theme_name)); return; }; match selection { settings::ThemeSelection::Static(theme) => { *theme = theme_name; } settings::ThemeSelection::Dynamic { mode, light, dark } => { // Update the appropriate theme slot based on appearance. match theme_appearance { Appearance::Light => *light = theme_name, Appearance::Dark => *dark = theme_name, } // Don't update the theme mode if it is set to system and the new theme has the same // appearance. let should_update_mode = !(mode == &ThemeAppearanceMode::System && theme_appearance == system_appearance); if should_update_mode { // Update the mode to the specified appearance (otherwise we might set the theme and // nothing gets updated because the system specified the other mode appearance). *mode = ThemeAppearanceMode::from(theme_appearance); } } } } /// Sets the icon theme for the given appearance to the icon theme with the specified name. pub fn set_icon_theme( current: &mut SettingsContent, icon_theme_name: IconThemeName, appearance: Appearance, ) { if let Some(selection) = current.theme.icon_theme.as_mut() { let icon_theme_to_update = match selection { settings::IconThemeSelection::Static(theme) => theme, settings::IconThemeSelection::Dynamic { mode, light, dark } => match mode { ThemeAppearanceMode::Light => light, ThemeAppearanceMode::Dark => dark, ThemeAppearanceMode::System => match appearance { Appearance::Light => light, Appearance::Dark => dark, }, }, }; *icon_theme_to_update = icon_theme_name; } else { current.theme.icon_theme = Some(settings::IconThemeSelection::Static(icon_theme_name)); } } /// Sets the mode for the theme. pub fn set_mode(content: &mut SettingsContent, mode: ThemeAppearanceMode) { let theme = content.theme.as_mut(); if let Some(selection) = theme.theme.as_mut() { match selection { settings::ThemeSelection::Static(_) => { // If the theme was previously set to a single static theme, // reset to the default dynamic light/dark pair and let users // customize light/dark themes explicitly afterward. *selection = settings::ThemeSelection::Dynamic { mode: ThemeAppearanceMode::System, light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()), dark: ThemeName(settings::DEFAULT_DARK_THEME.into()), }; } settings::ThemeSelection::Dynamic { mode: mode_to_update, .. } => *mode_to_update = mode, } } else { theme.theme = Some(settings::ThemeSelection::Dynamic { mode, light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()), dark: ThemeName(settings::DEFAULT_DARK_THEME.into()), }); } if let Some(selection) = theme.icon_theme.as_mut() { match selection { settings::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. *selection = settings::IconThemeSelection::Dynamic { mode, light: icon_theme.clone(), dark: icon_theme.clone(), }; } settings::IconThemeSelection::Dynamic { mode: mode_to_update, .. } => *mode_to_update = mode, } } else { theme.icon_theme = Some(settings::IconThemeSelection::Static(IconThemeName( DEFAULT_ICON_THEME_NAME.into(), ))); } } // } /// The buffer's line height. #[derive(Clone, Copy, Debug, PartialEq, Default)] pub enum BufferLineHeight { /// A less dense line height. #[default] Comfortable, /// The default line height. Standard, /// A custom line height, where 1.0 is the font's height. Must be at least 1.0. Custom(f32), } impl From<settings::BufferLineHeight> 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) } } } } impl BufferLineHeight { /// Returns the value of the line height. pub fn value(&self) -> f32 { match self { BufferLineHeight::Comfortable => 1.618, BufferLineHeight::Standard => 1.3, BufferLineHeight::Custom(line_height) => *line_height, } } } impl ThemeSettings { /// Returns the buffer font size. pub fn buffer_font_size(&self, cx: &App) -> Pixels { let font_size = cx .try_global::<BufferFontSize>() .map(|size| size.0) .unwrap_or(self.buffer_font_size); clamp_font_size(font_size) } /// Returns the UI font size. pub fn ui_font_size(&self, cx: &App) -> Pixels { let font_size = cx .try_global::<UiFontSize>() .map(|size| size.0) .unwrap_or(self.ui_font_size); clamp_font_size(font_size) } /// Returns the agent panel font size. Falls back to the UI font size if unset. pub fn agent_ui_font_size(&self, cx: &App) -> Pixels { cx.try_global::<AgentFontSize>() .map(|size| size.0) .or(self.agent_ui_font_size) .map(clamp_font_size) .unwrap_or_else(|| self.ui_font_size(cx)) } /// Returns the agent panel buffer font size. pub fn agent_buffer_font_size(&self, cx: &App) -> Pixels { cx.try_global::<AgentFontSize>() .map(|size| size.0) .or(self.agent_buffer_font_size) .map(clamp_font_size) .unwrap_or_else(|| self.buffer_font_size(cx)) }