feat: Network chart, closes #79
This commit is contained in:
+163
-14
@@ -576,8 +576,116 @@ impl fmt::Display for ByteStats {
|
||||
}
|
||||
}
|
||||
|
||||
pub type MemTuple = (Vec<(f64, f64)>, ByteStats, State);
|
||||
pub type CpuTuple = (Vec<(f64, f64)>, CpuStats, State);
|
||||
#[derive(Debug, Default, Clone, Copy, Eq)]
|
||||
pub struct BandwidthStat(u64);
|
||||
|
||||
#[cfg(test)]
|
||||
impl BandwidthStat {
|
||||
pub fn new(x: u64) -> Self {
|
||||
Self(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BandwidthStat {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for BandwidthStat {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BandwidthStat {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
impl Stats for BandwidthStat {
|
||||
fn get_value(&self) -> f64 {
|
||||
self.0 as f64
|
||||
}
|
||||
}
|
||||
|
||||
/// convert from bytes to per second, using 1000 instead of 1024
|
||||
impl fmt::Display for BandwidthStat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let as_f64 = self.get_value();
|
||||
let p = match as_f64 {
|
||||
x if x >= ONE_GB => format!("{y:.2} GB/s", y = as_f64 / ONE_GB),
|
||||
x if x >= ONE_MB => format!("{y:.2} Mb/s", y = as_f64 / ONE_MB),
|
||||
_ => format!("{y:.2} kb/s", y = as_f64 / ONE_KB),
|
||||
};
|
||||
write!(f, "{p:>x$}", x = f.width().unwrap_or(1))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NetworkBandwidth(VecDeque<BandwidthStat>);
|
||||
|
||||
impl NetworkBandwidth {
|
||||
pub fn new() -> Self {
|
||||
Self(VecDeque::with_capacity(60))
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Find the highest speed recorded in the vecque
|
||||
pub fn max(&self) -> BandwidthStat {
|
||||
self.to_vec_f64()
|
||||
.iter()
|
||||
.map(|(_, speed)| *speed)
|
||||
.max_by(|a, b| a.total_cmp(b))
|
||||
.map(|m| BandwidthStat(m as u64))
|
||||
.unwrap_or(BandwidthStat(0))
|
||||
}
|
||||
|
||||
pub fn push(&mut self, x: u64) {
|
||||
if self.0.len() >= 60 {
|
||||
self.0.pop_front();
|
||||
}
|
||||
self.0.push_back(BandwidthStat(x));
|
||||
}
|
||||
|
||||
/// Get the current total amount of traffic on a given device
|
||||
pub fn current_total(&self) -> ByteStats {
|
||||
self.0
|
||||
.back()
|
||||
.map_or(ByteStats::default(), |i| ByteStats::new(i.0))
|
||||
}
|
||||
|
||||
/// Convert to f64 for use in the network graph
|
||||
pub fn to_vec_f64(&self) -> Vec<(f64, f64)> {
|
||||
self.0
|
||||
.iter()
|
||||
.zip(self.0.iter().skip(1))
|
||||
.enumerate()
|
||||
.map(|(i, (prev, current))| (i as f64, current.0.saturating_sub(prev.0) as f64))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ChartsData {
|
||||
pub memory: ChartSeries<ByteStats>,
|
||||
pub cpu: ChartSeries<CpuStats>,
|
||||
pub rx: ChartSeries<BandwidthStat>,
|
||||
pub tx: ChartSeries<BandwidthStat>,
|
||||
pub state: State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ChartSeries<T: Stats> {
|
||||
pub dataset: Vec<(f64, f64)>,
|
||||
pub max: T,
|
||||
pub current: T,
|
||||
}
|
||||
|
||||
/// Used to make sure that each log entry, for each container, is unique,
|
||||
/// will only push a log entry into the logs vec if timestamp of said log entry isn't in the hashset
|
||||
@@ -992,10 +1100,10 @@ pub struct ContainerItem {
|
||||
pub mem_stats: VecDeque<ByteStats>,
|
||||
pub name: ContainerName,
|
||||
pub ports: Vec<ContainerPorts>,
|
||||
pub rx: ByteStats,
|
||||
pub rx: NetworkBandwidth,
|
||||
pub state: State,
|
||||
pub status: ContainerStatus,
|
||||
pub tx: ByteStats,
|
||||
pub tx: NetworkBandwidth,
|
||||
}
|
||||
|
||||
/// Basic display information, for when running in debug mode
|
||||
@@ -1042,10 +1150,10 @@ impl ContainerItem {
|
||||
mem_stats: VecDeque::with_capacity(60),
|
||||
name: name.into(),
|
||||
ports,
|
||||
rx: ByteStats::default(),
|
||||
rx: NetworkBandwidth::new(),
|
||||
state,
|
||||
status,
|
||||
tx: ByteStats::default(),
|
||||
tx: NetworkBandwidth::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1071,7 +1179,7 @@ impl ContainerItem {
|
||||
self.cpu_stats
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|i| (i.0 as f64, i.1.0))
|
||||
.map(|(i, v)| (i as f64, v.0))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
@@ -1081,24 +1189,65 @@ impl ContainerItem {
|
||||
self.mem_stats
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|i| (i.0 as f64, i.1.0 as f64))
|
||||
.map(|(i, v)| (i as f64, v.0 as f64))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Get all cpu chart data
|
||||
fn get_cpu_chart_data(&self) -> CpuTuple {
|
||||
(self.get_cpu_dataset(), self.max_cpu_stats(), self.state)
|
||||
fn get_cpu_chart_data(&self) -> ChartSeries<CpuStats> {
|
||||
ChartSeries {
|
||||
dataset: self.get_cpu_dataset(),
|
||||
max: self.max_cpu_stats(),
|
||||
current: self
|
||||
.cpu_stats
|
||||
.back()
|
||||
.map_or_else(|| CpuStats::new(0.0), |i| *i),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all mem chart data
|
||||
fn get_mem_chart_data(&self) -> MemTuple {
|
||||
(self.get_mem_dataset(), self.max_mem_stats(), self.state)
|
||||
fn get_mem_chart_data(&self) -> ChartSeries<ByteStats> {
|
||||
ChartSeries {
|
||||
dataset: self.get_mem_dataset(),
|
||||
max: self.max_mem_stats(),
|
||||
current: self
|
||||
.mem_stats
|
||||
.back()
|
||||
.map_or_else(|| ByteStats::new(0), |i| *i),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all mem chart data
|
||||
/// Don't understand what we are doing here
|
||||
fn get_bandwidth_chart_tx_data(&self) -> ChartSeries<BandwidthStat> {
|
||||
let data = self.tx.to_vec_f64();
|
||||
ChartSeries {
|
||||
current: BandwidthStat(data.last().map_or(0, |i| i.1 as u64)),
|
||||
dataset: data,
|
||||
max: self.tx.max(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all mem chart data
|
||||
fn get_bandwidth_chart_rx_data(&self) -> ChartSeries<BandwidthStat> {
|
||||
let data = self.rx.to_vec_f64();
|
||||
ChartSeries {
|
||||
current: BandwidthStat(data.last().map_or(0, |i| i.1 as u64)),
|
||||
dataset: data,
|
||||
max: self.rx.max(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get chart info for cpu & memory in one function
|
||||
/// So only need to call .lock() once
|
||||
pub fn get_chart_data(&self) -> (CpuTuple, MemTuple) {
|
||||
(self.get_cpu_chart_data(), self.get_mem_chart_data())
|
||||
pub fn get_chart_data(&self) -> ChartsData {
|
||||
ChartsData {
|
||||
memory: self.get_mem_chart_data(),
|
||||
cpu: self.get_cpu_chart_data(),
|
||||
rx: self.get_bandwidth_chart_rx_data(),
|
||||
tx: self.get_bandwidth_chart_tx_data(),
|
||||
state: self.state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+90
-32
@@ -467,12 +467,14 @@ impl AppData {
|
||||
Header::Rx => item_ord
|
||||
.0
|
||||
.rx
|
||||
.cmp(&item_ord.1.rx)
|
||||
.current_total()
|
||||
.cmp(&item_ord.1.rx.current_total())
|
||||
.then_with(|| item_ord.0.name.get().cmp(item_ord.1.name.get())),
|
||||
Header::Tx => item_ord
|
||||
.0
|
||||
.tx
|
||||
.cmp(&item_ord.1.tx)
|
||||
.current_total()
|
||||
.cmp(&item_ord.1.tx.current_total())
|
||||
.then_with(|| item_ord.0.name.get().cmp(item_ord.1.name.get())),
|
||||
Header::Name => item_ord
|
||||
.0
|
||||
@@ -610,10 +612,17 @@ impl AppData {
|
||||
}
|
||||
|
||||
/// Get a mutable container by given id
|
||||
#[cfg(not(test))]
|
||||
fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> {
|
||||
self.containers.items.iter_mut().find(|i| &i.id == id)
|
||||
}
|
||||
|
||||
/// As above, but make it public to testing
|
||||
#[cfg(test)]
|
||||
pub fn get_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> {
|
||||
self.containers.items.iter_mut().find(|i| &i.id == id)
|
||||
}
|
||||
|
||||
/// Get a mutable container by given id in the tmp_container vec
|
||||
fn get_hidden_container_by_id(&mut self, id: &ContainerId) -> Option<&mut ContainerItem> {
|
||||
self.hidden_containers.iter_mut().find(|i| &i.id == id)
|
||||
@@ -790,7 +799,7 @@ impl AppData {
|
||||
|
||||
/// Chart data related methods
|
||||
/// Get mutable Option of the currently selected container chart data
|
||||
pub fn get_chart_data(&self) -> Option<(CpuTuple, MemTuple)> {
|
||||
pub fn get_chart_data(&self) -> Option<ChartsData> {
|
||||
self.containers
|
||||
.state
|
||||
.selected()
|
||||
@@ -839,6 +848,7 @@ impl AppData {
|
||||
|
||||
for container in [&self.containers.items, &self.hidden_containers] {
|
||||
for container in container {
|
||||
// TODO refactor these
|
||||
let cpu_count = container.cpu_stats.back().map_or_else(
|
||||
|| count(&CpuStats::default().to_string()),
|
||||
|i| count(&i.to_string()),
|
||||
@@ -848,14 +858,19 @@ impl AppData {
|
||||
|| count(&ByteStats::default().to_string()),
|
||||
|i| count(&i.to_string()),
|
||||
);
|
||||
|
||||
columns.cpu.1 = columns.cpu.1.max(cpu_count);
|
||||
columns.image.1 = columns.image.1.max(count(&container.image.to_string()));
|
||||
columns.mem.1 = columns.mem.1.max(mem_current_count);
|
||||
columns.mem.2 = columns.mem.2.max(count(&container.mem_limit.to_string()));
|
||||
columns.name.1 = columns.name.1.max(count(&container.name.to_string()));
|
||||
columns.net_rx.1 = columns.net_rx.1.max(count(&container.rx.to_string()));
|
||||
columns.net_tx.1 = columns.net_tx.1.max(count(&container.tx.to_string()));
|
||||
columns.net_rx.1 = columns
|
||||
.net_rx
|
||||
.1
|
||||
.max(count(&container.rx.current_total().to_string()));
|
||||
columns.net_tx.1 = columns
|
||||
.net_tx
|
||||
.1
|
||||
.max(count(&container.tx.current_total().to_string()));
|
||||
columns.state.1 = columns.state.1.max(count(&container.state.to_string()));
|
||||
columns.status.1 = columns.status.1.max(count(container.status.get()));
|
||||
}
|
||||
@@ -899,8 +914,12 @@ impl AppData {
|
||||
container.mem_stats.push_back(ByteStats::new(mem));
|
||||
}
|
||||
|
||||
container.rx.update(rx);
|
||||
container.tx.update(tx);
|
||||
// Only insert if alive, or if is empty, need two to create an entry in the bandwidth chart, so instead this fills in the RX/TX total columns
|
||||
if container.rx.is_empty() || container.state.is_alive() {
|
||||
container.rx.push(rx);
|
||||
container.tx.push(tx);
|
||||
}
|
||||
|
||||
container.mem_limit.update(mem_limit);
|
||||
}
|
||||
if self.is_selected_container(id) {
|
||||
@@ -1336,13 +1355,16 @@ mod tests {
|
||||
assert_eq!(result, &containers);
|
||||
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) {
|
||||
i.rx = ByteStats::new(40);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(40);
|
||||
}
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) {
|
||||
i.rx = ByteStats::new(80);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(80);
|
||||
}
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) {
|
||||
i.rx = ByteStats::new(2);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(2);
|
||||
}
|
||||
|
||||
// descending
|
||||
@@ -1373,13 +1395,16 @@ mod tests {
|
||||
assert_eq!(result, &containers);
|
||||
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) {
|
||||
i.rx = ByteStats::new(400);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(400);
|
||||
}
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) {
|
||||
i.rx = ByteStats::new(80);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(80);
|
||||
}
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) {
|
||||
i.rx = ByteStats::new(83);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(83);
|
||||
}
|
||||
|
||||
// descending
|
||||
@@ -1437,13 +1462,16 @@ mod tests {
|
||||
assert_eq!(result, &containers);
|
||||
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("1")) {
|
||||
i.rx = ByteStats::new(400);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(400);
|
||||
}
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("2")) {
|
||||
i.rx = ByteStats::new(80);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(80);
|
||||
}
|
||||
if let Some(i) = app_data.get_container_by_id(&ContainerId::from("3")) {
|
||||
i.rx = ByteStats::new(83);
|
||||
i.rx = NetworkBandwidth::new();
|
||||
i.rx.push(83);
|
||||
}
|
||||
|
||||
app_data.set_sorted(Some((Header::Rx, SortedOrder::Asc)));
|
||||
@@ -2223,26 +2251,49 @@ mod tests {
|
||||
|
||||
app_data.containers_start();
|
||||
|
||||
let mut rx = NetworkBandwidth::new();
|
||||
rx.push(200);
|
||||
rx.push(100);
|
||||
rx.push(200);
|
||||
|
||||
let mut tx = NetworkBandwidth::new();
|
||||
tx.push(300);
|
||||
tx.push(600);
|
||||
tx.push(900);
|
||||
|
||||
if let Some(item) = app_data.get_container_by_id(&ContainerId::from("1")) {
|
||||
item.cpu_stats = VecDeque::from([CpuStats::new(1.1), CpuStats::new(1.2)]);
|
||||
item.cpu_stats = VecDeque::from([CpuStats::new(1.2), CpuStats::new(1.2)]);
|
||||
item.mem_stats = VecDeque::from([ByteStats::new(1), ByteStats::new(2)]);
|
||||
item.rx = rx;
|
||||
item.tx = tx;
|
||||
}
|
||||
|
||||
let result = app_data.get_chart_data();
|
||||
assert_eq!(
|
||||
result,
|
||||
Some((
|
||||
(
|
||||
vec![(0.0, 1.1), (1.0, 1.2)],
|
||||
CpuStats::new(1.2),
|
||||
State::Running(RunningState::Healthy),
|
||||
),
|
||||
(
|
||||
vec![(0.0, 1.0), (1.0, 2.0)],
|
||||
ByteStats::new(2),
|
||||
State::Running(RunningState::Healthy),
|
||||
)
|
||||
))
|
||||
Some(ChartsData {
|
||||
memory: ChartSeries {
|
||||
dataset: vec![(0.0, 1.0), (1.0, 2.0)],
|
||||
max: ByteStats::new(2),
|
||||
current: ByteStats::new(2)
|
||||
},
|
||||
cpu: ChartSeries {
|
||||
dataset: vec![(0.0, 1.2), (1.0, 1.2)],
|
||||
max: CpuStats::new(1.2),
|
||||
current: CpuStats::new(1.2)
|
||||
},
|
||||
rx: ChartSeries {
|
||||
dataset: vec![(0.0, 0.0), (1.0, 100.0)],
|
||||
max: BandwidthStat::new(100),
|
||||
current: BandwidthStat::new(100)
|
||||
},
|
||||
tx: ChartSeries {
|
||||
dataset: vec![(0.0, 300.0), (1.0, 300.0)],
|
||||
max: BandwidthStat::new(300),
|
||||
current: BandwidthStat::new(300)
|
||||
},
|
||||
state: State::Running(RunningState::Healthy)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2392,8 +2443,15 @@ mod tests {
|
||||
assert_eq!(result[0].cpu_stats, VecDeque::from([CpuStats::new(10.0)]));
|
||||
assert_eq!(result[0].mem_stats, VecDeque::from([ByteStats::new(10)]));
|
||||
assert_eq!(result[0].mem_limit, ByteStats::new(10));
|
||||
assert_eq!(result[0].rx, ByteStats::new(10));
|
||||
assert_eq!(result[0].tx, ByteStats::new(10));
|
||||
|
||||
let mut rx = NetworkBandwidth::new();
|
||||
rx.push(10);
|
||||
let mut tx = NetworkBandwidth::new();
|
||||
tx.push(10);
|
||||
assert_eq!(result[0].rx, rx);
|
||||
// VecDeque::from([ByteStats::new(10)]));
|
||||
assert_eq!(result[0].tx, tx);
|
||||
// VecDeque::from([ByteStats::new(10)]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use ratatui::style::Color;
|
||||
|
||||
static COLOR_RX: Color = Color::Rgb(255, 233, 193);
|
||||
static COLOR_TX: Color = Color::Rgb(205, 140, 140);
|
||||
|
||||
/// The macro accepts a list of struct names with key names
|
||||
/// Returns a struct where every key name is an Option<String>, with the correct derived attributes
|
||||
macro_rules! optional_config_struct {
|
||||
@@ -249,6 +252,8 @@ optional_config_struct!(
|
||||
ConfigBackgroundText, background, text;
|
||||
ConfigBackgroundTextHighlight, background, text, text_highlight;
|
||||
ConfigBorders, selected, unselected;
|
||||
ConfigChartBandwidth, background, border, max_rx, max_tx, title_tx, title_rx, points_rx, points_tx, y_axis;
|
||||
|
||||
ConfigChartCpu, background, border, order, title, max, points,y_axis;
|
||||
ConfigChartMemory, background, border, title, max, points, y_axis;
|
||||
ConfigChartPorts, background, border, title, headings, text;
|
||||
@@ -265,6 +270,8 @@ config_struct!(
|
||||
Borders, selected, unselected;
|
||||
ChartCpu, background, border, title, max, points, y_axis;
|
||||
ChartMemory, background, border, title, max, points, y_axis;
|
||||
ChartBandwidth, background, border, max_rx, max_tx, title_rx, title_tx, points_rx, points_tx, y_axis;
|
||||
|
||||
ChartPorts, background, border, title, headings, text;
|
||||
Commands, background, pause, restart, stop, delete, resume, start;
|
||||
Containers, background, icon, text, text_rx, text_tx;
|
||||
@@ -284,6 +291,7 @@ pub struct ConfigColors {
|
||||
borders: Option<ConfigBorders>,
|
||||
chart_cpu: Option<ConfigChartCpu>,
|
||||
chart_memory: Option<ConfigChartMemory>,
|
||||
chart_bandwidth: Option<ConfigChartBandwidth>,
|
||||
chart_ports: Option<ConfigChartPorts>,
|
||||
commands: Option<ConfigCommands>,
|
||||
container_state: Option<ConfigContainerState>,
|
||||
@@ -335,7 +343,24 @@ impl Commands {
|
||||
}
|
||||
}
|
||||
|
||||
/// Default colours for the help popup
|
||||
/// Default colours for the Bandwidth chart
|
||||
impl ChartBandwidth {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
background: Color::Reset,
|
||||
border: Color::White,
|
||||
max_rx: COLOR_RX,
|
||||
title_rx: COLOR_RX,
|
||||
title_tx: COLOR_TX,
|
||||
max_tx: COLOR_TX,
|
||||
points_rx: COLOR_RX,
|
||||
points_tx: COLOR_TX,
|
||||
y_axis: Color::White,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Default colours for the CPU chart
|
||||
impl ChartCpu {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
@@ -383,8 +408,8 @@ impl Containers {
|
||||
background: Color::Reset,
|
||||
icon: Color::White,
|
||||
text: Color::Blue,
|
||||
text_rx: Color::Rgb(255, 233, 193),
|
||||
text_tx: Color::Rgb(205, 140, 140),
|
||||
text_rx: COLOR_RX,
|
||||
text_tx: COLOR_TX,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -487,6 +512,7 @@ pub struct AppColors {
|
||||
pub borders: Borders,
|
||||
pub chart_cpu: ChartCpu,
|
||||
pub chart_memory: ChartMemory,
|
||||
pub chart_bandwidth: ChartBandwidth,
|
||||
pub chart_ports: ChartPorts,
|
||||
pub commands: Commands,
|
||||
pub container_state: ContainerState,
|
||||
@@ -507,6 +533,7 @@ impl AppColors {
|
||||
borders: Borders::new(),
|
||||
chart_cpu: ChartCpu::new(),
|
||||
chart_memory: ChartMemory::new(),
|
||||
chart_bandwidth: ChartBandwidth::new(),
|
||||
chart_ports: ChartPorts::new(),
|
||||
commands: Commands::new(),
|
||||
container_state: ContainerState::new(),
|
||||
|
||||
@@ -254,6 +254,27 @@ points = "cyan"
|
||||
# The charts y-axis
|
||||
y_axis = "white"
|
||||
|
||||
# The bandwidth chart
|
||||
[colors.chart_bandwidth]
|
||||
# Background color of panel
|
||||
background = "reset"
|
||||
# Border color
|
||||
border = "white"
|
||||
# Maximum RX value - again paused & stopped colors not yet customizable
|
||||
max_rx = "#FFE9C1"
|
||||
# Maximum TX value - again paused & stopped colors not yet customizable
|
||||
max_tx = "#CD8C8C"
|
||||
# RX points on the chart - again paused & stopped colors not yet customizable
|
||||
points_rx = "#FFE9C1"
|
||||
# TX points on the chart - again paused & stopped colors not yet customizable
|
||||
points_tx = "#CD8C8C"
|
||||
# TX title color
|
||||
title_rx = "#FFE9C1"
|
||||
# RX title color
|
||||
title_tx = "#CD8C8C"
|
||||
# The charts y-axis
|
||||
y_axis = "white"
|
||||
|
||||
# The ports chart
|
||||
[colors.chart_ports]
|
||||
# Background color of panel
|
||||
|
||||
@@ -441,8 +441,7 @@ mod tests {
|
||||
exec: gen_v(("g", "h")),
|
||||
filter_mode: gen_v(("i", "j")),
|
||||
force_redraw: gen_v(("k", "l")),
|
||||
// TODO test me
|
||||
inspect: None,
|
||||
inspect: gen_v(("m", "n")),
|
||||
scroll_back: gen_v(("s", "t")),
|
||||
scroll_forward: gen_v(("q", "r")),
|
||||
log_search_mode: gen_v(("1", "2")),
|
||||
@@ -481,7 +480,6 @@ mod tests {
|
||||
exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))),
|
||||
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
|
||||
force_redraw: (KeyCode::Char('k'), Some(KeyCode::Char('l'))),
|
||||
//todo test me
|
||||
inspect: (KeyCode::Char('i'), None),
|
||||
scroll_back: (KeyCode::Char('s'), Some(KeyCode::Char('t'))),
|
||||
scroll_forward: (KeyCode::Char('q'), Some(KeyCode::Char('r'))),
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use ratatui::{
|
||||
Frame,
|
||||
layout::{Alignment, Rect},
|
||||
style::{Color, Modifier, Style, Stylize},
|
||||
symbols::{self, Marker},
|
||||
text::{Line, Span},
|
||||
widgets::{Axis, Block, BorderType, Borders, Chart, Dataset, GraphType},
|
||||
};
|
||||
|
||||
use super::FrameData;
|
||||
use crate::{
|
||||
app_data::{State, Stats},
|
||||
config::AppColors,
|
||||
};
|
||||
|
||||
fn make_chart<'a, T: Stats + Display>(
|
||||
state: State,
|
||||
colors: AppColors,
|
||||
dataset: Vec<Dataset<'a>>,
|
||||
current_rx: &'a T,
|
||||
max_rx: &'a T,
|
||||
current_tx: &'a T,
|
||||
max_tx: &'a T,
|
||||
) -> Chart<'a> {
|
||||
let gen_color = |state: &State, default: Color| {
|
||||
if state.is_healthy() {
|
||||
default
|
||||
} else {
|
||||
state.get_color(colors)
|
||||
}
|
||||
};
|
||||
|
||||
let mut labels = [
|
||||
Span::raw(""),
|
||||
Span::styled(
|
||||
format!("{max_rx}"),
|
||||
Style::default()
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.fg(gen_color(&state, colors.chart_bandwidth.max_rx)),
|
||||
),
|
||||
Span::styled(
|
||||
format!("{max_tx}"),
|
||||
Style::default()
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.fg(gen_color(&state, colors.chart_bandwidth.max_tx)),
|
||||
),
|
||||
Span::raw(""),
|
||||
];
|
||||
|
||||
// Set the order of rx/tx on the y axis, based on which is the highest value
|
||||
if max_rx.get_value() > max_tx.get_value() {
|
||||
labels.reverse();
|
||||
}
|
||||
|
||||
Chart::new(dataset)
|
||||
.bg(colors.chart_bandwidth.background)
|
||||
.block(
|
||||
Block::default()
|
||||
.title_alignment(Alignment::Center)
|
||||
.title(Line::from(vec![
|
||||
Span::styled(
|
||||
format!(" rx: {current_rx}"),
|
||||
Style::default()
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.fg(gen_color(&state, colors.chart_bandwidth.title_rx)),
|
||||
),
|
||||
Span::styled(
|
||||
format!(" tx: {current_tx} "),
|
||||
Style::default()
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.fg(gen_color(&state, colors.chart_bandwidth.title_tx)),
|
||||
),
|
||||
]))
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(Style::default().fg(colors.chart_bandwidth.border)),
|
||||
)
|
||||
.x_axis(Axis::default().bounds([0.0, 60.0]))
|
||||
.y_axis(
|
||||
Axis::default()
|
||||
.labels(labels)
|
||||
.style(Style::default().fg(colors.chart_bandwidth.y_axis))
|
||||
.bounds([0.0, (max_rx.get_value()).max(max_tx.get_value()) + 0.01]),
|
||||
)
|
||||
}
|
||||
|
||||
/// Draw bandwidth chart
|
||||
pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) {
|
||||
if let Some(x) = fd.chart_data.as_ref() {
|
||||
let mut dataset = vec![
|
||||
Dataset::default()
|
||||
.marker(symbols::Marker::Dot)
|
||||
.style(Style::default().fg(colors.chart_bandwidth.points_tx))
|
||||
.graph_type(GraphType::Line)
|
||||
.marker(Marker::Dot)
|
||||
.style(Style::default().fg(colors.chart_bandwidth.points_tx))
|
||||
.data(&x.tx.dataset),
|
||||
];
|
||||
dataset.extend(vec![
|
||||
Dataset::default()
|
||||
.marker(symbols::Marker::Dot)
|
||||
.style(Style::default().fg(colors.chart_bandwidth.points_rx))
|
||||
.marker(Marker::Dot)
|
||||
.style(Style::default().fg(colors.chart_bandwidth.points_rx))
|
||||
.graph_type(GraphType::Line)
|
||||
.data(&x.rx.dataset),
|
||||
]);
|
||||
|
||||
let chart = make_chart(
|
||||
x.state,
|
||||
colors,
|
||||
dataset,
|
||||
&x.rx.current,
|
||||
&x.rx.max,
|
||||
&x.tx.current,
|
||||
&x.tx.max,
|
||||
);
|
||||
|
||||
f.render_widget(chart, area);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use insta::assert_snapshot;
|
||||
use ratatui::style::Color;
|
||||
|
||||
use crate::{
|
||||
app_data::{ContainerId, NetworkBandwidth, State},
|
||||
config::AppColors,
|
||||
ui::{
|
||||
FrameData,
|
||||
draw_blocks::tests::{COLOR_RX, COLOR_TX, get_result, test_setup},
|
||||
},
|
||||
};
|
||||
|
||||
const TX_DOTS: [(usize, usize); 14] = [
|
||||
(1, 21),
|
||||
(2, 19),
|
||||
(2, 20),
|
||||
(3, 18),
|
||||
(3, 19),
|
||||
(4, 10),
|
||||
(4, 11),
|
||||
(4, 17),
|
||||
(4, 18),
|
||||
(5, 16),
|
||||
(6, 14),
|
||||
(6, 15),
|
||||
(7, 13),
|
||||
(7, 14),
|
||||
];
|
||||
|
||||
const RX_DOTS: [(usize, usize); 15] = [
|
||||
(1, 21),
|
||||
(2, 19),
|
||||
(2, 20),
|
||||
(3, 18),
|
||||
(3, 19),
|
||||
(4, 10),
|
||||
(4, 11),
|
||||
(4, 17),
|
||||
(4, 18),
|
||||
(5, 16),
|
||||
(6, 16),
|
||||
(6, 15),
|
||||
(7, 13),
|
||||
(7, 14),
|
||||
(8, 13),
|
||||
];
|
||||
|
||||
const COMBINED_DOTS_RX: [(usize, usize); 15] = [
|
||||
(1, 21),
|
||||
(2, 19),
|
||||
(2, 20),
|
||||
(3, 18),
|
||||
(3, 19),
|
||||
(4, 10),
|
||||
(4, 11),
|
||||
(4, 17),
|
||||
(4, 18),
|
||||
(5, 16),
|
||||
(6, 15),
|
||||
(6, 16),
|
||||
(7, 13),
|
||||
(7, 14),
|
||||
(8, 13),
|
||||
];
|
||||
|
||||
const COMBINED_DOTS_TX: [(usize, usize); 8] = [
|
||||
(7, 19),
|
||||
(7, 20),
|
||||
(7, 21),
|
||||
(8, 14),
|
||||
(8, 15),
|
||||
(8, 16),
|
||||
(8, 17),
|
||||
(8, 18),
|
||||
];
|
||||
|
||||
#[test]
|
||||
/// When status is Running, but not data, charts drawn without dots etc, colours correct
|
||||
fn test_draw_blocks_charts_running_none() {
|
||||
let mut setup = test_setup(40, 10, true, true);
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
|
||||
})
|
||||
.unwrap();
|
||||
assert_snapshot!(setup.terminal.backend());
|
||||
for (row_index, result_row) in get_result(&setup) {
|
||||
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
|
||||
match (row_index, result_cell_index) {
|
||||
// border
|
||||
(9, _) | (1..=9, 0 | 39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Border first row only
|
||||
(0, 0..=4 | 34..=39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Title RX
|
||||
(0, 5..=18) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
// Title TX
|
||||
(0, 19..=33) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// Y axis
|
||||
(1..=8, 10) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// TX max
|
||||
(4, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// RX max
|
||||
(6, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(result_cell.fg, Color::Reset);
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test with TX data
|
||||
fn test_draw_blocks_charts_running_with_data_tx() {
|
||||
let mut setup = test_setup(40, 10, true, true);
|
||||
let mut tx = NetworkBandwidth::new();
|
||||
|
||||
for i in 0..=20 {
|
||||
tx.push(1000 * i * (10 + 5 * i));
|
||||
}
|
||||
|
||||
if let Some(item) = setup
|
||||
.app_data
|
||||
.lock()
|
||||
.get_container_by_id(&ContainerId::from("1"))
|
||||
{
|
||||
item.tx = tx;
|
||||
}
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
|
||||
})
|
||||
.unwrap();
|
||||
assert_snapshot!(setup.terminal.backend());
|
||||
for (row_index, result_row) in get_result(&setup) {
|
||||
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
|
||||
match (row_index, result_cell_index) {
|
||||
// border
|
||||
(9, _) | (1..=9, 0 | 39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Border first row only
|
||||
(0, 0..=3 | 35..=39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Title RX
|
||||
(0, 4..=17) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
// Title TX
|
||||
(0, 18..=34) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// Y axis
|
||||
(1..=8, 12) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// TX max
|
||||
(4, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// RX max
|
||||
(6, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
// TX dots
|
||||
x if TX_DOTS.contains(&(row_index, result_cell_index)) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(result_cell.fg, Color::Reset);
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test with RX data
|
||||
fn test_draw_blocks_charts_running_with_data_rx() {
|
||||
let mut setup = test_setup(40, 10, true, true);
|
||||
let mut rx = NetworkBandwidth::new();
|
||||
|
||||
for i in 0..=20 {
|
||||
rx.push(2000 * i * (10 + 7 * i));
|
||||
}
|
||||
|
||||
if let Some(item) = setup
|
||||
.app_data
|
||||
.lock()
|
||||
.get_container_by_id(&ContainerId::from("1"))
|
||||
{
|
||||
item.rx = rx;
|
||||
}
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
|
||||
})
|
||||
.unwrap();
|
||||
assert_snapshot!(setup.terminal.backend());
|
||||
for (row_index, result_row) in get_result(&setup) {
|
||||
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
|
||||
match (row_index, result_cell_index) {
|
||||
// border
|
||||
(9, _) | (1..=9, 0 | 39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Border first row only
|
||||
(0, 0..=3 | 35..=39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Title RX
|
||||
(0, 4..=19) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
// Title TX
|
||||
(0, 20..=34) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// Y axis
|
||||
(1..=8, 12) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// RX max
|
||||
(4, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
// TX max
|
||||
(6, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// RX dots
|
||||
x if RX_DOTS.contains(&(row_index, result_cell_index)) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(result_cell.fg, Color::Reset);
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test with RX & TX data
|
||||
fn test_draw_blocks_charts_running_with_data_tx_and_rx() {
|
||||
let mut setup = test_setup(40, 10, true, true);
|
||||
let mut rx = NetworkBandwidth::new();
|
||||
let mut tx = NetworkBandwidth::new();
|
||||
for i in 0..=20 {
|
||||
rx.push(2000 * i * (10 + 7 * i));
|
||||
tx.push(200 * i * (10 + 7 * i));
|
||||
}
|
||||
|
||||
if let Some(item) = setup
|
||||
.app_data
|
||||
.lock()
|
||||
.get_container_by_id(&ContainerId::from("1"))
|
||||
{
|
||||
item.rx = rx;
|
||||
item.tx = tx;
|
||||
}
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
|
||||
})
|
||||
.unwrap();
|
||||
assert_snapshot!(setup.terminal.backend());
|
||||
for (row_index, result_row) in get_result(&setup) {
|
||||
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
|
||||
match (row_index, result_cell_index) {
|
||||
// border
|
||||
(9, _) | (1..=9, 0 | 39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Border first row only
|
||||
(0, 0..=3 | 36..=39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Title RX
|
||||
(0, 4..=19) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
// Title TX
|
||||
(0, 20..=35) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// Y axis
|
||||
(1..=8, 12) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// RX max
|
||||
(4, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
// TX max
|
||||
(6, 1..=10) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// TX dots
|
||||
x if COMBINED_DOTS_TX.contains(&(row_index, result_cell_index)) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_TX);
|
||||
}
|
||||
// RX dots
|
||||
x if COMBINED_DOTS_RX.contains(&(row_index, result_cell_index)) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, COLOR_RX);
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(result_cell.fg, Color::Reset);
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Whens status paused, some text is now Yellow
|
||||
fn test_draw_blocks_charts_paused() {
|
||||
let mut setup = test_setup(40, 10, true, true);
|
||||
setup.app_data.lock().containers.items[0].state = State::Paused;
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
|
||||
})
|
||||
.unwrap();
|
||||
assert_snapshot!(setup.terminal.backend());
|
||||
for (row_index, result_row) in get_result(&setup) {
|
||||
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
|
||||
match (row_index, result_cell_index) {
|
||||
// border
|
||||
(9, _) | (1..=9, 0 | 39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Border first row only
|
||||
(0, 0..=4 | 34..=39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Title & y-axis max
|
||||
(0, 5..=33) | (4 | 6, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::Yellow);
|
||||
}
|
||||
// Y axis
|
||||
(1..=8, 10) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(result_cell.fg, Color::Reset);
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Whens status dead, some text is now red
|
||||
fn test_draw_blocks_charts_dead() {
|
||||
let mut setup = test_setup(40, 10, true, true);
|
||||
setup.app_data.lock().containers.items[0].state = State::Dead;
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
super::draw(setup.area, setup.app_data.lock().config.app_colors, f, &fd);
|
||||
})
|
||||
.unwrap();
|
||||
assert_snapshot!(setup.terminal.backend());
|
||||
for (row_index, result_row) in get_result(&setup) {
|
||||
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
|
||||
match (row_index, result_cell_index) {
|
||||
// border
|
||||
(9, _) | (1..=9, 0 | 39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Border first row only
|
||||
(0, 0..=4 | 34..=39) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
// Title & y-axis max
|
||||
(0, 5..=33) | (4 | 6, 1..=9) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::Red);
|
||||
}
|
||||
// Y axis
|
||||
(1..=8, 10) => {
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
assert_eq!(result_cell.fg, Color::White);
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(result_cell.fg, Color::Reset);
|
||||
assert_eq!(result_cell.bg, Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Custom colours correctly applied to each part of the charts
|
||||
fn test_draw_blocks_charts_custom_colors() {
|
||||
let mut colors = AppColors::new();
|
||||
|
||||
colors.chart_bandwidth.background = Color::White;
|
||||
colors.chart_bandwidth.border = Color::Red;
|
||||
colors.chart_bandwidth.max_rx = Color::Green;
|
||||
colors.chart_bandwidth.max_tx = Color::Magenta;
|
||||
colors.chart_bandwidth.title_rx = Color::LightGreen;
|
||||
colors.chart_bandwidth.title_tx = Color::LightRed;
|
||||
colors.chart_bandwidth.points_rx = Color::Black;
|
||||
colors.chart_bandwidth.points_tx = Color::Blue;
|
||||
colors.chart_bandwidth.y_axis = Color::Yellow;
|
||||
|
||||
let mut setup = test_setup(40, 10, true, true);
|
||||
|
||||
let mut rx = NetworkBandwidth::new();
|
||||
let mut tx = NetworkBandwidth::new();
|
||||
for i in 0..=20 {
|
||||
rx.push(2000 * i * (10 + 7 * i));
|
||||
tx.push(200 * i * (10 + 7 * i));
|
||||
}
|
||||
|
||||
if let Some(item) = setup
|
||||
.app_data
|
||||
.lock()
|
||||
.get_container_by_id(&ContainerId::from("1"))
|
||||
{
|
||||
item.rx = rx;
|
||||
item.tx = tx;
|
||||
}
|
||||
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
super::draw(setup.area, colors, f, &fd);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_snapshot!(setup.terminal.backend());
|
||||
|
||||
for (row_index, result_row) in get_result(&setup) {
|
||||
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
|
||||
match (row_index, result_cell_index) {
|
||||
// border
|
||||
(9, _) | (1..=9, 0 | 39) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Red);
|
||||
}
|
||||
// Border first row only
|
||||
(0, 0..=3 | 36..=39) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Red);
|
||||
}
|
||||
// Title RX
|
||||
(0, 4..=19) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::LightGreen);
|
||||
}
|
||||
// Title TX
|
||||
(0, 20..=35) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::LightRed);
|
||||
}
|
||||
// Y axis
|
||||
(1..=8, 12) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Yellow);
|
||||
}
|
||||
// RX max
|
||||
(4, 1..=11) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Green);
|
||||
}
|
||||
// TX max
|
||||
(6, 1..=10) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Magenta);
|
||||
}
|
||||
// TX dots
|
||||
x if COMBINED_DOTS_TX.contains(&(row_index, result_cell_index)) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Blue);
|
||||
}
|
||||
// RX dots
|
||||
x if COMBINED_DOTS_RX.contains(&(row_index, result_cell_index)) => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Black);
|
||||
}
|
||||
_ => {
|
||||
assert_eq!(result_cell.bg, Color::White);
|
||||
assert_eq!(result_cell.fg, Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ use ratatui::{
|
||||
|
||||
use super::{CONSTRAINT_50_50, FrameData};
|
||||
use crate::{
|
||||
app_data::{ByteStats, CpuStats, State, Stats},
|
||||
app_data::{State, Stats},
|
||||
config::AppColors,
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ fn make_chart<'a, T: Stats + Display>(
|
||||
|
||||
/// Draw the cpu + mem charts
|
||||
pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) {
|
||||
if let Some((cpu, mem)) = fd.chart_data.as_ref() {
|
||||
if let Some(x) = fd.chart_data.as_ref() {
|
||||
let area = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(CONSTRAINT_50_50)
|
||||
@@ -129,34 +129,34 @@ pub fn draw(area: Rect, colors: AppColors, f: &mut Frame, fd: &FrameData) {
|
||||
.marker(symbols::Marker::Dot)
|
||||
.style(Style::default().fg(colors.chart_cpu.points))
|
||||
.graph_type(GraphType::Line)
|
||||
.data(&cpu.0),
|
||||
.data(&x.cpu.dataset),
|
||||
];
|
||||
let mem_dataset = vec![
|
||||
Dataset::default()
|
||||
.marker(symbols::Marker::Dot)
|
||||
.style(Style::default().fg(colors.chart_memory.points))
|
||||
.graph_type(GraphType::Line)
|
||||
.data(&mem.0),
|
||||
.data(&x.memory.dataset),
|
||||
];
|
||||
|
||||
let cpu_stats = CpuStats::new(cpu.0.last().map_or(0.00, |f| f.1));
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
let mem_stats = ByteStats::new(mem.0.last().map_or(0, |f| f.1 as u64));
|
||||
// let cpu_stats = CpuStats::new(cpu.0.last().map_or(0.00, |f| f.1));
|
||||
// #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
// let mem_stats = ByteStats::new(mem.0.last().map_or(0, |f| f.1 as u64));
|
||||
let cpu_chart = make_chart(
|
||||
ChartVariant::Cpu,
|
||||
colors,
|
||||
&cpu_stats,
|
||||
&x.cpu.current,
|
||||
cpu_dataset,
|
||||
&cpu.1,
|
||||
cpu.2,
|
||||
&x.cpu.max,
|
||||
x.state,
|
||||
);
|
||||
let mem_chart = make_chart(
|
||||
ChartVariant::Memory,
|
||||
colors,
|
||||
&mem_stats,
|
||||
&x.memory.current,
|
||||
mem_dataset,
|
||||
&mem.1,
|
||||
mem.2,
|
||||
&x.memory.max,
|
||||
x.state,
|
||||
);
|
||||
|
||||
f.render_widget(cpu_chart, area[0]);
|
||||
@@ -175,7 +175,7 @@ mod tests {
|
||||
config::AppColors,
|
||||
ui::{
|
||||
FrameData,
|
||||
draw_blocks::tests::{COLOR_ORANGE, get_result, insert_chart_data, test_setup},
|
||||
draw_blocks::tests::{COLOR_ORANGE, get_result, insert_all_chart_data, test_setup},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -274,7 +274,7 @@ mod tests {
|
||||
fn test_draw_blocks_charts_running_some() {
|
||||
let mut setup = test_setup(80, 10, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
setup
|
||||
@@ -323,7 +323,7 @@ mod tests {
|
||||
fn test_draw_blocks_charts_paused() {
|
||||
let mut setup = test_setup(80, 10, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
setup.app_data.lock().containers.items[0].state = State::Paused;
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
@@ -369,7 +369,7 @@ mod tests {
|
||||
/// When dead, text is red
|
||||
fn test_draw_blocks_charts_dead() {
|
||||
let mut setup = test_setup(80, 10, true, true);
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
setup.app_data.lock().containers.items[0].state = State::Dead;
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
@@ -429,7 +429,7 @@ mod tests {
|
||||
|
||||
let mut setup = test_setup(80, 10, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
|
||||
|
||||
setup
|
||||
@@ -82,11 +82,19 @@ fn format_containers<'a>(colors: AppColors, i: &ContainerItem, widths: &Columns)
|
||||
colors.containers.text,
|
||||
),
|
||||
Span::styled(
|
||||
format!("{:>width$}{MARGIN}", i.rx, width = widths.net_rx.1.into()),
|
||||
format!(
|
||||
"{:>width$}{MARGIN}",
|
||||
i.rx.current_total(),
|
||||
width = widths.net_rx.1.into()
|
||||
),
|
||||
Style::default().fg(colors.containers.text_rx),
|
||||
),
|
||||
Span::styled(
|
||||
format!("{:>width$}{MARGIN}", i.tx, width = widths.net_tx.1.into()),
|
||||
format!(
|
||||
"{:>width$}{MARGIN}",
|
||||
i.tx.current_total(),
|
||||
width = widths.net_tx.1.into()
|
||||
),
|
||||
Style::default().fg(colors.containers.text_tx),
|
||||
),
|
||||
])
|
||||
|
||||
@@ -190,7 +190,6 @@ mod tests {
|
||||
AppColors::new(),
|
||||
&AppError::DockerExec,
|
||||
f,
|
||||
// TODO test me
|
||||
None,
|
||||
&Keymap::new(),
|
||||
Some(4),
|
||||
@@ -229,7 +228,6 @@ mod tests {
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
// TODO test me
|
||||
super::draw(
|
||||
colors,
|
||||
&AppError::DockerExec,
|
||||
@@ -272,7 +270,6 @@ mod tests {
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
// TODO test me
|
||||
super::draw(
|
||||
AppColors::new(),
|
||||
&AppError::DockerExec,
|
||||
@@ -297,7 +294,6 @@ mod tests {
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
// TODO test me
|
||||
super::draw(
|
||||
AppColors::new(),
|
||||
&AppError::DockerExec,
|
||||
@@ -322,7 +318,6 @@ mod tests {
|
||||
setup
|
||||
.terminal
|
||||
.draw(|f| {
|
||||
// TODO test me
|
||||
super::draw(
|
||||
AppColors::new(),
|
||||
&AppError::DockerExec,
|
||||
|
||||
@@ -134,11 +134,6 @@ pub fn draw(
|
||||
f.render_widget(paragraph, rect);
|
||||
}
|
||||
|
||||
// TODO TESTS
|
||||
// Test keymap
|
||||
// Test colors
|
||||
// Test offset y & x
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
|
||||
+12
-12
@@ -11,7 +11,8 @@ use crate::config::AppColors;
|
||||
|
||||
use super::{FrameData, GuiState, SelectablePanel, Status, gui_state::Region};
|
||||
|
||||
pub mod charts;
|
||||
pub mod chart_bandwidth;
|
||||
pub mod chart_cpu_mem;
|
||||
pub mod commands;
|
||||
pub mod containers;
|
||||
pub mod delete_confirm;
|
||||
@@ -39,7 +40,6 @@ pub const REPO: &str = env!("CARGO_PKG_REPOSITORY");
|
||||
pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
||||
pub const MARGIN: &str = " ";
|
||||
pub const SELECT_ARROW: &str = "▶ ";
|
||||
// TODO use me all over the place
|
||||
pub const LEFT_ARROW: &str = "←";
|
||||
pub const RIGHT_ARROW: &str = "→";
|
||||
pub const DOWN_ARROW: &str = "↓";
|
||||
@@ -246,7 +246,7 @@ pub mod tests {
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
// Add fixed data to the cpu & mem vecdeques
|
||||
pub fn insert_chart_data(setup: &TuiTestSetup) {
|
||||
pub fn insert_all_chart_data(setup: &TuiTestSetup) {
|
||||
for i in 1..=10 {
|
||||
setup.app_data.lock().update_stats_by_id(
|
||||
&setup.ids[0],
|
||||
@@ -277,7 +277,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout() {
|
||||
let mut setup = test_setup(160, 30, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
@@ -305,7 +305,7 @@ pub mod tests {
|
||||
/// Check that the whole layout is drawn correctly
|
||||
fn test_draw_blocks_whole_layout_with_filter_bar() {
|
||||
let mut setup = test_setup(160, 30, true, true);
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
|
||||
setup.app_data.lock().containers.items[1]
|
||||
@@ -341,7 +341,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout_long_name() {
|
||||
let mut setup = test_setup(190, 30, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
@@ -374,7 +374,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout_no_logs() {
|
||||
let mut setup = test_setup(160, 30, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
@@ -403,7 +403,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout_short_height_logs() {
|
||||
let mut setup = test_setup(160, 30, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
@@ -435,7 +435,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout_help_panel() {
|
||||
let mut setup = test_setup(160, 40, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
@@ -465,7 +465,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout_error() {
|
||||
let mut setup = test_setup(160, 40, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
@@ -499,7 +499,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout_delete() {
|
||||
let mut setup = test_setup(160, 40, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
@@ -531,7 +531,7 @@ pub mod tests {
|
||||
fn test_draw_blocks_whole_layout_info_box() {
|
||||
let mut setup = test_setup(160, 40, true, true);
|
||||
|
||||
insert_chart_data(&setup);
|
||||
insert_all_chart_data(&setup);
|
||||
insert_logs(&setup);
|
||||
setup.app_data.lock().containers.items[0]
|
||||
.ports
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭─── rx: 566.00 kb/s tx: 56.60 kb/s ───╮"
|
||||
"│ │ • │"
|
||||
"│ │ •• │"
|
||||
"│ │ •• │"
|
||||
"│566.00 kb/s│ •• │"
|
||||
"│ │ • │"
|
||||
"│56.60 kb/s │ •• │"
|
||||
"│ │•• ••• │"
|
||||
"│ │•••••• │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭──── rx: 0.00 kb/s tx: 0.00 kb/s ─────╮"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"│0.00 kb/s│ │"
|
||||
"│ │ │"
|
||||
"│0.00 kb/s│ │"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭──── rx: 0.00 kb/s tx: 0.00 kb/s ─────╮"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"│0.00 kb/s│ │"
|
||||
"│ │ │"
|
||||
"│0.00 kb/s│ │"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭──── rx: 0.00 kb/s tx: 0.00 kb/s ─────╮"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"│0.00 kb/s│ │"
|
||||
"│ │ │"
|
||||
"│0.00 kb/s│ │"
|
||||
"│ │ │"
|
||||
"│ │ │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭─── rx: 0.00 kb/s tx: 205.00 kb/s ────╮"
|
||||
"│ │ • │"
|
||||
"│ │ •• │"
|
||||
"│ │ •• │"
|
||||
"│205.00 kb/s│ •• │"
|
||||
"│ │ • │"
|
||||
"│0.00 kb/s │ •• │"
|
||||
"│ │•• │"
|
||||
"│ │ │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭─── rx: 566.00 kb/s tx: 0.00 kb/s ────╮"
|
||||
"│ │ • │"
|
||||
"│ │ •• │"
|
||||
"│ │ •• │"
|
||||
"│566.00 kb/s│ •• │"
|
||||
"│ │ • │"
|
||||
"│0.00 kb/s │ •• │"
|
||||
"│ │•• │"
|
||||
"│ │• │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭─── rx: 0.00 kb/s tx: 205.00 kb/s ────╮"
|
||||
"│ │ • │"
|
||||
"│ │ •• │"
|
||||
"│ │ •• │"
|
||||
"│205.00 kb/s│ •• │"
|
||||
"│ │ • │"
|
||||
"│0.00 kb/s │ •• │"
|
||||
"│ │•• │"
|
||||
"│ │ │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_bandwidth.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭─── rx: 566.00 kb/s tx: 56.60 kb/s ───╮"
|
||||
"│ │ • │"
|
||||
"│ │ •• │"
|
||||
"│ │ •• │"
|
||||
"│566.00 kb/s│ •• │"
|
||||
"│ │ • │"
|
||||
"│56.60 kb/s │ •• │"
|
||||
"│ │•• ••• │"
|
||||
"│ │•••••• │"
|
||||
"╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_cpu_mem.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • │"
|
||||
"│ │ •• ││ │ •• │"
|
||||
"│ │ • • ││ │ •• │"
|
||||
"│ │ • • ││ │ • • │"
|
||||
"│ │ • • ││ │ •• • │"
|
||||
"│ │ • •• ││ │ • • │"
|
||||
"│ │•• •• ││ │• • │"
|
||||
"│ │ ││ │ │"
|
||||
"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_cpu_mem.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • │"
|
||||
"│ │ •• ││ │ •• │"
|
||||
"│ │ • • ││ │ •• │"
|
||||
"│ │ • • ││ │ • • │"
|
||||
"│ │ • • ││ │ •• • │"
|
||||
"│ │ • •• ││ │ • • │"
|
||||
"│ │•• •• ││ │• • │"
|
||||
"│ │ ││ │ │"
|
||||
"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_cpu_mem.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • │"
|
||||
"│ │ •• ││ │ •• │"
|
||||
"│ │ • • ││ │ •• │"
|
||||
"│ │ • • ││ │ • • │"
|
||||
"│ │ • • ││ │ •• • │"
|
||||
"│ │ • •• ││ │ • • │"
|
||||
"│ │•• •• ││ │• • │"
|
||||
"│ │ ││ │ │"
|
||||
"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_cpu_mem.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭───────────── cpu 00.00% ─────────────╮╭─────────── memory 0.00 kB ───────────╮"
|
||||
"│00.00%│ ││0.00 kB│ │"
|
||||
"│ │ ││ │ │"
|
||||
"│ │ ││ │ │"
|
||||
"│ │ ││ │ │"
|
||||
"│ │ ││ │ │"
|
||||
"│ │ ││ │ │"
|
||||
"│ │ ││ │ │"
|
||||
"│ │ ││ │ │"
|
||||
"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/ui/draw_blocks/chart_cpu_mem.rs
|
||||
expression: setup.terminal.backend()
|
||||
---
|
||||
"╭───────────── cpu 03.00% ─────────────╮╭────────── memory 30.00 kB ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • │"
|
||||
"│ │ •• ││ │ •• │"
|
||||
"│ │ • • ││ │ •• │"
|
||||
"│ │ • • ││ │ • • │"
|
||||
"│ │ • • ││ │ •• • │"
|
||||
"│ │ • •• ││ │ • • │"
|
||||
"│ │•• •• ││ │• • │"
|
||||
"│ │ ││ │ │"
|
||||
"╰──────────────────────────────────────╯╰──────────────────────────────────────╯"
|
||||
+7
-7
@@ -25,10 +25,10 @@ expression: setup.terminal.backend()
|
||||
"│ │"
|
||||
"│ │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ ip private public│"
|
||||
"│ │ • • ││ │ •• • ││ 8001 │"
|
||||
"│ │ ••• • ││ │ •• • ││127.0.0.1 8003 8003│"
|
||||
"│ │ •• ••• ││ │ •• •• ││ │"
|
||||
"│ │• • ││ │• • ││ │"
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭────────────── cpu 03.00% ───────────────╮╭──────────── memory 30.00 kB ────────────╮╭────── rx: 0.00 kb/s tx: 0.00 kb/s ──────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ │••••••• │ │ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ │ •• │ │ 8001 │"
|
||||
"│ │ •• • ││ │ •• • ││0.00 kb/s│ •• │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │• • ││ │• • ││ │ • │ │ │"
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
|
||||
+10
-10
@@ -32,13 +32,13 @@ expression: setup.terminal.backend()
|
||||
"│ │"
|
||||
"│ │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • ││ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ 8001 │"
|
||||
"│ │ • • ││ │ • • ││127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││ │"
|
||||
"│ │ •• • ││ │ • • ││ │"
|
||||
"│ │ • • • ││ │ • •• ││ │"
|
||||
"│ │•• •• ││ │•• •• ││ │"
|
||||
"│ │ ││ │ ││ │"
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭────────────── cpu 03.00% ───────────────╮╭──────────── memory 30.00 kB ────────────╮╭────── rx: 0.00 kb/s tx: 0.00 kb/s ──────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • ││ │••••••• │ │ ip private public│"
|
||||
"│ │ •• ││ │ •• ││ │ •• │ │ 8001 │"
|
||||
"│ │ • • ││ │ • • ││ │ •• │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││0.00 kb/s│ •• │ │ │"
|
||||
"│ │ • • ││ │ • • ││ │ • │ │ │"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │•• • ││ │•• •• ││ │ • │ │ │"
|
||||
"│ │ ││ │ ││ │ • │ │ │"
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
|
||||
+10
-10
@@ -32,13 +32,13 @@ expression: setup.terminal.backend()
|
||||
"│ │"
|
||||
"│ │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • ││ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ 8001 │"
|
||||
"│ │ • • ││ │ • • ││127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││ │"
|
||||
"│ │ •• • ││ │ • • ││ │"
|
||||
"│ │ • • • ││ │ • •• ││ │"
|
||||
"│ │•• •• ││ │•• •• ││ │"
|
||||
"│ │ ││ │ ││ │"
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭────────────── cpu 03.00% ───────────────╮╭──────────── memory 30.00 kB ────────────╮╭────── rx: 0.00 kb/s tx: 0.00 kb/s ──────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • ││ │••••••• │ │ ip private public│"
|
||||
"│ │ •• ││ │ •• ││ │ •• │ │ 8001 │"
|
||||
"│ │ • • ││ │ • • ││ │ •• │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││0.00 kb/s│ •• │ │ │"
|
||||
"│ │ • • ││ │ • • ││ │ • │ │ │"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │•• • ││ │•• •• ││ │ • │ │ │"
|
||||
"│ │ ││ │ ││ │ • │ │ │"
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
|
||||
+10
-10
@@ -32,13 +32,13 @@ expression: setup.terminal.backend()
|
||||
"│ │ 1 ~ 9 sort by header - or click header 0 stop sort │ │"
|
||||
"│ │ Tab Back Tab change panel m toggle mouse capture - allows text selection │ │"
|
||||
"╰──────────────────────│ s save logs to file │──────────────────────╯"
|
||||
"╭──────────────────────│ │──── ports ───────────╮"
|
||||
"│10.00%│ • ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ 8001 │"
|
||||
"│ │ • • ││ │ • • ││127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││ │"
|
||||
"│ │ •• • ││ │ • • ││ │"
|
||||
"│ │ • • • ││ │ • •• ││ │"
|
||||
"│ │•• •• ││ │•• •• ││ │"
|
||||
"│ │ ││ │ ││ │"
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭────────────── cpu 03.│ │──── ports ───────────╮"
|
||||
"│10.00%│ • ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ip private public│"
|
||||
"│ │ •• ││ │ •• ││ │ •• │ │ 8001 │"
|
||||
"│ │ • • ││ │ • • ││ │ •• │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││0.00 kb/s│ •• │ │ │"
|
||||
"│ │ • • ││ │ • • ││ │ • │ │ │"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │•• • ││ │•• •• ││ │ • │ │ │"
|
||||
"│ │ ││ │ ││ │ • │ │ │"
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
|
||||
+10
-10
@@ -32,13 +32,13 @@ expression: setup.terminal.backend()
|
||||
"│ │"
|
||||
"│ │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • ││ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ 8001 │"
|
||||
"│ │ • • ││ │ • • ││127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││ │"
|
||||
"│ │ •• • ││ │ • • ││ │"
|
||||
"│ │ • • • ││ │ • •• ││ "
|
||||
"│ │•• •• ││ │•• •• ││ This is a test "
|
||||
"│ │ ││ │ ││ "
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰─────── "
|
||||
"╭────────────── cpu 03.00% ───────────────╮╭──────────── memory 30.00 kB ────────────╮╭────── rx: 0.00 kb/s tx: 0.00 kb/s ──────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ • ││ │••••••• │ │ ip private public│"
|
||||
"│ │ •• ││ │ •• ││ │ •• │ │ 8001 │"
|
||||
"│ │ • • ││ │ • • ││ │ •• │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • • ││ │ • • ││0.00 kb/s│ •• │ │ │"
|
||||
"│ │ • • ││ │ • • ││ │ • │ │ │"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ "
|
||||
"│ │•• • ││ │•• •• ││ │ • │ │ This is a test "
|
||||
"│ │ ││ │ ││ │ • │ │ "
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰─────── "
|
||||
|
||||
+7
-7
@@ -25,10 +25,10 @@ expression: setup.terminal.backend()
|
||||
"│ │"
|
||||
"│ │"
|
||||
"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
"╭───────────────────────────────── cpu 03.00% ─────────────────────────────────╮╭────────────────────────────── memory 30.00 kB ───────────────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
|
||||
"│ │ •• • ││ │ •• • ││ 8001 │"
|
||||
"│ │ ••• • ││ │ •••• • ││127.0.0.1 8003 8003│"
|
||||
"│ │ ••• ••• ││ │ •• ••• ││ │"
|
||||
"│ │• • ││ │• • ││ │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭─────────────────── cpu 03.00% ────────────────────╮╭───────────────── memory 30.00 kB ─────────────────╮╭────────── rx: 0.00 kb/s tx: 0.00 kb/s ───────────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ • ││100.00 kB│ •• ││ │•••••• • │ │ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ │ • • │ │ 8001 │"
|
||||
"│ │ ••• • ││ │ •• • ││0.00 kb/s│ • • │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • ••• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │• • ││ │• • ││ │ • │ │ │"
|
||||
"╰───────────────────────────────────────────────────╯╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
|
||||
+7
-7
@@ -25,10 +25,10 @@ expression: setup.terminal.backend()
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
|
||||
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ ip private public│"
|
||||
"│ │ • • ││ │ •• • ││ 8001 │"
|
||||
"│ │ ••• • ││ │ •• • ││127.0.0.1 8003 8003│"
|
||||
"│ │ •• ••• ││ │ •• •• ││ │"
|
||||
"│ │• • ││ │• • ││ │"
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭────────────── cpu 03.00% ───────────────╮╭──────────── memory 30.00 kB ────────────╮╭────── rx: 0.00 kb/s tx: 0.00 kb/s ──────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ │••••••• │ │ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ │ •• │ │ 8001 │"
|
||||
"│ │ •• • ││ │ •• • ││0.00 kb/s│ •• │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │• • ││ │• • ││ │ • │ │ │"
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
|
||||
+7
-7
@@ -25,10 +25,10 @@ expression: setup.terminal.backend()
|
||||
"│ line 2 │"
|
||||
"│▶ line 3 │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ ip private public│"
|
||||
"│ │ • • ││ │ •• • ││ 8001 │"
|
||||
"│ │ ••• • ││ │ •• • ││127.0.0.1 8003 8003│"
|
||||
"│ │ •• ••• ││ │ •• •• ││ │"
|
||||
"│ │• • ││ │• • ││ │"
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭────────────── cpu 03.00% ───────────────╮╭──────────── memory 30.00 kB ────────────╮╭────── rx: 0.00 kb/s tx: 0.00 kb/s ──────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ │••••••• │ │ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ │ •• │ │ 8001 │"
|
||||
"│ │ •• • ││ │ •• • ││0.00 kb/s│ •• │ │127.0.0.1 8003 8003│"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │• • ││ │• • ││ │ • │ │ │"
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
|
||||
+7
-7
@@ -24,11 +24,11 @@ expression: setup.terminal.backend()
|
||||
"│ │"
|
||||
"│ │"
|
||||
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ ip private public│"
|
||||
"│ │ • • ││ │ •• • ││ 8001 │"
|
||||
"│ │ ••• • ││ │ •• • ││ │"
|
||||
"│ │ •• ••• ││ │ •• •• ││ │"
|
||||
"│ │• • ││ │• • ││ │"
|
||||
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
|
||||
"╭────────────── cpu 03.00% ───────────────╮╭──────────── memory 30.00 kB ────────────╮╭────── rx: 0.00 kb/s tx: 0.00 kb/s ──────╮ ╭────────── ports ───────────╮"
|
||||
"│10.00%│ •• ││100.00 kB│ •• ││ │••••••• │ │ ip private public│"
|
||||
"│ │ ••• ││ │ ••• ││ │ •• │ │ 8001 │"
|
||||
"│ │ •• • ││ │ •• • ││0.00 kb/s│ •• │ │ │"
|
||||
"│ │ • •• ││ │ • •• ││0.00 kb/s│ • │ │ │"
|
||||
"│ │• • ││ │• • ││ │ • │ │ │"
|
||||
"╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯╰─────────────────────────────────────────╯ ╰────────────────────────────╯"
|
||||
" Esc clear ← by → Name Image Status All filter term: r_1 "
|
||||
|
||||
+11
-4
@@ -30,8 +30,8 @@ pub use self::color_match::*;
|
||||
pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status};
|
||||
use crate::{
|
||||
app_data::{
|
||||
AppData, Columns, ContainerId, ContainerPorts, CpuTuple, FilterBy, Header, LogSearch,
|
||||
MemTuple, SortedOrder, State,
|
||||
AppData, ChartsData, Columns, ContainerId, ContainerPorts, FilterBy, Header, LogSearch,
|
||||
SortedOrder, State,
|
||||
},
|
||||
app_error::AppError,
|
||||
config::{AppColors, Keymap},
|
||||
@@ -301,7 +301,7 @@ impl Ui {
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct FrameData {
|
||||
chart_data: Option<(CpuTuple, MemTuple)>,
|
||||
chart_data: Option<ChartsData>,
|
||||
color_logs: bool,
|
||||
columns: Columns,
|
||||
container_title: String,
|
||||
@@ -462,7 +462,14 @@ fn draw_frame(
|
||||
.constraints([Constraint::Min(1), Constraint::Max(ports_len)])
|
||||
.split(upper_main[1]);
|
||||
|
||||
draw_blocks::charts::draw(lower[0], colors, f, fd);
|
||||
let charts_rect = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(66), Constraint::Percentage(33)])
|
||||
.split(lower[0]);
|
||||
|
||||
draw_blocks::chart_cpu_mem::draw(charts_rect[0], colors, f, fd);
|
||||
draw_blocks::chart_bandwidth::draw(charts_rect[1], colors, f, fd);
|
||||
|
||||
draw_blocks::ports::draw(lower[1], colors, f, fd);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user