214 lines · 6.4 KB · Rust
RawDownload
use crate::{Theme, ThemeRegistry};use anyhow::Result;use gpui::{font_cache::FamilyId, fonts, AppContext};use schemars::{    gen::SchemaGenerator,    schema::{InstanceType, Schema, SchemaObject},    JsonSchema,};use serde::{Deserialize, Serialize};use serde_json::Value;use settings::SettingsJsonSchemaParams;use std::sync::Arc;use util::ResultExt as _; const MIN_FONT_SIZE: f32 = 6.0;const MIN_LINE_HEIGHT: f32 = 1.0; #[derive(Clone, JsonSchema)]pub struct ThemeSettings {    pub buffer_font_family_name: String,    pub buffer_font_features: fonts::Features,    pub buffer_font_family: FamilyId,    pub(crate) buffer_font_size: f32,    pub(crate) buffer_line_height: BufferLineHeight,    #[serde(skip)]    pub theme: Arc<Theme>,} pub struct AdjustedBufferFontSize(pub f32); #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]pub struct ThemeSettingsContent {    #[serde(default)]    pub buffer_font_family: Option<String>,    #[serde(default)]    pub buffer_font_size: Option<f32>,    #[serde(default)]    pub buffer_line_height: Option<BufferLineHeight>,    #[serde(default)]    pub buffer_font_features: Option<fonts::Features>,    #[serde(default)]    pub theme: Option<String>,} #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]#[serde(rename_all = "snake_case")]pub enum BufferLineHeight {    #[default]    Comfortable,    Standard,    Custom(f32),} impl BufferLineHeight {    pub fn value(&self) -> f32 {        match self {            BufferLineHeight::Comfortable => 1.618,            BufferLineHeight::Standard => 1.3,            BufferLineHeight::Custom(line_height) => *line_height,        }    }} impl ThemeSettings {    pub fn buffer_font_size(&self, cx: &AppContext) -> f32 {        if cx.has_global::<AdjustedBufferFontSize>() {            cx.global::<AdjustedBufferFontSize>().0        } else {            self.buffer_font_size        }        .max(MIN_FONT_SIZE)    }     pub fn line_height(&self) -> f32 {        f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT)    }} pub fn adjusted_font_size(size: f32, cx: &AppContext) -> f32 {    if cx.has_global::<AdjustedBufferFontSize>() {        let buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;        let delta = cx.global::<AdjustedBufferFontSize>().0 - buffer_font_size;        size + delta    } else {        size    }    .max(MIN_FONT_SIZE)} pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut f32)) {    if !cx.has_global::<AdjustedBufferFontSize>() {        let buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;        cx.set_global(AdjustedBufferFontSize(buffer_font_size));    }     cx.update_global::<AdjustedBufferFontSize, _, _>(|delta, cx| {        f(&mut delta.0);        delta.0 = delta            .0            .max(MIN_FONT_SIZE - settings::get::<ThemeSettings>(cx).buffer_font_size);    });    cx.refresh_windows();} pub fn reset_font_size(cx: &mut AppContext) {    if cx.has_global::<AdjustedBufferFontSize>() {        cx.remove_global::<AdjustedBufferFontSize>();        cx.refresh_windows();    }} impl settings::Setting for ThemeSettings {    const KEY: Option<&'static str> = None;     type FileContent = ThemeSettingsContent;     fn load(        defaults: &Self::FileContent,        user_values: &[&Self::FileContent],        cx: &AppContext,    ) -> Result<Self> {        let buffer_font_features = defaults.buffer_font_features.clone().unwrap();        let themes = cx.global::<Arc<ThemeRegistry>>();         let mut this = Self {            buffer_font_family: cx                .font_cache()                .load_family(                    &[defaults.buffer_font_family.as_ref().unwrap()],                    &buffer_font_features,                )                .unwrap(),            buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(),            buffer_font_features,            buffer_font_size: defaults.buffer_font_size.unwrap(),            buffer_line_height: defaults.buffer_line_height.unwrap(),            theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),        };         for value in user_values.into_iter().copied().cloned() {            let font_cache = cx.font_cache();            let mut family_changed = false;            if let Some(value) = value.buffer_font_family {                this.buffer_font_family_name = value;                family_changed = true;            }            if let Some(value) = value.buffer_font_features {                this.buffer_font_features = value;                family_changed = true;            }            if family_changed {                if let Some(id) = font_cache                    .load_family(&[&this.buffer_font_family_name], &this.buffer_font_features)                    .log_err()                {                    this.buffer_font_family = id;                }            }             if let Some(value) = &value.theme {                if let Some(theme) = themes.get(value).log_err() {                    this.theme = theme;                }            }             merge(&mut this.buffer_font_size, value.buffer_font_size);            merge(&mut this.buffer_line_height, value.buffer_line_height);        }         Ok(this)    }     fn json_schema(        generator: &mut SchemaGenerator,        params: &SettingsJsonSchemaParams,        cx: &AppContext,    ) -> schemars::schema::RootSchema {        let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();        let theme_names = cx            .global::<Arc<ThemeRegistry>>()            .list_names(params.staff_mode)            .map(|theme_name| Value::String(theme_name.to_string()))            .collect();         let theme_name_schema = SchemaObject {            instance_type: Some(InstanceType::String.into()),            enum_values: Some(theme_names),            ..Default::default()        };         root_schema            .definitions            .extend([("ThemeName".into(), theme_name_schema.into())]);         root_schema            .schema            .object            .as_mut()            .unwrap()            .properties            .extend([(                "theme".to_owned(),                Schema::new_ref("#/definitions/ThemeName".into()),            )]);         root_schema    }} fn merge<T: Copy>(target: &mut T, value: Option<T>) {    if let Some(value) = value {        *target = value;    }}