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