diff --git a/crates/story/examples/dock.rs b/crates/story/examples/dock.rs index 0522f372..de238f14 100644 --- a/crates/story/examples/dock.rs +++ b/crates/story/examples/dock.rs @@ -11,7 +11,7 @@ use gpui_component_assets::Assets; use gpui_component_story::{ AccordionStory, AppState, AppTitleBar, ButtonStory, CalendarStory, DialogStory, FormStory, IconStory, ImageStory, InputStory, LabelStory, ListStory, NotificationStory, Open, - PopoverStory, ProgressStory, ResizableStory, ScrollableStory, SelectStory, SidebarStory, + PopoverStory, ProgressStory, ResizableStory, ScrollbarStory, SelectStory, SidebarStory, StoryContainer, SwitchStory, TableStory, TooltipStory, WebViewStory, }; use serde::Deserialize; @@ -269,7 +269,7 @@ impl StoryWorkspace { ), DockItem::tabs( vec![ - Arc::new(StoryContainer::panel::(window, cx)), + Arc::new(StoryContainer::panel::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), ], None, @@ -358,7 +358,7 @@ impl StoryWorkspace { Arc::new(StoryContainer::panel::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), - Arc::new(StoryContainer::panel::(window, cx)), + Arc::new(StoryContainer::panel::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), @@ -447,7 +447,7 @@ impl StoryWorkspace { 12 => Arc::new(StoryContainer::panel::(window, cx)), 13 => Arc::new(StoryContainer::panel::(window, cx)), 14 => Arc::new(StoryContainer::panel::(window, cx)), - 15 => Arc::new(StoryContainer::panel::(window, cx)), + 15 => Arc::new(StoryContainer::panel::(window, cx)), 16 => Arc::new(StoryContainer::panel::(window, cx)), 17 => Arc::new(StoryContainer::panel::(window, cx)), _ => Arc::new(StoryContainer::panel::(window, cx)), diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 2530ad50..2f45f520 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -30,7 +30,7 @@ mod popover_story; mod progress_story; mod radio_story; mod resizable_story; -mod scrollable_story; +mod scrollbar_story; mod select_story; mod settings_story; mod sheet_story; @@ -90,7 +90,7 @@ pub use popover_story::PopoverStory; pub use progress_story::ProgressStory; pub use radio_story::RadioStory; pub use resizable_story::ResizableStory; -pub use scrollable_story::ScrollableStory; +pub use scrollbar_story::ScrollbarStory; pub use select_story::SelectStory; use serde::{Deserialize, Serialize}; pub use settings_story::SettingsStory; @@ -670,7 +670,7 @@ impl StoryState { "PopoverStory" => story!(PopoverStory), "ProgressStory" => story!(ProgressStory), "ResizableStory" => story!(ResizableStory), - "ScrollableStory" => story!(ScrollableStory), + "ScrollbarStory" => story!(ScrollbarStory), "SwitchStory" => story!(SwitchStory), "TableStory" => story!(TableStory), "LabelStory" => story!(LabelStory), diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index 2d0e0aac..a6747b0e 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -68,7 +68,7 @@ impl Gallery { StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), - StoryContainer::panel::(window, cx), + StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), diff --git a/crates/story/src/scrollable_story.rs b/crates/story/src/scrollbar_story.rs similarity index 68% rename from crates/story/src/scrollable_story.rs rename to crates/story/src/scrollbar_story.rs index 36b5bfc0..9106b5d2 100644 --- a/crates/story/src/scrollable_story.rs +++ b/crates/story/src/scrollbar_story.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use gpui::{ 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::{ ActiveTheme as _, Selectable, @@ -12,17 +12,18 @@ use gpui_component::{ v_flex, }; -pub struct ScrollableStory { +pub struct ScrollbarStory { focus_handle: FocusHandle, items: Vec, item_sizes: Rc>>, test_width: Pixels, 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 { let items = (0..5000).map(|i| format!("Item {}", i)).collect::>(); let test_width = px(3000.); @@ -37,6 +38,7 @@ impl ScrollableStory { item_sizes: Rc::new(item_sizes), test_width, 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 { - "Scrollable" + "Scrollbar" } fn description() -> &'static str { - "A scrollable container." + "Add scrollbar to a scrollable element." } fn new_view(window: &mut Window, cx: &mut App) -> Entity { @@ -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 { self.focus_handle.clone() } } -impl Render for ScrollableStory { +impl Render for ScrollbarStory { fn render( &mut self, _: &mut gpui::Window, cx: &mut gpui::Context, ) -> impl gpui::IntoElement { - let test_width = self.test_width; v_flex() .size_full() .gap_4() @@ -149,26 +150,38 @@ impl Render for ScrollableStory { .relative() .border_1() .border_color(cx.theme().border) - .w_full() - .max_h(px(400.)) - .min_h(px(200.)) + .flex_1() .child( - v_flex() - .w(test_width) - .p_3() - .gap_1() - .overflow_y_scrollbar() - .child("Scrollable Example") - .children(self.items.iter().map(|item| { - div() - .h(ITEM_HEIGHT) - .bg(cx.theme().background) - .items_center() - .justify_center() - .text_sm() - .child(item.to_string()) - })), + uniform_list("list", self.items.len(), { + let items = self.items.clone(); + move |visible_range, _, cx| { + let mut elements = Vec::with_capacity(visible_range.len()); + for ix in visible_range { + let item = &items[ix]; + elements.push( + div() + .h(ITEM_HEIGHT) + .pt_1() + .items_center() + .justify_center() + .text_sm() + .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) }) } } diff --git a/crates/story/src/table_story.rs b/crates/story/src/table_story.rs index 7a3cff19..26659bdc 100644 --- a/crates/story/src/table_story.rs +++ b/crates/story/src/table_story.rs @@ -187,6 +187,7 @@ struct StockTableDelegate { columns: Vec, size: Size, loading: bool, + lazy_load: bool, full_loading: bool, eof: bool, visible_rows: Range, @@ -200,6 +201,7 @@ impl StockTableDelegate { Self { size: Size::default(), stocks: random_stocks(size), + lazy_load: false, columns: vec![ Column::new("id", "ID") .width(60.) @@ -543,6 +545,10 @@ impl TableDelegate for StockTableDelegate { } fn is_eof(&self, _: &App) -> bool { + if !self.lazy_load { + return true; + } + return !self.loading && !self.eof; } @@ -551,6 +557,10 @@ impl TableDelegate for StockTableDelegate { } fn load_more(&mut self, _: &mut Window, cx: &mut Context>) { + if !self.lazy_load { + return; + } + self.loading = true; self._load_task = cx.spawn(async move |view, cx| { @@ -959,7 +969,18 @@ impl Render for TableStory { this.child( 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( h_flex() diff --git a/crates/ui/tests/fixtures/layout.json b/crates/ui/tests/fixtures/layout.json index 17d4807a..d7cfa48a 100644 --- a/crates/ui/tests/fixtures/layout.json +++ b/crates/ui/tests/fixtures/layout.json @@ -136,7 +136,7 @@ "children": [], "info": { "panel": { - "story_klass": "ScrollableStory" + "story_klass": "ScrollbarStory" } } }