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