summaryrefslogtreecommitdiffstats
path: root/src/canvas
diff options
context:
space:
mode:
authorClement Tsang <34804052+ClementTsang@users.noreply.github.com>2020-09-09 21:51:52 -0400
committerGitHub <noreply@github.com>2020-09-09 21:51:52 -0400
commitc58b2c2bb9b0013d4f0fddbf39df5c7a6efa9bc4 (patch)
treede70c812920489228ca463c120b735fc855b5600 /src/canvas
parentc426b0c7c4b30b09a63cfdcd3bb41a7d7d9ad00c (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')
-rw-r--r--src/canvas/drawing_utils.rs147
-rw-r--r--src/canvas/widgets/cpu_graph.rs76
-rw-r--r--src/canvas/widgets/disk_table.rs114
-rw-r--r--src/canvas/widgets/network_graph.rs25
-rw-r--r--src/canvas/widgets/process_table.rs281
-rw-r--r--src/canvas/widgets/temp_table.rs95
6 files changed, 546 insertions, 192 deletions
diff --git a/src/canvas/drawing_utils.rs b/src/canvas/drawing_utils.rs
index 22d56419..99bf799f 100644
--- a/src/canvas/drawing_utils.rs
+++ b/src/canvas/drawing_utils.rs
@@ -1,78 +1,107 @@
use crate::app;
-use itertools::izip;
+use std::cmp::{max, min};
-// TODO: Reverse intrinsic?
-/// A somewhat jury-rigged solution to simulate a variable intrinsic layout for
-/// table widths. Note that this will do one main pass to try to properly
-/// allocate widths. This will thus potentially cut off latter elements
-/// (return size of 0) if it is too small (threshold), but will try its best.
+/// Return a (hard)-width vector for column widths.
///
-/// `width thresholds` and `desired_widths_ratio` should be the same length.
-/// Otherwise bad things happen.
-pub fn get_variable_intrinsic_widths(
- total_width: u16, desired_widths_ratio: &[f64], width_thresholds: &[usize],
-) -> (Vec<u16>, usize) {
- let num_widths = desired_widths_ratio.len();
- let mut resulting_widths: Vec<u16> = vec![0; num_widths];
- let mut last_index = 0;
+/// * `total_width` is the, well, total width available. **NOTE:** This function automatically
+/// takes away 2 from the width as part of the left/right
+/// bounds.
+/// * `hard_widths` is inflexible column widths. Use a `None` to represent a soft width.
+/// * `soft_widths_min` is the lower limit for a soft width. Use `None` if a hard width goes there.
+/// * `soft_widths_max` is the upper limit for a soft width, in percentage of the total width. Use
+/// `None` if a hard width goes there.
+/// * `soft_widths_desired` is the desired soft width. Use `None` if a hard width goes there.
+/// * `left_to_right` is a boolean whether to go from left to right if true, or right to left if
+/// false.
+///
+/// **NOTE:** This function ASSUMES THAT ALL PASSED SLICES ARE OF THE SAME SIZE.
+///
+/// **NOTE:** The returned vector may not be the same size as the slices, this is because including
+/// 0-constraints breaks tui-rs.
+pub fn get_column_widths(
+ total_width: u16, hard_widths: &[Option<u16>], soft_widths_min: &[Option<u16>],
+ soft_widths_max: &[Option<f64>], soft_widths_desired: &[Option<u16>], left_to_right: bool,
+) -> Vec<u16> {
+ let initial_width = total_width - 2;
+ let mut total_width_left = initial_width;
+ let mut column_widths: Vec<u16> = vec![0; hard_widths.len()];
+ let range: Vec<usize> = if left_to_right {
+ (0..hard_widths.len()).collect()
+ } else {
+ (0..hard_widths.len()).rev().collect()
+ };
- let mut remaining_width = (total_width - (num_widths as u16 - 1)) as i32; // Required for spaces...
- let desired_widths = desired_widths_ratio
- .iter()
- .map(|&desired_width_ratio| (desired_width_ratio * total_width as f64) as i32);
+ for itx in &range {
+ if let Some(Some(hard_width)) = hard_widths.get(*itx) {
+ // Hard width...
+ let space_taken = min(*hard_width, total_width_left);
- for (desired_width, resulting_width, width_threshold) in izip!(
- desired_widths,
- resulting_widths.iter_mut(),
- width_thresholds
- ) {
- *resulting_width = if desired_width < *width_threshold as i32 {
- // Try to take threshold, else, 0
- if remaining_width < *width_threshold as i32 {
- 0
- } else {
- remaining_width -= *width_threshold as i32;
- *width_threshold as u16
+ // TODO [COLUMN MOVEMENT]: Remove this
+ if *hard_width > space_taken {
+ break;
}
- } else {
- // Take as large as possible
- if remaining_width < desired_width {
- // Check the biggest chunk possible
- if remaining_width < *width_threshold as i32 {
- 0
+
+ column_widths[*itx] = space_taken;
+ total_width_left -= space_taken;
+ total_width_left = total_width_left.saturating_sub(1);
+ } else if let (
+ Some(Some(soft_width_max)),
+ Some(Some(soft_width_min)),
+ Some(Some(soft_width_desired)),
+ ) = (
+ soft_widths_max.get(*itx),
+ soft_widths_min.get(*itx),
+ soft_widths_desired.get(*itx),
+ ) {
+ // Soft width...
+ let soft_limit = max(
+ if soft_width_max.is_sign_negative() {
+ *soft_width_desired
} else {
- let temp_width = remaining_width;
- remaining_width = 0;
- temp_width as u16
- }
- } else {
- remaining_width -= desired_width;
- desired_width as u16
+ (*soft_width_max * initial_width as f64).ceil() as u16
+ },
+ *soft_width_min,
+ );
+ let space_taken = min(min(soft_limit, *soft_width_desired), total_width_left);
+
+ // TODO [COLUMN MOVEMENT]: Remove this
+ if *soft_width_min > space_taken {
+ break;
}
- };
- if *resulting_width == 0 {
- break;
- } else {
- last_index += 1;
+ column_widths[*itx] = space_taken;
+ total_width_left -= space_taken;
+ total_width_left = total_width_left.saturating_sub(1);
}
}
- // Simple redistribution tactic - if there's any space left, split it evenly amongst all members
- if last_index < num_widths && last_index != 0 {
- let for_all_widths = (remaining_width / last_index as i32) as u16;
- let mut remainder = remaining_width % last_index as i32;
-
- for resulting_width in &mut resulting_widths {
- *resulting_width += for_all_widths;
- if remainder > 0 {
- *resulting_width += 1;
- remainder -= 1;
+ // Redistribute remaining.
+ while total_width_left > 0 {
+ for itx in &range {
+ if column_widths[*itx] > 0 {
+ column_widths[*itx] += 1;
+ total_width_left -= 1;
+ if total_width_left == 0 {
+ break;
+ }
}
}
}
- (resulting_widths, last_index)
+ let mut filtered_column_widths: Vec<u16> = vec![];
+ let mut still_seeing_zeros = true;
+ column_widths.iter().rev().for_each(|width| {
+ if still_seeing_zeros {
+ if *width != 0 {
+ still_seeing_zeros = false;
+ filtered_column_widths.push(*width);
+ }
+ } else {
+ filtered_column_widths.push(*width);
+ }
+ });
+ filtered_column_widths.reverse();
+ filtered_column_widths
}
pub fn get_search_start_position(
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();
+