onboarding: Add design adjustments (#35480)

Release Notes:

- N/A

---------

Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
Danilo Leal 2025-08-01 15:08:15 -03:00 committed by GitHub
parent b31f893408
commit faa45c53d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 348 additions and 185 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

View file

@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.6" d="M3.5 11V5.5L8.5 8L3.5 11Z" fill="black"/>
<path opacity="0.4" d="M8.5 14L3.5 11L8.5 8V14Z" fill="black"/>
<path opacity="0.6" d="M8.5 5.5H3.5L8.5 2.5L8.5 5.5Z" fill="black"/>
<path opacity="0.8" d="M8.5 5.5V2.5L13.5 5.5H8.5Z" fill="black"/>
<path opacity="0.2" d="M13.5 11L8.5 14L11 9.5L13.5 11Z" fill="black"/>
<path opacity="0.5" d="M13.5 11L11 9.5L13.5 5V11Z" fill="black"/>
<path d="M3.5 11V5L8.5 2.11325L13.5 5V11L8.5 13.8868L3.5 11Z" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 583 B

View file

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2716_663)">
<path d="M8.47552 2.45453C11.5167 2.45457 13.9814 4.94501 13.9814 8.01623C13.9814 11.0875 11.5167 13.578 8.47552 13.5781C5.43427 13.5781 2.96948 11.0875 2.96948 8.01623C2.9695 4.94498 5.43429 2.45453 8.47552 2.45453ZM10.8795 4.70348C10.7605 4.16887 10.1328 3.85468 9.53627 3.96342C8.97622 4.06552 7.62871 4.45681 7.62057 4.45916C9.29414 4.44469 9.57429 4.4726 9.69939 4.64751C9.77324 4.7508 9.66576 4.89248 9.21944 4.96538C8.73515 5.04447 7.73014 5.13958 7.72343 5.14022C6.75441 5.19776 6.07177 5.20168 5.86705 5.63512C5.73334 5.91827 6.00968 6.16857 6.13082 6.32527C6.64271 6.89455 7.38215 7.20158 7.85809 7.42767C8.03716 7.51274 8.56257 7.67345 8.56257 7.67345C7.01855 7.58853 5.90474 8.06267 5.2514 8.60855C4.51246 9.29204 4.83937 10.1067 6.35327 10.6084C7.24742 10.9047 7.69094 11.0439 9.02473 10.9238C9.81031 10.8815 9.9342 10.9068 9.94203 10.9712C9.95275 11.062 9.06932 11.2874 8.82812 11.357C8.21455 11.534 6.60645 11.8913 6.59758 11.8932C6.60115 11.8935 7.06249 11.9257 7.65531 11.8735C7.89632 11.8522 8.81142 11.7624 9.49557 11.6123C9.49557 11.6123 10.3297 11.4338 10.7759 11.2693C11.2429 11.0973 11.497 10.9512 11.6113 10.7443C11.6063 10.7019 11.6465 10.5516 11.4313 10.4613C10.8807 10.2304 10.2423 10.2721 8.9789 10.2453C7.57789 10.1972 7.11184 9.9626 6.86356 9.77373C6.62548 9.58212 6.74518 9.05204 7.76528 8.5851C8.27917 8.33646 10.2935 7.87759 10.2935 7.87759C9.61511 7.54227 8.35014 6.95284 8.09005 6.82552C7.86199 6.71388 7.49701 6.54572 7.4179 6.34233C7.32824 6.14709 7.6297 5.97888 7.79813 5.9307C8.34057 5.77424 9.10635 5.67701 9.8033 5.66609C10.1536 5.66061 10.2105 5.63806 10.2105 5.63806C10.6939 5.55787 11.0121 5.22722 10.8795 4.70348Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2716_663">
<rect width="12" height="12" fill="white" transform="translate(2.5 2)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.6725 13.9985C3.36161 13.9982 3.06354 13.8746 2.84371 13.6548C2.62388 13.435 2.50026 13.1369 2.5 12.826V7.494C2.5 6.8325 2.7675 6.185 3.2365 5.7165L6.219 2.736C6.45192 2.50247 6.72867 2.31724 7.03335 2.19094C7.33804 2.06464 7.66467 1.99975 7.9945 2H13.3275C13.6384 2.00027 13.9365 2.12388 14.1563 2.34371C14.3761 2.56354 14.4997 2.86162 14.5 3.1725V8.5045C14.4983 9.17074 14.2336 9.80936 13.7635 10.2815L10.781 13.264C10.5477 13.4976 10.2706 13.6829 9.96561 13.8092C9.66059 13.9355 9.33364 14.0003 9.0035 14V13.9985H3.6725ZM8.157 10.5715H5.243V11.257H8.157V10.5715ZM4.4815 5.257H11.243V12.0165L13.3715 9.888C13.7373 9.52036 13.9433 9.02316 13.9445 8.5045V3.1725C13.9445 2.8335 13.6685 2.5555 13.3275 2.5555H7.9945C7.73753 2.55499 7.483 2.6053 7.24556 2.70356C7.00813 2.80181 6.79246 2.94606 6.611 3.128L4.4815 5.257ZM4.3855 5.353L3.628 6.11C3.26258 6.47809 3.0569 6.97533 3.0555 7.494V12.826C3.0555 13.165 3.3315 13.443 3.6725 13.443H9.0055C9.26249 13.4434 9.51701 13.3929 9.75445 13.2946C9.99188 13.1963 10.2075 13.052 10.389 12.87L11.145 12.1145H4.3855V5.353Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0945 8.01611C13.0945 7.87619 12.9911 7.79551 12.8642 7.8356L4.13456 10.6038C4.00742 10.6441 3.90427 10.7904 3.90427 10.9301V13.7593C3.90427 13.8992 4.00742 13.9801 4.13456 13.9398L12.8642 11.1719C12.9911 11.1315 13.0945 10.9852 13.0945 10.8453V8.01611Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.90427 7.92597C3.90427 8.06588 4.00742 8.21218 4.13456 8.25252L12.8655 11.0209C12.9926 11.0613 13.0958 10.9803 13.0958 10.8407V8.01124C13.0958 7.87158 12.9926 7.72529 12.8655 7.68494L4.13456 4.91652C4.00742 4.87618 3.90427 4.95686 3.90427 5.09677V7.92597Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0945 2.20248C13.0945 2.06256 12.9911 1.98163 12.8642 2.02197L4.13456 4.78988C4.00742 4.83022 3.90427 4.97652 3.90427 5.11644V7.94563C3.90427 8.08554 4.00742 8.16622 4.13456 8.12614L12.8642 5.35797C12.9911 5.31763 13.0945 5.17133 13.0945 5.03167V2.20248Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0094 13.9181C11.1984 13.9917 11.4139 13.987 11.6047 13.8952L14.0753 12.7064C14.3349 12.5814 14.5 12.3187 14.5 12.0305V3.9696C14.5 3.68136 14.3349 3.41862 14.0753 3.2937L11.6047 2.10485C11.3543 1.98438 11.0614 2.01389 10.8416 2.17363C10.8102 2.19645 10.7803 2.22193 10.7523 2.25001L6.02261 6.56498L3.96246 5.00115C3.77068 4.85558 3.50244 4.86751 3.32432 5.02953L2.66356 5.63059C2.44569 5.82877 2.44544 6.17152 2.66302 6.37004L4.44965 8.00001L2.66302 9.62998C2.44544 9.82849 2.44569 10.1713 2.66356 10.3694L3.32432 10.9705C3.50244 11.1325 3.77068 11.1444 3.96246 10.9989L6.02261 9.43504L10.7523 13.75C10.8271 13.8249 10.915 13.8812 11.0094 13.9181ZM11.5018 5.27587L7.91309 8.00001L11.5018 10.7241V5.27587Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 876 B

View file

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.0001 8.62505C13.0001 11.75 10.8126 13.3125 8.21266 14.2187C8.07651 14.2648 7.92862 14.2626 7.79392 14.2125C5.18771 13.3125 3.00024 11.75 3.00024 8.62505V4.25012C3.00024 4.08436 3.06609 3.92539 3.1833 3.80818C3.30051 3.69098 3.45948 3.62513 3.62523 3.62513C4.87521 3.62513 6.43769 2.87514 7.52517 1.92516C7.65758 1.81203 7.82601 1.74988 8.00016 1.74988C8.17431 1.74988 8.34275 1.81203 8.47515 1.92516C9.56889 2.88139 11.1251 3.62513 12.3751 3.62513C12.5408 3.62513 12.6998 3.69098 12.817 3.80818C12.9342 3.92539 13.0001 4.08436 13.0001 4.25012V8.62505Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 8.00002L7.33333 9.33335L10 6.66669" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 883 B

View file

@ -107,6 +107,12 @@ pub enum IconName {
Disconnected,
DocumentText,
Download,
EditorAtom,
EditorCursor,
EditorEmacs,
EditorJetBrains,
EditorSublime,
EditorVsCode,
Ellipsis,
EllipsisVertical,
Envelope,
@ -229,6 +235,7 @@ pub enum IconName {
Server,
Settings,
SettingsAlt,
ShieldCheck,
Shift,
Slash,
SlashSquare,

View file

@ -43,6 +43,8 @@ fn render_llm_provider_section(
}
fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
let privacy_badge = || Badge::new("Privacy").icon(IconName::ShieldCheck);
v_flex()
.relative()
.pt_2()
@ -71,7 +73,7 @@ fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
.size(IconSize::XSmall),
),
)
.child(Badge::new("PRIVACY").icon(IconName::FileLock)),
.child(privacy_badge()),
)
.child(
Label::new("Re-enable it any time in Settings.")
@ -85,22 +87,17 @@ fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
.justify_between()
.child(Label::new("We don't train models using your data"))
.child(
h_flex()
.gap_1()
.child(Badge::new("Privacy").icon(IconName::FileLock))
.child(
Button::new("learn_more", "Learn More")
.style(ButtonStyle::Outlined)
.label_size(LabelSize::Small)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
.on_click(|_, _, cx| {
cx.open_url(
"https://zed.dev/docs/ai/privacy-and-security",
);
}),
),
h_flex().gap_1().child(privacy_badge()).child(
Button::new("learn_more", "Learn More")
.style(ButtonStyle::Outlined)
.label_size(LabelSize::Small)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
.on_click(|_, _, cx| {
cx.open_url("https://zed.dev/docs/ai/privacy-and-security");
}),
),
),
)
.child(

View file

@ -48,7 +48,11 @@ fn render_theme_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme_registry = ThemeRegistry::global(cx);
let current_theme_name = theme_selection.theme(appearance);
let theme_mode = theme_selection.mode();
let theme_mode = theme_selection.mode().unwrap_or_default();
// let theme_mode = theme_selection.mode();
// TODO: Clean this up once the "System" button inside the
// toggle button group is done
let selected_index = match appearance {
Appearance::Light => 0,
@ -72,8 +76,28 @@ fn render_theme_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
let is_selected = theme.name == current_theme_name;
let name = theme.name.clone();
let colors = cx.theme().colors();
v_flex()
.id(name.clone())
.w_full()
.items_center()
.gap_1()
.child(
div()
.w_full()
.border_2()
.border_color(colors.border_transparent)
.rounded(ThemePreviewTile::CORNER_RADIUS)
.map(|this| {
if is_selected {
this.border_color(colors.border_selected)
} else {
this.opacity(0.8).hover(|s| s.border_color(colors.border))
}
})
.child(ThemePreviewTile::new(theme.clone(), theme_seed)),
)
.child(Label::new(name).color(Color::Muted).size(LabelSize::Small))
.on_click({
let theme_name = theme.name.clone();
move |_, _, cx| {
@ -84,84 +108,45 @@ fn render_theme_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
});
}
})
.flex_1()
.child(
div()
.border_2()
.border_color(colors.border_transparent)
.rounded(ThemePreviewTile::CORNER_RADIUS)
.hover(|mut style| {
if !is_selected {
style.border_color = Some(colors.element_hover);
}
style
})
.when(is_selected, |this| {
this.border_color(colors.border_selected)
})
.cursor_pointer()
.child(ThemePreviewTile::new(theme, theme_seed)),
)
.child(
h_flex()
.justify_center()
.items_baseline()
.child(Label::new(name).color(Color::Muted)),
)
});
return v_flex()
.gap_2()
.child(
h_flex().justify_between().child(Label::new("Theme")).child(
h_flex()
.gap_2()
.child(
ToggleButtonGroup::single_row(
"theme-selector-onboarding-dark-light",
[
ToggleButtonSimple::new("Light", {
let appearance_state = appearance_state.clone();
move |_, _, cx| {
write_appearance_change(
&appearance_state,
Appearance::Light,
cx,
);
}
}),
ToggleButtonSimple::new("Dark", {
let appearance_state = appearance_state.clone();
move |_, _, cx| {
write_appearance_change(
&appearance_state,
Appearance::Dark,
cx,
);
}
}),
],
)
.selected_index(selected_index)
.style(ui::ToggleButtonGroupStyle::Outlined)
.button_width(rems_from_px(64.)),
)
.child(
ToggleButtonGroup::single_row(
"theme-selector-onboarding-system",
[ToggleButtonSimple::new("System", {
let theme = theme_selection.clone();
move |_, _, cx| {
toggle_system_theme_mode(theme.clone(), appearance, cx);
}
})],
)
.selected_index((theme_mode != Some(ThemeMode::System)) as usize)
.style(ui::ToggleButtonGroupStyle::Outlined)
.button_width(rems_from_px(64.)),
),
ToggleButtonGroup::single_row(
"theme-selector-onboarding-dark-light",
[
ToggleButtonSimple::new("Light", {
let appearance_state = appearance_state.clone();
move |_, _, cx| {
write_appearance_change(&appearance_state, Appearance::Light, cx);
}
}),
ToggleButtonSimple::new("Dark", {
let appearance_state = appearance_state.clone();
move |_, _, cx| {
write_appearance_change(&appearance_state, Appearance::Dark, cx);
}
}),
// TODO: Properly put the System back as a button within this group
// Currently, given "System" is not an option in the Appearance enum,
// this button doesn't get selected
ToggleButtonSimple::new("System", {
let theme = theme_selection.clone();
move |_, _, cx| {
toggle_system_theme_mode(theme.clone(), appearance, cx);
}
})
.selected(theme_mode == ThemeMode::System),
],
)
.selected_index(selected_index)
.style(ui::ToggleButtonGroupStyle::Outlined)
.button_width(rems_from_px(64.)),
),
)
.child(h_flex().justify_between().children(theme_previews));
.child(h_flex().gap_4().justify_between().children(theme_previews));
fn write_appearance_change(
appearance_state: &Entity<Appearance>,
@ -210,7 +195,6 @@ fn render_theme_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
};
ThemeSelection::Dynamic { mode, light, dark }
}
ThemeSelection::Dynamic {
mode: _,
light,
@ -311,30 +295,31 @@ pub(crate) fn render_basics_page(window: &mut Window, cx: &mut App) -> impl Into
ToggleButtonGroup::two_rows(
"multiple_row_test",
[
ToggleButtonWithIcon::new("VS Code", IconName::AiZed, |_, _, cx| {
ToggleButtonWithIcon::new("VS Code", IconName::EditorVsCode, |_, _, cx| {
write_keymap_base(BaseKeymap::VSCode, cx);
}),
ToggleButtonWithIcon::new("Jetbrains", IconName::AiZed, |_, _, cx| {
ToggleButtonWithIcon::new("Jetbrains", IconName::EditorJetBrains, |_, _, cx| {
write_keymap_base(BaseKeymap::JetBrains, cx);
}),
ToggleButtonWithIcon::new("Sublime Text", IconName::AiZed, |_, _, cx| {
ToggleButtonWithIcon::new("Sublime Text", IconName::EditorSublime, |_, _, cx| {
write_keymap_base(BaseKeymap::SublimeText, cx);
}),
],
[
ToggleButtonWithIcon::new("Atom", IconName::AiZed, |_, _, cx| {
ToggleButtonWithIcon::new("Atom", IconName::EditorAtom, |_, _, cx| {
write_keymap_base(BaseKeymap::Atom, cx);
}),
ToggleButtonWithIcon::new("Emacs", IconName::AiZed, |_, _, cx| {
ToggleButtonWithIcon::new("Emacs", IconName::EditorEmacs, |_, _, cx| {
write_keymap_base(BaseKeymap::Emacs, cx);
}),
ToggleButtonWithIcon::new("Cursor (Beta)", IconName::AiZed, |_, _, 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(230.))
.button_width(rems_from_px(216.))
.size(ui::ToggleButtonGroupSize::Medium)
.style(ui::ToggleButtonGroupStyle::Outlined)
),
)

View file

@ -143,7 +143,7 @@ fn render_import_settings_section() -> impl IntoElement {
.gap_1p5()
.px_1()
.child(
Icon::new(IconName::Sparkle)
Icon::new(IconName::EditorVsCode)
.color(Color::Muted)
.size(IconSize::XSmall),
)
@ -169,7 +169,7 @@ fn render_import_settings_section() -> impl IntoElement {
.gap_1p5()
.px_1()
.child(
Icon::new(IconName::Sparkle)
Icon::new(IconName::EditorCursor)
.color(Color::Muted)
.size(IconSize::XSmall),
)

View file

@ -14,8 +14,8 @@ use serde::Deserialize;
use settings::{SettingsStore, VsCodeSettingsSource};
use std::sync::Arc;
use ui::{
Avatar, FluentBuilder, Headline, KeyBinding, ParentElement as _, StatefulInteractiveElement,
Vector, VectorName, prelude::*, rems_from_px,
Avatar, ButtonLike, FluentBuilder, Headline, KeyBinding, ParentElement as _,
StatefulInteractiveElement, Vector, VectorName, prelude::*, rems_from_px,
};
use workspace::{
AppState, Workspace, WorkspaceId,
@ -344,12 +344,73 @@ impl Onboarding {
.into_element(),
]),
)
.child(Button::new("skip_all", "Skip All")),
.child(
ButtonLike::new("skip_all")
.child(Label::new("Skip All").ml_1())
.on_click(|_, _, cx| {
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,
);
});
},
);
}),
),
),
)
.child(
if let Some(user) = self.user_store.read(cx).current_user() {
h_flex()
.pl_1p5()
.gap_2()
.child(Avatar::new(user.avatar_uri.clone()))
.child(Label::new(user.github_login.clone()))

View file

@ -35,7 +35,7 @@ impl RenderOnce for ThemePreviewTile {
let item_skeleton = |w: Length, h: Pixels, bg: Hsla| div().w(w).h(h).rounded_full().bg(bg);
let skeleton_height = px(4.);
let skeleton_height = px(2.);
let sidebar_seeded_width = |seed: f32, index: usize| {
let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;
@ -62,12 +62,10 @@ impl RenderOnce for ThemePreviewTile {
.border_color(color.border_transparent)
.bg(color.panel_background)
.child(
div()
v_flex()
.p_2()
.flex()
.flex_col()
.size_full()
.gap(px(4.))
.gap_1()
.children(sidebar_skeleton),
);
@ -143,32 +141,19 @@ impl RenderOnce for ThemePreviewTile {
v_flex()
.size_full()
.p_1()
.gap(px(6.))
.gap_1p5()
.children(lines)
.into_any_element()
};
let pane = div()
.h_full()
.flex_grow()
.flex()
.flex_col()
// .child(
// div()
// .w_full()
// .border_color(color.border)
// .border_b(px(1.))
// .h(relative(0.1))
// .bg(color.tab_bar_background),
// )
.child(
div()
.size_full()
.overflow_hidden()
.bg(color.editor_background)
.p_2()
.child(pseudo_code_skeleton(self.theme.clone(), self.seed)),
);
let pane = v_flex().h_full().flex_grow().child(
div()
.size_full()
.overflow_hidden()
.bg(color.editor_background)
.p_2()
.child(pseudo_code_skeleton(self.theme.clone(), self.seed)),
);
let content = div().size_full().flex().child(sidebar).child(pane);

View file

@ -4,11 +4,14 @@ use gpui::{
};
use ui::{ButtonLike, Divider, DividerColor, KeyBinding, Vector, VectorName, prelude::*};
use workspace::{
NewFile, Open, Workspace, WorkspaceId,
NewFile, Open, WorkspaceId,
item::{Item, ItemEvent},
with_active_or_new_workspace,
};
use zed_actions::{Extensions, OpenSettings, agent, command_palette};
use crate::{Onboarding, OpenOnboarding};
actions!(
zed,
[
@ -216,7 +219,64 @@ impl Render for WelcomePage {
div().child(
Button::new("welcome-exit", "Return to Setup")
.full_width()
.label_size(LabelSize::XSmall),
.label_size(LabelSize::XSmall)
.on_click(|_, window, cx| {
window.dispatch_action(
OpenOnboarding.boxed_clone(),
cx,
);
with_active_or_new_workspace(cx, |workspace, window, cx| {
let Some((welcome_id, welcome_idx)) = workspace
.active_pane()
.read(cx)
.items()
.enumerate()
.find_map(|(idx, item)| {
let _ = item.downcast::<WelcomePage>()?;
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::<Onboarding>()?;
Some(idx)
},
);
if let Some(idx) = idx {
pane.activate_item(
idx, true, true, window, cx,
);
} else {
let item =
Box::new(Onboarding::new(workspace, cx));
pane.add_item(
item,
true,
true,
Some(welcome_idx),
window,
cx,
);
}
pane.remove_item(
welcome_id,
false,
false,
window,
cx,
);
});
});
}),
),
),
),
@ -227,7 +287,7 @@ impl Render for WelcomePage {
}
impl WelcomePage {
pub fn new(window: &mut Window, cx: &mut Context<Workspace>) -> Entity<Self> {
pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
cx.new(|cx| {
let focus_handle = cx.focus_handle();
cx.on_focus(&focus_handle, window, |_, _, cx| cx.notify())

View file

@ -32,7 +32,7 @@ impl RenderOnce for Badge {
.pl_1()
.pr_2()
.border_1()
.border_color(cx.theme().colors().border)
.border_color(cx.theme().colors().border.opacity(0.6))
.bg(cx.theme().colors().element_background)
.rounded_sm()
.overflow_hidden()
@ -42,12 +42,7 @@ impl RenderOnce for Badge {
.color(Color::Muted),
)
.child(Divider::vertical().color(DividerColor::Border))
.child(
Label::new(self.label.clone())
.size(LabelSize::XSmall)
.buffer_font(cx)
.ml_1(),
)
.child(Label::new(self.label.clone()).size(LabelSize::Small).ml_1())
}
}

View file

@ -358,6 +358,7 @@ impl ButtonStyle {
#[derive(Default, PartialEq, Clone, Copy)]
pub enum ButtonSize {
Large,
Medium,
#[default]
Default,
Compact,
@ -368,6 +369,7 @@ impl ButtonSize {
pub fn rems(self) -> Rems {
match self {
ButtonSize::Large => rems_from_px(32.),
ButtonSize::Medium => rems_from_px(28.),
ButtonSize::Default => rems_from_px(22.),
ButtonSize::Compact => rems_from_px(18.),
ButtonSize::None => rems_from_px(16.),
@ -573,7 +575,7 @@ impl RenderOnce for ButtonLike {
})
.gap(DynamicSpacing::Base04.rems(cx))
.map(|this| match self.size {
ButtonSize::Large => this.px(DynamicSpacing::Base06.rems(cx)),
ButtonSize::Large | ButtonSize::Medium => this.px(DynamicSpacing::Base06.rems(cx)),
ButtonSize::Default | ButtonSize::Compact => {
this.px(DynamicSpacing::Base04.rems(cx))
}

View file

@ -295,6 +295,7 @@ pub struct ButtonConfiguration {
label: SharedString,
icon: Option<IconName>,
on_click: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
selected: bool,
}
mod private {
@ -308,6 +309,7 @@ pub trait ButtonBuilder: 'static + private::ToggleButtonStyle {
pub struct ToggleButtonSimple {
label: SharedString,
on_click: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
selected: bool,
}
impl ToggleButtonSimple {
@ -318,8 +320,14 @@ impl ToggleButtonSimple {
Self {
label: label.into(),
on_click: Box::new(on_click),
selected: false,
}
}
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
}
impl private::ToggleButtonStyle for ToggleButtonSimple {}
@ -330,6 +338,7 @@ impl ButtonBuilder for ToggleButtonSimple {
label: self.label,
icon: None,
on_click: self.on_click,
selected: self.selected,
}
}
}
@ -338,6 +347,7 @@ pub struct ToggleButtonWithIcon {
label: SharedString,
icon: IconName,
on_click: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
selected: bool,
}
impl ToggleButtonWithIcon {
@ -350,8 +360,14 @@ impl ToggleButtonWithIcon {
label: label.into(),
icon,
on_click: Box::new(on_click),
selected: false,
}
}
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
}
impl private::ToggleButtonStyle for ToggleButtonWithIcon {}
@ -362,6 +378,7 @@ impl ButtonBuilder for ToggleButtonWithIcon {
label: self.label,
icon: Some(self.icon),
on_click: self.on_click,
selected: self.selected,
}
}
}
@ -373,6 +390,12 @@ pub enum ToggleButtonGroupStyle {
Outlined,
}
#[derive(Clone, Copy, PartialEq)]
pub enum ToggleButtonGroupSize {
Default,
Medium,
}
#[derive(IntoElement)]
pub struct ToggleButtonGroup<T, const COLS: usize = 3, const ROWS: usize = 1>
where
@ -381,6 +404,7 @@ where
group_name: &'static str,
rows: [[T; COLS]; ROWS],
style: ToggleButtonGroupStyle,
size: ToggleButtonGroupSize,
button_width: Rems,
selected_index: usize,
}
@ -391,6 +415,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS> {
group_name,
rows: [buttons],
style: ToggleButtonGroupStyle::Transparent,
size: ToggleButtonGroupSize::Default,
button_width: rems_from_px(100.),
selected_index: 0,
}
@ -403,6 +428,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS, 2> {
group_name,
rows: [first_row, second_row],
style: ToggleButtonGroupStyle::Transparent,
size: ToggleButtonGroupSize::Default,
button_width: rems_from_px(100.),
selected_index: 0,
}
@ -415,6 +441,11 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> ToggleButtonGroup<T
self
}
pub fn size(mut self, size: ToggleButtonGroupSize) -> Self {
self.size = size;
self
}
pub fn button_width(mut self, button_width: Rems) -> Self {
self.button_width = button_width;
self
@ -430,53 +461,56 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
for ToggleButtonGroup<T, COLS, ROWS>
{
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let entries = self.rows.into_iter().enumerate().map(|(row_index, row)| {
row.into_iter().enumerate().map(move |(col_index, button)| {
let ButtonConfiguration {
label,
icon,
on_click,
} = button.into_configuration();
let entries =
self.rows.into_iter().enumerate().map(|(row_index, row)| {
row.into_iter().enumerate().map(move |(col_index, button)| {
let ButtonConfiguration {
label,
icon,
on_click,
selected,
} = button.into_configuration();
let entry_index = row_index * COLS + col_index;
let entry_index = row_index * COLS + col_index;
ButtonLike::new((self.group_name, entry_index))
.when(entry_index == self.selected_index, |this| {
this.toggle_state(true)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
})
.rounding(None)
.when(self.style == ToggleButtonGroupStyle::Filled, |button| {
button.style(ButtonStyle::Filled)
})
.child(
h_flex()
.min_w(self.button_width)
.gap_1p5()
.px_3()
.py_1()
.justify_center()
.when_some(icon, |this, icon| {
this.child(Icon::new(icon).size(IconSize::XSmall).map(|this| {
if entry_index == self.selected_index {
this.color(Color::Accent)
} else {
this.color(Color::Muted)
}
}))
})
.child(
Label::new(label)
.size(LabelSize::Small)
.when(entry_index == self.selected_index, |this| {
this.color(Color::Accent)
}),
),
)
.on_click(on_click)
.into_any_element()
})
});
ButtonLike::new((self.group_name, entry_index))
.when(entry_index == self.selected_index || selected, |this| {
this.toggle_state(true)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
})
.rounding(None)
.when(self.style == ToggleButtonGroupStyle::Filled, |button| {
button.style(ButtonStyle::Filled)
})
.when(self.size == ToggleButtonGroupSize::Medium, |button| {
button.size(ButtonSize::Medium)
})
.child(
h_flex()
.min_w(self.button_width)
.gap_1p5()
.px_3()
.py_1()
.justify_center()
.when_some(icon, |this, icon| {
this.py_2()
.child(Icon::new(icon).size(IconSize::XSmall).map(|this| {
if entry_index == self.selected_index || selected {
this.color(Color::Accent)
} else {
this.color(Color::Muted)
}
}))
})
.child(Label::new(label).size(LabelSize::Small).when(
entry_index == self.selected_index || selected,
|this| this.color(Color::Accent),
)),
)
.on_click(on_click)
.into_any_element()
})
});
let border_color = cx.theme().colors().border.opacity(0.6);
let is_outlined_or_filled = self.style == ToggleButtonGroupStyle::Outlined