editing_page.rs

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