table: Improve table render performance. (#421)

Before: ~85 - 100FPS
After:  ~110 - 115FPS
This commit is contained in:
Jason Lee 2024-11-18 14:43:22 +08:00 committed by GitHub
parent fa6abe5a9e
commit e6012fcc8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,9 +11,10 @@ use crate::{
use gpui::{ use gpui::{
actions, canvas, div, prelude::FluentBuilder, px, uniform_list, AppContext, Bounds, Div, actions, canvas, div, prelude::FluentBuilder, px, uniform_list, AppContext, Bounds, Div,
DragMoveEvent, Edges, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, DragMoveEvent, Edges, Entity, EntityId, EventEmitter, FocusHandle, FocusableView,
InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, Render, InteractiveElement, IntoElement, KeyBinding, ListSizingBehavior, MouseButton, ParentElement,
ScrollHandle, ScrollStrategy, SharedString, Stateful, StatefulInteractiveElement as _, Styled, Pixels, Point, Render, ScrollHandle, ScrollStrategy, SharedString, Stateful,
UniformListScrollHandle, ViewContext, VisualContext as _, WindowContext, StatefulInteractiveElement as _, Styled, UniformListScrollHandle, ViewContext,
VisualContext as _, WindowContext,
}; };
actions!( actions!(
@ -102,6 +103,11 @@ pub enum TableEvent {
ColWidthsChanged(Vec<Option<Pixels>>), ColWidthsChanged(Vec<Option<Pixels>>),
} }
#[derive(Clone, Copy, Default)]
struct FixedCols {
left: usize,
}
pub struct Table<D: TableDelegate> { pub struct Table<D: TableDelegate> {
focus_handle: FocusHandle, focus_handle: FocusHandle,
delegate: D, delegate: D,
@ -113,6 +119,7 @@ pub struct Table<D: TableDelegate> {
head_content_bounds: Bounds<Pixels>, head_content_bounds: Bounds<Pixels>,
col_groups: Vec<ColGroup>, col_groups: Vec<ColGroup>,
fixed_cols: FixedCols,
pub vertical_scroll_handle: UniformListScrollHandle, pub vertical_scroll_handle: UniformListScrollHandle,
pub scrollbar_state: Rc<Cell<ScrollbarState>>, pub scrollbar_state: Rc<Cell<ScrollbarState>>,
@ -272,6 +279,7 @@ where
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
delegate, delegate,
col_groups: Vec::new(), col_groups: Vec::new(),
fixed_cols: FixedCols::default(),
horizontal_scroll_handle: ScrollHandle::new(), horizontal_scroll_handle: ScrollHandle::new(),
vertical_scroll_handle: UniformListScrollHandle::new(), vertical_scroll_handle: UniformListScrollHandle::new(),
scrollbar_state: Rc::new(Cell::new(ScrollbarState::new())), scrollbar_state: Rc::new(Cell::new(ScrollbarState::new())),
@ -339,6 +347,11 @@ where
fixed: self.delegate.col_fixed(col_ix, cx), fixed: self.delegate.col_fixed(col_ix, cx),
}) })
.collect(); .collect();
self.fixed_cols.left = self
.col_groups
.iter()
.filter(|col| col.fixed == Some(ColFixed::Left))
.count();
cx.notify(); cx.notify();
} }
@ -733,14 +746,12 @@ where
fn render_sort_icon( fn render_sort_icon(
&self, &self,
col_ix: usize, col_ix: usize,
col_group: &ColGroup,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<impl IntoElement> { ) -> Option<impl IntoElement> {
let sort = self.col_groups.get(col_ix).and_then(|g| g.sort); let Some(sort) = col_group.sort else {
if sort.is_none() {
return None; return None;
} };
let sort = sort.unwrap();
let (icon, is_on) = match sort { let (icon, is_on) = match sort {
ColSort::Ascending => (IconName::SortAscending, true), ColSort::Ascending => (IconName::SortAscending, true),
@ -776,8 +787,10 @@ where
fn render_th(&self, col_ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render_th(&self, col_ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
let entity_id = cx.entity_id(); let entity_id = cx.entity_id();
let col_group = self.col_groups.get(col_ix).expect("BUG: invalid col index"); let col_group = self.col_groups.get(col_ix).expect("BUG: invalid col index");
let moveable = self.delegate.can_move_col(col_ix, cx);
let paddings = self.delegate.col_padding(col_ix, cx);
let name = self.delegate.col_name(col_ix, cx); let name = self.delegate.col_name(col_ix, cx);
h_flex() h_flex()
.child( .child(
self.render_cell(col_ix, cx) self.render_cell(col_ix, cx)
@ -794,16 +807,15 @@ where
.justify_between() .justify_between()
.items_center() .items_center()
.child(self.delegate.render_th(col_ix, cx)) .child(self.delegate.render_th(col_ix, cx))
.when_some(self.delegate().col_padding(col_ix, cx), |this, padding| { .when_some(paddings, |this, paddings| {
// Leave right space for the sort icon, if this column have custom padding // Leave right space for the sort icon, if this column have custom padding
let offset_pr = let offset_pr =
self.size.table_cell_padding().right - padding.right; self.size.table_cell_padding().right - paddings.right;
this.pr(offset_pr.max(px(0.))) this.pr(offset_pr.max(px(0.)))
}) })
.children(self.render_sort_icon(col_ix, cx)), .children(self.render_sort_icon(col_ix, &col_group, cx)),
) )
.when(self.delegate.can_move_col(col_ix, cx), |this| { .when(moveable, |this| {
this.on_drag( this.on_drag(
DragCol { DragCol {
entity_id, entity_id,
@ -855,11 +867,6 @@ where
) -> impl IntoElement { ) -> impl IntoElement {
let view = cx.view().clone(); let view = cx.view().clone();
let horizontal_scroll_handle = self.horizontal_scroll_handle.clone(); let horizontal_scroll_handle = self.horizontal_scroll_handle.clone();
let fixed_cols_count = self
.col_groups
.iter()
.filter(|col| col.fixed.is_some())
.count();
h_flex() h_flex()
.w_full() .w_full()
@ -868,7 +875,7 @@ where
.border_b_1() .border_b_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.text_color(cx.theme().table_head_foreground) .text_color(cx.theme().table_head_foreground)
.when(fixed_cols_count > 0, |this| { .when(left_cols_count > 0, |this| {
let view = view.clone(); let view = view.clone();
// Render left fixed columns // Render left fixed columns
this.child( this.child(
@ -898,55 +905,38 @@ where
) )
}) })
.child( .child(
// Render other normal columns // Columns
uniform_list(view.clone(), "table-head-uniform-list", 1, { h_flex()
let horizontal_scroll_handle = horizontal_scroll_handle.clone(); .id("table-head")
let view = view.clone(); .size_full()
move |table, _, cx| { .overflow_scroll()
let view = view.clone(); .relative()
.track_scroll(&horizontal_scroll_handle)
// Columns .bg(cx.theme().table_head)
.child(
h_flex() h_flex()
.id("table-head")
.h_full()
.w_full()
.overflow_scroll()
.relative() .relative()
.track_scroll(&horizontal_scroll_handle) .children(
.bg(cx.theme().table_head) self.col_groups
.child( .iter()
h_flex() .filter(|col| col.fixed == None)
.relative() .enumerate()
.children( .map(|(col_ix, _)| {
table self.render_th(left_cols_count + col_ix, cx)
.col_groups }),
.iter()
.filter(|col| col.fixed == None)
.enumerate()
.map(|(col_ix, _)| {
table.render_th(left_cols_count + col_ix, cx)
}),
)
.child(table.delegate.render_last_empty_col(cx))
.child(
canvas(
move |bounds, cx| {
view.update(cx, |r, _| {
r.head_content_bounds = bounds
})
},
|_, _, _| {},
)
.absolute()
.size_full(),
),
) )
.map(|this| vec![this]) .child(self.delegate.render_last_empty_col(cx))
} .child(
}) canvas(
.h(self.size.table_row_height()) move |bounds, cx| {
.h_full() view.update(cx, |r, _| r.head_content_bounds = bounds)
.flex_1(), },
|_, _, _| {},
)
.absolute()
.size_full(),
),
),
) )
} }
@ -1111,6 +1101,7 @@ where
let vertical_scroll_handle = self.vertical_scroll_handle.clone(); let vertical_scroll_handle = self.vertical_scroll_handle.clone();
let horizontal_scroll_handle = self.horizontal_scroll_handle.clone(); let horizontal_scroll_handle = self.horizontal_scroll_handle.clone();
let cols_count: usize = self.delegate.cols_count(cx); let cols_count: usize = self.delegate.cols_count(cx);
let left_cols_count = self.fixed_cols.left;
let rows_count = self.delegate.rows_count(cx); let rows_count = self.delegate.rows_count(cx);
let row_height = self let row_height = self
@ -1140,12 +1131,6 @@ where
} }
} }
let left_cols_count = self
.col_groups
.iter()
.filter(|col| col.fixed == Some(ColFixed::Left))
.count();
let inner_table = v_flex() let inner_table = v_flex()
.key_context("Table") .key_context("Table")
.id("table") .id("table")
@ -1197,7 +1182,7 @@ where
) )
.flex_grow() .flex_grow()
.size_full() .size_full()
.with_sizing_behavior(gpui::ListSizingBehavior::Auto) .with_sizing_behavior(ListSizingBehavior::Auto)
.track_scroll(vertical_scroll_handle) .track_scroll(vertical_scroll_handle)
.into_any_element(), .into_any_element(),
), ),