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    /// Render the table head row.
39    fn render_header(
40        &mut self,
41        window: &mut Window,
42        cx: &mut Context<TableState<Self>>,
43    ) -> Stateful<Div> {
44        div().id("header")
45    }
46
47    /// Return the group headers definitions (can be multi-level).
48    ///
49    /// By default, it returns None, meaning no group headers.
50    fn group_headers(&self, cx: &App) -> Option<Vec<Vec<ColumnGroup>>> {
51        None
52    }
53
54    /// Custom render for a group header cell.
55    /// Receives the group label, the logical col_span, and the pixel width.
56    fn render_group_th(
57        &mut self,
58        label: &SharedString,
59        _col_span: usize,
60        width: Pixels,
61        _window: &mut Window,
62        cx: &mut Context<TableState<Self>>,
63    ) -> impl IntoElement {
64        div()
65            .w(width)
66            .h_full()
67            .flex_shrink_0()
68            .flex()
69            .items_center()
70            .justify_center()
71            .border_r_1()
72            .border_color(cx.theme().border)
73            .child(label.clone())
74    }
75
76    /// Render the header cell at the given column index, default to the column name.
77    fn render_th(
78        &mut self,
79        col_ix: usize,
80        window: &mut Window,
81        cx: &mut Context<TableState<Self>>,
82    ) -> impl IntoElement {
83        div()
84            .size_full()
85            .child(self.column(col_ix, cx).name.clone())
86    }
87
88    /// Render the row at the given row and column.
89    ///
90    /// Not include the table head row.
91    fn render_tr(
92        &mut self,
93        row_ix: usize,
94        window: &mut Window,
95        cx: &mut Context<TableState<Self>>,
96    ) -> Stateful<Div> {
97        div().id(("row", row_ix))
98    }
99
100    /// Render the context menu for the row at the given row index.
101    fn context_menu(
102        &mut self,
103        row_ix: usize,
104        menu: PopupMenu,
105        window: &mut Window,
106        cx: &mut Context<TableState<Self>>,
107    ) -> PopupMenu {
108        menu
109    }
110
111    /// Render cell at the given row and column.
112    fn render_td(
113        &mut self,
114        row_ix: usize,
115        col_ix: usize,
116        window: &mut Window,
117        cx: &mut Context<TableState<Self>>,
118    ) -> impl IntoElement;
119
120    /// Move the column at the given `col_ix` so that it ends up at the index `to_ix`.
121    ///
122    /// e.g.: `let col = self.columns.remove(col_ix); self.columns.insert(to_ix, col);`
123    fn move_column(
124        &mut self,
125        col_ix: usize,
126        to_ix: usize,
127        window: &mut Window,
128        cx: &mut Context<TableState<Self>>,
129    ) {
130    }
131
132    /// Return a Element to show when table is empty.
133    fn render_empty(
134        &mut self,
135        window: &mut Window,
136        cx: &mut Context<TableState<Self>>,
137    ) -> impl IntoElement {
138        h_flex()
139            .size_full()
140            .justify_center()
141            .text_color(cx.theme().muted_foreground.opacity(0.6))
142            .child(Icon::new(IconName::Inbox).size_12())
143            .into_any_element()
144    }
145
146    /// Return true to show the loading view.
147    fn loading(&self, cx: &App) -> bool {
148        false
149    }
150
151    /// Return a Element to show when table is loading, default is built-in Skeleton loading view.
152    ///
153    /// The size is the size of the Table.
154    fn render_loading(
155        &mut self,
156        size: Size,
157        window: &mut Window,
158        cx: &mut Context<TableState<Self>>,
159    ) -> impl IntoElement {
160        Loading::new().size(size)
161    }
162
163    /// Return true to enable load more data when scrolling to the bottom.
164    ///
165    /// Default: false
166    fn has_more(&self, cx: &App) -> bool {
167        false
168    }
169
170    /// Returns a threshold value (n rows), of course, when scrolling to the bottom,
171    /// the remaining number of rows triggers `load_more`.
172    /// This should smaller than the total number of first load rows.
173    ///
174    /// Default: 20 rows
175    fn load_more_threshold(&self) -> usize {
176        20
177    }
178
179    /// Load more data when the table is scrolled to the bottom.
180    ///
181    /// This will performed in a background task.
182    ///
183    /// This is always called when the table is near the bottom,
184    /// so you must check if there is more data to load or lock the loading state.
185    fn load_more(&mut self, window: &mut Window, cx: &mut Context<TableState<Self>>) {}
186
187    /// Render the last empty column, default to empty.
188    fn render_last_empty_col(
189        &mut self,
190        window: &mut Window,
191        cx: &mut Context<TableState<Self>>,
192    ) -> impl IntoElement {
193        h_flex().w_3().h_full().flex_shrink_0()
194    }
195
196    /// Called when the visible range of the rows changed.
197    ///
198    /// NOTE: Make sure this method is fast, because it will be called frequently.
199    ///
200    /// This can used to handle some data update, to only update the visible rows.
201    /// Please ensure that the data is updated in the background task.
202    fn visible_rows_changed(
203        &mut self,
204        visible_range: Range<usize>,
205        window: &mut Window,
206        cx: &mut Context<TableState<Self>>,
207    ) {
208    }
209
210    /// Called when the visible range of the columns changed.
211    ///
212    /// NOTE: Make sure this method is fast, because it will be called frequently.
213    ///
214    /// This can used to handle some data update, to only update the visible rows.
215    /// Please ensure that the data is updated in the background task.
216    fn visible_columns_changed(
217        &mut self,
218        visible_range: Range<usize>,
219        window: &mut Window,
220        cx: &mut Context<TableState<Self>>,
221    ) {
222    }
223
224    /// Get the text representation of a cell for export purposes (e.g., CSV export).
225    ///
226    /// Returns an empty string by default. Implement this method to support export.
227    /// The text should be formatted as it should appear in the exported data.
228    fn cell_text(&self, row_ix: usize, col_ix: usize, cx: &App) -> String {
229        String::new()
230    }
231}