From ce10a0340c36a4d795cf740574965157aed2d3b3 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 3 Oct 2024 10:14:44 +0800 Subject: [PATCH] table: Set fixed height to table head to fix some case table head disappear bug. (#300) Closes #299 - Improved sort icon to better color. - Improved table story. - Disable `horizontal_scroll_handle.scroll_to_item` on select_col, there have a bug need to fix. --- crates/story/src/table_story.rs | 106 ++++++++++++++++++++++---------- crates/ui/src/styled.rs | 3 +- crates/ui/src/table.rs | 78 +++++++++++++++-------- crates/ui/src/theme.rs | 2 + 4 files changed, 128 insertions(+), 61 deletions(-) diff --git a/crates/story/src/table_story.rs b/crates/story/src/table_story.rs index e2bb3f61..96a97180 100644 --- a/crates/story/src/table_story.rs +++ b/crates/story/src/table_story.rs @@ -2,9 +2,11 @@ use std::time::{self, Duration}; use fake::{Fake, Faker}; use gpui::{ - div, AnyElement, ClickEvent, IntoElement, ParentElement, Pixels, Render, SharedString, Styled, - Timer, View, ViewContext, VisualContext as _, WindowContext, + div, impl_actions, AnyElement, ClickEvent, InteractiveElement, IntoElement, ParentElement, + Pixels, Render, SharedString, Styled, Timer, View, ViewContext, VisualContext as _, + WindowContext, }; +use serde::Deserialize; use ui::{ button::{Button, ButtonStyled}, checkbox::Checkbox, @@ -12,11 +14,17 @@ use ui::{ indicator::Indicator, input::{InputEvent, TextInput}, label::Label, + popup_menu::PopupMenuExt, prelude::FluentBuilder as _, table::{ColFixed, ColSort, Table, TableDelegate, TableEvent}, v_flex, Selectable, Sizable, Size, }; +#[derive(Clone, PartialEq, Eq, Deserialize)] +struct ChangeSize(Size); + +impl_actions!(table_story, [ChangeSize]); + #[derive(Clone, Debug, Default)] struct Stock { id: usize, @@ -166,6 +174,7 @@ struct StockTableDelegate { col_sort: bool, col_selection: bool, loading: bool, + fixed_cols: bool, is_eof: bool, } @@ -232,6 +241,7 @@ impl StockTableDelegate { col_order: true, col_sort: true, col_selection: true, + fixed_cols: false, loading: false, is_eof: false, } @@ -250,9 +260,10 @@ impl StockTableDelegate { let right_num = ((val - val.floor()) * 1000.).floor() as i32; let this = if right_num % 3 == 0 { - this.text_color(ui::red_600()).bg(ui::red_50()) + this.text_color(ui::red_600()).bg(ui::red_50().opacity(0.6)) } else if right_num % 3 == 1 { - this.text_color(ui::green_600()).bg(ui::green_50()) + this.text_color(ui::green_600()) + .bg(ui::green_50().opacity(0.6)) } else { this }; @@ -287,6 +298,10 @@ impl TableDelegate for StockTableDelegate { } fn col_fixed(&self, col_ix: usize) -> Option { + if !self.fixed_cols { + return None; + } + if col_ix < 4 { Some(ColFixed::Left) } else { @@ -548,40 +563,35 @@ impl TableStory { } fn toggle_loop_selection(&mut self, checked: &bool, cx: &mut ViewContext) { - let table = self.table.clone(); - table.update(cx, |table, cx| { + self.table.update(cx, |table, cx| { table.delegate_mut().loop_selection = *checked; cx.notify(); }); } fn toggle_col_resize(&mut self, checked: &bool, cx: &mut ViewContext) { - let table = self.table.clone(); - table.update(cx, |table, cx| { + self.table.update(cx, |table, cx| { table.delegate_mut().col_resize = *checked; cx.notify(); }); } fn toggle_col_order(&mut self, checked: &bool, cx: &mut ViewContext) { - let table = self.table.clone(); - table.update(cx, |table, cx| { + self.table.update(cx, |table, cx| { table.delegate_mut().col_order = *checked; cx.notify(); }); } fn toggle_col_sort(&mut self, checked: &bool, cx: &mut ViewContext) { - let table = self.table.clone(); - table.update(cx, |table, cx| { + self.table.update(cx, |table, cx| { table.delegate_mut().col_sort = *checked; cx.notify(); }); } fn toggle_col_selection(&mut self, checked: &bool, cx: &mut ViewContext) { - let table = self.table.clone(); - table.update(cx, |table, cx| { + self.table.update(cx, |table, cx| { table.delegate_mut().col_selection = *checked; cx.notify(); }); @@ -590,24 +600,24 @@ impl TableStory { fn toggle_stripe(&mut self, checked: &bool, cx: &mut ViewContext) { self.stripe = *checked; let stripe = self.stripe; - let table = self.table.clone(); - table.update(cx, |table, cx| { + self.table.update(cx, |table, cx| { table.set_stripe(stripe, cx); cx.notify(); }); } - fn toggle_size(&mut self, _: &ClickEvent, cx: &mut ViewContext) { - self.size = match self.size { - Size::XSmall => Size::Small, - Size::Small => Size::Medium, - Size::Medium => Size::Large, - Size::Large => Size::XSmall, - _ => Size::default(), - }; - + fn toggle_fixed_cols(&mut self, checked: &bool, cx: &mut ViewContext) { self.table.update(cx, |table, cx| { - table.set_size(self.size, cx); + table.delegate_mut().fixed_cols = *checked; + table.refresh(cx); + cx.notify(); + }); + } + + fn on_change_size(&mut self, a: &ChangeSize, cx: &mut ViewContext) { + self.size = a.0; + self.table.update(cx, |table, cx| { + table.set_size(a.0, cx); }); } @@ -635,14 +645,46 @@ impl TableStory { impl Render for TableStory { fn render(&mut self, cx: &mut ViewContext) -> impl gpui::IntoElement { let delegate = self.table.read(cx).delegate(); + let size = self.size; v_flex() + .on_action(cx.listener(Self::on_change_size)) .size_full() + .text_sm() .gap_2() .child( h_flex() .items_center() - .gap_2() + .gap_3() + .flex_wrap() + .child( + Button::new("size") + .compact() + .outline() + .label(format!("size: {:?}", self.size)) + .popup_menu(move |menu, cx| { + menu.menu_with_check( + "Large", + size == Size::Large, + Box::new(ChangeSize(Size::Large)), + ) + .menu_with_check( + "Medium", + size == Size::Medium, + Box::new(ChangeSize(Size::Medium)), + ) + .menu_with_check( + "Small", + size == Size::Small, + Box::new(ChangeSize(Size::Small)), + ) + .menu_with_check( + "XSmall", + size == Size::XSmall, + Box::new(ChangeSize(Size::XSmall)), + ) + }), + ) .child( Checkbox::new("loop-selection") .label("Loop Selection") @@ -680,12 +722,10 @@ impl Render for TableStory { .on_click(cx.listener(Self::toggle_stripe)), ) .child( - Button::new("size") - .small() - .compact() - .outline() - .label(format!("size: {:?}", self.size)) - .on_click(cx.listener(Self::toggle_size)), + Checkbox::new("fixed-cols") + .label("Fixed Columns") + .selected(delegate.fixed_cols) + .on_click(cx.listener(Self::toggle_fixed_cols)), ) .child( Checkbox::new("refresh-data") diff --git a/crates/ui/src/styled.rs b/crates/ui/src/styled.rs index e0f513ee..90c9a624 100644 --- a/crates/ui/src/styled.rs +++ b/crates/ui/src/styled.rs @@ -5,6 +5,7 @@ use crate::{ theme::ActiveTheme, }; use gpui::{div, px, Axis, Div, Element, EntityId, FocusHandle, Pixels, Styled, WindowContext}; +use serde::{Deserialize, Serialize}; /// Returns a `Div` as horizontal flex layout. pub fn h_flex() -> Div { @@ -133,7 +134,7 @@ pub trait StyledExt: Styled + Sized { impl StyledExt for E {} /// A size for elements. -#[derive(Clone, Default, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Default, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)] pub enum Size { Size(Pixels), XSmall, diff --git a/crates/ui/src/table.rs b/crates/ui/src/table.rs index ad33816c..77682680 100644 --- a/crates/ui/src/table.rs +++ b/crates/ui/src/table.rs @@ -336,9 +336,9 @@ where self.selected_col = Some(col_ix); if let Some(col_ix) = self.selected_col { // TODO: Fix scroll to selected col, this was not working after fixed col. - if self.col_groups[col_ix].fixed.is_none() { - self.horizontal_scroll_handle.scroll_to_item(col_ix); - } + // if self.col_groups[col_ix].fixed.is_none() { + // self.horizontal_scroll_handle.scroll_to_item(col_ix); + // } } cx.emit(TableEvent::SelectCol(col_ix)); cx.notify(); @@ -426,8 +426,8 @@ where .whitespace_nowrap() .map(|this| match self.size { Size::XSmall => this.text_sm().py_0p5().px_1(), - Size::Small => this.text_sm().py_1().px_1p5(), - Size::Large => this.py_1p5().px_3(), + Size::Small => this.text_sm().py(px(3.)).px_1p5(), + Size::Large => this.py_2().px_3(), _ => this.py_1().px_2(), }) } @@ -658,10 +658,10 @@ where let sort = sort.unwrap(); - let icon = match sort { - ColSort::Ascending => IconName::SortAscending, - ColSort::Descending => IconName::SortDescending, - ColSort::Default => IconName::ChevronsUpDown, + let (icon, is_on) = match sort { + ColSort::Ascending => (IconName::SortAscending, true), + ColSort::Descending => (IconName::SortDescending, true), + ColSort::Default => (IconName::ChevronsUpDown, false), }; Some( @@ -671,8 +671,12 @@ where .ml_2() .p(px(2.)) .rounded_sm() - .hover(|this| this.bg(cx.theme().secondary)) - .active(|this| this.bg(cx.theme().secondary_active)) + .map(|this| match is_on { + true => this, + false => this.opacity(0.5), + }) + .hover(|this| this.bg(cx.theme().secondary).opacity(7.)) + .active(|this| this.bg(cx.theme().secondary_active).opacity(1.)) .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation()) .on_click(cx.listener(move |table, _, cx| table.perform_sort(col_ix, cx))) .child( @@ -787,31 +791,45 @@ where ) -> impl IntoElement { let view = cx.view().clone(); let horizontal_scroll_handle = self.horizontal_scroll_handle.clone(); + let fixed_cols_count = self + .col_groups + .iter() + .filter(|col| col.fixed.is_some()) + .count(); h_flex() .w_full() + .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(), + }) .flex_shrink_0() .border_b_1() .border_color(cx.theme().border) - .child( + .text_color(cx.theme().table_head_foreground) + .when(fixed_cols_count > 0, |this| { // Render left fixed columns - h_flex() - .id("table-head-fixed-left") - .h_full() - .bg(cx.theme().table_head) - .border_r_1() - .border_color(cx.theme().border) - .children( - self.col_groups - .iter() - .filter(|col| col.fixed == Some(ColFixed::Left)) - .enumerate() - .map(|(col_ix, _)| self.render_th(col_ix, cx)), - ), - ) + this.child( + h_flex() + .id("table-head-fixed-left") + .h_full() + .bg(cx.theme().table_head) + .border_r_1() + .border_color(cx.theme().border) + .children( + self.col_groups + .iter() + .filter(|col| col.fixed == Some(ColFixed::Left)) + .enumerate() + .map(|(col_ix, _)| self.render_th(col_ix, cx)), + ), + ) + }) .child( // Render other normal columns - uniform_list(view.clone(), "table-uniform-list-head", 1, { + uniform_list(view.clone(), "table-head-uniform-list", 1, { let horizontal_scroll_handle = horizontal_scroll_handle.clone(); let view = view.clone(); move |table, _, cx| { @@ -856,6 +874,12 @@ 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_full() .flex_1(), ) diff --git a/crates/ui/src/theme.rs b/crates/ui/src/theme.rs index 5653377c..323f313e 100644 --- a/crates/ui/src/theme.rs +++ b/crates/ui/src/theme.rs @@ -315,6 +315,7 @@ pub struct Theme { pub table: Hsla, pub table_even: Hsla, pub table_head: Hsla, + pub table_head_foreground: Hsla, pub table_row_border: Hsla, pub table_active: Hsla, pub table_hover: Hsla, @@ -398,6 +399,7 @@ impl From for Theme { table_active: colors.list_active, table_hover: colors.list_active.opacity(0.8), table_row_border: colors.border.opacity(0.5), + table_head_foreground: colors.foreground.opacity(0.7), link: colors.link, link_hover: colors.link.lighten(0.2), link_active: colors.link.darken(0.2),