feat: add scrollbar for virtualized lists

This commit is contained in:
2026-11-04 13:05:39 +07:00
parent 4bfc816cbe
commit 50da1d4a9d
10 changed files with 1033 additions and 23 deletions
+124 -2
View File
@@ -3,8 +3,9 @@ use std::time::Duration;
use gpui::prelude::FluentBuilder as _;
use gpui::{
App, AppContext as _, Bounds, Context, Entity, FontWeight, InteractiveElement as _,
IntoElement, KeyBinding, ParentElement as _, Pixels, Render, SharedString, Styled as _,
WeakEntity, Window, WindowBounds, WindowOptions, actions, div, px, size,
IntoElement, KeyBinding, ListAlignment, ListState, ParentElement as _, Pixels, Render,
SharedString, Styled as _, UniformListScrollHandle, WeakEntity, Window, WindowBounds,
WindowOptions, actions, div, list, px, size, uniform_list,
};
use gpui_native_core::alert::{Alert, AlertSeverity, Alerts, AlertsView};
use gpui_native_core::avatar::Avatar;
@@ -25,6 +26,7 @@ use gpui_native_core::popover::Popover;
use gpui_native_core::progress::Progress;
use gpui_native_core::radio::{Radio, RadioGroup};
use gpui_native_core::scroll_area::{ScrollArea, ScrollAxes};
use gpui_native_core::scrollbar::{Scrollbar, ScrollbarVisibility};
use gpui_native_core::segmented::{Segmented, SegmentedOption};
use gpui_native_core::select::{Select, SelectOption};
use gpui_native_core::slider::Slider;
@@ -93,6 +95,7 @@ const CATALOG: &[(&str, &[(&str, &str)])] = &[
("tooltip", "Tooltip"),
("empty-state", "Empty state"),
("scroll-area", "Scroll area"),
("scrollbar", "Scrollbar"),
],
),
(
@@ -614,6 +617,12 @@ impl Gallery {
}),
cx,
);
dock.register_panel(
PanelDescriptor::new("scrollbar", "Scrollbar", |_, cx| {
cx.new(|_| ScrollbarDemo::new()).into()
}),
cx,
);
dock.register_panel(
PanelDescriptor::new("sidebar", "Sidebar", |_, cx| {
cx.new(|_| SidebarDemo { selected: None }).into()
@@ -2614,6 +2623,119 @@ impl Render for ScrollAreaDemo {
}
}
/// The virtualized lists a [Scrollbar] drives: gpui's `uniform_list` and
/// `list`, whose handles the overlay reads and writes.
struct ScrollbarDemo {
uniform: UniformListScrollHandle,
list: ListState,
visibility: ScrollbarVisibility,
}
impl ScrollbarDemo {
fn new() -> Self {
Self {
uniform: UniformListScrollHandle::new(),
list: ListState::new(64, ListAlignment::Top, px(400.)),
visibility: ScrollbarVisibility::Platform,
}
}
}
impl Render for ScrollbarDemo {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
let colors = theme.colors;
let radius = theme.radius;
let list_state = self.list.clone();
let visibility = self.visibility;
let view = cx.entity();
ScrollArea::new("scrollbar-demo").size_full().p_4().child(
v_flex()
.gap_3()
.child(demo_section(
"visibility",
colors,
Segmented::new("scrollbar-demo-visibility", visibility)
.options(vec![
(ScrollbarVisibility::Platform, "Platform"),
(ScrollbarVisibility::Always, "Always"),
(ScrollbarVisibility::Autohide, "Autohide"),
])
.on_change(move |next, _, cx| {
view.update(cx, |demo, cx| {
demo.visibility = next;
cx.notify();
});
}),
))
.child(demo_section(
"uniform_list",
colors,
div()
.relative()
.h(px(240.))
.rounded(radius.control)
.border_1()
.border_color(colors.border)
.overflow_hidden()
.child(
uniform_list("scrollbar-demo-uniform", 120, move |range, _, _| {
range
.map(|row| {
div()
.h(px(24.))
.px_3()
.flex()
.items_center()
.border_b_1()
.border_color(colors.border)
.child(format!("Row {row} of 120"))
})
.collect()
})
.track_scroll(&self.uniform)
.h_full()
.w_full(),
)
.child(Scrollbar::vertical(self.uniform.clone()).visibility(visibility)),
))
.child(demo_section(
"list",
colors,
div()
.relative()
.h(px(240.))
.rounded(radius.control)
.border_1()
.border_color(colors.border)
.overflow_hidden()
.child(
list(list_state, move |row, _, _| {
div()
.px_3()
.py_1()
.border_b_1()
.border_color(colors.border)
.child(format!("Item {row} — {} lines tall", 1 + row % 3))
.h(px(28. + (row % 3) as f32 * 12.))
.into_any_element()
})
.h_full()
.w_full(),
)
.child(Scrollbar::vertical(self.list.clone()).visibility(visibility)),
))
.child(
div()
.text_xs()
.text_color(colors.muted_foreground)
.child("Platform follows the running OS; scroll or grab the rail to reveal an autohidden thumb."),
),
)
}
}
struct SidebarDemo {
selected: Option<usize>,
}