Skip to main content

gpui_component/table/
delegate.rs

1use std::ops::Range;
2
3use gpui::{
4    App, Context, Div, InteractiveElement as _, IntoElement, ParentElement as _, Pixels,
5    SharedString, Stateful, Styled as _, Window, div,
6};
7
8use crate::{
9    ActiveTheme as _, Icon, IconName, Size, h_flex,
10    menu::PopupMenu,
11    table::{Column, ColumnGroup, ColumnSort, TableState, loading::Loading},
12};
13
14/// A delegate trait for providing data and rendering for a table.
15#[allow(unused)]
16pub trait TableDelegate: Sized + 'static {
17    /// Return the number of columns in the table.
18    fn columns_count(&self, cx: &App) -> usize;
19
20    /// Return the number of rows in the table.
21    fn rows_count(&self, cx: &App) -> usize;
22
23    /// Returns the table column at the given index.
24    ///
25    /// This only call on Table prepare or refresh.
26    fn column(&self, col_ix: usize, cx: &App) -> Column;
27
28    /// Perform sort on the column at the given index.
29    fn perform_sort(
30        &mut self,
31        col_ix: usize,
32        sort: ColumnSort,
33        window: &mut Window,
34        cx: &mut Context<TableState<Self>>,
35    ) {
36    }
37
38    /// Return the width the content of column `col_ix` needs to be read
39    /// whole, or `None` to leave the column as it is.
40    ///
41    /// Called when the column's resize handle is double-clicked. Measure the
42    /// widest of the column's cells and its header as drawn; the table adds
43    /// the cell padding, clamps the sum to the column's `min_width` and
44    /// `max_width`, and emits [`TableEvent::ColumnWidthsChanged`]. A sortable
45    /// column's header also holds the table's sort icon (a 0.75rem icon with
46    /// 2 px of padding on each side), which a header measurement must count.
47    ///
48    /// This is called from inside the table's own update, so an
49    /// implementation must not update the table's entity again.
50    ///
51    /// Returns `None` by default, so a double-click changes nothing.
52    ///
53    /// [`TableEvent::ColumnWidthsChanged`]: crate::table::TableEvent::ColumnWidthsChanged
54    fn fit_column(
55        &mut self,
56        col_ix: usize,
57        window: &mut Window,
58        cx: &mut Context<TableState<Self>>,
59    ) -> Option<Pixels> {
60        None
61    }
62
63    /// Render the table head row.
64    fn render_header(
65        &mut self,
66        window: &mut Window,
67        cx: &mut Context<TableState<Self>>,
68    ) -> Stateful<Div> {
69        div().id("header")
70    }
71
72    /// Return the group headers definitions (can be multi-level).
73    ///
74    /// By default, it returns None, meaning no group headers.
75    fn group_headers(&self, cx: &App) -> Option<Vec<Vec<ColumnGroup>>> {
76        None
77    }
78
79    /// Custom render for a group header cell.
80    /// Receives the group label, the logical col_span, and the pixel width.
81    fn render_group_th(
82        &mut self,
83        label: &SharedString,
84        _col_span: usize,
85        width: Pixels,
86        _window: &mut Window,
87        cx: &mut Context<TableState<Self>>,
88    ) -> impl IntoElement {
89        div()
90            .w(width)
91            .h_full()
92            .flex_shrink_0()
93            .flex()
94            .items_center()
95            .justify_center()
96            .border_r_1()
97            .border_color(cx.theme().border)
98            .child(label.clone())
99    }
100
101    /// Render the header cell at the given column index, default to the column name.
102    fn render_th(
103        &mut self,
104        col_ix: usize,
105        window: &mut Window,
106        cx: &mut Context<TableState<Self>>,
107    ) -> impl IntoElement {
108        div()
109            .size_full()
110            .child(self.column(col_ix, cx).name.clone())
111    }
112
113    /// Render the row at the given row and column.
114    ///
115    /// Not include the table head row.
116    fn render_tr(
117        &mut self,
118        row_ix: usize,
119        window: &mut Window,
120        cx: &mut Context<TableState<Self>>,
121    ) -> Stateful<Div> {
122        div().id(("row", row_ix))
123    }
124
125    /// Render the context menu for the row at the given row index.
126    fn context_menu(
127        &mut self,
128        row_ix: usize,
129        menu: PopupMenu,
130        window: &mut Window,
131        cx: &mut Context<TableState<Self>>,
132    ) -> PopupMenu {
133        menu
134    }
135
136    /// Render cell at the given row and column.
137    fn render_td(
138        &mut self,
139        row_ix: usize,
140        col_ix: usize,
141        window: &mut Window,
142        cx: &mut Context<TableState<Self>>,
143    ) -> impl IntoElement;
144
145    /// Return the text a screen reader should read for the cell at the given
146    /// row and column, usually the text [`render_td`](Self::render_td) draws.
147    ///
148    /// Plain text drawn by a cell is not exposed to assistive technology, so
149    /// without this a cell is announced with no content. Returns `None` by
150    /// default.
151    fn cell_label(&self, row_ix: usize, col_ix: usize, cx: &App) -> Option<SharedString> {
152        None
153    }
154
155    /// Move the column at the given `col_ix` so that it ends up at the index `to_ix`.
156    ///
157    /// e.g.: `let col = self.columns.remove(col_ix); self.columns.insert(to_ix, col);`
158    fn move_column(
159        &mut self,
160        col_ix: usize,
161        to_ix: usize,
162        window: &mut Window,
163        cx: &mut Context<TableState<Self>>,
164    ) {
165    }
166
167    /// Return a Element to show when table is empty.
168    fn render_empty(
169        &mut self,
170        window: &mut Window,
171        cx: &mut Context<TableState<Self>>,
172    ) -> impl IntoElement {
173        h_flex()
174            .size_full()
175            .justify_center()
176            .text_color(cx.theme().muted_foreground.opacity(0.6))
177            .child(Icon::new(IconName::Inbox).size_12())
178            .into_any_element()
179    }
180
181    /// Return true to show the loading view.
182    fn loading(&self, cx: &App) -> bool {
183        false
184    }
185
186    /// Return a Element to show when table is loading, default is built-in Skeleton loading view.
187    ///
188    /// The size is the size of the Table.
189    fn render_loading(
190        &mut self,
191        size: Size,
192        window: &mut Window,
193        cx: &mut Context<TableState<Self>>,
194    ) -> impl IntoElement {
195        Loading::new().size(size)
196    }
197
198    /// Return true to enable load more data when scrolling to the bottom.
199    ///
200    /// Default: false
201    fn has_more(&self, cx: &App) -> bool {
202        false
203    }
204
205    /// Returns a threshold value (n rows), of course, when scrolling to the bottom,
206    /// the remaining number of rows triggers `load_more`.
207    /// This should smaller than the total number of first load rows.
208    ///
209    /// Default: 20 rows
210    fn load_more_threshold(&self) -> usize {
211        20
212    }
213
214    /// Load more data when the table is scrolled to the bottom.
215    ///
216    /// This will performed in a background task.
217    ///
218    /// This is always called when the table is near the bottom,
219    /// so you must check if there is more data to load or lock the loading state.
220    fn load_more(&mut self, window: &mut Window, cx: &mut Context<TableState<Self>>) {}
221
222    /// Render the last empty column, default to empty.
223    fn render_last_empty_col(
224        &mut self,
225        window: &mut Window,
226        cx: &mut Context<TableState<Self>>,
227    ) -> impl IntoElement {
228        h_flex().w_3().h_full().flex_shrink_0()
229    }
230
231    /// Called when the visible range of the rows changed.
232    ///
233    /// The range never ends past `rows_count`; a table whose rows drop to none
234    /// reports `0..0`.
235    ///
236    /// NOTE: Make sure this method is fast, because it will be called frequently.
237    ///
238    /// This can used to handle some data update, to only update the visible rows.
239    /// Please ensure that the data is updated in the background task.
240    fn visible_rows_changed(
241        &mut self,
242        visible_range: Range<usize>,
243        window: &mut Window,
244        cx: &mut Context<TableState<Self>>,
245    ) {
246    }
247
248    /// Called when the visible range of the columns changed.
249    ///
250    /// NOTE: Make sure this method is fast, because it will be called frequently.
251    ///
252    /// This can used to handle some data update, to only update the visible rows.
253    /// Please ensure that the data is updated in the background task.
254    fn visible_columns_changed(
255        &mut self,
256        visible_range: Range<usize>,
257        window: &mut Window,
258        cx: &mut Context<TableState<Self>>,
259    ) {
260    }
261
262    /// Get the text representation of a cell for export purposes (e.g., CSV export).
263    ///
264    /// Returns an empty string by default. Implement this method to support export.
265    /// The text should be formatted as it should appear in the exported data.
266    fn cell_text(&self, row_ix: usize, col_ix: usize, cx: &App) -> String {
267        String::new()
268    }
269}