Skip to content

Scrollbar

Scrollbar 是连接 GPUI 滚动句柄的自绘滚动条,支持纵向、横向和双轴视口、轨道点击、滑块拖动、可配置可见模式、类型化绘制样式、减少动态效果,以及可反向的可见性和宽度过渡。gpui-base 负责交互与过渡生命周期,应用或设计系统负责颜色、几何、时序和进入编排。

运行示例

bash
cargo run -p gpui-base --example components -- scrollbar

原生与 WASM 共用 scrollbar.rs

基本用法

ScrollHandle 保存在持久视图状态中,通过 track_scroll 连接可滚动内容,并在同一个 relative() 容器中叠加 ScrollbarScrollbar::new 启用双轴;单轴使用 verticalhorizontal.axis(...)。滚动条是绝对定位覆盖层,进入动画不会移动内容或命中区域。

可见模式

  • Scrolling:滚动或拖动后显示,离开悬停区域后重新计算空闲等待。
  • Hover:指针进入轨道后显示。
  • Always:始终可见并跳过可见性过渡。

默认静止滑块宽 6 px,滑块悬停或拖动目标宽度为 8 px。隐藏的轨道与滑块不响应点击。

全局主题与实例覆盖

使用 ScrollbarStylestracktrack_hovertrack_activethumbthumb_hoverthumb_active builder 设置外观;使用 ScrollbarMotion 配置 idleenterexitexpand 以及 ScrollbarEntrance。再把它们装入 Theme::global_mut(cx).scrollbar = ScrollbarTheme::new().with_mode(...).with_motion(...).with_styles(...)。字段保持私有,可通过 reader 查询。单个实例的 .styles(...) 优先于全局主题。

动画行为

Base 不附带产品动画。默认仅有 2 秒行为性空闲等待,进入、退出和展开时长均为零。Fade 原地淡入;SlideAndFade 让纵向滚动条从右侧、横向滚动条从底部进入。被中断的过渡从当前视觉值反向;零时长立即采用目标值。GPUI 的减少动态效果偏好也会把可见性和宽度时长降为零。

自定义视口与句柄

默认视口来自 ScrollbarHandle::viewport_bounds。组合控件可用 .viewport_bounds(bounds) 指定绘制视口,或 .viewport_from_layout() 使用覆盖层布局;只有句柄无法报告完整范围时才用 .scroll_size(...) 覆盖内容尺寸。ScrollHandleUniformListScrollHandleListState 已实现 ScrollbarHandle;自定义容器需实现视口、偏移、设置偏移和内容尺寸,必要时实现 start_drag / end_drag

稳定身份

构造器默认从调用位置派生 ID。同一调用位置生成多个独立滚动条时,应使用 .id(("activity-list", panel_id)) 提供稳定 ID,以跨渲染保留可见性和宽度动画状态。

完整示例

rust
use super::*;

impl BaseShowcase {
    pub(in super::super) fn scrollbar(&self) -> impl IntoElement {
        div()
            .id("example-scroll-region")
            .relative()
            .w_72()
            .h_48()
            .text_xs()
            .border_1()
            .border_color(super::example_rgb(0x171717))
            .overflow_scroll()
            .track_scroll(&self.example_scroll)
            .child(div().children((1..=20).map(|row| {
                div()
                    .h_7()
                    .px_2()
                    .flex()
                    .items_center()
                    .border_b_1()
                    .border_color(super::example_rgb(0xe5e7eb))
                    .justify_between()
                    .child(format!("Activity {row}"))
                    .child(if row % 3 == 0 { "Completed" } else { "Pending" })
            })))
            .child(Scrollbar::new(&self.example_scroll).mode(ScrollbarMode::Always))
    }
}

可访问性与交互检查

  • 保留底层视口的滚轮、触控板和键盘滚动。
  • 即使绘制的滑块很窄,也保留完整轨道命中区域。
  • 验证普通、悬停和活动状态对比度。
  • 动画不要移动布局或命中区域。
  • 在减少动态效果下分别测试三种模式以及纵向、横向和双轴溢出。