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:
Jason Lee 2025-07-16 17:11:49 +08:00 committed by GitHub
parent ea1f5331c8
commit ed75659765
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 245 additions and 180 deletions

View file

@ -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),
),
)
} }
} }

View file

@ -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(

View file

@ -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,