virtual_list: Fix VirtualList by measure item to get correct content_size to avoid scroll overflow. (#1388)
- Fixed VirtualList example the horizontal scrollbar not appear. - Fixed VirtualList example scroll slow, this because not call `self.scroll_handle.set_offset` to update.
This commit is contained in:
parent
2a9bc2a2f8
commit
e0c1f91ed4
1 changed files with 45 additions and 16 deletions
|
|
@ -119,7 +119,8 @@ impl VirtualListScrollHandle {
|
|||
///
|
||||
/// This is like `uniform_list` in GPUI, but support two axis.
|
||||
///
|
||||
/// The `item_sizes` is the size of each column.
|
||||
/// The `item_sizes` is the size of each column,
|
||||
/// only the `height` is used, `width` is ignored and VirtualList will measure the first item width.
|
||||
///
|
||||
/// See also [`h_virtual_list`]
|
||||
#[inline]
|
||||
|
|
@ -138,6 +139,9 @@ where
|
|||
|
||||
/// Create a [`VirtualList`] in horizontal direction.
|
||||
///
|
||||
/// The `item_sizes` is the size of each column,
|
||||
/// only the `width` is used, `height` is ignored and VirtualList will measure the first item height.
|
||||
///
|
||||
/// See also [`v_virtual_list`]
|
||||
#[inline]
|
||||
pub fn h_virtual_list<R, V>(
|
||||
|
|
@ -280,6 +284,31 @@ impl VirtualList {
|
|||
self.scroll_handle.set_offset(scroll_offset);
|
||||
scroll_offset
|
||||
}
|
||||
|
||||
/// Ref from: https://github.com/zed-industries/zed/blob/83f9f9d9e3f5914392cab9a09e3472711a1d7b38/crates/gpui/src/elements/uniform_list.rs#L660
|
||||
fn measure_item(
|
||||
&self,
|
||||
list_width: Option<Pixels>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Size<Pixels> {
|
||||
if self.items_count == 0 {
|
||||
return Size::default();
|
||||
}
|
||||
|
||||
let item_ix = 0;
|
||||
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();
|
||||
};
|
||||
let available_space = size(
|
||||
list_width.map_or(AvailableSpace::MinContent, |width| {
|
||||
AvailableSpace::Definite(width)
|
||||
}),
|
||||
AvailableSpace::MinContent,
|
||||
);
|
||||
item_to_measure.layout_as_root(available_space, window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Frame state used by the [VirtualItem].
|
||||
|
|
@ -328,6 +357,7 @@ impl Element for VirtualList {
|
|||
let rem_size = window.rem_size();
|
||||
let font_size = window.text_style().font_size.to_pixels(rem_size);
|
||||
let mut size_layout = ItemSizeLayout::default();
|
||||
let longest_item_size = self.measure_item(None, window, cx);
|
||||
|
||||
let layout_id = self.base.interactivity().request_layout(
|
||||
global_id,
|
||||
|
|
@ -388,17 +418,11 @@ impl Element for VirtualList {
|
|||
.iter()
|
||||
.map(|size| size.as_f32())
|
||||
.sum::<f32>()),
|
||||
height: state
|
||||
.items_sizes
|
||||
.get(0)
|
||||
.map_or(px(0.), |size| size.height),
|
||||
height: longest_item_size.height,
|
||||
}
|
||||
} else {
|
||||
Size {
|
||||
width: state
|
||||
.items_sizes
|
||||
.get(0)
|
||||
.map_or(px(0.), |size| size.width),
|
||||
width: longest_item_size.width,
|
||||
height: px(state
|
||||
.sizes
|
||||
.iter()
|
||||
|
|
@ -557,7 +581,16 @@ impl Element for VirtualList {
|
|||
scroll_to_item,
|
||||
);
|
||||
}
|
||||
scroll_offset = scroll_offset.min(&point(px(0.), px(0.)));
|
||||
|
||||
scroll_offset = scroll_offset
|
||||
.max(&point(
|
||||
content_bounds.size.width - layout.size_layout.content_size.width,
|
||||
content_bounds.size.height - layout.size_layout.content_size.height,
|
||||
))
|
||||
.min(&point(px(0.), px(0.)));
|
||||
if scroll_offset != self.scroll_handle.offset() {
|
||||
self.scroll_handle.set_offset(scroll_offset);
|
||||
}
|
||||
|
||||
self.base.interactivity().prepaint(
|
||||
global_id,
|
||||
|
|
@ -571,20 +604,16 @@ impl Element for VirtualList {
|
|||
let min_scroll_offset = content_bounds.size.along(self.axis)
|
||||
- layout.size_layout.content_size.along(self.axis);
|
||||
|
||||
// Do not trigger scrolling if the content is smaller than the container.
|
||||
if min_scroll_offset.as_f32() >= 0. {
|
||||
scroll_offset.x = px(0.);
|
||||
scroll_offset.y = px(0.);
|
||||
}
|
||||
|
||||
let is_scrolled = !scroll_offset.along(self.axis).is_zero();
|
||||
if is_scrolled {
|
||||
match self.axis {
|
||||
Axis::Horizontal if scroll_offset.x < min_scroll_offset => {
|
||||
scroll_offset.x = min_scroll_offset;
|
||||
self.scroll_handle.set_offset(scroll_offset);
|
||||
}
|
||||
Axis::Vertical if scroll_offset.y < min_scroll_offset => {
|
||||
scroll_offset.y = min_scroll_offset;
|
||||
self.scroll_handle.set_offset(scroll_offset);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue