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),
),
)
.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(
section("Button Group").child(
ButtonGroup::new("button-group")
@ -936,5 +900,41 @@ impl Render for ButtonStory {
.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::{
div, prelude::FluentBuilder as _, px, Action, AnyElement, App, AppContext, ClickEvent, Context,
Edges, Entity, Focusable, InteractiveElement, IntoElement, ParentElement, Pixels, Render,
SharedString, StatefulInteractiveElement, Styled, Timer, Window,
SharedString, StatefulInteractiveElement, Styled, TextAlign, Timer, Window,
};
use gpui_component::{
button::Button,
@ -19,7 +19,7 @@ use gpui_component::{
label::Label,
popup_menu::{PopupMenu, PopupMenuExt},
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};
@ -174,28 +174,63 @@ fn random_stocks(size: usize) -> Vec<Stock> {
struct Column {
id: SharedString,
name: SharedString,
align: TextAlign,
sort: Option<ColSort>,
paddings: Option<Edges<Pixels>>,
width: Pixels,
fixed: bool,
resizable: bool,
}
impl Column {
fn new(
id: impl Into<SharedString>,
name: impl Into<SharedString>,
sort: Option<ColSort>,
) -> Self {
fn new(id: impl Into<SharedString>, name: impl Into<SharedString>) -> Self {
Self {
id: id.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 {
stocks: Vec<Stock>,
columns: Vec<Column>,
size: Size,
loop_selection: bool,
col_resize: bool,
col_order: bool,
col_sort: bool,
@ -214,53 +249,64 @@ impl StockTableDelegate {
size: Size::default(),
stocks: random_stocks(size),
columns: vec![
Column::new("id", "ID", None),
Column::new("market", "Market", None),
Column::new("symbol", "Symbol", Some(ColSort::Default)),
Column::new("name", "Name", None),
Column::new("price", "Price", Some(ColSort::Default)),
Column::new("change", "Chg", Some(ColSort::Default)),
Column::new("change_percent", "Chg%", Some(ColSort::Default)),
Column::new("volume", "Volume", None),
Column::new("turnover", "Turnover", None),
Column::new("market_cap", "Market Cap", None),
Column::new("ttm", "TTM", None),
Column::new("five_mins_ranking", "5m Ranking", None),
Column::new("th60_days_ranking", "60d Ranking", None),
Column::new("year_change_percent", "Year Chg%", None),
Column::new("bid", "Bid", None),
Column::new("bid_volume", "Bid Vol", None),
Column::new("ask", "Ask", None),
Column::new("ask_volume", "Ask Vol", None),
Column::new("open", "Open", None),
Column::new("prev_close", "Prev Close", None),
Column::new("high", "High", None),
Column::new("low", "Low", None),
Column::new("turnover_rate", "Turnover Rate", None),
Column::new("rise_rate", "Rise Rate", None),
Column::new("amplitude", "Amplitude", None),
Column::new("pe_status", "P/E", None),
Column::new("pb_status", "P/B", None),
Column::new("volume_ratio", "Volume Ratio", None),
Column::new("bid_ask_ratio", "Bid Ask Ratio", None),
Column::new("latest_pre_close", "Latest Pre Close", None),
Column::new("latest_post_close", "Latest Post Close", None),
Column::new("pre_market_cap", "Pre Mkt Cap", None),
Column::new("pre_market_percent", "Pre Mkt%", None),
Column::new("pre_market_change", "Pre Mkt Chg", None),
Column::new("post_market_cap", "Post Mkt Cap", None),
Column::new("post_market_percent", "Post Mkt%", None),
Column::new("post_market_change", "Post Mkt Chg", None),
Column::new("float_cap", "Float Cap", None),
Column::new("shares", "Shares", None),
Column::new("shares_float", "Float Shares", None),
Column::new("day_5_ranking", "5d Ranking", None),
Column::new("day_10_ranking", "10d Ranking", None),
Column::new("day_30_ranking", "30d Ranking", None),
Column::new("day_120_ranking", "120d Ranking", None),
Column::new("day_250_ranking", "250d Ranking", None),
Column::new("id", "ID").w(60.).fixed().resizable(false),
Column::new("market", "Market")
.w(60.)
.fixed()
.resizable(false),
Column::new("symbol", "Symbol").w(100.).fixed().sortable(),
Column::new("name", "Name").w(180.).fixed(),
Column::new("price", "Price").sortable().text_right().p_0(),
Column::new("change", "Chg").sortable().text_right().p_0(),
Column::new("change_percent", "Chg%")
.sortable()
.text_right()
.p_0(),
Column::new("volume", "Volume").p_0(),
Column::new("turnover", "Turnover").p_0(),
Column::new("market_cap", "Market Cap").p_0(),
Column::new("ttm", "TTM").p_0(),
Column::new("five_mins_ranking", "5m Ranking")
.text_right()
.p_0(),
Column::new("th60_days_ranking", "60d Ranking"),
Column::new("year_change_percent", "Year Chg%"),
Column::new("bid", "Bid").text_right().p_0(),
Column::new("bid_volume", "Bid Vol").text_right().p_0(),
Column::new("ask", "Ask").text_right().p_0(),
Column::new("ask_volume", "Ask Vol").text_right().p_0(),
Column::new("open", "Open").text_right().p_0(),
Column::new("prev_close", "Prev Close").text_right().p_0(),
Column::new("high", "High").text_right().p_0(),
Column::new("low", "Low").text_right().p_0(),
Column::new("turnover_rate", "Turnover Rate"),
Column::new("rise_rate", "Rise Rate"),
Column::new("amplitude", "Amplitude"),
Column::new("pe_status", "P/E"),
Column::new("pb_status", "P/B"),
Column::new("volume_ratio", "Volume Ratio")
.text_right()
.p_0(),
Column::new("bid_ask_ratio", "Bid Ask Ratio")
.text_right()
.p_0(),
Column::new("latest_pre_close", "Latest Pre Close"),
Column::new("latest_post_close", "Latest Post Close"),
Column::new("pre_market_cap", "Pre Mkt Cap"),
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_order: true,
col_sort: true,
@ -281,7 +327,12 @@ impl StockTableDelegate {
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()
.h_full()
.table_cell_size(self.size)
@ -300,7 +351,10 @@ impl StockTableDelegate {
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 {
if let Some(col) = self.columns.get(col_ix) {
col.name.clone()
} else {
"--".into()
}
self.columns
.get(col_ix)
.map(|col| col.name.clone())
.unwrap_or("--".into())
}
fn col_width(&self, col_ix: usize, _: &App) -> Pixels {
px(match col_ix {
0 => 60.,
1 => 60.,
2 => 100.,
3 => 180.,
_ => 100.,
})
self.columns
.get(col_ix)
.map(|col| col.width)
.unwrap_or(px(100.))
}
fn col_padding(&self, col_ix: usize, _: &App) -> Option<Edges<Pixels>> {
if col_ix >= 3 && col_ix <= 10 {
Some(Edges::all(px(0.)))
} else {
None
}
fn col_paddings(&self, col_ix: usize, _: &App) -> Option<Edges<Pixels>> {
let Some(col) = self.columns.get(col_ix) else {
return None;
};
col.paddings
}
fn col_fixed(&self, col_ix: usize, _: &App) -> Option<table::ColFixed> {
@ -344,18 +394,26 @@ impl TableDelegate for StockTableDelegate {
return None;
}
if col_ix < 4 {
let Some(col) = self.columns.get(col_ix) else {
return None;
};
if col.fixed {
Some(ColFixed::Left)
} else {
None
}
}
fn can_resize_col(&self, col_ix: usize, _: &App) -> bool {
return self.col_resize && col_ix > 1;
fn col_resizable(&self, col_ix: usize, _: &App) -> bool {
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;
}
@ -363,15 +421,18 @@ impl TableDelegate for StockTableDelegate {
&self,
col_ix: usize,
_: &mut Window,
cx: &mut Context<Table<Self>>,
_: &mut Context<Table<Self>>,
) -> 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 {
th.table_cell_size(self.size)
} else {
th
}
div()
.child(col.name.clone())
.when(col_ix >= 3 && col_ix <= 10, |this| {
this.table_cell_size(self.size)
})
.when(col.align == TextAlign::Right, |this| {
this.h_flex().w_full().justify_end()
})
}
fn context_menu(
@ -440,14 +501,14 @@ impl TableDelegate for StockTableDelegate {
.into_any_element(),
"symbol" => stock.counter.symbol_code().into_any_element(),
"name" => stock.counter.name.clone().into_any_element(),
"price" => self.render_value_cell(stock.price, cx),
"change" => self.render_value_cell(stock.change, cx),
"change_percent" => self.render_value_cell(stock.change_percent, cx),
"volume" => self.render_value_cell(stock.volume, cx),
"turnover" => self.render_value_cell(stock.turnover, cx),
"market_cap" => self.render_value_cell(stock.market_cap, cx),
"ttm" => self.render_value_cell(stock.ttm, cx),
"five_mins_ranking" => self.render_value_cell(stock.five_mins_ranking, cx),
"price" => self.render_value_cell(&col, stock.price, cx),
"change" => self.render_value_cell(&col, stock.change, cx),
"change_percent" => self.render_value_cell(&col, stock.change_percent, cx),
"volume" => self.render_value_cell(&col, stock.volume, cx),
"turnover" => self.render_value_cell(&col, stock.turnover, cx),
"market_cap" => self.render_value_cell(&col, stock.market_cap, cx),
"ttm" => self.render_value_cell(&col, stock.ttm, cx),
"five_mins_ranking" => self.render_value_cell(&col, stock.five_mins_ranking, cx),
"th60_days_ranking" => stock
.th60_days_ranking
.floor()
@ -457,14 +518,14 @@ impl TableDelegate for StockTableDelegate {
.floor()
.to_string()
.into_any_element(),
"bid" => self.render_value_cell(stock.bid, cx),
"bid_volume" => self.render_value_cell(stock.bid_volume, cx),
"ask" => self.render_value_cell(stock.ask, cx),
"ask_volume" => self.render_value_cell(stock.ask_volume, cx),
"open" => stock.open.floor().to_string().into_any_element(),
"prev_close" => stock.prev_close.floor().to_string().into_any_element(),
"high" => self.render_value_cell(stock.high, cx),
"low" => self.render_value_cell(stock.low, cx),
"bid" => self.render_value_cell(&col, stock.bid, cx),
"bid_volume" => self.render_value_cell(&col, stock.bid_volume, cx),
"ask" => self.render_value_cell(&col, stock.ask, cx),
"ask_volume" => self.render_value_cell(&col, stock.ask_volume, cx),
"open" => self.render_value_cell(&col, stock.open, cx),
"prev_close" => self.render_value_cell(&col, stock.prev_close, cx),
"high" => self.render_value_cell(&col, stock.high, cx),
"low" => self.render_value_cell(&col, stock.low, cx),
"turnover_rate" => (stock.turnover_rate * 100.0)
.floor()
.to_string()
@ -479,8 +540,8 @@ impl TableDelegate for StockTableDelegate {
.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(),
"volume_ratio" => self.render_value_cell(stock.volume_ratio, cx),
"bid_ask_ratio" => self.render_value_cell(stock.bid_ask_ratio, cx),
"volume_ratio" => self.render_value_cell(&col, stock.volume_ratio, cx),
"bid_ask_ratio" => self.render_value_cell(&col, stock.bid_ask_ratio, cx),
"latest_pre_close" => stock
.latest_pre_close
.floor()
@ -523,11 +584,7 @@ impl TableDelegate for StockTableDelegate {
}
}
fn can_loop_select(&self, _: &App) -> bool {
self.loop_selection
}
fn can_move_col(&self, _: usize, _: &App) -> bool {
fn col_movable(&self, _: usize, _: &App) -> bool {
self.col_order
}
@ -736,9 +793,13 @@ impl TableStory {
// Update when the user presses Enter or the input loses focus
InputEvent::PressEnter { .. } | InputEvent::Blur => {
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, _| {
table.delegate_mut().update_stocks(num);
table.delegate_mut().update_stocks(total_count);
});
cx.notify();
}
@ -749,7 +810,7 @@ impl TableStory {
fn toggle_loop_selection(&mut self, checked: &bool, _: &mut Window, cx: &mut Context<Self>) {
self.table.update(cx, |table, cx| {
table.delegate_mut().loop_selection = *checked;
table.loop_selection = *checked;
cx.notify();
});
}
@ -840,6 +901,7 @@ impl TableStory {
impl Render for TableStory {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl gpui::IntoElement {
let delegate = self.table.read(cx).delegate();
let loop_selection = self.table.read(cx).loop_selection;
let rows_count = delegate.rows_count(cx);
let size = self.size;
@ -856,7 +918,7 @@ impl Render for TableStory {
.child(
Checkbox::new("loop-selection")
.label("Loop Selection")
.selected(delegate.loop_selection)
.selected(loop_selection)
.on_click(cx.listener(Self::toggle_loop_selection)),
)
.child(

View file

@ -43,7 +43,7 @@ pub(crate) struct ColGroup {
pub(crate) bounds: Bounds<Pixels>,
pub(crate) sort: Option<ColSort>,
pub(crate) fixed: Option<ColFixed>,
pub(crate) padding: Option<Edges<Pixels>>,
pub(crate) paddings: Option<Edges<Pixels>>,
}
#[derive(Clone)]
@ -144,6 +144,11 @@ pub struct Table<D: TableDelegate> {
col_groups: Vec<ColGroup>,
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_state: ScrollbarState,
pub horizontal_scroll_handle: ScrollHandle,
@ -182,12 +187,12 @@ pub trait TableDelegate: Sized + 'static {
fn col_name(&self, col_ix: usize, cx: &App) -> SharedString;
/// 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
}
/// 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
}
@ -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 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
}
/// 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.
fn perform_sort(
&mut self,
@ -264,20 +274,6 @@ pub trait TableDelegate: Sized + 'static {
cx: &mut Context<Table<Self>>,
) -> 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`.
fn move_col(
&mut self,
@ -403,6 +399,7 @@ where
size: Size::default(),
scrollbar_visible: Edges::all(true),
visible_range: VisibleRangeState::default(),
loop_selection: true,
_load_more_task: Task::ready(()),
_measure: Vec::new(),
};
@ -436,6 +433,12 @@ where
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.
pub fn set_size(&mut self, size: Size, cx: &mut Context<Self>) {
self.size = size;
@ -466,7 +469,7 @@ where
self.col_groups = (0..self.delegate.cols_count(cx))
.map(|col_ix| ColGroup {
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(),
sort: self.delegate.col_sort(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>) {
if !self.delegate.can_select_col(col_ix, cx) {
if !self.delegate.col_selectable(col_ix, cx) {
return;
}
@ -584,7 +587,7 @@ where
if selected_row > 0 {
selected_row = selected_row.saturating_sub(1);
} else {
if self.delegate.can_loop_select(cx) {
if self.loop_selection {
selected_row = rows_count.saturating_sub(1);
}
}
@ -601,7 +604,7 @@ where
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 self.delegate.can_loop_select(cx) {
if self.loop_selection {
0
} else {
selected_row
@ -624,7 +627,7 @@ where
if selected_col > 0 {
selected_col = selected_col.saturating_sub(1);
} else {
if self.delegate.can_loop_select(cx) {
if self.loop_selection {
selected_col = cols_count.saturating_sub(1);
}
}
@ -641,7 +644,7 @@ where
if selected_col < self.delegate.cols_count(cx).saturating_sub(1) {
selected_col += 1;
} else {
if self.delegate.can_loop_select(cx) {
if self.loop_selection {
selected_col = 0;
}
}
@ -678,7 +681,7 @@ where
const MIN_WIDTH: Pixels = px(10.0);
const MAX_WIDTH: Pixels = px(1200.0);
if !self.delegate.can_resize_col(ix, cx) {
if !self.delegate.col_resizable(ix, cx) {
return;
}
let size = size.floor();
@ -810,7 +813,7 @@ where
};
let col_width = col_group.width;
let col_padding = col_group.padding;
let col_padding = col_group.paddings;
div()
.w(col_width)
@ -833,7 +836,7 @@ where
fn render_col_wrap(&self, col_ix: usize, _: &mut Window, cx: &mut Context<Self>) -> Div {
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.selection_state == SelectionState::Column
{
@ -896,7 +899,7 @@ where
) -> impl IntoElement {
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();
}
@ -1024,8 +1027,8 @@ where
) -> impl IntoElement {
let entity_id = cx.entity_id();
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 paddings = self.delegate.col_padding(col_ix, cx);
let movable = self.delegate.col_movable(col_ix, cx);
let paddings = self.delegate.col_paddings(col_ix, cx);
let name = self.delegate.col_name(col_ix, cx);
h_flex()
@ -1052,7 +1055,7 @@ where
})
.children(self.render_sort_icon(col_ix, &col_group, window, cx)),
)
.when(moveable, |this| {
.when(movable, |this| {
this.on_drag(
DragCol {
entity_id,