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    /// Move the column at the given `col_ix` so that it ends up at the index `to_ix`.
146    ///
147    /// e.g.: `let col = self.columns.remove(col_ix); self.columns.insert(to_ix, col);`
148    fn move_column(
149        &mut self,
150        col_ix: usize,
151        to_ix: usize,
152        window: &mut Window,
153        cx: &mut Context<TableState<Self>>,
154    ) {
155    }
156
157    /// Return a Element to show when table is empty.
158    fn render_empty(
159        &mut self,
160        window: &mut Window,
161        cx: &mut Context<TableState<Self>>,
162    ) -> impl IntoElement {
163        h_flex()
164            .size_full()
165            .justify_center()
166            .text_color(cx.theme().muted_foreground.opacity(0.6))
167            .child(Icon::new(IconName::Inbox).size_12())
168            .into_any_element()
169    }
170
171    /// Return true to show the loading view.
172    fn loading(&self, cx: &App) -> bool {
173        false
174    }
175
176    /// Return a Element to show when table is loading, default is built-in Skeleton loading view.
177    ///
178    /// The size is the size of the Table.
179    fn render_loading(
180        &mut self,
181        size: Size,
182        window: &mut Window,
183        cx: &mut Context<TableState<Self>>,
184    ) -> impl IntoElement {
185        Loading::new().size(size)
186    }
187
188    /// Return true to enable load more data when scrolling to the bottom.
189    ///
190    /// Default: false
191    fn has_more(&self, cx: &App) -> bool {
192        false
193    }
194
195    /// Returns a threshold value (n rows), of course, when scrolling to the bottom,
196    /// the remaining number of rows triggers `load_more`.
197    /// This should smaller than the total number of first load rows.
198    ///
199    /// Default: 20 rows
200    fn load_more_threshold(&self) -> usize {
201        20
202    }
203
204    /// Load more data when the table is scrolled to the bottom.
205    ///
206    /// This will performed in a background task.
207    ///
208    /// This is always called when the table is near the bottom,
209    /// so you must check if there is more data to load or lock the loading state.
210    fn load_more(&mut self, window: &mut Window, cx: &mut Context<TableState<Self>>) {}
211
212    /// Render the last empty column, default to empty.
213    fn render_last_empty_col(
214        &mut self,
215        window: &mut Window,
216        cx: &mut Context<TableState<Self>>,
217    ) -> impl IntoElement {
218        h_flex().w_3().h_full().flex_shrink_0()
219    }
220
221    /// Called when the visible range of the rows changed.
222    ///
223    /// The range never ends past `rows_count`; a table whose rows drop to none
224    /// reports `0..0`.
225    ///
226    /// NOTE: Make sure this method is fast, because it will be called frequently.
227    ///
228    /// This can used to handle some data update, to only update the visible rows.
229    /// Please ensure that the data is updated in the background task.
230    fn visible_rows_changed(
231        &mut self,
232        visible_range: Range<usize>,
233        window: &mut Window,
234        cx: &mut Context<TableState<Self>>,
235    ) {
236    }
237
238    /// Called when the visible range of the columns changed.
239    ///
240    /// NOTE: Make sure this method is fast, because it will be called frequently.
241    ///
242    /// This can used to handle some data update, to only update the visible rows.
243    /// Please ensure that the data is updated in the background task.
244    fn visible_columns_changed(
245        &mut self,
246        visible_range: Range<usize>,
247        window: &mut Window,
248        cx: &mut Context<TableState<Self>>,
249    ) {
250    }
251
252    /// Get the text representation of a cell for export purposes (e.g., CSV export).
253    ///
254    /// Returns an empty string by default. Implement this method to support export.
255    /// The text should be formatted as it should appear in the exported data.
256    fn cell_text(&self, row_ix: usize, col_ix: usize, cx: &App) -> String {
257        String::new()
258    }
259}