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.
This commit is contained in:
Jason Lee 2024-10-03 10:14:44 +08:00 committed by GitHub
parent 494e6d15f3
commit ce10a0340c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 128 additions and 61 deletions

View file

@ -2,9 +2,11 @@ use std::time::{self, Duration};
use fake::{Fake, Faker}; use fake::{Fake, Faker};
use gpui::{ use gpui::{
div, AnyElement, ClickEvent, IntoElement, ParentElement, Pixels, Render, SharedString, Styled, div, impl_actions, AnyElement, ClickEvent, InteractiveElement, IntoElement, ParentElement,
Timer, View, ViewContext, VisualContext as _, WindowContext, Pixels, Render, SharedString, Styled, Timer, View, ViewContext, VisualContext as _,
WindowContext,
}; };
use serde::Deserialize;
use ui::{ use ui::{
button::{Button, ButtonStyled}, button::{Button, ButtonStyled},
checkbox::Checkbox, checkbox::Checkbox,
@ -12,11 +14,17 @@ use ui::{
indicator::Indicator, indicator::Indicator,
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
label::Label, label::Label,
popup_menu::PopupMenuExt,
prelude::FluentBuilder as _, prelude::FluentBuilder as _,
table::{ColFixed, ColSort, Table, TableDelegate, TableEvent}, table::{ColFixed, ColSort, Table, TableDelegate, TableEvent},
v_flex, Selectable, Sizable, Size, v_flex, Selectable, Sizable, Size,
}; };
#[derive(Clone, PartialEq, Eq, Deserialize)]
struct ChangeSize(Size);
impl_actions!(table_story, [ChangeSize]);
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct Stock { struct Stock {
id: usize, id: usize,
@ -166,6 +174,7 @@ struct StockTableDelegate {
col_sort: bool, col_sort: bool,
col_selection: bool, col_selection: bool,
loading: bool, loading: bool,
fixed_cols: bool,
is_eof: bool, is_eof: bool,
} }
@ -232,6 +241,7 @@ impl StockTableDelegate {
col_order: true, col_order: true,
col_sort: true, col_sort: true,
col_selection: true, col_selection: true,
fixed_cols: false,
loading: false, loading: false,
is_eof: false, is_eof: false,
} }
@ -250,9 +260,10 @@ impl StockTableDelegate {
let right_num = ((val - val.floor()) * 1000.).floor() as i32; let right_num = ((val - val.floor()) * 1000.).floor() as i32;
let this = if right_num % 3 == 0 { 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 { } 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 { } else {
this this
}; };
@ -287,6 +298,10 @@ impl TableDelegate for StockTableDelegate {
} }
fn col_fixed(&self, col_ix: usize) -> Option<ui::table::ColFixed> { fn col_fixed(&self, col_ix: usize) -> Option<ui::table::ColFixed> {
if !self.fixed_cols {
return None;
}
if col_ix < 4 { if col_ix < 4 {
Some(ColFixed::Left) Some(ColFixed::Left)
} else { } else {
@ -548,40 +563,35 @@ impl TableStory {
} }
fn toggle_loop_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) { fn toggle_loop_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
let table = self.table.clone(); self.table.update(cx, |table, cx| {
table.update(cx, |table, cx| {
table.delegate_mut().loop_selection = *checked; table.delegate_mut().loop_selection = *checked;
cx.notify(); cx.notify();
}); });
} }
fn toggle_col_resize(&mut self, checked: &bool, cx: &mut ViewContext<Self>) { fn toggle_col_resize(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
let table = self.table.clone(); self.table.update(cx, |table, cx| {
table.update(cx, |table, cx| {
table.delegate_mut().col_resize = *checked; table.delegate_mut().col_resize = *checked;
cx.notify(); cx.notify();
}); });
} }
fn toggle_col_order(&mut self, checked: &bool, cx: &mut ViewContext<Self>) { fn toggle_col_order(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
let table = self.table.clone(); self.table.update(cx, |table, cx| {
table.update(cx, |table, cx| {
table.delegate_mut().col_order = *checked; table.delegate_mut().col_order = *checked;
cx.notify(); cx.notify();
}); });
} }
fn toggle_col_sort(&mut self, checked: &bool, cx: &mut ViewContext<Self>) { fn toggle_col_sort(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
let table = self.table.clone(); self.table.update(cx, |table, cx| {
table.update(cx, |table, cx| {
table.delegate_mut().col_sort = *checked; table.delegate_mut().col_sort = *checked;
cx.notify(); cx.notify();
}); });
} }
fn toggle_col_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) { fn toggle_col_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
let table = self.table.clone(); self.table.update(cx, |table, cx| {
table.update(cx, |table, cx| {
table.delegate_mut().col_selection = *checked; table.delegate_mut().col_selection = *checked;
cx.notify(); cx.notify();
}); });
@ -590,24 +600,24 @@ impl TableStory {
fn toggle_stripe(&mut self, checked: &bool, cx: &mut ViewContext<Self>) { fn toggle_stripe(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
self.stripe = *checked; self.stripe = *checked;
let stripe = self.stripe; let stripe = self.stripe;
let table = self.table.clone(); self.table.update(cx, |table, cx| {
table.update(cx, |table, cx| {
table.set_stripe(stripe, cx); table.set_stripe(stripe, cx);
cx.notify(); cx.notify();
}); });
} }
fn toggle_size(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) { fn toggle_fixed_cols(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
self.size = match self.size {
Size::XSmall => Size::Small,
Size::Small => Size::Medium,
Size::Medium => Size::Large,
Size::Large => Size::XSmall,
_ => Size::default(),
};
self.table.update(cx, |table, cx| { 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>) {
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 { impl Render for TableStory {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl gpui::IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl gpui::IntoElement {
let delegate = self.table.read(cx).delegate(); let delegate = self.table.read(cx).delegate();
let size = self.size;
v_flex() v_flex()
.on_action(cx.listener(Self::on_change_size))
.size_full() .size_full()
.text_sm()
.gap_2() .gap_2()
.child( .child(
h_flex() h_flex()
.items_center() .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( .child(
Checkbox::new("loop-selection") Checkbox::new("loop-selection")
.label("Loop Selection") .label("Loop Selection")
@ -680,12 +722,10 @@ impl Render for TableStory {
.on_click(cx.listener(Self::toggle_stripe)), .on_click(cx.listener(Self::toggle_stripe)),
) )
.child( .child(
Button::new("size") Checkbox::new("fixed-cols")
.small() .label("Fixed Columns")
.compact() .selected(delegate.fixed_cols)
.outline() .on_click(cx.listener(Self::toggle_fixed_cols)),
.label(format!("size: {:?}", self.size))
.on_click(cx.listener(Self::toggle_size)),
) )
.child( .child(
Checkbox::new("refresh-data") Checkbox::new("refresh-data")

View file

@ -5,6 +5,7 @@ use crate::{
theme::ActiveTheme, theme::ActiveTheme,
}; };
use gpui::{div, px, Axis, Div, Element, EntityId, FocusHandle, Pixels, Styled, WindowContext}; use gpui::{div, px, Axis, Div, Element, EntityId, FocusHandle, Pixels, Styled, WindowContext};
use serde::{Deserialize, Serialize};
/// Returns a `Div` as horizontal flex layout. /// Returns a `Div` as horizontal flex layout.
pub fn h_flex() -> Div { pub fn h_flex() -> Div {
@ -133,7 +134,7 @@ pub trait StyledExt: Styled + Sized {
impl<E: Styled> StyledExt for E {} impl<E: Styled> StyledExt for E {}
/// A size for elements. /// A size for elements.
#[derive(Clone, Default, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Default, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub enum Size { pub enum Size {
Size(Pixels), Size(Pixels),
XSmall, XSmall,

View file

@ -336,9 +336,9 @@ where
self.selected_col = Some(col_ix); self.selected_col = Some(col_ix);
if let Some(col_ix) = self.selected_col { if let Some(col_ix) = self.selected_col {
// TODO: Fix scroll to selected col, this was not working after fixed col. // TODO: Fix scroll to selected col, this was not working after fixed col.
if self.col_groups[col_ix].fixed.is_none() { // if self.col_groups[col_ix].fixed.is_none() {
self.horizontal_scroll_handle.scroll_to_item(col_ix); // self.horizontal_scroll_handle.scroll_to_item(col_ix);
} // }
} }
cx.emit(TableEvent::SelectCol(col_ix)); cx.emit(TableEvent::SelectCol(col_ix));
cx.notify(); cx.notify();
@ -426,8 +426,8 @@ where
.whitespace_nowrap() .whitespace_nowrap()
.map(|this| match self.size { .map(|this| match self.size {
Size::XSmall => this.text_sm().py_0p5().px_1(), Size::XSmall => this.text_sm().py_0p5().px_1(),
Size::Small => this.text_sm().py_1().px_1p5(), Size::Small => this.text_sm().py(px(3.)).px_1p5(),
Size::Large => this.py_1p5().px_3(), Size::Large => this.py_2().px_3(),
_ => this.py_1().px_2(), _ => this.py_1().px_2(),
}) })
} }
@ -658,10 +658,10 @@ where
let sort = sort.unwrap(); let sort = sort.unwrap();
let icon = match sort { let (icon, is_on) = match sort {
ColSort::Ascending => IconName::SortAscending, ColSort::Ascending => (IconName::SortAscending, true),
ColSort::Descending => IconName::SortDescending, ColSort::Descending => (IconName::SortDescending, true),
ColSort::Default => IconName::ChevronsUpDown, ColSort::Default => (IconName::ChevronsUpDown, false),
}; };
Some( Some(
@ -671,8 +671,12 @@ where
.ml_2() .ml_2()
.p(px(2.)) .p(px(2.))
.rounded_sm() .rounded_sm()
.hover(|this| this.bg(cx.theme().secondary)) .map(|this| match is_on {
.active(|this| this.bg(cx.theme().secondary_active)) 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_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
.on_click(cx.listener(move |table, _, cx| table.perform_sort(col_ix, cx))) .on_click(cx.listener(move |table, _, cx| table.perform_sort(col_ix, cx)))
.child( .child(
@ -787,31 +791,45 @@ where
) -> impl IntoElement { ) -> impl IntoElement {
let view = cx.view().clone(); let view = cx.view().clone();
let horizontal_scroll_handle = self.horizontal_scroll_handle.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() h_flex()
.w_full() .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() .flex_shrink_0()
.border_b_1() .border_b_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.child( .text_color(cx.theme().table_head_foreground)
.when(fixed_cols_count > 0, |this| {
// Render left fixed columns // Render left fixed columns
h_flex() this.child(
.id("table-head-fixed-left") h_flex()
.h_full() .id("table-head-fixed-left")
.bg(cx.theme().table_head) .h_full()
.border_r_1() .bg(cx.theme().table_head)
.border_color(cx.theme().border) .border_r_1()
.children( .border_color(cx.theme().border)
self.col_groups .children(
.iter() self.col_groups
.filter(|col| col.fixed == Some(ColFixed::Left)) .iter()
.enumerate() .filter(|col| col.fixed == Some(ColFixed::Left))
.map(|(col_ix, _)| self.render_th(col_ix, cx)), .enumerate()
), .map(|(col_ix, _)| self.render_th(col_ix, cx)),
) ),
)
})
.child( .child(
// Render other normal columns // 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 horizontal_scroll_handle = horizontal_scroll_handle.clone();
let view = view.clone(); let view = view.clone();
move |table, _, cx| { move |table, _, cx| {
@ -856,6 +874,12 @@ where
.map(|this| vec![this]) .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() .h_full()
.flex_1(), .flex_1(),
) )

View file

@ -315,6 +315,7 @@ pub struct Theme {
pub table: Hsla, pub table: Hsla,
pub table_even: Hsla, pub table_even: Hsla,
pub table_head: Hsla, pub table_head: Hsla,
pub table_head_foreground: Hsla,
pub table_row_border: Hsla, pub table_row_border: Hsla,
pub table_active: Hsla, pub table_active: Hsla,
pub table_hover: Hsla, pub table_hover: Hsla,
@ -398,6 +399,7 @@ impl From<Colors> for Theme {
table_active: colors.list_active, table_active: colors.list_active,
table_hover: colors.list_active.opacity(0.8), table_hover: colors.list_active.opacity(0.8),
table_row_border: colors.border.opacity(0.5), table_row_border: colors.border.opacity(0.5),
table_head_foreground: colors.foreground.opacity(0.7),
link: colors.link, link: colors.link,
link_hover: colors.link.lighten(0.2), link_hover: colors.link.lighten(0.2),
link_active: colors.link.darken(0.2), link_active: colors.link.darken(0.2),