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
15#[derive(Clone)]
16pub struct ThemeSettings {
17 pub buffer_font_family_name: String,
18 pub buffer_font_features: fonts::Features,
19 pub buffer_font_family: FamilyId,
20 pub buffer_font_size: f32,
21 pub theme: Arc<Theme>,
22}
23
24#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
25pub struct ThemeSettingsContent {
26 #[serde(default)]
27 pub buffer_font_family: Option<String>,
28 #[serde(default)]
29 pub buffer_font_size: Option<f32>,
30 #[serde(default)]
31 pub buffer_font_features: Option<fonts::Features>,
32 #[serde(default)]
33 pub theme: Option<String>,
34}
35
36impl settings::Setting for ThemeSettings {
37 const KEY: Option<&'static str> = None;
38
39 type FileContent = ThemeSettingsContent;
40
41 fn load(
42 defaults: &Self::FileContent,
43 user_values: &[&Self::FileContent],
44 cx: &AppContext,
45 ) -> Result<Self> {
46 let buffer_font_features = defaults.buffer_font_features.clone().unwrap();
47 let themes = cx.global::<Arc<ThemeRegistry>>();
48
49 let mut this = Self {
50 buffer_font_family: cx
51 .font_cache()
52 .load_family(
53 &[defaults.buffer_font_family.as_ref().unwrap()],
54 &buffer_font_features,
55 )
56 .unwrap(),
57 buffer_font_family_name: defaults.buffer_font_family.clone().unwrap(),
58 buffer_font_features,
59 buffer_font_size: defaults.buffer_font_size.unwrap(),
60 theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
61 };
62
63 for value in user_values.into_iter().copied().cloned() {
64 let font_cache = cx.font_cache();
65 let mut family_changed = false;
66 if let Some(value) = value.buffer_font_family {
67 this.buffer_font_family_name = value;
68 family_changed = true;
69 }
70 if let Some(value) = value.buffer_font_features {
71 this.buffer_font_features = value;
72 family_changed = true;
73 }
74 if family_changed {
75 if let Some(id) = font_cache
76 .load_family(&[&this.buffer_font_family_name], &this.buffer_font_features)
77 .log_err()
78 {
79 this.buffer_font_family = id;
80 }
81 }
82
83 if let Some(value) = &value.theme {
84 if let Some(theme) = themes.get(value).log_err() {
85 this.theme = theme;
86 }
87 }
88
89 merge(&mut this.buffer_font_size, value.buffer_font_size);
90 }
91
92 Ok(this)
93 }
94
95 fn json_schema(
96 generator: &mut SchemaGenerator,
97 params: &SettingsJsonSchemaParams,
98 cx: &AppContext,
99 ) -> schemars::schema::RootSchema {
100 let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();
101 let theme_names = cx
102 .global::<Arc<ThemeRegistry>>()
103 .list(params.staff_mode)
104 .map(|theme| Value::String(theme.name.clone()))
105 .collect();
106
107 let theme_name_schema = SchemaObject {
108 instance_type: Some(InstanceType::String.into()),
109 enum_values: Some(theme_names),
110 ..Default::default()
111 };
112
113 root_schema
114 .definitions
115 .extend([("ThemeName".into(), theme_name_schema.into())]);
116
117 root_schema
118 .schema
119 .object
120 .as_mut()
121 .unwrap()
122 .properties
123 .extend([(
124 "theme".to_owned(),
125 Schema::new_ref("#/definitions/ThemeName".into()),
126 )]);
127
128 root_schema
129 }
130}
131
132fn merge<T: Copy>(target: &mut T, value: Option<T>) {
133 if let Some(value) = value {
134 *target = value;
135 }
136}