table: Update to standardization the TableDelegate API. (#1067)
## Break Changes - Renamed some method name of the TableDelegate. ```diff - fn can_select_col + fn col_selectable - fn can_resize_col + fn col_resizable - fn col_padding + fn col_paddings - fn can_move_col + fn col_movable ``` - Removed `fn can_loop_select` remove TableDelegate, instead of to use `loop_selection` option on `Table`.
This commit is contained in:
parent
ea1f5331c8
commit
ed75659765
3 changed files with 245 additions and 180 deletions
|
|
@ -587,42 +587,6 @@ impl Render for ButtonStory {
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
|
||||||
section("Custom Button")
|
|
||||||
.child(
|
|
||||||
Button::new("button-6-custom")
|
|
||||||
.custom(custom_variant)
|
|
||||||
.label("Custom Button")
|
|
||||||
.disabled(disabled)
|
|
||||||
.selected(selected)
|
|
||||||
.loading(loading)
|
|
||||||
.when(compact, |this| this.compact())
|
|
||||||
.on_click(Self::on_click),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("button-outline-6-custom")
|
|
||||||
.outline()
|
|
||||||
.custom(custom_variant)
|
|
||||||
.label("Outline Button")
|
|
||||||
.disabled(disabled)
|
|
||||||
.selected(selected)
|
|
||||||
.loading(loading)
|
|
||||||
.when(compact, |this| this.compact())
|
|
||||||
.on_click(Self::on_click),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("button-outline-6-custom-1")
|
|
||||||
.outline()
|
|
||||||
.icon(IconName::Bell)
|
|
||||||
.custom(custom_variant)
|
|
||||||
.label("Icon Button")
|
|
||||||
.disabled(disabled)
|
|
||||||
.selected(selected)
|
|
||||||
.loading(loading)
|
|
||||||
.when(compact, |this| this.compact())
|
|
||||||
.on_click(Self::on_click),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
section("Button Group").child(
|
section("Button Group").child(
|
||||||
ButtonGroup::new("button-group")
|
ButtonGroup::new("button-group")
|
||||||
|
|
@ -936,5 +900,41 @@ impl Render for ButtonStory {
|
||||||
.when(compact, |this| this.compact()),
|
.when(compact, |this| this.compact()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
section("Custom Button")
|
||||||
|
.child(
|
||||||
|
Button::new("button-6-custom")
|
||||||
|
.custom(custom_variant)
|
||||||
|
.label("Custom Button")
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("button-outline-6-custom")
|
||||||
|
.outline()
|
||||||
|
.custom(custom_variant)
|
||||||
|
.label("Outline Button")
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("button-outline-6-custom-1")
|
||||||
|
.outline()
|
||||||
|
.icon(IconName::Bell)
|
||||||
|
.custom(custom_variant)
|
||||||
|
.label("Icon Button")
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use fake::Fake;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, px, Action, AnyElement, App, AppContext, ClickEvent, Context,
|
div, prelude::FluentBuilder as _, px, Action, AnyElement, App, AppContext, ClickEvent, Context,
|
||||||
Edges, Entity, Focusable, InteractiveElement, IntoElement, ParentElement, Pixels, Render,
|
Edges, Entity, Focusable, InteractiveElement, IntoElement, ParentElement, Pixels, Render,
|
||||||
SharedString, StatefulInteractiveElement, Styled, Timer, Window,
|
SharedString, StatefulInteractiveElement, Styled, TextAlign, Timer, Window,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
button::Button,
|
button::Button,
|
||||||
|
|
@ -19,7 +19,7 @@ use gpui_component::{
|
||||||
label::Label,
|
label::Label,
|
||||||
popup_menu::{PopupMenu, PopupMenuExt},
|
popup_menu::{PopupMenu, PopupMenuExt},
|
||||||
table::{self, ColFixed, ColSort, Table, TableDelegate, TableEvent},
|
table::{self, ColFixed, ColSort, Table, TableDelegate, TableEvent},
|
||||||
v_flex, ActiveTheme as _, Selectable, Sizable as _, Size, StyleSized as _,
|
v_flex, ActiveTheme as _, Selectable, Sizable as _, Size, StyleSized as _, StyledExt,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -174,28 +174,63 @@ fn random_stocks(size: usize) -> Vec<Stock> {
|
||||||
struct Column {
|
struct Column {
|
||||||
id: SharedString,
|
id: SharedString,
|
||||||
name: SharedString,
|
name: SharedString,
|
||||||
|
align: TextAlign,
|
||||||
sort: Option<ColSort>,
|
sort: Option<ColSort>,
|
||||||
|
paddings: Option<Edges<Pixels>>,
|
||||||
|
width: Pixels,
|
||||||
|
fixed: bool,
|
||||||
|
resizable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Column {
|
impl Column {
|
||||||
fn new(
|
fn new(id: impl Into<SharedString>, name: impl Into<SharedString>) -> Self {
|
||||||
id: impl Into<SharedString>,
|
|
||||||
name: impl Into<SharedString>,
|
|
||||||
sort: Option<ColSort>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
sort,
|
align: TextAlign::Left,
|
||||||
|
sort: None,
|
||||||
|
paddings: None,
|
||||||
|
width: px(100.),
|
||||||
|
fixed: false,
|
||||||
|
resizable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sortable(mut self) -> Self {
|
||||||
|
self.sort = Some(ColSort::Default);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_right(mut self) -> Self {
|
||||||
|
self.align = TextAlign::Right;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p_0(mut self) -> Self {
|
||||||
|
self.paddings = Some(Edges::all(px(0.)));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn w(mut self, width: impl Into<Pixels>) -> Self {
|
||||||
|
self.width = width.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixed(mut self) -> Self {
|
||||||
|
self.fixed = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resizable(mut self, resizable: bool) -> Self {
|
||||||
|
self.resizable = resizable;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StockTableDelegate {
|
struct StockTableDelegate {
|
||||||
stocks: Vec<Stock>,
|
stocks: Vec<Stock>,
|
||||||
columns: Vec<Column>,
|
columns: Vec<Column>,
|
||||||
size: Size,
|
size: Size,
|
||||||
loop_selection: bool,
|
|
||||||
col_resize: bool,
|
col_resize: bool,
|
||||||
col_order: bool,
|
col_order: bool,
|
||||||
col_sort: bool,
|
col_sort: bool,
|
||||||
|
|
@ -214,53 +249,64 @@ impl StockTableDelegate {
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
stocks: random_stocks(size),
|
stocks: random_stocks(size),
|
||||||
columns: vec![
|
columns: vec![
|
||||||
Column::new("id", "ID", None),
|
Column::new("id", "ID").w(60.).fixed().resizable(false),
|
||||||
Column::new("market", "Market", None),
|
Column::new("market", "Market")
|
||||||
Column::new("symbol", "Symbol", Some(ColSort::Default)),
|
.w(60.)
|
||||||
Column::new("name", "Name", None),
|
.fixed()
|
||||||
Column::new("price", "Price", Some(ColSort::Default)),
|
.resizable(false),
|
||||||
Column::new("change", "Chg", Some(ColSort::Default)),
|
Column::new("symbol", "Symbol").w(100.).fixed().sortable(),
|
||||||
Column::new("change_percent", "Chg%", Some(ColSort::Default)),
|
Column::new("name", "Name").w(180.).fixed(),
|
||||||
Column::new("volume", "Volume", None),
|
Column::new("price", "Price").sortable().text_right().p_0(),
|
||||||
Column::new("turnover", "Turnover", None),
|
Column::new("change", "Chg").sortable().text_right().p_0(),
|
||||||
Column::new("market_cap", "Market Cap", None),
|
Column::new("change_percent", "Chg%")
|
||||||
Column::new("ttm", "TTM", None),
|
.sortable()
|
||||||
Column::new("five_mins_ranking", "5m Ranking", None),
|
.text_right()
|
||||||
Column::new("th60_days_ranking", "60d Ranking", None),
|
.p_0(),
|
||||||
Column::new("year_change_percent", "Year Chg%", None),
|
Column::new("volume", "Volume").p_0(),
|
||||||
Column::new("bid", "Bid", None),
|
Column::new("turnover", "Turnover").p_0(),
|
||||||
Column::new("bid_volume", "Bid Vol", None),
|
Column::new("market_cap", "Market Cap").p_0(),
|
||||||
Column::new("ask", "Ask", None),
|
Column::new("ttm", "TTM").p_0(),
|
||||||
Column::new("ask_volume", "Ask Vol", None),
|
Column::new("five_mins_ranking", "5m Ranking")
|
||||||
Column::new("open", "Open", None),
|
.text_right()
|
||||||
Column::new("prev_close", "Prev Close", None),
|
.p_0(),
|
||||||
Column::new("high", "High", None),
|
Column::new("th60_days_ranking", "60d Ranking"),
|
||||||
Column::new("low", "Low", None),
|
Column::new("year_change_percent", "Year Chg%"),
|
||||||
Column::new("turnover_rate", "Turnover Rate", None),
|
Column::new("bid", "Bid").text_right().p_0(),
|
||||||
Column::new("rise_rate", "Rise Rate", None),
|
Column::new("bid_volume", "Bid Vol").text_right().p_0(),
|
||||||
Column::new("amplitude", "Amplitude", None),
|
Column::new("ask", "Ask").text_right().p_0(),
|
||||||
Column::new("pe_status", "P/E", None),
|
Column::new("ask_volume", "Ask Vol").text_right().p_0(),
|
||||||
Column::new("pb_status", "P/B", None),
|
Column::new("open", "Open").text_right().p_0(),
|
||||||
Column::new("volume_ratio", "Volume Ratio", None),
|
Column::new("prev_close", "Prev Close").text_right().p_0(),
|
||||||
Column::new("bid_ask_ratio", "Bid Ask Ratio", None),
|
Column::new("high", "High").text_right().p_0(),
|
||||||
Column::new("latest_pre_close", "Latest Pre Close", None),
|
Column::new("low", "Low").text_right().p_0(),
|
||||||
Column::new("latest_post_close", "Latest Post Close", None),
|
Column::new("turnover_rate", "Turnover Rate"),
|
||||||
Column::new("pre_market_cap", "Pre Mkt Cap", None),
|
Column::new("rise_rate", "Rise Rate"),
|
||||||
Column::new("pre_market_percent", "Pre Mkt%", None),
|
Column::new("amplitude", "Amplitude"),
|
||||||
Column::new("pre_market_change", "Pre Mkt Chg", None),
|
Column::new("pe_status", "P/E"),
|
||||||
Column::new("post_market_cap", "Post Mkt Cap", None),
|
Column::new("pb_status", "P/B"),
|
||||||
Column::new("post_market_percent", "Post Mkt%", None),
|
Column::new("volume_ratio", "Volume Ratio")
|
||||||
Column::new("post_market_change", "Post Mkt Chg", None),
|
.text_right()
|
||||||
Column::new("float_cap", "Float Cap", None),
|
.p_0(),
|
||||||
Column::new("shares", "Shares", None),
|
Column::new("bid_ask_ratio", "Bid Ask Ratio")
|
||||||
Column::new("shares_float", "Float Shares", None),
|
.text_right()
|
||||||
Column::new("day_5_ranking", "5d Ranking", None),
|
.p_0(),
|
||||||
Column::new("day_10_ranking", "10d Ranking", None),
|
Column::new("latest_pre_close", "Latest Pre Close"),
|
||||||
Column::new("day_30_ranking", "30d Ranking", None),
|
Column::new("latest_post_close", "Latest Post Close"),
|
||||||
Column::new("day_120_ranking", "120d Ranking", None),
|
Column::new("pre_market_cap", "Pre Mkt Cap"),
|
||||||
Column::new("day_250_ranking", "250d Ranking", None),
|
Column::new("pre_market_percent", "Pre Mkt%"),
|
||||||
|
Column::new("pre_market_change", "Pre Mkt Chg"),
|
||||||
|
Column::new("post_market_cap", "Post Mkt Cap"),
|
||||||
|
Column::new("post_market_percent", "Post Mkt%"),
|
||||||
|
Column::new("post_market_change", "Post Mkt Chg"),
|
||||||
|
Column::new("float_cap", "Float Cap"),
|
||||||
|
Column::new("shares", "Shares"),
|
||||||
|
Column::new("shares_float", "Float Shares"),
|
||||||
|
Column::new("day_5_ranking", "5d Ranking"),
|
||||||
|
Column::new("day_10_ranking", "10d Ranking"),
|
||||||
|
Column::new("day_30_ranking", "30d Ranking"),
|
||||||
|
Column::new("day_120_ranking", "120d Ranking"),
|
||||||
|
Column::new("day_250_ranking", "250d Ranking"),
|
||||||
],
|
],
|
||||||
loop_selection: true,
|
|
||||||
col_resize: true,
|
col_resize: true,
|
||||||
col_order: true,
|
col_order: true,
|
||||||
col_sort: true,
|
col_sort: true,
|
||||||
|
|
@ -281,7 +327,12 @@ impl StockTableDelegate {
|
||||||
self.full_loading = false;
|
self.full_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_value_cell(&self, val: f64, cx: &mut Context<Table<Self>>) -> AnyElement {
|
fn render_value_cell(
|
||||||
|
&self,
|
||||||
|
col: &Column,
|
||||||
|
val: f64,
|
||||||
|
cx: &mut Context<Table<Self>>,
|
||||||
|
) -> AnyElement {
|
||||||
let this = div()
|
let this = div()
|
||||||
.h_full()
|
.h_full()
|
||||||
.table_cell_size(self.size)
|
.table_cell_size(self.size)
|
||||||
|
|
@ -300,7 +351,10 @@ impl StockTableDelegate {
|
||||||
this
|
this
|
||||||
};
|
};
|
||||||
|
|
||||||
this.into_any_element()
|
this.when(col.align == TextAlign::Right, |this| {
|
||||||
|
this.h_flex().justify_end()
|
||||||
|
})
|
||||||
|
.into_any_element()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,29 +368,25 @@ impl TableDelegate for StockTableDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_name(&self, col_ix: usize, _: &App) -> SharedString {
|
fn col_name(&self, col_ix: usize, _: &App) -> SharedString {
|
||||||
if let Some(col) = self.columns.get(col_ix) {
|
self.columns
|
||||||
col.name.clone()
|
.get(col_ix)
|
||||||
} else {
|
.map(|col| col.name.clone())
|
||||||
"--".into()
|
.unwrap_or("--".into())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_width(&self, col_ix: usize, _: &App) -> Pixels {
|
fn col_width(&self, col_ix: usize, _: &App) -> Pixels {
|
||||||
px(match col_ix {
|
self.columns
|
||||||
0 => 60.,
|
.get(col_ix)
|
||||||
1 => 60.,
|
.map(|col| col.width)
|
||||||
2 => 100.,
|
.unwrap_or(px(100.))
|
||||||
3 => 180.,
|
|
||||||
_ => 100.,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_padding(&self, col_ix: usize, _: &App) -> Option<Edges<Pixels>> {
|
fn col_paddings(&self, col_ix: usize, _: &App) -> Option<Edges<Pixels>> {
|
||||||
if col_ix >= 3 && col_ix <= 10 {
|
let Some(col) = self.columns.get(col_ix) else {
|
||||||
Some(Edges::all(px(0.)))
|
return None;
|
||||||
} else {
|
};
|
||||||
None
|
|
||||||
}
|
col.paddings
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_fixed(&self, col_ix: usize, _: &App) -> Option<table::ColFixed> {
|
fn col_fixed(&self, col_ix: usize, _: &App) -> Option<table::ColFixed> {
|
||||||
|
|
@ -344,18 +394,26 @@ impl TableDelegate for StockTableDelegate {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if col_ix < 4 {
|
let Some(col) = self.columns.get(col_ix) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
if col.fixed {
|
||||||
Some(ColFixed::Left)
|
Some(ColFixed::Left)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_resize_col(&self, col_ix: usize, _: &App) -> bool {
|
fn col_resizable(&self, col_ix: usize, _: &App) -> bool {
|
||||||
return self.col_resize && col_ix > 1;
|
let Some(col) = self.columns.get(col_ix) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
col.resizable
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_select_col(&self, _: usize, _: &App) -> bool {
|
fn col_selectable(&self, _: usize, _: &App) -> bool {
|
||||||
return self.col_selection;
|
return self.col_selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,15 +421,18 @@ impl TableDelegate for StockTableDelegate {
|
||||||
&self,
|
&self,
|
||||||
col_ix: usize,
|
col_ix: usize,
|
||||||
_: &mut Window,
|
_: &mut Window,
|
||||||
cx: &mut Context<Table<Self>>,
|
_: &mut Context<Table<Self>>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let th = div().child(self.col_name(col_ix, cx));
|
let col = self.columns.get(col_ix).unwrap();
|
||||||
|
|
||||||
if col_ix >= 3 && col_ix <= 10 {
|
div()
|
||||||
th.table_cell_size(self.size)
|
.child(col.name.clone())
|
||||||
} else {
|
.when(col_ix >= 3 && col_ix <= 10, |this| {
|
||||||
th
|
this.table_cell_size(self.size)
|
||||||
}
|
})
|
||||||
|
.when(col.align == TextAlign::Right, |this| {
|
||||||
|
this.h_flex().w_full().justify_end()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn context_menu(
|
fn context_menu(
|
||||||
|
|
@ -440,14 +501,14 @@ impl TableDelegate for StockTableDelegate {
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
"symbol" => stock.counter.symbol_code().into_any_element(),
|
"symbol" => stock.counter.symbol_code().into_any_element(),
|
||||||
"name" => stock.counter.name.clone().into_any_element(),
|
"name" => stock.counter.name.clone().into_any_element(),
|
||||||
"price" => self.render_value_cell(stock.price, cx),
|
"price" => self.render_value_cell(&col, stock.price, cx),
|
||||||
"change" => self.render_value_cell(stock.change, cx),
|
"change" => self.render_value_cell(&col, stock.change, cx),
|
||||||
"change_percent" => self.render_value_cell(stock.change_percent, cx),
|
"change_percent" => self.render_value_cell(&col, stock.change_percent, cx),
|
||||||
"volume" => self.render_value_cell(stock.volume, cx),
|
"volume" => self.render_value_cell(&col, stock.volume, cx),
|
||||||
"turnover" => self.render_value_cell(stock.turnover, cx),
|
"turnover" => self.render_value_cell(&col, stock.turnover, cx),
|
||||||
"market_cap" => self.render_value_cell(stock.market_cap, cx),
|
"market_cap" => self.render_value_cell(&col, stock.market_cap, cx),
|
||||||
"ttm" => self.render_value_cell(stock.ttm, cx),
|
"ttm" => self.render_value_cell(&col, stock.ttm, cx),
|
||||||
"five_mins_ranking" => self.render_value_cell(stock.five_mins_ranking, cx),
|
"five_mins_ranking" => self.render_value_cell(&col, stock.five_mins_ranking, cx),
|
||||||
"th60_days_ranking" => stock
|
"th60_days_ranking" => stock
|
||||||
.th60_days_ranking
|
.th60_days_ranking
|
||||||
.floor()
|
.floor()
|
||||||
|
|
@ -457,14 +518,14 @@ impl TableDelegate for StockTableDelegate {
|
||||||
.floor()
|
.floor()
|
||||||
.to_string()
|
.to_string()
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
"bid" => self.render_value_cell(stock.bid, cx),
|
"bid" => self.render_value_cell(&col, stock.bid, cx),
|
||||||
"bid_volume" => self.render_value_cell(stock.bid_volume, cx),
|
"bid_volume" => self.render_value_cell(&col, stock.bid_volume, cx),
|
||||||
"ask" => self.render_value_cell(stock.ask, cx),
|
"ask" => self.render_value_cell(&col, stock.ask, cx),
|
||||||
"ask_volume" => self.render_value_cell(stock.ask_volume, cx),
|
"ask_volume" => self.render_value_cell(&col, stock.ask_volume, cx),
|
||||||
"open" => stock.open.floor().to_string().into_any_element(),
|
"open" => self.render_value_cell(&col, stock.open, cx),
|
||||||
"prev_close" => stock.prev_close.floor().to_string().into_any_element(),
|
"prev_close" => self.render_value_cell(&col, stock.prev_close, cx),
|
||||||
"high" => self.render_value_cell(stock.high, cx),
|
"high" => self.render_value_cell(&col, stock.high, cx),
|
||||||
"low" => self.render_value_cell(stock.low, cx),
|
"low" => self.render_value_cell(&col, stock.low, cx),
|
||||||
"turnover_rate" => (stock.turnover_rate * 100.0)
|
"turnover_rate" => (stock.turnover_rate * 100.0)
|
||||||
.floor()
|
.floor()
|
||||||
.to_string()
|
.to_string()
|
||||||
|
|
@ -479,8 +540,8 @@ impl TableDelegate for StockTableDelegate {
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
"pe_status" => stock.pe_status.floor().to_string().into_any_element(),
|
"pe_status" => stock.pe_status.floor().to_string().into_any_element(),
|
||||||
"pb_status" => stock.pb_status.floor().to_string().into_any_element(),
|
"pb_status" => stock.pb_status.floor().to_string().into_any_element(),
|
||||||
"volume_ratio" => self.render_value_cell(stock.volume_ratio, cx),
|
"volume_ratio" => self.render_value_cell(&col, stock.volume_ratio, cx),
|
||||||
"bid_ask_ratio" => self.render_value_cell(stock.bid_ask_ratio, cx),
|
"bid_ask_ratio" => self.render_value_cell(&col, stock.bid_ask_ratio, cx),
|
||||||
"latest_pre_close" => stock
|
"latest_pre_close" => stock
|
||||||
.latest_pre_close
|
.latest_pre_close
|
||||||
.floor()
|
.floor()
|
||||||
|
|
@ -523,11 +584,7 @@ impl TableDelegate for StockTableDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_loop_select(&self, _: &App) -> bool {
|
fn col_movable(&self, _: usize, _: &App) -> bool {
|
||||||
self.loop_selection
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_move_col(&self, _: usize, _: &App) -> bool {
|
|
||||||
self.col_order
|
self.col_order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -736,9 +793,13 @@ impl TableStory {
|
||||||
// Update when the user presses Enter or the input loses focus
|
// Update when the user presses Enter or the input loses focus
|
||||||
InputEvent::PressEnter { .. } | InputEvent::Blur => {
|
InputEvent::PressEnter { .. } | InputEvent::Blur => {
|
||||||
let text = self.num_stocks_input.read(cx).value().to_string();
|
let text = self.num_stocks_input.read(cx).value().to_string();
|
||||||
if let Ok(num) = text.parse::<usize>() {
|
if let Ok(total_count) = text.parse::<usize>() {
|
||||||
|
if total_count == self.table.read(cx).delegate().stocks.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.table.update(cx, |table, _| {
|
self.table.update(cx, |table, _| {
|
||||||
table.delegate_mut().update_stocks(num);
|
table.delegate_mut().update_stocks(total_count);
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
@ -749,7 +810,7 @@ impl TableStory {
|
||||||
|
|
||||||
fn toggle_loop_selection(&mut self, checked: &bool, _: &mut Window, cx: &mut Context<Self>) {
|
fn toggle_loop_selection(&mut self, checked: &bool, _: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.table.update(cx, |table, cx| {
|
self.table.update(cx, |table, cx| {
|
||||||
table.delegate_mut().loop_selection = *checked;
|
table.loop_selection = *checked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -840,6 +901,7 @@ impl TableStory {
|
||||||
impl Render for TableStory {
|
impl Render for TableStory {
|
||||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl gpui::IntoElement {
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl gpui::IntoElement {
|
||||||
let delegate = self.table.read(cx).delegate();
|
let delegate = self.table.read(cx).delegate();
|
||||||
|
let loop_selection = self.table.read(cx).loop_selection;
|
||||||
let rows_count = delegate.rows_count(cx);
|
let rows_count = delegate.rows_count(cx);
|
||||||
let size = self.size;
|
let size = self.size;
|
||||||
|
|
||||||
|
|
@ -856,7 +918,7 @@ impl Render for TableStory {
|
||||||
.child(
|
.child(
|
||||||
Checkbox::new("loop-selection")
|
Checkbox::new("loop-selection")
|
||||||
.label("Loop Selection")
|
.label("Loop Selection")
|
||||||
.selected(delegate.loop_selection)
|
.selected(loop_selection)
|
||||||
.on_click(cx.listener(Self::toggle_loop_selection)),
|
.on_click(cx.listener(Self::toggle_loop_selection)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ pub(crate) struct ColGroup {
|
||||||
pub(crate) bounds: Bounds<Pixels>,
|
pub(crate) bounds: Bounds<Pixels>,
|
||||||
pub(crate) sort: Option<ColSort>,
|
pub(crate) sort: Option<ColSort>,
|
||||||
pub(crate) fixed: Option<ColFixed>,
|
pub(crate) fixed: Option<ColFixed>,
|
||||||
pub(crate) padding: Option<Edges<Pixels>>,
|
pub(crate) paddings: Option<Edges<Pixels>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -144,6 +144,11 @@ pub struct Table<D: TableDelegate> {
|
||||||
col_groups: Vec<ColGroup>,
|
col_groups: Vec<ColGroup>,
|
||||||
fixed_cols: FixedCols,
|
fixed_cols: FixedCols,
|
||||||
|
|
||||||
|
/// Whether the table can loop selection, default is true.
|
||||||
|
///
|
||||||
|
/// When the prev/next selection is out of the table bounds, the selection will loop to the other side.
|
||||||
|
pub loop_selection: bool,
|
||||||
|
|
||||||
pub vertical_scroll_handle: UniformListScrollHandle,
|
pub vertical_scroll_handle: UniformListScrollHandle,
|
||||||
pub vertical_scroll_state: ScrollbarState,
|
pub vertical_scroll_state: ScrollbarState,
|
||||||
pub horizontal_scroll_handle: ScrollHandle,
|
pub horizontal_scroll_handle: ScrollHandle,
|
||||||
|
|
@ -182,12 +187,12 @@ pub trait TableDelegate: Sized + 'static {
|
||||||
fn col_name(&self, col_ix: usize, cx: &App) -> SharedString;
|
fn col_name(&self, col_ix: usize, cx: &App) -> SharedString;
|
||||||
|
|
||||||
/// Returns whether the column at the given index can be resized. Default: true
|
/// Returns whether the column at the given index can be resized. Default: true
|
||||||
fn can_resize_col(&self, col_ix: usize, cx: &App) -> bool {
|
fn col_resizable(&self, col_ix: usize, cx: &App) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the column at the given index can be selected. Default: false
|
/// Returns whether the column at the given index can be selected. Default: false
|
||||||
fn can_select_col(&self, col_ix: usize, cx: &App) -> bool {
|
fn col_selectable(&self, col_ix: usize, cx: &App) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,10 +221,15 @@ pub trait TableDelegate: Sized + 'static {
|
||||||
/// Return the padding of the column at the given index to override the default padding.
|
/// Return the padding of the column at the given index to override the default padding.
|
||||||
///
|
///
|
||||||
/// Return None, use the default padding.
|
/// Return None, use the default padding.
|
||||||
fn col_padding(&self, col_ix: usize, cx: &App) -> Option<Edges<Pixels>> {
|
fn col_paddings(&self, col_ix: usize, cx: &App) -> Option<Edges<Pixels>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true to enable column order change.
|
||||||
|
fn col_movable(&self, col_ix: usize, cx: &App) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform sort on the column at the given index.
|
/// Perform sort on the column at the given index.
|
||||||
fn perform_sort(
|
fn perform_sort(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -264,20 +274,6 @@ pub trait TableDelegate: Sized + 'static {
|
||||||
cx: &mut Context<Table<Self>>,
|
cx: &mut Context<Table<Self>>,
|
||||||
) -> impl IntoElement;
|
) -> impl IntoElement;
|
||||||
|
|
||||||
/// Return true to enable loop selection on the table.
|
|
||||||
///
|
|
||||||
/// When the prev/next selection is out of the table bounds, the selection will loop to the other side.
|
|
||||||
///
|
|
||||||
/// Default: true
|
|
||||||
fn can_loop_select(&self, _: &App) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return true to enable column order change.
|
|
||||||
fn can_move_col(&self, col_ix: usize, cx: &App) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Move the column at the given `col_ix` to insert before the column at the given `to_ix`.
|
/// Move the column at the given `col_ix` to insert before the column at the given `to_ix`.
|
||||||
fn move_col(
|
fn move_col(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -403,6 +399,7 @@ where
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
scrollbar_visible: Edges::all(true),
|
scrollbar_visible: Edges::all(true),
|
||||||
visible_range: VisibleRangeState::default(),
|
visible_range: VisibleRangeState::default(),
|
||||||
|
loop_selection: true,
|
||||||
_load_more_task: Task::ready(()),
|
_load_more_task: Task::ready(()),
|
||||||
_measure: Vec::new(),
|
_measure: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
@ -436,6 +433,12 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set to loop selection, default to true.
|
||||||
|
pub fn loop_selection(mut self, loop_selection: bool) -> Self {
|
||||||
|
self.loop_selection = loop_selection;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the size to the table.
|
/// Set the size to the table.
|
||||||
pub fn set_size(&mut self, size: Size, cx: &mut Context<Self>) {
|
pub fn set_size(&mut self, size: Size, cx: &mut Context<Self>) {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
|
|
@ -466,7 +469,7 @@ where
|
||||||
self.col_groups = (0..self.delegate.cols_count(cx))
|
self.col_groups = (0..self.delegate.cols_count(cx))
|
||||||
.map(|col_ix| ColGroup {
|
.map(|col_ix| ColGroup {
|
||||||
width: self.delegate.col_width(col_ix, cx),
|
width: self.delegate.col_width(col_ix, cx),
|
||||||
padding: self.delegate.col_padding(col_ix, cx),
|
paddings: self.delegate.col_paddings(col_ix, cx),
|
||||||
bounds: Bounds::default(),
|
bounds: Bounds::default(),
|
||||||
sort: self.delegate.col_sort(col_ix, cx),
|
sort: self.delegate.col_sort(col_ix, cx),
|
||||||
fixed: self.delegate.col_fixed(col_ix, cx),
|
fixed: self.delegate.col_fixed(col_ix, cx),
|
||||||
|
|
@ -563,7 +566,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_col_head_click(&mut self, col_ix: usize, _: &mut Window, cx: &mut Context<Self>) {
|
fn on_col_head_click(&mut self, col_ix: usize, _: &mut Window, cx: &mut Context<Self>) {
|
||||||
if !self.delegate.can_select_col(col_ix, cx) {
|
if !self.delegate.col_selectable(col_ix, cx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,7 +587,7 @@ where
|
||||||
if selected_row > 0 {
|
if selected_row > 0 {
|
||||||
selected_row = selected_row.saturating_sub(1);
|
selected_row = selected_row.saturating_sub(1);
|
||||||
} else {
|
} else {
|
||||||
if self.delegate.can_loop_select(cx) {
|
if self.loop_selection {
|
||||||
selected_row = rows_count.saturating_sub(1);
|
selected_row = rows_count.saturating_sub(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -601,7 +604,7 @@ where
|
||||||
let selected_row = match self.selected_row {
|
let selected_row = match self.selected_row {
|
||||||
Some(selected_row) if selected_row < rows_count.saturating_sub(1) => selected_row + 1,
|
Some(selected_row) if selected_row < rows_count.saturating_sub(1) => selected_row + 1,
|
||||||
Some(selected_row) => {
|
Some(selected_row) => {
|
||||||
if self.delegate.can_loop_select(cx) {
|
if self.loop_selection {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
selected_row
|
selected_row
|
||||||
|
|
@ -624,7 +627,7 @@ where
|
||||||
if selected_col > 0 {
|
if selected_col > 0 {
|
||||||
selected_col = selected_col.saturating_sub(1);
|
selected_col = selected_col.saturating_sub(1);
|
||||||
} else {
|
} else {
|
||||||
if self.delegate.can_loop_select(cx) {
|
if self.loop_selection {
|
||||||
selected_col = cols_count.saturating_sub(1);
|
selected_col = cols_count.saturating_sub(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -641,7 +644,7 @@ where
|
||||||
if selected_col < self.delegate.cols_count(cx).saturating_sub(1) {
|
if selected_col < self.delegate.cols_count(cx).saturating_sub(1) {
|
||||||
selected_col += 1;
|
selected_col += 1;
|
||||||
} else {
|
} else {
|
||||||
if self.delegate.can_loop_select(cx) {
|
if self.loop_selection {
|
||||||
selected_col = 0;
|
selected_col = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -678,7 +681,7 @@ where
|
||||||
const MIN_WIDTH: Pixels = px(10.0);
|
const MIN_WIDTH: Pixels = px(10.0);
|
||||||
const MAX_WIDTH: Pixels = px(1200.0);
|
const MAX_WIDTH: Pixels = px(1200.0);
|
||||||
|
|
||||||
if !self.delegate.can_resize_col(ix, cx) {
|
if !self.delegate.col_resizable(ix, cx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let size = size.floor();
|
let size = size.floor();
|
||||||
|
|
@ -810,7 +813,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let col_width = col_group.width;
|
let col_width = col_group.width;
|
||||||
let col_padding = col_group.padding;
|
let col_padding = col_group.paddings;
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.w(col_width)
|
.w(col_width)
|
||||||
|
|
@ -833,7 +836,7 @@ where
|
||||||
fn render_col_wrap(&self, col_ix: usize, _: &mut Window, cx: &mut Context<Self>) -> Div {
|
fn render_col_wrap(&self, col_ix: usize, _: &mut Window, cx: &mut Context<Self>) -> Div {
|
||||||
let el = h_flex().h_full();
|
let el = h_flex().h_full();
|
||||||
|
|
||||||
if self.delegate().can_select_col(col_ix, cx)
|
if self.delegate().col_selectable(col_ix, cx)
|
||||||
&& self.selected_col == Some(col_ix)
|
&& self.selected_col == Some(col_ix)
|
||||||
&& self.selection_state == SelectionState::Column
|
&& self.selection_state == SelectionState::Column
|
||||||
{
|
{
|
||||||
|
|
@ -896,7 +899,7 @@ where
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
const HANDLE_SIZE: Pixels = px(2.);
|
const HANDLE_SIZE: Pixels = px(2.);
|
||||||
|
|
||||||
if !self.delegate.can_resize_col(ix, cx) {
|
if !self.delegate.col_resizable(ix, cx) {
|
||||||
return div().into_any_element();
|
return div().into_any_element();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1024,8 +1027,8 @@ where
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let entity_id = cx.entity_id();
|
let entity_id = cx.entity_id();
|
||||||
let col_group = self.col_groups.get(col_ix).expect("BUG: invalid col index");
|
let col_group = self.col_groups.get(col_ix).expect("BUG: invalid col index");
|
||||||
let moveable = self.delegate.can_move_col(col_ix, cx);
|
let movable = self.delegate.col_movable(col_ix, cx);
|
||||||
let paddings = self.delegate.col_padding(col_ix, cx);
|
let paddings = self.delegate.col_paddings(col_ix, cx);
|
||||||
let name = self.delegate.col_name(col_ix, cx);
|
let name = self.delegate.col_name(col_ix, cx);
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|
@ -1052,7 +1055,7 @@ where
|
||||||
})
|
})
|
||||||
.children(self.render_sort_icon(col_ix, &col_group, window, cx)),
|
.children(self.render_sort_icon(col_ix, &col_group, window, cx)),
|
||||||
)
|
)
|
||||||
.when(moveable, |this| {
|
.when(movable, |this| {
|
||||||
this.on_drag(
|
this.on_drag(
|
||||||
DragCol {
|
DragCol {
|
||||||
entity_id,
|
entity_id,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue