onboarding ui: Add editing page to onboarding page (#35298)

Anthony Eid created

I added buttons for inlay values, showing the mini map, git blame, and
controlling the UI/Editor Font/Font size. The only thing left for this
page is some UI clean up and adding buttons for setting import from
VSCode/cursor.

I also added Numeric Stepper as a component preview.

Current state:
<img width="1085" height="585" alt="image"
src="https://github.com/user-attachments/assets/230df474-da81-4810-ba64-05673896d119"
/>


Release Notes:

- N/A

Change summary

Cargo.lock                                  |   3 
crates/editor/src/editor.rs                 |   2 
crates/onboarding/Cargo.toml                |   5 
crates/onboarding/src/editing_page.rs       | 287 +++++++++++++++++++++++
crates/onboarding/src/onboarding.rs         |  10 
crates/ui/src/components/numeric_stepper.rs |  33 ++
6 files changed, 331 insertions(+), 9 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -10942,9 +10942,12 @@ dependencies = [
  "anyhow",
  "command_palette_hooks",
  "db",
+ "editor",
  "feature_flags",
  "fs",
  "gpui",
+ "language",
+ "project",
  "settings",
  "theme",
  "ui",

crates/editor/src/editor.rs 🔗

@@ -65,7 +65,7 @@ use display_map::*;
 pub use display_map::{ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder};
 pub use editor_settings::{
     CurrentLineHighlight, DocumentColorsRenderMode, EditorSettings, HideMouseMode,
-    ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowScrollbar,
+    ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowMinimap, ShowScrollbar,
 };
 use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
 pub use editor_settings_controls::*;

crates/onboarding/Cargo.toml 🔗

@@ -18,12 +18,15 @@ default = []
 anyhow.workspace = true
 command_palette_hooks.workspace = true
 db.workspace = true
+editor.workspace = true
 feature_flags.workspace = true
 fs.workspace = true
 gpui.workspace = true
+language.workspace = true
+project.workspace = true
 settings.workspace = true
 theme.workspace = true
 ui.workspace = true
-workspace.workspace = true
 workspace-hack.workspace = true
+workspace.workspace = true
 zed_actions.workspace = true

crates/onboarding/src/editing_page.rs 🔗

@@ -0,0 +1,287 @@
+use editor::{EditorSettings, ShowMinimap};
+use fs::Fs;
+use gpui::{App, IntoElement, Pixels, Window};
+use language::language_settings::AllLanguageSettings;
+use project::project_settings::ProjectSettings;
+use settings::{Settings as _, update_settings_file};
+use theme::{FontFamilyCache, FontFamilyName, ThemeSettings};
+use ui::{
+    ContextMenu, DropdownMenu, IconButton, Label, LabelCommon, LabelSize, NumericStepper,
+    ParentElement, SharedString, Styled, SwitchColor, SwitchField, ToggleButtonGroup,
+    ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, div, h_flex, px, v_flex,
+};
+
+fn read_show_mini_map(cx: &App) -> ShowMinimap {
+    editor::EditorSettings::get_global(cx).minimap.show
+}
+
+fn write_show_mini_map(show: ShowMinimap, cx: &mut App) {
+    let fs = <dyn Fs>::global(cx);
+
+    update_settings_file::<EditorSettings>(fs, cx, move |editor_settings, _| {
+        editor_settings.minimap.get_or_insert_default().show = Some(show);
+    });
+}
+
+fn read_inlay_hints(cx: &App) -> bool {
+    AllLanguageSettings::get_global(cx)
+        .defaults
+        .inlay_hints
+        .enabled
+}
+
+fn write_inlay_hints(enabled: bool, cx: &mut App) {
+    let fs = <dyn Fs>::global(cx);
+
+    update_settings_file::<AllLanguageSettings>(fs, cx, move |all_language_settings, cx| {
+        all_language_settings
+            .defaults
+            .inlay_hints
+            .get_or_insert_with(|| {
+                AllLanguageSettings::get_global(cx)
+                    .clone()
+                    .defaults
+                    .inlay_hints
+            })
+            .enabled = enabled;
+    });
+}
+
+fn read_git_blame(cx: &App) -> bool {
+    ProjectSettings::get_global(cx).git.inline_blame_enabled()
+}
+
+fn set_git_blame(enabled: bool, cx: &mut App) {
+    let fs = <dyn Fs>::global(cx);
+
+    update_settings_file::<ProjectSettings>(fs, cx, move |project_settings, _| {
+        project_settings
+            .git
+            .inline_blame
+            .get_or_insert_default()
+            .enabled = enabled;
+    });
+}
+
+fn write_ui_font_family(font: SharedString, cx: &mut App) {
+    let fs = <dyn Fs>::global(cx);
+
+    update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
+        theme_settings.ui_font_family = Some(FontFamilyName(font.into()));
+    });
+}
+
+fn write_ui_font_size(size: Pixels, cx: &mut App) {
+    let fs = <dyn Fs>::global(cx);
+
+    update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
+        theme_settings.ui_font_size = Some(size.into());
+    });
+}
+
+fn write_buffer_font_size(size: Pixels, cx: &mut App) {
+    let fs = <dyn Fs>::global(cx);
+
+    update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
+        theme_settings.buffer_font_size = Some(size.into());
+    });
+}
+
+fn write_buffer_font_family(font_family: SharedString, cx: &mut App) {
+    let fs = <dyn Fs>::global(cx);
+
+    update_settings_file::<ThemeSettings>(fs, cx, move |theme_settings, _| {
+        theme_settings.buffer_font_family = Some(FontFamilyName(font_family.into()));
+    });
+}
+
+pub(crate) fn render_editing_page(window: &mut Window, cx: &mut App) -> impl IntoElement {
+    let theme_settings = ThemeSettings::get_global(cx);
+    let ui_font_size = theme_settings.ui_font_size(cx);
+    let font_family = theme_settings.buffer_font.family.clone();
+    let buffer_font_size = theme_settings.buffer_font_size(cx);
+
+    v_flex()
+        .gap_4()
+        .child(Label::new("Import Settings").size(LabelSize::Large))
+        .child(
+            Label::new("Automatically pull your settings from other editors.")
+                .size(LabelSize::Small),
+        )
+        .child(
+            h_flex()
+                .child(IconButton::new(
+                    "import-vs-code-settings",
+                    ui::IconName::Code,
+                ))
+                .child(IconButton::new(
+                    "import-cursor-settings",
+                    ui::IconName::CursorIBeam,
+                )),
+        )
+        .child(Label::new("Popular Settings").size(LabelSize::Large))
+        .child(
+            h_flex()
+                .gap_4()
+                .justify_between()
+                .child(
+                    v_flex()
+                        .justify_between()
+                        .gap_1()
+                        .child(Label::new("UI Font"))
+                        .child(
+                            h_flex()
+                                .justify_between()
+                                .gap_2()
+                                .child(div().min_w(px(120.)).child(DropdownMenu::new(
+                                    "ui-font-family",
+                                    theme_settings.ui_font.family.clone(),
+                                    ContextMenu::build(window, cx, |mut menu, _, cx| {
+                                        let font_family_cache = FontFamilyCache::global(cx);
+
+                                        for font_name in font_family_cache.list_font_families(cx) {
+                                            menu = menu.custom_entry(
+                                                {
+                                                    let font_name = font_name.clone();
+                                                    move |_window, _cx| {
+                                                        Label::new(font_name.clone())
+                                                            .into_any_element()
+                                                    }
+                                                },
+                                                {
+                                                    let font_name = font_name.clone();
+                                                    move |_window, cx| {
+                                                        write_ui_font_family(font_name.clone(), cx);
+                                                    }
+                                                },
+                                            )
+                                        }
+
+                                        menu
+                                    }),
+                                )))
+                                .child(NumericStepper::new(
+                                    "ui-font-size",
+                                    ui_font_size.to_string(),
+                                    move |_, _, cx| {
+                                        write_ui_font_size(ui_font_size - px(1.), cx);
+                                    },
+                                    move |_, _, cx| {
+                                        write_ui_font_size(ui_font_size + px(1.), cx);
+                                    },
+                                )),
+                        ),
+                )
+                .child(
+                    v_flex()
+                        .justify_between()
+                        .gap_1()
+                        .child(Label::new("Editor Font"))
+                        .child(
+                            h_flex()
+                                .justify_between()
+                                .gap_2()
+                                .child(DropdownMenu::new(
+                                    "buffer-font-family",
+                                    font_family,
+                                    ContextMenu::build(window, cx, |mut menu, _, cx| {
+                                        let font_family_cache = FontFamilyCache::global(cx);
+
+                                        for font_name in font_family_cache.list_font_families(cx) {
+                                            menu = menu.custom_entry(
+                                                {
+                                                    let font_name = font_name.clone();
+                                                    move |_window, _cx| {
+                                                        Label::new(font_name.clone())
+                                                            .into_any_element()
+                                                    }
+                                                },
+                                                {
+                                                    let font_name = font_name.clone();
+                                                    move |_window, cx| {
+                                                        write_buffer_font_family(
+                                                            font_name.clone(),
+                                                            cx,
+                                                        );
+                                                    }
+                                                },
+                                            )
+                                        }
+
+                                        menu
+                                    }),
+                                ))
+                                .child(NumericStepper::new(
+                                    "buffer-font-size",
+                                    buffer_font_size.to_string(),
+                                    move |_, _, cx| {
+                                        write_buffer_font_size(buffer_font_size - px(1.), cx);
+                                    },
+                                    move |_, _, cx| {
+                                        write_buffer_font_size(buffer_font_size + px(1.), cx);
+                                    },
+                                )),
+                        ),
+                ),
+        )
+        .child(
+            h_flex()
+                .justify_between()
+                .child(Label::new("Mini Map"))
+                .child(
+                    ToggleButtonGroup::single_row(
+                        "onboarding-show-mini-map",
+                        [
+                            ToggleButtonSimple::new("Auto", |_, _, cx| {
+                                write_show_mini_map(ShowMinimap::Auto, cx);
+                            }),
+                            ToggleButtonSimple::new("Always", |_, _, cx| {
+                                write_show_mini_map(ShowMinimap::Always, cx);
+                            }),
+                            ToggleButtonSimple::new("Never", |_, _, cx| {
+                                write_show_mini_map(ShowMinimap::Never, cx);
+                            }),
+                        ],
+                    )
+                    .selected_index(match read_show_mini_map(cx) {
+                        ShowMinimap::Auto => 0,
+                        ShowMinimap::Always => 1,
+                        ShowMinimap::Never => 2,
+                    })
+                    .style(ToggleButtonGroupStyle::Outlined)
+                    .button_width(ui::rems_from_px(64.)),
+                ),
+        )
+        .child(
+            SwitchField::new(
+                "onboarding-enable-inlay-hints",
+                "Inlay Hints",
+                "See parameter names for function and method calls inline.",
+                if read_inlay_hints(cx) {
+                    ui::ToggleState::Selected
+                } else {
+                    ui::ToggleState::Unselected
+                },
+                |toggle_state, _, cx| {
+                    write_inlay_hints(toggle_state == &ToggleState::Selected, cx);
+                },
+            )
+            .color(SwitchColor::Accent),
+        )
+        .child(
+            SwitchField::new(
+                "onboarding-git-blame-switch",
+                "Git Blame",
+                "See who committed each line on a given file.",
+                if read_git_blame(cx) {
+                    ui::ToggleState::Selected
+                } else {
+                    ui::ToggleState::Unselected
+                },
+                |toggle_state, _, cx| {
+                    set_git_blame(toggle_state == &ToggleState::Selected, cx);
+                },
+            )
+            .color(SwitchColor::Accent),
+        )
+}

crates/onboarding/src/onboarding.rs 🔗

@@ -21,6 +21,7 @@ use workspace::{
     open_new, with_active_or_new_workspace,
 };
 
+mod editing_page;
 mod welcome;
 
 pub struct OnBoardingFeatureFlag {}
@@ -246,7 +247,9 @@ impl Onboarding {
     fn render_page(&mut self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
         match self.selected_page {
             SelectedPage::Basics => self.render_basics_page(window, cx).into_any_element(),
-            SelectedPage::Editing => self.render_editing_page(window, cx).into_any_element(),
+            SelectedPage::Editing => {
+                crate::editing_page::render_editing_page(window, cx).into_any_element()
+            }
             SelectedPage::AiSetup => self.render_ai_setup_page(window, cx).into_any_element(),
         }
     }
@@ -281,11 +284,6 @@ impl Onboarding {
         )
     }
 
-    fn render_editing_page(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
-        // div().child("editing page")
-        "Right"
-    }
-
     fn render_ai_setup_page(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
         div().child("ai setup page")
     }

crates/ui/src/components/numeric_stepper.rs 🔗

@@ -2,7 +2,7 @@ use gpui::ClickEvent;
 
 use crate::{IconButtonShape, prelude::*};
 
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
 pub struct NumericStepper {
     id: ElementId,
     value: SharedString,
@@ -93,3 +93,34 @@ impl RenderOnce for NumericStepper {
             )
     }
 }
+
+impl Component for NumericStepper {
+    fn scope() -> ComponentScope {
+        ComponentScope::Input
+    }
+
+    fn name() -> &'static str {
+        "NumericStepper"
+    }
+
+    fn sort_name() -> &'static str {
+        Self::name()
+    }
+
+    fn description() -> Option<&'static str> {
+        Some("A button used to increment or decrement a numeric value. ")
+    }
+
+    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+        Some(
+            div()
+                .child(NumericStepper::new(
+                    "numeric-stepper-component-preview",
+                    "10",
+                    move |_, _, _| {},
+                    move |_, _, _| {},
+                ))
+                .into_any_element(),
+        )
+    }
+}