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) image - 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)