1use gpui::{AppContext, FontWeight};
2use project::project_settings::{InlineBlameSettings, ProjectSettings};
3use settings::{EditableSettingControl, Settings};
4use theme::{FontFamilyCache, ThemeSettings};
5use ui::{
6 prelude::*, CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer,
7 SettingsGroup,
8};
9
10#[derive(IntoElement)]
11pub struct EditorSettingsControls {}
12
13impl EditorSettingsControls {
14 pub fn new() -> Self {
15 Self {}
16 }
17}
18
19impl RenderOnce for EditorSettingsControls {
20 fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
21 SettingsContainer::new()
22 .child(
23 SettingsGroup::new("Font")
24 .child(
25 h_flex()
26 .gap_2()
27 .justify_between()
28 .child(BufferFontFamilyControl)
29 .child(BufferFontWeightControl),
30 )
31 .child(BufferFontSizeControl),
32 )
33 .child(SettingsGroup::new("Editor").child(InlineGitBlameControl))
34 }
35}
36
37#[derive(IntoElement)]
38struct BufferFontFamilyControl;
39
40impl EditableSettingControl for BufferFontFamilyControl {
41 type Value = SharedString;
42 type Settings = ThemeSettings;
43
44 fn name(&self) -> SharedString {
45 "Buffer Font Family".into()
46 }
47
48 fn read(cx: &AppContext) -> Self::Value {
49 let settings = ThemeSettings::get_global(cx);
50 settings.buffer_font.family.clone()
51 }
52
53 fn apply(
54 settings: &mut <Self::Settings as Settings>::FileContent,
55 value: Self::Value,
56 _cx: &AppContext,
57 ) {
58 settings.buffer_font_family = Some(value.to_string());
59 }
60}
61
62impl RenderOnce for BufferFontFamilyControl {
63 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
64 let value = Self::read(cx);
65
66 h_flex()
67 .gap_2()
68 .child(Icon::new(IconName::Font))
69 .child(DropdownMenu::new(
70 "buffer-font-family",
71 value.clone(),
72 ContextMenu::build(cx, |mut menu, cx| {
73 let font_family_cache = FontFamilyCache::global(cx);
74
75 for font_name in font_family_cache.list_font_families(cx) {
76 menu = menu.custom_entry(
77 {
78 let font_name = font_name.clone();
79 move |_cx| Label::new(font_name.clone()).into_any_element()
80 },
81 {
82 let font_name = font_name.clone();
83 move |cx| {
84 Self::write(font_name.clone(), cx);
85 }
86 },
87 )
88 }
89
90 menu
91 }),
92 ))
93 }
94}
95
96#[derive(IntoElement)]
97struct BufferFontSizeControl;
98
99impl EditableSettingControl for BufferFontSizeControl {
100 type Value = Pixels;
101 type Settings = ThemeSettings;
102
103 fn name(&self) -> SharedString {
104 "Buffer Font Size".into()
105 }
106
107 fn read(cx: &AppContext) -> Self::Value {
108 let settings = ThemeSettings::get_global(cx);
109 settings.buffer_font_size
110 }
111
112 fn apply(
113 settings: &mut <Self::Settings as Settings>::FileContent,
114 value: Self::Value,
115 _cx: &AppContext,
116 ) {
117 settings.buffer_font_size = Some(value.into());
118 }
119}
120
121impl RenderOnce for BufferFontSizeControl {
122 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
123 let value = Self::read(cx);
124
125 h_flex()
126 .gap_2()
127 .child(Icon::new(IconName::FontSize))
128 .child(NumericStepper::new(
129 value.to_string(),
130 move |_, cx| {
131 Self::write(value - px(1.), cx);
132 },
133 move |_, cx| {
134 Self::write(value + px(1.), cx);
135 },
136 ))
137 }
138}
139
140#[derive(IntoElement)]
141struct BufferFontWeightControl;
142
143impl EditableSettingControl for BufferFontWeightControl {
144 type Value = FontWeight;
145 type Settings = ThemeSettings;
146
147 fn name(&self) -> SharedString {
148 "Buffer Font Weight".into()
149 }
150
151 fn read(cx: &AppContext) -> Self::Value {
152 let settings = ThemeSettings::get_global(cx);
153 settings.buffer_font.weight
154 }
155
156 fn apply(
157 settings: &mut <Self::Settings as Settings>::FileContent,
158 value: Self::Value,
159 _cx: &AppContext,
160 ) {
161 settings.buffer_font_weight = Some(value.0);
162 }
163}
164
165impl RenderOnce for BufferFontWeightControl {
166 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
167 let value = Self::read(cx);
168
169 h_flex()
170 .gap_2()
171 .child(Icon::new(IconName::FontWeight))
172 .child(DropdownMenu::new(
173 "buffer-font-weight",
174 value.0.to_string(),
175 ContextMenu::build(cx, |mut menu, _cx| {
176 for weight in FontWeight::ALL {
177 menu = menu.custom_entry(
178 move |_cx| Label::new(weight.0.to_string()).into_any_element(),
179 {
180 move |cx| {
181 Self::write(weight, cx);
182 }
183 },
184 )
185 }
186
187 menu
188 }),
189 ))
190 }
191}
192
193#[derive(IntoElement)]
194struct InlineGitBlameControl;
195
196impl EditableSettingControl for InlineGitBlameControl {
197 type Value = bool;
198 type Settings = ProjectSettings;
199
200 fn name(&self) -> SharedString {
201 "Inline Git Blame".into()
202 }
203
204 fn read(cx: &AppContext) -> Self::Value {
205 let settings = ProjectSettings::get_global(cx);
206 settings.git.inline_blame_enabled()
207 }
208
209 fn apply(
210 settings: &mut <Self::Settings as Settings>::FileContent,
211 value: Self::Value,
212 _cx: &AppContext,
213 ) {
214 if let Some(inline_blame) = settings.git.inline_blame.as_mut() {
215 inline_blame.enabled = value;
216 } else {
217 settings.git.inline_blame = Some(InlineBlameSettings {
218 enabled: false,
219 ..Default::default()
220 });
221 }
222 }
223}
224
225impl RenderOnce for InlineGitBlameControl {
226 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
227 let value = Self::read(cx);
228
229 CheckboxWithLabel::new(
230 "inline-git-blame",
231 Label::new(self.name()),
232 value.into(),
233 |selection, cx| {
234 Self::write(
235 match selection {
236 Selection::Selected => true,
237 Selection::Unselected | Selection::Indeterminate => false,
238 },
239 cx,
240 );
241 },
242 )
243 }
244}