From 1e44b5659f677423de52c437e25613d24379cc68 Mon Sep 17 00:00:00 2001
From: xda <150917089+xda2023@users.noreply.github.com>
Date: Tue, 24 Sep 2024 23:07:27 +0900
Subject: [PATCH] table: Render table stripe on background even there is no
enough rows (#256)
- And add `flex_shrink_0` to table head.
Fix https://github.com/huacnlee/gpui-component/issues/160
---------
Co-authored-by: Jason Lee
---
crates/ui/src/table.rs | 202 +++++++++++++++++++++++++++++------------
1 file changed, 144 insertions(+), 58 deletions(-)
diff --git a/crates/ui/src/table.rs b/crates/ui/src/table.rs
index 70add161..4cc6a993 100644
--- a/crates/ui/src/table.rs
+++ b/crates/ui/src/table.rs
@@ -284,6 +284,11 @@ where
cx.notify();
}
+ fn scroll_to_row(&mut self, row_ix: usize, cx: &mut ViewContext) {
+ self.vertical_scroll_handle.scroll_to_item(row_ix);
+ cx.notify();
+ }
+
fn set_selected_row(&mut self, row_ix: usize, cx: &mut ViewContext) {
self.selection_state = SelectionState::Row;
self.selected_row = Some(row_ix);
@@ -737,6 +742,28 @@ where
let cols_count: usize = self.delegate.cols_count();
let rows_count = self.delegate.rows_count();
+ let row_height = self.vertical_scroll_handle.0.borrow().last_item_height;
+ let total_height = self
+ .vertical_scroll_handle
+ .0
+ .borrow()
+ .base_handle
+ .bounds()
+ .size
+ .height;
+
+ // Calculate the extra rows needed to fill the table for stripe style.
+ let mut extra_rows_needed = 0;
+ if let Some(row_height) = row_height {
+ if row_height > px(0.) {
+ let actual_height = row_height * rows_count as f32;
+ let remaining_height = total_height - actual_height;
+ if remaining_height > px(0.) {
+ extra_rows_needed = (remaining_height / row_height).ceil() as usize;
+ }
+ }
+ }
+
fn last_empty_col(_: &mut WindowContext) -> Div {
h_flex().w(px(100.)).h_full().flex_shrink_0()
}
@@ -761,6 +788,7 @@ where
.flex_grow()
.h_10()
.w_full()
+ .flex_shrink_0()
.border_b_1()
.border_color(cx.theme().border)
.child(
@@ -795,70 +823,128 @@ where
} else {
this.child(
h_flex().id("table-body").flex_grow().size_full().child(
- uniform_list(view, "table-uniform-list", rows_count, {
- let horizontal_scroll_handle = horizontal_scroll_handle.clone();
- move |table, visible_range, cx| {
- table.load_more(visible_range.clone(), cx);
+ uniform_list(
+ view,
+ "table-uniform-list",
+ rows_count + extra_rows_needed,
+ {
+ let horizontal_scroll_handle = horizontal_scroll_handle.clone();
+ move |table, visible_range, cx| {
+ table.load_more(visible_range.clone(), cx);
- visible_range
- .map(|row_ix| {
- table
- .delegate
- .render_tr(row_ix, cx)
- .id(("table-row", row_ix))
- .w_full()
- .when(row_ix > 0, |this| {
- this.border_t_1()
- .border_color(cx.theme().border)
- })
- .when(table.stripe && row_ix % 2 != 0, |this| {
- this.bg(cx.theme().table_even)
- })
- .hover(|this| {
- if table.selected_row == Some(row_ix) {
- this
- } else {
- this.bg(cx.theme().table_hover)
- }
- })
- .children((0..cols_count).map(|col_ix| {
+ if visible_range.end > rows_count {
+ table.scroll_to_row(
+ std::cmp::min(visible_range.start, rows_count - 1),
+ cx,
+ );
+ }
+
+ // Render fake rows to fill the table
+ visible_range
+ .map(|row_ix| {
+ // Render real rows for available data
+ if row_ix < rows_count {
table
- .col_wrap(col_ix, cx) // Make the row scroll sync with the horizontal_scroll_handle to support horizontal scrolling.
- .left(horizontal_scroll_handle.offset().x)
- .child(
+ .delegate
+ .render_tr(row_ix, cx)
+ .id(("table-row", row_ix))
+ .w_full()
+ .when(row_ix > 0, |this| {
+ this.border_t_1()
+ .border_color(cx.theme().border)
+ })
+ .when(
+ table.stripe && row_ix % 2 != 0,
+ |this| this.bg(cx.theme().table_even),
+ )
+ .hover(|this| {
+ if table.selected_row == Some(row_ix) {
+ this
+ } else {
+ this.bg(cx.theme().table_hover)
+ }
+ })
+ .children((0..cols_count).map(|col_ix| {
table
- .render_cell(col_ix, cx)
- .flex_shrink_0()
+ // Make the row scroll sync with the horizontal_scroll_handle to support horizontal scrolling.
+ .col_wrap(col_ix, cx)
+ .left(
+ horizontal_scroll_handle
+ .offset()
+ .x,
+ )
.child(
- table.delegate.render_td(
- row_ix, col_ix, cx,
- ),
- ),
+ table
+ .render_cell(col_ix, cx)
+ .flex_shrink_0()
+ .child(
+ table
+ .delegate
+ .render_td(
+ row_ix, col_ix,
+ cx,
+ ),
+ ),
+ )
+ }))
+ .child(last_empty_col(cx))
+ // Row selected style
+ .when_some(
+ table.selected_row,
+ |this, selected_row| {
+ this.when(
+ row_ix == selected_row
+ && table.selection_state
+ == SelectionState::Row,
+ |this| {
+ this.bg(cx
+ .theme()
+ .table_active)
+ },
+ )
+ },
)
- }))
- .child(last_empty_col(cx))
- // Row selected style
- .when_some(
- table.selected_row,
- |this, selected_row| {
- this.when(
- row_ix == selected_row
- && table.selection_state
- == SelectionState::Row,
- |this| this.bg(cx.theme().table_active),
+ .on_mouse_down(
+ MouseButton::Left,
+ cx.listener(move |this, _, cx| {
+ this.on_row_click(row_ix, cx);
+ }),
)
- },
- )
- .on_mouse_down(
- MouseButton::Left,
- cx.listener(move |this, _, cx| {
- this.on_row_click(row_ix, cx);
- }),
- )
- })
- .collect::>()
- }
- })
+ } else {
+ // Render fake rows to fill the rest table space
+ table
+ .delegate
+ .render_tr(row_ix, cx)
+ .id(("table-row-fake", row_ix))
+ .w_full()
+ .h_full()
+ .border_t_1()
+ .border_color(cx.theme().border)
+ .when(
+ table.stripe && row_ix % 2 != 0,
+ |this| this.bg(cx.theme().table_even),
+ )
+ .children((0..cols_count).map(|col_ix| {
+ h_flex()
+ .left(
+ horizontal_scroll_handle
+ .offset()
+ .x,
+ )
+ .child(
+ table
+ .render_cell(col_ix, cx)
+ .flex_shrink_0()
+ .child(div().size_full()),
+ )
+ }))
+ .child(last_empty_col(cx))
+ }
+ })
+ .collect::>()
+ }
+ },
+ )
.flex_grow()
.size_full()
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)