story: Use uniform_list for scroll example. (#1709)
Ref #1621 Update scrollable story to use `UniformList` to avoid performance issue in story. And also renamed it to `ScrollbarStory`.
This commit is contained in:
parent
a792719dbe
commit
4e0f9af789
6 changed files with 72 additions and 38 deletions
|
|
@ -11,7 +11,7 @@ use gpui_component_assets::Assets;
|
||||||
use gpui_component_story::{
|
use gpui_component_story::{
|
||||||
AccordionStory, AppState, AppTitleBar, ButtonStory, CalendarStory, DialogStory, FormStory,
|
AccordionStory, AppState, AppTitleBar, ButtonStory, CalendarStory, DialogStory, FormStory,
|
||||||
IconStory, ImageStory, InputStory, LabelStory, ListStory, NotificationStory, Open,
|
IconStory, ImageStory, InputStory, LabelStory, ListStory, NotificationStory, Open,
|
||||||
PopoverStory, ProgressStory, ResizableStory, ScrollableStory, SelectStory, SidebarStory,
|
PopoverStory, ProgressStory, ResizableStory, ScrollbarStory, SelectStory, SidebarStory,
|
||||||
StoryContainer, SwitchStory, TableStory, TooltipStory, WebViewStory,
|
StoryContainer, SwitchStory, TableStory, TooltipStory, WebViewStory,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
@ -269,7 +269,7 @@ impl StoryWorkspace {
|
||||||
),
|
),
|
||||||
DockItem::tabs(
|
DockItem::tabs(
|
||||||
vec![
|
vec![
|
||||||
Arc::new(StoryContainer::panel::<ScrollableStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<ScrollbarStory>(window, cx)),
|
||||||
Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
|
||||||
],
|
],
|
||||||
None,
|
None,
|
||||||
|
|
@ -358,7 +358,7 @@ impl StoryWorkspace {
|
||||||
Arc::new(StoryContainer::panel::<TooltipStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<TooltipStory>(window, cx)),
|
||||||
Arc::new(StoryContainer::panel::<CalendarStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<CalendarStory>(window, cx)),
|
||||||
Arc::new(StoryContainer::panel::<ResizableStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<ResizableStory>(window, cx)),
|
||||||
Arc::new(StoryContainer::panel::<ScrollableStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<ScrollbarStory>(window, cx)),
|
||||||
Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
|
||||||
Arc::new(StoryContainer::panel::<SidebarStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<SidebarStory>(window, cx)),
|
||||||
Arc::new(StoryContainer::panel::<FormStory>(window, cx)),
|
Arc::new(StoryContainer::panel::<FormStory>(window, cx)),
|
||||||
|
|
@ -447,7 +447,7 @@ impl StoryWorkspace {
|
||||||
12 => Arc::new(StoryContainer::panel::<ProgressStory>(window, cx)),
|
12 => Arc::new(StoryContainer::panel::<ProgressStory>(window, cx)),
|
||||||
13 => Arc::new(StoryContainer::panel::<CalendarStory>(window, cx)),
|
13 => Arc::new(StoryContainer::panel::<CalendarStory>(window, cx)),
|
||||||
14 => Arc::new(StoryContainer::panel::<ResizableStory>(window, cx)),
|
14 => Arc::new(StoryContainer::panel::<ResizableStory>(window, cx)),
|
||||||
15 => Arc::new(StoryContainer::panel::<ScrollableStory>(window, cx)),
|
15 => Arc::new(StoryContainer::panel::<ScrollbarStory>(window, cx)),
|
||||||
16 => Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
|
16 => Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
|
||||||
17 => Arc::new(StoryContainer::panel::<WebViewStory>(window, cx)),
|
17 => Arc::new(StoryContainer::panel::<WebViewStory>(window, cx)),
|
||||||
_ => Arc::new(StoryContainer::panel::<ButtonStory>(window, cx)),
|
_ => Arc::new(StoryContainer::panel::<ButtonStory>(window, cx)),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ mod popover_story;
|
||||||
mod progress_story;
|
mod progress_story;
|
||||||
mod radio_story;
|
mod radio_story;
|
||||||
mod resizable_story;
|
mod resizable_story;
|
||||||
mod scrollable_story;
|
mod scrollbar_story;
|
||||||
mod select_story;
|
mod select_story;
|
||||||
mod settings_story;
|
mod settings_story;
|
||||||
mod sheet_story;
|
mod sheet_story;
|
||||||
|
|
@ -90,7 +90,7 @@ pub use popover_story::PopoverStory;
|
||||||
pub use progress_story::ProgressStory;
|
pub use progress_story::ProgressStory;
|
||||||
pub use radio_story::RadioStory;
|
pub use radio_story::RadioStory;
|
||||||
pub use resizable_story::ResizableStory;
|
pub use resizable_story::ResizableStory;
|
||||||
pub use scrollable_story::ScrollableStory;
|
pub use scrollbar_story::ScrollbarStory;
|
||||||
pub use select_story::SelectStory;
|
pub use select_story::SelectStory;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
pub use settings_story::SettingsStory;
|
pub use settings_story::SettingsStory;
|
||||||
|
|
@ -670,7 +670,7 @@ impl StoryState {
|
||||||
"PopoverStory" => story!(PopoverStory),
|
"PopoverStory" => story!(PopoverStory),
|
||||||
"ProgressStory" => story!(ProgressStory),
|
"ProgressStory" => story!(ProgressStory),
|
||||||
"ResizableStory" => story!(ResizableStory),
|
"ResizableStory" => story!(ResizableStory),
|
||||||
"ScrollableStory" => story!(ScrollableStory),
|
"ScrollbarStory" => story!(ScrollbarStory),
|
||||||
"SwitchStory" => story!(SwitchStory),
|
"SwitchStory" => story!(SwitchStory),
|
||||||
"TableStory" => story!(TableStory),
|
"TableStory" => story!(TableStory),
|
||||||
"LabelStory" => story!(LabelStory),
|
"LabelStory" => story!(LabelStory),
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ impl Gallery {
|
||||||
StoryContainer::panel::<ProgressStory>(window, cx),
|
StoryContainer::panel::<ProgressStory>(window, cx),
|
||||||
StoryContainer::panel::<RadioStory>(window, cx),
|
StoryContainer::panel::<RadioStory>(window, cx),
|
||||||
StoryContainer::panel::<ResizableStory>(window, cx),
|
StoryContainer::panel::<ResizableStory>(window, cx),
|
||||||
StoryContainer::panel::<ScrollableStory>(window, cx),
|
StoryContainer::panel::<ScrollbarStory>(window, cx),
|
||||||
StoryContainer::panel::<SelectStory>(window, cx),
|
StoryContainer::panel::<SelectStory>(window, cx),
|
||||||
StoryContainer::panel::<SettingsStory>(window, cx),
|
StoryContainer::panel::<SettingsStory>(window, cx),
|
||||||
StoryContainer::panel::<SheetStory>(window, cx),
|
StoryContainer::panel::<SheetStory>(window, cx),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, AppContext, Context, Entity, FocusHandle, Focusable, IntoElement, ParentElement, Pixels,
|
App, AppContext, Context, Entity, FocusHandle, Focusable, IntoElement, ParentElement, Pixels,
|
||||||
Render, Size, Styled, Window, div, px, size,
|
Render, Size, Styled, UniformListScrollHandle, Window, div, px, size, uniform_list,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
ActiveTheme as _, Selectable,
|
ActiveTheme as _, Selectable,
|
||||||
|
|
@ -12,17 +12,18 @@ use gpui_component::{
|
||||||
v_flex,
|
v_flex,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ScrollableStory {
|
pub struct ScrollbarStory {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
items: Vec<String>,
|
items: Vec<String>,
|
||||||
item_sizes: Rc<Vec<Size<Pixels>>>,
|
item_sizes: Rc<Vec<Size<Pixels>>>,
|
||||||
test_width: Pixels,
|
test_width: Pixels,
|
||||||
size_mode: usize,
|
size_mode: usize,
|
||||||
|
scroll_handle: UniformListScrollHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ITEM_HEIGHT: Pixels = px(30.);
|
const ITEM_HEIGHT: Pixels = px(50.);
|
||||||
|
|
||||||
impl ScrollableStory {
|
impl ScrollbarStory {
|
||||||
fn new(_: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(_: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let items = (0..5000).map(|i| format!("Item {}", i)).collect::<Vec<_>>();
|
let items = (0..5000).map(|i| format!("Item {}", i)).collect::<Vec<_>>();
|
||||||
let test_width = px(3000.);
|
let test_width = px(3000.);
|
||||||
|
|
@ -37,6 +38,7 @@ impl ScrollableStory {
|
||||||
item_sizes: Rc::new(item_sizes),
|
item_sizes: Rc::new(item_sizes),
|
||||||
test_width,
|
test_width,
|
||||||
size_mode: 0,
|
size_mode: 0,
|
||||||
|
scroll_handle: UniformListScrollHandle::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,13 +115,13 @@ impl ScrollableStory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Story for ScrollableStory {
|
impl super::Story for ScrollbarStory {
|
||||||
fn title() -> &'static str {
|
fn title() -> &'static str {
|
||||||
"Scrollable"
|
"Scrollbar"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn description() -> &'static str {
|
fn description() -> &'static str {
|
||||||
"A scrollable container."
|
"Add scrollbar to a scrollable element."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render> {
|
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render> {
|
||||||
|
|
@ -127,19 +129,18 @@ impl super::Story for ScrollableStory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Focusable for ScrollableStory {
|
impl Focusable for ScrollbarStory {
|
||||||
fn focus_handle(&self, _: &gpui::App) -> gpui::FocusHandle {
|
fn focus_handle(&self, _: &gpui::App) -> gpui::FocusHandle {
|
||||||
self.focus_handle.clone()
|
self.focus_handle.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for ScrollableStory {
|
impl Render for ScrollbarStory {
|
||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &mut gpui::Window,
|
_: &mut gpui::Window,
|
||||||
cx: &mut gpui::Context<Self>,
|
cx: &mut gpui::Context<Self>,
|
||||||
) -> impl gpui::IntoElement {
|
) -> impl gpui::IntoElement {
|
||||||
let test_width = self.test_width;
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
|
|
@ -149,26 +150,38 @@ impl Render for ScrollableStory {
|
||||||
.relative()
|
.relative()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().border)
|
.border_color(cx.theme().border)
|
||||||
.w_full()
|
.flex_1()
|
||||||
.max_h(px(400.))
|
|
||||||
.min_h(px(200.))
|
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
uniform_list("list", self.items.len(), {
|
||||||
.w(test_width)
|
let items = self.items.clone();
|
||||||
.p_3()
|
move |visible_range, _, cx| {
|
||||||
.gap_1()
|
let mut elements = Vec::with_capacity(visible_range.len());
|
||||||
.overflow_y_scrollbar()
|
for ix in visible_range {
|
||||||
.child("Scrollable Example")
|
let item = &items[ix];
|
||||||
.children(self.items.iter().map(|item| {
|
elements.push(
|
||||||
div()
|
div()
|
||||||
.h(ITEM_HEIGHT)
|
.h(ITEM_HEIGHT)
|
||||||
.bg(cx.theme().background)
|
.pt_1()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.child(item.to_string())
|
.child(
|
||||||
})),
|
div()
|
||||||
|
.p_2()
|
||||||
|
.bg(cx.theme().secondary)
|
||||||
|
.child(item.to_string()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
elements
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.py_1()
|
||||||
|
.px_3()
|
||||||
|
.size_full()
|
||||||
|
.track_scroll(self.scroll_handle.clone()),
|
||||||
)
|
)
|
||||||
|
.vertical_scrollbar(&self.scroll_handle)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -187,6 +187,7 @@ struct StockTableDelegate {
|
||||||
columns: Vec<Column>,
|
columns: Vec<Column>,
|
||||||
size: Size,
|
size: Size,
|
||||||
loading: bool,
|
loading: bool,
|
||||||
|
lazy_load: bool,
|
||||||
full_loading: bool,
|
full_loading: bool,
|
||||||
eof: bool,
|
eof: bool,
|
||||||
visible_rows: Range<usize>,
|
visible_rows: Range<usize>,
|
||||||
|
|
@ -200,6 +201,7 @@ impl StockTableDelegate {
|
||||||
Self {
|
Self {
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
stocks: random_stocks(size),
|
stocks: random_stocks(size),
|
||||||
|
lazy_load: false,
|
||||||
columns: vec![
|
columns: vec![
|
||||||
Column::new("id", "ID")
|
Column::new("id", "ID")
|
||||||
.width(60.)
|
.width(60.)
|
||||||
|
|
@ -543,6 +545,10 @@ impl TableDelegate for StockTableDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_eof(&self, _: &App) -> bool {
|
fn is_eof(&self, _: &App) -> bool {
|
||||||
|
if !self.lazy_load {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return !self.loading && !self.eof;
|
return !self.loading && !self.eof;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,6 +557,10 @@ impl TableDelegate for StockTableDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_more(&mut self, _: &mut Window, cx: &mut Context<TableState<Self>>) {
|
fn load_more(&mut self, _: &mut Window, cx: &mut Context<TableState<Self>>) {
|
||||||
|
if !self.lazy_load {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.loading = true;
|
self.loading = true;
|
||||||
|
|
||||||
self._load_task = cx.spawn(async move |view, cx| {
|
self._load_task = cx.spawn(async move |view, cx| {
|
||||||
|
|
@ -959,7 +969,18 @@ impl Render for TableStory {
|
||||||
this.child(
|
this.child(
|
||||||
h_flex().gap_1().child(Spinner::new()).child("Loading..."),
|
h_flex().gap_1().child(Spinner::new()).child("Loading..."),
|
||||||
)
|
)
|
||||||
}),
|
})
|
||||||
|
.child(
|
||||||
|
Checkbox::new("lazy-load")
|
||||||
|
.label("Lazy Load")
|
||||||
|
.checked(delegate.lazy_load)
|
||||||
|
.on_click(cx.listener(|this, check: &bool, _, cx| {
|
||||||
|
this.table.update(cx, |table, cx| {
|
||||||
|
table.delegate_mut().lazy_load = *check;
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
})),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|
|
||||||
2
crates/ui/tests/fixtures/layout.json
vendored
2
crates/ui/tests/fixtures/layout.json
vendored
|
|
@ -136,7 +136,7 @@
|
||||||
"children": [],
|
"children": [],
|
||||||
"info": {
|
"info": {
|
||||||
"panel": {
|
"panel": {
|
||||||
"story_klass": "ScrollableStory"
|
"story_klass": "ScrollbarStory"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue