From 8f790d0b756828e4e46385b521478db5a2270b73 Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sun, 26 Dec 2021 18:05:56 -0500 Subject: [PATCH] Sorting --- src/app.rs | 4 +- .../component/base/text_table/data_row.rs | 9 ++ src/tuine/component/base/text_table/mod.rs | 140 ++++++++++++------ src/tuine/component/base/text_table/props.rs | 4 +- .../component/base/text_table/table_column.rs | 12 -- src/tuine/context/draw_context.rs | 2 +- 6 files changed, 107 insertions(+), 64 deletions(-) diff --git a/src/app.rs b/src/app.rs index f02df4e3..0d8dcddf 100644 --- a/src/app.rs +++ b/src/app.rs @@ -262,7 +262,9 @@ impl Application for AppState { )), FlexElement::new(TextTable::build( ctx, - TextTableProps::new(vec!["L", "EM", "NO", "PQ"]), + TextTableProps::new(vec!["L", "EM", "NO", "PQ"]) + .rows(vec![vec![1, 2, 3, 4], vec![4, 3, 2, 1]]) + .default_sort(crate::tuine::SortType::Descending(0)), )), ]), 2, diff --git a/src/tuine/component/base/text_table/data_row.rs b/src/tuine/component/base/text_table/data_row.rs index 0d7ee8ba..4af5153f 100644 --- a/src/tuine/component/base/text_table/data_row.rs +++ b/src/tuine/component/base/text_table/data_row.rs @@ -35,6 +35,15 @@ impl DataRow { } } +impl> From> for DataRow { + fn from(vec: Vec) -> Self { + Self { + cells: vec.into_iter().map(Into::into).collect(), + style: None, + } + } +} + impl From for Row<'_> { fn from(row: DataRow) -> Self { if let Some(style) = row.style { diff --git a/src/tuine/component/base/text_table/mod.rs b/src/tuine/component/base/text_table/mod.rs index 59e200a2..3542334f 100644 --- a/src/tuine/component/base/text_table/mod.rs +++ b/src/tuine/component/base/text_table/mod.rs @@ -59,40 +59,6 @@ pub struct TextTable { on_selected_click: Option Message>>, } -impl StatefulComponent for TextTable { - type Properties = TextTableProps; - - type ComponentState = TextTableState; - - #[track_caller] - fn build(ctx: &mut crate::tuine::ViewContext<'_>, mut props: Self::Properties) -> Self { - let sort = props.sort; - let (key, state) = ctx.register_and_mut_state_with_default::<_, Self::ComponentState, _>( - Location::caller(), - || TextTableState { - scroll: Default::default(), - sort, - }, - ); - - state.scroll.set_num_items(props.rows.len()); - props.try_sort_data(state.sort); - - TextTable { - key, - column_widths: props.column_widths, - columns: props.columns, - show_gap: props.show_gap, - show_selected_entry: props.show_selected_entry, - rows: props.rows, - style_sheet: props.style_sheet, - table_gap: props.table_gap, - on_select: props.on_select, - on_selected_click: props.on_selected_click, - } - } -} - impl TextTable { fn update_column_widths(&mut self, bounds: Rect) { let total_width = bounds.width; @@ -143,6 +109,40 @@ impl TextTable { } } +impl StatefulComponent for TextTable { + type Properties = TextTableProps; + + type ComponentState = TextTableState; + + #[track_caller] + fn build(ctx: &mut crate::tuine::ViewContext<'_>, mut props: Self::Properties) -> Self { + let sort = props.sort; + let (key, state) = ctx.register_and_mut_state_with_default::<_, Self::ComponentState, _>( + Location::caller(), + || TextTableState { + scroll: Default::default(), + sort, + }, + ); + + state.scroll.set_num_items(props.rows.len()); + props.try_sort_data(state.sort); + + TextTable { + key, + column_widths: props.column_widths, + columns: props.columns, + show_gap: props.show_gap, + show_selected_entry: props.show_selected_entry, + rows: props.rows, + style_sheet: props.style_sheet, + table_gap: props.table_gap, + on_select: props.on_select, + on_selected_click: props.on_selected_click, + } + } +} + impl TmpComponent for TextTable { fn draw( &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, @@ -150,7 +150,7 @@ impl TmpComponent for TextTable { ) where B: Backend, { - let rect = draw_ctx.rect(); + let rect = draw_ctx.global_rect(); let state = state_ctx.mut_state::(self.key); state.scroll.set_num_items(self.rows.len()); // FIXME: Not a fan of this system like this - should be easier to do. @@ -182,7 +182,6 @@ impl TmpComponent for TextTable { .display_start_index(rect, scrollable_height as usize); let end = min(state.scroll.num_items(), start + scrollable_height as usize); - debug!("Start: {}, end: {}", start, end); self.rows.drain(start..end).into_iter().map(|row| { let r: Row<'_> = row.into(); r @@ -212,7 +211,7 @@ impl TmpComponent for TextTable { use crate::tuine::MouseBoundIntersect; use crossterm::event::{MouseButton, MouseEventKind}; - let rect = draw_ctx.rect(); + let rect = draw_ctx.global_rect(); let state = state_ctx.mut_state::(self.key); match event { @@ -230,24 +229,69 @@ impl TmpComponent for TextTable { match mouse_event.kind { MouseEventKind::Down(MouseButton::Left) => { let y = mouse_event.row - rect.top(); - if y == 0 { + let x = mouse_event.column - rect.left(); match state.sort { SortType::Unsortable => Status::Ignored, - SortType::Ascending(column) => { - // Sort by the clicked column! If already using column, reverse! - // self.sort_data(); - todo!() - } - SortType::Descending(column) => { - // Sort by the clicked column! If already using column, reverse! - // self.sort_data(); - todo!() + SortType::Ascending(column) | SortType::Descending(column) => { + let mut cursor = 0; + for (selected_column, width) in + self.column_widths.iter().enumerate() + { + let end = cursor + width; + + if x >= cursor && x <= end { + match state.sort { + SortType::Ascending(_) => { + if selected_column == column { + // FIXME: This should handle default sorting orders... + state.sort = SortType::Descending( + selected_column, + ); + } else { + state.sort = SortType::Ascending( + selected_column, + ); + } + } + SortType::Descending(_) => { + if selected_column == column { + // FIXME: This should handle default sorting orders... + state.sort = SortType::Ascending( + selected_column, + ); + } else { + state.sort = SortType::Descending( + selected_column, + ); + } + } + SortType::Unsortable => unreachable!(), // Should be impossible by above check. + } + + return Status::Captured; + } else { + cursor += width; + } + } + Status::Ignored } } } else if y > self.table_gap { let visual_index = usize::from(y - self.table_gap); - state.scroll.set_visual_index(visual_index) + match state.scroll.set_visual_index(visual_index) { + Status::Captured => Status::Captured, + Status::Ignored => { + if let Some(on_selected_click) = &self.on_selected_click { + messages.push(on_selected_click( + state.scroll.current_index(), + )); + Status::Captured + } else { + Status::Ignored + } + } + } } else { Status::Ignored } diff --git a/src/tuine/component/base/text_table/props.rs b/src/tuine/component/base/text_table/props.rs index ae436066..360aaa1c 100644 --- a/src/tuine/component/base/text_table/props.rs +++ b/src/tuine/component/base/text_table/props.rs @@ -39,8 +39,8 @@ impl TextTableProps { /// Sets the row to display in the table. /// /// Defaults to displaying no data if not set. - pub fn rows(mut self, rows: Vec) -> Self { - self.rows = rows; + pub fn rows>(mut self, rows: Vec) -> Self { + self.rows = rows.into_iter().map(Into::into).collect(); self } diff --git a/src/tuine/component/base/text_table/table_column.rs b/src/tuine/component/base/text_table/table_column.rs index 4ab430b7..9fc972c8 100644 --- a/src/tuine/component/base/text_table/table_column.rs +++ b/src/tuine/component/base/text_table/table_column.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; pub struct TextColumn { pub name: Cow<'static, str>, pub width_constraint: TextColumnConstraint, - x_bounds: Option<(u16, u16)>, } pub enum TextColumnConstraint { @@ -29,7 +28,6 @@ impl TextColumn { Self { name: name.into(), width_constraint: TextColumnConstraint::Fill, - x_bounds: None, } } @@ -37,14 +35,4 @@ impl TextColumn { self.width_constraint = width_constraint; self } - - /// Set the text column's x bounds. - pub(crate) fn set_x_bounds(&mut self, x_bounds: Option<(u16, u16)>) { - self.x_bounds = x_bounds; - } - - /// Get the text column's x-coordinates. - pub fn x_bounds(&self) -> Option<(u16, u16)> { - self.x_bounds - } } diff --git a/src/tuine/context/draw_context.rs b/src/tuine/context/draw_context.rs index 97c885dc..954d6dad 100644 --- a/src/tuine/context/draw_context.rs +++ b/src/tuine/context/draw_context.rs @@ -16,7 +16,7 @@ impl<'a> DrawContext<'a> { } } - pub(crate) fn rect(&self) -> Rect { + pub(crate) fn global_rect(&self) -> Rect { let mut rect = self.current_node.rect; rect.x += self.current_offset.0; rect.y += self.current_offset.1;