diff --git a/crates/story/src/table_story.rs b/crates/story/src/table_story.rs index 23ba09db..1e45f883 100644 --- a/crates/story/src/table_story.rs +++ b/crates/story/src/table_story.rs @@ -1,4 +1,7 @@ -use std::time::{self, Duration}; +use std::{ + ops::Range, + time::{self, Duration}, +}; use fake::{Fake, Faker}; use gpui::{ @@ -181,6 +184,8 @@ struct StockTableDelegate { loading: bool, fixed_cols: bool, is_eof: bool, + visible_rows: Range, + visible_cols: Range, } impl StockTableDelegate { @@ -250,6 +255,8 @@ impl StockTableDelegate { fixed_cols: false, loading: false, is_eof: false, + visible_cols: Range::default(), + visible_rows: Range::default(), } } @@ -490,6 +497,22 @@ impl TableDelegate for StockTableDelegate { }) .detach(); } + + fn visible_rows_changed( + &mut self, + visible_range: Range, + _: &mut ViewContext>, + ) { + self.visible_rows = visible_range; + } + + fn visible_cols_changed( + &mut self, + visible_range: Range, + _: &mut ViewContext>, + ) { + self.visible_cols = visible_range; + } } pub struct TableStory { @@ -842,6 +865,8 @@ impl Render for TableStory { this.child(h_flex().gap_1().child(Indicator::new()).child("Loading...")) }) .child(format!("Total Rows: {}", rows_count)) + .child(format!("Visible Rows: {:?}", delegate.visible_rows)) + .child(format!("Visible Cols: {:?}", delegate.visible_cols)) .when(delegate.is_eof, |this| this.child("All data loaded.")), ), ) diff --git a/crates/ui/src/table.rs b/crates/ui/src/table.rs index 5286aa3d..96208466 100644 --- a/crates/ui/src/table.rs +++ b/crates/ui/src/table.rs @@ -116,6 +116,27 @@ struct FixedCols { left: usize, } +/// The visible range of the rows and columns. +#[derive(Debug, Default)] +pub struct VisibleRangeState { + /// The visible range of the rows. + rows: Range, + /// The visible range of the columns. + cols: Range, +} + +impl VisibleRangeState { + /// Returns the visible range of the rows. + pub fn rows(&self) -> Range { + self.rows.clone() + } + + /// Returns the visible range of the columns. + pub fn cols(&self) -> Range { + self.cols.clone() + } +} + pub struct Table { focus_handle: FocusHandle, delegate: D, @@ -148,6 +169,8 @@ pub struct Table { border: bool, /// The cell size of the table. size: Size, + /// The visible range of the rows and columns. + visible_range: VisibleRangeState, } #[allow(unused)] @@ -280,6 +303,32 @@ pub trait TableDelegate: Sized + 'static { fn render_last_empty_col(&mut self, cx: &mut ViewContext>) -> Div { h_flex().w_5().h_full().flex_shrink_0() } + + /// Called when the visible range of the rows changed. + /// + /// NOTE: Make sure this method is fast, because it will be called frequently. + /// + /// This can used to handle some data update, to only update the visible rows. + /// Please ensure that the data is updated in the background task. + fn visible_rows_changed( + &mut self, + visible_range: Range, + cx: &mut ViewContext>, + ) { + } + + /// Called when the visible range of the columns changed. + /// + /// NOTE: Make sure this method is fast, because it will be called frequently. + /// + /// This can used to handle some data update, to only update the visible rows. + /// Please ensure that the data is updated in the background task. + fn visible_cols_changed( + &mut self, + visible_range: Range, + cx: &mut ViewContext>, + ) { + } } impl Table @@ -307,6 +356,7 @@ where stripe: false, border: true, size: Size::default(), + visible_range: VisibleRangeState::default(), }; this.prepare_col_groups(cx); @@ -436,6 +486,11 @@ where cx.notify(); } + /// Returns the visible range of the rows and columns. + pub fn visible_range(&self) -> &VisibleRangeState { + &self.visible_range + } + fn on_row_click(&mut self, ev: &MouseDownEvent, row_ix: usize, cx: &mut ViewContext) { if ev.button == MouseButton::Right { self.right_clicked_row = Some(row_ix); @@ -630,6 +685,35 @@ where } } + fn update_visible_range_if_need( + &mut self, + visible_range: Range, + axis: Axis, + cx: &mut ViewContext, + ) { + // Skip when visible range is only 1 item. + // The visual_list will use first item to measure. + if visible_range.len() <= 1 { + return; + } + + if axis == Axis::Vertical { + if self.visible_range.rows == visible_range { + return; + } + self.delegate_mut() + .visible_rows_changed(visible_range.clone(), cx); + self.visible_range.rows = visible_range; + } else { + if self.visible_range.cols == visible_range { + return; + } + self.delegate_mut() + .visible_cols_changed(visible_range.clone(), cx); + self.visible_range.cols = visible_range; + } + } + /// Returns the size of the content area. fn head_content_bounds(&self) -> gpui::Bounds { let has_fixed_cols = self.fixed_head_cols_bounds.size.width > px(0.0); @@ -1054,6 +1138,12 @@ where .child( virtual_list(view, row_ix, Axis::Horizontal, col_sizes, { move |table, visible_range: Range, _, cx| { + table.update_visible_range_if_need( + visible_range.clone(), + Axis::Horizontal, + cx, + ); + visible_range .map(|col_ix| { let col_ix = col_ix + left_cols_count; @@ -1227,6 +1317,11 @@ where { move |table, visible_range, cx| { table.load_more_if_need(visible_range.clone(), cx); + table.update_visible_range_if_need( + visible_range.clone(), + Axis::Vertical, + cx, + ); if visible_range.end > rows_count { table.scroll_to_row(