summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2020-01-25 21:49:55 +0100
committerGitHub <noreply@github.com>2020-01-25 21:49:55 +0100
commit198a1d41ff52f85d2946d6386f0d937cf992fd70 (patch)
treed38b8db66aefad8fa4d8d4f44fb5bb3349bb0101
parent218d71eb39345cd8ec549cf242e57f2cf8bde05c (diff)
feat(layout): new layout (#139)
-rw-r--r--src/display/components/layout.rs26
-rw-r--r--src/display/components/table.rs199
-rw-r--r--src/display/ui.rs2
-rw-r--r--src/tests/cases/snapshots/ui__basic_only_addresses.snap2
-rw-r--r--src/tests/cases/snapshots/ui__basic_only_connections.snap2
-rw-r--r--src/tests/cases/snapshots/ui__basic_only_processes.snap2
-rw-r--r--src/tests/cases/snapshots/ui__basic_startup.snap52
-rw-r--r--src/tests/cases/snapshots/ui__bi_directional_traffic-2.snap4
-rw-r--r--src/tests/cases/snapshots/ui__bi_directional_traffic.snap52
-rw-r--r--src/tests/cases/snapshots/ui__layout_full_width_under_30_height-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__layout_full_width_under_30_height.snap4
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_full_height-2.snap16
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_full_height.snap6
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_30_height-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_30_height.snap2
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_address-2.snap6
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_address.snap52
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_different_connections-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_different_connections.snap52
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_single_connection-2.snap4
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_single_connection.snap52
-rw-r--r--src/tests/cases/snapshots/ui__multiple_processes_with_multiple_connections-2.snap16
-rw-r--r--src/tests/cases/snapshots/ui__multiple_processes_with_multiple_connections.snap52
-rw-r--r--src/tests/cases/snapshots/ui__no_resolve_mode-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__no_resolve_mode.snap8
-rw-r--r--src/tests/cases/snapshots/ui__one_packet_of_traffic-2.snap4
-rw-r--r--src/tests/cases/snapshots/ui__one_packet_of_traffic.snap52
-rw-r--r--src/tests/cases/snapshots/ui__one_process_with_multiple_connections-2.snap6
-rw-r--r--src/tests/cases/snapshots/ui__one_process_with_multiple_connections.snap52
-rw-r--r--src/tests/cases/snapshots/ui__pause_by_space.snap52
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes.snap8
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional.snap8
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process-2.snap4
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process.snap4
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names.snap8
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap4
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event.snap52
-rw-r--r--src/tests/cases/snapshots/ui__truncate_long_hostnames-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__truncate_long_hostnames.snap55
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_addresses-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_addresses.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_connections-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_connections.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_processes-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_processes.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_windows_split_horizontally.snap4
-rw-r--r--src/tests/cases/snapshots/ui__two_windows_split_vertically.snap2
-rw-r--r--src/tests/cases/ui.rs208
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 │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│