refactor: separate out sorted and non-sorted text tables

This commit is contained in:
ClementTsang 2021-08-28 01:17:46 -04:00
parent 6b69e373de
commit b72e76aa71
8 changed files with 397 additions and 221 deletions

View File

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

View File

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

View File

@ -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<KeyEvent>, 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<KeyEvent>, default_descending: bool,
hard_length: Option<u16>,
) -> 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<KeyEvent>, 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<SortableColumn>,
}
impl SortableTextTable {
pub fn new(columns: Vec<SortableColumn>) -> 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;
}
}

View File

@ -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<KeyEvent>, 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<KeyEvent>, default_descending: bool,
hard_length: Option<u16>,
) -> Self {
// TODO: It should really be based on the shortcut name...
Column::new(
pub fn new_hard(name: Cow<'static, str>, hard_length: Option<u16>) -> 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<KeyEvent>, 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<C = SimpleColumn>
where
C: TableColumn,
{
/// Controls the scrollable state.
scrollable: Scrollable,
pub scrollable: Scrollable,
/// The columns themselves.
columns: Vec<Column>,
pub columns: Vec<C>,
/// 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<Column>) -> Self {
impl<C> TextTable<C>
where
C: TableColumn,
{
pub fn new(columns: Vec<C>) -> 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<String> {
const UP_ARROW: char = '▲';
const DOWN_ARROW: char = '▼';
pub fn displayed_column_names(&self) -> Vec<Cow<'static, str>> {
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<Cow<'static, str>>)>],
columns: &[C], data: &[Vec<(Cow<'static, str>, Option<Cow<'static, str>>)>],
) -> Vec<DesiredColumnWidth> {
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::<Vec<_>>()
}
@ -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<C> Component for TextTable<C>
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 {

View File

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

View File

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

View File

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

View File

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