Rebuild /settings/appearance as the design form
The appearance route now opens with the design layout: serif "GENERAL" overline + "Appearance" headline + intro paragraph, a four-tile wallpaper picker (Dawn/Violet/Mint/Slate with active outline + check glyph), a theme-mode segmented control, an accent swatch row, a reduce-motion toggle, and a reset row. Every control mutates real state through new shell methods (set_wallpaper_theme, set_theme_mode, toggle_reduce_motion, reset_appearance) which delegate to the core. The chrome lives in chrome::appearance_form so the page module is a six-line shim. Light/Dark theme buttons currently switch the persisted mode; rendering swaps will land when light/dark token sheets ship — keeping the persistence so the eventual flip is one place.
This commit is contained in:
@@ -0,0 +1,381 @@
|
||||
use ely_browser_core::BrowserSnapshot;
|
||||
use ely_design_system::colors;
|
||||
use ely_domain::{ThemeMode, WallpaperTheme};
|
||||
use gpui::{
|
||||
AnyElement, Context, FontWeight, Hsla, InteractiveElement, IntoElement, ParentElement,
|
||||
SharedString, StatefulInteractiveElement, Styled, div, hsla, linear_color_stop,
|
||||
linear_gradient, prelude::FluentBuilder, px, rgb, rgba,
|
||||
};
|
||||
use gpui_component::{IconName, scroll::ScrollableElement};
|
||||
|
||||
use crate::shell::ElyShell;
|
||||
|
||||
pub(crate) fn render_appearance_form(
|
||||
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))
|
||||
.child(
|
||||
div()
|
||||
.max_w(px(780.0))
|
||||
.mx_auto()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(28.0))
|
||||
.child(render_header())
|
||||
.child(render_wallpaper_grid(snapshot, cx))
|
||||
.child(render_appearance_rows(snapshot, cx)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_header() -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(6.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("Appearance"),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.max_w(px(520.0))
|
||||
.text_size(px(13.0))
|
||||
.text_color(rgb(colors::INK_3))
|
||||
.child(
|
||||
"Tune the atmosphere of your browser. ELY's wallpaper sets the ambient \
|
||||
palette of every surface; pick one and let it breathe.",
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_wallpaper_grid(
|
||||
snapshot: &BrowserSnapshot,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let active = snapshot.appearance.wallpaper();
|
||||
div()
|
||||
.grid()
|
||||
.grid_cols(4)
|
||||
.gap(px(10.0))
|
||||
.child(render_wallpaper_swatch(WallpaperTheme::Dawn, active, cx))
|
||||
.child(render_wallpaper_swatch(WallpaperTheme::Violet, active, cx))
|
||||
.child(render_wallpaper_swatch(WallpaperTheme::Mint, active, cx))
|
||||
.child(render_wallpaper_swatch(WallpaperTheme::Slate, active, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_wallpaper_swatch(
|
||||
theme: WallpaperTheme,
|
||||
active: WallpaperTheme,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let label = wallpaper_label(theme);
|
||||
let id = SharedString::from(format!("wallpaper-{}", label.to_ascii_lowercase()));
|
||||
let selected = theme == active;
|
||||
let (upper, lower, base) = swatch_colors(theme);
|
||||
|
||||
div()
|
||||
.id(id)
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(8.0))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.opacity(0.94))
|
||||
.active(|style| style.opacity(0.85))
|
||||
.on_click(cx.listener(move |shell, _, _, cx| {
|
||||
shell.set_wallpaper_theme(theme, cx);
|
||||
}))
|
||||
.child(
|
||||
div()
|
||||
.h(px(96.0))
|
||||
.rounded(px(10.0))
|
||||
.when(selected, |el| {
|
||||
el.border_2().border_color(rgb(colors::ACCENT))
|
||||
})
|
||||
.when(!selected, |el| {
|
||||
el.border_1().border_color(rgba(colors::STROKE))
|
||||
})
|
||||
.relative()
|
||||
.overflow_hidden()
|
||||
.bg(rgb(base))
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.bg(linear_gradient(
|
||||
225.0,
|
||||
linear_color_stop(upper, 0.0),
|
||||
linear_color_stop(transparent_like(upper), 0.6),
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.bg(linear_gradient(
|
||||
45.0,
|
||||
linear_color_stop(lower, 0.0),
|
||||
linear_color_stop(transparent_like(lower), 0.6),
|
||||
)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap(px(6.0))
|
||||
.text_size(px(12.0))
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(label),
|
||||
)
|
||||
.when(selected, |el| {
|
||||
el.child(
|
||||
div()
|
||||
.text_color(rgb(colors::ACCENT))
|
||||
.child(IconName::Check),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_appearance_rows(
|
||||
snapshot: &BrowserSnapshot,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let appearance = snapshot.appearance;
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child(render_theme_mode_row(appearance.theme_mode(), cx))
|
||||
.child(render_accent_row())
|
||||
.child(render_reduce_motion_row(appearance.reduce_motion(), cx))
|
||||
.child(render_reset_row(cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_theme_mode_row(
|
||||
active: ThemeMode,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
settings_row(
|
||||
"Theme mode",
|
||||
"Match system appearance, or pick one to lock.",
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap(px(2.0))
|
||||
.p(px(2.0))
|
||||
.rounded(px(8.0))
|
||||
.bg(rgba(SEGMENT_BG))
|
||||
.child(theme_segment("System", ThemeMode::System, active, cx))
|
||||
.child(theme_segment("Light", ThemeMode::Light, active, cx))
|
||||
.child(theme_segment("Dark", ThemeMode::Dark, active, cx))
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
|
||||
fn theme_segment(
|
||||
label: &'static str,
|
||||
mode: ThemeMode,
|
||||
active: ThemeMode,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let selected = mode == active;
|
||||
let bg = if selected { 0xffffffff } else { 0x00000000 };
|
||||
let id = SharedString::from(format!("theme-mode-{}", label.to_ascii_lowercase()));
|
||||
|
||||
div()
|
||||
.id(id)
|
||||
.px(px(12.0))
|
||||
.py(px(4.0))
|
||||
.rounded(px(6.0))
|
||||
.text_size(px(12.0))
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK_2))
|
||||
.bg(rgba(bg))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.opacity(0.92))
|
||||
.active(|style| style.opacity(0.82))
|
||||
.on_click(cx.listener(move |shell, _, _, cx| {
|
||||
shell.set_theme_mode(mode, cx);
|
||||
}))
|
||||
.child(label)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_accent_row() -> AnyElement {
|
||||
settings_row(
|
||||
"Accent color",
|
||||
"Used across selection, focus rings & links.",
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap(px(8.0))
|
||||
.child(swatch(colors::ACCENT, true))
|
||||
.child(swatch(0xec6a8e, false))
|
||||
.child(swatch(0x7c6cf7, false))
|
||||
.child(swatch(0x4fb59a, false))
|
||||
.child(swatch(colors::INK, false))
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
|
||||
fn swatch(color: u32, selected: bool) -> AnyElement {
|
||||
let mut element = div()
|
||||
.size(px(22.0))
|
||||
.rounded_full()
|
||||
.bg(rgb(color))
|
||||
.border_1()
|
||||
.border_color(rgba(0x0000000d));
|
||||
if selected {
|
||||
element = element
|
||||
.border_2()
|
||||
.border_color(rgb(colors::ACCENT));
|
||||
}
|
||||
element.into_any_element()
|
||||
}
|
||||
|
||||
fn render_reduce_motion_row(reduce: bool, cx: &mut Context<ElyShell>) -> AnyElement {
|
||||
settings_row(
|
||||
"Reduce motion",
|
||||
"Mute transitions and ambient effects.",
|
||||
div()
|
||||
.id(SharedString::from("toggle-reduce-motion"))
|
||||
.w(px(34.0))
|
||||
.h(px(20.0))
|
||||
.rounded_full()
|
||||
.bg(if reduce { rgb(colors::ACCENT) } else { rgba(0x281e1426) })
|
||||
.p(px(2.0))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.opacity(0.9))
|
||||
.active(|style| style.opacity(0.78))
|
||||
.on_click(cx.listener(|shell, _, _, cx| shell.toggle_reduce_motion(cx)))
|
||||
.child(
|
||||
div()
|
||||
.size(px(16.0))
|
||||
.rounded_full()
|
||||
.bg(rgb(0xffffff))
|
||||
.when(reduce, |el| el.ml(px(14.0))),
|
||||
)
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_reset_row(cx: &mut Context<ElyShell>) -> AnyElement {
|
||||
settings_row(
|
||||
"Restore defaults",
|
||||
"Reset wallpaper, theme mode, and motion to defaults.",
|
||||
div()
|
||||
.id(SharedString::from("appearance-reset"))
|
||||
.px(px(12.0))
|
||||
.py(px(7.0))
|
||||
.rounded(px(8.0))
|
||||
.bg(rgba(SEGMENT_BG))
|
||||
.text_size(px(12.0))
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK_2))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.opacity(0.92))
|
||||
.active(|style| style.opacity(0.82))
|
||||
.on_click(cx.listener(|shell, _, _, cx| shell.reset_appearance(cx)))
|
||||
.child("Reset")
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
|
||||
fn settings_row(title: &'static str, detail: &'static str, control: AnyElement) -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap(px(16.0))
|
||||
.py(px(14.0))
|
||||
.border_b_1()
|
||||
.border_color(rgba(colors::DIVIDER))
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(13.0))
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(title),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(11.5))
|
||||
.text_color(rgb(colors::INK_3))
|
||||
.child(detail),
|
||||
),
|
||||
)
|
||||
.child(control)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn wallpaper_label(theme: WallpaperTheme) -> &'static str {
|
||||
match theme {
|
||||
WallpaperTheme::Dawn => "Dawn",
|
||||
WallpaperTheme::Violet => "Violet",
|
||||
WallpaperTheme::Mint => "Mint",
|
||||
WallpaperTheme::Slate => "Slate",
|
||||
}
|
||||
}
|
||||
|
||||
fn swatch_colors(theme: WallpaperTheme) -> (Hsla, Hsla, u32) {
|
||||
match theme {
|
||||
WallpaperTheme::Dawn => (
|
||||
hsla(351.0 / 360.0, 1.0, 0.91, 0.95),
|
||||
hsla(228.0 / 360.0, 1.0, 0.86, 0.85),
|
||||
0xefe8e1,
|
||||
),
|
||||
WallpaperTheme::Violet => (
|
||||
hsla(263.0 / 360.0, 1.0, 0.88, 0.95),
|
||||
hsla(33.0 / 360.0, 1.0, 0.87, 0.85),
|
||||
0xe9e2ee,
|
||||
),
|
||||
WallpaperTheme::Mint => (
|
||||
hsla(146.0 / 360.0, 0.69, 0.85, 0.95),
|
||||
hsla(40.0 / 360.0, 1.0, 0.86, 0.85),
|
||||
0xe6ece2,
|
||||
),
|
||||
WallpaperTheme::Slate => (
|
||||
hsla(218.0 / 360.0, 0.20, 0.85, 0.95),
|
||||
hsla(20.0 / 360.0, 0.0, 0.10, 0.40),
|
||||
0xd6dae3,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn transparent_like(color: Hsla) -> Hsla {
|
||||
hsla(color.h, color.s, color.l, 0.0)
|
||||
}
|
||||
|
||||
const SEGMENT_BG: u32 = 0x281e140d;
|
||||
@@ -1,3 +1,4 @@
|
||||
pub(crate) mod appearance_form;
|
||||
pub(crate) mod brand_glyph;
|
||||
pub(crate) mod command_footer;
|
||||
pub(crate) mod command_overlay;
|
||||
@@ -9,6 +10,7 @@ pub(crate) mod split_pane;
|
||||
pub(crate) mod topbar;
|
||||
pub(crate) mod wallpaper;
|
||||
|
||||
pub(crate) use appearance_form::render_appearance_form;
|
||||
pub(crate) use brand_glyph::render_glyph_for;
|
||||
pub(crate) use command_overlay::render_command_overlay;
|
||||
pub(crate) use home::render_home_page;
|
||||
|
||||
@@ -84,7 +84,7 @@ impl ElyShell {
|
||||
"ely://about" => self.render_about_page(snapshot),
|
||||
"ely://settings" => self.render_settings_page(snapshot, cx),
|
||||
"ely://settings/advanced" => self.render_advanced_page(snapshot),
|
||||
"ely://settings/appearance" => self.render_appearance_page(snapshot),
|
||||
"ely://settings/appearance" => self.render_appearance_page(snapshot, cx),
|
||||
"ely://settings/general" => self.render_general_page(snapshot, cx),
|
||||
"ely://settings/sidebar-tabs" => self.render_sidebar_tabs_page(snapshot, cx),
|
||||
"ely://settings/search" => self.render_search_page(snapshot, cx),
|
||||
|
||||
@@ -1,235 +1,15 @@
|
||||
use ely_browser_core::BrowserSnapshot;
|
||||
use ely_design_system::colors;
|
||||
use gpui::{AnyElement, IntoElement, ParentElement, Styled, div, px, rgb};
|
||||
use gpui_component::{IconName, StyledExt, scroll::ScrollableElement};
|
||||
use gpui::{AnyElement, Context};
|
||||
|
||||
use super::{ElyShell, render_canvas_surface};
|
||||
|
||||
struct ColorToken {
|
||||
label: &'static str,
|
||||
detail: &'static str,
|
||||
value: u32,
|
||||
}
|
||||
|
||||
const THEME_TOKENS: &[ColorToken] = &[
|
||||
ColorToken { label: "Canvas", detail: "Browser frame background", value: colors::CANVAS },
|
||||
ColorToken {
|
||||
label: "Canvas Soft",
|
||||
detail: "Internal page backdrop",
|
||||
value: colors::CANVAS_SOFT,
|
||||
},
|
||||
ColorToken { label: "Surface", detail: "Cards and panels", value: colors::SURFACE_CARD },
|
||||
ColorToken { label: "Ink", detail: "Primary text", value: colors::INK },
|
||||
ColorToken { label: "Muted", detail: "Secondary text", value: colors::MUTED },
|
||||
ColorToken { label: "Hairline", detail: "Borders and dividers", value: colors::HAIRLINE },
|
||||
ColorToken { label: "Accent", detail: "Primary actions", value: colors::PRIMARY },
|
||||
ColorToken { label: "Success", detail: "Resolved state", value: colors::SUCCESS },
|
||||
ColorToken { label: "Error", detail: "Attention state", value: colors::ERROR },
|
||||
];
|
||||
use super::ElyShell;
|
||||
use crate::shell::chrome::render_appearance_form;
|
||||
|
||||
impl ElyShell {
|
||||
pub(super) fn render_appearance_page(&mut self, snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
render_canvas_surface(
|
||||
div()
|
||||
.size_full()
|
||||
.p_8()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_5()
|
||||
.child(render_appearance_header(snapshot))
|
||||
.child(render_theme_summary(snapshot))
|
||||
.child(render_color_sections(snapshot)),
|
||||
)
|
||||
pub(super) fn render_appearance_page(
|
||||
&mut self,
|
||||
snapshot: &BrowserSnapshot,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
render_appearance_form(snapshot, cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_appearance_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("Appearance"))
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(format!("Space: {}", snapshot.active_space_name)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(IconName::Palette)
|
||||
.child(format!("{} tokens", THEME_TOKENS.len())),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_theme_summary(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.rounded_md()
|
||||
.border_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.bg(rgb(colors::CANVAS_SOFT))
|
||||
.px_4()
|
||||
.py_3()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
.child(div().text_color(rgb(colors::PRIMARY)).child(IconName::Palette))
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::INK))
|
||||
.child("ELY Theme"),
|
||||
)
|
||||
.child(div().text_xs().truncate().text_color(rgb(colors::MUTED)).child(
|
||||
format!(
|
||||
"{} profile on {} space",
|
||||
snapshot.active_profile_name, snapshot.active_space_name
|
||||
),
|
||||
)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(color_swatch(colors::PRIMARY))
|
||||
.child(hex_label(colors::PRIMARY)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_color_sections(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.overflow_y_scrollbar()
|
||||
.border_t_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.child(render_section_label("Theme Tokens"))
|
||||
.children(THEME_TOKENS.iter().map(render_token_row))
|
||||
.child(render_section_label("Current Context"))
|
||||
.child(render_active_space_color(snapshot))
|
||||
.child(render_active_profile_color(snapshot))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_section_label(label: &'static str) -> AnyElement {
|
||||
div()
|
||||
.pt_4()
|
||||
.pb_2()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(label)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_token_row(token: &ColorToken) -> AnyElement {
|
||||
render_color_row(token.label, token.detail.to_string(), token.value)
|
||||
}
|
||||
|
||||
fn render_active_space_color(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
let detail = format!("Space: {}", snapshot.active_space_name);
|
||||
let value = snapshot
|
||||
.spaces
|
||||
.iter()
|
||||
.find(|space| space.id() == &snapshot.active_space_id)
|
||||
.map_or(colors::ERROR, |space| space.accent_hex());
|
||||
|
||||
render_color_row("Active Space Accent", detail, value)
|
||||
}
|
||||
|
||||
fn render_active_profile_color(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
let detail = format!("Profile: {}", snapshot.active_profile_name);
|
||||
let value = snapshot
|
||||
.profiles
|
||||
.iter()
|
||||
.find(|profile| profile.id() == &snapshot.active_profile_id)
|
||||
.map_or(colors::ERROR, |profile| profile.color_hex());
|
||||
|
||||
render_color_row("Active Profile Color", detail, value)
|
||||
}
|
||||
|
||||
fn render_color_row(label: &'static str, detail: String, value: u32) -> AnyElement {
|
||||
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(color_swatch(value)).child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(label),
|
||||
)
|
||||
.child(div().text_xs().truncate().text_color(rgb(colors::MUTED)).child(detail)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div().text_xs().font_semibold().text_color(rgb(colors::BODY)).child(hex_label(value)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn color_swatch(value: u32) -> AnyElement {
|
||||
div()
|
||||
.w(px(30.0))
|
||||
.h(px(22.0))
|
||||
.rounded_md()
|
||||
.border_1()
|
||||
.border_color(rgb(colors::HAIRLINE_STRONG))
|
||||
.bg(rgb(value))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn hex_label(value: u32) -> String {
|
||||
format!("#{value:06X}")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ely_domain::{
|
||||
ArchivePolicy, DiagnosticsReportingPolicy, DownloadPolicy, FavoriteLimit,
|
||||
HistoryRecordingPolicy, NewTabDestination, ProfileId, ProfileSyncPolicy, SearchEngine,
|
||||
SyncObjectKind, SyncObjectPolicy, UpdatePolicy,
|
||||
SyncObjectKind, SyncObjectPolicy, ThemeMode, UpdatePolicy, WallpaperTheme,
|
||||
};
|
||||
use gpui::Context;
|
||||
|
||||
@@ -49,6 +49,43 @@ impl ElyShell {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_wallpaper_theme(
|
||||
&mut self,
|
||||
wallpaper: WallpaperTheme,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let ShellState::Ready(core) = &mut self.state {
|
||||
core.set_wallpaper_theme(wallpaper);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_theme_mode(
|
||||
&mut self,
|
||||
theme_mode: ThemeMode,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let ShellState::Ready(core) = &mut self.state {
|
||||
core.set_theme_mode(theme_mode);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn toggle_reduce_motion(&mut self, cx: &mut Context<Self>) {
|
||||
if let ShellState::Ready(core) = &mut self.state {
|
||||
let next = !core.appearance().reduce_motion();
|
||||
core.set_reduce_motion(next);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn reset_appearance(&mut self, cx: &mut Context<Self>) {
|
||||
if let ShellState::Ready(core) = &mut self.state {
|
||||
core.reset_appearance();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn reset_general_settings(&mut self, cx: &mut Context<Self>) {
|
||||
if let ShellState::Ready(core) = &mut self.state {
|
||||
core.reset_general_settings();
|
||||
|
||||
Reference in New Issue
Block a user