scrollbar: Fix scrollbar width on always show mode. (#1196)

And fixes #1192 render bug.
This commit is contained in:
Jason Lee 2025-09-02 15:48:06 +08:00 committed by GitHub
parent 6569bead8b
commit 02b432ed17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 33 additions and 43 deletions

View file

@ -143,13 +143,11 @@ actions!(story, [TestAction, Tab, TabPrev]);
pub struct AppState {
pub invisible_panels: Entity<Vec<SharedString>>,
pub theme_name: Option<SharedString>,
}
impl AppState {
fn init(cx: &mut App) {
let state = Self {
invisible_panels: cx.new(|_| Vec::new()),
theme_name: None,
};
cx.set_global::<AppState>(state);
}

View file

@ -6,41 +6,50 @@ use gpui::{
use gpui_component::{
button::{Button, ButtonVariants},
popup_menu::PopupMenuExt,
scroll::ScrollbarShow,
ActiveTheme, IconName, Sizable, Theme, ThemeRegistry,
};
use serde::{Deserialize, Serialize};
use crate::AppState;
const STATE_FILE: &str = "target/state.json";
#[derive(Debug, Clone, Serialize, Deserialize)]
struct State {
theme: SharedString,
scrollbar_show: Option<ScrollbarShow>,
}
pub fn init(cx: &mut App) {
// Load last theme state
let json = std::fs::read_to_string(STATE_FILE).unwrap_or(String::default());
tracing::info!("Load themes...");
if let Err(err) = ThemeRegistry::watch_dir(PathBuf::from("./themes"), cx, move |cx| {
if let Ok(state) = serde_json::from_str::<State>(&json) {
if let Ok(state) = serde_json::from_str::<State>(&json) {
if let Err(err) = ThemeRegistry::watch_dir(PathBuf::from("./themes"), cx, move |cx| {
if let Some(theme) = ThemeRegistry::global(cx)
.themes()
.get(&state.theme)
.cloned()
{
AppState::global_mut(cx).theme_name = Some(state.theme.clone());
Theme::global_mut(cx).apply_config(&theme);
cx.refresh_windows();
}
}) {
tracing::error!("Failed to watch themes directory: {}", err);
}
}) {
tracing::error!("Failed to watch themes directory: {}", err);
if let Some(scrollbar_show) = state.scrollbar_show {
Theme::global_mut(cx).scrollbar_show = scrollbar_show;
}
cx.refresh_windows();
}
cx.observe_global::<Theme>(|cx| {
AppState::global_mut(cx).theme_name = Some(cx.theme().theme_name().into());
let state = State {
theme: cx.theme().theme_name().clone(),
scrollbar_show: Some(cx.theme().scrollbar_show),
};
let json = serde_json::to_string_pretty(&state).unwrap();
std::fs::write(STATE_FILE, json).unwrap();
})
.detach();
}
@ -63,31 +72,17 @@ impl Render for ThemeSwitcher {
_: &mut gpui::Window,
cx: &mut gpui::Context<Self>,
) -> impl gpui::IntoElement {
let theme_name = AppState::global(cx)
.theme_name
.clone()
.unwrap_or("default-light".into());
let theme_name = cx.theme().theme_name().clone();
div()
.id("theme-switcher")
.on_action(cx.listener(|_, switch: &SwitchTheme, _, cx| {
let theme_name = switch.0.clone();
// Save AppState
let mut state = State {
theme: theme_name.clone(),
};
if let Some(theme_config) =
ThemeRegistry::global(cx).themes().get(&theme_name).cloned()
{
Theme::global_mut(cx).apply_config(&theme_config);
state.theme = theme_config.name.clone();
AppState::global_mut(cx).theme_name = Some(theme_name.clone());
}
let json = serde_json::to_string_pretty(&state).unwrap();
std::fs::write(STATE_FILE, json).unwrap();
cx.notify();
}))
.child(

View file

@ -34,13 +34,6 @@ impl AppTitleBar {
) -> Self {
let locale_selector = cx.new(|cx| LocaleSelector::new(window, cx));
let font_size_selector = cx.new(|cx| FontSizeSelector::new(window, cx));
if cx.should_auto_hide_scrollbars() {
Theme::global_mut(cx).scrollbar_show = ScrollbarShow::Scrolling;
} else {
Theme::global_mut(cx).scrollbar_show = ScrollbarShow::Hover;
}
let theme_switcher = cx.new(|cx| ThemeSwitcher::new(cx));
Self {

View file

@ -386,13 +386,18 @@ impl Scrollbar {
}
fn style_for_normal(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
let (width, inset, radius) = match cx.theme().scrollbar_show {
ScrollbarShow::Scrolling => (THUMB_WIDTH, THUMB_INSET, THUMB_RADIUS),
_ => (THUMB_ACTIVE_WIDTH, THUMB_ACTIVE_INSET, THUMB_ACTIVE_RADIUS),
};
(
cx.theme().scrollbar_thumb,
cx.theme().scrollbar,
gpui::transparent_black(),
THUMB_WIDTH,
THUMB_INSET,
THUMB_RADIUS,
width,
inset,
radius,
)
}
@ -509,7 +514,7 @@ impl Element for Scrollbar {
// The horizontal scrollbar is set avoid overlapping with the vertical scrollbar, if the vertical scrollbar is visible.
let margin_end = if has_both && !is_vertical {
THUMB_ACTIVE_WIDTH
WIDTH
} else {
px(0.)
};
@ -551,7 +556,6 @@ impl Element for Scrollbar {
let state = self.state.clone();
let is_always_to_show = cx.theme().scrollbar_show.is_always();
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
let is_hovered_on_bar = state.get().hovered_axis == Some(axis);
let is_hovered_on_thumb = state.get().hovered_on_thumb == Some(axis);
let is_offset_changed = state.get().last_scroll_offset != self.scroll_handle.offset();
@ -559,7 +563,7 @@ impl Element for Scrollbar {
let (thumb_bg, bar_bg, bar_border, thumb_width, inset, radius) =
if state.get().dragged_axis == Some(axis) {
Self::style_for_active(cx)
} else if is_hover_to_show && (is_hovered_on_bar || is_hovered_on_thumb) {
} else if is_hovered_on_bar || is_hovered_on_thumb {
if is_hovered_on_thumb {
Self::style_for_hovered_thumb(cx)
} else {

View file

@ -140,11 +140,11 @@ impl Theme {
/// Sync the Scrollbar showing behavior with the system
pub fn sync_scrollbar_appearance(cx: &mut App) {
if cx.should_auto_hide_scrollbars() {
cx.global_mut::<Theme>().scrollbar_show = ScrollbarShow::Scrolling;
Theme::global_mut(cx).scrollbar_show = if cx.should_auto_hide_scrollbars() {
ScrollbarShow::Scrolling
} else {
cx.global_mut::<Theme>().scrollbar_show = ScrollbarShow::Hover;
}
ScrollbarShow::Hover
};
}
pub fn change(mode: impl Into<ThemeMode>, window: Option<&mut Window>, cx: &mut App) {