scrollbar: Fix scrollbar width on always show mode. (#1196)
And fixes #1192 render bug.
This commit is contained in:
parent
6569bead8b
commit
02b432ed17
5 changed files with 33 additions and 43 deletions
|
|
@ -143,13 +143,11 @@ actions!(story, [TestAction, Tab, TabPrev]);
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub invisible_panels: Entity<Vec<SharedString>>,
|
pub invisible_panels: Entity<Vec<SharedString>>,
|
||||||
pub theme_name: Option<SharedString>,
|
|
||||||
}
|
}
|
||||||
impl AppState {
|
impl AppState {
|
||||||
fn init(cx: &mut App) {
|
fn init(cx: &mut App) {
|
||||||
let state = Self {
|
let state = Self {
|
||||||
invisible_panels: cx.new(|_| Vec::new()),
|
invisible_panels: cx.new(|_| Vec::new()),
|
||||||
theme_name: None,
|
|
||||||
};
|
};
|
||||||
cx.set_global::<AppState>(state);
|
cx.set_global::<AppState>(state);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,41 +6,50 @@ use gpui::{
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
button::{Button, ButtonVariants},
|
button::{Button, ButtonVariants},
|
||||||
popup_menu::PopupMenuExt,
|
popup_menu::PopupMenuExt,
|
||||||
|
scroll::ScrollbarShow,
|
||||||
ActiveTheme, IconName, Sizable, Theme, ThemeRegistry,
|
ActiveTheme, IconName, Sizable, Theme, ThemeRegistry,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::AppState;
|
|
||||||
|
|
||||||
const STATE_FILE: &str = "target/state.json";
|
const STATE_FILE: &str = "target/state.json";
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
struct State {
|
struct State {
|
||||||
theme: SharedString,
|
theme: SharedString,
|
||||||
|
scrollbar_show: Option<ScrollbarShow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
// Load last theme state
|
// Load last theme state
|
||||||
let json = std::fs::read_to_string(STATE_FILE).unwrap_or(String::default());
|
let json = std::fs::read_to_string(STATE_FILE).unwrap_or(String::default());
|
||||||
tracing::info!("Load themes...");
|
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)
|
if let Some(theme) = ThemeRegistry::global(cx)
|
||||||
.themes()
|
.themes()
|
||||||
.get(&state.theme)
|
.get(&state.theme)
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
AppState::global_mut(cx).theme_name = Some(state.theme.clone());
|
|
||||||
Theme::global_mut(cx).apply_config(&theme);
|
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| {
|
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();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
@ -63,31 +72,17 @@ impl Render for ThemeSwitcher {
|
||||||
_: &mut gpui::Window,
|
_: &mut gpui::Window,
|
||||||
cx: &mut gpui::Context<Self>,
|
cx: &mut gpui::Context<Self>,
|
||||||
) -> impl gpui::IntoElement {
|
) -> impl gpui::IntoElement {
|
||||||
let theme_name = AppState::global(cx)
|
let theme_name = cx.theme().theme_name().clone();
|
||||||
.theme_name
|
|
||||||
.clone()
|
|
||||||
.unwrap_or("default-light".into());
|
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.id("theme-switcher")
|
.id("theme-switcher")
|
||||||
.on_action(cx.listener(|_, switch: &SwitchTheme, _, cx| {
|
.on_action(cx.listener(|_, switch: &SwitchTheme, _, cx| {
|
||||||
let theme_name = switch.0.clone();
|
let theme_name = switch.0.clone();
|
||||||
// Save AppState
|
|
||||||
let mut state = State {
|
|
||||||
theme: theme_name.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(theme_config) =
|
if let Some(theme_config) =
|
||||||
ThemeRegistry::global(cx).themes().get(&theme_name).cloned()
|
ThemeRegistry::global(cx).themes().get(&theme_name).cloned()
|
||||||
{
|
{
|
||||||
Theme::global_mut(cx).apply_config(&theme_config);
|
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();
|
cx.notify();
|
||||||
}))
|
}))
|
||||||
.child(
|
.child(
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,6 @@ impl AppTitleBar {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let locale_selector = cx.new(|cx| LocaleSelector::new(window, cx));
|
let locale_selector = cx.new(|cx| LocaleSelector::new(window, cx));
|
||||||
let font_size_selector = cx.new(|cx| FontSizeSelector::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));
|
let theme_switcher = cx.new(|cx| ThemeSwitcher::new(cx));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -386,13 +386,18 @@ impl Scrollbar {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style_for_normal(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
|
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_thumb,
|
||||||
cx.theme().scrollbar,
|
cx.theme().scrollbar,
|
||||||
gpui::transparent_black(),
|
gpui::transparent_black(),
|
||||||
THUMB_WIDTH,
|
width,
|
||||||
THUMB_INSET,
|
inset,
|
||||||
THUMB_RADIUS,
|
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.
|
// 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 {
|
let margin_end = if has_both && !is_vertical {
|
||||||
THUMB_ACTIVE_WIDTH
|
WIDTH
|
||||||
} else {
|
} else {
|
||||||
px(0.)
|
px(0.)
|
||||||
};
|
};
|
||||||
|
|
@ -551,7 +556,6 @@ impl Element for Scrollbar {
|
||||||
|
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let is_always_to_show = cx.theme().scrollbar_show.is_always();
|
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_bar = state.get().hovered_axis == Some(axis);
|
||||||
let is_hovered_on_thumb = state.get().hovered_on_thumb == 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();
|
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) =
|
let (thumb_bg, bar_bg, bar_border, thumb_width, inset, radius) =
|
||||||
if state.get().dragged_axis == Some(axis) {
|
if state.get().dragged_axis == Some(axis) {
|
||||||
Self::style_for_active(cx)
|
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 {
|
if is_hovered_on_thumb {
|
||||||
Self::style_for_hovered_thumb(cx)
|
Self::style_for_hovered_thumb(cx)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -140,11 +140,11 @@ impl Theme {
|
||||||
|
|
||||||
/// Sync the Scrollbar showing behavior with the system
|
/// Sync the Scrollbar showing behavior with the system
|
||||||
pub fn sync_scrollbar_appearance(cx: &mut App) {
|
pub fn sync_scrollbar_appearance(cx: &mut App) {
|
||||||
if cx.should_auto_hide_scrollbars() {
|
Theme::global_mut(cx).scrollbar_show = if cx.should_auto_hide_scrollbars() {
|
||||||
cx.global_mut::<Theme>().scrollbar_show = ScrollbarShow::Scrolling;
|
ScrollbarShow::Scrolling
|
||||||
} else {
|
} 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) {
|
pub fn change(mode: impl Into<ThemeMode>, window: Option<&mut Window>, cx: &mut App) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue