Replace translucency presets with a real continuous slider
ElyShell now hosts a translucency_slider: Entity<SliderState> bound to 0..=100 step 1, defaulting to DEFAULT_TRANSLUCENCY_PCT. A subscription on SliderEvent::Change writes the rounded value into the core via set_translucency_pct, so dragging the thumb mutates the persisted appearance setting in real time. The appearance form swaps the static track-and-thumb visual for the gpui_component Slider (160 wide) plus a live percentage readout. The three preset chips move below the row as fast-set buttons that go through a new set_translucency_pct_from_preset helper which writes both core and the SliderState so the thumb tracks the chip choice. reset_appearance now resets the slider to DEFAULT_TRANSLUCENCY_PCT alongside resetting the core, keeping every UI source of truth in lock-step.
This commit is contained in:
@@ -6,7 +6,7 @@ use gpui::{
|
|||||||
SharedString, StatefulInteractiveElement, Styled, div, hsla, linear_color_stop,
|
SharedString, StatefulInteractiveElement, Styled, div, hsla, linear_color_stop,
|
||||||
linear_gradient, prelude::FluentBuilder, px, rgb, rgba,
|
linear_gradient, prelude::FluentBuilder, px, rgb, rgba,
|
||||||
};
|
};
|
||||||
use gpui_component::{IconName, scroll::ScrollableElement};
|
use gpui_component::{IconName, scroll::ScrollableElement, slider::Slider};
|
||||||
|
|
||||||
use super::appearance_layout_cards::render_sidebar_layout_section;
|
use super::appearance_layout_cards::render_sidebar_layout_section;
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ use crate::shell::ElyShell;
|
|||||||
use crate::shell::chrome::SERIF_FAMILY;
|
use crate::shell::chrome::SERIF_FAMILY;
|
||||||
|
|
||||||
pub(crate) fn render_appearance_form(
|
pub(crate) fn render_appearance_form(
|
||||||
|
shell: &mut ElyShell,
|
||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
@@ -33,7 +34,8 @@ pub(crate) fn render_appearance_form(
|
|||||||
.gap(px(28.0))
|
.gap(px(28.0))
|
||||||
.child(render_header())
|
.child(render_header())
|
||||||
.child(render_wallpaper_grid(snapshot, cx))
|
.child(render_wallpaper_grid(snapshot, cx))
|
||||||
.child(render_appearance_rows(snapshot, cx))
|
.child(render_appearance_rows(shell, snapshot, cx))
|
||||||
|
.child(render_translucency_presets(cx))
|
||||||
.child(render_sidebar_layout_section(snapshot, cx)),
|
.child(render_sidebar_layout_section(snapshot, cx)),
|
||||||
)
|
)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -167,6 +169,7 @@ fn render_wallpaper_swatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render_appearance_rows(
|
fn render_appearance_rows(
|
||||||
|
shell: &mut ElyShell,
|
||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
@@ -177,16 +180,17 @@ fn render_appearance_rows(
|
|||||||
.flex_col()
|
.flex_col()
|
||||||
.child(render_theme_mode_row(appearance.theme_mode(), cx))
|
.child(render_theme_mode_row(appearance.theme_mode(), cx))
|
||||||
.child(render_accent_row())
|
.child(render_accent_row())
|
||||||
.child(render_translucency_row(appearance.translucency_pct(), cx))
|
.child(render_translucency_row(shell, appearance.translucency_pct()))
|
||||||
.child(render_reduce_motion_row(appearance.reduce_motion(), cx))
|
.child(render_reduce_motion_row(appearance.reduce_motion(), cx))
|
||||||
.child(render_reset_row(cx))
|
.child(render_reset_row(cx))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_translucency_row(
|
fn render_translucency_row(
|
||||||
|
shell: &mut ElyShell,
|
||||||
pct: u8,
|
pct: u8,
|
||||||
cx: &mut Context<ElyShell>,
|
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
|
let slider_state = shell.translucency_slider.clone();
|
||||||
settings_row(
|
settings_row(
|
||||||
"Translucency",
|
"Translucency",
|
||||||
"Glass density of sidebar & panels.",
|
"Glass density of sidebar & panels.",
|
||||||
@@ -194,44 +198,41 @@ fn render_translucency_row(
|
|||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap(px(10.0))
|
.gap(px(10.0))
|
||||||
.child(render_translucency_track(pct))
|
.child(div().w(px(160.0)).child(Slider::new(&slider_state)))
|
||||||
.child(translucency_preset("Solid", 0, pct, cx))
|
.child(
|
||||||
.child(translucency_preset("Default", 40, pct, cx))
|
div()
|
||||||
.child(translucency_preset("Glassy", 75, pct, cx))
|
.min_w(px(36.0))
|
||||||
|
.text_size(px(11.0))
|
||||||
|
.text_color(rgb(colors::INK_3))
|
||||||
|
.child(format!("{pct}%")),
|
||||||
|
)
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_translucency_track(pct: u8) -> AnyElement {
|
fn render_translucency_presets(cx: &mut Context<ElyShell>) -> AnyElement {
|
||||||
let clamped = pct.min(100) as f32;
|
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.w(px(120.0))
|
.flex()
|
||||||
.h(px(4.0))
|
.items_center()
|
||||||
.rounded(px(2.0))
|
.gap(px(8.0))
|
||||||
.bg(rgba(TRACK_BG))
|
.pt(px(2.0))
|
||||||
.relative()
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.absolute()
|
.text_size(px(10.5))
|
||||||
.left_0()
|
.text_color(rgb(colors::INK_4))
|
||||||
.top_0()
|
.child("Presets"),
|
||||||
.bottom_0()
|
|
||||||
.w(px(120.0 * clamped / 100.0))
|
|
||||||
.rounded(px(2.0))
|
|
||||||
.bg(rgb(colors::ACCENT)),
|
|
||||||
)
|
)
|
||||||
|
.child(translucency_preset("Solid", 0, cx))
|
||||||
|
.child(translucency_preset("Default", 40, cx))
|
||||||
|
.child(translucency_preset("Glassy", 75, cx))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translucency_preset(
|
fn translucency_preset(
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
target_pct: u8,
|
target_pct: u8,
|
||||||
active_pct: u8,
|
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let selected = active_pct == target_pct;
|
|
||||||
let bg = if selected { 0xffffffff } else { 0x281e140d };
|
|
||||||
let id = SharedString::from(format!("translucency-{}", label.to_ascii_lowercase()));
|
let id = SharedString::from(format!("translucency-{}", label.to_ascii_lowercase()));
|
||||||
|
|
||||||
div()
|
div()
|
||||||
@@ -239,15 +240,15 @@ fn translucency_preset(
|
|||||||
.px(px(10.0))
|
.px(px(10.0))
|
||||||
.py(px(4.0))
|
.py(px(4.0))
|
||||||
.rounded(px(6.0))
|
.rounded(px(6.0))
|
||||||
.bg(rgba(bg))
|
.bg(rgba(SEGMENT_BG))
|
||||||
.text_size(px(11.5))
|
.text_size(px(11.5))
|
||||||
.font_weight(FontWeight(500.0))
|
.font_weight(FontWeight(500.0))
|
||||||
.text_color(rgb(colors::INK_2))
|
.text_color(rgb(colors::INK_2))
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.hover(|style| style.opacity(0.92))
|
.hover(|style| style.opacity(0.92))
|
||||||
.active(|style| style.opacity(0.82))
|
.active(|style| style.opacity(0.82))
|
||||||
.on_click(cx.listener(move |shell, _, _, cx| {
|
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||||
shell.set_translucency_pct(target_pct, cx);
|
shell.set_translucency_pct_from_preset(target_pct, window, cx);
|
||||||
}))
|
}))
|
||||||
.child(label)
|
.child(label)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -377,7 +378,9 @@ fn render_reset_row(cx: &mut Context<ElyShell>) -> AnyElement {
|
|||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.hover(|style| style.opacity(0.92))
|
.hover(|style| style.opacity(0.92))
|
||||||
.active(|style| style.opacity(0.82))
|
.active(|style| style.opacity(0.82))
|
||||||
.on_click(cx.listener(|shell, _, _, cx| shell.reset_appearance(cx)))
|
.on_click(cx.listener(|shell, _, window, cx| {
|
||||||
|
shell.reset_appearance(window, cx);
|
||||||
|
}))
|
||||||
.child("Reset")
|
.child("Reset")
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
@@ -455,4 +458,3 @@ fn transparent_like(color: Hsla) -> Hsla {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SEGMENT_BG: u32 = 0x281e140d;
|
const SEGMENT_BG: u32 = 0x281e140d;
|
||||||
const TRACK_BG: u32 = 0x281e1414;
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ const NAV_GROUPS: &[NavGroup] = &[
|
|||||||
];
|
];
|
||||||
|
|
||||||
pub(crate) fn render_settings_landing(
|
pub(crate) fn render_settings_landing(
|
||||||
|
shell: &mut ElyShell,
|
||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
active_route: &str,
|
active_route: &str,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
@@ -121,7 +122,7 @@ pub(crate) fn render_settings_landing(
|
|||||||
.h_full()
|
.h_full()
|
||||||
.flex()
|
.flex()
|
||||||
.child(render_nav_column(snapshot, active_route, cx))
|
.child(render_nav_column(snapshot, active_route, cx))
|
||||||
.child(render_appearance_form(snapshot, cx))
|
.child(render_appearance_form(shell, snapshot, cx))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ impl ElyShell {
|
|||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
render_appearance_form(snapshot, cx)
|
render_appearance_form(self, snapshot, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ impl ElyShell {
|
|||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
render_settings_landing(snapshot, "ely://settings/appearance", cx)
|
render_settings_landing(self, snapshot, "ely://settings/appearance", cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ mod web_surface_view;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use ely_browser_core::{BrowserCore, InitialBrowserConfig};
|
use ely_browser_core::{BrowserCore, InitialBrowserConfig};
|
||||||
use ely_domain::{ProfileId, SpaceId, TabId};
|
use ely_domain::{DEFAULT_TRANSLUCENCY_PCT, ProfileId, SpaceId, TabId};
|
||||||
use gpui::{AppContext, Context, Entity, FocusHandle, Subscription, Timer, Window};
|
use gpui::{AppContext, Context, Entity, FocusHandle, Subscription, Timer, Window};
|
||||||
use gpui_component::input::{InputEvent, InputState};
|
use gpui_component::input::{InputEvent, InputState};
|
||||||
|
use gpui_component::slider::{SliderEvent, SliderState, SliderValue};
|
||||||
|
|
||||||
use crate::shortcuts::ShortcutProfile;
|
use crate::shortcuts::ShortcutProfile;
|
||||||
use bookmarks::PendingBookmarkEdit;
|
use bookmarks::PendingBookmarkEdit;
|
||||||
@@ -63,6 +64,7 @@ pub struct ElyShell {
|
|||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
command_input: Entity<InputState>,
|
command_input: Entity<InputState>,
|
||||||
pub(crate) plugin_search_input: Entity<InputState>,
|
pub(crate) plugin_search_input: Entity<InputState>,
|
||||||
|
pub(crate) translucency_slider: Entity<SliderState>,
|
||||||
download_action_error: Option<String>,
|
download_action_error: Option<String>,
|
||||||
download_clear_confirmation: bool,
|
download_clear_confirmation: bool,
|
||||||
download_security_confirmation: Option<PendingDownloadFileAction>,
|
download_security_confirmation: Option<PendingDownloadFileAction>,
|
||||||
@@ -87,6 +89,7 @@ pub struct ElyShell {
|
|||||||
pending_plugin_uninstall: Option<PendingPluginUninstall>,
|
pending_plugin_uninstall: Option<PendingPluginUninstall>,
|
||||||
web_surfaces: WebSurfaceStore,
|
web_surfaces: WebSurfaceStore,
|
||||||
_command_subscription: Subscription,
|
_command_subscription: Subscription,
|
||||||
|
_translucency_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElyShell {
|
impl ElyShell {
|
||||||
@@ -107,6 +110,22 @@ impl ElyShell {
|
|||||||
cx.new(|cx| InputState::new(window, cx).placeholder("Search or enter address"));
|
cx.new(|cx| InputState::new(window, cx).placeholder("Search or enter address"));
|
||||||
let plugin_search_input =
|
let plugin_search_input =
|
||||||
cx.new(|cx| InputState::new(window, cx).placeholder("Search plugins…"));
|
cx.new(|cx| InputState::new(window, cx).placeholder("Search plugins…"));
|
||||||
|
let translucency_slider = cx.new(|_cx| {
|
||||||
|
SliderState::new()
|
||||||
|
.min(0.0)
|
||||||
|
.max(100.0)
|
||||||
|
.step(1.0)
|
||||||
|
.default_value(f32::from(DEFAULT_TRANSLUCENCY_PCT))
|
||||||
|
});
|
||||||
|
|
||||||
|
let translucency_subscription =
|
||||||
|
cx.subscribe(&translucency_slider, |shell: &mut Self, _state, event: &SliderEvent, cx| {
|
||||||
|
let SliderEvent::Change(SliderValue::Single(value)) = event else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let pct = value.clamp(0.0, 100.0).round() as u8;
|
||||||
|
shell.set_translucency_pct(pct, cx);
|
||||||
|
});
|
||||||
|
|
||||||
let command_subscription = cx.subscribe_in(
|
let command_subscription = cx.subscribe_in(
|
||||||
&command_input,
|
&command_input,
|
||||||
@@ -155,6 +174,7 @@ impl ElyShell {
|
|||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
command_input,
|
command_input,
|
||||||
plugin_search_input,
|
plugin_search_input,
|
||||||
|
translucency_slider,
|
||||||
download_action_error: None,
|
download_action_error: None,
|
||||||
download_clear_confirmation: false,
|
download_clear_confirmation: false,
|
||||||
download_security_confirmation: None,
|
download_security_confirmation: None,
|
||||||
@@ -179,6 +199,7 @@ impl ElyShell {
|
|||||||
pending_plugin_uninstall: None,
|
pending_plugin_uninstall: None,
|
||||||
web_surfaces: WebSurfaceStore::new(),
|
web_surfaces: WebSurfaceStore::new(),
|
||||||
_command_subscription: command_subscription,
|
_command_subscription: command_subscription,
|
||||||
|
_translucency_subscription: translucency_subscription,
|
||||||
};
|
};
|
||||||
start_external_web_surface_timer(cx);
|
start_external_web_surface_timer(cx);
|
||||||
shell
|
shell
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use ely_domain::{
|
use ely_domain::{
|
||||||
ArchivePolicy, DiagnosticsReportingPolicy, DownloadPolicy, FavoriteLimit,
|
ArchivePolicy, DEFAULT_TRANSLUCENCY_PCT, DiagnosticsReportingPolicy, DownloadPolicy,
|
||||||
HistoryRecordingPolicy, NewTabDestination, ProfileId, ProfileSyncPolicy, SearchEngine,
|
FavoriteLimit, HistoryRecordingPolicy, NewTabDestination, ProfileId, ProfileSyncPolicy,
|
||||||
SyncObjectKind, SyncObjectPolicy, ThemeMode, UpdatePolicy, WallpaperTheme,
|
SearchEngine, SyncObjectKind, SyncObjectPolicy, ThemeMode, UpdatePolicy, WallpaperTheme,
|
||||||
};
|
};
|
||||||
use gpui::Context;
|
use gpui::Context;
|
||||||
|
use gpui_component::slider::SliderValue;
|
||||||
|
|
||||||
use super::{ElyShell, ShellState};
|
use super::{ElyShell, ShellState};
|
||||||
|
|
||||||
@@ -86,11 +87,36 @@ impl ElyShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn reset_appearance(&mut self, cx: &mut Context<Self>) {
|
pub(crate) fn set_translucency_pct_from_preset(
|
||||||
|
&mut self,
|
||||||
|
value: u8,
|
||||||
|
window: &mut gpui::Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.set_translucency_pct(value, cx);
|
||||||
|
let slider = self.translucency_slider.clone();
|
||||||
|
slider.update(cx, |state, cx| {
|
||||||
|
state.set_value(SliderValue::Single(f32::from(value)), window, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn reset_appearance(
|
||||||
|
&mut self,
|
||||||
|
window: &mut gpui::Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
if let ShellState::Ready(core) = &mut self.state {
|
if let ShellState::Ready(core) = &mut self.state {
|
||||||
core.reset_appearance();
|
core.reset_appearance();
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
let slider = self.translucency_slider.clone();
|
||||||
|
slider.update(cx, |state, cx| {
|
||||||
|
state.set_value(
|
||||||
|
SliderValue::Single(f32::from(DEFAULT_TRANSLUCENCY_PCT)),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn reset_general_settings(&mut self, cx: &mut Context<Self>) {
|
pub(super) fn reset_general_settings(&mut self, cx: &mut Context<Self>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user