diff --git a/crates/story/src/table_story.rs b/crates/story/src/table_story.rs index e2d70761..8ecabec8 100644 --- a/crates/story/src/table_story.rs +++ b/crates/story/src/table_story.rs @@ -2,8 +2,9 @@ use std::time::{self, Duration}; use fake::{Fake, Faker}; use gpui::{ - div, impl_actions, AnyElement, InteractiveElement, IntoElement, ParentElement, Pixels, Render, - SharedString, Styled, Timer, View, ViewContext, VisualContext as _, WindowContext, + div, impl_actions, px, AnyElement, Edges, InteractiveElement, IntoElement, ParentElement, + Pixels, Render, SharedString, Styled, Timer, View, ViewContext, VisualContext as _, + WindowContext, }; use serde::Deserialize; use ui::{ @@ -16,7 +17,7 @@ use ui::{ popup_menu::PopupMenuExt, prelude::FluentBuilder as _, table::{ColFixed, ColSort, Table, TableDelegate, TableEvent}, - v_flex, Selectable, Size, + v_flex, Selectable, Size, StyleSized as _, }; #[derive(Clone, PartialEq, Eq, Deserialize)] @@ -167,6 +168,7 @@ impl Column { struct StockTableDelegate { stocks: Vec, columns: Vec, + size: Size, loop_selection: bool, col_resize: bool, col_order: bool, @@ -180,6 +182,7 @@ struct StockTableDelegate { impl StockTableDelegate { fn new(size: usize) -> Self { Self { + size: Size::default(), stocks: random_stocks(size), columns: vec![ Column::new("id", "ID", None), @@ -253,7 +256,10 @@ impl StockTableDelegate { } fn render_value_cell(&self, val: f64) -> AnyElement { - let this = div().child(format!("{:.3}", val)); + let this = div() + .h_full() + .table_cell_size(self.size) + .child(format!("{:.3}", val)); // Val is a 0.0 .. n.0 // 30% to red, 30% to green, others to default let right_num = ((val - val.floor()) * 1000.).floor() as i32; @@ -296,6 +302,14 @@ impl TableDelegate for StockTableDelegate { } } + fn col_padding(&self, col_ix: usize) -> Option> { + if col_ix >= 3 && col_ix <= 10 { + Some(Edges::all(px(0.))) + } else { + None + } + } + fn col_fixed(&self, col_ix: usize) -> Option { if !self.fixed_cols { return None; @@ -316,6 +330,16 @@ impl TableDelegate for StockTableDelegate { return self.col_selection; } + fn render_th(&self, col_ix: usize, _cx: &mut ViewContext>) -> impl IntoElement { + let th = div().child(self.col_name(col_ix)); + + if col_ix >= 3 && col_ix <= 10 { + th.table_cell_size(self.size) + } else { + th + } + } + fn render_td( &self, row_ix: usize, @@ -617,6 +641,7 @@ impl TableStory { self.size = a.0; self.table.update(cx, |table, cx| { table.set_size(a.0, cx); + table.delegate_mut().size = a.0; }); } diff --git a/crates/ui/src/styled.rs b/crates/ui/src/styled.rs index 9b16497a..d350bdf5 100644 --- a/crates/ui/src/styled.rs +++ b/crates/ui/src/styled.rs @@ -5,7 +5,8 @@ use crate::{ theme::ActiveTheme, }; use gpui::{ - div, px, Axis, Div, Element, ElementId, EntityId, FocusHandle, Pixels, Styled, WindowContext, + div, px, Axis, Div, Edges, Element, ElementId, EntityId, FocusHandle, Pixels, Styled, + WindowContext, }; use serde::{Deserialize, Serialize}; @@ -146,6 +147,47 @@ pub enum Size { Large, } +impl Size { + /// Returns the height for table row. + pub fn table_row_height(&self) -> Pixels { + match self { + Size::XSmall => px(26.), + Size::Small => px(30.), + Size::Large => px(40.), + _ => px(32.), + } + } + /// Returns the padding for a table cell. + pub fn table_cell_padding(&self) -> Edges { + match self { + Size::XSmall => Edges { + top: px(2.), + bottom: px(2.), + left: px(4.), + right: px(4.), + }, + Size::Small => Edges { + top: px(3.), + bottom: px(3.), + left: px(6.), + right: px(6.), + }, + Size::Large => Edges { + top: px(8.), + bottom: px(8.), + left: px(12.), + right: px(12.), + }, + _ => Edges { + top: px(4.), + bottom: px(4.), + left: px(8.), + right: px(8.), + }, + } + } +} + impl From for Size { fn from(size: Pixels) -> Self { Size::Size(size) @@ -203,6 +245,8 @@ pub trait StyleSized { fn list_py(self, size: Size) -> Self; /// Apply size with the given `Size`. fn size_with(self, size: Size) -> Self; + /// Apply the table cell size (Font size, padding) with the given `Size`. + fn table_cell_size(self, size: Size) -> Self; } impl StyleSized for T { @@ -290,6 +334,19 @@ impl StyleSized for T { Size::Size(size) => self.size(size), } } + + fn table_cell_size(self, size: Size) -> Self { + let padding = size.table_cell_padding(); + match size { + Size::XSmall => self.text_sm(), + Size::Small => self.text_sm(), + _ => self, + } + .pl(padding.left) + .pr(padding.right) + .pt(padding.top) + .pb(padding.bottom) + } } pub trait AxisExt { diff --git a/crates/ui/src/table.rs b/crates/ui/src/table.rs index 09adc1f0..0fa7fb30 100644 --- a/crates/ui/src/table.rs +++ b/crates/ui/src/table.rs @@ -4,14 +4,14 @@ use crate::{ h_flex, scroll::{ScrollableAxis, ScrollableMask, Scrollbar, ScrollbarState}, theme::ActiveTheme, - v_flex, Icon, IconName, Sizable, Size, + v_flex, Icon, IconName, Sizable, Size, StyleSized as _, }; use gpui::{ actions, canvas, div, prelude::FluentBuilder, px, uniform_list, AppContext, Bounds, Div, - DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, InteractiveElement, - IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, Render, ScrollHandle, - SharedString, StatefulInteractiveElement as _, Styled, UniformListScrollHandle, ViewContext, - VisualContext as _, WindowContext, + DragMoveEvent, Edges, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, + InteractiveElement, IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, Render, + ScrollHandle, SharedString, StatefulInteractiveElement as _, Styled, UniformListScrollHandle, + ViewContext, VisualContext as _, WindowContext, }; actions!( @@ -47,6 +47,7 @@ struct ColGroup { bounds: Bounds, sort: Option, fixed: Option, + padding: Option>, } #[derive(Clone)] @@ -169,6 +170,13 @@ pub trait TableDelegate: Sized + 'static { None } + /// Return the padding of the column at the given index to override the default padding. + /// + /// Return None, use the default padding. + fn col_padding(&self, col_ix: usize) -> Option> { + None + } + /// Perform sort on the column at the given index. fn perform_sort(&mut self, col_ix: usize, sort: ColSort, cx: &mut ViewContext>) {} @@ -302,16 +310,6 @@ where cx.notify(); } - /// Return the height of the table head. - fn table_head_height(&self) -> Pixels { - match self.size { - Size::Large => px(48.), - Size::Small => px(30.), - Size::XSmall => px(26.), - _ => px(32.), - } - } - /// When we update columns or rows, we need to refresh the table. pub fn refresh(&mut self, cx: &mut ViewContext) { self.prepare_col_groups(cx); @@ -321,6 +319,7 @@ where self.col_groups = (0..self.delegate.cols_count()) .map(|col_ix| ColGroup { width: self.delegate.col_width(col_ix), + padding: self.delegate.col_padding(col_ix), bounds: Bounds::default(), sort: self.delegate.col_sort(col_ix), fixed: self.delegate.col_fixed(col_ix), @@ -560,29 +559,36 @@ where fn render_cell(&self, col_ix: usize, _cx: &mut ViewContext) -> Div { let col_width = self.col_groups[col_ix].width; + let col_padding = self.col_groups[col_ix].padding; div() .when_some(col_width, |this, width| this.w(width)) + .h_full() .flex_shrink_0() .overflow_hidden() .whitespace_nowrap() - .map(|this| match self.size { - Size::XSmall => this.text_sm().py_0p5().px_1(), - Size::Small => this.text_sm().py(px(3.)).px_1p5(), - Size::Large => this.py_2().px_3(), - _ => this.py_1().px_2(), + .table_cell_size(self.size) + .map(|this| match col_padding { + Some(padding) => this + .pl(padding.left) + .pr(padding.right) + .pt(padding.top) + .pb(padding.bottom), + None => this, }) } /// Show Column selection style, when the column is selected and the selection state is Column. fn render_col_wrap(&self, col_ix: usize, cx: &mut ViewContext) -> Div { + let el = h_flex().h_full(); + if self.delegate().can_select_col(col_ix) && self.selected_col == Some(col_ix) && self.selection_state == SelectionState::Column { - h_flex().bg(cx.theme().table_active) + el.bg(cx.theme().table_active) } else { - h_flex() + el } } @@ -592,7 +598,7 @@ where Some( div() .absolute() - .top(self.table_head_height()) + .top(self.size.table_row_height()) .left_0() .right_0() .bottom_0() @@ -765,6 +771,13 @@ where .justify_between() .items_center() .child(self.delegate.render_th(col_ix, cx)) + .when_some(self.delegate().col_padding(col_ix), |this, padding| { + // Leave right space for the sort icon, if this column have custom padding + let offset_pr = + self.size.table_cell_padding().right - padding.right; + + this.pr(offset_pr.max(px(0.))) + }) .children(self.render_sort_icon(col_ix, cx)), ) .when(self.delegate.can_move_col(col_ix), |this| { @@ -827,7 +840,7 @@ where h_flex() .w_full() - .h(self.table_head_height()) + .h(self.size.table_row_height()) .flex_shrink_0() .border_b_1() .border_color(cx.theme().border) @@ -908,12 +921,7 @@ where .map(|this| vec![this]) } }) - .map(|this| match self.size { - Size::Large => this.h_10(), - Size::Small => this.h(px(30.)), - Size::XSmall => this.h(px(26.)), - _ => this.h_8(), - }) + .h(self.size.table_row_height()) .h_full() .flex_1(), ) @@ -936,6 +944,7 @@ where .render_tr(row_ix, cx) .id(("table-row", row_ix)) .w_full() + .h(self.size.table_row_height()) .when(row_ix > 0, |this| { this.border_t_1().border_color(cx.theme().table_row_border) }) @@ -951,6 +960,7 @@ where // Left fixed columns Some( h_flex() + .h_full() .border_r_1() .border_color(cx.theme().table_row_border) .children((0..left_cols_count).map(|col_ix| { @@ -966,6 +976,7 @@ where .child( h_flex() .flex_1() + .h_full() .overflow_hidden() .children((left_cols_count..cols_count).map(|col_ix| { self