From 7ee6f6a7373c915bde9e0c3860456b0aba6fdc8c Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sat, 7 May 2022 03:38:55 -0400 Subject: refactor: begin migration of process widget --- src/canvas/components/text_table.rs | 206 +++++++++-- src/canvas/dialogs/dd_dialog.rs | 10 +- src/canvas/drawing_utils.rs | 256 ------------- src/canvas/widgets/cpu_graph.rs | 4 +- src/canvas/widgets/process_table.rs | 703 ++++++++++-------------------------- 5 files changed, 385 insertions(+), 794 deletions(-) (limited to 'src/canvas') diff --git a/src/canvas/components/text_table.rs b/src/canvas/components/text_table.rs index cf9788ad..f75a5eb3 100644 --- a/src/canvas/components/text_table.rs +++ b/src/canvas/components/text_table.rs @@ -1,4 +1,7 @@ -use std::{borrow::Cow, cmp::min}; +use std::{ + borrow::Cow, + cmp::{max, min}, +}; use concat_string::concat_string; use tui::{ @@ -12,9 +15,12 @@ use tui::{ use unicode_segmentation::UnicodeSegmentation; use crate::{ - app::{self, TableComponentState}, + app::{ + self, CellContent, SortState, TableComponentColumn, TableComponentHeader, + TableComponentState, + }, constants::{SIDE_BORDERS, TABLE_GAP_HEIGHT_LIMIT}, - data_conversion::{CellContent, TableData, TableRow}, + data_conversion::{TableData, TableRow}, }; pub struct TextTableTitle<'a> { @@ -101,8 +107,8 @@ impl<'a> TextTable<'a> { } }) } - pub fn draw_text_table( - &self, f: &mut Frame<'_, B>, draw_loc: Rect, state: &mut TableComponentState, + pub fn draw_text_table( + &self, f: &mut Frame<'_, B>, draw_loc: Rect, state: &mut TableComponentState, table_data: &TableData, ) { // TODO: This is a *really* ugly hack to get basic mode to hide the border when not selected, without shifting everything. @@ -179,7 +185,7 @@ impl<'a> TextTable<'a> { desired, max_percentage: _, } => { - *desired = std::cmp::max(column.name.len(), *data_width) as u16; + *desired = max(column.header.header_text().len(), *data_width) as u16; } app::WidthBounds::Hard(_width) => {} }); @@ -188,15 +194,9 @@ impl<'a> TextTable<'a> { } let columns = &state.columns; - let header = Row::new(columns.iter().filter_map(|c| { - if c.calculated_width == 0 { - None - } else { - Some(truncate_text(&c.name, c.calculated_width.into(), None)) - } - })) - .style(self.header_style) - .bottom_margin(table_gap); + let header = build_header(columns, &state.sort_state) + .style(self.header_style) + .bottom_margin(table_gap); let table_rows = sliced_vec.iter().map(|row| { let (row, style) = match row { TableRow::Raw(row) => (row, None), @@ -245,9 +245,60 @@ impl<'a> TextTable<'a> { } } +/// Constructs the table header. +fn build_header<'a, H: TableComponentHeader>( + columns: &'a [TableComponentColumn], sort_state: &SortState, +) -> Row<'a> { + use itertools::Either; + + const UP_ARROW: &str = "▲"; + const DOWN_ARROW: &str = "▼"; + + let iter = match sort_state { + SortState::Unsortable => Either::Left(columns.iter().filter_map(|c| { + if c.calculated_width == 0 { + None + } else { + Some(truncate_text( + c.header.header_text(), + c.calculated_width.into(), + None, + )) + } + })), + SortState::Sortable { index, order } => { + let arrow = match order { + app::SortOrder::Ascending => UP_ARROW, + app::SortOrder::Descending => DOWN_ARROW, + }; + + Either::Right(columns.iter().enumerate().filter_map(move |(itx, c)| { + if c.calculated_width == 0 { + None + } else if itx == *index { + Some(truncate_suffixed_text( + c.header.header_text(), + arrow, + c.calculated_width.into(), + None, + )) + } else { + Some(truncate_text( + c.header.header_text(), + c.calculated_width.into(), + None, + )) + } + })) + } + }; + + Row::new(iter) +} + /// Truncates text if it is too long, and adds an ellipsis at the end if needed. -fn truncate_text(content: &CellContent, width: usize, row_style: Option