use std::{ops::Range, rc::Rc}; use gpui::{ AbsoluteLength, AppContext, Context, DefiniteLength, DragMoveEvent, Entity, EntityId, FocusHandle, Length, ListHorizontalSizingBehavior, ListSizingBehavior, Point, Stateful, UniformListScrollHandle, WeakEntity, transparent_black, uniform_list,}; use crate::{ ActiveTheme as _, AnyElement, App, Button, ButtonCommon as _, ButtonStyle, Color, Component, ComponentScope, Div, ElementId, FixedWidth as _, FluentBuilder as _, Indicator, InteractiveElement, IntoElement, ParentElement, Pixels, RegisterComponent, RenderOnce, ScrollableHandle, Scrollbars, SharedString, StatefulInteractiveElement, Styled, StyledExt as _, StyledTypography, Window, WithScrollbar, div, example_group_with_title, h_flex, px, single_example, v_flex,};use itertools::intersperse_with; const RESIZE_COLUMN_WIDTH: f32 = 8.0; #[derive(Debug)]struct DraggedColumn(usize); struct UniformListData<const COLS: usize> { render_item_fn: Box<dyn Fn(Range<usize>, &mut Window, &mut App) -> Vec<[AnyElement; COLS]>>, element_id: ElementId, row_count: usize,} enum TableContents<const COLS: usize> { Vec(Vec<[AnyElement; COLS]>), UniformList(UniformListData<COLS>),} impl<const COLS: usize> TableContents<COLS> { fn rows_mut(&mut self) -> Option<&mut Vec<[AnyElement; COLS]>> { match self { TableContents::Vec(rows) => Some(rows), TableContents::UniformList(_) => None, } } fn len(&self) -> usize { match self { TableContents::Vec(rows) => rows.len(), TableContents::UniformList(data) => data.row_count, } } fn is_empty(&self) -> bool { self.len() == 0 }} pub struct TableInteractionState { pub focus_handle: FocusHandle, pub scroll_handle: UniformListScrollHandle, pub custom_scrollbar: Option<Scrollbars>,} impl TableInteractionState { pub fn new(cx: &mut App) -> Self { Self { focus_handle: cx.focus_handle(), scroll_handle: UniformListScrollHandle::new(), custom_scrollbar: None, } } pub fn with_custom_scrollbar(mut self, custom_scrollbar: Scrollbars) -> Self { self.custom_scrollbar = Some(custom_scrollbar); self } pub fn scroll_offset(&self) -> Point<Pixels> { self.scroll_handle.offset() } pub fn set_scroll_offset(&self, offset: Point<Pixels>) { self.scroll_handle.set_offset(offset); } pub fn listener<E: ?Sized>( this: &Entity<Self>, f: impl Fn(&mut Self, &E, &mut Window, &mut Context<Self>) + 'static, ) -> impl Fn(&E, &mut Window, &mut App) + 'static { let view = this.downgrade(); move |e: &E, window: &mut Window, cx: &mut App| { view.update(cx, |view, cx| f(view, e, window, cx)).ok(); } } fn render_resize_handles<const COLS: usize>( &self, column_widths: &[Length; COLS], resizable_columns: &[TableResizeBehavior; COLS], initial_sizes: [DefiniteLength; COLS], columns: Option<Entity<TableColumnWidths<COLS>>>, window: &mut Window, cx: &mut App, ) -> AnyElement { let spacers = column_widths .iter() .map(|width| base_cell_style(Some(*width)).into_any_element()); let mut column_ix = 0; let resizable_columns_slice = *resizable_columns; let mut resizable_columns = resizable_columns.iter(); let dividers = intersperse_with(spacers, || { window.with_id(column_ix, |window| { let mut resize_divider = div() // This is required because this is evaluated at a different time than the use_state call above .id(column_ix) .relative() .top_0() .w_px() .h_full() .bg(cx.theme().colors().border.opacity(0.8)); let mut resize_handle = div() .id("column-resize-handle") .absolute() .left_neg_0p5() .w(px(RESIZE_COLUMN_WIDTH)) .h_full(); if resizable_columns .next() .is_some_and(TableResizeBehavior::is_resizable) { let hovered = window.use_state(cx, |_window, _cx| false); resize_divider = resize_divider.when(*hovered.read(cx), |div| { div.bg(cx.theme().colors().border_focused) }); resize_handle = resize_handle .on_hover(move |&was_hovered, _, cx| hovered.write(cx, was_hovered)) .cursor_col_resize() .when_some(columns.clone(), |this, columns| { this.on_click(move |event, window, cx| { if event.click_count() >= 2 { columns.update(cx, |columns, _| { columns.on_double_click( column_ix, &initial_sizes, &resizable_columns_slice, window, ); }) } cx.stop_propagation(); }) }) .on_drag(DraggedColumn(column_ix), |_, _offset, _window, cx| { cx.new(|_cx| gpui::Empty) }) } column_ix += 1; resize_divider.child(resize_handle).into_any_element() }) }); h_flex() .id("resize-handles") .absolute() .inset_0() .w_full() .children(dividers) .into_any_element() }} #[derive(Debug, Copy, Clone, PartialEq)]pub enum TableResizeBehavior { None, Resizable, MinSize(f32),} impl TableResizeBehavior { pub fn is_resizable(&self) -> bool { *self != TableResizeBehavior::None } pub fn min_size(&self) -> Option<f32> { match self { TableResizeBehavior::None => None, TableResizeBehavior::Resizable => Some(0.05), TableResizeBehavior::MinSize(min_size) => Some(*min_size), } }} pub struct TableColumnWidths<const COLS: usize> { widths: [DefiniteLength; COLS], visible_widths: [DefiniteLength; COLS], cached_bounds_width: Pixels, initialized: bool,} impl<const COLS: usize> TableColumnWidths<COLS> { pub fn new(_: &mut App) -> Self { Self { widths: [DefiniteLength::default(); COLS], visible_widths: [DefiniteLength::default(); COLS], cached_bounds_width: Default::default(), initialized: false, } } fn get_fraction(length: &DefiniteLength, bounds_width: Pixels, rem_size: Pixels) -> f32 { match length { DefiniteLength::Absolute(AbsoluteLength::Pixels(pixels)) => *pixels / bounds_width, DefiniteLength::Absolute(AbsoluteLength::Rems(rems_width)) => { rems_width.to_pixels(rem_size) / bounds_width } DefiniteLength::Fraction(fraction) => *fraction, } } fn on_double_click( &mut self, double_click_position: usize, initial_sizes: &[DefiniteLength; COLS], resize_behavior: &[TableResizeBehavior; COLS], window: &mut Window, ) { let bounds_width = self.cached_bounds_width; let rem_size = window.rem_size(); let initial_sizes = initial_sizes.map(|length| Self::get_fraction(&length, bounds_width, rem_size)); let widths = self .widths .map(|length| Self::get_fraction(&length, bounds_width, rem_size)); let updated_widths = Self::reset_to_initial_size( double_click_position, widths, initial_sizes, resize_behavior, ); self.widths = updated_widths.map(DefiniteLength::Fraction); self.visible_widths = self.widths; } fn reset_to_initial_size( col_idx: usize, mut widths: [f32; COLS], initial_sizes: [f32; COLS], resize_behavior: &[TableResizeBehavior; COLS], ) -> [f32; COLS] { // RESET: // Part 1: // Figure out if we should shrink/grow the selected column // Get diff which represents the change in column we want to make initial size delta curr_size = diff // // Part 2: We need to decide which side column we should move and where // // If we want to grow our column we should check the left/right columns diff to see what side // has a greater delta than their initial size. Likewise, if we shrink our column we should check // the left/right column diffs to see what side has the smallest delta. // // Part 3: resize // // col_idx represents the column handle to the right of an active column // // If growing and right has the greater delta { // shift col_idx to the right // } else if growing and left has the greater delta { // shift col_idx - 1 to the left // } else if shrinking and the right has the greater delta { // shift // } { // // } // } // // if we need to shrink, then if the right // // DRAGGING // we get diff which represents the change in the _drag handle_ position // -diff => dragging left -> // grow the column to the right of the handle as much as we can shrink columns to the left of the handle // +diff => dragging right -> growing handles column // grow the column to the left of the handle as much as we can shrink columns to the right of the handle // let diff = initial_sizes[col_idx] - widths[col_idx]; let left_diff = initial_sizes[..col_idx].iter().sum::<f32>() - widths[..col_idx].iter().sum::<f32>(); let right_diff = initial_sizes[col_idx + 1..].iter().sum::<f32>() - widths[col_idx + 1..].iter().sum::<f32>(); let go_left_first = if diff < 0.0 { left_diff > right_diff } else { left_diff < right_diff }; if !go_left_first { let diff_remaining = Self::propagate_resize_diff(diff, col_idx, &mut widths, resize_behavior, 1); if diff_remaining != 0.0 && col_idx > 0 { Self::propagate_resize_diff( diff_remaining, col_idx, &mut widths, resize_behavior, -1, ); } } else { let diff_remaining = Self::propagate_resize_diff(diff, col_idx, &mut widths, resize_behavior, -1); if diff_remaining != 0.0 { Self::propagate_resize_diff( diff_remaining, col_idx, &mut widths, resize_behavior, 1, ); } } widths } fn on_drag_move( &mut self, drag_event: &DragMoveEvent<DraggedColumn>, resize_behavior: &[TableResizeBehavior; COLS], window: &mut Window, cx: &mut Context<Self>, ) { let drag_position = drag_event.event.position; let bounds = drag_event.bounds; let mut col_position = 0.0; let rem_size = window.rem_size(); let bounds_width = bounds.right() - bounds.left(); let col_idx = drag_event.drag(cx).0; let column_handle_width = Self::get_fraction( &DefiniteLength::Absolute(AbsoluteLength::Pixels(px(RESIZE_COLUMN_WIDTH))), bounds_width, rem_size, ); let mut widths = self .widths .map(|length| Self::get_fraction(&length, bounds_width, rem_size)); for length in widths[0..=col_idx].iter() { col_position += length + column_handle_width; } let mut total_length_ratio = col_position; for length in widths[col_idx + 1..].iter() { total_length_ratio += length; } total_length_ratio += (COLS - 1 - col_idx) as f32 * column_handle_width; let drag_fraction = (drag_position.x - bounds.left()) / bounds_width; let drag_fraction = drag_fraction * total_length_ratio; let diff = drag_fraction - col_position - column_handle_width / 2.0; Self::drag_column_handle(diff, col_idx, &mut widths, resize_behavior); self.visible_widths = widths.map(DefiniteLength::Fraction); } fn drag_column_handle( diff: f32, col_idx: usize, widths: &mut [f32; COLS], resize_behavior: &[TableResizeBehavior; COLS], ) { // if diff > 0.0 then go right if diff > 0.0 { Self::propagate_resize_diff(diff, col_idx, widths, resize_behavior, 1); } else { Self::propagate_resize_diff(-diff, col_idx + 1, widths, resize_behavior, -1); } } fn propagate_resize_diff( diff: f32, col_idx: usize, widths: &mut [f32; COLS], resize_behavior: &[TableResizeBehavior; COLS], direction: i8, ) -> f32 { let mut diff_remaining = diff; if resize_behavior[col_idx].min_size().is_none() { return diff; } let step_right; let step_left; if direction < 0 { step_right = 0; step_left = 1; } else { step_right = 1; step_left = 0; } if col_idx == 0 && direction < 0 { return diff; } let mut curr_column = col_idx + step_right - step_left; while diff_remaining != 0.0 && curr_column < COLS { let Some(min_size) = resize_behavior[curr_column].min_size() else { if curr_column == 0 { break; } curr_column -= step_left; curr_column += step_right; continue; }; let curr_width = widths[curr_column] - diff_remaining; widths[curr_column] = curr_width; if min_size > curr_width { diff_remaining = min_size - curr_width; widths[curr_column] = min_size; } else { diff_remaining = 0.0; break; } if curr_column == 0 { break; } curr_column -= step_left; curr_column += step_right; } widths[col_idx] = widths[col_idx] + (diff - diff_remaining); diff_remaining }} pub struct TableWidths<const COLS: usize> { initial: [DefiniteLength; COLS], current: Option<Entity<TableColumnWidths<COLS>>>, resizable: [TableResizeBehavior; COLS],} impl<const COLS: usize> TableWidths<COLS> { pub fn new(widths: [impl Into<DefiniteLength>; COLS]) -> Self { let widths = widths.map(Into::into); TableWidths { initial: widths, current: None, resizable: [TableResizeBehavior::None; COLS], } } fn lengths(&self, cx: &App) -> [Length; COLS] { self.current .as_ref() .map(|entity| entity.read(cx).visible_widths.map(Length::Definite)) .unwrap_or(self.initial.map(Length::Definite)) }} /// A table component#[derive(RegisterComponent, IntoElement)]pub struct Table<const COLS: usize = 3> { striped: bool, width: Option<Length>, headers: Option<[AnyElement; COLS]>, rows: TableContents<COLS>, interaction_state: Option<WeakEntity<TableInteractionState>>, col_widths: Option<TableWidths<COLS>>, map_row: Option<Rc<dyn Fn((usize, Stateful<Div>), &mut Window, &mut App) -> AnyElement>>, empty_table_callback: Option<Rc<dyn Fn(&mut Window, &mut App) -> AnyElement>>,} impl<const COLS: usize> Table<COLS> { /// number of headers provided. pub fn new() -> Self { Self { striped: false, width: None, headers: None, rows: TableContents::Vec(Vec::new()), interaction_state: None, map_row: None,Lines 1–500 of 1376. Load more
use std::{ops::Range, rc::Rc}; use gpui::{ AbsoluteLength, AppContext, Context, DefiniteLength, DragMoveEvent, Entity, EntityId, FocusHandle, Length, ListHorizontalSizingBehavior, ListSizingBehavior, Point, Stateful, UniformListScrollHandle, WeakEntity, transparent_black, uniform_list, }; use crate::{ ActiveTheme as _, AnyElement, App, Button, ButtonCommon as _, ButtonStyle, Color, Component, ComponentScope, Div, ElementId, FixedWidth as _, FluentBuilder as _, Indicator, InteractiveElement, IntoElement, ParentElement, Pixels, RegisterComponent, RenderOnce, ScrollableHandle, Scrollbars, SharedString, StatefulInteractiveElement, Styled, StyledExt as _, StyledTypography, Window, WithScrollbar, div, example_group_with_title, h_flex, px, single_example, v_flex, }; use itertools::intersperse_with; const RESIZE_COLUMN_WIDTH: f32 = 8.0; #[derive(Debug)] struct DraggedColumn(usize); struct UniformListData<const COLS: usize> { render_item_fn: Box<dyn Fn(Range<usize>, &mut Window, &mut App) -> Vec<[AnyElement; COLS]>>, element_id: ElementId, row_count: usize, } enum TableContents<const COLS: usize> { Vec(Vec<[AnyElement; COLS]>), UniformList(UniformListData<COLS>), } impl<const COLS: usize> TableContents<COLS> { fn rows_mut(&mut self) -> Option<&mut Vec<[AnyElement; COLS]>> { match self { TableContents::Vec(rows) => Some(rows), TableContents::UniformList(_) => None, } } fn len(&self) -> usize { match self { TableContents::Vec(rows) => rows.len(), TableContents::UniformList(data) => data.row_count, } } fn is_empty(&self) -> bool { self.len() == 0 } } pub struct TableInteractionState { pub focus_handle: FocusHandle, pub scroll_handle: UniformListScrollHandle, pub custom_scrollbar: Option<Scrollbars>, } impl TableInteractionState { pub fn new(cx: &mut App) -> Self { Self { focus_handle: cx.focus_handle(), scroll_handle: UniformListScrollHandle::new(), custom_scrollbar: None, } } pub fn with_custom_scrollbar(mut self, custom_scrollbar: Scrollbars) -> Self { self.custom_scrollbar = Some(custom_scrollbar); self } pub fn scroll_offset(&self) -> Point<Pixels> { self.scroll_handle.offset() } pub fn set_scroll_offset(&self, offset: Point<Pixels>) { self.scroll_handle.set_offset(offset); } pub fn listener<E: ?Sized>( this: &Entity<Self>, f: impl Fn(&mut Self, &E, &mut Window, &mut Context<Self>) + 'static, ) -> impl Fn(&E, &mut Window, &mut App) + 'static { let view = this.downgrade(); move |e: &E, window: &mut Window, cx: &mut App| { view.update(cx, |view, cx| f(view, e, window, cx)).ok(); } } fn render_resize_handles<const COLS: usize>( &self, column_widths: &[Length; COLS], resizable_columns: &[TableResizeBehavior; COLS], initial_sizes: [DefiniteLength; COLS], columns: Option<Entity<TableColumnWidths<COLS>>>, window: &mut Window, cx: &mut App, ) -> AnyElement { let spacers = column_widths .iter() .map(|width| base_cell_style(Some(*width)).into_any_element()); let mut column_ix = 0; let resizable_columns_slice = *resizable_columns; let mut resizable_columns = resizable_columns.iter(); let dividers = intersperse_with(spacers, || { window.with_id(column_ix, |window| { let mut resize_divider = div() // This is required because this is evaluated at a different time than the use_state call above .id(column_ix) .relative() .top_0() .w_px() .h_full() .bg(cx.theme().colors().border.opacity(0.8)); let mut resize_handle = div() .id("column-resize-handle") .absolute() .left_neg_0p5() .w(px(RESIZE_COLUMN_WIDTH)) .h_full(); if resizable_columns .next() .is_some_and(TableResizeBehavior::is_resizable) { let hovered = window.use_state(cx, |_window, _cx| false); resize_divider = resize_divider.when(*hovered.read(cx), |div| { div.bg(cx.theme().colors().border_focused) }); resize_handle = resize_handle .on_hover(move |&was_hovered, _, cx| hovered.write(cx, was_hovered)) .cursor_col_resize() .when_some(columns.clone(), |this, columns| { this.on_click(move |event, window, cx| { if event.click_count() >= 2 { columns.update(cx, |columns, _| { columns.on_double_click( column_ix, &initial_sizes, &resizable_columns_slice, window, ); }) } cx.stop_propagation(); }) }) .on_drag(DraggedColumn(column_ix), |_, _offset, _window, cx| { cx.new(|_cx| gpui::Empty) }) } column_ix += 1; resize_divider.child(resize_handle).into_any_element() }) }); h_flex() .id("resize-handles") .absolute() .inset_0() .w_full() .children(dividers) .into_any_element() } } #[derive(Debug, Copy, Clone, PartialEq)] pub enum TableResizeBehavior { None, Resizable, MinSize(f32), } impl TableResizeBehavior { pub fn is_resizable(&self) -> bool { *self != TableResizeBehavior::None } pub fn min_size(&self) -> Option<f32> { match self { TableResizeBehavior::None => None, TableResizeBehavior::Resizable => Some(0.05), TableResizeBehavior::MinSize(min_size) => Some(*min_size), } } } pub struct TableColumnWidths<const COLS: usize> { widths: [DefiniteLength; COLS], visible_widths: [DefiniteLength; COLS], cached_bounds_width: Pixels, initialized: bool, } impl<const COLS: usize> TableColumnWidths<COLS> { pub fn new(_: &mut App) -> Self { Self { widths: [DefiniteLength::default(); COLS], visible_widths: [DefiniteLength::default(); COLS], cached_bounds_width: Default::default(), initialized: false, } } fn get_fraction(length: &DefiniteLength, bounds_width: Pixels, rem_size: Pixels) -> f32 { match length { DefiniteLength::Absolute(AbsoluteLength::Pixels(pixels)) => *pixels / bounds_width, DefiniteLength::Absolute(AbsoluteLength::Rems(rems_width)) => { rems_width.to_pixels(rem_size) / bounds_width } DefiniteLength::Fraction(fraction) => *fraction, } } fn on_double_click( &mut self, double_click_position: usize, initial_sizes: &[DefiniteLength; COLS], resize_behavior: &[TableResizeBehavior; COLS], window: &mut Window, ) { let bounds_width = self.cached_bounds_width; let rem_size = window.rem_size(); let initial_sizes = initial_sizes.map(|length| Self::get_fraction(&length, bounds_width, rem_size)); let widths = self .widths .map(|length| Self::get_fraction(&length, bounds_width, rem_size)); let updated_widths = Self::reset_to_initial_size( double_click_position, widths, initial_sizes, resize_behavior, ); self.widths = updated_widths.map(DefiniteLength::Fraction); self.visible_widths = self.widths; } fn reset_to_initial_size( col_idx: usize, mut widths: [f32; COLS], initial_sizes: [f32; COLS], resize_behavior: &[TableResizeBehavior; COLS], ) -> [f32; COLS] { // RESET: // Part 1: // Figure out if we should shrink/grow the selected column // Get diff which represents the change in column we want to make initial size delta curr_size = diff // // Part 2: We need to decide which side column we should move and where // // If we want to grow our column we should check the left/right columns diff to see what side // has a greater delta than their initial size. Likewise, if we shrink our column we should check // the left/right column diffs to see what side has the smallest delta. // // Part 3: resize // // col_idx represents the column handle to the right of an active column // // If growing and right has the greater delta { // shift col_idx to the right // } else if growing and left has the greater delta { // shift col_idx - 1 to the left // } else if shrinking and the right has the greater delta { // shift // } { // // } // } // // if we need to shrink, then if the right // // DRAGGING // we get diff which represents the change in the _drag handle_ position // -diff => dragging left -> // grow the column to the right of the handle as much as we can shrink columns to the left of the handle // +diff => dragging right -> growing handles column // grow the column to the left of the handle as much as we can shrink columns to the right of the handle // let diff = initial_sizes[col_idx] - widths[col_idx]; let left_diff = initial_sizes[..col_idx].iter().sum::<f32>() - widths[..col_idx].iter().sum::<f32>(); let right_diff = initial_sizes[col_idx + 1..].iter().sum::<f32>() - widths[col_idx + 1..].iter().sum::<f32>(); let go_left_first = if diff < 0.0 { left_diff > right_diff } else { left_diff < right_diff }; if !go_left_first { let diff_remaining = Self::propagate_resize_diff(diff, col_idx, &mut widths, resize_behavior, 1); if diff_remaining != 0.0 && col_idx > 0 { Self::propagate_resize_diff( diff_remaining, col_idx, &mut widths, resize_behavior, -1, ); } } else { let diff_remaining = Self::propagate_resize_diff(diff, col_idx, &mut widths, resize_behavior, -1); if diff_remaining != 0.0 { Self::propagate_resize_diff( diff_remaining, col_idx, &mut widths, resize_behavior, 1, ); } } widths } fn on_drag_move( &mut self, drag_event: &DragMoveEvent<DraggedColumn>, resize_behavior: &[TableResizeBehavior; COLS], window: &mut Window, cx: &mut Context<Self>, ) { let drag_position = drag_event.event.position; let bounds = drag_event.bounds; let mut col_position = 0.0; let rem_size = window.rem_size(); let bounds_width = bounds.right() - bounds.left(); let col_idx = drag_event.drag(cx).0; let column_handle_width = Self::get_fraction( &DefiniteLength::Absolute(AbsoluteLength::Pixels(px(RESIZE_COLUMN_WIDTH))), bounds_width, rem_size, ); let mut widths = self .widths .map(|length| Self::get_fraction(&length, bounds_width, rem_size)); for length in widths[0..=col_idx].iter() { col_position += length + column_handle_width; } let mut total_length_ratio = col_position; for length in widths[col_idx + 1..].iter() { total_length_ratio += length; } total_length_ratio += (COLS - 1 - col_idx) as f32 * column_handle_width; let drag_fraction = (drag_position.x - bounds.left()) / bounds_width; let drag_fraction = drag_fraction * total_length_ratio; let diff = drag_fraction - col_position - column_handle_width / 2.0; Self::drag_column_handle(diff, col_idx, &mut widths, resize_behavior); self.visible_widths = widths.map(DefiniteLength::Fraction); } fn drag_column_handle( diff: f32, col_idx: usize, widths: &mut [f32; COLS], resize_behavior: &[TableResizeBehavior; COLS], ) { // if diff > 0.0 then go right if diff > 0.0 { Self::propagate_resize_diff(diff, col_idx, widths, resize_behavior, 1); } else { Self::propagate_resize_diff(-diff, col_idx + 1, widths, resize_behavior, -1); } } fn propagate_resize_diff( diff: f32, col_idx: usize, widths: &mut [f32; COLS], resize_behavior: &[TableResizeBehavior; COLS], direction: i8, ) -> f32 { let mut diff_remaining = diff; if resize_behavior[col_idx].min_size().is_none() { return diff; } let step_right; let step_left; if direction < 0 { step_right = 0; step_left = 1; } else { step_right = 1; step_left = 0; } if col_idx == 0 && direction < 0 { return diff; } let mut curr_column = col_idx + step_right - step_left; while diff_remaining != 0.0 && curr_column < COLS { let Some(min_size) = resize_behavior[curr_column].min_size() else { if curr_column == 0 { break; } curr_column -= step_left; curr_column += step_right; continue; }; let curr_width = widths[curr_column] - diff_remaining; widths[curr_column] = curr_width; if min_size > curr_width { diff_remaining = min_size - curr_width; widths[curr_column] = min_size; } else { diff_remaining = 0.0; break; } if curr_column == 0 { break; } curr_column -= step_left; curr_column += step_right; } widths[col_idx] = widths[col_idx] + (diff - diff_remaining); diff_remaining } } pub struct TableWidths<const COLS: usize> { initial: [DefiniteLength; COLS], current: Option<Entity<TableColumnWidths<COLS>>>, resizable: [TableResizeBehavior; COLS], } impl<const COLS: usize> TableWidths<COLS> { pub fn new(widths: [impl Into<DefiniteLength>; COLS]) -> Self { let widths = widths.map(Into::into); TableWidths { initial: widths, current: None, resizable: [TableResizeBehavior::None; COLS], } } fn lengths(&self, cx: &App) -> [Length; COLS] { self.current .as_ref() .map(|entity| entity.read(cx).visible_widths.map(Length::Definite)) .unwrap_or(self.initial.map(Length::Definite)) } } /// A table component #[derive(RegisterComponent, IntoElement)] pub struct Table<const COLS: usize = 3> { striped: bool, width: Option<Length>, headers: Option<[AnyElement; COLS]>, rows: TableContents<COLS>, interaction_state: Option<WeakEntity<TableInteractionState>>, col_widths: Option<TableWidths<COLS>>, map_row: Option<Rc<dyn Fn((usize, Stateful<Div>), &mut Window, &mut App) -> AnyElement>>, empty_table_callback: Option<Rc<dyn Fn(&mut Window, &mut App) -> AnyElement>>, } impl<const COLS: usize> Table<COLS> { /// number of headers provided. pub fn new() -> Self { Self { striped: false, width: None, headers: None, rows: TableContents::Vec(Vec::new()), interaction_state: None, map_row: None,