tiles: Add scrollbar_show option to Tiles (#1496)

This commit is contained in:
ihavecoke 2025-11-04 17:42:46 +08:00 committed by GitHub
parent b7815ed6ec
commit fbaddbe9e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 67 additions and 27 deletions

View file

@ -7,6 +7,7 @@ use gpui_component::{
PanelState, PanelView, register_panel,
},
input::{Input, InputState},
scroll::ScrollbarShow,
};
use serde::{Deserialize, Serialize};
use std::{sync::Arc, time::Duration};
@ -239,6 +240,17 @@ impl StoryTiles {
Ok(())
}
fn set_scrollbar_show(dock_area: &mut DockArea, cx: &mut App) {
match dock_area.items() {
DockItem::Tiles { view, .. } => {
view.update(cx, |this, cx| {
this.set_scrollbar_show(Some(ScrollbarShow::Always), cx);
});
}
_ => {}
}
}
fn load_tiles(
dock_area: Entity<DockArea>,
window: &mut Window,
@ -273,7 +285,7 @@ impl StoryTiles {
dock_area.update(cx, |dock_area, cx| {
dock_area.load(state, window, cx).context("load layout")?;
Self::set_scrollbar_show(dock_area, cx);
Ok::<(), anyhow::Error>(())
})
}
@ -284,11 +296,12 @@ impl StoryTiles {
cx: &mut Context<Self>,
) {
let dock_item = Self::init_default_layout(&dock_area, window, cx);
_ = dock_area.update(cx, |view, cx| {
view.set_version(TILES_DOCK_AREA.version, window, cx);
view.set_center(dock_item, window, cx);
_ = dock_area.update(cx, |dock_area, cx| {
dock_area.set_version(TILES_DOCK_AREA.version, window, cx);
dock_area.set_center(dock_item, window, cx);
Self::save_tiles(&view.dump(cx)).unwrap();
Self::set_scrollbar_show(dock_area, cx);
Self::save_tiles(&dock_area.dump(cx)).unwrap();
});
}

View file

@ -7,7 +7,7 @@ use std::{
use crate::{
h_flex,
history::{History, HistoryItem},
scroll::{Scrollbar, ScrollbarState},
scroll::{Scrollbar, ScrollbarShow, ScrollbarState},
v_flex, ActiveTheme, Icon, IconName,
};
@ -15,10 +15,11 @@ use super::{
DockArea, Panel, PanelEvent, PanelInfo, PanelState, PanelView, StackPanel, TabPanel, TileMeta,
};
use gpui::{
actions, canvas, div, px, size, AnyElement, App, AppContext, Bounds, Context, DismissEvent,
DragMoveEvent, Empty, EntityId, EventEmitter, FocusHandle, Focusable, InteractiveElement,
IntoElement, MouseButton, MouseDownEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
ScrollHandle, Size, StatefulInteractiveElement, Styled, WeakEntity, Window,
actions, canvas, div, prelude::FluentBuilder, px, size, AnyElement, App, AppContext, Bounds,
Context, DismissEvent, DragMoveEvent, Empty, EntityId, EventEmitter, FocusHandle, Focusable,
InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseUpEvent, ParentElement,
Pixels, Point, Render, ScrollHandle, Size, StatefulInteractiveElement, Styled, WeakEntity,
Window,
};
actions!(tiles, [Undo, Redo]);
@ -140,6 +141,7 @@ pub struct Tiles {
history: History<TileChange>,
scroll_state: ScrollbarState,
scroll_handle: ScrollHandle,
scrollbar_show: Option<ScrollbarShow>,
}
impl Panel for Tiles {
@ -189,6 +191,7 @@ impl Tiles {
dragging_initial_mouse: Point::default(),
dragging_initial_bounds: Bounds::default(),
resizing_id: None,
scrollbar_show: None,
resizing_drag_data: None,
bounds: Bounds::default(),
history: History::new().group_interval(std::time::Duration::from_millis(100)),
@ -197,6 +200,16 @@ impl Tiles {
}
}
/// Set the scrollbar show mode [`ScrollbarShow`], if not set use the `cx.theme().scrollbar_show`.
pub fn set_scrollbar_show(
&mut self,
scrollbar_show: Option<ScrollbarShow>,
cx: &mut Context<Self>,
) {
self.scrollbar_show = scrollbar_show;
cx.notify();
}
pub fn panels(&self) -> &[TileItem] {
&self.panels
}
@ -1058,7 +1071,10 @@ impl Render for Tiles {
.bottom_0()
.child(
Scrollbar::both(&self.scroll_state, &self.scroll_handle)
.scroll_size(scroll_size),
.scroll_size(scroll_size)
.when_some(self.scrollbar_show, |this, scrollbar_show| {
this.scrollbar_show(scrollbar_show)
}),
),
)
.size_full()

View file

@ -7,11 +7,11 @@ use std::{
use crate::{ActiveTheme, AxisExt};
use gpui::{
fill, point, px, relative, size, App, Axis, BorderStyle, Bounds, ContentMask, Corner,
CursorStyle, Edges, Element, GlobalElementId, Hitbox, HitboxBehavior, Hsla, InspectorElementId,
IntoElement, IsZero, LayoutId, ListState, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
PaintQuad, Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, Size, Style, Timer,
UniformListScrollHandle, Window,
fill, point, px, relative, size, App, Axis, BorderStyle, Bounds,
ContentMask, Corner, CursorStyle, Edges, Element, GlobalElementId, Hitbox, HitboxBehavior,
Hsla, InspectorElementId, IntoElement, IsZero, LayoutId, ListState, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point, Position, ScrollHandle,
ScrollWheelEvent, Size, Style, Timer, UniformListScrollHandle, Window,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -289,6 +289,7 @@ impl ScrollbarAxis {
/// Scrollbar control for scroll-area or a uniform-list.
pub struct Scrollbar {
axis: ScrollbarAxis,
scrollbar_show: Option<ScrollbarShow>,
scroll_handle: Rc<Box<dyn ScrollHandleOffsetable>>,
state: ScrollbarState,
scroll_size: Option<Size<Pixels>>,
@ -308,6 +309,7 @@ impl Scrollbar {
Self {
state: state.clone(),
axis: axis.into(),
scrollbar_show: None,
scroll_handle: Rc::new(Box::new(scroll_handle.clone())),
max_fps: 120,
scroll_size: None,
@ -327,6 +329,12 @@ impl Scrollbar {
Self::new(ScrollbarAxis::Both, state, scroll_handle)
}
/// Set the scrollbar show mode [`ScrollbarShow`], if not set use the `cx.theme().scrollbar_show`.
pub fn scrollbar_show(mut self, scrollbar_show: ScrollbarShow) -> Self {
self.scrollbar_show = Some(scrollbar_show);
self
}
/// Create with horizontal scrollbar.
pub fn horizontal(
state: &ScrollbarState,
@ -408,8 +416,9 @@ impl Scrollbar {
)
}
fn style_for_normal(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
let (width, inset, radius) = match cx.theme().scrollbar_show {
fn style_for_normal(&self, cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
let (width, inset, radius) = match scrollbar_show {
ScrollbarShow::Scrolling => (THUMB_WIDTH, THUMB_INSET, THUMB_RADIUS),
_ => (THUMB_ACTIVE_WIDTH, THUMB_ACTIVE_INSET, THUMB_ACTIVE_RADIUS),
};
@ -424,8 +433,9 @@ impl Scrollbar {
)
}
fn style_for_idle(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
let (width, inset, radius) = match cx.theme().scrollbar_show {
fn style_for_idle(&self, cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
let (width, inset, radius) = match scrollbar_show {
ScrollbarShow::Scrolling => (THUMB_WIDTH, THUMB_INSET, THUMB_RADIUS),
_ => (THUMB_ACTIVE_WIDTH, THUMB_ACTIVE_INSET, THUMB_ACTIVE_RADIUS),
};
@ -577,9 +587,10 @@ impl Element for Scrollbar {
},
};
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
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_always_to_show = scrollbar_show.is_always();
let is_hover_to_show = 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();
@ -594,7 +605,7 @@ impl Element for Scrollbar {
Self::style_for_hovered_bar(cx)
}
} else if is_offset_changed {
Self::style_for_normal(cx)
self.style_for_normal(cx)
} else if is_always_to_show {
if is_hovered_on_thumb {
Self::style_for_hovered_thumb(cx)
@ -602,7 +613,7 @@ impl Element for Scrollbar {
Self::style_for_hovered_bar(cx)
}
} else {
let mut idle_state = Self::style_for_idle(cx);
let mut idle_state = self.style_for_idle(cx);
// Delay 2s to fade out the scrollbar thumb (in 1s)
if let Some(last_time) = state.get().last_scroll_time {
let elapsed = Instant::now().duration_since(last_time).as_secs_f32();
@ -705,11 +716,11 @@ impl Element for Scrollbar {
window: &mut Window,
cx: &mut App,
) {
let scrollbar_show = self.scrollbar_show.unwrap_or(cx.theme().scrollbar_show);
let view_id = window.current_view();
let hitbox_bounds = prepaint.hitbox.bounds;
let is_visible =
self.state.get().is_scrollbar_visible() || cx.theme().scrollbar_show.is_always();
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
let is_visible = self.state.get().is_scrollbar_visible() || scrollbar_show.is_always();
let is_hover_to_show = scrollbar_show.is_hover();
// Update last_scroll_time when offset is changed.
if self.scroll_handle.offset() != self.state.get().last_scroll_offset {