diff --git a/Cargo.lock b/Cargo.lock index dd99d886..74a098e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1432,7 +1432,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "indexmap 2.9.0", "rustc-hash 2.1.0", @@ -1990,7 +1990,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "proc-macro2", "quote", @@ -3142,7 +3142,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -3327,7 +3327,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3597,7 +3597,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "anyhow", "bytes", @@ -3615,7 +3615,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "rustls", "rustls-platform-verifier", @@ -4441,7 +4441,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "anyhow", "bindgen 0.71.1", @@ -6390,7 +6390,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "derive_refineable", "workspace-hack", @@ -6545,7 +6545,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "anyhow", "bytes", @@ -7121,7 +7121,7 @@ checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "anyhow", "serde", @@ -7704,7 +7704,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "arrayvec", "log", @@ -8931,7 +8931,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git#3e27fa1d92d084a3374691a83bd3ab5766b14ad8" +source = "git+https://github.com/zed-industries/zed.git#65759d43163bd36cce8f8aa492ba2628ca005a7a" dependencies = [ "anyhow", "async-fs 2.1.2", diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 6416361d..f5008d6a 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -43,6 +43,7 @@ mod themes; mod title_bar; mod toggle_story; mod tooltip_story; +mod virtual_list_story; mod webview_story; mod welcome_story; @@ -100,6 +101,7 @@ pub use title_bar::AppTitleBar; pub use toggle_story::ToggleStory; pub use tooltip_story::TooltipStory; use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _}; +pub use virtual_list_story::VirtualListStory; pub use webview_story::WebViewStory; pub use welcome_story::WelcomeStory; diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index e999cc66..37799d82 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -53,8 +53,8 @@ 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), StoryContainer::panel::(window, cx), @@ -78,6 +78,7 @@ impl Gallery { 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/scrollable_story.rs index feb8d82d..5d8bf9c5 100644 --- a/crates/story/src/scrollable_story.rs +++ b/crates/story/src/scrollable_story.rs @@ -1,29 +1,23 @@ use std::rc::Rc; use gpui::{ - div, px, size, App, AppContext, Axis, Context, Entity, Focusable, InteractiveElement, - IntoElement, ParentElement, Pixels, Render, ScrollHandle, SharedString, Size, Styled, Window, + div, px, size, App, AppContext, Axis, Context, Entity, FocusHandle, Focusable, + InteractiveElement, IntoElement, ParentElement, Pixels, Render, Size, Styled, Window, }; use gpui_component::{ button::{Button, ButtonGroup}, - divider::Divider, h_flex, - label::Label, - scroll::{Scrollbar, ScrollbarAxis, ScrollbarState}, - v_flex, v_virtual_list, ActiveTheme as _, Selectable, StyledExt as _, + scroll::ScrollbarState, + v_flex, ActiveTheme as _, Selectable, StyledExt as _, }; pub struct ScrollableStory { - focus_handle: gpui::FocusHandle, - scroll_handle: ScrollHandle, - scroll_size: gpui::Size, + focus_handle: FocusHandle, scroll_state: ScrollbarState, items: Vec, item_sizes: Rc>>, test_width: Pixels, - axis: ScrollbarAxis, size_mode: usize, - message: SharedString, } const ITEM_HEIGHT: Pixels = px(30.); @@ -39,15 +33,11 @@ impl ScrollableStory { Self { focus_handle: cx.focus_handle(), - scroll_handle: ScrollHandle::new(), scroll_state: ScrollbarState::default(), - scroll_size: gpui::Size::default(), items, item_sizes: Rc::new(item_sizes), test_width, - axis: ScrollbarAxis::Both, size_mode: 0, - message: SharedString::default(), } } @@ -83,91 +73,45 @@ impl ScrollableStory { cx.notify(); } - pub fn change_axis(&mut self, axis: ScrollbarAxis, cx: &mut Context) { - self.axis = axis; - cx.notify(); - } - - fn set_message(&mut self, msg: &str, cx: &mut Context) { - self.message = SharedString::from(msg.to_string()); - cx.notify(); - } - fn render_buttons(&mut self, cx: &mut Context) -> impl IntoElement { - h_flex() - .gap_2() - .justify_between() - .child( - h_flex() - .gap_2() + h_flex().gap_2().justify_between().child( + h_flex().gap_2().child( + ButtonGroup::new("test-cases") + .outline() + .compact() .child( - ButtonGroup::new("test-cases") - .outline() - .compact() - .child( - Button::new("test-0") - .label("Size 0") - .selected(self.size_mode == 0), - ) - .child( - Button::new("test-1") - .label("Size 1") - .selected(self.size_mode == 1), - ) - .child( - Button::new("test-2") - .label("Size 2") - .selected(self.size_mode == 2), - ) - .child( - Button::new("test-3") - .label("Size 3") - .selected(self.size_mode == 3), - ) - .on_click(cx.listener(|view, clicks: &Vec, _, cx| { - if clicks.contains(&0) { - view.change_test_cases(0, cx) - } else if clicks.contains(&1) { - view.change_test_cases(1, cx) - } else if clicks.contains(&2) { - view.change_test_cases(2, cx) - } else if clicks.contains(&3) { - view.change_test_cases(3, cx) - } - })), + Button::new("test-0") + .label("Size 0") + .selected(self.size_mode == 0), ) - .child(Divider::vertical().px_2()) .child( - ButtonGroup::new("scrollbars") - .outline() - .compact() - .child( - Button::new("test-axis-both") - .label("Both Scrollbar") - .selected(self.axis.is_both()), - ) - .child( - Button::new("test-axis-vertical") - .label("Vertical") - .selected(self.axis.is_vertical()), - ) - .child( - Button::new("test-axis-horizontal") - .label("Horizontal") - .selected(self.axis.is_horizontal()), - ) - .on_click(cx.listener(|view, clicks: &Vec, _, cx| { - if clicks.contains(&0) { - view.change_axis(ScrollbarAxis::Both, cx) - } else if clicks.contains(&1) { - view.change_axis(ScrollbarAxis::Vertical, cx) - } else if clicks.contains(&2) { - view.change_axis(ScrollbarAxis::Horizontal, cx) - } - })), - ), - ) - .child(Label::new(self.message.clone())) + Button::new("test-1") + .label("Size 1") + .selected(self.size_mode == 1), + ) + .child( + Button::new("test-2") + .label("Size 2") + .selected(self.size_mode == 2), + ) + .child( + Button::new("test-3") + .label("Size 3") + .selected(self.size_mode == 3), + ) + .on_click(cx.listener(|view, clicks: &Vec, _, cx| { + if clicks.contains(&0) { + view.change_test_cases(0, cx) + } else if clicks.contains(&1) { + view.change_test_cases(1, cx) + } else if clicks.contains(&2) { + view.change_test_cases(2, cx) + } else if clicks.contains(&3) { + view.change_test_cases(3, cx) + } + })), + ), + ) } } @@ -177,8 +121,7 @@ impl super::Story for ScrollableStory { } fn description() -> &'static str { - "Add vertical or horizontal, or both scrollbars to a container, \ - and use `virtual_list` to render a large number of items." + "A scrollable container." } fn new_view(window: &mut Window, cx: &mut App) -> Entity { @@ -198,79 +141,11 @@ impl Render for ScrollableStory { _: &mut gpui::Window, cx: &mut gpui::Context, ) -> impl gpui::IntoElement { + let test_width = self.test_width; v_flex() .size_full() .gap_4() .child(self.render_buttons(cx)) - .child( - div().w_full().flex_1().min_h_64().child( - div().relative().size_full().child( - v_flex() - .id("test-0") - .relative() - .size_full() - .child( - v_virtual_list( - cx.entity().clone(), - "items", - self.item_sizes.clone(), - move |story, visible_range, content_size, _, cx| { - story.set_message( - &format!("visible_range: {:?}", visible_range), - cx, - ); - story.scroll_size = content_size; - visible_range - .map(|ix| { - h_flex() - .h(ITEM_HEIGHT) - .gap_1() - .children( - (0..(story.test_width.0 as i32 / 100)) - .map(|i| { - div() - .flex() - .h_full() - .items_center() - .justify_center() - .text_sm() - .w(px(100.)) - .bg(cx.theme().secondary) - .child(if i == 0 { - format!("{}", ix) - } else { - format!("{}", i) - }) - }) - .collect::>(), - ) - .items_center() - }) - .collect::>() - }, - ) - .track_scroll(&self.scroll_handle) - .p_4() - .border_1() - .border_color(cx.theme().border) - .v_flex() - .gap_1(), - ) - .child({ - div() - .absolute() - .top_0() - .left_0() - .right_0() - .bottom_0() - .child( - Scrollbar::both(&self.scroll_state, &self.scroll_handle) - .axis(self.axis), - ) - }), - ), - ), - ) .child({ div() .relative() @@ -282,7 +157,7 @@ impl Render for ScrollableStory { .child( v_flex() .p_3() - .w(self.test_width) + .w(test_width) .id("test-1") .scrollable(Axis::Vertical) .gap_1() diff --git a/crates/story/src/virtual_list_story.rs b/crates/story/src/virtual_list_story.rs new file mode 100644 index 00000000..d978c8a3 --- /dev/null +++ b/crates/story/src/virtual_list_story.rs @@ -0,0 +1,256 @@ +use std::{ops::Range, rc::Rc}; + +use gpui::{ + div, px, size, App, AppContext, Context, Div, Entity, FocusHandle, Focusable, + InteractiveElement, IntoElement, ParentElement, Pixels, Render, ScrollHandle, Size, Styled, + Window, +}; +use gpui_component::{ + button::{Button, ButtonGroup}, + divider::Divider, + h_flex, + scroll::{Scrollbar, ScrollbarAxis, ScrollbarState}, + v_flex, v_virtual_list, ActiveTheme as _, Selectable, +}; + +pub struct VirtualListStory { + focus_handle: FocusHandle, + scroll_handle: ScrollHandle, + scroll_state: ScrollbarState, + items: Vec, + item_sizes: Rc>>, + columns_count: usize, + axis: ScrollbarAxis, + size_mode: usize, + visible_range: Range, +} + +const ITEM_SIZE: Size = size(px(100.), px(30.)); + +impl VirtualListStory { + fn new(_: &mut Window, cx: &mut Context) -> Self { + let items = (0..5000).map(|i| format!("Item {}", i)).collect::>(); + let item_sizes = items.iter().map(|_| ITEM_SIZE).collect::>(); + + Self { + focus_handle: cx.focus_handle(), + scroll_handle: ScrollHandle::new(), + scroll_state: ScrollbarState::default(), + items, + item_sizes: Rc::new(item_sizes), + columns_count: 100, + axis: ScrollbarAxis::Both, + size_mode: 0, + visible_range: (0..0), + } + } + + pub fn view(window: &mut Window, cx: &mut App) -> Entity { + cx.new(|cx| Self::new(window, cx)) + } + + pub fn change_test_cases(&mut self, n: usize, cx: &mut Context) { + self.size_mode = n; + if n == 0 { + self.items = (0..5000).map(|i| format!("Item {}", i)).collect::>(); + self.columns_count = 30; + } else if n == 1 { + self.items = (0..100).map(|i| format!("Item {}", i)).collect::>(); + self.columns_count = 100; + } else if n == 2 { + self.items = (0..500000) + .map(|i| format!("Item {}", i)) + .collect::>(); + self.columns_count = 100; + } else { + self.items = (0..5).map(|i| format!("Item {}", i)).collect::>(); + self.columns_count = 10; + } + + self.item_sizes = Rc::new(self.items.iter().map(|_| ITEM_SIZE).collect()); + + self.scroll_state = ScrollbarState::default(); + cx.notify(); + } + + pub fn change_axis(&mut self, axis: ScrollbarAxis, cx: &mut Context) { + self.axis = axis; + cx.notify(); + } + + fn render_buttons(&mut self, cx: &mut Context) -> impl IntoElement { + h_flex() + .gap_2() + .justify_between() + .child( + h_flex() + .gap_2() + .child( + ButtonGroup::new("test-cases") + .outline() + .compact() + .child( + Button::new("test-0") + .label("Size 0") + .selected(self.size_mode == 0), + ) + .child( + Button::new("test-1") + .label("Size 1") + .selected(self.size_mode == 1), + ) + .child( + Button::new("test-2") + .label("Size 2") + .selected(self.size_mode == 2), + ) + .child( + Button::new("test-3") + .label("Size 3") + .selected(self.size_mode == 3), + ) + .on_click(cx.listener(|view, clicks: &Vec, _, cx| { + if clicks.contains(&0) { + view.change_test_cases(0, cx) + } else if clicks.contains(&1) { + view.change_test_cases(1, cx) + } else if clicks.contains(&2) { + view.change_test_cases(2, cx) + } else if clicks.contains(&3) { + view.change_test_cases(3, cx) + } + })), + ) + .child(Divider::vertical().px_2()) + .child( + ButtonGroup::new("scrollbars") + .outline() + .compact() + .child( + Button::new("test-axis-both") + .label("Both Scrollbar") + .selected(self.axis.is_both()), + ) + .child( + Button::new("test-axis-vertical") + .label("Vertical") + .selected(self.axis.is_vertical()), + ) + .child( + Button::new("test-axis-horizontal") + .label("Horizontal") + .selected(self.axis.is_horizontal()), + ) + .on_click(cx.listener(|view, clicks: &Vec, _, cx| { + if clicks.contains(&0) { + view.change_axis(ScrollbarAxis::Both, cx) + } else if clicks.contains(&1) { + view.change_axis(ScrollbarAxis::Vertical, cx) + } else if clicks.contains(&2) { + view.change_axis(ScrollbarAxis::Horizontal, cx) + } + })), + ), + ) + .child(format!("visible_range: {:?}", self.visible_range)) + } +} + +impl super::Story for VirtualListStory { + fn title() -> &'static str { + "VirtualList" + } + + fn description() -> &'static str { + "Add vertical or horizontal, or both scrollbars to a container, \ + and use `virtual_list` to render a large number of items." + } + + fn new_view(window: &mut Window, cx: &mut App) -> Entity { + Self::view(window, cx) + } +} + +impl Focusable for VirtualListStory { + fn focus_handle(&self, _: &gpui::App) -> gpui::FocusHandle { + self.focus_handle.clone() + } +} + +impl Render for VirtualListStory { + fn render( + &mut self, + _: &mut gpui::Window, + cx: &mut gpui::Context, + ) -> impl gpui::IntoElement { + let columns_count = self.columns_count; + + fn render_item(cx: &App) -> Div { + div() + .flex() + .h_full() + .items_center() + .justify_center() + .text_sm() + .w(ITEM_SIZE.width) + .h(ITEM_SIZE.height) + .bg(cx.theme().secondary) + } + + v_flex() + .size_full() + .gap_4() + .child(self.render_buttons(cx)) + .child( + div().w_full().flex_1().min_h_64().child( + div().relative().size_full().child( + v_flex() + .id("list") + .relative() + .size_full() + .child( + v_virtual_list( + cx.entity().clone(), + "items", + self.item_sizes.clone(), + move |story, visible_range, _, cx| { + story.visible_range = visible_range.clone(); + + visible_range + .map(|ix| { + h_flex().gap_1().items_center().children( + (0..columns_count).map(|i| { + render_item(cx).child(if i == 0 { + format!("row: {}", ix) + } else { + format!("{}", i) + }) + }), + ) + }) + .collect() + }, + ) + .track_scroll(&self.scroll_handle) + .p_4() + .border_1() + .border_color(cx.theme().border) + .gap_1(), + ) + .child({ + div() + .absolute() + .top_0() + .left_0() + .right_0() + .bottom_0() + .child( + Scrollbar::both(&self.scroll_state, &self.scroll_handle) + .axis(self.axis), + ) + }), + ), + ), + ) + } +} diff --git a/crates/ui/src/table/mod.rs b/crates/ui/src/table/mod.rs index d012a4e1..d20e8770 100644 --- a/crates/ui/src/table/mod.rs +++ b/crates/ui/src/table/mod.rs @@ -1093,7 +1093,7 @@ where Axis::Horizontal, col_sizes, { - move |table, visible_range: Range, _, window, cx| { + move |table, visible_range: Range, window, cx| { table.update_visible_range_if_need( visible_range.clone(), Axis::Horizontal, diff --git a/crates/ui/src/virtual_list.rs b/crates/ui/src/virtual_list.rs index b1e16bc5..45ce3685 100644 --- a/crates/ui/src/virtual_list.rs +++ b/crates/ui/src/virtual_list.rs @@ -32,7 +32,7 @@ pub fn v_virtual_list( view: Entity, id: impl Into, item_sizes: Rc>>, - f: impl 'static + Fn(&mut V, Range, Size, &mut Window, &mut Context) -> Vec, + f: impl 'static + Fn(&mut V, Range, &mut Window, &mut Context) -> Vec, ) -> VirtualList where R: IntoElement, @@ -49,7 +49,7 @@ pub fn h_virtual_list( view: Entity, id: impl Into, item_sizes: Rc>>, - f: impl 'static + Fn(&mut V, Range, Size, &mut Window, &mut Context) -> Vec, + f: impl 'static + Fn(&mut V, Range, &mut Window, &mut Context) -> Vec, ) -> VirtualList where R: IntoElement, @@ -63,7 +63,7 @@ pub(crate) fn virtual_list( id: impl Into, axis: Axis, item_sizes: Rc>>, - f: impl 'static + Fn(&mut V, Range, Size, &mut Window, &mut Context) -> Vec, + f: impl 'static + Fn(&mut V, Range, &mut Window, &mut Context) -> Vec, ) -> VirtualList where R: IntoElement, @@ -71,9 +71,9 @@ where { let id: ElementId = id.into(); let scroll_handle = ScrollHandle::default(); - let render_range = move |visible_range, content_size, window: &mut Window, cx: &mut App| { + let render_range = move |visible_range, window: &mut Window, cx: &mut App| { view.update(cx, |this, cx| { - f(this, visible_range, content_size, window, cx) + f(this, visible_range, window, cx) .into_iter() .map(|component| component.into_any_element()) .collect() @@ -101,16 +101,10 @@ pub struct VirtualList { axis: Axis, base: Stateful
, scroll_handle: ScrollHandle, - // scroll_handle: ScrollHandle, items_count: usize, item_sizes: Rc>>, render_items: Box< - dyn for<'a> Fn( - Range, - Size, - &'a mut Window, - &'a mut App, - ) -> SmallVec<[AnyElement; 64]>, + dyn for<'a> Fn(Range, &'a mut Window, &'a mut App) -> SmallVec<[AnyElement; 64]>, >, } @@ -146,7 +140,7 @@ impl VirtualList { // So we try to use the second item to measure, if there is no second item, use the first item. let item_ix = if self.items_count > 1 { 1 } else { 0 }; - let mut items = (self.render_items)(item_ix..item_ix + 1, Size::default(), window, cx); + let mut items = (self.render_items)(item_ix..item_ix + 1, window, cx); let Some(mut item_to_measure) = items.pop() else { return Size::default(); }; @@ -165,7 +159,7 @@ pub struct VirtualListFrameState { #[derive(Default, Clone)] pub struct ItemSizeLayout { items_sizes: Rc>>, - container_size: Size, + content_size: Pixels, sizes: Vec, origins: Vec, } @@ -197,18 +191,18 @@ impl Element for VirtualList { window: &mut Window, cx: &mut App, ) -> (gpui::LayoutId, Self::RequestLayoutState) { + let rem_size = window.rem_size(); let style = self .base .interactivity() .compute_style(global_id, None, window, cx); - let font_size = window.text_style().font_size.to_pixels(window.rem_size()); + let font_size = window.text_style().font_size.to_pixels(rem_size); // Including the gap between items for calculate the item size - let gap = match self.axis { - Axis::Horizontal => style.gap.width, - Axis::Vertical => style.gap.height, - } - .to_pixels(font_size.into(), window.rem_size()); + let gap = style + .gap + .along(self.axis) + .to_pixels(font_size.into(), rem_size); let (layout_id, size_layout) = window.with_element_state( global_id.unwrap(), @@ -249,14 +243,7 @@ impl Element for VirtualList { } }) .collect::>(); - state.container_size = Size { - width: px(self.item_sizes.iter().map(|size| size.width.0).sum::()), - height: px(self - .item_sizes - .iter() - .map(|size| size.height.0) - .sum::()), - }; + state.content_size = px(state.sizes.iter().map(|size| size.0).sum::()); } let (layout_id, _) = self @@ -285,6 +272,8 @@ impl Element for VirtualList { window: &mut Window, cx: &mut App, ) -> Self::PrepaintState { + let first_item_size = self.measure_item(window, cx); + let style = self .base .interactivity() @@ -294,16 +283,14 @@ impl Element for VirtualList { .padding .to_pixels(bounds.size.into(), window.rem_size()); - let first_item_size = self.measure_item(window, cx); - - let padded_bounds = Bounds::from_corners( + let container_with_padding_bounds = Bounds::from_corners( bounds.origin + point(border.left + padding.left, border.top + padding.top), bounds.bottom_right() - point(border.right + padding.right, border.bottom + padding.bottom), ); // Get border + padding pixel size - let padding_size = match self.axis { + let padding_width = match self.axis { Axis::Horizontal => border.left + padding.left + border.right + padding.right, Axis::Vertical => border.top + padding.top + border.bottom + padding.bottom, }; @@ -311,14 +298,16 @@ impl Element for VirtualList { let item_sizes = &layout.size_layout.sizes; let item_origins = &layout.size_layout.origins; - let content_size = match self.axis { + let scroll_size = match self.axis { Axis::Horizontal => Size { - width: layout.size_layout.container_size.width + padding_size, - height: (first_item_size.height + padding_size).max(padded_bounds.size.height), + width: layout.size_layout.content_size + padding_width, + height: (first_item_size.height + padding_width) + .max(container_with_padding_bounds.size.height), }, Axis::Vertical => Size { - width: (first_item_size.width + padding_size).max(padded_bounds.size.width), - height: layout.size_layout.container_size.height + padding_size, + width: (first_item_size.width + padding_width) + .max(container_with_padding_bounds.size.width), + height: layout.size_layout.content_size + padding_width, }, }; @@ -326,7 +315,7 @@ impl Element for VirtualList { global_id, inspector_id, bounds, - content_size, + scroll_size, window, cx, |style, _, hitbox, window, cx| { @@ -342,15 +331,9 @@ impl Element for VirtualList { ); if self.items_count > 0 { - let is_scrolled = match self.axis { - Axis::Horizontal => !scroll_offset.x.is_zero(), - Axis::Vertical => !scroll_offset.y.is_zero(), - }; - - let min_scroll_offset = match self.axis { - Axis::Horizontal => padded_bounds.size.width - content_size.width, - Axis::Vertical => padded_bounds.size.height - content_size.height, - }; + let is_scrolled = !scroll_offset.along(self.axis).is_zero(); + let min_scroll_offset = + padded_bounds.size.along(self.axis) - scroll_size.along(self.axis); if is_scrolled { match self.axis { @@ -425,8 +408,7 @@ impl Element for VirtualList { let visible_range = first_visible_element_ix ..cmp::min(last_visible_element_ix, self.items_count); - let items = - (self.render_items)(visible_range.clone(), content_size, window, cx); + let items = (self.render_items)(visible_range.clone(), window, cx); let content_mask = ContentMask { bounds }; window.with_content_mask(Some(content_mask), |window| {