741 lines · 26.8 KB · Rust
RawDownload
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.