Restructure settings landing as design's nav + content split
Settings now opens with the design's two-column layout: a 232 wide nav column on the left with grouped sections (General, Account, Power, About) and a content column on the right that leads with a serif headline, snapshot metric cards, and a "Jump in" list of every settings route. Each nav entry and jump row deep-links to the existing internal page so the destinations remain real. The previous settings.rs is now a thin shim that calls into the chrome module — keeps the module under 500 lines and lets future sub-page redesigns share the same shell layout.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
pub(crate) mod command_overlay;
|
pub(crate) mod command_overlay;
|
||||||
pub(crate) mod home;
|
pub(crate) mod home;
|
||||||
|
pub(crate) mod settings_layout;
|
||||||
pub(crate) mod sidebar;
|
pub(crate) mod sidebar;
|
||||||
pub(crate) mod sidebar_header;
|
pub(crate) mod sidebar_header;
|
||||||
pub(crate) mod topbar;
|
pub(crate) mod topbar;
|
||||||
@@ -7,6 +8,7 @@ pub(crate) mod wallpaper;
|
|||||||
|
|
||||||
pub(crate) use command_overlay::render_command_overlay;
|
pub(crate) use command_overlay::render_command_overlay;
|
||||||
pub(crate) use home::render_home_page;
|
pub(crate) use home::render_home_page;
|
||||||
|
pub(crate) use settings_layout::render_settings_landing;
|
||||||
pub(crate) use sidebar::{PANEL_BG, panel_shadow};
|
pub(crate) use sidebar::{PANEL_BG, panel_shadow};
|
||||||
pub(crate) use sidebar_header::render_sidebar_header;
|
pub(crate) use sidebar_header::render_sidebar_header;
|
||||||
pub(crate) use topbar::render_topbar;
|
pub(crate) use topbar::render_topbar;
|
||||||
|
|||||||
@@ -0,0 +1,406 @@
|
|||||||
|
use ely_browser_core::BrowserSnapshot;
|
||||||
|
use ely_design_system::colors;
|
||||||
|
use gpui::{
|
||||||
|
AnyElement, Context, FontWeight, InteractiveElement, IntoElement, ParentElement, SharedString,
|
||||||
|
StatefulInteractiveElement, Styled, div, px, rgb, rgba,
|
||||||
|
};
|
||||||
|
use gpui_component::{IconName, scroll::ScrollableElement};
|
||||||
|
|
||||||
|
use crate::shell::ElyShell;
|
||||||
|
|
||||||
|
struct NavGroup {
|
||||||
|
label: &'static str,
|
||||||
|
items: &'static [NavItem],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NavItem {
|
||||||
|
icon: IconName,
|
||||||
|
label: &'static str,
|
||||||
|
route: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NAV_GROUPS: &[NavGroup] = &[
|
||||||
|
NavGroup {
|
||||||
|
label: "GENERAL",
|
||||||
|
items: &[
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::Palette,
|
||||||
|
label: "Appearance",
|
||||||
|
route: "ely://settings/appearance",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::PanelLeft,
|
||||||
|
label: "Sidebar & Tabs",
|
||||||
|
route: "ely://settings/sidebar-tabs",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::GalleryVerticalEnd,
|
||||||
|
label: "Spaces",
|
||||||
|
route: "ely://settings/spaces",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::Search,
|
||||||
|
label: "Search",
|
||||||
|
route: "ely://settings/search",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::SquareTerminal,
|
||||||
|
label: "Shortcuts",
|
||||||
|
route: "ely://settings/shortcuts",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
NavGroup {
|
||||||
|
label: "ACCOUNT",
|
||||||
|
items: &[
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::ChartPie,
|
||||||
|
label: "Sync",
|
||||||
|
route: "ely://settings/sync",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::Eye,
|
||||||
|
label: "Privacy & Security",
|
||||||
|
route: "ely://settings/privacy-security",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::CircleUser,
|
||||||
|
label: "Profiles",
|
||||||
|
route: "ely://settings/profiles",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::Folder,
|
||||||
|
label: "Downloads",
|
||||||
|
route: "ely://settings/downloads",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::Globe,
|
||||||
|
label: "Site Permissions",
|
||||||
|
route: "ely://settings/site-permissions",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
NavGroup {
|
||||||
|
label: "POWER",
|
||||||
|
items: &[
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::Asterisk,
|
||||||
|
label: "Plugins",
|
||||||
|
route: "ely://settings/plugins",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::LoaderCircle,
|
||||||
|
label: "Updates",
|
||||||
|
route: "ely://settings/updates",
|
||||||
|
},
|
||||||
|
NavItem {
|
||||||
|
icon: IconName::Inspector,
|
||||||
|
label: "Advanced",
|
||||||
|
route: "ely://settings/advanced",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
NavGroup {
|
||||||
|
label: "ABOUT",
|
||||||
|
items: &[NavItem {
|
||||||
|
icon: IconName::Info,
|
||||||
|
label: "About",
|
||||||
|
route: "ely://about",
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
pub(crate) fn render_settings_landing(
|
||||||
|
snapshot: &BrowserSnapshot,
|
||||||
|
active_route: &str,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.h_full()
|
||||||
|
.flex()
|
||||||
|
.child(render_nav_column(snapshot, active_route, cx))
|
||||||
|
.child(render_content_column(snapshot, cx))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_nav_column(
|
||||||
|
snapshot: &BrowserSnapshot,
|
||||||
|
active_route: &str,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.w(px(232.0))
|
||||||
|
.h_full()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.border_r_1()
|
||||||
|
.border_color(rgba(colors::DIVIDER))
|
||||||
|
.px(px(10.0))
|
||||||
|
.pt(px(16.0))
|
||||||
|
.pb(px(12.0))
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(2.0))
|
||||||
|
.overflow_y_scrollbar()
|
||||||
|
.child(render_nav_brand(snapshot))
|
||||||
|
.children(NAV_GROUPS.iter().enumerate().map(|(group_index, group)| {
|
||||||
|
render_nav_group(group_index, group, active_route, cx)
|
||||||
|
}))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_nav_brand(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.px(px(12.0))
|
||||||
|
.pb(px(12.0))
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(18.0))
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child("Settings"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(11.5))
|
||||||
|
.text_color(rgb(colors::INK_4))
|
||||||
|
.child(format!(
|
||||||
|
"ELY 0.42 · Profile: {}",
|
||||||
|
snapshot.active_profile_name
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_nav_group(
|
||||||
|
group_index: usize,
|
||||||
|
group: &NavGroup,
|
||||||
|
active_route: &str,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(1.0))
|
||||||
|
.child(render_nav_label(group.label))
|
||||||
|
.children(group.items.iter().enumerate().map(|(item_index, item)| {
|
||||||
|
render_nav_item(group_index, item_index, item, active_route, cx)
|
||||||
|
}))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_nav_label(label: &'static str) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.pt(px(8.0))
|
||||||
|
.pb(px(4.0))
|
||||||
|
.px(px(12.0))
|
||||||
|
.text_size(px(10.0))
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK_4))
|
||||||
|
.child(label)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_nav_item(
|
||||||
|
group_index: usize,
|
||||||
|
item_index: usize,
|
||||||
|
item: &NavItem,
|
||||||
|
active_route: &str,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let active = item.route == active_route;
|
||||||
|
let bg = if active { ACTIVE_BG } else { 0x00000000 };
|
||||||
|
let text_color = if active { colors::INK } else { colors::INK_2 };
|
||||||
|
let route = item.route;
|
||||||
|
let icon = item.icon.clone();
|
||||||
|
|
||||||
|
div()
|
||||||
|
.id(SharedString::from(format!(
|
||||||
|
"settings-nav-{group_index}-{item_index}"
|
||||||
|
)))
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap(px(10.0))
|
||||||
|
.px(px(12.0))
|
||||||
|
.py(px(7.0))
|
||||||
|
.rounded(px(8.0))
|
||||||
|
.bg(rgba(bg))
|
||||||
|
.text_size(px(13.0))
|
||||||
|
.text_color(rgb(text_color))
|
||||||
|
.cursor_pointer()
|
||||||
|
.hover(|style| style.bg(rgba(HOVER_BG)))
|
||||||
|
.active(|style| style.opacity(0.85))
|
||||||
|
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||||
|
shell.open_internal_tab(route, window, cx);
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_color(rgb(colors::INK_3))
|
||||||
|
.child(icon),
|
||||||
|
)
|
||||||
|
.child(div().flex_1().truncate().child(item.label))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_content_column(
|
||||||
|
snapshot: &BrowserSnapshot,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.h_full()
|
||||||
|
.overflow_y_scrollbar()
|
||||||
|
.pt(px(28.0))
|
||||||
|
.px(px(40.0))
|
||||||
|
.pb(px(32.0))
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(24.0))
|
||||||
|
.child(render_content_header())
|
||||||
|
.child(render_summary_metrics(snapshot))
|
||||||
|
.child(render_quick_links(cx))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_content_header() -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(8.0))
|
||||||
|
.max_w(px(620.0))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(11.0))
|
||||||
|
.text_color(rgb(colors::INK_4))
|
||||||
|
.child("GENERAL"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(34.0))
|
||||||
|
.font_weight(FontWeight(400.0))
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child("Settings"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(13.0))
|
||||||
|
.text_color(rgb(colors::INK_3))
|
||||||
|
.child(
|
||||||
|
"Tune the atmosphere of ELY — choose a wallpaper, configure sync, install \
|
||||||
|
plugins. Each section opens its own page.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_summary_metrics(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.gap(px(10.0))
|
||||||
|
.child(metric_card("Profiles", snapshot.profiles.len()))
|
||||||
|
.child(metric_card("Spaces", snapshot.spaces.len()))
|
||||||
|
.child(metric_card("Plugins", snapshot.installed_plugins.len()))
|
||||||
|
.child(metric_card("Open tabs", snapshot.tabs.len()))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metric_card(label: &'static str, value: usize) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.px(px(14.0))
|
||||||
|
.py(px(12.0))
|
||||||
|
.rounded(px(12.0))
|
||||||
|
.bg(rgba(METRIC_BG))
|
||||||
|
.border_1()
|
||||||
|
.border_color(rgba(colors::STROKE_2))
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(10.5))
|
||||||
|
.text_color(rgb(colors::INK_4))
|
||||||
|
.child(label),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(18.0))
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child(value.to_string()),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_quick_links(cx: &mut Context<ElyShell>) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(8.0))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(11.0))
|
||||||
|
.text_color(rgb(colors::INK_4))
|
||||||
|
.child("JUMP IN"),
|
||||||
|
)
|
||||||
|
.children(NAV_GROUPS.iter().flat_map(|group| group.items).enumerate().map(
|
||||||
|
|(index, item)| render_quick_link(index, item, cx),
|
||||||
|
))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_quick_link(
|
||||||
|
index: usize,
|
||||||
|
item: &NavItem,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let route = item.route;
|
||||||
|
let icon = item.icon.clone();
|
||||||
|
|
||||||
|
div()
|
||||||
|
.id(SharedString::from(format!("settings-jump-{index}")))
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap(px(12.0))
|
||||||
|
.py(px(10.0))
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(rgba(colors::DIVIDER))
|
||||||
|
.cursor_pointer()
|
||||||
|
.hover(|style| style.opacity(0.92))
|
||||||
|
.active(|style| style.opacity(0.78))
|
||||||
|
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||||
|
shell.open_internal_tab(route, window, cx);
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.size(px(28.0))
|
||||||
|
.rounded(px(7.0))
|
||||||
|
.bg(rgba(METRIC_BG))
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.text_color(rgb(colors::INK_3))
|
||||||
|
.child(icon),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.text_size(px(13.0))
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child(item.label),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_color(rgb(colors::INK_4))
|
||||||
|
.child(IconName::ChevronRight),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
const ACTIVE_BG: u32 = 0xffffffd9;
|
||||||
|
const HOVER_BG: u32 = 0xffffff8c;
|
||||||
|
const METRIC_BG: u32 = 0xffffffc7;
|
||||||
@@ -1,114 +1,8 @@
|
|||||||
use ely_browser_core::BrowserSnapshot;
|
use ely_browser_core::BrowserSnapshot;
|
||||||
use ely_design_system::colors;
|
use gpui::{AnyElement, Context};
|
||||||
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
|
|
||||||
use gpui_component::{
|
|
||||||
IconName, Sizable, StyledExt,
|
|
||||||
button::{Button, ButtonVariants},
|
|
||||||
scroll::ScrollableElement,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{ElyShell, render_canvas_surface};
|
use super::ElyShell;
|
||||||
|
use crate::shell::chrome::render_settings_landing;
|
||||||
#[derive(Clone)]
|
|
||||||
struct SettingsRoute {
|
|
||||||
icon: IconName,
|
|
||||||
title: &'static str,
|
|
||||||
detail: &'static str,
|
|
||||||
route: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
const SETTINGS_ROUTES: &[SettingsRoute] = &[
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Settings2,
|
|
||||||
title: "General",
|
|
||||||
detail: "New Tab destination and browser startup defaults.",
|
|
||||||
route: "ely://settings/general",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Palette,
|
|
||||||
title: "Appearance",
|
|
||||||
detail: "Theme tokens, Space accent, and Profile color.",
|
|
||||||
route: "ely://settings/appearance",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::LayoutDashboard,
|
|
||||||
title: "Sidebar & Tabs",
|
|
||||||
detail: "Vertical tabs, pinned area, and auto archive policy.",
|
|
||||||
route: "ely://settings/sidebar-tabs",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::GalleryVerticalEnd,
|
|
||||||
title: "Spaces",
|
|
||||||
detail: "Space identity, accent color, and active context.",
|
|
||||||
route: "ely://settings/spaces",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Search,
|
|
||||||
title: "Search",
|
|
||||||
detail: "Default search engine for Command Bar queries.",
|
|
||||||
route: "ely://settings/search",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Eye,
|
|
||||||
title: "Privacy & Security",
|
|
||||||
detail: "History recording and profile-scoped privacy controls.",
|
|
||||||
route: "ely://settings/privacy-security",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Folder,
|
|
||||||
title: "Downloads",
|
|
||||||
detail: "Profile download location and save behavior.",
|
|
||||||
route: "ely://settings/downloads",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Globe,
|
|
||||||
title: "Site Permissions",
|
|
||||||
detail: "Profile-scoped site permissions and local audit state.",
|
|
||||||
route: "ely://settings/site-permissions",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::CircleUser,
|
|
||||||
title: "Profiles",
|
|
||||||
detail: "Profile identity, color, and download policy.",
|
|
||||||
route: "ely://settings/profiles",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::SquareTerminal,
|
|
||||||
title: "Shortcuts",
|
|
||||||
detail: "Registered key bindings and conflict state.",
|
|
||||||
route: "ely://settings/shortcuts",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Globe,
|
|
||||||
title: "Sync",
|
|
||||||
detail: "Local sync state and object scope.",
|
|
||||||
route: "ely://settings/sync",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::LoaderCircle,
|
|
||||||
title: "Updates",
|
|
||||||
detail: "Build identity and Elydora release manifest contract.",
|
|
||||||
route: "ely://settings/updates",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Inspector,
|
|
||||||
title: "Advanced",
|
|
||||||
detail: "Local runtime policies and audit counters.",
|
|
||||||
route: "ely://settings/advanced",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Asterisk,
|
|
||||||
title: "Plugins",
|
|
||||||
detail: "Installed plugin control and audit trail.",
|
|
||||||
route: "ely://settings/plugins",
|
|
||||||
},
|
|
||||||
SettingsRoute {
|
|
||||||
icon: IconName::Info,
|
|
||||||
title: "About",
|
|
||||||
detail: "Build, runtime, license, and product information.",
|
|
||||||
route: "ely://about",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
impl ElyShell {
|
impl ElyShell {
|
||||||
pub(super) fn render_settings_page(
|
pub(super) fn render_settings_page(
|
||||||
@@ -116,153 +10,6 @@ impl ElyShell {
|
|||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
render_canvas_surface(
|
render_settings_landing(snapshot, "ely://settings", cx)
|
||||||
div()
|
|
||||||
.size_full()
|
|
||||||
.p_8()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_5()
|
|
||||||
.child(render_settings_header(snapshot))
|
|
||||||
.child(render_settings_summary(snapshot))
|
|
||||||
.child(render_settings_routes(cx)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_settings_header(snapshot: &BrowserSnapshot) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_end()
|
|
||||||
.justify_between()
|
|
||||||
.gap_4()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_2()
|
|
||||||
.child(div().text_size(px(26.0)).text_color(rgb(colors::INK)).child("Settings"))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_sm()
|
|
||||||
.truncate()
|
|
||||||
.text_color(rgb(colors::MUTED))
|
|
||||||
.child(format!("Profile: {}", snapshot.active_profile_name)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.font_semibold()
|
|
||||||
.text_color(rgb(colors::MUTED))
|
|
||||||
.child(format!("{} routes", SETTINGS_ROUTES.len())),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_settings_summary(snapshot: &BrowserSnapshot) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.border_t_1()
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(rgb(colors::HAIRLINE))
|
|
||||||
.py_3()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.gap_4()
|
|
||||||
.children([
|
|
||||||
settings_metric("Profiles", snapshot.profiles.len()),
|
|
||||||
settings_metric("Plugins", snapshot.installed_plugins.len()),
|
|
||||||
settings_metric("Sync objects", snapshot.sync_status.objects().len()),
|
|
||||||
settings_metric("Open tabs", snapshot.tabs.len()),
|
|
||||||
])
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn settings_metric(label: &'static str, value: usize) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_1()
|
|
||||||
.child(div().text_xs().text_color(rgb(colors::MUTED)).child(label))
|
|
||||||
.child(
|
|
||||||
div().text_sm().font_semibold().text_color(rgb(colors::INK)).child(value.to_string()),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_settings_routes(cx: &mut Context<ElyShell>) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.min_h_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.overflow_y_scrollbar()
|
|
||||||
.children(
|
|
||||||
SETTINGS_ROUTES
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, route)| render_settings_route(index, route, cx)),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_settings_route(
|
|
||||||
index: usize,
|
|
||||||
settings_route: &SettingsRoute,
|
|
||||||
cx: &mut Context<ElyShell>,
|
|
||||||
) -> AnyElement {
|
|
||||||
let route = settings_route.route;
|
|
||||||
|
|
||||||
div()
|
|
||||||
.py_3()
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(rgb(colors::HAIRLINE))
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.gap_4()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_3()
|
|
||||||
.child(div().text_color(rgb(colors::MUTED)).child(settings_route.icon.clone()))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_sm()
|
|
||||||
.font_semibold()
|
|
||||||
.truncate()
|
|
||||||
.text_color(rgb(colors::INK))
|
|
||||||
.child(settings_route.title),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.truncate()
|
|
||||||
.text_color(rgb(colors::MUTED))
|
|
||||||
.child(settings_route.detail),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new(("open-settings-route", index))
|
|
||||||
.ghost()
|
|
||||||
.xsmall()
|
|
||||||
.label("Open")
|
|
||||||
.tooltip(settings_route.title)
|
|
||||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
|
||||||
shell.open_internal_tab(route, window, cx);
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user