1use crate::{
2 ActiveTheme, Sizable, Size,
3 actions::{
4 Cancel, SelectDown, SelectFirst, SelectLast, SelectNextColumn, SelectPageDown,
5 SelectPageUp, SelectPrevColumn, SelectUp,
6 },
7 table::{TableDelegate, TableState},
8};
9use gpui::{
10 App, Edges, Entity, Focusable, InteractiveElement, IntoElement, KeyBinding, ParentElement,
11 RenderOnce, Styled, Window, div, prelude::FluentBuilder,
12};
13use gpui_base::TestSupportExt as _;
14
15const CONTEXT: &'static str = "DataTable";
16pub(super) fn init(cx: &mut App) {
17 cx.bind_keys([
18 KeyBinding::new("escape", Cancel, Some(CONTEXT)),
19 KeyBinding::new("up", SelectUp, Some(CONTEXT)),
20 KeyBinding::new("down", SelectDown, Some(CONTEXT)),
21 KeyBinding::new("left", SelectPrevColumn, Some(CONTEXT)),
22 KeyBinding::new("right", SelectNextColumn, Some(CONTEXT)),
23 KeyBinding::new("home", SelectFirst, Some(CONTEXT)),
24 KeyBinding::new("end", SelectLast, Some(CONTEXT)),
25 KeyBinding::new("pageup", SelectPageUp, Some(CONTEXT)),
26 KeyBinding::new("pagedown", SelectPageDown, Some(CONTEXT)),
27 KeyBinding::new("tab", SelectNextColumn, Some(CONTEXT)),
28 KeyBinding::new("shift-tab", SelectPrevColumn, Some(CONTEXT)),
29 ]);
30}
31
32pub(super) struct TableOptions {
33 pub(super) scrollbar_visible: Edges<bool>,
34 pub(super) stripe: bool,
36 pub(super) bordered: bool,
38 pub(super) size: Size,
40}
41
42impl Default for TableOptions {
43 fn default() -> Self {
44 Self {
45 scrollbar_visible: Edges::all(true),
46 stripe: false,
47 bordered: true,
48 size: Size::default(),
49 }
50 }
51}
52
53#[derive(IntoElement)]
91pub struct DataTable<D: TableDelegate> {
92 state: Entity<TableState<D>>,
93 options: TableOptions,
94}
95
96impl<D> DataTable<D>
97where
98 D: TableDelegate,
99{
100 pub fn new(state: &Entity<TableState<D>>) -> Self {
102 Self {
103 state: state.clone(),
104 options: TableOptions::default(),
105 }
106 }
107
108 pub fn stripe(mut self, stripe: bool) -> Self {
110 self.options.stripe = stripe;
111 self
112 }
113
114 pub fn bordered(mut self, bordered: bool) -> Self {
116 self.options.bordered = bordered;
117 self
118 }
119
120 pub fn scrollbar_visible(mut self, vertical: bool, horizontal: bool) -> Self {
122 self.options.scrollbar_visible = Edges {
123 right: vertical,
124 bottom: horizontal,
125 ..Default::default()
126 };
127 self
128 }
129}
130
131impl<D> Sizable for DataTable<D>
132where
133 D: TableDelegate,
134{
135 fn with_size(mut self, size: impl Into<Size>) -> Self {
136 self.options.size = size.into();
137 self
138 }
139}
140
141impl<D> RenderOnce for DataTable<D>
142where
143 D: TableDelegate,
144{
145 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
146 let bordered = self.options.bordered;
147 let focus_handle = self.state.focus_handle(cx);
148 self.state.update(cx, |state, _| {
149 state.options = self.options;
150 });
151
152 div()
153 .id("table")
154 .test_support()
155 .size_full()
156 .key_context(CONTEXT)
157 .track_focus(&focus_handle)
158 .on_action(window.listener_for(&self.state, TableState::action_cancel))
159 .on_action(window.listener_for(&self.state, TableState::action_select_next))
160 .on_action(window.listener_for(&self.state, TableState::action_select_prev))
161 .on_action(window.listener_for(&self.state, TableState::action_select_next_col))
162 .on_action(window.listener_for(&self.state, TableState::action_select_prev_col))
163 .on_action(window.listener_for(&self.state, TableState::action_select_first_column))
164 .on_action(window.listener_for(&self.state, TableState::action_select_last_column))
165 .on_action(window.listener_for(&self.state, TableState::action_select_page_up))
166 .on_action(window.listener_for(&self.state, TableState::action_select_page_down))
167 .bg(cx.theme().tokens.table)
168 .when(bordered, |this| {
169 this.rounded(cx.theme().radius)
170 .border_1()
171 .border_color(cx.theme().border)
172 })
173 .child(self.state)
174 }
175}