virtual_list: Add element state to cache item size calculate. (#1048)
This commit is contained in:
parent
4156c831c6
commit
40a2467374
2 changed files with 82 additions and 68 deletions
|
|
@ -143,6 +143,7 @@ pub fn measure_if(name: impl Into<SharedString>, if_: bool, f: impl FnOnce()) {
|
|||
|
||||
/// Measures the execution time.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn measure(name: impl Into<SharedString>, f: impl FnOnce()) {
|
||||
measure_if(name, true, f);
|
||||
}
|
||||
|
|
@ -153,6 +154,7 @@ pub struct Measure {
|
|||
}
|
||||
|
||||
impl Measure {
|
||||
#[track_caller]
|
||||
pub fn new(name: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
|
|
@ -160,6 +162,7 @@ impl Measure {
|
|||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn end(self) {
|
||||
let duration = self.start.elapsed();
|
||||
tracing::trace!("{} in {:?}", self.name, duration);
|
||||
|
|
|
|||
|
|
@ -13,18 +13,20 @@
|
|||
use std::{cmp, ops::Range, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
div, point, px, size, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask, Context, Div,
|
||||
Element, ElementId, Entity, GlobalElementId, Hitbox, InteractiveElement, IntoElement,
|
||||
IsZero as _, Pixels, Render, ScrollHandle, Size, Stateful, StatefulInteractiveElement,
|
||||
StyleRefinement, Styled, Window,
|
||||
div, point, px, size, Along, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask,
|
||||
Context, Div, Element, ElementId, Entity, GlobalElementId, Hitbox, InteractiveElement,
|
||||
IntoElement, IsZero as _, Pixels, Render, ScrollHandle, Size, Stateful,
|
||||
StatefulInteractiveElement, StyleRefinement, Styled, Window,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// Create a virtual list in Vertical direction.
|
||||
/// Create a [`VirtualList`] in vertical direction.
|
||||
///
|
||||
/// This is like `uniform_list` in GPUI, but support two axis.
|
||||
///
|
||||
/// The `item_sizes` is the size of each column.
|
||||
///
|
||||
/// See also [`h_virtual_list`]
|
||||
#[inline]
|
||||
pub fn v_virtual_list<R, V>(
|
||||
view: Entity<V>,
|
||||
|
|
@ -39,7 +41,9 @@ where
|
|||
virtual_list(view, id, Axis::Vertical, item_sizes, f)
|
||||
}
|
||||
|
||||
/// Create a virtual list in Horizontal direction.
|
||||
/// Create a [`VirtualList`] in horizontal direction.
|
||||
///
|
||||
/// See also [`v_virtual_list`]
|
||||
#[inline]
|
||||
pub fn h_virtual_list<R, V>(
|
||||
view: Entity<V>,
|
||||
|
|
@ -91,7 +95,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// VirtualItem component for rendering a large number of differently sized columns.
|
||||
/// VirtualList component for rendering a large number of differently sized items.
|
||||
pub struct VirtualList {
|
||||
id: ElementId,
|
||||
axis: Axis,
|
||||
|
|
@ -153,8 +157,15 @@ impl VirtualList {
|
|||
pub struct VirtualListFrameState {
|
||||
/// Visible items to be painted.
|
||||
items: SmallVec<[AnyElement; 32]>,
|
||||
item_sizes: Vec<Pixels>,
|
||||
item_origins: Vec<Pixels>,
|
||||
size_layout: ItemSizeLayout,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ItemSizeLayout {
|
||||
items_sizes: Rc<Vec<Size<Pixels>>>,
|
||||
container_size: Size<Pixels>,
|
||||
sizes: Vec<Pixels>,
|
||||
origins: Vec<Pixels>,
|
||||
}
|
||||
|
||||
impl IntoElement for VirtualList {
|
||||
|
|
@ -197,68 +208,68 @@ impl Element for VirtualList {
|
|||
}
|
||||
.to_pixels(font_size.into(), window.rem_size());
|
||||
|
||||
// TODO: To cache the item_sizes, item_origins
|
||||
// If there have 500,000 items, this method will speed about 500~600µs
|
||||
// let start = std::time::Instant::now();
|
||||
// Prepare each item's size by axis
|
||||
let item_sizes = match self.axis {
|
||||
Axis::Horizontal => self
|
||||
.item_sizes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, size)| {
|
||||
if i == self.items_count - 1 {
|
||||
size.width
|
||||
} else {
|
||||
size.width + gap
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
Axis::Vertical => self
|
||||
.item_sizes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, size)| {
|
||||
if i == self.items_count - 1 {
|
||||
size.height
|
||||
} else {
|
||||
size.height + gap
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
let (layout_id, size_layout) = window.with_element_state(
|
||||
global_id.unwrap(),
|
||||
|state: Option<ItemSizeLayout>, window| {
|
||||
let mut state = state.unwrap_or(ItemSizeLayout::default());
|
||||
|
||||
// Prepare each item's origin by axis
|
||||
let item_origins = match self.axis {
|
||||
Axis::Horizontal => item_sizes
|
||||
.iter()
|
||||
.scan(px(0.), |cumulative_x, size| {
|
||||
let x = *cumulative_x;
|
||||
*cumulative_x += *size;
|
||||
Some(x)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
Axis::Vertical => item_sizes
|
||||
.iter()
|
||||
.scan(px(0.), |cumulative_y, size| {
|
||||
let y = *cumulative_y;
|
||||
*cumulative_y += *size;
|
||||
Some(y)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
// println!("layout: {} {:?}", item_sizes.len(), start.elapsed());
|
||||
if state.items_sizes != self.item_sizes {
|
||||
state.items_sizes = self.item_sizes.clone();
|
||||
// Prepare each item's size by axis
|
||||
state.sizes = self
|
||||
.item_sizes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, size)| {
|
||||
let size = size.along(self.axis);
|
||||
if i + 1 == self.items_count {
|
||||
size
|
||||
} else {
|
||||
size + gap
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (layout_id, _) = self
|
||||
.base
|
||||
.request_layout(global_id, inspector_id, window, cx);
|
||||
// Prepare each item's origin by axis
|
||||
state.origins = state
|
||||
.sizes
|
||||
.iter()
|
||||
.scan(px(0.), |cumulative, size| match self.axis {
|
||||
Axis::Horizontal => {
|
||||
let x = *cumulative;
|
||||
*cumulative += *size;
|
||||
Some(x)
|
||||
}
|
||||
Axis::Vertical => {
|
||||
let y = *cumulative;
|
||||
*cumulative += *size;
|
||||
Some(y)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
state.container_size = Size {
|
||||
width: px(self.item_sizes.iter().map(|size| size.width.0).sum::<f32>()),
|
||||
height: px(self
|
||||
.item_sizes
|
||||
.iter()
|
||||
.map(|size| size.height.0)
|
||||
.sum::<f32>()),
|
||||
};
|
||||
}
|
||||
|
||||
let (layout_id, _) = self
|
||||
.base
|
||||
.request_layout(global_id, inspector_id, window, cx);
|
||||
|
||||
((layout_id, state.clone()), state)
|
||||
},
|
||||
);
|
||||
|
||||
(
|
||||
layout_id,
|
||||
VirtualListFrameState {
|
||||
items: SmallVec::new(),
|
||||
item_sizes,
|
||||
item_origins,
|
||||
size_layout,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -295,17 +306,17 @@ impl Element for VirtualList {
|
|||
Axis::Vertical => border.top + padding.top + border.bottom + padding.bottom,
|
||||
};
|
||||
|
||||
let item_sizes = &layout.item_sizes;
|
||||
let item_origins = &layout.item_origins;
|
||||
let item_sizes = &layout.size_layout.sizes;
|
||||
let item_origins = &layout.size_layout.origins;
|
||||
|
||||
let content_size = match self.axis {
|
||||
Axis::Horizontal => Size {
|
||||
width: px(item_sizes.iter().map(|size| size.0).sum::<f32>()) + padding_size,
|
||||
width: layout.size_layout.container_size.width + padding_size,
|
||||
height: (first_item_size.height + padding_size).max(padded_bounds.size.height),
|
||||
},
|
||||
Axis::Vertical => Size {
|
||||
width: (first_item_size.width + padding_size).max(padded_bounds.size.width),
|
||||
height: px(item_sizes.iter().map(|size| size.0).sum::<f32>()) + padding_size,
|
||||
height: layout.size_layout.container_size.height + padding_size,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue