onboarding: Wire up tab index (#35659)
Closes #ISSUE Allows tabbing through everything in all three pages. Until #35075 is merged it is not possible to actually "click" tab focused buttons with the keyboard. Additionally adds an action `onboarding::Finish` and displays the keybind. The action corresponds to both the "Skip all" and "Start Building" buttons, with the keybind displayed similar to how it is for the page nav buttons Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: MrSubidubi <finn@zed.dev>
This commit is contained in:
parent
0b5592d788
commit
6b77654f66
11 changed files with 505 additions and 280 deletions
|
@ -1,9 +1,11 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use ai_onboarding::{AiUpsellCard, SignInStatus};
|
||||
use client::UserStore;
|
||||
use fs::Fs;
|
||||
use gpui::{
|
||||
Action, AnyView, App, DismissEvent, EventEmitter, FocusHandle, Focusable, Window, prelude::*,
|
||||
Action, AnyView, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity,
|
||||
Window, prelude::*,
|
||||
};
|
||||
use itertools;
|
||||
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
||||
|
@ -14,15 +16,14 @@ use ui::{
|
|||
prelude::*, tooltip_container,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use workspace::ModalView;
|
||||
use workspace::{ModalView, Workspace};
|
||||
use zed_actions::agent::OpenSettings;
|
||||
|
||||
use crate::Onboarding;
|
||||
|
||||
const FEATURED_PROVIDERS: [&'static str; 4] = ["anthropic", "google", "openai", "ollama"];
|
||||
|
||||
fn render_llm_provider_section(
|
||||
onboarding: &Onboarding,
|
||||
tab_index: &mut isize,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
disabled: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
|
@ -37,10 +38,10 @@ fn render_llm_provider_section(
|
|||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(render_llm_provider_card(onboarding, disabled, window, cx))
|
||||
.child(render_llm_provider_card(tab_index, workspace, disabled, window, cx))
|
||||
}
|
||||
|
||||
fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
|
||||
fn render_privacy_card(tab_index: &mut isize, disabled: bool, cx: &mut App) -> impl IntoElement {
|
||||
let privacy_badge = || {
|
||||
Badge::new("Privacy")
|
||||
.icon(IconName::ShieldCheck)
|
||||
|
@ -98,6 +99,10 @@ fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
|
|||
.icon_color(Color::Muted)
|
||||
.on_click(|_, _, cx| {
|
||||
cx.open_url("https://zed.dev/docs/ai/privacy-and-security");
|
||||
})
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
@ -114,7 +119,8 @@ fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
|
|||
}
|
||||
|
||||
fn render_llm_provider_card(
|
||||
onboarding: &Onboarding,
|
||||
tab_index: &mut isize,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
disabled: bool,
|
||||
_: &mut Window,
|
||||
cx: &mut App,
|
||||
|
@ -140,6 +146,10 @@ fn render_llm_provider_card(
|
|||
|
||||
ButtonLike::new(("onboarding-ai-setup-buttons", index))
|
||||
.size(ButtonSize::Large)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.group(&group_name)
|
||||
|
@ -188,7 +198,7 @@ fn render_llm_provider_card(
|
|||
),
|
||||
)
|
||||
.on_click({
|
||||
let workspace = onboarding.workspace.clone();
|
||||
let workspace = workspace.clone();
|
||||
move |_, window, cx| {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
|
@ -219,57 +229,56 @@ fn render_llm_provider_card(
|
|||
.icon_size(IconSize::XSmall)
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(OpenSettings.boxed_clone(), cx)
|
||||
})
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn render_ai_setup_page(
|
||||
onboarding: &Onboarding,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
user_store: Entity<UserStore>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
let mut tab_index = 0;
|
||||
let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
|
||||
|
||||
let backdrop = div()
|
||||
.id("backdrop")
|
||||
.size_full()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.opacity(0.8)
|
||||
.block_mouse_except_scroll();
|
||||
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.child(SwitchField::new(
|
||||
"enable_ai",
|
||||
"Enable AI features",
|
||||
None,
|
||||
if is_ai_disabled {
|
||||
ToggleState::Unselected
|
||||
} else {
|
||||
ToggleState::Selected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
let enabled = match toggle_state {
|
||||
ToggleState::Indeterminate => {
|
||||
return;
|
||||
}
|
||||
ToggleState::Unselected => false,
|
||||
ToggleState::Selected => true,
|
||||
};
|
||||
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
update_settings_file::<DisableAiSettings>(
|
||||
fs,
|
||||
cx,
|
||||
move |ai_settings: &mut Option<bool>, _| {
|
||||
*ai_settings = Some(!enabled);
|
||||
},
|
||||
);
|
||||
},
|
||||
))
|
||||
.child(render_privacy_card(is_ai_disabled, cx))
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"enable_ai",
|
||||
"Enable AI features",
|
||||
None,
|
||||
if is_ai_disabled {
|
||||
ToggleState::Unselected
|
||||
} else {
|
||||
ToggleState::Selected
|
||||
},
|
||||
|&toggle_state, _, cx| {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
update_settings_file::<DisableAiSettings>(
|
||||
fs,
|
||||
cx,
|
||||
move |ai_settings: &mut Option<bool>, _| {
|
||||
*ai_settings = match toggle_state {
|
||||
ToggleState::Indeterminate => None,
|
||||
ToggleState::Unselected => Some(true),
|
||||
ToggleState::Selected => Some(false),
|
||||
};
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
tab_index += 1;
|
||||
tab_index - 1
|
||||
}),
|
||||
)
|
||||
.child(render_privacy_card(&mut tab_index, is_ai_disabled, cx))
|
||||
.child(
|
||||
v_flex()
|
||||
.mt_2()
|
||||
|
@ -277,15 +286,31 @@ pub(crate) fn render_ai_setup_page(
|
|||
.child(AiUpsellCard {
|
||||
sign_in_status: SignInStatus::SignedIn,
|
||||
sign_in: Arc::new(|_, _| {}),
|
||||
user_plan: onboarding.user_store.read(cx).plan(),
|
||||
user_plan: user_store.read(cx).plan(),
|
||||
tab_index: Some({
|
||||
tab_index += 1;
|
||||
tab_index - 1
|
||||
}),
|
||||
})
|
||||
.child(render_llm_provider_section(
|
||||
onboarding,
|
||||
&mut tab_index,
|
||||
workspace,
|
||||
is_ai_disabled,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.when(is_ai_disabled, |this| this.child(backdrop)),
|
||||
.when(is_ai_disabled, |this| {
|
||||
this.child(
|
||||
div()
|
||||
.id("backdrop")
|
||||
.size_full()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.opacity(0.8)
|
||||
.block_mouse_except_scroll(),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||
|
||||
use client::TelemetrySettings;
|
||||
use fs::Fs;
|
||||
use gpui::{App, IntoElement, Window};
|
||||
use gpui::{App, IntoElement};
|
||||
use settings::{BaseKeymap, Settings, update_settings_file};
|
||||
use theme::{
|
||||
Appearance, SystemAppearance, ThemeMode, ThemeName, ThemeRegistry, ThemeSelection,
|
||||
|
@ -16,7 +16,7 @@ use vim_mode_setting::VimModeSetting;
|
|||
|
||||
use crate::theme_preview::{ThemePreviewStyle, ThemePreviewTile};
|
||||
|
||||
fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_theme_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
|
||||
let theme_selection = ThemeSettings::get_global(cx).theme_selection.clone();
|
||||
let system_appearance = theme::SystemAppearance::global(cx);
|
||||
let theme_selection = theme_selection.unwrap_or_else(|| ThemeSelection::Dynamic {
|
||||
|
@ -55,6 +55,7 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
)
|
||||
}),
|
||||
)
|
||||
.tab_index(tab_index)
|
||||
.selected_index(theme_mode as usize)
|
||||
.style(ui::ToggleButtonGroupStyle::Outlined)
|
||||
.button_width(rems_from_px(64.)),
|
||||
|
@ -64,10 +65,11 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
h_flex()
|
||||
.gap_4()
|
||||
.justify_between()
|
||||
.children(render_theme_previews(&theme_selection, cx)),
|
||||
.children(render_theme_previews(tab_index, &theme_selection, cx)),
|
||||
);
|
||||
|
||||
fn render_theme_previews(
|
||||
tab_index: &mut isize,
|
||||
theme_selection: &ThemeSelection,
|
||||
cx: &mut App,
|
||||
) -> [impl IntoElement; 3] {
|
||||
|
@ -110,12 +112,12 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
let colors = cx.theme().colors();
|
||||
|
||||
v_flex()
|
||||
.id(name.clone())
|
||||
.w_full()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.id(name.clone())
|
||||
.relative()
|
||||
.w_full()
|
||||
.border_2()
|
||||
|
@ -128,6 +130,20 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
this.opacity(0.8).hover(|s| s.border_color(colors.border))
|
||||
}
|
||||
})
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.focus(|mut style| {
|
||||
style.border_color = Some(colors.border_focused);
|
||||
style
|
||||
})
|
||||
.on_click({
|
||||
let theme_name = theme.name.clone();
|
||||
move |_, _, cx| {
|
||||
write_theme_change(theme_name.clone(), theme_mode, cx);
|
||||
}
|
||||
})
|
||||
.map(|this| {
|
||||
if theme_mode == ThemeMode::System {
|
||||
let (light, dark) = (
|
||||
|
@ -151,12 +167,6 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.on_click({
|
||||
let theme_name = theme.name.clone();
|
||||
move |_, _, cx| {
|
||||
write_theme_change(theme_name.clone(), theme_mode, cx);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
theme_previews
|
||||
|
@ -187,15 +197,7 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
}
|
||||
}
|
||||
|
||||
fn write_keymap_base(keymap_base: BaseKeymap, cx: &App) {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
update_settings_file::<BaseKeymap>(fs, cx, move |setting, _| {
|
||||
*setting = Some(keymap_base);
|
||||
});
|
||||
}
|
||||
|
||||
fn render_telemetry_section(cx: &App) -> impl IntoElement {
|
||||
fn render_telemetry_section(tab_index: &mut isize, cx: &App) -> impl IntoElement {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
v_flex()
|
||||
|
@ -225,7 +227,10 @@ fn render_telemetry_section(cx: &App) -> impl IntoElement {
|
|||
move |setting, _| setting.metrics = Some(enabled),
|
||||
);
|
||||
}},
|
||||
))
|
||||
).tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index
|
||||
}))
|
||||
.child(SwitchField::new(
|
||||
"onboarding-telemetry-crash-reports",
|
||||
"Help Fix Zed",
|
||||
|
@ -251,10 +256,13 @@ fn render_telemetry_section(cx: &App) -> impl IntoElement {
|
|||
);
|
||||
}
|
||||
}
|
||||
))
|
||||
).tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn render_basics_page(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_base_keymap_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
|
||||
let base_keymap = match BaseKeymap::get_global(cx) {
|
||||
BaseKeymap::VSCode => Some(0),
|
||||
BaseKeymap::JetBrains => Some(1),
|
||||
|
@ -265,67 +273,89 @@ pub(crate) fn render_basics_page(window: &mut Window, cx: &mut App) -> impl Into
|
|||
BaseKeymap::TextMate | BaseKeymap::None => None,
|
||||
};
|
||||
|
||||
return v_flex().gap_2().child(Label::new("Base Keymap")).child(
|
||||
ToggleButtonGroup::two_rows(
|
||||
"base_keymap_selection",
|
||||
[
|
||||
ToggleButtonWithIcon::new("VS Code", IconName::EditorVsCode, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::VSCode, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Jetbrains", IconName::EditorJetBrains, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::JetBrains, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Sublime Text", IconName::EditorSublime, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::SublimeText, cx);
|
||||
}),
|
||||
],
|
||||
[
|
||||
ToggleButtonWithIcon::new("Atom", IconName::EditorAtom, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Atom, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Emacs", IconName::EditorEmacs, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Emacs, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Cursor (Beta)", IconName::EditorCursor, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Cursor, cx);
|
||||
}),
|
||||
],
|
||||
)
|
||||
.when_some(base_keymap, |this, base_keymap| {
|
||||
this.selected_index(base_keymap)
|
||||
})
|
||||
.tab_index(tab_index)
|
||||
.button_width(rems_from_px(216.))
|
||||
.size(ui::ToggleButtonGroupSize::Medium)
|
||||
.style(ui::ToggleButtonGroupStyle::Outlined),
|
||||
);
|
||||
|
||||
fn write_keymap_base(keymap_base: BaseKeymap, cx: &App) {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
update_settings_file::<BaseKeymap>(fs, cx, move |setting, _| {
|
||||
*setting = Some(keymap_base);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
|
||||
let toggle_state = if VimModeSetting::get_global(cx).0 {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
};
|
||||
SwitchField::new(
|
||||
"onboarding-vim-mode",
|
||||
"Vim Mode",
|
||||
Some(
|
||||
"Coming from Neovim? Zed's first-class implementation of Vim Mode has got your back."
|
||||
.into(),
|
||||
),
|
||||
toggle_state,
|
||||
{
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
move |&selection, _, cx| {
|
||||
update_settings_file::<VimModeSetting>(fs.clone(), cx, move |setting, _| {
|
||||
*setting = match selection {
|
||||
ToggleState::Selected => Some(true),
|
||||
ToggleState::Unselected => Some(false),
|
||||
ToggleState::Indeterminate => None,
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_basics_page(cx: &mut App) -> impl IntoElement {
|
||||
let mut tab_index = 0;
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(render_theme_section(window, cx))
|
||||
.child(
|
||||
v_flex().gap_2().child(Label::new("Base Keymap")).child(
|
||||
ToggleButtonGroup::two_rows(
|
||||
"multiple_row_test",
|
||||
[
|
||||
ToggleButtonWithIcon::new("VS Code", IconName::EditorVsCode, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::VSCode, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Jetbrains", IconName::EditorJetBrains, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::JetBrains, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Sublime Text", IconName::EditorSublime, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::SublimeText, cx);
|
||||
}),
|
||||
],
|
||||
[
|
||||
ToggleButtonWithIcon::new("Atom", IconName::EditorAtom, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Atom, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Emacs", IconName::EditorEmacs, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Emacs, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Cursor (Beta)", IconName::EditorCursor, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Cursor, cx);
|
||||
}),
|
||||
],
|
||||
)
|
||||
.when_some(base_keymap, |this, base_keymap| this.selected_index(base_keymap))
|
||||
.button_width(rems_from_px(216.))
|
||||
.size(ui::ToggleButtonGroupSize::Medium)
|
||||
.style(ui::ToggleButtonGroupStyle::Outlined)
|
||||
),
|
||||
)
|
||||
.child(SwitchField::new(
|
||||
"onboarding-vim-mode",
|
||||
"Vim Mode",
|
||||
Some("Coming from Neovim? Zed's first-class implementation of Vim Mode has got your back.".into()),
|
||||
if VimModeSetting::get_global(cx).0 {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
{
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
move |selection, _, cx| {
|
||||
let enabled = match selection {
|
||||
ToggleState::Selected => true,
|
||||
ToggleState::Unselected => false,
|
||||
ToggleState::Indeterminate => { return; },
|
||||
};
|
||||
|
||||
update_settings_file::<VimModeSetting>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |setting, _| *setting = Some(enabled),
|
||||
);
|
||||
}
|
||||
},
|
||||
))
|
||||
.child(render_telemetry_section(cx))
|
||||
.child(render_theme_section(&mut tab_index, cx))
|
||||
.child(render_base_keymap_section(&mut tab_index, cx))
|
||||
.child(render_vim_mode_switch(&mut tab_index, cx))
|
||||
.child(render_telemetry_section(&mut tab_index, cx))
|
||||
}
|
||||
|
|
|
@ -171,6 +171,7 @@ fn write_format_on_save(format_on_save: bool, cx: &mut App) {
|
|||
}
|
||||
|
||||
fn render_setting_import_button(
|
||||
tab_index: isize,
|
||||
label: SharedString,
|
||||
icon_name: IconName,
|
||||
action: &dyn Action,
|
||||
|
@ -182,6 +183,7 @@ fn render_setting_import_button(
|
|||
.full_width()
|
||||
.style(ButtonStyle::Outlined)
|
||||
.size(ButtonSize::Large)
|
||||
.tab_index(tab_index)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
|
@ -214,7 +216,7 @@ fn render_setting_import_button(
|
|||
)
|
||||
}
|
||||
|
||||
fn render_import_settings_section(cx: &App) -> impl IntoElement {
|
||||
fn render_import_settings_section(tab_index: &mut isize, cx: &App) -> impl IntoElement {
|
||||
let import_state = SettingsImportState::global(cx);
|
||||
let imports: [(SharedString, IconName, &dyn Action, bool); 2] = [
|
||||
(
|
||||
|
@ -232,7 +234,8 @@ fn render_import_settings_section(cx: &App) -> impl IntoElement {
|
|||
];
|
||||
|
||||
let [vscode, cursor] = imports.map(|(label, icon_name, action, imported)| {
|
||||
render_setting_import_button(label, icon_name, action, imported)
|
||||
*tab_index += 1;
|
||||
render_setting_import_button(*tab_index - 1, label, icon_name, action, imported)
|
||||
});
|
||||
|
||||
v_flex()
|
||||
|
@ -248,7 +251,11 @@ fn render_import_settings_section(cx: &App) -> impl IntoElement {
|
|||
.child(h_flex().w_full().gap_4().child(vscode).child(cursor))
|
||||
}
|
||||
|
||||
fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_font_customization_section(
|
||||
tab_index: &mut isize,
|
||||
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 ui_font_family = theme_settings.ui_font.family.clone();
|
||||
|
@ -294,6 +301,10 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
.style(ButtonStyle::Outlined)
|
||||
.size(ButtonSize::Medium)
|
||||
.full_width()
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
|
@ -325,7 +336,11 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
write_ui_font_size(ui_font_size + px(1.), cx);
|
||||
},
|
||||
)
|
||||
.style(ui::NumericStepperStyle::Outlined),
|
||||
.style(ui::NumericStepperStyle::Outlined)
|
||||
.tab_index({
|
||||
*tab_index += 2;
|
||||
*tab_index - 2
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -350,6 +365,10 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
.style(ButtonStyle::Outlined)
|
||||
.size(ButtonSize::Medium)
|
||||
.full_width()
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
|
@ -381,7 +400,11 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
write_buffer_font_size(buffer_font_size + px(1.), cx);
|
||||
},
|
||||
)
|
||||
.style(ui::NumericStepperStyle::Outlined),
|
||||
.style(ui::NumericStepperStyle::Outlined)
|
||||
.tab_index({
|
||||
*tab_index += 2;
|
||||
*tab_index - 2
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -556,13 +579,17 @@ fn font_picker(
|
|||
.max_height(Some(rems(20.).into()))
|
||||
}
|
||||
|
||||
fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_popular_settings_section(
|
||||
tab_index: &mut isize,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
const LIGATURE_TOOLTIP: &'static str = "Ligatures are when a font creates a special character out of combining two characters into one. For example, with ligatures turned on, =/= would become ≠.";
|
||||
|
||||
v_flex()
|
||||
.gap_5()
|
||||
.child(Label::new("Popular Settings").size(LabelSize::Large).mt_8())
|
||||
.child(render_font_customization_section(window, cx))
|
||||
.child(render_font_customization_section(tab_index, window, cx))
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"onboarding-font-ligatures",
|
||||
|
@ -577,47 +604,69 @@ fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl In
|
|||
write_font_ligatures(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.tooltip(Tooltip::text(LIGATURE_TOOLTIP)),
|
||||
)
|
||||
.child(SwitchField::new(
|
||||
"onboarding-format-on-save",
|
||||
"Format on Save",
|
||||
Some("Format code automatically when saving.".into()),
|
||||
if read_format_on_save(cx) {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
write_format_on_save(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
))
|
||||
.child(SwitchField::new(
|
||||
"onboarding-enable-inlay-hints",
|
||||
"Inlay Hints",
|
||||
Some("See parameter names for function and method calls inline.".into()),
|
||||
if read_inlay_hints(cx) {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
write_inlay_hints(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
))
|
||||
.child(SwitchField::new(
|
||||
"onboarding-git-blame-switch",
|
||||
"Git Blame",
|
||||
Some("See who committed each line on a given file.".into()),
|
||||
if read_git_blame(cx) {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
set_git_blame(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
))
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"onboarding-format-on-save",
|
||||
"Format on Save",
|
||||
Some("Format code automatically when saving.".into()),
|
||||
if read_format_on_save(cx) {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
write_format_on_save(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"onboarding-enable-inlay-hints",
|
||||
"Inlay Hints",
|
||||
Some("See parameter names for function and method calls inline.".into()),
|
||||
if read_inlay_hints(cx) {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
write_inlay_hints(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"onboarding-git-blame-switch",
|
||||
"Git Blame",
|
||||
Some("See who committed each line on a given file.".into()),
|
||||
if read_git_blame(cx) {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
set_git_blame(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.items_start()
|
||||
|
@ -648,6 +697,7 @@ fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl In
|
|||
ShowMinimap::Always => 1,
|
||||
ShowMinimap::Never => 2,
|
||||
})
|
||||
.tab_index(tab_index)
|
||||
.style(ToggleButtonGroupStyle::Outlined)
|
||||
.button_width(ui::rems_from_px(64.)),
|
||||
),
|
||||
|
@ -655,8 +705,9 @@ fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl In
|
|||
}
|
||||
|
||||
pub(crate) fn render_editing_page(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let mut tab_index = 0;
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.child(render_import_settings_section(cx))
|
||||
.child(render_popular_settings_section(window, cx))
|
||||
.child(render_import_settings_section(&mut tab_index, cx))
|
||||
.child(render_popular_settings_section(&mut tab_index, window, cx))
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ actions!(
|
|||
ActivateEditingPage,
|
||||
/// Activates the AI Setup page.
|
||||
ActivateAISetupPage,
|
||||
/// Finish the onboarding process.
|
||||
Finish,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -261,40 +263,6 @@ impl Onboarding {
|
|||
cx.emit(ItemEvent::UpdateTab);
|
||||
}
|
||||
|
||||
fn go_to_welcome_page(&self, cx: &mut App) {
|
||||
with_active_or_new_workspace(cx, |workspace, window, cx| {
|
||||
let Some((onboarding_id, onboarding_idx)) = workspace
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.items()
|
||||
.enumerate()
|
||||
.find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<Onboarding>()?;
|
||||
Some((item.item_id(), idx))
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
// Get the index here to get around the borrow checker
|
||||
let idx = pane.items().enumerate().find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<WelcomePage>()?;
|
||||
Some(idx)
|
||||
});
|
||||
|
||||
if let Some(idx) = idx {
|
||||
pane.activate_item(idx, true, true, window, cx);
|
||||
} else {
|
||||
let item = Box::new(WelcomePage::new(window, cx));
|
||||
pane.add_item(item, true, true, Some(onboarding_idx), window, cx);
|
||||
}
|
||||
|
||||
pane.remove_item(onboarding_id, false, false, window, cx);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn render_nav_buttons(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
|
@ -401,6 +369,13 @@ impl Onboarding {
|
|||
.children(self.render_nav_buttons(window, cx)),
|
||||
)
|
||||
.map(|this| {
|
||||
let keybinding = KeyBinding::for_action_in(
|
||||
&Finish,
|
||||
&self.focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.map(|kb| kb.size(rems_from_px(12.)));
|
||||
if ai_setup_page {
|
||||
this.child(
|
||||
ButtonLike::new("start_building")
|
||||
|
@ -412,23 +387,37 @@ impl Onboarding {
|
|||
.w_full()
|
||||
.justify_between()
|
||||
.child(Label::new("Start Building"))
|
||||
.child(
|
||||
Icon::new(IconName::Check)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
.child(keybinding.map_or_else(
|
||||
|| {
|
||||
Icon::new(IconName::Check)
|
||||
.size(IconSize::Small)
|
||||
.into_any_element()
|
||||
},
|
||||
IntoElement::into_any_element,
|
||||
)),
|
||||
)
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.go_to_welcome_page(cx);
|
||||
})),
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(Finish.boxed_clone(), cx);
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
this.child(
|
||||
ButtonLike::new("skip_all")
|
||||
.size(ButtonSize::Medium)
|
||||
.child(Label::new("Skip All").ml_1())
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.go_to_welcome_page(cx);
|
||||
})),
|
||||
.child(
|
||||
h_flex()
|
||||
.ml_1()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(Label::new("Skip All"))
|
||||
.child(keybinding.map_or_else(
|
||||
|| gpui::Empty.into_any_element(),
|
||||
IntoElement::into_any_element,
|
||||
)),
|
||||
)
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(Finish.boxed_clone(), cx);
|
||||
}),
|
||||
)
|
||||
}
|
||||
}),
|
||||
|
@ -464,17 +453,23 @@ impl Onboarding {
|
|||
|
||||
fn render_page(&mut self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
|
||||
match self.selected_page {
|
||||
SelectedPage::Basics => {
|
||||
crate::basics_page::render_basics_page(window, cx).into_any_element()
|
||||
}
|
||||
SelectedPage::Basics => crate::basics_page::render_basics_page(cx).into_any_element(),
|
||||
SelectedPage::Editing => {
|
||||
crate::editing_page::render_editing_page(window, cx).into_any_element()
|
||||
}
|
||||
SelectedPage::AiSetup => {
|
||||
crate::ai_setup_page::render_ai_setup_page(&self, window, cx).into_any_element()
|
||||
}
|
||||
SelectedPage::AiSetup => crate::ai_setup_page::render_ai_setup_page(
|
||||
self.workspace.clone(),
|
||||
self.user_store.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.into_any_element(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_finish(_: &Finish, _: &mut Window, cx: &mut App) {
|
||||
go_to_welcome_page(cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Onboarding {
|
||||
|
@ -484,11 +479,13 @@ impl Render for Onboarding {
|
|||
.key_context({
|
||||
let mut ctx = KeyContext::new_with_defaults();
|
||||
ctx.add("Onboarding");
|
||||
ctx.add("menu");
|
||||
ctx
|
||||
})
|
||||
.track_focus(&self.focus_handle)
|
||||
.size_full()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.on_action(Self::on_finish)
|
||||
.on_action(cx.listener(|this, _: &ActivateBasicsPage, _, cx| {
|
||||
this.set_page(SelectedPage::Basics, cx);
|
||||
}))
|
||||
|
@ -498,6 +495,14 @@ impl Render for Onboarding {
|
|||
.on_action(cx.listener(|this, _: &ActivateAISetupPage, _, cx| {
|
||||
this.set_page(SelectedPage::AiSetup, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|_, _: &menu::SelectNext, window, cx| {
|
||||
window.focus_next();
|
||||
cx.notify();
|
||||
}))
|
||||
.on_action(cx.listener(|_, _: &menu::SelectPrevious, window, cx| {
|
||||
window.focus_prev();
|
||||
cx.notify();
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
.max_w(rems_from_px(1100.))
|
||||
|
@ -561,6 +566,40 @@ impl Item for Onboarding {
|
|||
}
|
||||
}
|
||||
|
||||
fn go_to_welcome_page(cx: &mut App) {
|
||||
with_active_or_new_workspace(cx, |workspace, window, cx| {
|
||||
let Some((onboarding_id, onboarding_idx)) = workspace
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.items()
|
||||
.enumerate()
|
||||
.find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<Onboarding>()?;
|
||||
Some((item.item_id(), idx))
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
// Get the index here to get around the borrow checker
|
||||
let idx = pane.items().enumerate().find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<WelcomePage>()?;
|
||||
Some(idx)
|
||||
});
|
||||
|
||||
if let Some(idx) = idx {
|
||||
pane.activate_item(idx, true, true, window, cx);
|
||||
} else {
|
||||
let item = Box::new(WelcomePage::new(window, cx));
|
||||
pane.add_item(item, true, true, Some(onboarding_idx), window, cx);
|
||||
}
|
||||
|
||||
pane.remove_item(onboarding_id, false, false, window, cx);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn handle_import_vscode_settings(
|
||||
workspace: WeakEntity<Workspace>,
|
||||
source: VsCodeSettingsSource,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue