use std::rc::Rc;
use gpui::{App, Context, Entity, IntoElement, KeyBinding, Pixels, RenderOnce, Window};
use crate::{
Sizable, Size, TableDelegate, TableState,
actions::{
Cancel, SelectDown, SelectFirst, SelectLast, SelectNextColumn, SelectPageDown, SelectPageUp,
SelectPrevColumn, SelectUp,
},
};
const CONTEXT: &str = "Table";
pub(crate) fn init(cx: &mut App) {
cx.bind_keys([
KeyBinding::new("escape", Cancel, Some(CONTEXT)),
KeyBinding::new("up", SelectUp, Some(CONTEXT)),
KeyBinding::new("down", SelectDown, Some(CONTEXT)),
KeyBinding::new("left", SelectPrevColumn, Some(CONTEXT)),
KeyBinding::new("right", SelectNextColumn, Some(CONTEXT)),
KeyBinding::new("home", SelectFirst, Some(CONTEXT)),
KeyBinding::new("end", SelectLast, Some(CONTEXT)),
KeyBinding::new("pageup", SelectPageUp, Some(CONTEXT)),
KeyBinding::new("pagedown", SelectPageDown, Some(CONTEXT)),
KeyBinding::new("tab", SelectNextColumn, Some(CONTEXT)),
KeyBinding::new("shift-tab", SelectPrevColumn, Some(CONTEXT)),
]);
}
type BlankContextMenuBuilder<D> =
dyn Fn(crate::PopupMenu, &mut Window, &mut Context<TableState<D>>) -> crate::PopupMenu;
#[derive(IntoElement)]
pub struct Table<D: TableDelegate> {
state: Entity<TableState<D>>,
stripe: bool,
bordered: bool,
size: Size,
auto_detect_col_width: bool,
scrollbar_visible_vertical: bool,
scrollbar_visible_horizontal: bool,
bottom_gap: Option<Pixels>,
blank_context_menu_builder: Option<Rc<BlankContextMenuBuilder<D>>>,
}
impl<D> Table<D>
where
D: TableDelegate,
{
pub fn new(state: &Entity<TableState<D>>) -> Self {
Self {
state: state.clone(),
stripe: false,
bordered: true,
size: Size::default(),
auto_detect_col_width: false,
scrollbar_visible_vertical: true,
scrollbar_visible_horizontal: true,
bottom_gap: None,
blank_context_menu_builder: None,
}
}
pub fn stripe(mut self, stripe: bool) -> Self {
self.stripe = stripe;
self
}
pub fn bordered(mut self, bordered: bool) -> Self {
self.bordered = bordered;
self
}
pub fn scrollbar_visible(mut self, vertical: bool, horizontal: bool) -> Self {
self.scrollbar_visible_vertical = vertical;
self.scrollbar_visible_horizontal = horizontal;
self
}
pub fn bottom_gap(mut self, gap: impl Into<Pixels>) -> Self {
self.bottom_gap = Some(gap.into());
self
}
pub fn auto_detect_col_width(mut self, auto_detect_col_width: bool) -> Self {
self.auto_detect_col_width = auto_detect_col_width;
self
}
pub fn blank_context_menu<F>(mut self, builder: F) -> Self
where
F: Fn(crate::PopupMenu, &mut Window, &mut Context<TableState<D>>) -> crate::PopupMenu + 'static,
{
self.blank_context_menu_builder = Some(Rc::new(builder));
self
}
}
impl<D> Sizable for Table<D>
where
D: TableDelegate,
{
fn with_size(mut self, size: impl Into<Size>) -> Self {
self.size = size.into();
self
}
}
impl<D> RenderOnce for Table<D>
where
D: TableDelegate,
{
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
self.state.update(cx, |state, cx| {
let should_refresh_col_groups = state.options.auto_detect_col_width
!= self.auto_detect_col_width
|| (self.auto_detect_col_width && state.options.size != self.size);
state.options.bordered = self.bordered;
state.options.stripe = self.stripe;
state.options.size = self.size;
state.options.auto_detect_col_width = self.auto_detect_col_width;
state.options.scrollbar_visible = gpui::Edges {
right: self.scrollbar_visible_vertical,
bottom: self.scrollbar_visible_horizontal,
..Default::default()
};
state.options.bottom_gap = self.bottom_gap;
state.blank_context_menu_builder = self.blank_context_menu_builder;
if should_refresh_col_groups {
state.refresh(cx);
}
});
self.state
}
}