diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs index 4dcca70c..36a4e5d6 100644 --- a/src/app/layout_manager.rs +++ b/src/app/layout_manager.rs @@ -1,6 +1,7 @@ use crate::{ app::{ - text_table::Column, DiskTable, MemGraph, NetGraph, OldNetGraph, ProcessManager, TempTable, + sort_text_table::SortableColumn, DiskTable, MemGraph, NetGraph, OldNetGraph, + ProcessManager, TempTable, }, error::{BottomError, Result}, options::layout_options::{Row, RowChildren}, @@ -14,7 +15,9 @@ use typed_builder::*; use crate::app::widgets::Widget; use crate::constants::DEFAULT_WIDGET_ID; -use super::{event::SelectionAction, CpuGraph, TextTable, TimeGraph, TmpBottomWidget, UsedWidgets}; +use super::{ + event::SelectionAction, CpuGraph, SortableTextTable, TimeGraph, TmpBottomWidget, UsedWidgets, +}; /// Represents a more usable representation of the layout, derived from the /// config. @@ -1058,9 +1061,9 @@ pub fn create_layout_tree( match widget_type { BottomWidgetType::Cpu => { let graph = TimeGraph::from_config(app_config_fields); - let legend = TextTable::new(vec![ - Column::new_flex("CPU", None, false, 0.5), - Column::new_flex("Use%", None, false, 0.5), + let legend = SortableTextTable::new(vec![ + SortableColumn::new_flex("CPU".into(), None, false, 0.5), + SortableColumn::new_flex("Use%".into(), None, false, 0.5), ]); let legend_position = super::CpuGraphLegendPosition::Right; diff --git a/src/app/widgets/base.rs b/src/app/widgets/base.rs index 851baf24..d32ea72e 100644 --- a/src/app/widgets/base.rs +++ b/src/app/widgets/base.rs @@ -3,6 +3,9 @@ pub mod text_table; pub use text_table::TextTable; +pub mod sort_text_table; +pub use sort_text_table::SortableTextTable; + pub mod time_graph; pub use time_graph::TimeGraph; diff --git a/src/app/widgets/base/sort_text_table.rs b/src/app/widgets/base/sort_text_table.rs new file mode 100644 index 00000000..0c282d49 --- /dev/null +++ b/src/app/widgets/base/sort_text_table.rs @@ -0,0 +1,275 @@ +use std::borrow::Cow; + +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}; +use tui::{ + layout::Rect, + widgets::{Table, TableState}, +}; + +use crate::app::{event::EventResult, Component, TextTable}; + +use super::text_table::{DesiredColumnWidth, SimpleColumn, TableColumn}; + +fn get_shortcut_name(e: &KeyEvent) -> String { + let modifier = if e.modifiers.is_empty() { + "" + } else if let KeyModifiers::ALT = e.modifiers { + "Alt+" + } else if let KeyModifiers::SHIFT = e.modifiers { + "Shift+" + } else if let KeyModifiers::CONTROL = e.modifiers { + "Ctrl+" + } else { + // For now, that's all we support, though combos/more could be added. + "" + }; + + let key: Cow<'static, str> = match e.code { + KeyCode::Backspace => "Backspace".into(), + KeyCode::Enter => "Enter".into(), + KeyCode::Left => "Left".into(), + KeyCode::Right => "Right".into(), + KeyCode::Up => "Up".into(), + KeyCode::Down => "Down".into(), + KeyCode::Home => "Home".into(), + KeyCode::End => "End".into(), + KeyCode::PageUp => "PgUp".into(), + KeyCode::PageDown => "PgDown".into(), + KeyCode::Tab => "Tab".into(), + KeyCode::BackTab => "BackTab".into(), + KeyCode::Delete => "Del".into(), + KeyCode::Insert => "Insert".into(), + KeyCode::F(num) => format!("F{}", num).into(), + KeyCode::Char(c) => format!("{}", c).into(), + KeyCode::Null => "Null".into(), + KeyCode::Esc => "Esc".into(), + }; + + format!("({}{})", modifier, key).into() +} + +#[derive(Debug)] +enum SortStatus { + NotSorting, + SortAscending, + SortDescending, +} + +/// A [`SortableColumn`] represents some column in a [`SortableTextTable`]. +#[derive(Debug)] +pub struct SortableColumn { + pub shortcut: Option<(KeyEvent, String)>, + pub default_descending: bool, + pub internal: SimpleColumn, + sorting: SortStatus, +} + +impl SortableColumn { + /// Creates a new [`SortableColumn`]. + fn new( + shortcut_name: Cow<'static, str>, shortcut: Option, default_descending: bool, + desired_width: DesiredColumnWidth, + ) -> Self { + let shortcut = shortcut.map(|e| (e, get_shortcut_name(&e))); + Self { + shortcut, + default_descending, + internal: SimpleColumn::new(shortcut_name, desired_width), + sorting: SortStatus::NotSorting, + } + } + + /// Creates a new [`SortableColumn`] with a hard desired width. If none is specified, + /// it will instead use the name's length + 1. + pub fn new_hard( + name: Cow<'static, str>, shortcut: Option, default_descending: bool, + hard_length: Option, + ) -> Self { + let shortcut_name = if let Some(shortcut) = shortcut { + get_shortcut_name(&shortcut).into() + } else { + name + }; + let shortcut_name_len = shortcut_name.len(); + + SortableColumn::new( + shortcut_name, + shortcut, + default_descending, + DesiredColumnWidth::Hard(hard_length.unwrap_or(shortcut_name_len as u16 + 1)), + ) + } + + /// Creates a new [`SortableColumn`] with a flexible desired width. + pub fn new_flex( + name: Cow<'static, str>, shortcut: Option, default_descending: bool, + max_percentage: f64, + ) -> Self { + let shortcut_name = if let Some(shortcut) = shortcut { + get_shortcut_name(&shortcut).into() + } else { + name + }; + let shortcut_name_len = shortcut_name.len(); + + SortableColumn::new( + shortcut_name, + shortcut, + default_descending, + DesiredColumnWidth::Flex { + desired: shortcut_name_len as u16, + max_percentage, + }, + ) + } +} + +impl TableColumn for SortableColumn { + fn display_name(&self) -> Cow<'static, str> { + const UP_ARROW: &'static str = "▲"; + const DOWN_ARROW: &'static str = "▼"; + format!( + "{}{}", + self.internal.display_name(), + match &self.sorting { + SortStatus::NotSorting => "", + SortStatus::SortAscending => UP_ARROW, + SortStatus::SortDescending => DOWN_ARROW, + } + ) + .into() + } + + fn get_desired_width(&self) -> &DesiredColumnWidth { + self.internal.get_desired_width() + } + + fn get_x_bounds(&self) -> Option<(u16, u16)> { + self.internal.get_x_bounds() + } + + fn set_x_bounds(&mut self, x_bounds: Option<(u16, u16)>) { + self.internal.set_x_bounds(x_bounds) + } +} + +/// A sortable, scrollable table with columns. +pub struct SortableTextTable { + /// Which index we're sorting by. + sort_index: usize, + + /// The underlying [`TextTable`]. + pub table: TextTable, +} + +impl SortableTextTable { + pub fn new(columns: Vec) -> Self { + let mut st = Self { + sort_index: 0, + table: TextTable::new(columns), + }; + st.set_sort_index(0); + st + } + + pub fn default_ltr(mut self, ltr: bool) -> Self { + self.table = self.table.default_ltr(ltr); + self + } + + pub fn default_sort_index(mut self, index: usize) -> Self { + self.set_sort_index(index); + self + } + + fn set_sort_index(&mut self, new_index: usize) { + if new_index == self.sort_index { + if let Some(column) = self.table.columns.get_mut(self.sort_index) { + match column.sorting { + SortStatus::NotSorting => { + if column.default_descending { + column.sorting = SortStatus::SortDescending; + } else { + column.sorting = SortStatus::SortAscending; + } + } + SortStatus::SortAscending => { + column.sorting = SortStatus::SortDescending; + } + SortStatus::SortDescending => { + column.sorting = SortStatus::SortAscending; + } + } + } + } else { + if let Some(column) = self.table.columns.get_mut(self.sort_index) { + column.sorting = SortStatus::NotSorting; + } + + if let Some(column) = self.table.columns.get_mut(new_index) { + if column.default_descending { + column.sorting = SortStatus::SortDescending; + } else { + column.sorting = SortStatus::SortAscending; + } + } + + self.sort_index = new_index; + } + } + + /// Creates a [`Table`] representing the sort list. + pub fn create_sort_list(&mut self) -> (Table<'_>, TableState) { + todo!() + } +} + +impl Component for SortableTextTable { + fn handle_key_event(&mut self, event: KeyEvent) -> EventResult { + for (index, column) in self.table.columns.iter().enumerate() { + if let Some((shortcut, _)) = column.shortcut { + if shortcut == event { + self.set_sort_index(index); + return EventResult::Redraw; + } + } + } + + self.table.scrollable.handle_key_event(event) + } + + fn handle_mouse_event(&mut self, event: MouseEvent) -> EventResult { + if let MouseEventKind::Down(MouseButton::Left) = event.kind { + if !self.does_intersect_mouse(&event) { + return EventResult::NoRedraw; + } + + // Note these are representing RELATIVE coordinates! They *need* the above intersection check for validity! + let x = event.column - self.table.bounds.left(); + let y = event.row - self.table.bounds.top(); + + if y == 0 { + for (index, column) in self.table.columns.iter().enumerate() { + if let Some((start, end)) = column.internal.get_x_bounds() { + if x >= start && x <= end { + self.set_sort_index(index); + return EventResult::Redraw; + } + } + } + } + + self.table.scrollable.handle_mouse_event(event) + } else { + self.table.scrollable.handle_mouse_event(event) + } + } + + fn bounds(&self) -> Rect { + self.table.bounds + } + + fn set_bounds(&mut self, new_bounds: Rect) { + self.table.bounds = new_bounds; + } +} diff --git a/src/app/widgets/base/text_table.rs b/src/app/widgets/base/text_table.rs index 59395578..0edda286 100644 --- a/src/app/widgets/base/text_table.rs +++ b/src/app/widgets/base/text_table.rs @@ -1,9 +1,9 @@ use std::{ borrow::Cow, - cmp::{max, min, Ordering}, + cmp::{max, min}, }; -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}; +use crossterm::event::{KeyEvent, MouseEvent}; use tui::{ layout::{Constraint, Rect}, text::Text, @@ -24,110 +24,79 @@ pub enum DesiredColumnWidth { Flex { desired: u16, max_percentage: f64 }, } -/// A [`ColumnType`] is a -pub trait ColumnType { - type DataType; +/// A trait that must be implemented for anything using a [`TextTable`]. +#[allow(unused_variables)] +pub trait TableColumn { + fn display_name(&self) -> Cow<'static, str>; - fn sort_function(a: Self::DataType, b: Self::DataType) -> Ordering; + fn get_desired_width(&self) -> &DesiredColumnWidth; + + fn get_x_bounds(&self) -> Option<(u16, u16)>; + + fn set_x_bounds(&mut self, x_bounds: Option<(u16, u16)>); } -/// A [`Column`] represents some column in a [`TextTable`]. +/// A [`SimpleColumn`] represents some column in a [`TextTable`]. #[derive(Debug)] -pub struct Column { - pub name: &'static str, - pub shortcut: Option<(KeyEvent, String)>, - pub default_descending: bool, +pub struct SimpleColumn { + name: Cow<'static, str>, // TODO: I would remove these in the future, storing them here feels weird... - pub desired_width: DesiredColumnWidth, - pub x_bounds: Option<(u16, u16)>, + desired_width: DesiredColumnWidth, + x_bounds: Option<(u16, u16)>, } -impl Column { - /// Creates a new [`Column`]. - pub fn new( - name: &'static str, shortcut: Option, default_descending: bool, - desired_width: DesiredColumnWidth, - ) -> Self { +impl SimpleColumn { + /// Creates a new [`SimpleColumn`]. + pub fn new(name: Cow<'static, str>, desired_width: DesiredColumnWidth) -> Self { Self { name, x_bounds: None, - shortcut: shortcut.map(|e| { - let modifier = if e.modifiers.is_empty() { - "" - } else if let KeyModifiers::ALT = e.modifiers { - "Alt+" - } else if let KeyModifiers::SHIFT = e.modifiers { - "Shift+" - } else if let KeyModifiers::CONTROL = e.modifiers { - "Ctrl+" - } else { - // For now, that's all we support, though combos/more could be added. - "" - }; - - let key: Cow<'static, str> = match e.code { - KeyCode::Backspace => "Backspace".into(), - KeyCode::Enter => "Enter".into(), - KeyCode::Left => "Left".into(), - KeyCode::Right => "Right".into(), - KeyCode::Up => "Up".into(), - KeyCode::Down => "Down".into(), - KeyCode::Home => "Home".into(), - KeyCode::End => "End".into(), - KeyCode::PageUp => "PgUp".into(), - KeyCode::PageDown => "PgDown".into(), - KeyCode::Tab => "Tab".into(), - KeyCode::BackTab => "BackTab".into(), - KeyCode::Delete => "Del".into(), - KeyCode::Insert => "Insert".into(), - KeyCode::F(num) => format!("F{}", num).into(), - KeyCode::Char(c) => format!("{}", c).into(), - KeyCode::Null => "Null".into(), - KeyCode::Esc => "Esc".into(), - }; - - let shortcut_name = format!("({}{})", modifier, key); - - (e, shortcut_name) - }), - default_descending, desired_width, } } - /// Creates a new [`Column`] with a hard desired width. If none is specified, + /// Creates a new [`SimpleColumn`] with a hard desired width. If none is specified, /// it will instead use the name's length + 1. - pub fn new_hard( - name: &'static str, shortcut: Option, default_descending: bool, - hard_length: Option, - ) -> Self { - // TODO: It should really be based on the shortcut name... - Column::new( + pub fn new_hard(name: Cow<'static, str>, hard_length: Option) -> Self { + let name_len = name.len(); + SimpleColumn::new( name, - shortcut, - default_descending, - DesiredColumnWidth::Hard(hard_length.unwrap_or(name.len() as u16 + 1)), + DesiredColumnWidth::Hard(hard_length.unwrap_or(name_len as u16 + 1)), ) } - /// Creates a new [`Column`] with a flexible desired width. - pub fn new_flex( - name: &'static str, shortcut: Option, default_descending: bool, - max_percentage: f64, - ) -> Self { - Column::new( + /// Creates a new [`SimpleColumn`] with a flexible desired width. + pub fn new_flex(name: Cow<'static, str>, max_percentage: f64) -> Self { + let name_len = name.len(); + SimpleColumn::new( name, - shortcut, - default_descending, DesiredColumnWidth::Flex { - desired: name.len() as u16, + desired: name_len as u16, max_percentage, }, ) } } +impl TableColumn for SimpleColumn { + fn display_name(&self) -> Cow<'static, str> { + self.name.clone() + } + + fn get_desired_width(&self) -> &DesiredColumnWidth { + &self.desired_width + } + + fn get_x_bounds(&self) -> Option<(u16, u16)> { + self.x_bounds + } + + fn set_x_bounds(&mut self, x_bounds: Option<(u16, u16)>) { + self.x_bounds = x_bounds; + } +} + #[derive(Clone)] enum CachedColumnWidths { Uncached, @@ -138,47 +107,45 @@ enum CachedColumnWidths { } /// A sortable, scrollable table with columns. -pub struct TextTable { +pub struct TextTable +where + C: TableColumn, +{ /// Controls the scrollable state. - scrollable: Scrollable, + pub scrollable: Scrollable, /// The columns themselves. - columns: Vec, + pub columns: Vec, /// Cached column width data. cached_column_widths: CachedColumnWidths, /// Whether to show a gap between the column headers and the columns. - show_gap: bool, + pub show_gap: bool, /// The bounding box of the [`TextTable`]. - bounds: Rect, // TODO: Consider moving bounds to something else??? - - /// Which index we're sorting by. - sort_index: usize, - - /// Whether we're sorting by ascending order. - sort_ascending: bool, + pub bounds: Rect, // TODO: Consider moving bounds to something else??? /// Whether we draw columns from left-to-right. - left_to_right: bool, + pub left_to_right: bool, } -impl TextTable { - pub fn new(columns: Vec) -> Self { +impl TextTable +where + C: TableColumn, +{ + pub fn new(columns: Vec) -> Self { Self { scrollable: Scrollable::new(0), columns, cached_column_widths: CachedColumnWidths::Uncached, show_gap: true, bounds: Rect::default(), - sort_index: 0, - sort_ascending: true, left_to_right: true, } } - pub fn left_to_right(mut self, ltr: bool) -> Self { + pub fn default_ltr(mut self, ltr: bool) -> Self { self.left_to_right = ltr; self } @@ -188,50 +155,10 @@ impl TextTable { self } - pub fn sort_index(mut self, sort_index: usize) -> Self { - self.sort_index = sort_index; - self - } - - pub fn column_names(&self) -> Vec<&'static str> { - self.columns.iter().map(|column| column.name).collect() - } - - pub fn sorted_column_names(&self) -> Vec { - const UP_ARROW: char = '▲'; - const DOWN_ARROW: char = '▼'; - + pub fn displayed_column_names(&self) -> Vec> { self.columns .iter() - .enumerate() - .map(|(index, column)| { - if index == self.sort_index { - format!( - "{}{}{}", - column.name, - if let Some(shortcut) = &column.shortcut { - shortcut.1.as_str() - } else { - "" - }, - if self.sort_ascending { - UP_ARROW - } else { - DOWN_ARROW - } - ) - } else { - format!( - "{}{}", - column.name, - if let Some(shortcut) = &column.shortcut { - shortcut.1.as_str() - } else { - "" - } - ) - } - }) + .map(|column| column.display_name()) .collect() } @@ -239,19 +166,19 @@ impl TextTable { self.scrollable.update_num_items(num_items); } - pub fn update_a_column(&mut self, index: usize, column: Column) { + pub fn update_single_column(&mut self, index: usize, column: C) { if let Some(c) = self.columns.get_mut(index) { *c = column; } } pub fn get_desired_column_widths( - columns: &[Column], data: &[Vec<(Cow<'static, str>, Option>)>], + columns: &[C], data: &[Vec<(Cow<'static, str>, Option>)>], ) -> Vec { columns .iter() .enumerate() - .map(|(column_index, c)| match c.desired_width { + .map(|(column_index, c)| match c.get_desired_width() { DesiredColumnWidth::Hard(width) => { let max_len = data .iter() @@ -274,12 +201,12 @@ impl TextTable { .map(|(s, _)| s.len()) .unwrap_or(0) as u16; - DesiredColumnWidth::Hard(max(max_len, width)) + DesiredColumnWidth::Hard(max(max_len, *width)) } DesiredColumnWidth::Flex { desired: _, max_percentage: _, - } => c.desired_width.clone(), + } => c.get_desired_width().clone(), }) .collect::>() } @@ -390,7 +317,7 @@ impl TextTable { let mut column_start = 0; for (column, width) in self.columns.iter_mut().zip(&column_widths) { let column_end = column_start + *width; - column.x_bounds = Some((column_start, column_end)); + column.set_x_bounds(Some((column_start, column_end))); column_start = column_end + 1; } } @@ -468,7 +395,7 @@ impl TextTable { }); // Now build up our headers... - let header = Row::new(self.sorted_column_names()) + let header = Row::new(self.displayed_column_names()) .style(painter.colours.table_header_style) .bottom_margin(table_gap); @@ -483,59 +410,23 @@ impl TextTable { tui_state, ) } + + /// Creates a [`Table`] representing the sort list. + pub fn create_sort_list(&mut self) -> (Table<'_>, TableState) { + todo!() + } } -impl Component for TextTable { +impl Component for TextTable +where + C: TableColumn, +{ fn handle_key_event(&mut self, event: KeyEvent) -> EventResult { - for (index, column) in self.columns.iter().enumerate() { - if let Some((shortcut, _)) = column.shortcut { - if shortcut == event { - if self.sort_index == index { - // Just flip the sort if we're already sorting by this. - self.sort_ascending = !self.sort_ascending; - } else { - self.sort_index = index; - self.sort_ascending = !column.default_descending; - } - return EventResult::Redraw; - } - } - } - self.scrollable.handle_key_event(event) } fn handle_mouse_event(&mut self, event: MouseEvent) -> EventResult { - if let MouseEventKind::Down(MouseButton::Left) = event.kind { - if !self.does_intersect_mouse(&event) { - return EventResult::NoRedraw; - } - - // Note these are representing RELATIVE coordinates! They *need* the above intersection check for validity! - let x = event.column - self.bounds.left(); - let y = event.row - self.bounds.top(); - - if y == 0 { - for (index, column) in self.columns.iter().enumerate() { - if let Some((start, end)) = column.x_bounds { - if x >= start && x <= end { - if self.sort_index == index { - // Just flip the sort if we're already sorting by this. - self.sort_ascending = !self.sort_ascending; - } else { - self.sort_index = index; - self.sort_ascending = !column.default_descending; - } - return EventResult::Redraw; - } - } - } - } - - self.scrollable.handle_mouse_event(event) - } else { - self.scrollable.handle_mouse_event(event) - } + self.scrollable.handle_mouse_event(event) } fn bounds(&self) -> Rect { diff --git a/src/app/widgets/cpu.rs b/src/app/widgets/cpu.rs index 50994c02..15fe9efe 100644 --- a/src/app/widgets/cpu.rs +++ b/src/app/widgets/cpu.rs @@ -5,7 +5,9 @@ use tui::layout::Rect; use crate::app::event::EventResult; -use super::{AppScrollWidgetState, CanvasTableWidthState, Component, TextTable, TimeGraph, Widget}; +use super::{ + AppScrollWidgetState, CanvasTableWidthState, Component, SortableTextTable, TimeGraph, Widget, +}; pub struct CpuWidgetState { pub current_display_time: u64, @@ -67,7 +69,7 @@ pub enum CpuGraphLegendPosition { /// A widget designed to show CPU usage via a graph, along with a side legend implemented as a [`TextTable`]. pub struct CpuGraph { graph: TimeGraph, - legend: TextTable, + legend: SortableTextTable, pub legend_position: CpuGraphLegendPosition, bounds: Rect, @@ -77,7 +79,7 @@ pub struct CpuGraph { impl CpuGraph { /// Creates a new [`CpuGraph`]. pub fn new( - graph: TimeGraph, legend: TextTable, legend_position: CpuGraphLegendPosition, + graph: TimeGraph, legend: SortableTextTable, legend_position: CpuGraphLegendPosition, ) -> Self { Self { graph, diff --git a/src/app/widgets/disk.rs b/src/app/widgets/disk.rs index ebf3ca47..a9ceaaaa 100644 --- a/src/app/widgets/disk.rs +++ b/src/app/widgets/disk.rs @@ -9,11 +9,11 @@ use tui::{ }; use crate::{ - app::{event::EventResult, text_table::Column}, + app::{event::EventResult, sort_text_table::SortableColumn}, canvas::{DisplayableData, Painter}, }; -use super::{AppScrollWidgetState, CanvasTableWidthState, Component, TextTable, Widget}; +use super::{AppScrollWidgetState, CanvasTableWidthState, Component, SortableTextTable, Widget}; pub struct DiskWidgetState { pub scroll_state: AppScrollWidgetState, @@ -50,20 +50,20 @@ impl DiskState { /// A table displaying disk data. Essentially a wrapper around a [`TextTable`]. pub struct DiskTable { - table: TextTable, + table: SortableTextTable, bounds: Rect, } impl Default for DiskTable { fn default() -> Self { - let table = TextTable::new(vec![ - Column::new_flex("Disk", None, false, 0.2), - Column::new_flex("Mount", None, false, 0.2), - Column::new_hard("Used", None, false, Some(4)), - Column::new_hard("Free", None, false, Some(6)), - Column::new_hard("Total", None, false, Some(6)), - Column::new_hard("R/s", None, false, Some(7)), - Column::new_hard("W/s", None, false, Some(7)), + let table = SortableTextTable::new(vec![ + SortableColumn::new_flex("Disk".into(), None, false, 0.2), + SortableColumn::new_flex("Mount".into(), None, false, 0.2), + SortableColumn::new_hard("Used".into(), None, false, Some(5)), + SortableColumn::new_hard("Free".into(), None, false, Some(6)), + SortableColumn::new_hard("Total".into(), None, false, Some(6)), + SortableColumn::new_hard("R/s".into(), None, false, Some(7)), + SortableColumn::new_hard("W/s".into(), None, false, Some(7)), ]); Self { @@ -112,6 +112,7 @@ impl Widget for DiskTable { let draw_area = block.inner(area); let (table, widths, mut tui_state) = self.table + .table .create_draw_table(painter, &data.disk_data, draw_area); let table = table.highlight_style(if selected { diff --git a/src/app/widgets/process.rs b/src/app/widgets/process.rs index a4abcd9a..d643072c 100644 --- a/src/app/widgets/process.rs +++ b/src/app/widgets/process.rs @@ -23,7 +23,7 @@ use ProcessSorting::*; use super::{ AppScrollWidgetState, CanvasTableWidthState, Component, CursorDirection, ScrollDirection, - TextInput, TextTable, Widget, + SortableTextTable, TextInput, TextTable, Widget, }; /// AppSearchState deals with generic searching (I might do this in the future). @@ -640,7 +640,7 @@ struct SearchModifiers { /// A searchable, sortable table to manage processes. pub struct ProcessManager { bounds: Rect, - process_table: TextTable, + process_table: SortableTextTable, sort_table: TextTable, search_input: TextInput, @@ -662,8 +662,8 @@ impl ProcessManager { let mut manager = Self { bounds: Rect::default(), - process_table: TextTable::new(process_table_columns), // TODO: Do this - sort_table: TextTable::new(vec![]), // TODO: Do this too + process_table: SortableTextTable::new(process_table_columns), // TODO: Do this + sort_table: TextTable::new(vec![]), // TODO: Do this too search_input: TextInput::new(), dd_multi: MultiKey::register(vec!['d', 'd']), // TODO: Maybe use something static... selected: ProcessManagerSelection::Processes, @@ -854,7 +854,7 @@ impl Widget for ProcessManager { self.set_bounds(area); let draw_area = block.inner(area); - let (process_table, widths, mut tui_state) = self.process_table.create_draw_table( + let (process_table, widths, mut tui_state) = self.process_table.table.create_draw_table( painter, &vec![], // TODO: Fix this draw_area, diff --git a/src/app/widgets/temp.rs b/src/app/widgets/temp.rs index 9b1e2214..9cbe412f 100644 --- a/src/app/widgets/temp.rs +++ b/src/app/widgets/temp.rs @@ -9,11 +9,11 @@ use tui::{ }; use crate::{ - app::{event::EventResult, text_table::Column}, + app::{event::EventResult, sort_text_table::SortableColumn}, canvas::{DisplayableData, Painter}, }; -use super::{AppScrollWidgetState, CanvasTableWidthState, Component, TextTable, Widget}; +use super::{AppScrollWidgetState, CanvasTableWidthState, Component, SortableTextTable, Widget}; pub struct TempWidgetState { pub scroll_state: AppScrollWidgetState, @@ -50,17 +50,17 @@ impl TempState { /// A table displaying disk data. Essentially a wrapper around a [`TextTable`]. pub struct TempTable { - table: TextTable, + table: SortableTextTable, bounds: Rect, } impl Default for TempTable { fn default() -> Self { - let table = TextTable::new(vec![ - Column::new_flex("Sensor", None, false, 0.8), - Column::new_hard("Temp", None, false, Some(4)), + let table = SortableTextTable::new(vec![ + SortableColumn::new_flex("Sensor".into(), None, false, 0.8), + SortableColumn::new_hard("Temp".into(), None, false, Some(5)), ]) - .left_to_right(false); + .default_ltr(false); Self { table, @@ -108,6 +108,7 @@ impl Widget for TempTable { let draw_area = block.inner(area); let (table, widths, mut tui_state) = self.table + .table .create_draw_table(painter, &data.temp_sensor_data, draw_area); let table = table.highlight_style(if selected {