theme_settings.rs

  1use crate::{Theme, ThemeRegistry};
  2use anyhow::Result;
  3use gpui::{font_cache::FamilyId, fonts, AppContext};
  4use schemars::{
  5    gen::SchemaGenerator,
  6    schema::{InstanceType, Schema, SchemaObject},
  7    JsonSchema,
  8};
  9use serde::{Deserialize, Serialize};
 10use serde_json::Value;
 11use settings::SettingsJsonSchemaParams;
 12use std::sync::Arc;
 13use util::ResultExt as _;
 14
 15const MIN_FONT_SIZE: f32 = 6.0;
 16
 17#[derive(Clone, JsonSchema)]
 18pub struct ThemeSettings {
 19    pub buffer_font_family_name: String,
 20    pub buffer_font_features: fonts::Features,
 21    pub buffer_font_family: FamilyId,
 22    pub(crate) buffer_font_size: f32,
 23    #[serde(skip)]
 24    pub theme: Arc<Theme>,
 25}
 26
 27pub struct AdjustedBufferFontSize(pub f32);
 28
 29#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 30pub struct ThemeSettingsContent {
 31    #[serde(default)]
 32    pub buffer_font_family: Option<String>,
 33    #[serde(default)]
 34    pub buffer_font_size: Option<f32>,
 35    #[serde(default)]
 36    pub buffer_font_features: Option<fonts::Features>,
 37    #[serde(default)]
 38    pub theme: Option<String>,
 39}
 40
 41impl ThemeSettings {
 42    pub fn buffer_font_size(&self, cx: &AppContext) -> f32 {
 43        if cx.has_global::<AdjustedBufferFontSize>() {
 44            cx.global::<AdjustedBufferFontSize>().0
 45        } else {
 46            self.buffer_font_size
 47        }
 48        .max(MIN_FONT_SIZE)
 49    }
 50}
 51
 52pub fn adjusted_font_size(size: f32, cx: &AppContext) -> f32 {
 53    if cx.has_global::<AdjustedBufferFontSize>() {
 54        let buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;
 55        let delta = cx.global::<AdjustedBufferFontSize>().0 - buffer_font_size;
 56        size + delta
 57    } else {
 58        size
 59    }
 60    .max(MIN_FONT_SIZE)
 61}
 62
 63pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut f32)) {
 64    if !cx.has_global::<AdjustedBufferFontSize>() {
 65        let buffer_font_size = settings::get::<ThemeSettings>(cx).buffer_font_size;
 66        cx.set_global(AdjustedBufferFontSize(buffer_font_size));
 67    }
 68
 69    cx.update_global::<AdjustedBufferFontSize, _, _>(|delta, cx| {
 70        f(&mut delta.0);
 71        delta.0 = delta
 72            .0
 73            .max(MIN_FONT_SIZE - settings::get::<ThemeSettings>(cx).buffer_font_size);
 74    });
 75    cx.refresh_windows();
 76}
 77
 78pub fn reset_font_size(cx: &mut AppContext) {
 79    if cx.has_global::<AdjustedBufferFontSize>() {
 80        cx.remove_global::<AdjustedBufferFontSize>();
 81        cx.refresh_windows();
 82    }
 83}
 84
 85impl settings::Setting for ThemeSettings {
 86    const KEY: Option<&'static str> = None;
 87
 88    type FileContent = ThemeSettingsContent;
 89
 90    fn load(
 91        defaults: &Self::FileContent,
 92        user_values: &[&Self::FileContent],
 93        cx: &AppContext,
 94    ) -> Result<Self> {
 95        let buffer_font_features = defaults.buffer_font_features.clone().unwrap();
 96        let themes = cx.global::<Arc<ThemeRegistry>>();
 97
 98        let mut this = Self {
 99            buffer_font_family: cx
100                .font_cache()
101                .load_family(
102                    &[defaults.buffer_font_family.as_ref().unwrap()],
103                    &buffer_font_features,
104                )
105                .unwrap(),
106            buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(),
107            buffer_font_features,
108            buffer_font_size: defaults.buffer_font_size.unwrap(),
109            theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
110        };
111
112        for value in user_values.into_iter().copied().cloned() {
113            let font_cache = cx.font_cache();
114            let mut family_changed = false;
115            if let Some(value) = value.buffer_font_family {
116                this.buffer_font_family_name = value;
117                family_changed = true;
118            }
119            if let Some(value) = value.buffer_font_features {
120                this.buffer_font_features = value;
121                family_changed = true;
122            }
123            if family_changed {
124                if let Some(id) = font_cache
125                    .load_family(&[&this.buffer_font_family_name], &this.buffer_font_features)
126                    .log_err()
127                {
128                    this.buffer_font_family = id;
129                }
130            }
131
132            if let Some(value) = &value.theme {
133                if let Some(theme) = themes.get(value).log_err() {
134                    this.theme = theme;
135                }
136            }
137
138            merge(&mut this.buffer_font_size, value.buffer_font_size);
139        }
140
141        Ok(this)
142    }
143
144    fn json_schema(
145        generator: &mut SchemaGenerator,
146        params: &SettingsJsonSchemaParams,
147        cx: &AppContext,
148    ) -> schemars::schema::RootSchema {
149        let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();
150        let theme_names = cx
151            .global::<Arc<ThemeRegistry>>()
152            .list(params.staff_mode)
153            .map(|theme| Value::String(theme.name.clone()))
154            .collect();
155
156        let theme_name_schema = SchemaObject {
157            instance_type: Some(InstanceType::String.into()),
158            enum_values: Some(theme_names),
159            ..Default::default()
160        };
161
162        root_schema
163            .definitions
164            .extend([("ThemeName".into(), theme_name_schema.into())]);
165
166        root_schema
167            .schema
168            .object
169            .as_mut()
170            .unwrap()
171            .properties
172            .extend([(
173                "theme".to_owned(),
174                Schema::new_ref("#/definitions/ThemeName".into()),
175            )]);
176
177        root_schema
178    }
179}
180
181fn merge<T: Copy>(target: &mut T, value: Option<T>) {
182    if let Some(value) = value {
183        *target = value;
184    }
185}