mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-12-08 06:20:21 +01:00
Update dependencies to most recent versions if applicable. Refactor to deal with breaking changes. Drop MSRV due to dependency issues, just support stable and later.
292 lines
11 KiB
Rust
292 lines
11 KiB
Rust
use lazy_static::lazy_static;
|
|
use std::cmp::max;
|
|
|
|
use crate::{
|
|
app::App,
|
|
canvas::{drawing_utils::get_variable_intrinsic_widths, Painter},
|
|
constants::*,
|
|
};
|
|
|
|
use tui::{
|
|
backend::Backend,
|
|
layout::{Constraint, Direction, Layout, Rect},
|
|
symbols::Marker,
|
|
terminal::Frame,
|
|
text::Span,
|
|
widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table},
|
|
};
|
|
|
|
const NETWORK_HEADERS: [&str; 4] = ["RX", "TX", "Total RX", "Total TX"];
|
|
|
|
lazy_static! {
|
|
static ref NETWORK_HEADERS_LENS: Vec<usize> = NETWORK_HEADERS
|
|
.iter()
|
|
.map(|entry| max(FORCE_MIN_THRESHOLD, entry.len()))
|
|
.collect::<Vec<_>>();
|
|
}
|
|
|
|
pub trait NetworkGraphWidget {
|
|
fn draw_network<B: Backend>(
|
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
|
);
|
|
|
|
fn draw_network_graph<B: Backend>(
|
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
|
hide_legend: bool,
|
|
);
|
|
|
|
fn draw_network_labels<B: Backend>(
|
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
|
);
|
|
}
|
|
|
|
impl NetworkGraphWidget for Painter {
|
|
fn draw_network<B: Backend>(
|
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
|
) {
|
|
if app_state.app_config_fields.use_old_network_legend {
|
|
let network_chunk = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.margin(0)
|
|
.constraints(
|
|
[
|
|
Constraint::Length(max(draw_loc.height as i64 - 5, 0) as u16),
|
|
Constraint::Length(5),
|
|
]
|
|
.as_ref(),
|
|
)
|
|
.split(draw_loc);
|
|
|
|
self.draw_network_graph(f, app_state, network_chunk[0], widget_id, true);
|
|
self.draw_network_labels(f, app_state, network_chunk[1], widget_id);
|
|
} else {
|
|
self.draw_network_graph(f, app_state, draw_loc, widget_id, false);
|
|
}
|
|
}
|
|
|
|
fn draw_network_graph<B: Backend>(
|
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
|
hide_legend: bool,
|
|
) {
|
|
if let Some(network_widget_state) = app_state.net_state.widget_states.get_mut(&widget_id) {
|
|
let network_data_rx: &[(f64, f64)] = &app_state.canvas_data.network_data_rx;
|
|
let network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx;
|
|
|
|
let display_time_labels = vec![
|
|
Span::styled(
|
|
format!("{}s", network_widget_state.current_display_time / 1000),
|
|
self.colours.graph_style,
|
|
),
|
|
Span::styled("0s".to_string(), self.colours.graph_style),
|
|
];
|
|
let x_axis = if app_state.app_config_fields.hide_time
|
|
|| (app_state.app_config_fields.autohide_time
|
|
&& network_widget_state.autohide_timer.is_none())
|
|
{
|
|
Axis::default().bounds([-(network_widget_state.current_display_time as f64), 0.0])
|
|
} else if let Some(time) = network_widget_state.autohide_timer {
|
|
if std::time::Instant::now().duration_since(time).as_millis()
|
|
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
|
{
|
|
Axis::default()
|
|
.bounds([-(network_widget_state.current_display_time as f64), 0.0])
|
|
.style(self.colours.graph_style)
|
|
.labels(display_time_labels)
|
|
} else {
|
|
network_widget_state.autohide_timer = None;
|
|
Axis::default()
|
|
.bounds([-(network_widget_state.current_display_time as f64), 0.0])
|
|
}
|
|
} else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT {
|
|
Axis::default().bounds([-(network_widget_state.current_display_time as f64), 0.0])
|
|
} else {
|
|
Axis::default()
|
|
.bounds([-(network_widget_state.current_display_time as f64), 0.0])
|
|
.style(self.colours.graph_style)
|
|
.labels(display_time_labels)
|
|
};
|
|
|
|
// 0 is offset.
|
|
let y_axis_labels = vec![
|
|
Span::styled("0B", self.colours.graph_style),
|
|
Span::styled("1KiB", self.colours.graph_style),
|
|
Span::styled("1MiB", self.colours.graph_style),
|
|
Span::styled("1GiB", self.colours.graph_style),
|
|
];
|
|
let y_axis = Axis::default()
|
|
.style(self.colours.graph_style)
|
|
.bounds([-0.5, 30_f64])
|
|
.labels(y_axis_labels);
|
|
|
|
let title = if app_state.is_expanded {
|
|
const TITLE_BASE: &str = " Network ── Esc to go back ";
|
|
Span::styled(
|
|
format!(
|
|
" Network ─{}─ Esc to go back ",
|
|
"─".repeat(
|
|
usize::from(draw_loc.width)
|
|
.saturating_sub(TITLE_BASE.chars().count() + 2)
|
|
)
|
|
),
|
|
self.colours.highlighted_border_style,
|
|
)
|
|
} else {
|
|
Span::styled(" Network ".to_string(), self.colours.widget_title_style)
|
|
};
|
|
|
|
let legend_constraints = if hide_legend {
|
|
(Constraint::Ratio(0, 1), Constraint::Ratio(0, 1))
|
|
} else {
|
|
(Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))
|
|
};
|
|
|
|
let dataset = if app_state.app_config_fields.use_old_network_legend && !hide_legend {
|
|
let mut ret_val = vec![];
|
|
|
|
ret_val.push(
|
|
Dataset::default()
|
|
.name(format!("RX: {:7}", app_state.canvas_data.rx_display))
|
|
.marker(if app_state.app_config_fields.use_dot {
|
|
Marker::Dot
|
|
} else {
|
|
Marker::Braille
|
|
})
|
|
.style(self.colours.rx_style)
|
|
.data(&network_data_rx)
|
|
.graph_type(tui::widgets::GraphType::Line),
|
|
);
|
|
|
|
ret_val.push(
|
|
Dataset::default()
|
|
.name(format!("TX: {:7}", app_state.canvas_data.tx_display))
|
|
.marker(if app_state.app_config_fields.use_dot {
|
|
Marker::Dot
|
|
} else {
|
|
Marker::Braille
|
|
})
|
|
.style(self.colours.tx_style)
|
|
.data(&network_data_tx)
|
|
.graph_type(tui::widgets::GraphType::Line),
|
|
);
|
|
ret_val.push(
|
|
Dataset::default()
|
|
.name(format!(
|
|
"Total RX: {:7}",
|
|
app_state.canvas_data.total_rx_display
|
|
))
|
|
.style(self.colours.total_rx_style),
|
|
);
|
|
|
|
ret_val.push(
|
|
Dataset::default()
|
|
.name(format!(
|
|
"Total TX: {:7}",
|
|
app_state.canvas_data.total_tx_display
|
|
))
|
|
.style(self.colours.total_tx_style),
|
|
);
|
|
|
|
ret_val
|
|
} else {
|
|
let mut ret_val = vec![];
|
|
|
|
ret_val.push(
|
|
Dataset::default()
|
|
.name(&app_state.canvas_data.rx_display)
|
|
.marker(if app_state.app_config_fields.use_dot {
|
|
Marker::Dot
|
|
} else {
|
|
Marker::Braille
|
|
})
|
|
.style(self.colours.rx_style)
|
|
.data(&network_data_rx)
|
|
.graph_type(tui::widgets::GraphType::Line),
|
|
);
|
|
|
|
ret_val.push(
|
|
Dataset::default()
|
|
.name(&app_state.canvas_data.tx_display)
|
|
.marker(if app_state.app_config_fields.use_dot {
|
|
Marker::Dot
|
|
} else {
|
|
Marker::Braille
|
|
})
|
|
.style(self.colours.tx_style)
|
|
.data(&network_data_tx)
|
|
.graph_type(tui::widgets::GraphType::Line),
|
|
);
|
|
|
|
ret_val
|
|
};
|
|
|
|
f.render_widget(
|
|
Chart::new(dataset)
|
|
.block(
|
|
Block::default()
|
|
.title(title)
|
|
.borders(Borders::ALL)
|
|
.border_style(if app_state.current_widget.widget_id == widget_id {
|
|
self.colours.highlighted_border_style
|
|
} else {
|
|
self.colours.border_style
|
|
}),
|
|
)
|
|
.x_axis(x_axis)
|
|
.y_axis(y_axis)
|
|
.hidden_legend_constraints(legend_constraints),
|
|
draw_loc,
|
|
);
|
|
}
|
|
}
|
|
|
|
fn draw_network_labels<B: Backend>(
|
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
|
) {
|
|
let rx_display = &app_state.canvas_data.rx_display;
|
|
let tx_display = &app_state.canvas_data.tx_display;
|
|
let total_rx_display = &app_state.canvas_data.total_rx_display;
|
|
let total_tx_display = &app_state.canvas_data.total_tx_display;
|
|
|
|
// Gross but I need it to work...
|
|
let total_network = vec![vec![
|
|
rx_display,
|
|
tx_display,
|
|
total_rx_display,
|
|
total_tx_display,
|
|
]];
|
|
let mapped_network = total_network
|
|
.iter()
|
|
.map(|val| Row::StyledData(val.iter(), self.colours.text_style));
|
|
|
|
// Calculate widths
|
|
let width_ratios: Vec<f64> = vec![0.25, 0.25, 0.25, 0.25];
|
|
let lens: &[usize] = &NETWORK_HEADERS_LENS;
|
|
let width = f64::from(draw_loc.width);
|
|
|
|
let variable_intrinsic_results =
|
|
get_variable_intrinsic_widths(width as u16, &width_ratios, lens);
|
|
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
|
|
|
// Draw
|
|
f.render_widget(
|
|
Table::new(NETWORK_HEADERS.iter(), mapped_network)
|
|
.block(Block::default().borders(Borders::ALL).border_style(
|
|
if app_state.current_widget.widget_id == widget_id {
|
|
self.colours.highlighted_border_style
|
|
} else {
|
|
self.colours.border_style
|
|
},
|
|
))
|
|
.header_style(self.colours.table_header_style)
|
|
.style(self.colours.text_style)
|
|
.widths(
|
|
&(intrinsic_widths
|
|
.iter()
|
|
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
|
.collect::<Vec<_>>()),
|
|
),
|
|
draw_loc,
|
|
);
|
|
}
|
|
}
|