2020-04-07 03:04:04 +00:00
|
|
|
use itertools::izip;
|
2022-05-15 08:25:48 +00:00
|
|
|
use std::str::FromStr;
|
2020-02-29 03:24:24 +00:00
|
|
|
|
2019-12-06 05:57:04 +00:00
|
|
|
use tui::{
|
2020-02-29 23:47:30 +00:00
|
|
|
backend::Backend,
|
2020-04-07 03:38:00 +00:00
|
|
|
layout::{Constraint, Direction, Layout, Rect},
|
2020-09-27 00:04:34 +00:00
|
|
|
text::{Span, Spans},
|
2020-10-18 03:26:03 +00:00
|
|
|
widgets::Paragraph,
|
2020-04-07 03:38:00 +00:00
|
|
|
Frame, Terminal,
|
2019-09-11 03:37:20 +00:00
|
|
|
};
|
2019-09-12 02:10:49 +00:00
|
|
|
|
2020-02-08 23:00:50 +00:00
|
|
|
use canvas_colours::*;
|
2020-02-10 02:34:44 +00:00
|
|
|
|
2020-02-29 03:24:24 +00:00
|
|
|
use crate::{
|
2020-04-02 00:31:43 +00:00
|
|
|
app::{
|
|
|
|
self,
|
2020-04-07 03:38:00 +00:00
|
|
|
layout_manager::{BottomColRow, BottomLayout, BottomWidgetType},
|
|
|
|
App,
|
2020-04-02 00:31:43 +00:00
|
|
|
},
|
2020-02-29 22:07:47 +00:00
|
|
|
constants::*,
|
|
|
|
utils::error,
|
2020-11-15 10:16:47 +00:00
|
|
|
utils::error::BottomError,
|
2020-02-29 03:24:24 +00:00
|
|
|
};
|
|
|
|
|
2022-10-12 20:25:38 +00:00
|
|
|
pub mod canvas_colours;
|
2020-03-08 17:56:18 +00:00
|
|
|
mod dialogs;
|
2020-02-10 02:34:44 +00:00
|
|
|
mod drawing_utils;
|
2020-03-08 04:47:53 +00:00
|
|
|
mod widgets;
|
2019-09-12 02:10:49 +00:00
|
|
|
|
2020-11-15 10:16:47 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum ColourScheme {
|
|
|
|
Default,
|
|
|
|
DefaultLight,
|
|
|
|
Gruvbox,
|
|
|
|
GruvboxLight,
|
2021-02-15 19:12:43 +00:00
|
|
|
Nord,
|
|
|
|
NordLight,
|
2020-11-15 10:16:47 +00:00
|
|
|
Custom,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for ColourScheme {
|
|
|
|
type Err = BottomError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> error::Result<Self> {
|
|
|
|
let lower_case = s.to_lowercase();
|
|
|
|
match lower_case.as_str() {
|
|
|
|
"default" => Ok(ColourScheme::Default),
|
|
|
|
"default-light" => Ok(ColourScheme::DefaultLight),
|
|
|
|
"gruvbox" => Ok(ColourScheme::Gruvbox),
|
|
|
|
"gruvbox-light" => Ok(ColourScheme::GruvboxLight),
|
2021-02-15 19:12:43 +00:00
|
|
|
"nord" => Ok(ColourScheme::Nord),
|
|
|
|
"nord-light" => Ok(ColourScheme::NordLight),
|
2020-11-15 10:16:47 +00:00
|
|
|
_ => Err(BottomError::ConfigError(format!(
|
|
|
|
"\"{}\" is an invalid built-in color scheme.",
|
|
|
|
s
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-14 22:31:32 +00:00
|
|
|
/// Handles the canvas' state.
|
2020-02-05 04:21:44 +00:00
|
|
|
pub struct Painter {
|
2020-04-02 00:31:43 +00:00
|
|
|
pub colours: CanvasColours,
|
2020-02-29 22:05:01 +00:00
|
|
|
height: u16,
|
|
|
|
width: u16,
|
2020-09-27 00:04:34 +00:00
|
|
|
styled_help_text: Vec<Spans<'static>>,
|
2022-05-15 09:01:19 +00:00
|
|
|
is_mac_os: bool, // TODO: This feels out of place...
|
2022-05-15 08:39:47 +00:00
|
|
|
|
2022-05-15 09:01:19 +00:00
|
|
|
// TODO: Redo this entire thing.
|
2020-04-02 00:31:43 +00:00
|
|
|
row_constraints: Vec<Constraint>,
|
|
|
|
col_constraints: Vec<Vec<Constraint>>,
|
|
|
|
col_row_constraints: Vec<Vec<Vec<Constraint>>>,
|
|
|
|
layout_constraints: Vec<Vec<Vec<Vec<Constraint>>>>,
|
2020-04-07 03:38:00 +00:00
|
|
|
derived_widget_draw_locs: Vec<Vec<Vec<Vec<Rect>>>>,
|
2020-09-22 22:12:36 +00:00
|
|
|
widget_layout: BottomLayout,
|
2020-02-08 19:28:19 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 03:44:49 +00:00
|
|
|
impl Painter {
|
2022-10-12 20:25:38 +00:00
|
|
|
pub fn init(widget_layout: BottomLayout, colours: CanvasColours) -> anyhow::Result<Self> {
|
2020-04-02 00:31:43 +00:00
|
|
|
// Now for modularity; we have to also initialize the base layouts!
|
|
|
|
// We want to do this ONCE and reuse; after this we can just construct
|
|
|
|
// based on the console size.
|
|
|
|
|
|
|
|
let mut row_constraints = Vec::new();
|
|
|
|
let mut col_constraints = Vec::new();
|
|
|
|
let mut col_row_constraints = Vec::new();
|
|
|
|
let mut layout_constraints = Vec::new();
|
|
|
|
|
|
|
|
widget_layout.rows.iter().for_each(|row| {
|
|
|
|
if row.canvas_handle_height {
|
|
|
|
row_constraints.push(Constraint::Length(0));
|
|
|
|
} else {
|
|
|
|
row_constraints.push(Constraint::Ratio(
|
|
|
|
row.row_height_ratio,
|
|
|
|
widget_layout.total_row_height_ratio,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut new_col_constraints = Vec::new();
|
|
|
|
let mut new_widget_constraints = Vec::new();
|
|
|
|
let mut new_col_row_constraints = Vec::new();
|
|
|
|
row.children.iter().for_each(|col| {
|
|
|
|
if col.canvas_handle_width {
|
|
|
|
new_col_constraints.push(Constraint::Length(0));
|
|
|
|
} else {
|
|
|
|
new_col_constraints
|
|
|
|
.push(Constraint::Ratio(col.col_width_ratio, row.total_col_ratio));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut new_new_col_row_constraints = Vec::new();
|
|
|
|
let mut new_new_widget_constraints = Vec::new();
|
|
|
|
col.children.iter().for_each(|col_row| {
|
|
|
|
if col_row.canvas_handle_height {
|
|
|
|
new_new_col_row_constraints.push(Constraint::Length(0));
|
|
|
|
} else if col_row.flex_grow {
|
|
|
|
new_new_col_row_constraints.push(Constraint::Min(0));
|
|
|
|
} else {
|
|
|
|
new_new_col_row_constraints.push(Constraint::Ratio(
|
|
|
|
col_row.col_row_height_ratio,
|
|
|
|
col.total_col_row_ratio,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut new_new_new_widget_constraints = Vec::new();
|
|
|
|
col_row.children.iter().for_each(|widget| {
|
|
|
|
if widget.canvas_handle_width {
|
|
|
|
new_new_new_widget_constraints.push(Constraint::Length(0));
|
|
|
|
} else if widget.flex_grow {
|
|
|
|
new_new_new_widget_constraints.push(Constraint::Min(0));
|
|
|
|
} else {
|
|
|
|
new_new_new_widget_constraints.push(Constraint::Ratio(
|
|
|
|
widget.width_ratio,
|
|
|
|
col_row.total_widget_ratio,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
new_new_widget_constraints.push(new_new_new_widget_constraints);
|
|
|
|
});
|
|
|
|
new_col_row_constraints.push(new_new_col_row_constraints);
|
|
|
|
new_widget_constraints.push(new_new_widget_constraints);
|
|
|
|
});
|
|
|
|
col_row_constraints.push(new_col_row_constraints);
|
|
|
|
layout_constraints.push(new_widget_constraints);
|
|
|
|
col_constraints.push(new_col_constraints);
|
|
|
|
});
|
|
|
|
|
2020-09-22 22:12:36 +00:00
|
|
|
let mut painter = Painter {
|
2022-10-12 20:25:38 +00:00
|
|
|
colours,
|
2020-04-02 00:31:43 +00:00
|
|
|
height: 0,
|
|
|
|
width: 0,
|
2020-08-12 00:22:39 +00:00
|
|
|
styled_help_text: Vec::default(),
|
2020-12-09 03:34:21 +00:00
|
|
|
is_mac_os: cfg!(target_os = "macos"),
|
2020-04-02 00:31:43 +00:00
|
|
|
row_constraints,
|
|
|
|
col_constraints,
|
|
|
|
col_row_constraints,
|
|
|
|
layout_constraints,
|
|
|
|
widget_layout,
|
2020-08-12 00:22:39 +00:00
|
|
|
derived_widget_draw_locs: Vec::default(),
|
2020-09-22 22:12:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
painter.complete_painter_init();
|
|
|
|
|
|
|
|
Ok(painter)
|
|
|
|
}
|
|
|
|
|
refactor: consolidate time graph components
This consolidates all the time graph drawing to one main location, as well
as some small improvements. This is helpful in that I don't have to
reimplement the same thing across three locations if I have to make one
change that in theory should affect them all. In particular, the CPU
graph, memory graph, and network graph are all now using the same,
generic implementation for drawing, which we call (for now) a component.
Note this only affects drawing - it accepts some parameters affecting style
and labels, as well as data points, and draw similarly to how it used to
before. Widget-specific actions, or things affecting widget state,
should all be handled by the widget-specific code instead. For example,
our current implementation of x-axis autohide is still controlled by the
widget, not the component, even if some of the code is shared. Components
are, again, only responsible for drawing (at least for now). For that
matter, the graph component does not have mutable access to any form of
state outside of tui-rs' `Frame`. Note this *might* change in the
future, where we might give the component state.
Note that while functionally, the graph behaviour for now is basically
the same, a few changes were made internally other than the move to
components. The big change is that rather than using tui-rs' `Chart`
for the underlying drawing, we now use a tweaked custom `TimeChart`
tui-rs widget, which also handles all interpolation steps and some extra
customization. Personally, I don't like having to deviate from the
library's implementation, but this gives us more flexibility and allows
greater control. For example, this allows me to move away from the old
hacks required to do interpolation (where I had to mutate the existing
list to avoid having to reallocate an extra vector just to insert one
extra interpolated point). I can also finally allow customizable
legends (which will be added in the future).
2022-04-27 06:13:48 +00:00
|
|
|
/// Determines the border style.
|
|
|
|
pub fn get_border_style(&self, widget_id: u64, selected_widget_id: u64) -> tui::style::Style {
|
|
|
|
let is_on_widget = widget_id == selected_widget_id;
|
|
|
|
if is_on_widget {
|
|
|
|
self.colours.highlighted_border_style
|
|
|
|
} else {
|
|
|
|
self.colours.border_style
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-29 22:05:01 +00:00
|
|
|
/// Must be run once before drawing, but after setting colours.
|
|
|
|
/// This is to set some remaining styles and text.
|
2020-09-22 22:12:36 +00:00
|
|
|
fn complete_painter_init(&mut self) {
|
2020-08-12 00:22:39 +00:00
|
|
|
let mut styled_help_spans = Vec::new();
|
2020-02-29 22:05:01 +00:00
|
|
|
|
2020-04-24 23:17:58 +00:00
|
|
|
// Init help text:
|
2020-04-25 21:32:31 +00:00
|
|
|
(*HELP_TEXT).iter().enumerate().for_each(|(itx, section)| {
|
|
|
|
if itx == 0 {
|
2020-08-12 00:22:39 +00:00
|
|
|
styled_help_spans.extend(
|
2020-04-25 21:32:31 +00:00
|
|
|
section
|
|
|
|
.iter()
|
2020-09-27 00:04:34 +00:00
|
|
|
.map(|&text| Span::styled(text, self.colours.text_style))
|
2020-04-25 21:32:31 +00:00
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// Not required check but it runs only a few times... so whatever ig, prevents me from
|
|
|
|
// being dumb and leaving a help text section only one line long.
|
|
|
|
if section.len() > 1 {
|
2020-09-27 00:04:34 +00:00
|
|
|
styled_help_spans.push(Span::raw(""));
|
2020-08-12 00:22:39 +00:00
|
|
|
styled_help_spans
|
2020-09-27 00:04:34 +00:00
|
|
|
.push(Span::styled(section[0], self.colours.table_header_style));
|
2020-08-12 00:22:39 +00:00
|
|
|
styled_help_spans.extend(
|
2020-04-25 21:32:31 +00:00
|
|
|
section[1..]
|
|
|
|
.iter()
|
2020-09-27 00:04:34 +00:00
|
|
|
.map(|&text| Span::styled(text, self.colours.text_style))
|
2020-04-25 21:32:31 +00:00
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2020-08-12 00:22:39 +00:00
|
|
|
|
2020-09-27 00:04:34 +00:00
|
|
|
self.styled_help_text = styled_help_spans.into_iter().map(Spans::from).collect();
|
2020-02-29 22:05:01 +00:00
|
|
|
}
|
|
|
|
|
2020-10-18 03:26:03 +00:00
|
|
|
fn draw_frozen_indicator<B: Backend>(&self, f: &mut Frame<'_, B>, draw_loc: Rect) {
|
|
|
|
f.render_widget(
|
|
|
|
Paragraph::new(Span::styled(
|
|
|
|
"Frozen, press 'f' to unfreeze",
|
|
|
|
self.colours.currently_selected_text_style,
|
|
|
|
)),
|
|
|
|
Layout::default()
|
|
|
|
.horizontal_margin(1)
|
|
|
|
.constraints([Constraint::Length(1)])
|
|
|
|
.split(draw_loc)[0],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-02-29 23:47:30 +00:00
|
|
|
pub fn draw_data<B: Backend>(
|
2020-02-29 22:05:01 +00:00
|
|
|
&mut self, terminal: &mut Terminal<B>, app_state: &mut app::App,
|
|
|
|
) -> error::Result<()> {
|
2020-04-02 00:31:43 +00:00
|
|
|
use BottomWidgetType::*;
|
|
|
|
|
2021-12-19 17:07:13 +00:00
|
|
|
terminal.draw(|f| {
|
2022-10-12 20:25:38 +00:00
|
|
|
let (terminal_size, frozen_draw_loc) = if app_state.frozen_state.is_frozen() {
|
2020-10-18 03:26:03 +00:00
|
|
|
let split_loc = Layout::default()
|
|
|
|
.constraints([Constraint::Min(0), Constraint::Length(1)])
|
|
|
|
.split(f.size());
|
|
|
|
(split_loc[0], Some(split_loc[1]))
|
|
|
|
} else {
|
|
|
|
(f.size(), None)
|
|
|
|
};
|
2020-09-18 16:35:32 +00:00
|
|
|
let terminal_height = terminal_size.height;
|
|
|
|
let terminal_width = terminal_size.width;
|
|
|
|
|
|
|
|
if (self.height == 0 && self.width == 0)
|
|
|
|
|| (self.height != terminal_height || self.width != terminal_width)
|
|
|
|
{
|
|
|
|
app_state.is_force_redraw = true;
|
|
|
|
self.height = terminal_height;
|
|
|
|
self.width = terminal_width;
|
2020-08-29 22:54:18 +00:00
|
|
|
}
|
2020-09-11 08:20:14 +00:00
|
|
|
|
2020-09-18 16:35:32 +00:00
|
|
|
if app_state.should_get_widget_bounds() {
|
|
|
|
// If we're force drawing, reset ALL mouse boundaries.
|
|
|
|
for widget in app_state.widget_map.values_mut() {
|
|
|
|
widget.top_left_corner = None;
|
|
|
|
widget.bottom_right_corner = None;
|
|
|
|
}
|
|
|
|
|
2021-02-18 22:10:51 +00:00
|
|
|
// Reset dd_dialog...
|
2020-12-16 02:39:17 +00:00
|
|
|
app_state.delete_dialog_state.button_positions = vec![];
|
2020-09-11 08:20:14 +00:00
|
|
|
|
2021-02-18 22:10:51 +00:00
|
|
|
// Reset battery dialog...
|
2020-09-18 16:35:32 +00:00
|
|
|
for battery_widget in app_state.battery_state.widget_states.values_mut() {
|
|
|
|
battery_widget.tab_click_locs = None;
|
|
|
|
}
|
2020-09-11 08:20:14 +00:00
|
|
|
}
|
2020-08-29 22:54:18 +00:00
|
|
|
|
2020-02-29 22:05:01 +00:00
|
|
|
if app_state.help_dialog_state.is_showing_help {
|
2020-03-06 04:54:39 +00:00
|
|
|
let gen_help_len = GENERAL_HELP_TEXT.len() as u16 + 3;
|
2020-09-18 16:35:32 +00:00
|
|
|
let border_len = terminal_height.saturating_sub(gen_help_len) / 2;
|
2020-02-29 22:05:01 +00:00
|
|
|
let vertical_dialog_chunk = Layout::default()
|
|
|
|
.direction(Direction::Vertical)
|
2020-10-17 22:35:21 +00:00
|
|
|
.constraints([
|
|
|
|
Constraint::Length(border_len),
|
|
|
|
Constraint::Length(gen_help_len),
|
|
|
|
Constraint::Length(border_len),
|
|
|
|
])
|
2020-09-18 16:35:32 +00:00
|
|
|
.split(terminal_size);
|
2020-02-29 22:05:01 +00:00
|
|
|
|
|
|
|
let middle_dialog_chunk = Layout::default()
|
|
|
|
.direction(Direction::Horizontal)
|
2020-10-17 22:35:21 +00:00
|
|
|
.constraints(if terminal_width < 100 {
|
|
|
|
// TODO: [REFACTOR] The point we start changing size at currently hard-coded in.
|
|
|
|
[
|
|
|
|
Constraint::Percentage(0),
|
|
|
|
Constraint::Percentage(100),
|
|
|
|
Constraint::Percentage(0),
|
|
|
|
]
|
|
|
|
} else {
|
|
|
|
[
|
2022-09-03 08:04:03 +00:00
|
|
|
Constraint::Percentage(15),
|
|
|
|
Constraint::Percentage(70),
|
|
|
|
Constraint::Percentage(15),
|
2020-10-17 22:35:21 +00:00
|
|
|
]
|
|
|
|
})
|
2020-02-29 22:05:01 +00:00
|
|
|
.split(vertical_dialog_chunk[1]);
|
|
|
|
|
2021-12-19 17:07:13 +00:00
|
|
|
self.draw_help_dialog(f, app_state, middle_dialog_chunk[1]);
|
2020-02-29 22:05:01 +00:00
|
|
|
} else if app_state.delete_dialog_state.is_showing_dd {
|
2020-08-12 04:27:02 +00:00
|
|
|
// TODO: This needs the paragraph wrap feature from tui-rs to be pushed to complete... but for now it's pretty close!
|
|
|
|
// The main problem right now is that I cannot properly calculate the height offset since
|
|
|
|
// line-wrapping is NOT the same as taking the width of the text and dividing by width.
|
|
|
|
// So, I need the height AFTER wrapping.
|
|
|
|
// See: https://github.com/fdehau/tui-rs/pull/349. Land this after this pushes to release.
|
|
|
|
|
|
|
|
let dd_text = self.get_dd_spans(app_state);
|
|
|
|
|
2020-12-16 02:39:17 +00:00
|
|
|
let text_width = if terminal_width < 100 {
|
|
|
|
terminal_width * 90 / 100
|
|
|
|
} else {
|
|
|
|
terminal_width * 50 / 100
|
|
|
|
};
|
2021-02-16 03:23:22 +00:00
|
|
|
|
|
|
|
let text_height = if cfg!(target_os = "windows")
|
|
|
|
|| !app_state.app_config_fields.is_advanced_kill
|
2020-12-16 02:39:17 +00:00
|
|
|
{
|
2021-02-16 03:23:22 +00:00
|
|
|
7
|
|
|
|
} else {
|
|
|
|
22
|
|
|
|
};
|
|
|
|
|
2020-08-16 08:25:59 +00:00
|
|
|
// let (text_width, text_height) = if let Some(dd_text) = &dd_text {
|
2020-09-18 16:35:32 +00:00
|
|
|
// let width = if current_width < 100 {
|
|
|
|
// current_width * 90 / 100
|
2020-08-16 08:25:59 +00:00
|
|
|
// } else {
|
2020-09-18 16:35:32 +00:00
|
|
|
// let min_possible_width = (current_width * 50 / 100) as usize;
|
2020-08-16 08:25:59 +00:00
|
|
|
// let mut width = dd_text.width();
|
2020-08-12 04:27:02 +00:00
|
|
|
|
2020-08-16 08:25:59 +00:00
|
|
|
// // This should theoretically never allow width to be 0... we can be safe and do an extra check though.
|
2020-09-18 16:35:32 +00:00
|
|
|
// while width > (current_width as usize) && width / 2 > min_possible_width {
|
2020-08-16 08:25:59 +00:00
|
|
|
// width /= 2;
|
|
|
|
// }
|
2020-08-12 04:27:02 +00:00
|
|
|
|
2020-08-16 08:25:59 +00:00
|
|
|
// std::cmp::max(width, min_possible_width) as u16
|
|
|
|
// };
|
2020-08-12 04:27:02 +00:00
|
|
|
|
2020-08-16 08:25:59 +00:00
|
|
|
// (
|
|
|
|
// width,
|
|
|
|
// (dd_text.height() + 2 + (dd_text.width() / width as usize)) as u16,
|
|
|
|
// )
|
|
|
|
// } else {
|
|
|
|
// // AFAIK this shouldn't happen, unless something went wrong...
|
|
|
|
// (
|
2020-09-18 16:35:32 +00:00
|
|
|
// if current_width < 100 {
|
|
|
|
// current_width * 90 / 100
|
2020-08-16 08:25:59 +00:00
|
|
|
// } else {
|
2020-09-18 16:35:32 +00:00
|
|
|
// current_width * 50 / 100
|
2020-08-16 08:25:59 +00:00
|
|
|
// },
|
|
|
|
// 7,
|
|
|
|
// )
|
|
|
|
// };
|
2020-08-12 04:27:02 +00:00
|
|
|
|
2020-09-18 16:35:32 +00:00
|
|
|
let vertical_bordering = terminal_height.saturating_sub(text_height) / 2;
|
2020-02-29 22:05:01 +00:00
|
|
|
let vertical_dialog_chunk = Layout::default()
|
|
|
|
.direction(Direction::Vertical)
|
2020-10-17 22:35:21 +00:00
|
|
|
.constraints([
|
|
|
|
Constraint::Length(vertical_bordering),
|
|
|
|
Constraint::Length(text_height),
|
|
|
|
Constraint::Length(vertical_bordering),
|
|
|
|
])
|
2020-09-18 16:35:32 +00:00
|
|
|
.split(terminal_size);
|
2020-02-29 22:05:01 +00:00
|
|
|
|
2020-09-18 16:35:32 +00:00
|
|
|
let horizontal_bordering = terminal_width.saturating_sub(text_width) / 2;
|
2020-02-29 22:05:01 +00:00
|
|
|
let middle_dialog_chunk = Layout::default()
|
|
|
|
.direction(Direction::Horizontal)
|
2020-10-17 22:35:21 +00:00
|
|
|
.constraints([
|
|
|
|
Constraint::Length(horizontal_bordering),
|
|
|
|
Constraint::Length(text_width),
|
|
|
|
Constraint::Length(horizontal_bordering),
|
|
|
|
])
|
2020-02-29 22:05:01 +00:00
|
|
|
.split(vertical_dialog_chunk[1]);
|
|
|
|
|
2020-08-12 04:27:02 +00:00
|
|
|
// This is a bit nasty, but it works well... I guess.
|
|
|
|
app_state.delete_dialog_state.is_showing_dd =
|
2021-12-19 17:07:13 +00:00
|
|
|
self.draw_dd_dialog(f, dd_text, app_state, middle_dialog_chunk[1]);
|
2020-02-29 22:05:01 +00:00
|
|
|
} else if app_state.is_expanded {
|
2020-10-18 03:26:03 +00:00
|
|
|
if let Some(frozen_draw_loc) = frozen_draw_loc {
|
2021-12-19 17:07:13 +00:00
|
|
|
self.draw_frozen_indicator(f, frozen_draw_loc);
|
2020-10-18 03:26:03 +00:00
|
|
|
}
|
|
|
|
|
2020-02-29 22:05:01 +00:00
|
|
|
let rect = Layout::default()
|
2020-04-02 00:31:43 +00:00
|
|
|
.margin(0)
|
2020-10-17 22:35:21 +00:00
|
|
|
.constraints([Constraint::Percentage(100)])
|
2020-09-18 16:35:32 +00:00
|
|
|
.split(terminal_size);
|
2020-04-02 00:31:43 +00:00
|
|
|
match &app_state.current_widget.widget_type {
|
2021-12-19 17:07:13 +00:00
|
|
|
Cpu => self.draw_cpu(f, app_state, rect[0], app_state.current_widget.widget_id),
|
2020-04-02 00:31:43 +00:00
|
|
|
CpuLegend => self.draw_cpu(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-04-02 00:31:43 +00:00
|
|
|
app_state,
|
|
|
|
rect[0],
|
|
|
|
app_state.current_widget.widget_id - 1,
|
|
|
|
),
|
|
|
|
Mem | BasicMem => self.draw_memory_graph(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-04-02 00:31:43 +00:00
|
|
|
app_state,
|
|
|
|
rect[0],
|
|
|
|
app_state.current_widget.widget_id,
|
|
|
|
),
|
|
|
|
Disk => self.draw_disk_table(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-04-02 00:31:43 +00:00
|
|
|
app_state,
|
|
|
|
rect[0],
|
|
|
|
app_state.current_widget.widget_id,
|
|
|
|
),
|
|
|
|
Temp => self.draw_temp_table(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-04-02 00:31:43 +00:00
|
|
|
app_state,
|
|
|
|
rect[0],
|
|
|
|
app_state.current_widget.widget_id,
|
|
|
|
),
|
|
|
|
Net => self.draw_network_graph(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-04-02 00:31:43 +00:00
|
|
|
app_state,
|
|
|
|
rect[0],
|
|
|
|
app_state.current_widget.widget_id,
|
2020-04-19 01:11:20 +00:00
|
|
|
false,
|
2020-04-02 00:31:43 +00:00
|
|
|
),
|
2020-09-03 02:02:49 +00:00
|
|
|
Proc | ProcSearch | ProcSort => {
|
2020-08-16 00:35:49 +00:00
|
|
|
let widget_id = app_state.current_widget.widget_id
|
2020-09-03 02:02:49 +00:00
|
|
|
- match &app_state.current_widget.widget_type {
|
2020-08-16 00:35:49 +00:00
|
|
|
ProcSearch => 1,
|
|
|
|
ProcSort => 2,
|
|
|
|
_ => 0,
|
|
|
|
};
|
|
|
|
|
2022-05-07 07:38:55 +00:00
|
|
|
self.draw_process_widget(f, app_state, rect[0], true, widget_id);
|
2020-08-16 00:35:49 +00:00
|
|
|
}
|
2020-04-17 00:06:50 +00:00
|
|
|
Battery => self.draw_battery_display(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-04-17 00:06:50 +00:00
|
|
|
app_state,
|
|
|
|
rect[0],
|
2020-04-27 19:52:39 +00:00
|
|
|
true,
|
2020-04-17 00:06:50 +00:00
|
|
|
app_state.current_widget.widget_id,
|
|
|
|
),
|
2020-04-02 00:31:43 +00:00
|
|
|
_ => {}
|
2020-02-29 22:05:01 +00:00
|
|
|
}
|
2020-02-29 23:47:30 +00:00
|
|
|
} else if app_state.app_config_fields.use_basic_mode {
|
|
|
|
// Basic mode. This basically removes all graphs but otherwise
|
|
|
|
// the same info.
|
2020-10-18 03:26:03 +00:00
|
|
|
if let Some(frozen_draw_loc) = frozen_draw_loc {
|
2021-12-19 17:07:13 +00:00
|
|
|
self.draw_frozen_indicator(f, frozen_draw_loc);
|
2020-10-18 03:26:03 +00:00
|
|
|
}
|
2020-02-29 23:47:30 +00:00
|
|
|
|
2022-05-15 08:25:48 +00:00
|
|
|
let actual_cpu_data_len = app_state.converted_data.cpu_data.len().saturating_sub(1);
|
2021-04-24 19:08:19 +00:00
|
|
|
|
|
|
|
// This fixes #397, apparently if the height is 1, it can't render the CPU bars...
|
|
|
|
let cpu_height = {
|
|
|
|
let c = (actual_cpu_data_len / 4) as u16
|
|
|
|
+ (if actual_cpu_data_len % 4 == 0 { 0 } else { 1 });
|
|
|
|
|
|
|
|
if c <= 1 {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
c
|
|
|
|
}
|
|
|
|
};
|
2021-01-25 07:21:33 +00:00
|
|
|
|
2022-10-15 19:08:48 +00:00
|
|
|
let mut mem_rows = 1;
|
|
|
|
|
|
|
|
if app_state.converted_data.swap_labels.is_some() {
|
|
|
|
mem_rows += 1; // add row for swap
|
|
|
|
}
|
2022-08-22 06:47:22 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "zfs")]
|
|
|
|
{
|
2022-10-15 19:08:48 +00:00
|
|
|
if app_state.converted_data.arc_labels.is_some() {
|
|
|
|
mem_rows += 1; // add row for arc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "gpu")]
|
|
|
|
{
|
|
|
|
if let Some(gpu_data) = &app_state.converted_data.gpu_data {
|
|
|
|
mem_rows += gpu_data.len() as u16; // add row(s) for gpu
|
2022-08-22 06:47:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-15 19:08:48 +00:00
|
|
|
if mem_rows == 1 {
|
|
|
|
mem_rows += 1; // need at least 2 rows for RX and TX
|
|
|
|
}
|
2022-08-22 06:47:22 +00:00
|
|
|
|
2020-03-01 22:16:08 +00:00
|
|
|
let vertical_chunks = Layout::default()
|
2020-02-29 23:47:30 +00:00
|
|
|
.direction(Direction::Vertical)
|
2021-01-25 07:21:33 +00:00
|
|
|
.margin(0)
|
2020-10-17 22:35:21 +00:00
|
|
|
.constraints([
|
2021-04-24 19:08:19 +00:00
|
|
|
Constraint::Length(cpu_height),
|
2022-08-22 06:47:22 +00:00
|
|
|
Constraint::Length(mem_rows),
|
2020-10-17 22:35:21 +00:00
|
|
|
Constraint::Length(2),
|
|
|
|
Constraint::Min(5),
|
|
|
|
])
|
2020-09-18 16:35:32 +00:00
|
|
|
.split(terminal_size);
|
2020-02-29 23:47:30 +00:00
|
|
|
|
2020-03-05 07:09:29 +00:00
|
|
|
let middle_chunks = Layout::default()
|
|
|
|
.direction(Direction::Horizontal)
|
2020-10-17 22:35:21 +00:00
|
|
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
2021-04-24 19:08:19 +00:00
|
|
|
.split(vertical_chunks[1]);
|
2022-01-28 00:16:27 +00:00
|
|
|
|
|
|
|
if vertical_chunks[0].width >= 2 {
|
|
|
|
self.draw_basic_cpu(f, app_state, vertical_chunks[0], 1);
|
|
|
|
}
|
|
|
|
if middle_chunks[0].width >= 2 {
|
|
|
|
self.draw_basic_memory(f, app_state, middle_chunks[0], 2);
|
|
|
|
}
|
|
|
|
if middle_chunks[1].width >= 2 {
|
|
|
|
self.draw_basic_network(f, app_state, middle_chunks[1], 3);
|
|
|
|
}
|
2020-08-29 22:54:18 +00:00
|
|
|
|
|
|
|
let mut later_widget_id: Option<u64> = None;
|
2020-04-02 00:31:43 +00:00
|
|
|
if let Some(basic_table_widget_state) = &app_state.basic_table_widget_state {
|
|
|
|
let widget_id = basic_table_widget_state.currently_displayed_widget_id;
|
2020-08-29 22:54:18 +00:00
|
|
|
later_widget_id = Some(widget_id);
|
2022-01-28 00:16:27 +00:00
|
|
|
if vertical_chunks[3].width >= 2 {
|
|
|
|
match basic_table_widget_state.currently_displayed_widget_type {
|
2022-10-12 20:25:38 +00:00
|
|
|
Disk => {
|
|
|
|
self.draw_disk_table(f, app_state, vertical_chunks[3], widget_id)
|
|
|
|
}
|
2022-01-28 00:16:27 +00:00
|
|
|
Proc | ProcSort => {
|
|
|
|
let wid = widget_id
|
|
|
|
- match basic_table_widget_state.currently_displayed_widget_type
|
|
|
|
{
|
|
|
|
ProcSearch => 1,
|
|
|
|
ProcSort => 2,
|
|
|
|
_ => 0,
|
|
|
|
};
|
2022-05-07 07:38:55 +00:00
|
|
|
self.draw_process_widget(
|
2022-01-28 00:16:27 +00:00
|
|
|
f,
|
|
|
|
app_state,
|
|
|
|
vertical_chunks[3],
|
|
|
|
false,
|
|
|
|
wid,
|
|
|
|
);
|
|
|
|
}
|
2022-10-12 20:25:38 +00:00
|
|
|
Temp => {
|
|
|
|
self.draw_temp_table(f, app_state, vertical_chunks[3], widget_id)
|
|
|
|
}
|
2022-01-28 00:16:27 +00:00
|
|
|
Battery => self.draw_battery_display(
|
|
|
|
f,
|
|
|
|
app_state,
|
|
|
|
vertical_chunks[3],
|
|
|
|
false,
|
|
|
|
widget_id,
|
|
|
|
),
|
|
|
|
_ => {}
|
2021-12-19 17:07:13 +00:00
|
|
|
}
|
2020-04-02 00:31:43 +00:00
|
|
|
}
|
2020-03-02 04:53:49 +00:00
|
|
|
}
|
2020-08-29 22:54:18 +00:00
|
|
|
|
|
|
|
if let Some(widget_id) = later_widget_id {
|
2021-12-19 17:07:13 +00:00
|
|
|
self.draw_basic_table_arrows(f, app_state, vertical_chunks[2], widget_id);
|
2020-08-29 22:54:18 +00:00
|
|
|
}
|
2020-02-29 22:05:01 +00:00
|
|
|
} else {
|
2020-09-10 01:51:52 +00:00
|
|
|
// Draws using the passed in (or default) layout.
|
2020-10-18 03:26:03 +00:00
|
|
|
if let Some(frozen_draw_loc) = frozen_draw_loc {
|
2021-12-19 17:07:13 +00:00
|
|
|
self.draw_frozen_indicator(f, frozen_draw_loc);
|
2020-10-18 03:26:03 +00:00
|
|
|
}
|
|
|
|
|
2020-04-24 23:17:58 +00:00
|
|
|
if self.derived_widget_draw_locs.is_empty() || app_state.is_force_redraw {
|
2020-12-09 03:34:21 +00:00
|
|
|
let draw_locs = Layout::default()
|
2020-04-07 03:38:00 +00:00
|
|
|
.margin(0)
|
2022-04-27 22:34:49 +00:00
|
|
|
.constraints(self.row_constraints.as_slice())
|
2020-04-07 03:38:00 +00:00
|
|
|
.direction(Direction::Vertical)
|
2020-09-18 16:35:32 +00:00
|
|
|
.split(terminal_size);
|
2020-04-07 03:38:00 +00:00
|
|
|
|
2020-12-09 03:34:21 +00:00
|
|
|
self.derived_widget_draw_locs = izip!(
|
|
|
|
draw_locs,
|
|
|
|
&self.col_constraints,
|
|
|
|
&self.col_row_constraints,
|
2020-04-07 03:38:00 +00:00
|
|
|
&self.layout_constraints,
|
|
|
|
&self.widget_layout.rows
|
|
|
|
)
|
2020-12-09 03:34:21 +00:00
|
|
|
.map(
|
|
|
|
|(
|
|
|
|
draw_loc,
|
|
|
|
col_constraint,
|
|
|
|
col_row_constraint,
|
|
|
|
row_constraint_vec,
|
|
|
|
cols,
|
|
|
|
)| {
|
|
|
|
izip!(
|
|
|
|
Layout::default()
|
2022-04-27 22:34:49 +00:00
|
|
|
.constraints(col_constraint.as_slice())
|
2020-12-09 03:34:21 +00:00
|
|
|
.direction(Direction::Horizontal)
|
|
|
|
.split(draw_loc)
|
|
|
|
.into_iter(),
|
|
|
|
col_row_constraint,
|
|
|
|
row_constraint_vec,
|
|
|
|
&cols.children
|
|
|
|
)
|
|
|
|
.map(|(split_loc, constraint, col_constraint_vec, col_rows)| {
|
|
|
|
izip!(
|
|
|
|
Layout::default()
|
2022-04-27 22:34:49 +00:00
|
|
|
.constraints(constraint.as_slice())
|
2020-12-09 03:34:21 +00:00
|
|
|
.direction(Direction::Vertical)
|
|
|
|
.split(split_loc)
|
|
|
|
.into_iter(),
|
|
|
|
col_constraint_vec,
|
|
|
|
&col_rows.children
|
|
|
|
)
|
|
|
|
.map(|(draw_loc, col_row_constraint_vec, widgets)| {
|
|
|
|
// Note that col_row_constraint_vec CONTAINS the widget constraints
|
|
|
|
let widget_draw_locs = Layout::default()
|
2022-04-27 22:34:49 +00:00
|
|
|
.constraints(col_row_constraint_vec.as_slice())
|
2020-12-09 03:34:21 +00:00
|
|
|
.direction(Direction::Horizontal)
|
|
|
|
.split(draw_loc);
|
|
|
|
|
|
|
|
// Side effect, draw here.
|
|
|
|
self.draw_widgets_with_constraints(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-12-09 03:34:21 +00:00
|
|
|
app_state,
|
|
|
|
widgets,
|
|
|
|
&widget_draw_locs,
|
2020-04-07 03:38:00 +00:00
|
|
|
);
|
2020-12-09 03:34:21 +00:00
|
|
|
|
|
|
|
widget_draw_locs
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.collect();
|
2020-04-07 03:38:00 +00:00
|
|
|
} else {
|
|
|
|
self.widget_layout
|
|
|
|
.rows
|
|
|
|
.iter()
|
2021-12-28 19:31:42 +00:00
|
|
|
.flat_map(|row| &row.children)
|
|
|
|
.flat_map(|col| &col.children)
|
2020-12-09 03:34:21 +00:00
|
|
|
.zip(self.derived_widget_draw_locs.iter().flatten().flatten())
|
|
|
|
.for_each(|(widgets, widget_draw_locs)| {
|
|
|
|
self.draw_widgets_with_constraints(
|
2021-12-19 17:07:13 +00:00
|
|
|
f,
|
2020-12-09 03:34:21 +00:00
|
|
|
app_state,
|
|
|
|
widgets,
|
2021-07-31 20:24:16 +00:00
|
|
|
widget_draw_locs,
|
2020-04-07 03:04:04 +00:00
|
|
|
);
|
2020-04-07 03:38:00 +00:00
|
|
|
});
|
|
|
|
}
|
2020-02-29 22:05:01 +00:00
|
|
|
}
|
|
|
|
})?;
|
|
|
|
|
2021-12-27 23:23:11 +00:00
|
|
|
if let Some(updated_current_widget) = app_state
|
|
|
|
.widget_map
|
|
|
|
.get(&app_state.current_widget.widget_id)
|
|
|
|
{
|
|
|
|
app_state.current_widget = updated_current_widget.clone();
|
|
|
|
}
|
|
|
|
|
2020-04-24 23:17:58 +00:00
|
|
|
app_state.is_force_redraw = false;
|
2020-08-29 22:54:18 +00:00
|
|
|
app_state.is_determining_widget_boundary = false;
|
2020-02-29 22:05:01 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-07 03:38:00 +00:00
|
|
|
|
|
|
|
fn draw_widgets_with_constraints<B: Backend>(
|
|
|
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, widgets: &BottomColRow,
|
|
|
|
widget_draw_locs: &[Rect],
|
|
|
|
) {
|
|
|
|
use BottomWidgetType::*;
|
|
|
|
for (widget, widget_draw_loc) in widgets.children.iter().zip(widget_draw_locs) {
|
2022-01-28 00:16:27 +00:00
|
|
|
if widget_draw_loc.width >= 2 && widget_draw_loc.height >= 2 {
|
|
|
|
match &widget.widget_type {
|
|
|
|
Empty => {}
|
|
|
|
Cpu => self.draw_cpu(f, app_state, *widget_draw_loc, widget.widget_id),
|
|
|
|
Mem => self.draw_memory_graph(f, app_state, *widget_draw_loc, widget.widget_id),
|
|
|
|
Net => self.draw_network(f, app_state, *widget_draw_loc, widget.widget_id),
|
2022-10-12 20:25:38 +00:00
|
|
|
Temp => self.draw_temp_table(f, app_state, *widget_draw_loc, widget.widget_id),
|
|
|
|
Disk => self.draw_disk_table(f, app_state, *widget_draw_loc, widget.widget_id),
|
2022-05-07 07:38:55 +00:00
|
|
|
Proc => self.draw_process_widget(
|
2022-01-28 00:16:27 +00:00
|
|
|
f,
|
|
|
|
app_state,
|
|
|
|
*widget_draw_loc,
|
|
|
|
true,
|
|
|
|
widget.widget_id,
|
|
|
|
),
|
|
|
|
Battery => self.draw_battery_display(
|
|
|
|
f,
|
|
|
|
app_state,
|
|
|
|
*widget_draw_loc,
|
|
|
|
true,
|
|
|
|
widget.widget_id,
|
|
|
|
),
|
|
|
|
_ => {}
|
2020-04-07 03:38:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-28 03:39:25 +00:00
|
|
|
}
|