diff options
author | Clement Tsang <34804052+ClementTsang@users.noreply.github.com> | 2020-09-09 21:51:52 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-09 21:51:52 -0400 |
commit | c58b2c2bb9b0013d4f0fddbf39df5c7a6efa9bc4 (patch) | |
tree | de70c812920489228ca463c120b735fc855b5600 /src/canvas/widgets | |
parent | c426b0c7c4b30b09a63cfdcd3bb41a7d7d9ad00c (diff) |
refactor: rewrite column algorithm (#227)
Update how we position and generate column widths to look less terrible. This also adds truncation w/ ellipsis to the columns, and for processes, the state will automatically shrink to a short form (just a character) if there isn't enough space.
Diffstat (limited to 'src/canvas/widgets')
-rw-r--r-- | src/canvas/widgets/cpu_graph.rs | 76 | ||||
-rw-r--r-- | src/canvas/widgets/disk_table.rs | 114 | ||||
-rw-r--r-- | src/canvas/widgets/network_graph.rs | 25 | ||||
-rw-r--r-- | src/canvas/widgets/process_table.rs | 281 | ||||
-rw-r--r-- | src/canvas/widgets/temp_table.rs | 95 |
5 files changed, 458 insertions, 133 deletions
diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs index 86999345..97ba064d 100644 --- a/src/canvas/widgets/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -1,11 +1,10 @@ use lazy_static::lazy_static; use std::borrow::Cow; -use std::cmp::max; use crate::{ app::{layout_manager::WidgetDirection, App}, canvas::{ - drawing_utils::{get_start_position, get_variable_intrinsic_widths}, + drawing_utils::{get_column_widths, get_start_position}, Painter, }, constants::*, @@ -20,19 +19,14 @@ use tui::{ widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table}, }; -const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show"]; const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"]; const AVG_POSITION: usize = 1; const ALL_POSITION: usize = 0; lazy_static! { - static ref CPU_LEGEND_HEADER_LENS: Vec<usize> = CPU_LEGEND_HEADER + static ref CPU_LEGEND_HEADER_LENS: Vec<u16> = CPU_LEGEND_HEADER .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) - .collect::<Vec<_>>(); - static ref CPU_SELECT_LEGEND_HEADER_LENS: Vec<usize> = CPU_SELECT_LEGEND_HEADER - .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) + .map(|entry| entry.len() as u16) .collect::<Vec<_>>(); } @@ -273,6 +267,7 @@ impl CpuGraphWidget for Painter { fn draw_cpu_legend<B: Backend>( &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64, ) { + let recalculate_column_widths = app_state.should_get_widget_bounds(); if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&(widget_id - 1)) { cpu_widget_state.is_legend_hidden = false; @@ -308,11 +303,54 @@ impl CpuGraphWidget for Painter { .saturating_sub(start_position); let show_avg_cpu = app_state.app_config_fields.show_average_cpu; + // Calculate widths + if recalculate_column_widths { + cpu_widget_state.table_width_state.desired_column_widths = vec![6, 4]; + cpu_widget_state.table_width_state.calculated_column_widths = get_column_widths( + draw_loc.width, + &[None, None], + &(CPU_LEGEND_HEADER_LENS + .iter() + .map(|width| Some(*width)) + .collect::<Vec<_>>()), + &[Some(0.5), Some(0.5)], + &(cpu_widget_state + .table_width_state + .desired_column_widths + .iter() + .map(|width| Some(*width)) + .collect::<Vec<_>>()), + false, + ); + } + + let dcw = &cpu_widget_state.table_width_state.desired_column_widths; + let ccw = &cpu_widget_state.table_width_state.calculated_column_widths; let cpu_rows = sliced_cpu_data.iter().enumerate().filter_map(|(itx, cpu)| { - let cpu_string_row: Vec<Cow<'_, str>> = vec![ - Cow::Borrowed(&cpu.cpu_name), - Cow::Borrowed(&cpu.legend_value), - ]; + let truncated_name: Cow<'_, str> = + if let (Some(desired_column_width), Some(calculated_column_width)) = + (dcw.get(0), ccw.get(0)) + { + if *desired_column_width > *calculated_column_width { + Cow::Borrowed(&cpu.short_cpu_name) + } else { + Cow::Borrowed(&cpu.cpu_name) + } + } else { + Cow::Borrowed(&cpu.cpu_name) + }; + let truncated_legend: Cow<'_, str> = + if let Some(calculated_column_width) = ccw.get(0) { + if *calculated_column_width == 0 && cpu.legend_value.is_empty() { + Cow::Borrowed("All") + } else { + Cow::Borrowed(&cpu.legend_value) + } + } else { + Cow::Borrowed(&cpu.legend_value) + }; + + let cpu_string_row: Vec<Cow<'_, str>> = vec![truncated_name, truncated_legend]; if cpu_string_row.is_empty() { offset_scroll_index += 1; @@ -341,14 +379,6 @@ impl CpuGraphWidget for Painter { } }); - // Calculate widths - let width = f64::from(draw_loc.width); - let width_ratios = vec![0.5, 0.5]; - - let variable_intrinsic_results = - get_variable_intrinsic_widths(width as u16, &width_ratios, &CPU_LEGEND_HEADER_LENS); - let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; - // Note we don't set highlight_style, as it should always be shown for this widget. let border_and_title_style = if is_on_widget { self.colours.highlighted_border_style @@ -367,7 +397,9 @@ impl CpuGraphWidget for Painter { .header_style(self.colours.table_header_style) .highlight_style(self.colours.currently_selected_text_style) .widths( - &(intrinsic_widths + &(cpu_widget_state + .table_width_state + .calculated_column_widths .iter() .map(|calculated_width| Constraint::Length(*calculated_width as u16)) .collect::<Vec<_>>()), diff --git a/src/canvas/widgets/disk_table.rs b/src/canvas/widgets/disk_table.rs index f791500b..440687df 100644 --- a/src/canvas/widgets/disk_table.rs +++ b/src/canvas/widgets/disk_table.rs @@ -1,5 +1,4 @@ use lazy_static::lazy_static; -use std::cmp::max; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, @@ -10,18 +9,20 @@ use tui::{ use crate::{ app, canvas::{ - drawing_utils::{get_start_position, get_variable_intrinsic_widths}, + drawing_utils::{get_column_widths, get_start_position}, Painter, }, constants::*, }; +use std::borrow::Cow; +use unicode_segmentation::UnicodeSegmentation; const DISK_HEADERS: [&str; 7] = ["Disk", "Mount", "Used", "Free", "Total", "R/s", "W/s"]; lazy_static! { - static ref DISK_HEADERS_LENS: Vec<usize> = DISK_HEADERS + static ref DISK_HEADERS_LENS: Vec<u16> = DISK_HEADERS .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) + .map(|entry| entry.len() as u16) .collect::<Vec<_>>(); } @@ -37,6 +38,7 @@ impl DiskTableWidget for Painter { &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool, widget_id: u64, ) { + let recalculate_column_widths = app_state.should_get_widget_bounds(); if let Some(disk_widget_state) = app_state.disk_state.widget_states.get_mut(&widget_id) { let disk_data: &mut [Vec<String>] = &mut app_state.canvas_data.disk_data; let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { @@ -61,16 +63,100 @@ impl DiskTableWidget for Painter { .current_scroll_position .saturating_sub(start_position), )); - let sliced_vec = &mut disk_data[start_position..]; - let disk_rows = sliced_vec.iter().map(|disk| Row::Data(disk.iter())); + let sliced_vec = &disk_data[start_position..]; // Calculate widths - // TODO: [PRETTY] Ellipsis on strings? - let width = f64::from(draw_loc.width); - let width_ratios = [0.2, 0.15, 0.13, 0.13, 0.13, 0.13, 0.13]; - let variable_intrinsic_results = - get_variable_intrinsic_widths(width as u16, &width_ratios, &DISK_HEADERS_LENS); - let intrinsic_widths = &variable_intrinsic_results.0[0..variable_intrinsic_results.1]; + let hard_widths = [None, None, Some(4), Some(6), Some(6), Some(7), Some(7)]; + if recalculate_column_widths { + disk_widget_state.table_width_state.desired_column_widths = { + let mut column_widths = DISK_HEADERS_LENS.clone(); + for row in sliced_vec { + for (col, entry) in row.iter().enumerate() { + if entry.len() as u16 > column_widths[col] { + column_widths[col] = entry.len() as u16; + } + } + } + column_widths + }; + disk_widget_state.table_width_state.desired_column_widths = disk_widget_state + .table_width_state + .desired_column_widths + .iter() + .zip(&hard_widths) + .map(|(current, hard)| { + if let Some(hard) = hard { + if *hard > *current { + *hard + } else { + *current + } + } else { + *current + } + }) + .collect::<Vec<_>>(); + + disk_widget_state.table_width_state.calculated_column_widths = get_column_widths( + draw_loc.width, + &hard_widths, + &(DISK_HEADERS_LENS + .iter() + .map(|w| Some(*w)) + .collect::<Vec<_>>()), + &[Some(0.2), Some(0.2), None, None, None, None, None], + &(disk_widget_state + .table_width_state + .desired_column_widths + .iter() + .map(|w| Some(*w)) + .collect::<Vec<_>>()), + true, + ); + } + + let dcw = &disk_widget_state.table_width_state.desired_column_widths; + let ccw = &disk_widget_state.table_width_state.calculated_column_widths; + let disk_rows = + sliced_vec.iter().map(|disk_row| { + let truncated_data = disk_row.iter().zip(&hard_widths).enumerate().map( + |(itx, (entry, width))| { + if width.is_none() { + if let (Some(desired_col_width), Some(calculated_col_width)) = + (dcw.get(itx), ccw.get(itx)) + { + if *desired_col_width > *calculated_col_width + && *calculated_col_width > 0 + { + let graphemes = + UnicodeSegmentation::graphemes(entry.as_str(), true) + .collect::<Vec<&str>>(); + + if graphemes.len() > *calculated_col_width as usize + && *calculated_col_width > 1 + { + // Truncate with ellipsis + let first_n = graphemes + [..(*calculated_col_width as usize - 1)] + .concat(); + Cow::Owned(format!("{}…", first_n)) + } else { + Cow::Borrowed(entry) + } + } else { + Cow::Borrowed(entry) + } + } else { + Cow::Borrowed(entry) + } + } else { + Cow::Borrowed(entry) + } + }, + ); + + Row::Data(truncated_data) + }); // TODO: This seems to be bugged? The selected text style gets "stuck"? I think this gets fixed with tui 0.10? let (border_and_title_style, highlight_style) = if is_on_widget { @@ -148,7 +234,9 @@ impl DiskTableWidget for Painter { .highlight_style(highlight_style) .style(self.colours.text_style) .widths( - &(intrinsic_widths + &(disk_widget_state + .table_width_state + .calculated_column_widths .iter() .map(|calculated_width| Constraint::Length(*calculated_width as u16)) .collect::<Vec<_>>()), diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs index 32887c32..23c1927c 100644 --- a/src/canvas/widgets/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -3,7 +3,7 @@ use std::cmp::max; use crate::{ app::App, - canvas::{drawing_utils::get_variable_intrinsic_widths, Painter}, + canvas::{drawing_utils::get_column_widths, Painter}, constants::*, utils::gen_util::*, }; @@ -19,9 +19,9 @@ use tui::{ const NETWORK_HEADERS: [&str; 4] = ["RX", "TX", "Total RX", "Total TX"]; lazy_static! { - static ref NETWORK_HEADERS_LENS: Vec<usize> = NETWORK_HEADERS + static ref NETWORK_HEADERS_LENS: Vec<u16> = NETWORK_HEADERS .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) + .map(|entry| entry.len() as u16) .collect::<Vec<_>>(); } @@ -342,6 +342,7 @@ impl NetworkGraphWidget for Painter { } } + // TODO: [DEPRECATED] Get rid of this in, like, 0.6...? fn draw_network_labels<B: Backend>( &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64, ) { @@ -362,13 +363,17 @@ impl NetworkGraphWidget for Painter { .map(|val| Row::StyledData(val.iter(), self.colours.text_style)); // Calculate widths - let width_ratios: Vec<f64> = vec![0.25, 0.25, 0.25, 0.25]; - let lens: &[usize] = &NETWORK_HEADERS_LENS; - let width = f64::from(draw_loc.width); - - let variable_intrinsic_results = - get_variable_intrinsic_widths(width as u16, &width_ratios, lens); - let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; + let intrinsic_widths = get_column_widths( + draw_loc.width, + &[None, None, None, None], + &[Some(6); 4], + &[Some(0.25); 4], + &(NETWORK_HEADERS_LENS + .iter() + .map(|s| Some(*s)) + .collect::<Vec<_>>()), + true, + ); // Draw f.render_widget( diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs index cce7835c..ad878263 100644 --- a/src/canvas/widgets/process_table.rs +++ b/src/canvas/widgets/process_table.rs @@ -1,9 +1,7 @@ use crate::{ - app::{data_harvester::processes::ProcessSorting, App}, + app::App, canvas::{ - drawing_utils::{ - get_search_start_position, get_start_position, get_variable_intrinsic_widths, - }, + drawing_utils::{get_column_widths, get_search_start_position, get_start_position}, Painter, }, constants::*, @@ -16,6 +14,7 @@ use tui::{ widgets::{Block, Borders, Paragraph, Row, Table, Text}, }; +use std::borrow::Cow; use unicode_segmentation::{GraphemeIndices, UnicodeSegmentation}; use unicode_width::UnicodeWidthStr; @@ -108,7 +107,14 @@ impl ProcessTableWidget for Painter { &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool, widget_id: u64, ) { + let should_get_widget_bounds = app_state.should_get_widget_bounds(); if let Some(proc_widget_state) = app_state.proc_state.widget_states.get_mut(&widget_id) { + let recalculate_column_widths = + should_get_widget_bounds || proc_widget_state.requires_redraw; + if proc_widget_state.requires_redraw { + proc_widget_state.requires_redraw = false; + } + let is_on_widget = widget_id == app_state.current_widget.widget_id; let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)].as_ref()) @@ -170,7 +176,7 @@ impl ProcessTableWidget for Painter { if let Some(process_data) = &app_state .canvas_data - .finalized_process_data_map + .stringified_process_data_map .get(&widget_id) { let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { @@ -197,6 +203,14 @@ impl ProcessTableWidget for Painter { }; let sliced_vec = &process_data[start_position..]; + let processed_sliced_vec = sliced_vec.iter().map(|(data, disabled)| { + ( + data.iter() + .map(|(entry, _alternative)| entry) + .collect::<Vec<_>>(), + disabled, + ) + }); let proc_table_state = &mut proc_widget_state.scroll_state.table_state; proc_table_state.select(Some( proc_widget_state @@ -206,85 +220,198 @@ impl ProcessTableWidget for Painter { )); // Draw! - let is_proc_widget_grouped = proc_widget_state.is_grouped; - let is_using_command = proc_widget_state.is_using_command; - let is_tree = proc_widget_state.is_tree_mode; - let mem_enabled = proc_widget_state.columns.is_enabled(&ProcessSorting::Mem); - - // FIXME: [PROC OPTIMIZE] This can definitely be optimized; string references work fine here! - let process_rows = sliced_vec.iter().map(|process| { - let data = vec![ - if is_proc_widget_grouped { - process.group_pids.len().to_string() - } else { - process.pid.to_string() - }, - if is_tree { - if let Some(prefix) = &process.process_description_prefix { - prefix.clone() - } else { - String::default() - } - } else if is_using_command { - process.command.clone() - } else { - process.name.clone() - }, - format!("{:.1}%", process.cpu_percent_usage), - if mem_enabled { - format!("{:.0}{}", process.mem_usage_str.0, process.mem_usage_str.1) - } else { - format!("{:.1}%", process.mem_percent_usage) - }, - process.read_per_sec.clone(), - process.write_per_sec.clone(), - process.total_read.clone(), - process.total_write.clone(), - process.process_state.clone(), - ] - .into_iter(); - - if process.is_disabled_entry { - Row::StyledData(data, self.colours.disabled_text_style) - } else { - Row::Data(data) - } - }); - let process_headers = proc_widget_state.columns.get_column_headers( &proc_widget_state.process_sorting_type, proc_widget_state.is_process_sort_descending, ); - let process_headers_lens: Vec<usize> = process_headers - .iter() - .map(|entry| entry.len()) - .collect::<Vec<_>>(); - // Calculate widths - let width = f64::from(draw_loc.width); + let hard_widths = if proc_widget_state.is_grouped { + vec![ + Some(7), + None, + Some(8), + Some(8), + Some(8), + Some(8), + Some(7), + Some(8), + ] + } else { + vec![ + Some(7), + None, + Some(8), + Some(8), + Some(8), + Some(8), + Some(7), + Some(8), + None, + ] + }; - // TODO: This is a ugly work-around for now. - let width_ratios = if proc_widget_state.is_grouped { - if proc_widget_state.is_using_command { - vec![0.05, 0.7, 0.05, 0.05, 0.0375, 0.0375, 0.0375, 0.0375] + if recalculate_column_widths { + let mut column_widths = process_headers + .iter() + .map(|entry| UnicodeWidthStr::width(entry.as_str()) as u16) + .collect::<Vec<_>>(); + let soft_widths_min = column_widths + .iter() + .map(|width| Some(*width)) + .collect::<Vec<_>>(); + + proc_widget_state.table_width_state.desired_column_widths = { + for (row, _disabled) in processed_sliced_vec.clone() { + for (col, entry) in row.iter().enumerate() { + if let Some(col_width) = column_widths.get_mut(col) { + let grapheme_len = UnicodeWidthStr::width(entry.as_str()); + if grapheme_len as u16 > *col_width { + *col_width = grapheme_len as u16; + } + } + } + } + column_widths + }; + + proc_widget_state.table_width_state.desired_column_widths = proc_widget_state + .table_width_state + .desired_column_widths + .iter() + .zip(&hard_widths) + .map(|(current, hard)| { + if let Some(hard) = hard { + if *hard > *current { + *hard + } else { + *current + } + } else { + *current + } + }) + .collect::<Vec<_>>(); + + let soft_widths_max = if proc_widget_state.is_grouped { + if proc_widget_state.is_using_command { + vec![None, Some(0.7), None, None, None, None, None, None] + } else if proc_widget_state.is_tree_mode { + vec![None, Some(0.5), None, None, None, None, None, None] + } else { + vec![None, Some(0.4), None, None, None, None, None, None] + } + } else if proc_widget_state.is_using_command { + vec![ + None, + Some(0.7), + None, + None, + None, + None, + None, + None, + Some(0.2), + ] + } else if proc_widget_state.is_tree_mode { + vec![ + None, + Some(0.5), + None, + None, + None, + None, + None, + None, + Some(0.2), + ] } else { - vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.15, 0.15] + vec![ + None, + Some(0.3), + None, + None, + None, + None, + None, + None, + Some(0.2), + ] + }; + + proc_widget_state.table_width_state.calculated_column_widths = + get_column_widths( + draw_loc.width, + &hard_widths, + &soft_widths_min, + &soft_widths_max, + &(proc_widget_state + .table_width_state + .desired_column_widths + .iter() + .map(|width| Some(*width)) + .collect::<Vec<_>>()), + true, + ); + + // debug!( + // "DCW: {:?}", + // proc_widget_state.table_width_state.desired_column_widths + // ); + // debug!( + // "CCW: {:?}", + // proc_widget_state.table_width_state.calculated_column_widths + // ); + } + + let dcw = &proc_widget_state.table_width_state.desired_column_widths; + let ccw = &proc_widget_state.table_width_state.calculated_column_widths; + + let process_rows = sliced_vec.iter().map(|(data, disabled)| { + let truncated_data = data.iter().zip(&hard_widths).enumerate().map( + |(itx, ((entry, alternative), width))| { + if let (Some(desired_col_width), Some(calculated_col_width)) = + (dcw.get(itx), ccw.get(itx)) + { + if width.is_none() { + if *desired_col_width > *calculated_col_width + && *calculated_col_width > 0 + { + let graphemes = + UnicodeSegmentation::graphemes(entry.as_str(), true) + .collect::<Vec<&str>>(); + + if let Some(alternative) = alternative { + Cow::Borrowed(alternative) + } else if graphemes.len() > *calculated_col_width as usize + && *calculated_col_width > 1 + { + // Truncate with ellipsis + let first_n = graphemes + [..(*calculated_col_width as usize - 1)] + .concat(); + Cow::Owned(format!("{}…", first_n)) + } else { + Cow::Borrowed(entry) + } + } else { + Cow::Borrowed(entry) + } + } else { + Cow::Borrowed(entry) + } + } else { + Cow::Borrowed(entry) + } + }, + ); + + if *disabled { + Row::StyledData(truncated_data, self.colours.disabled_text_style) + } else { + Row::Data(truncated_data) } - } else if proc_widget_state.is_using_command { - vec![0.05, 0.7, 0.05, 0.05, 0.03, 0.03, 0.03, 0.03] - } else if proc_widget_state.is_tree_mode { - vec![0.05, 0.4, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] - } else { - vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] - }; - let variable_intrinsic_results = get_variable_intrinsic_widths( - width as u16, - &width_ratios, - &process_headers_lens, - ); - let intrinsic_widths = - &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; + }); // TODO: gotop's "x out of y" thing is really nice to help keep track of the scroll position. f.render_stateful_widget( @@ -294,7 +421,9 @@ impl ProcessTableWidget for Painter { .highlight_style(highlight_style) .style(self.colours.text_style) .widths( - &(intrinsic_widths + &(proc_widget_state + .table_width_state + .calculated_column_widths .iter() .map(|calculated_width| { Constraint::Length(*calculated_width as u16) diff --git a/src/canvas/widgets/temp_table.rs b/src/canvas/widgets/temp_table.rs index 9ab6f985..b17bb0e0 100644 --- a/src/canvas/widgets/temp_table.rs +++ b/src/canvas/widgets/temp_table.rs @@ -1,6 +1,4 @@ use lazy_static::lazy_static; -use std::cmp::max; - use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, @@ -11,18 +9,20 @@ use tui::{ use crate::{ app, canvas::{ - drawing_utils::{get_start_position, get_variable_intrinsic_widths}, + drawing_utils::{get_column_widths, get_start_position}, Painter, }, constants::*, }; +use std::borrow::Cow; +use unicode_segmentation::UnicodeSegmentation; const TEMP_HEADERS: [&str; 2] = ["Sensor", "Temp"]; lazy_static! { - static ref TEMP_HEADERS_LENS: Vec<usize> = TEMP_HEADERS + static ref TEMP_HEADERS_LENS: Vec<u16> = TEMP_HEADERS .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) + .map(|entry| entry.len() as u16) .collect::<Vec<_>>(); } pub trait TempTableWidget { @@ -37,6 +37,7 @@ impl TempTableWidget for Painter { &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool, widget_id: u64, ) { + let recalculate_column_widths = app_state.should_get_widget_bounds(); if let Some(temp_widget_state) = app_state.temp_state.widget_states.get_mut(&widget_id) { let temp_sensor_data: &mut [Vec<String>] = &mut app_state.canvas_data.temp_sensor_data; @@ -63,14 +64,82 @@ impl TempTableWidget for Painter { .saturating_sub(start_position), )); let sliced_vec = &temp_sensor_data[start_position..]; - let temperature_rows = sliced_vec.iter().map(|temp_row| Row::Data(temp_row.iter())); // Calculate widths - let width = f64::from(draw_loc.width); - let width_ratios = [0.5, 0.5]; - let variable_intrinsic_results = - get_variable_intrinsic_widths(width as u16, &width_ratios, &TEMP_HEADERS_LENS); - let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; + let hard_widths = [None, None]; + if recalculate_column_widths { + temp_widget_state.table_width_state.desired_column_widths = { + let mut column_widths = TEMP_HEADERS_LENS.clone(); + for row in sliced_vec { + for (col, entry) in row.iter().enumerate() { + if entry.len() as u16 > column_widths[col] { + column_widths[col] = entry.len() as u16; + } + } + } + + column_widths + }; + temp_widget_state.table_width_state.calculated_column_widths = get_column_widths( + draw_loc.width, + &hard_widths, + &(TEMP_HEADERS_LENS + .iter() + .map(|width| Some(*width)) + .collect::<Vec<_>>()), + &[Some(0.80), Some(-1.0)], + &temp_widget_state + .table_width_state + .desired_column_widths + .iter() + .map(|width| Some(*width)) + .collect::<Vec<_>>(), + false, + ); + } + + let dcw = &temp_widget_state.table_width_state.desired_column_widths; + let ccw = &temp_widget_state.table_width_state.calculated_column_widths; + let temperature_rows = + sliced_vec.iter().map(|temp_row| { + let truncated_data = temp_row.iter().zip(&hard_widths).enumerate().map( + |(itx, (entry, width))| { + if width.is_none() { + if let (Some(desired_col_width), Some(calculated_col_width)) = + (dcw.get(itx), ccw.get(itx)) + { + if *desired_col_width > *calculated_col_width + && *calculated_col_width > 0 + { + let graphemes = + UnicodeSegmentation::graphemes(entry.as_str(), true) + .collect::<Vec<&str>>(); + + if graphemes.len() > *calculated_col_width as usize + && *calculated_col_width > 1< |