diff options
author | Aram Drevekenin <aram@poor.dev> | 2020-01-25 21:49:55 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-25 21:49:55 +0100 |
commit | 198a1d41ff52f85d2946d6386f0d937cf992fd70 (patch) | |
tree | d38b8db66aefad8fa4d8d4f44fb5bb3349bb0101 | |
parent | 218d71eb39345cd8ec549cf242e57f2cf8bde05c (diff) |
feat(layout): new layout (#139)
51 files changed, 746 insertions, 511 deletions
diff --git a/src/display/components/layout.rs b/src/display/components/layout.rs index bd1fbfd..c047774 100644 --- a/src/display/components/layout.rs +++ b/src/display/components/layout.rs @@ -8,7 +8,6 @@ use super::TotalBandwidth; const FIRST_HEIGHT_BREAKPOINT: u16 = 30; const FIRST_WIDTH_BREAKPOINT: u16 = 120; -const SECOND_WIDTH_BREAKPOINT: u16 = 150; fn top_app_and_bottom_split(rect: Rect) -> (Rect, Rect, Rect) { let parts = ::tui::layout::Layout::default() @@ -51,8 +50,8 @@ impl<'a> Layout<'a> { fn build_two_children_layout(&self, rect: Rect) -> Vec<Rect> { // if there are two elements if rect.height < FIRST_HEIGHT_BREAKPOINT && rect.width < FIRST_WIDTH_BREAKPOINT { - //if the space is not enough, we drop one element - self.progressive_split(rect, vec![]) + // if the space is not enough, we drop one element + vec![rect] } else if rect.width < FIRST_WIDTH_BREAKPOINT { // if the horizontal space is not enough, we drop one element and we split horizontally self.progressive_split(rect, vec![Direction::Vertical]) @@ -63,23 +62,30 @@ impl<'a> Layout<'a> { } fn build_three_children_layout(&self, rect: Rect) -> Vec<Rect> { - //if there are three elements + // if there are three elements if rect.height < FIRST_HEIGHT_BREAKPOINT && rect.width < FIRST_WIDTH_BREAKPOINT { //if the space is not enough, we drop two elements - self.progressive_split(rect, vec![]) + vec![rect] } else if rect.height < FIRST_HEIGHT_BREAKPOINT { // if the vertical space is not enough, we drop one element and we split vertically self.progressive_split(rect, vec![Direction::Horizontal]) } else if rect.width < FIRST_WIDTH_BREAKPOINT { // if the horizontal space is not enough, we drop one element and we split horizontally self.progressive_split(rect, vec![Direction::Vertical]) - } else if rect.width < SECOND_WIDTH_BREAKPOINT { - // if the horizontal space is not enough for the default layout, we display one wide element - // on top and we split horizontally the bottom - self.progressive_split(rect, vec![Direction::Vertical, Direction::Horizontal]) } else { // default layout - self.progressive_split(rect, vec![Direction::Horizontal, Direction::Vertical]) + let halves = ::tui::layout::Layout::default() + .direction(Direction::Vertical) + .margin(0) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) + .split(rect); + let top_quarters = ::tui::layout::Layout::default() + .direction(Direction::Horizontal) + .margin(0) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) + .split(halves[0]); + + vec![top_quarters[0], top_quarters[1], halves[1]] } } diff --git a/src/display/components/table.rs b/src/display/components/table.rs index 1ca56d7..5786106 100644 --- a/src/display/components/table.rs +++ b/src/display/components/table.rs @@ -1,4 +1,4 @@ -use ::std::collections::HashMap; +use ::std::collections::{BTreeMap, HashMap}; use ::tui::backend::Backend; use ::tui::layout::Rect; @@ -12,17 +12,6 @@ use crate::network::{display_connection_string, display_ip_or_host}; use ::std::net::IpAddr; use std::iter::FromIterator; -const FIRST_WIDTH_BREAKPOINT: u16 = 50; -const SECOND_WIDTH_BREAKPOINT: u16 = 71; -const THIRD_WIDTH_BREAKPOINT: u16 = 95; - -const MAX_FIRST_COLUMN_WIDTH_PERCENTAGE: u16 = 53; -const MAX_SECOND_COLUMN_WIDTH_PERCENTAGE: u16 = 21; -const MAX_THIRD_COLUMN_WIDTH_PERCENTAGE: u16 = 22; - -const FIRST_COLUMN_WIDTHS: [u16; 4] = [10, 30, 40, 50]; -const THIRD_COLUMN_WIDTHS: [u16; 4] = [20, 20, 20, 20]; - fn display_upload_and_download(bandwidth: &impl Bandwidth) -> String { format!( "{} / {}", @@ -50,10 +39,40 @@ fn sort_by_bandwidth<'a, T>( list } +pub enum ColumnCount { + Two, + Three, +} + +impl ColumnCount { + pub fn as_u16(&self) -> u16 { + match &self { + ColumnCount::Two => 2, + ColumnCount::Three => 3, + } + } +} + +pub struct ColumnData { + column_count: ColumnCount, + column_widths: Vec<u16>, +} + pub struct Table<'a> { title: &'a str, column_names: &'a [&'a str], rows: Vec<Vec<String>>, + breakpoints: BTreeMap<u16, ColumnData>, +} + +fn truncate_middle(row: &str, max_length: u16) -> String { + if row.len() as u16 > max_length { + let first_slice = &row[0..(max_length as usize / 2) - 2]; + let second_slice = &row[(row.len() - (max_length / 2) as usize + 2)..row.len()]; + format!("{}[..]{}", first_slice, second_slice) + } else { + row.to_string() + } } impl<'a> Table<'a> { @@ -76,10 +95,40 @@ impl<'a> Table<'a> { .collect(); let connections_title = "Utilization by connection"; let connections_column_names = &["Connection", "Process", "Rate Up / Down"]; + let mut breakpoints = BTreeMap::new(); + breakpoints.insert( + 0, + ColumnData { + column_count: ColumnCount::Two, + column_widths: vec![20, 23], + }, + ); + breakpoints.insert( + 70, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![30, 12, 23], + }, + ); + breakpoints.insert( + 100, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![60, 12, 23], + }, + ); + breakpoints.insert( + 140, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![100, 12, 23], + }, + ); Table { title: connections_title, column_names: connections_column_names, rows: connections_rows, + breakpoints, } } pub fn create_processes_table(state: &UIState) -> Self { @@ -96,11 +145,41 @@ impl<'a> Table<'a> { }) .collect(); let processes_title = "Utilization by process name"; - let processes_column_names = &["Process", "Connection count", "Rate Up / Down"]; + let processes_column_names = &["Process", "Connections", "Rate Up / Down"]; + let mut breakpoints = BTreeMap::new(); + breakpoints.insert( + 0, + ColumnData { + column_count: ColumnCount::Two, + column_widths: vec![12, 23], + }, + ); + breakpoints.insert( + 50, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![12, 12, 23], + }, + ); + breakpoints.insert( + 100, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![40, 12, 23], + }, + ); + breakpoints.insert( + 140, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![40, 12, 23], + }, + ); Table { title: processes_title, column_names: processes_column_names, rows: processes_rows, + breakpoints, } } pub fn create_remote_addresses_table( @@ -121,47 +200,83 @@ impl<'a> Table<'a> { }) .collect(); let remote_addresses_title = "Utilization by remote address"; - let remote_addresses_column_names = - &["Remote Address", "Connection Count", "Rate Up / Down"]; + let remote_addresses_column_names = &["Remote Address", "Connections", "Rate Up / Down"]; + let mut breakpoints = BTreeMap::new(); + breakpoints.insert( + 0, + ColumnData { + column_count: ColumnCount::Two, + column_widths: vec![12, 23], + }, + ); + breakpoints.insert( + 70, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![30, 12, 23], + }, + ); + breakpoints.insert( + 100, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![60, 12, 23], + }, + ); + breakpoints.insert( + 140, + ColumnData { + column_count: ColumnCount::Three, + column_widths: vec![100, 12, 23], + }, + ); Table { title: remote_addresses_title, column_names: remote_addresses_column_names, rows: remote_addresses_rows, + breakpoints, } } pub fn render(&self, frame: &mut Frame<impl Backend>, rect: Rect) { - // the second column is only rendered if there is enough room for it - // (over third breakpoint) - let widths = if rect.width < FIRST_WIDTH_BREAKPOINT { - vec![FIRST_COLUMN_WIDTHS[0], THIRD_COLUMN_WIDTHS[0]] - } else if rect.width < SECOND_WIDTH_BREAKPOINT { - vec![FIRST_COLUMN_WIDTHS[1], THIRD_COLUMN_WIDTHS[1]] - } else if rect.width < THIRD_WIDTH_BREAKPOINT { - vec![FIRST_COLUMN_WIDTHS[2], THIRD_COLUMN_WIDTHS[2]] - } else { - vec![ - rect.width * MAX_FIRST_COLUMN_WIDTH_PERCENTAGE / 100, - rect.width * MAX_SECOND_COLUMN_WIDTH_PERCENTAGE / 100, - rect.width * MAX_THIRD_COLUMN_WIDTH_PERCENTAGE / 100 - 1, - ] - }; + let mut column_spacing: u16 = 0; + let mut widths = &vec![]; + let mut column_count: &ColumnCount = &ColumnCount::Three; - let column_names = if rect.width < THIRD_WIDTH_BREAKPOINT { - vec![self.column_names[0], self.column_names[2]] - } else { - vec![ + for (width_breakpoint, column_data) in self.breakpoints.iter() { + if *width_breakpoint < rect.width { + widths = &column_data.column_widths; + column_count = &column_data.column_count; + + let total_column_width: u16 = widths.iter().sum(); + if rect.width < total_column_width - column_count.as_u16() { + column_spacing = 0; + } else { + column_spacing = (rect.width - total_column_width) / column_count.as_u16(); + } + } + } + + let column_names = match column_count { + ColumnCount::Two => { + vec![self.column_names[0], self.column_names[2]] // always lose the middle column when needed + } + ColumnCount::Three => vec![ self.column_names[0], self.column_names[1], self.column_names[2], - ] + ], }; - let rows = self.rows.iter().map(|row| { - if rect.width < THIRD_WIDTH_BREAKPOINT { - vec![&row[0], &row[2]] - } else { - vec![&row[0], &row[1], &row[2]] - } + let rows = self.rows.iter().map(|row| match column_count { + ColumnCount::Two => vec![ + truncate_middle(&row[0], widths[0]), + truncate_middle(&row[2], widths[1]), + ], + ColumnCount::Three => vec![ + truncate_middle(&row[0], widths[0]), + truncate_middle(&row[1], widths[1]), + truncate_middle(&row[2], widths[2]), + ], }); let table_rows = rows.map(|row| Row::StyledData(row.into_iter(), Style::default())); @@ -171,7 +286,7 @@ impl<'a> Table<'a> { .header_style(Style::default().fg(Color::Yellow)) .widths(&widths[..]) .style(Style::default()) - .column_spacing(2) + .column_spacing(column_spacing) .render(frame, rect); } } diff --git a/src/display/ui.rs b/src/display/ui.rs index 4b193d5..cb4e7bd 100644 --- a/src/display/ui.rs +++ b/src/display/ui.rs @@ -119,8 +119,8 @@ where if !(opts.processes || opts.addresses || opts.connections) { children = vec![ Table::create_processes_table(&self.state), - Table::create_connections_table(&self.state, &self.ip_to_host), Table::create_remote_addresses_table(&self.state, &self.ip_to_host), + Table::create_connections_table(&self.state, &self.ip_to_host), ]; } children diff --git a/src/tests/cases/snapshots/ui__basic_only_addresses.snap b/src/tests/cases/snapshots/ui__basic_only_addresses.snap index c3c5e53..78465cd 100644 --- a/src/tests/cases/snapshots/ui__basic_only_addresses.snap +++ b/src/tests/cases/snapshots/ui__basic_only_addresses.snap @@ -4,7 +4,7 @@ expression: "&terminal_draw_events_mirror[0]" --- Total Rate Up / Down: 0Bps / 0Bps ┌Utilization by remote address───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│Remote Address Connection Count Rate Up / Down │ +│Remote Address Connections Rate Up / Down │ │ │ │ │ │ │ diff --git a/src/tests/cases/snapshots/ui__basic_only_connections.snap b/src/tests/cases/snapshots/ui__basic_only_connections.snap index 27f8036..8c9ae26 100644 --- a/src/tests/cases/snapshots/ui__basic_only_connections.snap +++ b/src/tests/cases/snapshots/ui__basic_only_connections.snap @@ -4,7 +4,7 @@ expression: "&terminal_draw_events_mirror[0]" --- Total Rate Up / Down: 0Bps / 0Bps ┌Utilization by connection───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│Connection Process Rate Up / Down │ +│Connection Process Rate Up / Down │ │ │ │ │ │ │ diff --git a/src/tests/cases/snapshots/ui__basic_only_processes.snap b/src/tests/cases/snapshots/ui__basic_only_processes.snap index 617b1f6..4a03f5b 100644 --- a/src/tests/cases/snapshots/ui__basic_only_processes.snap +++ b/src/tests/cases/snapshots/ui__basic_only_processes.snap @@ -4,7 +4,7 @@ expression: "&terminal_draw_events_mirror[0]" --- Total Rate Up / Down: 0Bps / 0Bps ┌Utilization by process name─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│Process Connection count Rate Up / Down │ +│Process Connections Rate Up / Down │ │ │ │ │ │ │ diff --git a/src/tests/cases/snapshots/ui__basic_startup.snap b/src/tests/cases/snapshots/ui__basic_startup.snap index 5dea671..7a32a5b 100644 --- a/src/tests/cases/snapshots/ui__basic_startup.snap +++ b/src/tests/cases/snapshots/ui__basic_startup.snap @@ -3,32 +3,8 @@ source: src/tests/cases/ui.rs expression: "&terminal_draw_events_mirror[0]" --- Total Rate Up / Down: 0Bps / 0Bps -┌Utilization by process name──────────────────────────────────────────────────────────────────┐┌Utilization by connection────────────────────────────────────────────────────────────────────┐ -│Process Connection count Rate Up / Down ││Connection Process Rate Up / Down │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ ││ │ -│ |