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    pub(crate) 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    if cx.has_global::<AdjustedBufferFontSize>() {
 79        cx.remove_global::<AdjustedBufferFontSize>();
 80        cx.refresh_windows();
 81    }
 82}
 83
 84impl settings::Setting for ThemeSettings {
 85    const KEY: Option<&'static str> = None;
 86
 87    type FileContent = ThemeSettingsContent;
 88
 89    fn load(
 90        defaults: &Self::FileContent,
 91        user_values: &[&Self::FileContent],
 92        cx: &AppContext,
 93    ) -> Result<Self> {
 94        let buffer_font_features = defaults.buffer_font_features.clone().unwrap();
 95        let themes = cx.global::<Arc<ThemeRegistry>>();
 96
 97        let mut this = Self {
 98            buffer_font_family: cx
 99                .font_cache()
100                .load_family(
101                    &[defaults.buffer_font_family.as_ref().unwrap()],
102                    &buffer_font_features,
103                )
104                .unwrap(),
105            buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(),
106            buffer_font_features,
107            buffer_font_size: defaults.buffer_font_size.unwrap(),
108            theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
109        };
110
111        for value in user_values.into_iter().copied().cloned() {
112            let font_cache = cx.font_cache();
113            let mut family_changed = false;
114            if let Some(value) = value.buffer_font_family {
115                this.buffer_font_family_name = value;
116                family_changed = true;
117            }
118            if let Some(value) = value.buffer_font_features {
119                this.buffer_font_features = value;
120                family_changed = true;
121            }
122            if family_changed {
123                if let Some(id) = font_cache
124                    .load_family(&[&this.buffer_font_family_name], &this.buffer_font_features)
125                    .log_err()
126                {
127                    this.buffer_font_family = id;
128                }
129            }
130
131            if let Some(value) = &value.theme {
132                if let Some(theme) = themes.get(value).log_err() {
133                    this.theme = theme;
134                }
135            }
136
137            merge(&mut this.buffer_font_size, value.buffer_font_size);
138        }
139
140        Ok(this)
141    }
142
143    fn json_schema(
144        generator: &mut SchemaGenerator,
145        params: &SettingsJsonSchemaParams,
146        cx: &AppContext,
147    ) -> schemars::schema::RootSchema {
148        let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();
149        let theme_names = cx
150            .global::<Arc<ThemeRegistry>>()
151            .list(params.staff_mode)
152            .map(|theme| Value::String(theme.name.clone()))
153            .collect();
154
155        let theme_name_schema = SchemaObject {
156            instance_type: Some(InstanceType::String.into()),
157            enum_values: Some(theme_names),
158            ..Default::default()
159        };
160
161        root_schema
162            .definitions
163            .extend([("ThemeName".into(), theme_name_schema.into())]);
164
165        root_schema
166            .schema
167            .object
168            .as_mut()
169            .unwrap()
170            .properties
171            .extend([(
172                "theme".to_owned(),
173                Schema::new_ref("#/definitions/ThemeName".into()),
174            )]);
175
176        root_schema
177    }
178}
179
180fn merge<T: Copy>(target: &mut T, value: Option<T>) {
181    if let Some(value) = value {
182        *target = value;
183    }
184}