1use editor::{EditorSettings, ShowMinimap};
2use fs::Fs;
3use gpui::{Action, App, IntoElement, Pixels, Window};
4use language::language_settings::AllLanguageSettings;
5use project::project_settings::ProjectSettings;
6use settings::{Settings as _, update_settings_file};
7use theme::{FontFamilyCache, FontFamilyName, ThemeSettings};
8use ui::{
9 Clickable, ContextMenu, DropdownMenu, IconButton, Label, LabelCommon, LabelSize,
10 NumericStepper, ParentElement, SharedString, Styled, SwitchColor, SwitchField,
11 ToggleButtonGroup, ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, div, h_flex, px,
12 v_flex,
13};
14
15use crate::{ImportCursorSettings, ImportVsCodeSettings};
16
17fn read_show_mini_map(cx: &App) -> ShowMinimap {
18 editor::EditorSettings::get_global(cx).minimap.show
19}
20
21fn write_show_mini_map(show: ShowMinimap, cx: &mut App) {
22 let fs = <dyn Fs>::global(cx);
23
24 // This is used to speed up the UI
25 // the UI reads the current values to get what toggle state to show on buttons
26 // there's a slight delay if we just call update_settings_file so we manually set
27 // the value here then call update_settings file to get around the delay
28 let mut curr_settings = EditorSettings::get_global(cx).clone();
29 curr_settings.minimap.show = show;
30 EditorSettings::override_global(curr_settings, cx);
31
32 update_settings_file::<EditorSettings>(fs, cx, move |editor_settings, _| {
33 editor_settings.minimap.get_or_insert_default().show = Some(show);
34 });
35}
36
37fn read_inlay_hints(cx: &App) -> bool {
38 AllLanguageSettings::get_global(cx)
39 .defaults
40 .inlay_hints
41 .enabled
42}
43
44fn write_inlay_hints(enabled: bool, cx: &mut App) {
45 let fs = <dyn Fs>::global(cx);
46
47 let mut curr_settings = AllLanguageSettings::get_global(cx).clone();
48 curr_settings.defaults.inlay_hints.enabled = enabled;
49 AllLanguageSettings::override_global(curr_settings, cx);
50
51 update_settings_file::<AllLanguageSettings>(fs, cx, move |all_language_settings, cx| {
52 all_language_settings
53 .defaults
54 .inlay_hints
55 .get_or_insert_with(|| {
56 AllLanguageSettings::get_global(cx)
57 .clone()
58 .defaults
59 .inlay_hints
60 })
61 .enabled = enabled;
62 });
63}
64
65fn read_git_blame(cx: &App) -> bool {
66 ProjectSettings::get_global(cx).git.inline_blame_enabled()
67}
68
69fn set_git_blame(enabled: bool, cx: &mut App) {
70 let fs = <dyn Fs>::global(cx);
71
72 let mut curr_settings = ProjectSettings::get_global(cx).clone();
73 curr_settings
74 .git
75 .inline_blame
76 .get_or_insert_default()
77 .enabled = enabled;
78 ProjectSettings::override_global(curr_settings, cx);
79
80 update_settings_file::<ProjectSettings>(fs, cx, move |project_settings, _| {
81 project_settings
82 .git
83 .inline_blame
84 .get_or_insert_default()
85 .enabled = enabled;
86 });
87}
88
89fn write_ui_font_family(font: SharedString, cx: &mut App) {
90 let fs = <dyn Fs>::global(cx);
91
92 update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
93 theme_settings.ui_font_family = Some(FontFamilyName(font.into()));
94 });
95}
96
97fn write_ui_font_size(size: Pixels, cx: &mut App) {
98 let fs = <dyn Fs>::global(cx);
99
100 update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
101 theme_settings.ui_font_size = Some(size.into());
102 });
103}
104
105fn write_buffer_font_size(size: Pixels, cx: &mut App) {
106 let fs = <dyn Fs>::global(cx);
107
108 update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
109 theme_settings.buffer_font_size = Some(size.into());
110 });
111}
112
113fn write_buffer_font_family(font_family: SharedString, cx: &mut App) {
114 let fs = <dyn Fs>::global(cx);
115
116 update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
117 theme_settings.buffer_font_family = Some(FontFamilyName(font_family.into()));
118 });
119}
120
121pub(crate) fn render_editing_page(window: &mut Window, cx: &mut App) -> impl IntoElement {
122 let theme_settings = ThemeSettings::get_global(cx);
123 let ui_font_size = theme_settings.ui_font_size(cx);
124 let font_family = theme_settings.buffer_font.family.clone();
125 let buffer_font_size = theme_settings.buffer_font_size(cx);
126
127 v_flex()
128 .gap_4()
129 .child(Label::new("Import Settings").size(LabelSize::Large))
130 .child(
131 Label::new("Automatically pull your settings from other editors.")
132 .size(LabelSize::Small),
133 )
134 .child(
135 h_flex()
136 .child(
137 IconButton::new("import-vs-code-settings", ui::IconName::Code).on_click(
138 |_, window, cx| {
139 window
140 .dispatch_action(ImportVsCodeSettings::default().boxed_clone(), cx)
141 },
142 ),
143 )
144 .child(
145 IconButton::new("import-cursor-settings", ui::IconName::CursorIBeam).on_click(
146 |_, window, cx| {
147 window
148 .dispatch_action(ImportCursorSettings::default().boxed_clone(), cx)
149 },
150 ),
151 ),
152 )
153 .child(Label::new("Popular Settings").size(LabelSize::Large))
154 .child(
155 h_flex()
156 .gap_4()
157 .justify_between()
158 .child(
159 v_flex()
160 .justify_between()
161 .gap_1()
162 .child(Label::new("UI Font"))
163 .child(
164 h_flex()
165 .justify_between()
166 .gap_2()
167 .child(div().min_w(px(120.)).child(DropdownMenu::new(
168 "ui-font-family",
169 theme_settings.ui_font.family.clone(),
170 ContextMenu::build(window, cx, |mut menu, _, cx| {
171 let font_family_cache = FontFamilyCache::global(cx);
172
173 for font_name in font_family_cache.list_font_families(cx) {
174 menu = menu.custom_entry(
175 {
176 let font_name = font_name.clone();
177 move |_window, _cx| {
178 Label::new(font_name.clone())
179 .into_any_element()
180 }
181 },
182 {
183 let font_name = font_name.clone();
184 move |_window, cx| {
185 write_ui_font_family(font_name.clone(), cx);
186 }
187 },
188 )
189 }
190
191 menu
192 }),
193 )))
194 .child(
195 NumericStepper::new(
196 "ui-font-size",
197 ui_font_size.to_string(),
198 move |_, _, cx| {
199 write_ui_font_size(ui_font_size - px(1.), cx);
200 },
201 move |_, _, cx| {
202 write_ui_font_size(ui_font_size + px(1.), cx);
203 },
204 )
205 .border(),
206 ),
207 ),
208 )
209 .child(
210 v_flex()
211 .justify_between()
212 .gap_1()
213 .child(Label::new("Editor Font"))
214 .child(
215 h_flex()
216 .justify_between()
217 .gap_2()
218 .child(DropdownMenu::new(
219 "buffer-font-family",
220 font_family,
221 ContextMenu::build(window, cx, |mut menu, _, cx| {
222 let font_family_cache = FontFamilyCache::global(cx);
223
224 for font_name in font_family_cache.list_font_families(cx) {
225 menu = menu.custom_entry(
226 {
227 let font_name = font_name.clone();
228 move |_window, _cx| {
229 Label::new(font_name.clone())
230 .into_any_element()
231 }
232 },
233 {
234 let font_name = font_name.clone();
235 move |_window, cx| {
236 write_buffer_font_family(
237 font_name.clone(),
238 cx,
239 );
240 }
241 },
242 )
243 }
244
245 menu
246 }),
247 ))
248 .child(
249 NumericStepper::new(
250 "buffer-font-size",
251 buffer_font_size.to_string(),
252 move |_, _, cx| {
253 write_buffer_font_size(buffer_font_size - px(1.), cx);
254 },
255 move |_, _, cx| {
256 write_buffer_font_size(buffer_font_size + px(1.), cx);
257 },
258 )
259 .border(),
260 ),
261 ),
262 ),
263 )
264 .child(
265 h_flex()
266 .justify_between()
267 .child(Label::new("Mini Map"))
268 .child(
269 ToggleButtonGroup::single_row(
270 "onboarding-show-mini-map",
271 [
272 ToggleButtonSimple::new("Auto", |_, _, cx| {
273 write_show_mini_map(ShowMinimap::Auto, cx);
274 }),
275 ToggleButtonSimple::new("Always", |_, _, cx| {
276 write_show_mini_map(ShowMinimap::Always, cx);
277 }),
278 ToggleButtonSimple::new("Never", |_, _, cx| {
279 write_show_mini_map(ShowMinimap::Never, cx);
280 }),
281 ],
282 )
283 .selected_index(match read_show_mini_map(cx) {
284 ShowMinimap::Auto => 0,
285 ShowMinimap::Always => 1,
286 ShowMinimap::Never => 2,
287 })
288 .style(ToggleButtonGroupStyle::Outlined)
289 .button_width(ui::rems_from_px(64.)),
290 ),
291 )
292 .child(
293 SwitchField::new(
294 "onboarding-enable-inlay-hints",
295 "Inlay Hints",
296 "See parameter names for function and method calls inline.",
297 if read_inlay_hints(cx) {
298 ui::ToggleState::Selected
299 } else {
300 ui::ToggleState::Unselected
301 },
302 |toggle_state, _, cx| {
303 write_inlay_hints(toggle_state == &ToggleState::Selected, cx);
304 },
305 )
306 .color(SwitchColor::Accent),
307 )
308 .child(
309 SwitchField::new(
310 "onboarding-git-blame-switch",
311 "Git Blame",
312 "See who committed each line on a given file.",
313 if read_git_blame(cx) {
314 ui::ToggleState::Selected
315 } else {
316 ui::ToggleState::Unselected
317 },
318 |toggle_state, _, cx| {
319 set_git_blame(toggle_state == &ToggleState::Selected, cx);
320 },
321 )
322 .color(SwitchColor::Accent),
323 )
324}