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.
|
/// Measures the execution time.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
pub fn measure(name: impl Into<SharedString>, f: impl FnOnce()) {
|
pub fn measure(name: impl Into<SharedString>, f: impl FnOnce()) {
|
||||||
measure_if(name, true, f);
|
measure_if(name, true, f);
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +154,7 @@ pub struct Measure {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Measure {
|
impl Measure {
|
||||||
|
#[track_caller]
|
||||||
pub fn new(name: impl Into<SharedString>) -> Self {
|
pub fn new(name: impl Into<SharedString>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
|
|
@ -160,6 +162,7 @@ impl Measure {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn end(self) {
|
pub fn end(self) {
|
||||||
let duration = self.start.elapsed();
|
let duration = self.start.elapsed();
|
||||||
tracing::trace!("{} in {:?}", self.name, duration);
|
tracing::trace!("{} in {:?}", self.name, duration);
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,20 @@
|
||||||
use std::{cmp, ops::Range, rc::Rc};
|
use std::{cmp, ops::Range, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, point, px, size, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask, Context, Div,
|
div, point, px, size, Along, AnyElement, App, AvailableSpace, Axis, Bounds, ContentMask,
|
||||||
Element, ElementId, Entity, GlobalElementId, Hitbox, InteractiveElement, IntoElement,
|
Context, Div, Element, ElementId, Entity, GlobalElementId, Hitbox, InteractiveElement,
|
||||||
IsZero as _, Pixels, Render, ScrollHandle, Size, Stateful, StatefulInteractiveElement,
|
IntoElement, IsZero as _, Pixels, Render, ScrollHandle, Size, Stateful,
|
||||||
StyleRefinement, Styled, Window,
|
StatefulInteractiveElement, StyleRefinement, Styled, Window,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
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.
|
/// 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.
|
||||||
|
///
|
||||||
|
/// See also [`h_virtual_list`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn v_virtual_list<R, V>(
|
pub fn v_virtual_list<R, V>(
|
||||||
view: Entity<V>,
|
view: Entity<V>,
|
||||||
|
|
@ -39,7 +41,9 @@ where
|
||||||
virtual_list(view, id, Axis::Vertical, item_sizes, f)
|
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]
|
#[inline]
|
||||||
pub fn h_virtual_list<R, V>(
|
pub fn h_virtual_list<R, V>(
|
||||||
view: Entity<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 {
|
pub struct VirtualList {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
axis: Axis,
|
axis: Axis,
|
||||||
|
|
@ -153,8 +157,15 @@ impl VirtualList {
|
||||||
pub struct VirtualListFrameState {
|
pub struct VirtualListFrameState {
|
||||||
/// Visible items to be painted.
|
/// Visible items to be painted.
|
||||||
items: SmallVec<[AnyElement; 32]>,
|
items: SmallVec<[AnyElement; 32]>,
|
||||||
item_sizes: Vec<Pixels>,
|
size_layout: ItemSizeLayout,
|
||||||
item_origins: Vec<Pixels>,
|
}
|
||||||
|
|
||||||
|
#[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 {
|
impl IntoElement for VirtualList {
|
||||||
|
|
@ -197,68 +208,68 @@ impl Element for VirtualList {
|
||||||
}
|
}
|
||||||
.to_pixels(font_size.into(), window.rem_size());
|
.to_pixels(font_size.into(), window.rem_size());
|
||||||
|
|
||||||
// TODO: To cache the item_sizes, item_origins
|
let (layout_id, size_layout) = window.with_element_state(
|
||||||
// If there have 500,000 items, this method will speed about 500~600µs
|
global_id.unwrap(),
|
||||||
// let start = std::time::Instant::now();
|
|state: Option<ItemSizeLayout>, window| {
|
||||||
// Prepare each item's size by axis
|
let mut state = state.unwrap_or(ItemSizeLayout::default());
|
||||||
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<_>>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prepare each item's origin by axis
|
if state.items_sizes != self.item_sizes {
|
||||||
let item_origins = match self.axis {
|
state.items_sizes = self.item_sizes.clone();
|
||||||
Axis::Horizontal => item_sizes
|
// Prepare each item's size by axis
|
||||||
.iter()
|
state.sizes = self
|
||||||
.scan(px(0.), |cumulative_x, size| {
|
.item_sizes
|
||||||
let x = *cumulative_x;
|
.iter()
|
||||||
*cumulative_x += *size;
|
.enumerate()
|
||||||
Some(x)
|
.map(|(i, size)| {
|
||||||
})
|
let size = size.along(self.axis);
|
||||||
.collect::<Vec<_>>(),
|
if i + 1 == self.items_count {
|
||||||
Axis::Vertical => item_sizes
|
size
|
||||||
.iter()
|
} else {
|
||||||
.scan(px(0.), |cumulative_y, size| {
|
size + gap
|
||||||
let y = *cumulative_y;
|
}
|
||||||
*cumulative_y += *size;
|
})
|
||||||
Some(y)
|
.collect::<Vec<_>>();
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
};
|
|
||||||
// println!("layout: {} {:?}", item_sizes.len(), start.elapsed());
|
|
||||||
|
|
||||||
let (layout_id, _) = self
|
// Prepare each item's origin by axis
|
||||||
.base
|
state.origins = state
|
||||||
.request_layout(global_id, inspector_id, window, cx);
|
.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,
|
layout_id,
|
||||||
VirtualListFrameState {
|
VirtualListFrameState {
|
||||||
items: SmallVec::new(),
|
items: SmallVec::new(),
|
||||||
item_sizes,
|
size_layout,
|
||||||
item_origins,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -295,17 +306,17 @@ impl Element for VirtualList {
|
||||||
Axis::Vertical => border.top + padding.top + border.bottom + padding.bottom,
|
Axis::Vertical => border.top + padding.top + border.bottom + padding.bottom,
|
||||||
};
|
};
|
||||||
|
|
||||||
let item_sizes = &layout.item_sizes;
|
let item_sizes = &layout.size_layout.sizes;
|
||||||
let item_origins = &layout.item_origins;
|
let item_origins = &layout.size_layout.origins;
|
||||||
|
|
||||||
let content_size = match self.axis {
|
let content_size = match self.axis {
|
||||||
Axis::Horizontal => Size {
|
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),
|
height: (first_item_size.height + padding_size).max(padded_bounds.size.height),
|
||||||
},
|
},
|
||||||
Axis::Vertical => Size {
|
Axis::Vertical => Size {
|
||||||
width: (first_item_size.width + padding_size).max(padded_bounds.size.width),
|
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