mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-08 17:05:59 +02:00
Add modularity to widget placement and inclusion (#95)
This commit is contained in:
parent
c1a19f960f
commit
0b1d84fdf5
@ -9,7 +9,7 @@ os:
|
||||
jobs:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
- env: TARGET=x86_64-pc-windows-gnu # Seems to cause problems. TODO: Add test for it, but keep allow fail.
|
||||
- env: TARGET=x86_64-pc-windows-gnu # Seems to cause problems.
|
||||
fast_finish: true
|
||||
branches:
|
||||
only:
|
||||
|
@ -31,7 +31,7 @@ fern = "0.6.0"
|
||||
futures = "0.3.4"
|
||||
heim = "0.0.10"
|
||||
log = "0.4.8"
|
||||
regex = "1.3.4"
|
||||
regex = "1.3"
|
||||
sysinfo = "0.11"
|
||||
toml = "0.5.6"
|
||||
tui = {version = "0.8", features = ["crossterm"], default-features = false }
|
||||
|
1
clippy.toml
Normal file
1
clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
cognitive-complexity-threshold = 35
|
@ -8,7 +8,6 @@ One use of a config file is to set boot flags to execute without having to state
|
||||
- These options are generally the same as the long names as other flags (ex: `case_sensitive = true`).
|
||||
- Note that if a flag and an option conflict, the flag has higher precedence (ex: if the `-c` and `temperature_type = kelvin` both exist, the Celsius temperature type is ultimately chosen).
|
||||
- For temperature type, use `temperature_type = "kelvin|k|celsius|c|fahrenheit|f"`.
|
||||
- For default widgets, use `default_widget = "cpu_default|memory_default|disk_default|temperature_default|network_default|process_default"`.
|
||||
|
||||
## Colours
|
||||
|
||||
@ -36,6 +35,45 @@ Supported named colours are one of the following: `Reset, Black, Red, Green, Yel
|
||||
|
||||
Note some colours may not be compatible with the terminal you are using. For example, macOS's default Terminal does not play nice with many colours.
|
||||
|
||||
## Layout
|
||||
|
||||
As of 0.3.0, bottom supports custom layouts. Layouts are in the TOML specification, and are arranged by row -> column -> row. For example, the default layout:
|
||||
|
||||
```toml
|
||||
[[row]]
|
||||
ratio=30
|
||||
[[row.child]]
|
||||
type="cpu"
|
||||
[[row]]
|
||||
ratio=40
|
||||
[[row.child]]
|
||||
ratio=4
|
||||
type="mem"
|
||||
[[row.child]]
|
||||
ratio=3
|
||||
[[row.child.child]]
|
||||
type="temp"
|
||||
[[row.child.child]]
|
||||
type="disk"
|
||||
[[row]]
|
||||
ratio=30
|
||||
[[row.child]]
|
||||
type="net"
|
||||
[[row.child]]
|
||||
type="proc"
|
||||
default=true
|
||||
```
|
||||
|
||||
Valid types are:
|
||||
|
||||
- `cpu`
|
||||
- `mem`
|
||||
- `proc`
|
||||
- `net`
|
||||
- `temp`
|
||||
- `disk`
|
||||
- `empty`
|
||||
|
||||
## Default config locations
|
||||
|
||||
bottom will check specific locations by default for a config file. If no file is found, it will be created.
|
||||
|
3077
src/app.rs
3077
src/app.rs
File diff suppressed because it is too large
Load Diff
@ -80,8 +80,6 @@ fn cpu_usage_calculation(
|
||||
let total_delta: f64 = total - prev_total;
|
||||
let idle_delta: f64 = idle - *prev_idle;
|
||||
|
||||
//debug!("Vangelis function: CPU PERCENT: {}", (total_delta - idle_delta) / total_delta * 100_f64);
|
||||
|
||||
*prev_idle = idle;
|
||||
*prev_non_idle = non_idle;
|
||||
|
||||
@ -111,8 +109,6 @@ fn get_process_cpu_stats(pid: u32) -> std::io::Result<f64> {
|
||||
let utime = val[13].parse::<f64>().unwrap_or(0_f64);
|
||||
let stime = val[14].parse::<f64>().unwrap_or(0_f64);
|
||||
|
||||
//debug!("PID: {}, utime: {}, stime: {}", pid, utime, stime);
|
||||
|
||||
Ok(utime + stime) // This seems to match top...
|
||||
}
|
||||
|
||||
@ -134,15 +130,6 @@ fn linux_cpu_usage<S: core::hash::BuildHasher>(
|
||||
};
|
||||
let after_proc_val = get_process_cpu_stats(pid)?;
|
||||
|
||||
/*debug!(
|
||||
"PID - {} - Before: {}, After: {}, CPU: {}, Percentage: {}",
|
||||
pid,
|
||||
before_proc_val,
|
||||
after_proc_val,
|
||||
cpu_usage,
|
||||
(after_proc_val - before_proc_val) / cpu_usage * 100_f64
|
||||
);*/
|
||||
|
||||
new_pid_stats.insert(pid.to_string(), (after_proc_val, curr_time));
|
||||
|
||||
if use_current_cpu_total {
|
||||
|
940
src/app/layout_manager.rs
Normal file
940
src/app/layout_manager.rs
Normal file
@ -0,0 +1,940 @@
|
||||
use crate::error::{BottomError, Result};
|
||||
use std::collections::BTreeMap;
|
||||
use typed_builder::*;
|
||||
|
||||
use crate::constants::DEFAULT_WIDGET_ID;
|
||||
|
||||
/// Represents a more usable representation of the layout, derived from the
|
||||
/// config.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BottomLayout {
|
||||
pub rows: Vec<BottomRow>,
|
||||
pub total_row_height_ratio: u32,
|
||||
}
|
||||
|
||||
type WidgetMappings = (u32, BTreeMap<(u32, u32), u64>);
|
||||
type ColumnRowMappings = (u32, BTreeMap<(u32, u32), WidgetMappings>);
|
||||
type ColumnMappings = (u32, BTreeMap<(u32, u32), ColumnRowMappings>);
|
||||
|
||||
impl BottomLayout {
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn get_movement_mappings(&mut self) {
|
||||
fn is_intersecting(a: (u32, u32), b: (u32, u32)) -> bool {
|
||||
a.0 >= b.0 && a.1 <= b.1
|
||||
|| a.1 >= b.1 && a.0 <= b.0
|
||||
|| a.0 <= b.0 && a.1 >= b.0
|
||||
|| a.0 >= b.0 && a.0 < b.1 && a.1 >= b.1
|
||||
}
|
||||
|
||||
fn get_distance(target: (u32, u32), candidate: (u32, u32)) -> u32 {
|
||||
if candidate.0 < target.0 {
|
||||
candidate.1 - target.0
|
||||
} else if candidate.1 < target.1 {
|
||||
candidate.1 - candidate.0
|
||||
} else {
|
||||
target.1 - candidate.0
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to create the correct mapping for moving from a specific
|
||||
// widget to another
|
||||
|
||||
let mut layout_mapping: BTreeMap<(u32, u32), ColumnMappings> = BTreeMap::new();
|
||||
let mut total_height = 0;
|
||||
for row in &self.rows {
|
||||
let mut row_width = 0;
|
||||
let mut row_mapping: BTreeMap<(u32, u32), ColumnRowMappings> = BTreeMap::new();
|
||||
let mut is_valid_row = false;
|
||||
for col in &row.children {
|
||||
let mut col_row_height = 0;
|
||||
let mut col_mapping: BTreeMap<(u32, u32), WidgetMappings> = BTreeMap::new();
|
||||
let mut is_valid_col = false;
|
||||
|
||||
for col_row in &col.children {
|
||||
let mut widget_width = 0;
|
||||
let mut col_row_mapping: BTreeMap<(u32, u32), u64> = BTreeMap::new();
|
||||
let mut is_valid_col_row = false;
|
||||
for widget in &col_row.children {
|
||||
match widget.widget_type {
|
||||
BottomWidgetType::Empty => {}
|
||||
_ => {
|
||||
is_valid_col_row = true;
|
||||
col_row_mapping.insert(
|
||||
(
|
||||
widget_width * 100 / col_row.total_widget_ratio,
|
||||
(widget_width + widget.width_ratio) * 100
|
||||
/ col_row.total_widget_ratio,
|
||||
),
|
||||
widget.widget_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
widget_width += widget.width_ratio;
|
||||
}
|
||||
if is_valid_col_row {
|
||||
col_mapping.insert(
|
||||
(
|
||||
col_row_height * 100 / col.total_col_row_ratio,
|
||||
(col_row_height + col_row.col_row_height_ratio) * 100
|
||||
/ col.total_col_row_ratio,
|
||||
),
|
||||
(col.total_col_row_ratio, col_row_mapping),
|
||||
);
|
||||
is_valid_col = true;
|
||||
}
|
||||
|
||||
col_row_height += col_row.col_row_height_ratio;
|
||||
}
|
||||
if is_valid_col {
|
||||
row_mapping.insert(
|
||||
(
|
||||
row_width * 100 / row.total_col_ratio,
|
||||
(row_width + col.col_width_ratio) * 100 / row.total_col_ratio,
|
||||
),
|
||||
(row.total_col_ratio, col_mapping),
|
||||
);
|
||||
is_valid_row = true;
|
||||
}
|
||||
|
||||
row_width += col.col_width_ratio;
|
||||
}
|
||||
if is_valid_row {
|
||||
layout_mapping.insert(
|
||||
(
|
||||
total_height * 100 / self.total_row_height_ratio,
|
||||
(total_height + row.row_height_ratio) * 100 / self.total_row_height_ratio,
|
||||
),
|
||||
(self.total_row_height_ratio, row_mapping),
|
||||
);
|
||||
}
|
||||
total_height += row.row_height_ratio;
|
||||
}
|
||||
|
||||
// Now pass through a second time; this time we want to build up
|
||||
// our neighbour profile.
|
||||
let mut height_cursor = 0;
|
||||
for row in &mut self.rows {
|
||||
let mut col_cursor = 0;
|
||||
let row_height_percentage_start = height_cursor * 100 / self.total_row_height_ratio;
|
||||
let row_height_percentage_end =
|
||||
(height_cursor + row.row_height_ratio) * 100 / self.total_row_height_ratio;
|
||||
|
||||
for col in &mut row.children {
|
||||
let mut col_row_cursor = 0;
|
||||
let col_width_percentage_start = col_cursor * 100 / row.total_col_ratio;
|
||||
let col_width_percentage_end =
|
||||
(col_cursor + col.col_width_ratio) * 100 / row.total_col_ratio;
|
||||
|
||||
for col_row in &mut col.children {
|
||||
let mut widget_cursor = 0;
|
||||
let col_row_height_percentage_start =
|
||||
col_row_cursor * 100 / col.total_col_row_ratio;
|
||||
let col_row_height_percentage_end =
|
||||
(col_row_cursor + col_row.col_row_height_ratio) * 100
|
||||
/ col.total_col_row_ratio;
|
||||
let col_row_children_len = col_row.children.len();
|
||||
|
||||
for widget in &mut col_row.children {
|
||||
// Bail if empty.
|
||||
if let BottomWidgetType::Empty = widget.widget_type {
|
||||
continue;
|
||||
}
|
||||
|
||||
let widget_width_percentage_start =
|
||||
widget_cursor * 100 / col_row.total_widget_ratio;
|
||||
let widget_width_percentage_end =
|
||||
(widget_cursor + widget.width_ratio) * 100 / col_row.total_widget_ratio;
|
||||
|
||||
if let Some(current_row) = layout_mapping
|
||||
.get(&(row_height_percentage_start, row_height_percentage_end))
|
||||
{
|
||||
// First check for within the same col_row for left and right
|
||||
if let Some(current_col) = current_row
|
||||
.1
|
||||
.get(&(col_width_percentage_start, col_width_percentage_end))
|
||||
{
|
||||
if let Some(current_col_row) = current_col.1.get(&(
|
||||
col_row_height_percentage_start,
|
||||
col_row_height_percentage_end,
|
||||
)) {
|
||||
if let Some(to_left_widget) = current_col_row
|
||||
.1
|
||||
.range(
|
||||
..(
|
||||
widget_width_percentage_start,
|
||||
widget_width_percentage_start,
|
||||
),
|
||||
)
|
||||
.next_back()
|
||||
{
|
||||
widget.left_neighbour = Some(*to_left_widget.1);
|
||||
}
|
||||
|
||||
// Right
|
||||
if let Some(to_right_neighbour) = current_col_row
|
||||
.1
|
||||
.range(
|
||||
(
|
||||
widget_width_percentage_end,
|
||||
widget_width_percentage_end,
|
||||
)..,
|
||||
)
|
||||
.next()
|
||||
{
|
||||
widget.right_neighbour = Some(*to_right_neighbour.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if widget.left_neighbour.is_none() {
|
||||
if let Some(to_left_col) = current_row
|
||||
.1
|
||||
.range(
|
||||
..(col_width_percentage_start, col_width_percentage_start),
|
||||
)
|
||||
.next_back()
|
||||
{
|
||||
// Check left in same row
|
||||
let mut current_best_distance = 0;
|
||||
let mut current_best_widget_id = widget.widget_id;
|
||||
|
||||
for widget_position in &(to_left_col.1).1 {
|
||||
let candidate_start = (widget_position.0).0;
|
||||
let candidate_end = (widget_position.0).1;
|
||||
|
||||
if is_intersecting(
|
||||
(
|
||||
col_row_height_percentage_start,
|
||||
col_row_height_percentage_end,
|
||||
),
|
||||
(candidate_start, candidate_end),
|
||||
) {
|
||||
let candidate_distance = get_distance(
|
||||
(
|
||||
col_row_height_percentage_start,
|
||||
col_row_height_percentage_end,
|
||||
),
|
||||
(candidate_start, candidate_end),
|
||||
);
|
||||
|
||||
if current_best_distance < candidate_distance {
|
||||
if let Some(new_best_widget) =
|
||||
(widget_position.1).1.iter().next_back()
|
||||
{
|
||||
current_best_distance = candidate_distance + 1;
|
||||
current_best_widget_id = *(new_best_widget.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if current_best_distance > 0 {
|
||||
widget.left_neighbour = Some(current_best_widget_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if widget.right_neighbour.is_none() {
|
||||
if let Some(to_right_col) = current_row
|
||||
.1
|
||||
.range((col_width_percentage_end, col_width_percentage_end)..)
|
||||
.next()
|
||||
{
|
||||
// Check right in same row
|
||||
let mut current_best_distance = 0;
|
||||
let mut current_best_widget_id = widget.widget_id;
|
||||
|
||||
for widget_position in &(to_right_col.1).1 {
|
||||
let candidate_start = (widget_position.0).0;
|
||||
let candidate_end = (widget_position.0).1;
|
||||
|
||||
if is_intersecting(
|
||||
(
|
||||
col_row_height_percentage_start,
|
||||
col_row_height_percentage_end,
|
||||
),
|
||||
(candidate_start, candidate_end),
|
||||
) {
|
||||
let candidate_distance = get_distance(
|
||||
(
|
||||
col_row_height_percentage_start,
|
||||
col_row_height_percentage_end,
|
||||
),
|
||||
(candidate_start, candidate_end),
|
||||
);
|
||||
|
||||
if current_best_distance < candidate_distance {
|
||||
if let Some(new_best_widget) =
|
||||
(widget_position.1).1.iter().next()
|
||||
{
|
||||
current_best_distance = candidate_distance + 1;
|
||||
current_best_widget_id = *(new_best_widget.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if current_best_distance > 0 {
|
||||
widget.right_neighbour = Some(current_best_widget_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check up/down within same row;
|
||||
// else check up/down with other rows
|
||||
if let Some(current_col) = current_row
|
||||
.1
|
||||
.get(&(col_width_percentage_start, col_width_percentage_end))
|
||||
{
|
||||
if let Some(to_up) = current_col
|
||||
.1
|
||||
.range(
|
||||
..(
|
||||
col_row_height_percentage_start,
|
||||
col_row_height_percentage_start,
|
||||
),
|
||||
)
|
||||
.next_back()
|
||||
{
|
||||
// Now check each widget_width and pick the best
|
||||
for candidate_widget in &(to_up.1).1 {
|
||||
let mut current_best_distance = 0;
|
||||
let mut current_best_widget_id = widget.widget_id;
|
||||
if is_intersecting(
|
||||
(
|
||||
widget_width_percentage_start,
|
||||
widget_width_percentage_end,
|
||||
),
|
||||
((candidate_widget.0).0, (candidate_widget.0).1),
|
||||
) {
|
||||
let candidate_best_distance = get_distance(
|
||||
(
|
||||
widget_width_percentage_start,
|
||||
widget_width_percentage_end,
|
||||
),
|
||||
((candidate_widget.0).0, (candidate_widget.0).1),
|
||||
);
|
||||
|
||||
if current_best_distance < candidate_best_distance {
|
||||
current_best_distance = candidate_best_distance + 1;
|
||||
current_best_widget_id = *candidate_widget.1;
|
||||
}
|
||||
}
|
||||
|
||||
if current_best_distance > 0 {
|
||||
widget.up_neighbour = Some(current_best_widget_id);
|
||||
}
|
||||
}
|
||||
} else if let Some(next_row_up) = layout_mapping
|
||||
.range(
|
||||
..(
|
||||
row_height_percentage_start,
|
||||
row_height_percentage_start,
|
||||
),
|
||||
)
|
||||
.next_back()
|
||||
{
|
||||
let mut current_best_distance = 0;
|
||||
let mut current_best_widget_id = widget.widget_id;
|
||||
let (target_start_width, target_end_width) =
|
||||
if col_row_children_len > 1 {
|
||||
(
|
||||
col_width_percentage_start
|
||||
+ widget_width_percentage_start
|
||||
* (col_width_percentage_end
|
||||
- col_width_percentage_start)
|
||||
/ 100,
|
||||
col_width_percentage_start
|
||||
+ widget_width_percentage_end
|
||||
* (col_width_percentage_end
|
||||
- col_width_percentage_start)
|
||||
/ 100,
|
||||
)
|
||||
} else {
|
||||
(col_width_percentage_start, col_width_percentage_end)
|
||||
};
|
||||
|
||||
for col_position in &(next_row_up.1).1 {
|
||||
if let Some(next_col_row) =
|
||||
(col_position.1).1.iter().next_back()
|
||||
{
|
||||
let (candidate_col_start, candidate_col_end) =
|
||||
((col_position.0).0, (col_position.0).1);
|
||||
let candidate_difference =
|
||||
candidate_col_end - candidate_col_start;
|
||||
for candidate_widget in &(next_col_row.1).1 {
|
||||
let candidate_start = candidate_col_start
|
||||
+ (candidate_widget.0).0 * candidate_difference
|
||||
/ 100;
|
||||
let candidate_end = candidate_col_start
|
||||
+ (candidate_widget.0).1 * candidate_difference
|
||||
/ 100;
|
||||
|
||||
if is_intersecting(
|
||||
(target_start_width, target_end_width),
|
||||
(candidate_start, candidate_end),
|
||||
) {
|
||||
let candidate_distance = get_distance(
|
||||
(target_start_width, target_end_width),
|
||||
(candidate_start, candidate_end),
|
||||
);
|
||||
|
||||
if current_best_distance < candidate_distance {
|
||||
current_best_distance =
|
||||
candidate_distance + 1;
|
||||
current_best_widget_id =
|
||||
*(candidate_widget.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if current_best_distance > 0 {
|
||||
widget.up_neighbour = Some(current_best_widget_id);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(to_down) = current_col
|
||||
.1
|
||||
.range(
|
||||
(
|
||||
col_row_height_percentage_start + 1,
|
||||
col_row_height_percentage_start + 1,
|
||||
)..,
|
||||
)
|
||||
.next()
|
||||
{
|
||||
for candidate_widget in &(to_down.1).1 {
|
||||
let mut current_best_distance = 0;
|
||||
let mut current_best_widget_id = widget.widget_id;
|
||||
if is_intersecting(
|
||||
(
|
||||
widget_width_percentage_start,
|
||||
widget_width_percentage_end,
|
||||
),
|
||||
((candidate_widget.0).0, (candidate_widget.0).1),
|
||||
) {
|
||||
let candidate_best_distance = get_distance(
|
||||
(
|
||||
widget_width_percentage_start,
|
||||
widget_width_percentage_end,
|
||||
),
|
||||
((candidate_widget.0).0, (candidate_widget.0).1),
|
||||
);
|
||||
|
||||
if current_best_distance < candidate_best_distance {
|
||||
current_best_distance = candidate_best_distance + 1;
|
||||
current_best_widget_id = *candidate_widget.1;
|
||||
}
|
||||
}
|
||||
|
||||
if current_best_distance > 0 {
|
||||
widget.down_neighbour = Some(current_best_widget_id);
|
||||
}
|
||||
}
|
||||
} else if let Some(next_row_down) = layout_mapping
|
||||
.range(
|
||||
(
|
||||
row_height_percentage_start + 1,
|
||||
row_height_percentage_start + 1,
|
||||
)..,
|
||||
)
|
||||
.next()
|
||||
{
|
||||
let mut current_best_distance = 0;
|
||||
let mut current_best_widget_id = widget.widget_id;
|
||||
let (target_start_width, target_end_width) =
|
||||
if col_row_children_len > 1 {
|
||||
(
|
||||
col_width_percentage_start
|
||||
+ widget_width_percentage_start
|
||||
* (col_width_percentage_end
|
||||
- col_width_percentage_start)
|
||||
/ 100,
|
||||
col_width_percentage_start
|
||||
+ widget_width_percentage_end
|
||||
* (col_width_percentage_end
|
||||
- col_width_percentage_start)
|
||||
/ 100,
|
||||
)
|
||||
} else {
|
||||
(col_width_percentage_start, col_width_percentage_end)
|
||||
};
|
||||
|
||||
for col_position in &(next_row_down.1).1 {
|
||||
if let Some(next_col_row) = (col_position.1).1.iter().next()
|
||||
{
|
||||
let (candidate_col_start, candidate_col_end) =
|
||||
((col_position.0).0, (col_position.0).1);
|
||||
let candidate_difference =
|
||||
candidate_col_end - candidate_col_start;
|
||||
for candidate_widget in &(next_col_row.1).1 {
|
||||
let candidate_start = candidate_col_start
|
||||
+ (candidate_widget.0).0 * candidate_difference
|
||||
/ 100;
|
||||
let candidate_end = candidate_col_start
|
||||
+ (candidate_widget.0).1 * candidate_difference
|
||||
/ 100;
|
||||
|
||||
if is_intersecting(
|
||||
(target_start_width, target_end_width),
|
||||
(candidate_start, candidate_end),
|
||||
) {
|
||||
let candidate_distance = get_distance(
|
||||
(target_start_width, target_end_width),
|
||||
(candidate_start, candidate_end),
|
||||
);
|
||||
|
||||
if current_best_distance < candidate_distance {
|
||||
current_best_distance =
|
||||
candidate_distance + 1;
|
||||
current_best_widget_id =
|
||||
*(candidate_widget.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if current_best_distance > 0 {
|
||||
widget.down_neighbour = Some(current_best_widget_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
widget_cursor += widget.width_ratio;
|
||||
}
|
||||
col_row_cursor += col_row.col_row_height_ratio;
|
||||
}
|
||||
col_cursor += col.col_width_ratio;
|
||||
}
|
||||
height_cursor += row.row_height_ratio;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_basic_default() -> Self {
|
||||
BottomLayout {
|
||||
total_row_height_ratio: 3,
|
||||
rows: vec![
|
||||
BottomRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomCol::builder()
|
||||
.canvas_handle_width(true)
|
||||
.children(vec![BottomColRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::BasicCpu)
|
||||
.widget_id(1)
|
||||
.down_neighbour(Some(2))
|
||||
.build()])
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
BottomRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomCol::builder()
|
||||
.canvas_handle_width(true)
|
||||
.children(vec![BottomColRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![
|
||||
BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::BasicMem)
|
||||
.widget_id(2)
|
||||
.up_neighbour(Some(1))
|
||||
.down_neighbour(Some(100))
|
||||
.right_neighbour(Some(3))
|
||||
.build(),
|
||||
BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::BasicNet)
|
||||
.widget_id(3)
|
||||
.up_neighbour(Some(1))
|
||||
.down_neighbour(Some(100))
|
||||
.left_neighbour(Some(2))
|
||||
.build(),
|
||||
])
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
BottomRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomCol::builder()
|
||||
.canvas_handle_width(true)
|
||||
.children(vec![BottomColRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::BasicTables)
|
||||
.widget_id(100)
|
||||
.up_neighbour(Some(2))
|
||||
.build()])
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
BottomRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![
|
||||
BottomCol::builder()
|
||||
.canvas_handle_width(true)
|
||||
.children(vec![BottomColRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::Disk)
|
||||
.widget_id(4)
|
||||
.up_neighbour(Some(100))
|
||||
.left_neighbour(Some(7))
|
||||
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
BottomCol::builder()
|
||||
.canvas_handle_width(true)
|
||||
.children(vec![
|
||||
BottomColRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::Proc)
|
||||
.widget_id(DEFAULT_WIDGET_ID)
|
||||
.up_neighbour(Some(100))
|
||||
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
||||
.left_neighbour(Some(4))
|
||||
.right_neighbour(Some(7))
|
||||
.build()])
|
||||
.build(),
|
||||
BottomColRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::ProcSearch)
|
||||
.widget_id(DEFAULT_WIDGET_ID + 1)
|
||||
.up_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||
.left_neighbour(Some(4))
|
||||
.right_neighbour(Some(7))
|
||||
.build()])
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
BottomCol::builder()
|
||||
.canvas_handle_width(true)
|
||||
.children(vec![BottomColRow::builder()
|
||||
.canvas_handle_height(true)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.canvas_handle_width(true)
|
||||
.widget_type(BottomWidgetType::Temp)
|
||||
.widget_id(7)
|
||||
.up_neighbour(Some(100))
|
||||
.left_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||
.right_neighbour(Some(4))
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_default(left_legend: bool) -> Self {
|
||||
BottomLayout {
|
||||
total_row_height_ratio: 100,
|
||||
rows: vec![
|
||||
BottomRow::builder()
|
||||
.row_height_ratio(30)
|
||||
.children(vec![BottomCol::builder()
|
||||
.children(vec![BottomColRow::builder()
|
||||
.total_widget_ratio(20)
|
||||
.children(if left_legend {
|
||||
vec![
|
||||
BottomWidget::builder()
|
||||
.width_ratio(3)
|
||||
.widget_type(BottomWidgetType::CpuLegend)
|
||||
.widget_id(2)
|
||||
.down_neighbour(Some(11))
|
||||
.right_neighbour(Some(1))
|
||||
.canvas_handle_width(true)
|
||||
.build(),
|
||||
BottomWidget::builder()
|
||||
.width_ratio(17)
|
||||
.widget_type(BottomWidgetType::Cpu)
|
||||
.widget_id(1)
|
||||
.down_neighbour(Some(12))
|
||||
.left_neighbour(Some(2))
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
BottomWidget::builder()
|
||||
.width_ratio(17)
|
||||
.widget_type(BottomWidgetType::Cpu)
|
||||
.widget_id(1)
|
||||
.down_neighbour(Some(11))
|
||||
.right_neighbour(Some(2))
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
BottomWidget::builder()
|
||||
.width_ratio(3)
|
||||
.widget_type(BottomWidgetType::CpuLegend)
|
||||
.widget_id(2)
|
||||
.down_neighbour(Some(12))
|
||||
.left_neighbour(Some(1))
|
||||
.canvas_handle_width(true)
|
||||
.build(),
|
||||
]
|
||||
})
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
BottomRow::builder()
|
||||
.total_col_ratio(7)
|
||||
.row_height_ratio(40)
|
||||
.children(vec![
|
||||
BottomCol::builder()
|
||||
.col_width_ratio(4)
|
||||
.children(vec![BottomColRow::builder()
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::Mem)
|
||||
.widget_id(11)
|
||||
.right_neighbour(Some(12))
|
||||
.up_neighbour(Some(1))
|
||||
.down_neighbour(Some(21))
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
BottomCol::builder()
|
||||
.total_col_row_ratio(2)
|
||||
.col_width_ratio(3)
|
||||
.children(vec![
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(1)
|
||||
.total_widget_ratio(2)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::Temp)
|
||||
.widget_id(12)
|
||||
.left_neighbour(Some(11))
|
||||
.up_neighbour(Some(1))
|
||||
.down_neighbour(Some(13))
|
||||
.build()])
|
||||
.build(),
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(1)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::Disk)
|
||||
.widget_id(13)
|
||||
.left_neighbour(Some(11))
|
||||
.up_neighbour(Some(12))
|
||||
.down_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||
.build()])
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
BottomRow::builder()
|
||||
.total_col_ratio(2)
|
||||
.row_height_ratio(30)
|
||||
.children(vec![
|
||||
BottomCol::builder()
|
||||
.children(vec![BottomColRow::builder()
|
||||
.col_row_height_ratio(1)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::Net)
|
||||
.widget_id(21)
|
||||
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||
.up_neighbour(Some(11))
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
BottomCol::builder()
|
||||
.total_col_row_ratio(2)
|
||||
.children(vec![
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(1)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::Proc)
|
||||
.widget_id(DEFAULT_WIDGET_ID)
|
||||
.left_neighbour(Some(21))
|
||||
.up_neighbour(Some(13))
|
||||
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
||||
.build()])
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(1)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::ProcSearch)
|
||||
.widget_id(DEFAULT_WIDGET_ID + 1)
|
||||
.up_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||
.left_neighbour(Some(21))
|
||||
.build()])
|
||||
.canvas_handle_height(true)
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a single row in the layout.
|
||||
#[derive(Clone, Debug, TypedBuilder)]
|
||||
pub struct BottomRow {
|
||||
pub children: Vec<BottomCol>,
|
||||
|
||||
#[builder(default = 1)]
|
||||
pub total_col_ratio: u32,
|
||||
|
||||
#[builder(default = 1)]
|
||||
pub row_height_ratio: u32,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub canvas_handle_height: bool,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub flex_grow: bool,
|
||||
}
|
||||
|
||||
/// Represents a single column in the layout. We assume that even if the column
|
||||
/// contains only ONE element, it is still a column (rather than either a col or
|
||||
/// a widget, as per the config, for simplicity's sake).
|
||||
#[derive(Clone, Debug, TypedBuilder)]
|
||||
pub struct BottomCol {
|
||||
pub children: Vec<BottomColRow>,
|
||||
|
||||
#[builder(default = 1)]
|
||||
pub total_col_row_ratio: u32,
|
||||
|
||||
#[builder(default = 1)]
|
||||
pub col_width_ratio: u32,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub canvas_handle_width: bool,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub flex_grow: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, TypedBuilder)]
|
||||
pub struct BottomColRow {
|
||||
pub children: Vec<BottomWidget>,
|
||||
|
||||
#[builder(default = 1)]
|
||||
pub total_widget_ratio: u32,
|
||||
|
||||
#[builder(default = 1)]
|
||||
pub col_row_height_ratio: u32,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub canvas_handle_height: bool,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub flex_grow: bool,
|
||||
}
|
||||
|
||||
/// Represents a single widget.
|
||||
#[derive(Debug, Default, Clone, TypedBuilder)]
|
||||
pub struct BottomWidget {
|
||||
pub widget_type: BottomWidgetType,
|
||||
pub widget_id: u64,
|
||||
|
||||
#[builder(default = 1)]
|
||||
pub width_ratio: u32,
|
||||
|
||||
#[builder(default = None)]
|
||||
pub left_neighbour: Option<u64>,
|
||||
|
||||
#[builder(default = None)]
|
||||
pub right_neighbour: Option<u64>,
|
||||
|
||||
#[builder(default = None)]
|
||||
pub up_neighbour: Option<u64>,
|
||||
|
||||
#[builder(default = None)]
|
||||
pub down_neighbour: Option<u64>,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub canvas_handle_width: bool,
|
||||
|
||||
#[builder(default = false)]
|
||||
pub flex_grow: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BottomWidgetType {
|
||||
Empty,
|
||||
Cpu,
|
||||
CpuLegend,
|
||||
Mem,
|
||||
Net,
|
||||
Proc,
|
||||
ProcSearch,
|
||||
Temp,
|
||||
Disk,
|
||||
BasicCpu,
|
||||
BasicMem,
|
||||
BasicNet,
|
||||
BasicTables,
|
||||
}
|
||||
|
||||
impl BottomWidgetType {
|
||||
pub fn is_widget_table(&self) -> bool {
|
||||
use BottomWidgetType::*;
|
||||
match self {
|
||||
Disk | Proc | Temp | CpuLegend => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_widget_graph(&self) -> bool {
|
||||
use BottomWidgetType::*;
|
||||
match self {
|
||||
Cpu | Net | Mem => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pretty_name(&self) -> &str {
|
||||
use BottomWidgetType::*;
|
||||
match self {
|
||||
Cpu => "CPU",
|
||||
Mem => "Memory",
|
||||
Net => "Network",
|
||||
Proc => "Processes",
|
||||
Temp => "Temperature",
|
||||
Disk => "Disks",
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BottomWidgetType {
|
||||
fn default() -> Self {
|
||||
BottomWidgetType::Empty
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for BottomWidgetType {
|
||||
type Err = BottomError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
let lower_case = s.to_lowercase();
|
||||
match lower_case.as_str() {
|
||||
"cpu" => Ok(BottomWidgetType::Cpu),
|
||||
"mem" => Ok(BottomWidgetType::Mem),
|
||||
"net" => Ok(BottomWidgetType::Net),
|
||||
"proc" => Ok(BottomWidgetType::Proc),
|
||||
"temp" => Ok(BottomWidgetType::Temp),
|
||||
"disk" => Ok(BottomWidgetType::Disk),
|
||||
"empty" => Ok(BottomWidgetType::Empty),
|
||||
_ => Err(BottomError::ConfigError(format!(
|
||||
"Invalid widget type: {}",
|
||||
s
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ impl Process {
|
||||
fn open(pid: DWORD) -> Result<Process, String> {
|
||||
let pc = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, 0, pid) };
|
||||
if pc.is_null() {
|
||||
return Err("!OpenProcess".to_string());
|
||||
return Err("OpenProcess".to_string());
|
||||
}
|
||||
Ok(Process(pc))
|
||||
}
|
||||
|
480
src/canvas.rs
480
src/canvas.rs
@ -3,8 +3,7 @@ use std::collections::HashMap;
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
terminal::Frame,
|
||||
layout::{Constraint, Direction, Layout},
|
||||
widgets::Text,
|
||||
Terminal,
|
||||
};
|
||||
@ -14,7 +13,11 @@ use dialogs::*;
|
||||
use widgets::*;
|
||||
|
||||
use crate::{
|
||||
app::{self, data_harvester::processes::ProcessHarvest, WidgetPosition},
|
||||
app::{
|
||||
self,
|
||||
data_harvester::processes::ProcessHarvest,
|
||||
layout_manager::{BottomLayout, BottomWidgetType},
|
||||
},
|
||||
constants::*,
|
||||
data_conversion::{ConvertedCpuData, ConvertedProcessData},
|
||||
utils::error,
|
||||
@ -40,7 +43,7 @@ pub struct DisplayableData {
|
||||
// Not the final value
|
||||
pub grouped_process_data: Vec<ConvertedProcessData>,
|
||||
// What's actually displayed
|
||||
pub finalized_process_data: Vec<ConvertedProcessData>,
|
||||
pub finalized_process_data_map: HashMap<u64, Vec<ConvertedProcessData>>,
|
||||
pub mem_label: String,
|
||||
pub swap_label: String,
|
||||
pub mem_data: Vec<(f64, f64)>,
|
||||
@ -48,31 +51,110 @@ pub struct DisplayableData {
|
||||
pub cpu_data: Vec<ConvertedCpuData>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
/// Handles the canvas' state. TODO: [OPT] implement this.
|
||||
pub struct Painter {
|
||||
pub colours: CanvasColours,
|
||||
height: u16,
|
||||
width: u16,
|
||||
vertical_dialog_chunk: Vec<Rect>,
|
||||
middle_dialog_chunk: Vec<Rect>,
|
||||
vertical_chunks: Vec<Rect>,
|
||||
middle_chunks: Vec<Rect>,
|
||||
middle_divided_chunk_2: Vec<Rect>,
|
||||
bottom_chunks: Vec<Rect>,
|
||||
cpu_chunk: Vec<Rect>,
|
||||
network_chunk: Vec<Rect>,
|
||||
pub colours: CanvasColours,
|
||||
pub styled_general_help_text: Vec<Text<'static>>,
|
||||
pub styled_process_help_text: Vec<Text<'static>>,
|
||||
pub styled_search_help_text: Vec<Text<'static>>,
|
||||
styled_general_help_text: Vec<Text<'static>>,
|
||||
styled_process_help_text: Vec<Text<'static>>,
|
||||
styled_search_help_text: Vec<Text<'static>>,
|
||||
is_mac_os: bool,
|
||||
row_constraints: Vec<Constraint>,
|
||||
col_constraints: Vec<Vec<Constraint>>,
|
||||
col_row_constraints: Vec<Vec<Vec<Constraint>>>,
|
||||
layout_constraints: Vec<Vec<Vec<Vec<Constraint>>>>,
|
||||
widget_layout: BottomLayout,
|
||||
}
|
||||
|
||||
impl Painter {
|
||||
pub fn init(widget_layout: BottomLayout) -> Self {
|
||||
// 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);
|
||||
});
|
||||
|
||||
Painter {
|
||||
colours: CanvasColours::default(),
|
||||
height: 0,
|
||||
width: 0,
|
||||
styled_general_help_text: Vec::new(),
|
||||
styled_process_help_text: Vec::new(),
|
||||
styled_search_help_text: Vec::new(),
|
||||
is_mac_os: false,
|
||||
row_constraints,
|
||||
col_constraints,
|
||||
col_row_constraints,
|
||||
layout_constraints,
|
||||
widget_layout,
|
||||
}
|
||||
}
|
||||
|
||||
/// Must be run once before drawing, but after setting colours.
|
||||
/// This is to set some remaining styles and text.
|
||||
pub fn initialize(&mut self) {
|
||||
pub fn complete_painter_init(&mut self) {
|
||||
self.is_mac_os = cfg!(target_os = "macos");
|
||||
|
||||
if GENERAL_HELP_TEXT.len() > 1 {
|
||||
@ -115,24 +197,26 @@ impl Painter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_specific_table<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool,
|
||||
widget_selected: WidgetPosition,
|
||||
) {
|
||||
match widget_selected {
|
||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||
self.draw_process_and_search(f, app_state, draw_loc, draw_border)
|
||||
}
|
||||
WidgetPosition::Temp => self.draw_temp_table(f, app_state, draw_loc, draw_border),
|
||||
WidgetPosition::Disk => self.draw_disk_table(f, app_state, draw_loc, draw_border),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// pub fn draw_specific_table<B: Backend>(
|
||||
// &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool,
|
||||
// widget_selected: WidgetPosition,
|
||||
// ) {
|
||||
// match widget_selected {
|
||||
// WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||
// self.draw_process_and_search(f, app_state, draw_loc, draw_border)
|
||||
// }
|
||||
// WidgetPosition::Temp => self.draw_temp_table(f, app_state, draw_loc, draw_border),
|
||||
// WidgetPosition::Disk => self.draw_disk_table(f, app_state, draw_loc, draw_border),
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: [FEATURE] Auto-resizing dialog sizes.
|
||||
pub fn draw_data<B: Backend>(
|
||||
&mut self, terminal: &mut Terminal<B>, app_state: &mut app::App,
|
||||
) -> error::Result<()> {
|
||||
use BottomWidgetType::*;
|
||||
|
||||
let terminal_size = terminal.size()?;
|
||||
let current_height = terminal_size.height;
|
||||
let current_width = terminal_size.width;
|
||||
@ -231,55 +315,63 @@ impl Painter {
|
||||
}
|
||||
} else if app_state.is_expanded {
|
||||
let rect = Layout::default()
|
||||
.margin(1)
|
||||
.margin(0)
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.split(f.size());
|
||||
match &app_state.current_widget_selected {
|
||||
WidgetPosition::Cpu | WidgetPosition::BasicCpu | WidgetPosition::CpuLegend => {
|
||||
let cpu_chunk = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.margin(0)
|
||||
.constraints(
|
||||
if app_state.app_config_fields.left_legend {
|
||||
[Constraint::Percentage(15), Constraint::Percentage(85)]
|
||||
} else {
|
||||
[Constraint::Percentage(85), Constraint::Percentage(15)]
|
||||
}
|
||||
.as_ref(),
|
||||
)
|
||||
.split(rect[0]);
|
||||
|
||||
let legend_index = if app_state.app_config_fields.left_legend {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let graph_index = if app_state.app_config_fields.left_legend {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
self.draw_cpu_graph(&mut f, app_state, cpu_chunk[graph_index]);
|
||||
self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
|
||||
}
|
||||
WidgetPosition::Mem | WidgetPosition::BasicMem => {
|
||||
self.draw_memory_graph(&mut f, app_state, rect[0]);
|
||||
}
|
||||
WidgetPosition::Disk => {
|
||||
self.draw_disk_table(&mut f, app_state, rect[0], true);
|
||||
}
|
||||
WidgetPosition::Temp => {
|
||||
self.draw_temp_table(&mut f, app_state, rect[0], true);
|
||||
}
|
||||
WidgetPosition::Network
|
||||
| WidgetPosition::BasicNet
|
||||
| WidgetPosition::NetworkLegend => {
|
||||
self.draw_network_graph(&mut f, app_state, rect[0]);
|
||||
}
|
||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||
self.draw_process_and_search(&mut f, app_state, rect[0], true);
|
||||
}
|
||||
match &app_state.current_widget.widget_type {
|
||||
Cpu => self.draw_cpu(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
CpuLegend => self.draw_cpu(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
app_state.current_widget.widget_id - 1,
|
||||
),
|
||||
Mem | BasicMem => self.draw_memory_graph(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
Disk => self.draw_disk_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
true,
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
Temp => self.draw_temp_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
true,
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
Net => self.draw_network_graph(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
Proc => self.draw_process_and_search(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
true,
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
ProcSearch => self.draw_process_and_search(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
true,
|
||||
app_state.current_widget.widget_id - 1,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
// Basic mode. This basically removes all graphs but otherwise
|
||||
@ -309,112 +401,144 @@ impl Painter {
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.split(vertical_chunks[2]);
|
||||
self.draw_basic_cpu(&mut f, app_state, vertical_chunks[0]);
|
||||
self.draw_basic_memory(&mut f, app_state, middle_chunks[0]);
|
||||
self.draw_basic_network(&mut f, app_state, middle_chunks[1]);
|
||||
self.draw_basic_cpu(&mut f, app_state, vertical_chunks[0], 1);
|
||||
self.draw_basic_memory(&mut f, app_state, middle_chunks[0], 2);
|
||||
self.draw_basic_network(&mut f, app_state, middle_chunks[1], 3);
|
||||
self.draw_basic_table_arrows(&mut f, app_state, vertical_chunks[3]);
|
||||
if app_state.current_widget_selected.is_widget_table() {
|
||||
self.draw_specific_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
vertical_chunks[4],
|
||||
false,
|
||||
app_state.current_widget_selected,
|
||||
);
|
||||
} else {
|
||||
self.draw_specific_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
vertical_chunks[4],
|
||||
false,
|
||||
app_state.previous_basic_table_selected,
|
||||
);
|
||||
if let Some(basic_table_widget_state) = &app_state.basic_table_widget_state {
|
||||
let widget_id = basic_table_widget_state.currently_displayed_widget_id;
|
||||
match basic_table_widget_state.currently_displayed_widget_type {
|
||||
Disk => self.draw_disk_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
vertical_chunks[4],
|
||||
false,
|
||||
widget_id,
|
||||
),
|
||||
Proc => self.draw_process_and_search(
|
||||
&mut f,
|
||||
app_state,
|
||||
vertical_chunks[4],
|
||||
false,
|
||||
widget_id,
|
||||
),
|
||||
Temp => self.draw_temp_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
vertical_chunks[4],
|
||||
false,
|
||||
widget_id,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let vertical_chunks = Layout::default()
|
||||
// Draws using the passed in (or default) layout. NOT basic so far.
|
||||
let row_draw_locs = Layout::default()
|
||||
.margin(0)
|
||||
.constraints(self.row_constraints.as_ref())
|
||||
.direction(Direction::Vertical)
|
||||
.margin(1)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(37),
|
||||
Constraint::Percentage(33),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(f.size());
|
||||
let col_draw_locs = self
|
||||
.col_constraints
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(itx, col_constraint)| {
|
||||
Layout::default()
|
||||
.constraints(col_constraint.as_ref())
|
||||
.direction(Direction::Horizontal)
|
||||
.split(row_draw_locs[itx])
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let col_row_draw_locs = self
|
||||
.col_row_constraints
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(col_itx, col_row_constraints)| {
|
||||
col_row_constraints
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(itx, col_row_constraint)| {
|
||||
Layout::default()
|
||||
.constraints(col_row_constraint.as_ref())
|
||||
.direction(Direction::Vertical)
|
||||
.split(col_draw_locs[col_itx][itx])
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let middle_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.margin(0)
|
||||
.constraints([Constraint::Percentage(60), Constraint::Percentage(40)].as_ref())
|
||||
.split(vertical_chunks[1]);
|
||||
// Now... draw!
|
||||
self.layout_constraints.iter().enumerate().for_each(
|
||||
|(row_itx, col_constraint_vec)| {
|
||||
col_constraint_vec.iter().enumerate().for_each(
|
||||
|(col_itx, col_row_constraint_vec)| {
|
||||
col_row_constraint_vec.iter().enumerate().for_each(
|
||||
|(col_row_itx, widget_constraints)| {
|
||||
let widget_draw_locs = Layout::default()
|
||||
.constraints(widget_constraints.as_ref())
|
||||
.direction(Direction::Horizontal)
|
||||
.split(
|
||||
col_row_draw_locs[row_itx][col_itx][col_row_itx],
|
||||
);
|
||||
|
||||
let middle_divided_chunk_2 = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.margin(0)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.split(middle_chunks[1]);
|
||||
|
||||
let bottom_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.margin(0)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.split(vertical_chunks[2]);
|
||||
|
||||
// Component specific chunks
|
||||
let cpu_chunk = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.margin(0)
|
||||
.constraints(
|
||||
if app_state.app_config_fields.left_legend {
|
||||
[Constraint::Percentage(15), Constraint::Percentage(85)]
|
||||
} else {
|
||||
[Constraint::Percentage(85), Constraint::Percentage(15)]
|
||||
}
|
||||
.as_ref(),
|
||||
)
|
||||
.split(vertical_chunks[0]);
|
||||
|
||||
let network_chunk = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.margin(0)
|
||||
.constraints(
|
||||
if (bottom_chunks[0].height as f64 * 0.25) as u16 >= 4 {
|
||||
[Constraint::Percentage(75), Constraint::Percentage(25)]
|
||||
} else {
|
||||
let required = if bottom_chunks[0].height < 10 {
|
||||
bottom_chunks[0].height / 2
|
||||
} else {
|
||||
5
|
||||
};
|
||||
let remaining = bottom_chunks[0].height - required;
|
||||
[Constraint::Length(remaining), Constraint::Length(required)]
|
||||
}
|
||||
.as_ref(),
|
||||
)
|
||||
.split(bottom_chunks[0]);
|
||||
|
||||
// Default chunk index based on left or right legend setting
|
||||
let legend_index = if app_state.app_config_fields.left_legend {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let graph_index = if app_state.app_config_fields.left_legend {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
self.draw_cpu_graph(&mut f, app_state, cpu_chunk[graph_index]);
|
||||
self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
|
||||
self.draw_memory_graph(&mut f, app_state, middle_chunks[0]);
|
||||
self.draw_network_graph(&mut f, app_state, network_chunk[0]);
|
||||
self.draw_network_labels(&mut f, app_state, network_chunk[1]);
|
||||
self.draw_temp_table(&mut f, app_state, middle_divided_chunk_2[0], true);
|
||||
self.draw_disk_table(&mut f, app_state, middle_divided_chunk_2[1], true);
|
||||
self.draw_process_and_search(&mut f, app_state, bottom_chunks[1], true);
|
||||
for (widget_itx, widget) in self.widget_layout.rows[row_itx]
|
||||
.children[col_itx]
|
||||
.children[col_row_itx]
|
||||
.children
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
match widget.widget_type {
|
||||
Empty => {}
|
||||
Cpu => self.draw_cpu(
|
||||
&mut f,
|
||||
app_state,
|
||||
widget_draw_locs[widget_itx],
|
||||
widget.widget_id,
|
||||
),
|
||||
Mem => self.draw_memory_graph(
|
||||
&mut f,
|
||||
app_state,
|
||||
widget_draw_locs[widget_itx],
|
||||
widget.widget_id,
|
||||
),
|
||||
Net => self.draw_network(
|
||||
&mut f,
|
||||
app_state,
|
||||
widget_draw_locs[widget_itx],
|
||||
widget.widget_id,
|
||||
),
|
||||
Temp => self.draw_temp_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
widget_draw_locs[widget_itx],
|
||||
true,
|
||||
widget.widget_id,
|
||||
),
|
||||
Disk => self.draw_disk_table(
|
||||
&mut f,
|
||||
app_state,
|
||||
widget_draw_locs[widget_itx],
|
||||
true,
|
||||
widget.widget_id,
|
||||
),
|
||||
Proc => self.draw_process_and_search(
|
||||
&mut f,
|
||||
app_state,
|
||||
widget_draw_locs[widget_itx],
|
||||
true,
|
||||
widget.widget_id,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
})?;
|
||||
|
||||
|
@ -27,7 +27,7 @@ impl KillDialog for Painter {
|
||||
if let Some(to_kill_processes) = app_state.get_to_delete_processes() {
|
||||
if let Some(first_pid) = to_kill_processes.1.first() {
|
||||
let dd_text = vec![
|
||||
if app_state.is_grouped() {
|
||||
if app_state.is_grouped(app_state.current_widget.widget_id) {
|
||||
if to_kill_processes.1.len() != 1 {
|
||||
Text::raw(format!(
|
||||
"\nKill {} processes with the name {}?",
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::cmp::max;
|
||||
|
||||
use crate::{
|
||||
app::{App, WidgetPosition},
|
||||
app::{layout_manager::BottomWidgetType, App},
|
||||
canvas::Painter,
|
||||
};
|
||||
|
||||
@ -23,27 +23,19 @@ impl BasicTableArrows for Painter {
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
) {
|
||||
// Effectively a paragraph with a ton of spacing
|
||||
|
||||
// TODO: [MODULARITY] This is hard coded. Gross.
|
||||
let (left_table, right_table) = if app_state.current_widget_selected.is_widget_table() {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||
(WidgetPosition::Temp, WidgetPosition::Disk)
|
||||
let (left_table, right_table) =
|
||||
if let Some(basic_table_widget_state) = &app_state.basic_table_widget_state {
|
||||
match basic_table_widget_state.currently_displayed_widget_type {
|
||||
BottomWidgetType::Proc | BottomWidgetType::ProcSearch => {
|
||||
(BottomWidgetType::Temp, BottomWidgetType::Disk)
|
||||
}
|
||||
BottomWidgetType::Disk => (BottomWidgetType::Proc, BottomWidgetType::Temp),
|
||||
BottomWidgetType::Temp => (BottomWidgetType::Disk, BottomWidgetType::Proc),
|
||||
_ => (BottomWidgetType::Disk, BottomWidgetType::Temp),
|
||||
}
|
||||
WidgetPosition::Disk => (WidgetPosition::Process, WidgetPosition::Temp),
|
||||
WidgetPosition::Temp => (WidgetPosition::Disk, WidgetPosition::Process),
|
||||
_ => (WidgetPosition::Disk, WidgetPosition::Temp),
|
||||
}
|
||||
} else {
|
||||
match app_state.previous_basic_table_selected {
|
||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||
(WidgetPosition::Temp, WidgetPosition::Disk)
|
||||
}
|
||||
WidgetPosition::Disk => (WidgetPosition::Process, WidgetPosition::Temp),
|
||||
WidgetPosition::Temp => (WidgetPosition::Disk, WidgetPosition::Process),
|
||||
_ => (WidgetPosition::Disk, WidgetPosition::Temp),
|
||||
}
|
||||
};
|
||||
} else {
|
||||
(BottomWidgetType::Disk, BottomWidgetType::Temp)
|
||||
};
|
||||
|
||||
let left_name = left_table.get_pretty_name();
|
||||
let right_name = right_table.get_pretty_name();
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use crate::{
|
||||
app::{App, WidgetPosition},
|
||||
app::App,
|
||||
canvas::{drawing_utils::*, Painter},
|
||||
constants::*,
|
||||
data_conversion::ConvertedCpuData,
|
||||
@ -15,12 +15,14 @@ use tui::{
|
||||
};
|
||||
|
||||
pub trait CpuBasicWidget {
|
||||
fn draw_basic_cpu<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect);
|
||||
fn draw_basic_cpu<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl CpuBasicWidget for Painter {
|
||||
fn draw_basic_cpu<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
|
||||
|
||||
@ -34,7 +36,7 @@ impl CpuBasicWidget for Painter {
|
||||
// Then, from this, split the row space across ALL columns. From there, generate
|
||||
// the desired lengths.
|
||||
|
||||
if let WidgetPosition::BasicCpu = app_state.current_widget_selected {
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||
use std::cmp::max;
|
||||
|
||||
use crate::{
|
||||
app::{App, WidgetPosition},
|
||||
app::App,
|
||||
canvas::{
|
||||
drawing_utils::{get_start_position, get_variable_intrinsic_widths},
|
||||
Painter,
|
||||
@ -14,7 +14,7 @@ use crate::{
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Rect},
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
terminal::Frame,
|
||||
widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget},
|
||||
};
|
||||
@ -33,259 +33,307 @@ lazy_static! {
|
||||
}
|
||||
|
||||
pub trait CpuGraphWidget {
|
||||
fn draw_cpu_graph<B: Backend>(&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect);
|
||||
fn draw_cpu<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
fn draw_cpu_graph<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
fn draw_cpu_legend<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl CpuGraphWidget for Painter {
|
||||
fn draw_cpu_graph<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
fn draw_cpu<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
|
||||
if draw_loc.width as f64 * 0.15 <= 6.0 {
|
||||
// Skip drawing legend
|
||||
if app_state.current_widget.widget_id == (widget_id + 1) {
|
||||
if app_state.app_config_fields.left_legend {
|
||||
app_state.move_widget_selection_right();
|
||||
} else {
|
||||
app_state.move_widget_selection_left();
|
||||
}
|
||||
}
|
||||
self.draw_cpu_graph(f, app_state, draw_loc, widget_id);
|
||||
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
|
||||
cpu_widget_state.is_legend_hidden = true;
|
||||
}
|
||||
} else {
|
||||
let (graph_index, legend_index, constraints) =
|
||||
if app_state.app_config_fields.left_legend {
|
||||
(
|
||||
1,
|
||||
0,
|
||||
[Constraint::Percentage(15), Constraint::Percentage(85)],
|
||||
)
|
||||
} else {
|
||||
(
|
||||
0,
|
||||
1,
|
||||
[Constraint::Percentage(85), Constraint::Percentage(15)],
|
||||
)
|
||||
};
|
||||
|
||||
let display_time_labels = [
|
||||
format!("{}s", app_state.cpu_state.current_display_time / 1000),
|
||||
"0s".to_string(),
|
||||
];
|
||||
let partitioned_draw_loc = Layout::default()
|
||||
.margin(0)
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(constraints.as_ref())
|
||||
.split(draw_loc);
|
||||
|
||||
let x_axis = if app_state.app_config_fields.hide_time
|
||||
|| (app_state.app_config_fields.autohide_time
|
||||
&& app_state.cpu_state.autohide_timer.is_none())
|
||||
{
|
||||
Axis::default().bounds([0.0, app_state.cpu_state.current_display_time as f64])
|
||||
} else if let Some(time) = app_state.cpu_state.autohide_timer {
|
||||
if std::time::Instant::now().duration_since(time).as_millis()
|
||||
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
||||
self.draw_cpu_graph(f, app_state, partitioned_draw_loc[graph_index], widget_id);
|
||||
self.draw_cpu_legend(
|
||||
f,
|
||||
app_state,
|
||||
partitioned_draw_loc[legend_index],
|
||||
widget_id + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_cpu_graph<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
|
||||
let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
|
||||
|
||||
let display_time_labels = [
|
||||
format!("{}s", cpu_widget_state.current_display_time / 1000),
|
||||
"0s".to_string(),
|
||||
];
|
||||
|
||||
let x_axis = if app_state.app_config_fields.hide_time
|
||||
|| (app_state.app_config_fields.autohide_time
|
||||
&& cpu_widget_state.autohide_timer.is_none())
|
||||
{
|
||||
Axis::default().bounds([-(cpu_widget_state.current_display_time as f64), 0.0])
|
||||
} else if let Some(time) = cpu_widget_state.autohide_timer {
|
||||
if std::time::Instant::now().duration_since(time).as_millis()
|
||||
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
||||
{
|
||||
Axis::default()
|
||||
.bounds([-(cpu_widget_state.current_display_time as f64), 0.0])
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
} else {
|
||||
cpu_widget_state.autohide_timer = None;
|
||||
Axis::default().bounds([-(cpu_widget_state.current_display_time as f64), 0.0])
|
||||
}
|
||||
} else {
|
||||
Axis::default()
|
||||
.bounds([0.0, app_state.cpu_state.current_display_time as f64])
|
||||
.bounds([-(cpu_widget_state.current_display_time as f64), 0.0])
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
} else {
|
||||
app_state.cpu_state.autohide_timer = None;
|
||||
Axis::default().bounds([0.0, app_state.cpu_state.current_display_time as f64])
|
||||
}
|
||||
} else {
|
||||
Axis::default()
|
||||
.bounds([0.0, app_state.cpu_state.current_display_time as f64])
|
||||
};
|
||||
|
||||
// Note this is offset as otherwise the 0 value is not drawn!
|
||||
let y_axis = Axis::default()
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
};
|
||||
.bounds([-0.5, 100.5])
|
||||
.labels(&["0%", "100%"]);
|
||||
|
||||
// Note this is offset as otherwise the 0 value is not drawn!
|
||||
let y_axis = Axis::default()
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.bounds([-0.5, 100.5])
|
||||
.labels(&["0%", "100%"]);
|
||||
|
||||
let dataset_vector: Vec<Dataset<'_>> = cpu_data
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.filter_map(|(itx, cpu)| {
|
||||
if app_state.cpu_state.core_show_vec[itx] {
|
||||
Some(
|
||||
Dataset::default()
|
||||
.marker(if app_state.app_config_fields.use_dot {
|
||||
Marker::Dot
|
||||
} else {
|
||||
Marker::Braille
|
||||
})
|
||||
.style(
|
||||
if app_state.app_config_fields.show_average_cpu && itx == 0 {
|
||||
let use_dot = app_state.app_config_fields.use_dot;
|
||||
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
||||
let dataset_vector: Vec<Dataset<'_>> = cpu_data
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.filter_map(|(itx, cpu)| {
|
||||
if cpu_widget_state.core_show_vec[itx] {
|
||||
Some(
|
||||
Dataset::default()
|
||||
.marker(if use_dot {
|
||||
Marker::Dot
|
||||
} else {
|
||||
Marker::Braille
|
||||
})
|
||||
.style(if show_avg_cpu && itx == 0 {
|
||||
self.colours.avg_colour_style
|
||||
} else {
|
||||
self.colours.cpu_colour_styles
|
||||
[itx % self.colours.cpu_colour_styles.len()]
|
||||
},
|
||||
)
|
||||
.data(&cpu.cpu_data[..]),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let title = if app_state.is_expanded && !app_state.cpu_state.is_showing_tray {
|
||||
const TITLE_BASE: &str = " CPU ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title =
|
||||
format!(" CPU ─{}─ Esc to go back ", "─".repeat(repeat_num as usize));
|
||||
|
||||
result_title
|
||||
} else {
|
||||
" CPU ".to_string()
|
||||
};
|
||||
|
||||
let border_style = match app_state.current_widget_selected {
|
||||
WidgetPosition::Cpu => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
};
|
||||
|
||||
Chart::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
border_style
|
||||
})
|
||||
.data(&cpu.cpu_data[..]),
|
||||
)
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_style),
|
||||
)
|
||||
.x_axis(x_axis)
|
||||
.y_axis(y_axis)
|
||||
.datasets(&dataset_vector)
|
||||
.render(f, draw_loc);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let title = if app_state.is_expanded && !cpu_widget_state.is_showing_tray {
|
||||
const TITLE_BASE: &str = " CPU ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title =
|
||||
format!(" CPU ─{}─ Esc to go back ", "─".repeat(repeat_num as usize));
|
||||
|
||||
result_title
|
||||
} else {
|
||||
" CPU ".to_string()
|
||||
};
|
||||
|
||||
let border_style = if app_state.current_widget.widget_id == widget_id {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
};
|
||||
|
||||
Chart::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
border_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_style),
|
||||
)
|
||||
.x_axis(x_axis)
|
||||
.y_axis(y_axis)
|
||||
.datasets(&dataset_vector)
|
||||
.render(f, draw_loc);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_cpu_legend<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
|
||||
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&(widget_id - 1))
|
||||
{
|
||||
cpu_widget_state.is_legend_hidden = false;
|
||||
let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
|
||||
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let start_position = get_start_position(
|
||||
num_rows,
|
||||
&app_state.app_scroll_positions.scroll_direction,
|
||||
&mut app_state
|
||||
.app_scroll_positions
|
||||
.cpu_scroll_state
|
||||
.previous_scroll_position,
|
||||
app_state
|
||||
.app_scroll_positions
|
||||
.cpu_scroll_state
|
||||
.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let start_position = get_start_position(
|
||||
num_rows,
|
||||
&cpu_widget_state.scroll_state.scroll_direction,
|
||||
&mut cpu_widget_state.scroll_state.previous_scroll_position,
|
||||
cpu_widget_state.scroll_state.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
|
||||
let sliced_cpu_data = &cpu_data[start_position as usize..];
|
||||
let sliced_cpu_data = &cpu_data[start_position as usize..];
|
||||
|
||||
let mut offset_scroll_index = (app_state
|
||||
.app_scroll_positions
|
||||
.cpu_scroll_state
|
||||
.current_scroll_position
|
||||
- start_position) as usize;
|
||||
let cpu_rows = sliced_cpu_data.iter().enumerate().filter_map(|(itx, cpu)| {
|
||||
let cpu_string_row: Vec<Cow<'_, str>> = if app_state.cpu_state.is_showing_tray {
|
||||
vec![
|
||||
Cow::Borrowed(&cpu.cpu_name),
|
||||
if app_state.cpu_state.core_show_vec[itx + start_position as usize] {
|
||||
"[*]".into()
|
||||
} else {
|
||||
"[ ]".into()
|
||||
},
|
||||
]
|
||||
} else if app_state.app_config_fields.show_disabled_data
|
||||
|| app_state.cpu_state.core_show_vec[itx]
|
||||
{
|
||||
vec![
|
||||
Cow::Borrowed(&cpu.cpu_name),
|
||||
Cow::Borrowed(&cpu.legend_value),
|
||||
]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let mut offset_scroll_index =
|
||||
(cpu_widget_state.scroll_state.current_scroll_position - start_position) as usize;
|
||||
let show_disabled_data = app_state.app_config_fields.show_disabled_data;
|
||||
let current_widget_id = app_state.current_widget.widget_id;
|
||||
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
||||
|
||||
if cpu_string_row.is_empty() {
|
||||
offset_scroll_index += 1;
|
||||
None
|
||||
} else {
|
||||
Some(Row::StyledData(
|
||||
cpu_string_row.into_iter(),
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::CpuLegend => {
|
||||
let cpu_rows = sliced_cpu_data.iter().enumerate().filter_map(|(itx, cpu)| {
|
||||
let cpu_string_row: Vec<Cow<'_, str>> = if cpu_widget_state.is_showing_tray {
|
||||
vec![
|
||||
Cow::Borrowed(&cpu.cpu_name),
|
||||
if cpu_widget_state.core_show_vec[itx + start_position as usize] {
|
||||
"[*]".into()
|
||||
} else {
|
||||
"[ ]".into()
|
||||
},
|
||||
]
|
||||
} else if show_disabled_data || cpu_widget_state.core_show_vec[itx] {
|
||||
vec![
|
||||
Cow::Borrowed(&cpu.cpu_name),
|
||||
Cow::Borrowed(&cpu.legend_value),
|
||||
]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
if cpu_string_row.is_empty() {
|
||||
offset_scroll_index += 1;
|
||||
None
|
||||
} else {
|
||||
Some(Row::StyledData(
|
||||
cpu_string_row.into_iter(),
|
||||
if current_widget_id == widget_id {
|
||||
if itx == offset_scroll_index {
|
||||
self.colours.currently_selected_text_style
|
||||
} else if app_state.app_config_fields.show_average_cpu && itx == 0 {
|
||||
} else if show_avg_cpu && itx == 0 {
|
||||
self.colours.avg_colour_style
|
||||
} else {
|
||||
self.colours.cpu_colour_styles[itx
|
||||
+ start_position as usize
|
||||
% self.colours.cpu_colour_styles.len()]
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if app_state.app_config_fields.show_average_cpu && itx == 0 {
|
||||
self.colours.avg_colour_style
|
||||
} else {
|
||||
self.colours.cpu_colour_styles[itx
|
||||
+ start_position as usize
|
||||
% self.colours.cpu_colour_styles.len()]
|
||||
}
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
});
|
||||
} else if show_avg_cpu && itx == 0 {
|
||||
self.colours.avg_colour_style
|
||||
} else {
|
||||
self.colours.cpu_colour_styles[itx
|
||||
+ start_position as usize % self.colours.cpu_colour_styles.len()]
|
||||
},
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = vec![0.5, 0.5];
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = vec![0.5, 0.5];
|
||||
|
||||
let variable_intrinsic_results = get_variable_intrinsic_widths(
|
||||
width as u16,
|
||||
&width_ratios,
|
||||
if app_state.cpu_state.is_showing_tray {
|
||||
&CPU_SELECT_LEGEND_HEADER_LENS
|
||||
} else {
|
||||
&CPU_LEGEND_HEADER_LENS
|
||||
},
|
||||
);
|
||||
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
||||
|
||||
let title = if app_state.cpu_state.is_showing_tray {
|
||||
const TITLE_BASE: &str = " Esc to close ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
let variable_intrinsic_results = get_variable_intrinsic_widths(
|
||||
width as u16,
|
||||
&width_ratios,
|
||||
if cpu_widget_state.is_showing_tray {
|
||||
&CPU_SELECT_LEGEND_HEADER_LENS
|
||||
} else {
|
||||
&CPU_LEGEND_HEADER_LENS
|
||||
},
|
||||
);
|
||||
let result_title = format!("{} Esc to close ", "─".repeat(repeat_num as usize));
|
||||
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
||||
|
||||
result_title
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let title = if cpu_widget_state.is_showing_tray {
|
||||
const TITLE_BASE: &str = " Esc to close ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!("{} Esc to close ", "─".repeat(repeat_num as usize));
|
||||
|
||||
let title_and_border_style = match app_state.current_widget_selected {
|
||||
WidgetPosition::CpuLegend => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
};
|
||||
|
||||
// Draw
|
||||
Table::new(
|
||||
if app_state.cpu_state.is_showing_tray {
|
||||
CPU_SELECT_LEGEND_HEADER
|
||||
result_title
|
||||
} else {
|
||||
CPU_LEGEND_HEADER
|
||||
}
|
||||
.iter(),
|
||||
cpu_rows,
|
||||
)
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(title_and_border_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(title_and_border_style),
|
||||
)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, draw_loc);
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let title_and_border_style = if app_state.current_widget.widget_id == widget_id {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
};
|
||||
|
||||
// Draw
|
||||
Table::new(
|
||||
if cpu_widget_state.is_showing_tray {
|
||||
CPU_SELECT_LEGEND_HEADER
|
||||
} else {
|
||||
CPU_LEGEND_HEADER
|
||||
}
|
||||
.iter(),
|
||||
cpu_rows,
|
||||
)
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(title_and_border_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(title_and_border_style),
|
||||
)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, draw_loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use tui::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::{self, WidgetPosition},
|
||||
app::{self},
|
||||
canvas::{
|
||||
drawing_utils::{get_start_position, get_variable_intrinsic_widths},
|
||||
Painter,
|
||||
@ -28,42 +28,38 @@ lazy_static! {
|
||||
pub trait DiskTableWidget {
|
||||
fn draw_disk_table<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl DiskTableWidget for Painter {
|
||||
fn draw_disk_table<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
) {
|
||||
let disk_data: &[Vec<String>] = &app_state.canvas_data.disk_data;
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let start_position = get_start_position(
|
||||
num_rows,
|
||||
&app_state.app_scroll_positions.scroll_direction,
|
||||
&mut app_state
|
||||
.app_scroll_positions
|
||||
.disk_scroll_state
|
||||
.previous_scroll_position,
|
||||
app_state
|
||||
.app_scroll_positions
|
||||
.disk_scroll_state
|
||||
.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
if let Some(disk_widget_state) = app_state.disk_state.widget_states.get_mut(&widget_id) {
|
||||
let disk_data: &mut [Vec<String>] = &mut app_state.canvas_data.disk_data;
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let start_position = get_start_position(
|
||||
num_rows,
|
||||
&disk_widget_state.scroll_state.scroll_direction,
|
||||
&mut disk_widget_state.scroll_state.previous_scroll_position,
|
||||
disk_widget_state.scroll_state.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
|
||||
let sliced_vec = &disk_data[start_position as usize..];
|
||||
let mut disk_counter: i64 = 0;
|
||||
let sliced_vec = &mut disk_data[start_position as usize..];
|
||||
let mut disk_counter: i64 = 0;
|
||||
|
||||
let disk_rows = sliced_vec.iter().map(|disk| {
|
||||
Row::StyledData(
|
||||
disk.iter(),
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Disk => {
|
||||
let current_widget_id = app_state.current_widget.widget_id;
|
||||
let disk_rows = sliced_vec.iter().map(|disk| {
|
||||
Row::StyledData(
|
||||
disk.iter(),
|
||||
if current_widget_id == widget_id
|
||||
&& disk_widget_state.scroll_state.current_scroll_position >= start_position
|
||||
{
|
||||
if disk_counter as u64
|
||||
== app_state
|
||||
.app_scroll_positions
|
||||
.disk_scroll_state
|
||||
.current_scroll_position
|
||||
== disk_widget_state.scroll_state.current_scroll_position
|
||||
- start_position
|
||||
{
|
||||
disk_counter = -1;
|
||||
@ -74,82 +70,84 @@ impl DiskTableWidget for Painter {
|
||||
}
|
||||
self.colours.text_style
|
||||
}
|
||||
}
|
||||
_ => self.colours.text_style,
|
||||
},
|
||||
)
|
||||
});
|
||||
} else {
|
||||
self.colours.text_style
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
// Calculate widths
|
||||
// TODO: [PRETTY] Ellipsis on strings?
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = [0.2, 0.15, 0.13, 0.13, 0.13, 0.13, 0.13];
|
||||
let variable_intrinsic_results =
|
||||
get_variable_intrinsic_widths(width as u16, &width_ratios, &DISK_HEADERS_LENS);
|
||||
let intrinsic_widths = &variable_intrinsic_results.0[0..variable_intrinsic_results.1];
|
||||
// Calculate widths
|
||||
// TODO: [PRETTY] Ellipsis on strings?
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = [0.2, 0.15, 0.13, 0.13, 0.13, 0.13, 0.13];
|
||||
let variable_intrinsic_results =
|
||||
get_variable_intrinsic_widths(width as u16, &width_ratios, &DISK_HEADERS_LENS);
|
||||
let intrinsic_widths = &variable_intrinsic_results.0[0..variable_intrinsic_results.1];
|
||||
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Disk ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Disk ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
result_title
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
String::new()
|
||||
} else {
|
||||
" Disk ".to_string()
|
||||
};
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Disk ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Disk ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
result_title
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
String::new()
|
||||
} else {
|
||||
" Disk ".to_string()
|
||||
};
|
||||
|
||||
let disk_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Disk => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
}
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(match app_state.current_widget_selected {
|
||||
WidgetPosition::Disk => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
})
|
||||
} else {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Disk => Block::default()
|
||||
let border_and_title_style = if app_state.current_widget.widget_id == widget_id {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
};
|
||||
|
||||
let disk_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
border_and_title_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_and_title_style)
|
||||
} else if app_state.current_widget.widget_id == widget_id {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style),
|
||||
_ => Block::default().borders(Borders::NONE),
|
||||
}
|
||||
};
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(match app_state.current_widget_selected {
|
||||
WidgetPosition::Disk => 0,
|
||||
_ if !draw_border => 1,
|
||||
_ => 0,
|
||||
})
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(
|
||||
if app_state.current_widget.widget_id == widget_id || draw_border {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
},
|
||||
)
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
|
||||
// Draw!
|
||||
Table::new(DISK_HEADERS.iter(), disk_rows)
|
||||
.block(disk_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
// Draw!
|
||||
Table::new(DISK_HEADERS.iter(), disk_rows)
|
||||
.block(disk_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::cmp::max;
|
||||
|
||||
use crate::{
|
||||
app::{App, WidgetPosition},
|
||||
app::App,
|
||||
canvas::{drawing_utils::*, Painter},
|
||||
constants::*,
|
||||
};
|
||||
@ -15,13 +15,13 @@ use tui::{
|
||||
|
||||
pub trait MemBasicWidget {
|
||||
fn draw_basic_memory<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl MemBasicWidget for Painter {
|
||||
fn draw_basic_memory<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
||||
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
||||
@ -31,7 +31,7 @@ impl MemBasicWidget for Painter {
|
||||
.horizontal_margin(1)
|
||||
.split(draw_loc);
|
||||
|
||||
if let WidgetPosition::BasicMem = app_state.current_widget_selected {
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
|
@ -1,10 +1,6 @@
|
||||
use std::cmp::max;
|
||||
|
||||
use crate::{
|
||||
app::{App, WidgetPosition},
|
||||
canvas::Painter,
|
||||
constants::*,
|
||||
};
|
||||
use crate::{app::App, canvas::Painter, constants::*};
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
@ -15,109 +11,112 @@ use tui::{
|
||||
|
||||
pub trait MemGraphWidget {
|
||||
fn draw_memory_graph<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl MemGraphWidget for Painter {
|
||||
fn draw_memory_graph<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
||||
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
||||
if let Some(mem_widget_state) = app_state.mem_state.widget_states.get_mut(&widget_id) {
|
||||
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
||||
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
||||
|
||||
let display_time_labels = [
|
||||
format!("{}s", app_state.mem_state.current_display_time / 1000),
|
||||
"0s".to_string(),
|
||||
];
|
||||
let x_axis = if app_state.app_config_fields.hide_time
|
||||
|| (app_state.app_config_fields.autohide_time
|
||||
&& app_state.mem_state.autohide_timer.is_none())
|
||||
{
|
||||
Axis::default().bounds([0.0, app_state.mem_state.current_display_time as f64])
|
||||
} else if let Some(time) = app_state.mem_state.autohide_timer {
|
||||
if std::time::Instant::now().duration_since(time).as_millis()
|
||||
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
||||
let display_time_labels = [
|
||||
format!("{}s", mem_widget_state.current_display_time / 1000),
|
||||
"0s".to_string(),
|
||||
];
|
||||
let x_axis = if app_state.app_config_fields.hide_time
|
||||
|| (app_state.app_config_fields.autohide_time
|
||||
&& mem_widget_state.autohide_timer.is_none())
|
||||
{
|
||||
Axis::default().bounds([-(mem_widget_state.current_display_time as f64), 0.0])
|
||||
} else if let Some(time) = mem_widget_state.autohide_timer {
|
||||
if std::time::Instant::now().duration_since(time).as_millis()
|
||||
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
||||
{
|
||||
Axis::default()
|
||||
.bounds([-(mem_widget_state.current_display_time as f64), 0.0])
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
} else {
|
||||
mem_widget_state.autohide_timer = None;
|
||||
Axis::default().bounds([-(mem_widget_state.current_display_time as f64), 0.0])
|
||||
}
|
||||
} else {
|
||||
Axis::default()
|
||||
.bounds([0.0, app_state.mem_state.current_display_time as f64])
|
||||
.bounds([-(mem_widget_state.current_display_time as f64), 0.0])
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
} else {
|
||||
app_state.mem_state.autohide_timer = None;
|
||||
Axis::default().bounds([0.0, app_state.mem_state.current_display_time as f64])
|
||||
}
|
||||
} else {
|
||||
Axis::default()
|
||||
.bounds([0.0, app_state.mem_state.current_display_time as f64])
|
||||
};
|
||||
|
||||
// Offset as the zero value isn't drawn otherwise...
|
||||
let y_axis: Axis<'_, &str> = Axis::default()
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
};
|
||||
.bounds([-0.5, 100.5])
|
||||
.labels(&["0%", "100%"]);
|
||||
|
||||
// Offset as the zero value isn't drawn otherwise...
|
||||
let y_axis: Axis<'_, &str> = Axis::default()
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.bounds([-0.5, 100.5])
|
||||
.labels(&["0%", "100%"]);
|
||||
|
||||
let mem_canvas_vec: Vec<Dataset<'_>> = vec![
|
||||
Dataset::default()
|
||||
.name(&app_state.canvas_data.mem_label)
|
||||
.marker(if app_state.app_config_fields.use_dot {
|
||||
Marker::Dot
|
||||
} else {
|
||||
Marker::Braille
|
||||
})
|
||||
.style(self.colours.ram_style)
|
||||
.data(&mem_data),
|
||||
Dataset::default()
|
||||
.name(&app_state.canvas_data.swap_label)
|
||||
.marker(if app_state.app_config_fields.use_dot {
|
||||
Marker::Dot
|
||||
} else {
|
||||
Marker::Braille
|
||||
})
|
||||
.style(self.colours.swap_style)
|
||||
.data(&swap_data),
|
||||
];
|
||||
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Memory ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Memory ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
|
||||
result_title
|
||||
} else {
|
||||
" Memory ".to_string()
|
||||
};
|
||||
|
||||
Chart::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
self.colours.highlighted_border_style
|
||||
let mem_canvas_vec: Vec<Dataset<'_>> = vec![
|
||||
Dataset::default()
|
||||
.name(&app_state.canvas_data.mem_label)
|
||||
.marker(if app_state.app_config_fields.use_dot {
|
||||
Marker::Dot
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
Marker::Braille
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(match app_state.current_widget_selected {
|
||||
WidgetPosition::Mem => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
}),
|
||||
)
|
||||
.x_axis(x_axis)
|
||||
.y_axis(y_axis)
|
||||
.datasets(&mem_canvas_vec)
|
||||
.render(f, draw_loc);
|
||||
.style(self.colours.ram_style)
|
||||
.data(&mem_data),
|
||||
Dataset::default()
|
||||
.name(&app_state.canvas_data.swap_label)
|
||||
.marker(if app_state.app_config_fields.use_dot {
|
||||
Marker::Dot
|
||||
} else {
|
||||
Marker::Braille
|
||||
})
|
||||
.style(self.colours.swap_style)
|
||||
.data(&swap_data),
|
||||
];
|
||||
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Memory ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Memory ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
|
||||
result_title
|
||||
} else {
|
||||
" Memory ".to_string()
|
||||
};
|
||||
|
||||
Chart::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.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)
|
||||
.datasets(&mem_canvas_vec)
|
||||
.render(f, draw_loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
use crate::{
|
||||
app::{App, WidgetPosition},
|
||||
canvas::Painter,
|
||||
constants::*,
|
||||
};
|
||||
use crate::{app::App, canvas::Painter, constants::*};
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
@ -13,13 +9,13 @@ use tui::{
|
||||
|
||||
pub trait NetworkBasicWidget {
|
||||
fn draw_basic_network<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl NetworkBasicWidget for Painter {
|
||||
fn draw_basic_network<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let divided_loc = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
@ -38,7 +34,7 @@ impl NetworkBasicWidget for Painter {
|
||||
.horizontal_margin(1)
|
||||
.split(divided_loc[1]);
|
||||
|
||||
if let WidgetPosition::BasicNet = app_state.current_widget_selected {
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
|
@ -2,14 +2,14 @@ use lazy_static::lazy_static;
|
||||
use std::cmp::max;
|
||||
|
||||
use crate::{
|
||||
app::{App, WidgetPosition},
|
||||
app::App,
|
||||
canvas::{drawing_utils::get_variable_intrinsic_widths, Painter},
|
||||
constants::*,
|
||||
};
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Rect},
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
terminal::Frame,
|
||||
widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Row, Table, Widget},
|
||||
};
|
||||
@ -24,129 +24,156 @@ lazy_static! {
|
||||
}
|
||||
|
||||
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,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
|
||||
fn draw_network_labels<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl NetworkGraphWidget for Painter {
|
||||
fn draw_network_graph<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
fn draw_network<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
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 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);
|
||||
|
||||
let display_time_labels = [
|
||||
format!("{}s", app_state.net_state.current_display_time / 1000),
|
||||
"0s".to_string(),
|
||||
];
|
||||
let x_axis = if app_state.app_config_fields.hide_time
|
||||
|| (app_state.app_config_fields.autohide_time
|
||||
&& app_state.net_state.autohide_timer.is_none())
|
||||
{
|
||||
Axis::default().bounds([0.0, app_state.net_state.current_display_time as f64])
|
||||
} else if let Some(time) = app_state.net_state.autohide_timer {
|
||||
if std::time::Instant::now().duration_since(time).as_millis()
|
||||
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
||||
self.draw_network_graph(f, app_state, network_chunk[0], widget_id);
|
||||
self.draw_network_labels(f, app_state, network_chunk[1], widget_id);
|
||||
}
|
||||
|
||||
fn draw_network_graph<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
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 = [
|
||||
format!("{}s", network_widget_state.current_display_time / 1000),
|
||||
"0s".to_string(),
|
||||
];
|
||||
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_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 {
|
||||
Axis::default()
|
||||
.bounds([0.0, app_state.net_state.current_display_time as f64])
|
||||
.bounds([-(network_widget_state.current_display_time as f64), 0.0])
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
} else {
|
||||
app_state.net_state.autohide_timer = None;
|
||||
Axis::default().bounds([0.0, app_state.net_state.current_display_time as f64])
|
||||
}
|
||||
} else {
|
||||
Axis::default()
|
||||
.bounds([0.0, app_state.net_state.current_display_time as f64])
|
||||
};
|
||||
|
||||
// 0 is offset.
|
||||
let y_axis: Axis<'_, &str> = Axis::default()
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.labels(&display_time_labels)
|
||||
};
|
||||
.bounds([-0.5, 30_f64])
|
||||
.labels(&["0B", "1KiB", "1MiB", "1GiB"]);
|
||||
|
||||
// 0 is offset.
|
||||
let y_axis: Axis<'_, &str> = Axis::default()
|
||||
.style(self.colours.graph_style)
|
||||
.labels_style(self.colours.graph_style)
|
||||
.bounds([-0.5, 30_f64])
|
||||
.labels(&["0B", "1KiB", "1MiB", "1GiB"]);
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Network ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Network ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Network ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Network ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
result_title
|
||||
} else {
|
||||
" Network ".to_string()
|
||||
};
|
||||
|
||||
result_title
|
||||
} else {
|
||||
" Network ".to_string()
|
||||
};
|
||||
|
||||
Chart::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(match app_state.current_widget_selected {
|
||||
WidgetPosition::Network => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
}),
|
||||
)
|
||||
.x_axis(x_axis)
|
||||
.y_axis(y_axis)
|
||||
.datasets(&[
|
||||
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),
|
||||
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),
|
||||
Dataset::default()
|
||||
.name(&format!(
|
||||
"Total RX: {:7}",
|
||||
app_state.canvas_data.total_rx_display
|
||||
))
|
||||
.style(self.colours.total_rx_style),
|
||||
Dataset::default()
|
||||
.name(&format!(
|
||||
"Total TX: {:7}",
|
||||
app_state.canvas_data.total_tx_display
|
||||
))
|
||||
.style(self.colours.total_tx_style),
|
||||
])
|
||||
.render(f, draw_loc);
|
||||
Chart::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.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)
|
||||
.datasets(&[
|
||||
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),
|
||||
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),
|
||||
Dataset::default()
|
||||
.name(&format!(
|
||||
"Total RX: {:7}",
|
||||
app_state.canvas_data.total_rx_display
|
||||
))
|
||||
.style(self.colours.total_rx_style),
|
||||
Dataset::default()
|
||||
.name(&format!(
|
||||
"Total TX: {:7}",
|
||||
app_state.canvas_data.total_tx_display
|
||||
))
|
||||
.style(self.colours.total_tx_style),
|
||||
])
|
||||
.render(f, draw_loc);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_network_labels<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
&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;
|
||||
@ -176,9 +203,10 @@ impl NetworkGraphWidget for Painter {
|
||||
// Draw
|
||||
Table::new(NETWORK_HEADERS.iter(), mapped_network)
|
||||
.block(Block::default().borders(Borders::ALL).border_style(
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Network => self.colours.highlighted_border_style,
|
||||
_ => self.colours.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)
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use crate::{
|
||||
app::{self, App, WidgetPosition},
|
||||
app::{self, App},
|
||||
canvas::{
|
||||
drawing_utils::{
|
||||
get_search_start_position, get_start_position, get_variable_intrinsic_widths,
|
||||
@ -9,7 +9,6 @@ use crate::{
|
||||
Painter,
|
||||
},
|
||||
constants::*,
|
||||
data_conversion::ConvertedProcessData,
|
||||
};
|
||||
|
||||
use tui::{
|
||||
@ -25,260 +24,287 @@ use unicode_width::UnicodeWidthStr;
|
||||
pub trait ProcessTableWidget {
|
||||
fn draw_process_and_search<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
);
|
||||
|
||||
fn draw_processes_table<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
);
|
||||
|
||||
fn draw_search_field<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl ProcessTableWidget for Painter {
|
||||
fn draw_process_and_search<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
) {
|
||||
let search_width = if draw_border { 5 } else { 3 };
|
||||
if let Some(process_widget_state) = app_state.proc_state.widget_states.get(&widget_id) {
|
||||
let search_width = if draw_border { 5 } else { 3 };
|
||||
if process_widget_state.is_search_enabled() {
|
||||
let processes_chunk = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(0), Constraint::Length(search_width)].as_ref())
|
||||
.split(draw_loc);
|
||||
|
||||
if app_state.is_searching() {
|
||||
let processes_chunk = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(0), Constraint::Length(search_width)].as_ref())
|
||||
.split(draw_loc);
|
||||
|
||||
self.draw_processes_table(f, app_state, processes_chunk[0], draw_border);
|
||||
self.draw_search_field(f, app_state, processes_chunk[1], draw_border);
|
||||
} else {
|
||||
self.draw_processes_table(f, app_state, draw_loc, draw_border);
|
||||
self.draw_processes_table(f, app_state, processes_chunk[0], draw_border, widget_id);
|
||||
self.draw_search_field(
|
||||
f,
|
||||
app_state,
|
||||
processes_chunk[1],
|
||||
draw_border,
|
||||
widget_id + 1,
|
||||
);
|
||||
} else {
|
||||
self.draw_processes_table(f, app_state, draw_loc, draw_border, widget_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_processes_table<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
) {
|
||||
let process_data: &[ConvertedProcessData] = &app_state.canvas_data.finalized_process_data;
|
||||
if let Some(proc_widget_state) = app_state.proc_state.widget_states.get_mut(&widget_id) {
|
||||
if let Some(process_data) = &app_state
|
||||
.canvas_data
|
||||
.finalized_process_data_map
|
||||
.get(&widget_id)
|
||||
{
|
||||
// Admittedly this is kinda a hack... but we need to:
|
||||
// * Scroll
|
||||
// * Show/hide elements based on scroll position
|
||||
//
|
||||
// As such, we use a process_counter to know when we've
|
||||
// hit the process we've currently scrolled to.
|
||||
// We also need to move the list - we can
|
||||
// do so by hiding some elements!
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
|
||||
// Admittedly this is kinda a hack... but we need to:
|
||||
// * Scroll
|
||||
// * Show/hide elements based on scroll position
|
||||
//
|
||||
// As such, we use a process_counter to know when we've
|
||||
// hit the process we've currently scrolled to.
|
||||
// We also need to move the list - we can
|
||||
// do so by hiding some elements!
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let position = get_start_position(
|
||||
num_rows,
|
||||
&proc_widget_state.scroll_state.scroll_direction,
|
||||
&mut proc_widget_state.scroll_state.previous_scroll_position,
|
||||
proc_widget_state.scroll_state.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
|
||||
let position = get_start_position(
|
||||
num_rows,
|
||||
&app_state.app_scroll_positions.scroll_direction,
|
||||
&mut app_state
|
||||
.app_scroll_positions
|
||||
.process_scroll_state
|
||||
.previous_scroll_position,
|
||||
app_state
|
||||
.app_scroll_positions
|
||||
.process_scroll_state
|
||||
.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
|
||||
// Sanity check
|
||||
let start_position = if position >= process_data.len() as u64 {
|
||||
std::cmp::max(0, process_data.len() as i64 - 1) as u64
|
||||
} else {
|
||||
position
|
||||
};
|
||||
|
||||
let sliced_vec = &process_data[start_position as usize..];
|
||||
let mut process_counter: i64 = 0;
|
||||
|
||||
// Draw!
|
||||
let process_rows = sliced_vec.iter().map(|process| {
|
||||
let stringified_process_vec: Vec<String> = vec![
|
||||
if app_state.is_grouped() {
|
||||
process.group_pids.len().to_string()
|
||||
// Sanity check
|
||||
let start_position = if position >= process_data.len() as u64 {
|
||||
std::cmp::max(0, process_data.len() as i64 - 1) as u64
|
||||
} else {
|
||||
process.pid.to_string()
|
||||
},
|
||||
process.name.clone(),
|
||||
format!("{:.1}%", process.cpu_usage),
|
||||
format!("{:.1}%", process.mem_usage),
|
||||
];
|
||||
Row::StyledData(
|
||||
stringified_process_vec.into_iter(),
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Process => {
|
||||
if process_counter as u64
|
||||
== app_state
|
||||
.app_scroll_positions
|
||||
.process_scroll_state
|
||||
.current_scroll_position
|
||||
- start_position
|
||||
{
|
||||
process_counter = -1;
|
||||
self.colours.currently_selected_text_style
|
||||
position
|
||||
};
|
||||
|
||||
let sliced_vec = &process_data[start_position as usize..];
|
||||
let mut process_counter: i64 = 0;
|
||||
|
||||
// Draw!
|
||||
let process_rows = sliced_vec.iter().map(|process| {
|
||||
let stringified_process_vec: Vec<String> = vec![
|
||||
if proc_widget_state.is_grouped {
|
||||
process.group_pids.len().to_string()
|
||||
} else {
|
||||
if process_counter >= 0 {
|
||||
process_counter += 1;
|
||||
process.pid.to_string()
|
||||
},
|
||||
process.name.clone(),
|
||||
format!("{:.1}%", process.cpu_usage),
|
||||
format!("{:.1}%", process.mem_usage),
|
||||
];
|
||||
Row::StyledData(
|
||||
stringified_process_vec.into_iter(),
|
||||
if is_on_widget {
|
||||
if process_counter as u64
|
||||
== proc_widget_state.scroll_state.current_scroll_position
|
||||
- start_position
|
||||
{
|
||||
process_counter = -1;
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
if process_counter >= 0 {
|
||||
process_counter += 1;
|
||||
}
|
||||
self.colours.text_style
|
||||
}
|
||||
} else {
|
||||
self.colours.text_style
|
||||
}
|
||||
}
|
||||
_ => self.colours.text_style,
|
||||
},
|
||||
)
|
||||
});
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
use app::data_harvester::processes::ProcessSorting;
|
||||
let mut pid_or_name = if app_state.is_grouped() {
|
||||
"Count"
|
||||
} else {
|
||||
"PID(p)"
|
||||
}
|
||||
.to_string();
|
||||
let mut name = "Name(n)".to_string();
|
||||
let mut cpu = "CPU%(c)".to_string();
|
||||
let mut mem = "Mem%(m)".to_string();
|
||||
use app::data_harvester::processes::ProcessSorting;
|
||||
let mut pid_or_name = if proc_widget_state.is_grouped {
|
||||
"Count"
|
||||
} else {
|
||||
"PID(p)"
|
||||
}
|
||||
.to_string();
|
||||
let mut name = "Name(n)".to_string();
|
||||
let mut cpu = "CPU%(c)".to_string();
|
||||
let mut mem = "Mem%(m)".to_string();
|
||||
|
||||
let direction_val = if app_state.process_sorting_reverse {
|
||||
"▼".to_string()
|
||||
} else {
|
||||
"▲".to_string()
|
||||
};
|
||||
let direction_val = if proc_widget_state.process_sorting_reverse {
|
||||
"▼".to_string()
|
||||
} else {
|
||||
"▲".to_string()
|
||||
};
|
||||
|
||||
match app_state.process_sorting_type {
|
||||
ProcessSorting::CPU => cpu += &direction_val,
|
||||
ProcessSorting::MEM => mem += &direction_val,
|
||||
ProcessSorting::PID => pid_or_name += &direction_val,
|
||||
ProcessSorting::NAME => name += &direction_val,
|
||||
};
|
||||
match proc_widget_state.process_sorting_type {
|
||||
ProcessSorting::CPU => cpu += &direction_val,
|
||||
ProcessSorting::MEM => mem += &direction_val,
|
||||
ProcessSorting::PID => pid_or_name += &direction_val,
|
||||
ProcessSorting::NAME => name += &direction_val,
|
||||
};
|
||||
|
||||
let process_headers = [pid_or_name, name, cpu, mem];
|
||||
let process_headers_lens: Vec<usize> = process_headers
|
||||
.iter()
|
||||
.map(|entry| entry.len())
|
||||
.collect::<Vec<_>>();
|
||||
let process_headers = [pid_or_name, name, cpu, mem];
|
||||
let process_headers_lens: Vec<usize> = process_headers
|
||||
.iter()
|
||||
.map(|entry| entry.len())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = [0.2, 0.4, 0.2, 0.2];
|
||||
let variable_intrinsic_results =
|
||||
get_variable_intrinsic_widths(width as u16, &width_ratios, &process_headers_lens);
|
||||
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
||||
|
||||
let title = if draw_border {
|
||||
if app_state.is_expanded && !app_state.process_search_state.search_state.is_enabled {
|
||||
const TITLE_BASE: &str = " Processes ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Processes ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = [0.2, 0.4, 0.2, 0.2];
|
||||
let variable_intrinsic_results = get_variable_intrinsic_widths(
|
||||
width as u16,
|
||||
&width_ratios,
|
||||
&process_headers_lens,
|
||||
);
|
||||
let intrinsic_widths =
|
||||
&(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
||||
|
||||
result_title
|
||||
} else {
|
||||
" Processes ".to_string()
|
||||
}
|
||||
} else {
|
||||
String::default()
|
||||
};
|
||||
let title = if draw_border {
|
||||
if app_state.is_expanded
|
||||
&& !proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_enabled
|
||||
{
|
||||
const TITLE_BASE: &str = " Processes ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Processes ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
|
||||
let process_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Process => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
result_title
|
||||
} else {
|
||||
" Processes ".to_string()
|
||||
}
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(match app_state.current_widget_selected {
|
||||
WidgetPosition::Process => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
})
|
||||
} else {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Process => Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style),
|
||||
_ => Block::default().borders(Borders::NONE),
|
||||
String::default()
|
||||
};
|
||||
|
||||
let border_and_title_style = if is_on_widget {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
};
|
||||
|
||||
let process_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
border_and_title_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_and_title_style)
|
||||
} else if is_on_widget {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
|
||||
Table::new(process_headers.iter(), process_rows)
|
||||
.block(process_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
}
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(match app_state.current_widget_selected {
|
||||
WidgetPosition::Process => 0,
|
||||
_ if !draw_border => 1,
|
||||
_ => 0,
|
||||
})
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
|
||||
Table::new(process_headers.iter(), process_rows)
|
||||
.block(process_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_search_field<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
) {
|
||||
let pid_search_text = "Search by PID (Tab for Name): ";
|
||||
let name_search_text = "Search by Name (Tab for PID): ";
|
||||
let grouped_search_text = "Search by Name: ";
|
||||
let num_columns = draw_loc.width as usize;
|
||||
if let Some(proc_widget_state) =
|
||||
app_state.proc_state.widget_states.get_mut(&(widget_id - 1))
|
||||
{
|
||||
let pid_search_text = "Search by PID (Tab for Name): ";
|
||||
let name_search_text = "Search by Name (Tab for PID): ";
|
||||
let grouped_search_text = "Search by Name: ";
|
||||
let num_columns = draw_loc.width as usize;
|
||||
|
||||
let chosen_text = if app_state.is_grouped() {
|
||||
grouped_search_text
|
||||
} else if app_state.process_search_state.is_searching_with_pid {
|
||||
pid_search_text
|
||||
} else {
|
||||
name_search_text
|
||||
};
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
|
||||
let search_title: &str = if chosen_text.len() == min(num_columns / 2, chosen_text.len()) {
|
||||
chosen_text
|
||||
} else if chosen_text.is_empty() {
|
||||
""
|
||||
} else {
|
||||
"> "
|
||||
};
|
||||
let chosen_text = if proc_widget_state.is_grouped {
|
||||
grouped_search_text
|
||||
} else if proc_widget_state.process_search_state.is_searching_with_pid {
|
||||
pid_search_text
|
||||
} else {
|
||||
name_search_text
|
||||
};
|
||||
|
||||
let num_chars_for_text = search_title.len();
|
||||
let small_mode = chosen_text.len() != min(num_columns / 2, chosen_text.len());
|
||||
let search_title: &str = if !small_mode {
|
||||
chosen_text
|
||||
} else if chosen_text.is_empty() {
|
||||
""
|
||||
} else if proc_widget_state.process_search_state.is_searching_with_pid {
|
||||
"p> "
|
||||
} else {
|
||||
"n> "
|
||||
};
|
||||
|
||||
let mut search_text = vec![Text::styled(search_title, self.colours.table_header_style)];
|
||||
let num_chars_for_text = search_title.len();
|
||||
|
||||
let cursor_position = app_state.get_cursor_position();
|
||||
let current_cursor_position = app_state.get_char_cursor_position();
|
||||
let mut search_text = vec![Text::styled(search_title, self.colours.table_header_style)];
|
||||
|
||||
let start_position: usize = get_search_start_position(
|
||||
num_columns - num_chars_for_text - 5,
|
||||
&app_state.process_search_state.search_state.cursor_direction,
|
||||
&mut app_state.process_search_state.search_state.cursor_bar,
|
||||
current_cursor_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
let cursor_position = proc_widget_state.get_cursor_position();
|
||||
let current_cursor_position = proc_widget_state.get_char_cursor_position();
|
||||
|
||||
let query = app_state.get_current_search_query().as_str();
|
||||
let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true);
|
||||
let mut current_grapheme_posn = 0;
|
||||
let query_with_cursor: Vec<Text<'_>> =
|
||||
if let WidgetPosition::ProcessSearch = app_state.current_widget_selected {
|
||||
let start_position: usize = get_search_start_position(
|
||||
num_columns - num_chars_for_text - 5,
|
||||
&proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.cursor_direction,
|
||||
&mut proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.cursor_bar,
|
||||
current_cursor_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
|
||||
let query = proc_widget_state.get_current_search_query().as_str();
|
||||
let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true);
|
||||
let mut current_grapheme_posn = 0;
|
||||
let query_with_cursor: Vec<Text<'_>> = if is_on_widget {
|
||||
let mut res = grapheme_indices
|
||||
.filter_map(|grapheme| {
|
||||
current_grapheme_posn += UnicodeWidthStr::width(grapheme.1);
|
||||
@ -320,124 +346,117 @@ impl ProcessTableWidget for Painter {
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
// Text options shamelessly stolen from VS Code.
|
||||
let mut option_text = vec![];
|
||||
let case_style = if !app_state.process_search_state.is_ignoring_case {
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let whole_word_style = if app_state.process_search_state.is_searching_whole_word {
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let regex_style = if app_state.process_search_state.is_searching_with_regex {
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let case_text = format!(
|
||||
"Match Case ({})[{}]",
|
||||
if self.is_mac_os { "F1" } else { "Alt+C" },
|
||||
if !app_state.process_search_state.is_ignoring_case {
|
||||
"*"
|
||||
// Text options shamelessly stolen from VS Code.
|
||||
let case_style = if !proc_widget_state.process_search_state.is_ignoring_case {
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
" "
|
||||
}
|
||||
);
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let whole_text = format!(
|
||||
"Match Whole Word ({})[{}]",
|
||||
if self.is_mac_os { "F2" } else { "Alt+W" },
|
||||
if app_state.process_search_state.is_searching_whole_word {
|
||||
"*"
|
||||
let whole_word_style = if proc_widget_state
|
||||
.process_search_state
|
||||
.is_searching_whole_word
|
||||
{
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
" "
|
||||
}
|
||||
);
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let regex_text = format!(
|
||||
"Use Regex ({})[{}]",
|
||||
if self.is_mac_os { "F3" } else { "Alt+R" },
|
||||
if app_state.process_search_state.is_searching_with_regex {
|
||||
"*"
|
||||
let regex_style = if proc_widget_state
|
||||
.process_search_state
|
||||
.is_searching_with_regex
|
||||
{
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
" "
|
||||
}
|
||||
);
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let option_row = vec![
|
||||
Text::raw("\n\n"),
|
||||
Text::styled(&case_text, case_style),
|
||||
Text::raw(" "),
|
||||
Text::styled(&whole_text, whole_word_style),
|
||||
Text::raw(" "),
|
||||
Text::styled(®ex_text, regex_style),
|
||||
];
|
||||
option_text.extend(option_row);
|
||||
|
||||
search_text.extend(query_with_cursor);
|
||||
search_text.extend(option_text);
|
||||
|
||||
let current_border_style = if app_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_invalid_search
|
||||
{
|
||||
*INVALID_REGEX_STYLE
|
||||
} else {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::ProcessSearch => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
}
|
||||
};
|
||||
|
||||
let title = if draw_border {
|
||||
const TITLE_BASE: &str = " Esc to close ";
|
||||
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
let mut option_text = vec![];
|
||||
let case_text = format!(
|
||||
"{}({})",
|
||||
if small_mode { "Case" } else { "Match Case " },
|
||||
if self.is_mac_os { "F1" } else { "Alt+C" },
|
||||
);
|
||||
format!("{} Esc to close ", "─".repeat(repeat_num as usize))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let process_search_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(current_border_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(current_border_style)
|
||||
} else {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::ProcessSearch => Block::default()
|
||||
let whole_text = format!(
|
||||
"{}({})",
|
||||
if small_mode {
|
||||
"Whole"
|
||||
} else {
|
||||
"Match Whole Word "
|
||||
},
|
||||
if self.is_mac_os { "F2" } else { "Alt+W" },
|
||||
);
|
||||
|
||||
let regex_text = format!(
|
||||
"{}({})",
|
||||
if small_mode { "Regex" } else { "Use Regex " },
|
||||
if self.is_mac_os { "F3" } else { "Alt+R" },
|
||||
);
|
||||
|
||||
let option_row = vec![
|
||||
Text::raw("\n\n"),
|
||||
Text::styled(&case_text, case_style),
|
||||
Text::raw(if small_mode { " " } else { " " }),
|
||||
Text::styled(&whole_text, whole_word_style),
|
||||
Text::raw(if small_mode { " " } else { " " }),
|
||||
Text::styled(®ex_text, regex_style),
|
||||
];
|
||||
option_text.extend(option_row);
|
||||
|
||||
search_text.extend(query_with_cursor);
|
||||
search_text.extend(option_text);
|
||||
|
||||
let current_border_style = if proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_invalid_search
|
||||
{
|
||||
*INVALID_REGEX_STYLE
|
||||
} else if is_on_widget {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
};
|
||||
|
||||
let title = if draw_border {
|
||||
const TITLE_BASE: &str = " Esc to close ";
|
||||
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
format!("{} Esc to close ", "─".repeat(repeat_num as usize))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let process_search_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(current_border_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(current_border_style)
|
||||
} else if is_on_widget {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(current_border_style),
|
||||
_ => Block::default().borders(Borders::NONE),
|
||||
}
|
||||
};
|
||||
.border_style(current_border_style)
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(match app_state.current_widget_selected {
|
||||
WidgetPosition::ProcessSearch => 0,
|
||||
_ if !draw_border => 1,
|
||||
_ => 0,
|
||||
})
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
|
||||
Paragraph::new(search_text.iter())
|
||||
.block(process_search_block)
|
||||
.style(self.colours.text_style)
|
||||
.alignment(Alignment::Left)
|
||||
.wrap(false)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
Paragraph::new(search_text.iter())
|
||||
.block(process_search_block)
|
||||
.style(self.colours.text_style)
|
||||
.alignment(Alignment::Left)
|
||||
.wrap(false)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use tui::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::{self, WidgetPosition},
|
||||
app,
|
||||
canvas::{
|
||||
drawing_utils::{get_start_position, get_variable_intrinsic_widths},
|
||||
Painter,
|
||||
@ -28,43 +28,37 @@ lazy_static! {
|
||||
pub trait TempTableWidget {
|
||||
fn draw_temp_table<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl TempTableWidget for Painter {
|
||||
fn draw_temp_table<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
) {
|
||||
let temp_sensor_data: &[Vec<String>] = &app_state.canvas_data.temp_sensor_data;
|
||||
if let Some(temp_widget_state) = app_state.temp_state.widget_states.get_mut(&widget_id) {
|
||||
let temp_sensor_data: &mut [Vec<String>] = &mut app_state.canvas_data.temp_sensor_data;
|
||||
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let start_position = get_start_position(
|
||||
num_rows,
|
||||
&app_state.app_scroll_positions.scroll_direction,
|
||||
&mut app_state
|
||||
.app_scroll_positions
|
||||
.temp_scroll_state
|
||||
.previous_scroll_position,
|
||||
app_state
|
||||
.app_scroll_positions
|
||||
.temp_scroll_state
|
||||
.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||
let start_position = get_start_position(
|
||||
num_rows,
|
||||
&temp_widget_state.scroll_state.scroll_direction,
|
||||
&mut temp_widget_state.scroll_state.previous_scroll_position,
|
||||
temp_widget_state.scroll_state.current_scroll_position,
|
||||
app_state.is_resized,
|
||||
);
|
||||
|
||||
let sliced_vec = &temp_sensor_data[start_position as usize..];
|
||||
let mut temp_row_counter: i64 = 0;
|
||||
let sliced_vec = &temp_sensor_data[start_position as usize..];
|
||||
let mut temp_row_counter: i64 = 0;
|
||||
let current_widget_id = app_state.current_widget.widget_id;
|
||||
|
||||
let temperature_rows = sliced_vec.iter().map(|temp_row| {
|
||||
Row::StyledData(
|
||||
temp_row.iter(),
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Temp => {
|
||||
let temperature_rows = sliced_vec.iter().map(|temp_row| {
|
||||
Row::StyledData(
|
||||
temp_row.iter(),
|
||||
if current_widget_id == widget_id {
|
||||
if temp_row_counter as u64
|
||||
== app_state
|
||||
.app_scroll_positions
|
||||
.temp_scroll_state
|
||||
.current_scroll_position
|
||||
== temp_widget_state.scroll_state.current_scroll_position
|
||||
- start_position
|
||||
{
|
||||
temp_row_counter = -1;
|
||||
@ -75,82 +69,86 @@ impl TempTableWidget for Painter {
|
||||
}
|
||||
self.colours.text_style
|
||||
}
|
||||
}
|
||||
_ => self.colours.text_style,
|
||||
},
|
||||
)
|
||||
});
|
||||
} else {
|
||||
self.colours.text_style
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = [0.5, 0.5];
|
||||
let variable_intrinsic_results =
|
||||
get_variable_intrinsic_widths(width as u16, &width_ratios, &TEMP_HEADERS_LENS);
|
||||
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = [0.5, 0.5];
|
||||
let variable_intrinsic_results =
|
||||
get_variable_intrinsic_widths(width as u16, &width_ratios, &TEMP_HEADERS_LENS);
|
||||
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
||||
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Temperatures ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Temperatures ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Temperatures ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Temperatures ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
|
||||
result_title
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
String::new()
|
||||
} else {
|
||||
" Temperatures ".to_string()
|
||||
};
|
||||
result_title
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
String::new()
|
||||
} else {
|
||||
" Temperatures ".to_string()
|
||||
};
|
||||
|
||||
let temp_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Temp => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
}
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(match app_state.current_widget_selected {
|
||||
WidgetPosition::Temp => self.colours.highlighted_border_style,
|
||||
_ => self.colours.border_style,
|
||||
})
|
||||
} else {
|
||||
match app_state.current_widget_selected {
|
||||
WidgetPosition::Temp => Block::default()
|
||||
let temp_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
}
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(if app_state.current_widget.widget_id == widget_id {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
})
|
||||
} else if app_state.current_widget.widget_id == widget_id {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style),
|
||||
_ => Block::default().borders(Borders::NONE),
|
||||
}
|
||||
};
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(match app_state.current_widget_selected {
|
||||
WidgetPosition::Temp => 0,
|
||||
_ if !draw_border => 1,
|
||||
_ => 0,
|
||||
})
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(
|
||||
if app_state.current_widget.widget_id == widget_id || draw_border {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
},
|
||||
)
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
|
||||
// Draw
|
||||
Table::new(TEMP_HEADERS.iter(), temperature_rows)
|
||||
.block(temp_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
// Draw
|
||||
Table::new(TEMP_HEADERS.iter(), temperature_rows)
|
||||
.block(temp_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, margined_draw_loc[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
// Default widget ID
|
||||
pub const DEFAULT_WIDGET_ID: u64 = 56709;
|
||||
|
||||
// How long to store data.
|
||||
pub const STALE_MAX_MILLISECONDS: u64 = 600 * 1000; // Keep 10 minutes of data.
|
||||
|
||||
@ -56,7 +59,7 @@ pub const GENERAL_HELP_TEXT: [&str; 18] = [
|
||||
|
||||
pub const PROCESS_HELP_TEXT: [&str; 8] = [
|
||||
"Process Keybindings\n\n",
|
||||
"dd Kill the highlighted process\n",
|
||||
"dd, Delete Kill the highlighted process\n",
|
||||
"c Sort by CPU usage\n",
|
||||
"m Sort by memory usage\n",
|
||||
"p Sort by PID\n",
|
||||
@ -134,20 +137,16 @@ pub const DEFAULT_CONFIG_CONTENT: &str = r##"
|
||||
#temperature_type = "fahrenheit"
|
||||
#temperature_type = "celsius"
|
||||
|
||||
# Defaults to processes. Default widget is one of:
|
||||
#default_widget = "cpu_default"
|
||||
#default_widget = "memory_default"
|
||||
#default_widget = "disk_default"
|
||||
#default_widget = "temperature_default"
|
||||
#default_widget = "network_default"
|
||||
#default_widget = "process_default"
|
||||
|
||||
# The default time interval (in milliseconds).
|
||||
#default_time_value = 60000
|
||||
|
||||
# The time delta on each zoom in/out action (in milliseconds).
|
||||
#time_delta = 15000
|
||||
|
||||
# Override layout default widget
|
||||
#default_widget_type = "proc"
|
||||
#default_widget_count = 1
|
||||
|
||||
# These are all the components that support custom theming. Currently, it only
|
||||
# supports taking in a string representing a hex colour. Note that colour support
|
||||
# will, at the end of the day, depend on terminal support - for example, the
|
||||
@ -200,4 +199,28 @@ pub const DEFAULT_CONFIG_CONTENT: &str = r##"
|
||||
|
||||
# Represents the cursor's colour.
|
||||
#cursor_color="#458588"
|
||||
|
||||
# The default widget layout:
|
||||
#[[row]]
|
||||
# ratio=30
|
||||
# [[row.child]]
|
||||
# type="cpu"
|
||||
#[[row]]
|
||||
# ratio=40
|
||||
# [[row.child]]
|
||||
# ratio=4
|
||||
# type="mem"
|
||||
# [[row.child]]
|
||||
# ratio=3
|
||||
# [[row.child.child]]
|
||||
# type="temp"
|
||||
# [[row.child.child]]
|
||||
# type="disk"
|
||||
#[[row]]
|
||||
# ratio=30
|
||||
# [[row.child]]
|
||||
# type="net"
|
||||
# [[row.child]]
|
||||
# type="proc"
|
||||
# default=true
|
||||
"##;
|
||||
|
@ -103,7 +103,7 @@ pub fn convert_disk_row(current_data: &data_farmer::DataCollection) -> Vec<Vec<S
|
||||
}
|
||||
|
||||
pub fn convert_cpu_data_points(
|
||||
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||
current_data: &data_farmer::DataCollection, is_frozen: bool,
|
||||
) -> Vec<ConvertedCpuData> {
|
||||
let mut cpu_data_vector: Vec<ConvertedCpuData> = Vec::new();
|
||||
let current_time = if is_frozen {
|
||||
@ -117,8 +117,7 @@ pub fn convert_cpu_data_points(
|
||||
};
|
||||
|
||||
for (time, data) in ¤t_data.timed_data_vec {
|
||||
let time_from_start: f64 =
|
||||
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
let time_from_start: f64 = (current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
|
||||
for (itx, cpu) in data.cpu_data.iter().enumerate() {
|
||||
// Check if the vector exists yet
|
||||
@ -133,15 +132,15 @@ pub fn convert_cpu_data_points(
|
||||
|
||||
//Insert joiner points
|
||||
for &(joiner_offset, joiner_val) in &cpu.1 {
|
||||
let offset_time = time_from_start - joiner_offset as f64;
|
||||
let offset_time = time_from_start + joiner_offset as f64;
|
||||
cpu_data_vector[itx_offset]
|
||||
.cpu_data
|
||||
.push((offset_time, joiner_val));
|
||||
.push((-offset_time, joiner_val));
|
||||
}
|
||||
|
||||
cpu_data_vector[itx_offset]
|
||||
.cpu_data
|
||||
.push((time_from_start, cpu.0));
|
||||
.push((-time_from_start, cpu.0));
|
||||
}
|
||||
|
||||
if *time == current_time {
|
||||
@ -153,7 +152,7 @@ pub fn convert_cpu_data_points(
|
||||
}
|
||||
|
||||
pub fn convert_mem_data_points(
|
||||
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||
current_data: &data_farmer::DataCollection, is_frozen: bool,
|
||||
) -> Vec<Point> {
|
||||
let mut result: Vec<Point> = Vec::new();
|
||||
let current_time = if is_frozen {
|
||||
@ -167,16 +166,15 @@ pub fn convert_mem_data_points(
|
||||
};
|
||||
|
||||
for (time, data) in ¤t_data.timed_data_vec {
|
||||
let time_from_start: f64 =
|
||||
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
let time_from_start: f64 = (current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
|
||||
//Insert joiner points
|
||||
for &(joiner_offset, joiner_val) in &data.mem_data.1 {
|
||||
let offset_time = time_from_start - joiner_offset as f64;
|
||||
result.push((offset_time, joiner_val));
|
||||
let offset_time = time_from_start + joiner_offset as f64;
|
||||
result.push((-offset_time, joiner_val));
|
||||
}
|
||||
|
||||
result.push((time_from_start, data.mem_data.0));
|
||||
result.push((-time_from_start, data.mem_data.0));
|
||||
|
||||
if *time == current_time {
|
||||
break;
|
||||
@ -187,7 +185,7 @@ pub fn convert_mem_data_points(
|
||||
}
|
||||
|
||||
pub fn convert_swap_data_points(
|
||||
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||
current_data: &data_farmer::DataCollection, is_frozen: bool,
|
||||
) -> Vec<Point> {
|
||||
let mut result: Vec<Point> = Vec::new();
|
||||
let current_time = if is_frozen {
|
||||
@ -201,16 +199,15 @@ pub fn convert_swap_data_points(
|
||||
};
|
||||
|
||||
for (time, data) in ¤t_data.timed_data_vec {
|
||||
let time_from_start: f64 =
|
||||
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
let time_from_start: f64 = (current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
|
||||
//Insert joiner points
|
||||
for &(joiner_offset, joiner_val) in &data.swap_data.1 {
|
||||
let offset_time = time_from_start - joiner_offset as f64;
|
||||
result.push((offset_time, joiner_val));
|
||||
let offset_time = time_from_start + joiner_offset as f64;
|
||||
result.push((-offset_time, joiner_val));
|
||||
}
|
||||
|
||||
result.push((time_from_start, data.swap_data.0));
|
||||
result.push((-time_from_start, data.swap_data.0));
|
||||
|
||||
if *time == current_time {
|
||||
break;
|
||||
@ -257,7 +254,7 @@ pub fn convert_mem_labels(current_data: &data_farmer::DataCollection) -> (String
|
||||
}
|
||||
|
||||
pub fn get_rx_tx_data_points(
|
||||
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||
current_data: &data_farmer::DataCollection, is_frozen: bool,
|
||||
) -> (Vec<Point>, Vec<Point>) {
|
||||
let mut rx: Vec<Point> = Vec::new();
|
||||
let mut tx: Vec<Point> = Vec::new();
|
||||
@ -274,22 +271,21 @@ pub fn get_rx_tx_data_points(
|
||||
|
||||
// TODO: [REFACTOR] Can we use collect on this, CPU, and MEM?
|
||||
for (time, data) in ¤t_data.timed_data_vec {
|
||||
let time_from_start: f64 =
|
||||
(display_time as f64 - current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
let time_from_start: f64 = (current_time.duration_since(*time).as_millis() as f64).floor();
|
||||
|
||||
//Insert joiner points
|
||||
for &(joiner_offset, joiner_val) in &data.rx_data.1 {
|
||||
let offset_time = time_from_start - joiner_offset as f64;
|
||||
rx.push((offset_time, joiner_val));
|
||||
let offset_time = time_from_start + joiner_offset as f64;
|
||||
rx.push((-offset_time, joiner_val));
|
||||
}
|
||||
|
||||
for &(joiner_offset, joiner_val) in &data.tx_data.1 {
|
||||
let offset_time = time_from_start - joiner_offset as f64;
|
||||
tx.push((offset_time, joiner_val));
|
||||
let offset_time = time_from_start + joiner_offset as f64;
|
||||
tx.push((-offset_time, joiner_val));
|
||||
}
|
||||
|
||||
rx.push((time_from_start, data.rx_data.0));
|
||||
tx.push((time_from_start, data.tx_data.0));
|
||||
rx.push((-time_from_start, data.rx_data.0));
|
||||
tx.push((-time_from_start, data.tx_data.0));
|
||||
|
||||
if *time == current_time {
|
||||
break;
|
||||
@ -300,9 +296,9 @@ pub fn get_rx_tx_data_points(
|
||||
}
|
||||
|
||||
pub fn convert_network_data_points(
|
||||
current_data: &data_farmer::DataCollection, display_time: u64, is_frozen: bool,
|
||||
current_data: &data_farmer::DataCollection, is_frozen: bool,
|
||||
) -> ConvertedNetworkData {
|
||||
let (rx, tx) = get_rx_tx_data_points(current_data, display_time, is_frozen);
|
||||
let (rx, tx) = get_rx_tx_data_points(current_data, is_frozen);
|
||||
|
||||
let total_rx_converted_result: (f64, String);
|
||||
let rx_converted_result: (f64, String);
|
||||
|
267
src/main.rs
267
src/main.rs
@ -86,14 +86,8 @@ fn get_matches() -> clap::ArgMatches<'static> {
|
||||
(@arg TIME_DELTA: -d --time_delta +takes_value "The amount changed upon zooming in/out in milliseconds; minimum is 1s, defaults to 15s.")
|
||||
(@arg HIDE_TIME: --hide_time "Completely hide the time scaling")
|
||||
(@arg AUTOHIDE_TIME: --autohide_time "Automatically hide the time scaling in graphs after being shown for a brief moment when zoomed in/out. If time is disabled via --hide_time then this will have no effect.")
|
||||
(@group DEFAULT_WIDGET =>
|
||||
(@arg CPU_WIDGET: --cpu_default "Selects the CPU widget to be selected by default.")
|
||||
(@arg MEM_WIDGET: --memory_default "Selects the memory widget to be selected by default.")
|
||||
(@arg DISK_WIDGET: --disk_default "Selects the disk widget to be selected by default.")
|
||||
(@arg TEMP_WIDGET: --temperature_default "Selects the temp widget to be selected by default.")
|
||||
(@arg NET_WIDGET: --network_default "Selects the network widget to be selected by default.")
|
||||
(@arg PROC_WIDGET: --process_default "Selects the process widget to be selected by default. This is the default if nothing is set.")
|
||||
)
|
||||
(@arg DEFAULT_WIDGET_TYPE: --default_widget_type +takes_value "The default widget type to select by default.")
|
||||
(@arg DEFAULT_WIDGET_COUNT: --default_widget_count +takes_value "Which number of the selected widget type to select, from left to right, top to bottom. Defaults to 1.")
|
||||
//(@arg TURNED_OFF_CPUS: -t ... +takes_value "Hides CPU data points by default") // TODO: [FEATURE] Enable disabling cores in config/flags
|
||||
)
|
||||
.get_matches()
|
||||
@ -105,14 +99,11 @@ fn main() -> error::Result<()> {
|
||||
|
||||
let config: Config = create_config(matches.value_of("CONFIG_LOCATION"))?;
|
||||
|
||||
// Create "app" struct, which will control most of the program and store settings/state
|
||||
let mut app = build_app(&matches, &config)?;
|
||||
// Get widget layout separately
|
||||
let (widget_layout, default_widget_id) = get_widget_layout(&matches, &config)?;
|
||||
|
||||
// TODO: [REFACTOR] Change this
|
||||
enable_app_grouping(&matches, &config, &mut app);
|
||||
enable_app_case_sensitive(&matches, &config, &mut app);
|
||||
enable_app_match_whole_word(&matches, &config, &mut app);
|
||||
enable_app_use_regex(&matches, &config, &mut app);
|
||||
// Create "app" struct, which will control most of the program and store settings/state
|
||||
let mut app = build_app(&matches, &config, &widget_layout, default_widget_id)?;
|
||||
|
||||
// Set up up tui and crossterm
|
||||
let mut stdout_val = stdout();
|
||||
@ -150,13 +141,13 @@ fn main() -> error::Result<()> {
|
||||
app.app_config_fields.show_average_cpu,
|
||||
);
|
||||
|
||||
let mut painter = canvas::Painter::default();
|
||||
let mut painter = canvas::Painter::init(widget_layout);
|
||||
if let Err(config_check) = generate_config_colours(&config, &mut painter) {
|
||||
cleanup_terminal(&mut terminal)?;
|
||||
return Err(config_check);
|
||||
}
|
||||
painter.colours.generate_remaining_cpu_colours();
|
||||
painter.initialize();
|
||||
painter.complete_painter_init();
|
||||
|
||||
let mut first_run = true;
|
||||
loop {
|
||||
@ -179,11 +170,7 @@ fn main() -> error::Result<()> {
|
||||
// Convert all data into tui-compliant components
|
||||
|
||||
// Network
|
||||
let network_data = convert_network_data_points(
|
||||
&app.data_collection,
|
||||
app.net_state.current_display_time,
|
||||
false,
|
||||
);
|
||||
let network_data = convert_network_data_points(&app.data_collection, false);
|
||||
app.canvas_data.network_data_rx = network_data.rx;
|
||||
app.canvas_data.network_data_tx = network_data.tx;
|
||||
app.canvas_data.rx_display = network_data.rx_display;
|
||||
@ -196,17 +183,12 @@ fn main() -> error::Result<()> {
|
||||
|
||||
// Temperatures
|
||||
app.canvas_data.temp_sensor_data = convert_temp_row(&app);
|
||||
|
||||
// Memory
|
||||
app.canvas_data.mem_data = convert_mem_data_points(
|
||||
&app.data_collection,
|
||||
app.mem_state.current_display_time,
|
||||
false,
|
||||
);
|
||||
app.canvas_data.swap_data = convert_swap_data_points(
|
||||
&app.data_collection,
|
||||
app.mem_state.current_display_time,
|
||||
false,
|
||||
);
|
||||
app.canvas_data.mem_data =
|
||||
convert_mem_data_points(&app.data_collection, false);
|
||||
app.canvas_data.swap_data =
|
||||
convert_swap_data_points(&app.data_collection, false);
|
||||
let memory_and_swap_labels = convert_mem_labels(&app.data_collection);
|
||||
app.canvas_data.mem_label = memory_and_swap_labels.0;
|
||||
app.canvas_data.swap_label = memory_and_swap_labels.1;
|
||||
@ -214,23 +196,23 @@ fn main() -> error::Result<()> {
|
||||
// Pre-fill CPU if needed
|
||||
if first_run {
|
||||
let cpu_len = app.data_collection.cpu_harvest.len();
|
||||
app.cpu_state.core_show_vec = vec![true; cpu_len];
|
||||
app.cpu_state.num_cpus_shown = cpu_len as u64;
|
||||
app.cpu_state.widget_states.values_mut().for_each(|state| {
|
||||
state.core_show_vec = vec![true; cpu_len];
|
||||
state.num_cpus_shown = cpu_len;
|
||||
});
|
||||
app.cpu_state.num_cpus_total = cpu_len;
|
||||
first_run = false;
|
||||
}
|
||||
|
||||
// CPU
|
||||
app.canvas_data.cpu_data = convert_cpu_data_points(
|
||||
&app.data_collection,
|
||||
app.cpu_state.current_display_time,
|
||||
false,
|
||||
);
|
||||
app.canvas_data.cpu_data =
|
||||
convert_cpu_data_points(&app.data_collection, false);
|
||||
|
||||
// Processes
|
||||
let (single, grouped) = convert_process_data(&app.data_collection);
|
||||
app.canvas_data.process_data = single;
|
||||
app.canvas_data.grouped_process_data = grouped;
|
||||
update_final_process_list(&mut app);
|
||||
update_all_process_lists(&mut app);
|
||||
}
|
||||
}
|
||||
BottomEvent::Clean => {
|
||||
@ -240,14 +222,6 @@ fn main() -> error::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Quick fix for tab updating the table headers
|
||||
if let data_harvester::processes::ProcessSorting::PID = &app.process_sorting_type {
|
||||
if app.is_grouped() {
|
||||
app.process_sorting_type = data_harvester::processes::ProcessSorting::CPU; // Go back to default, negate PID for group
|
||||
app.process_sorting_reverse = true;
|
||||
}
|
||||
}
|
||||
|
||||
try_drawing(&mut terminal, &mut app, &mut painter)?;
|
||||
}
|
||||
|
||||
@ -292,19 +266,13 @@ fn handle_key_event_or_break(
|
||||
KeyCode::Backspace => app.on_backspace(),
|
||||
KeyCode::Delete => app.on_delete(),
|
||||
KeyCode::F(1) => {
|
||||
if app.is_in_search_widget() {
|
||||
app.toggle_ignore_case();
|
||||
}
|
||||
app.toggle_ignore_case();
|
||||
}
|
||||
KeyCode::F(2) => {
|
||||
if app.is_in_search_widget() {
|
||||
app.toggle_search_whole_word();
|
||||
}
|
||||
app.toggle_search_whole_word();
|
||||
}
|
||||
KeyCode::F(3) => {
|
||||
if app.is_in_search_widget() {
|
||||
app.toggle_search_regex();
|
||||
}
|
||||
app.toggle_search_regex();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -313,19 +281,13 @@ fn handle_key_event_or_break(
|
||||
if let KeyModifiers::ALT = event.modifiers {
|
||||
match event.code {
|
||||
KeyCode::Char('c') | KeyCode::Char('C') => {
|
||||
if app.is_in_search_widget() {
|
||||
app.toggle_ignore_case();
|
||||
}
|
||||
app.toggle_ignore_case();
|
||||
}
|
||||
KeyCode::Char('w') | KeyCode::Char('W') => {
|
||||
if app.is_in_search_widget() {
|
||||
app.toggle_search_whole_word();
|
||||
}
|
||||
app.toggle_search_whole_word();
|
||||
}
|
||||
KeyCode::Char('r') | KeyCode::Char('R') => {
|
||||
if app.is_in_search_widget() {
|
||||
app.toggle_search_regex();
|
||||
}
|
||||
app.toggle_search_regex();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -550,59 +512,65 @@ fn panic_hook(panic_info: &PanicInfo<'_>) {
|
||||
}
|
||||
|
||||
fn handle_force_redraws(app: &mut App) {
|
||||
if app.force_update_processes {
|
||||
update_final_process_list(app);
|
||||
app.force_update_processes = false;
|
||||
// Currently we use an Option... because we might want to future-proof this
|
||||
// if we eventually get widget-specific redrawing!
|
||||
if app.proc_state.force_update_all {
|
||||
update_all_process_lists(app);
|
||||
app.proc_state.force_update_all = false;
|
||||
} else if let Some(widget_id) = app.proc_state.force_update {
|
||||
update_final_process_list(app, widget_id);
|
||||
app.proc_state.force_update = None;
|
||||
}
|
||||
|
||||
if app.cpu_state.force_update {
|
||||
app.canvas_data.cpu_data = convert_cpu_data_points(
|
||||
&app.data_collection,
|
||||
app.cpu_state.current_display_time,
|
||||
app.is_frozen,
|
||||
);
|
||||
app.cpu_state.force_update = false;
|
||||
if app.cpu_state.force_update.is_some() {
|
||||
app.canvas_data.cpu_data = convert_cpu_data_points(&app.data_collection, app.is_frozen);
|
||||
app.cpu_state.force_update = None;
|
||||
}
|
||||
|
||||
if app.mem_state.force_update {
|
||||
app.canvas_data.mem_data = convert_mem_data_points(
|
||||
&app.data_collection,
|
||||
app.mem_state.current_display_time,
|
||||
app.is_frozen,
|
||||
);
|
||||
app.canvas_data.swap_data = convert_swap_data_points(
|
||||
&app.data_collection,
|
||||
app.mem_state.current_display_time,
|
||||
app.is_frozen,
|
||||
);
|
||||
app.mem_state.force_update = false;
|
||||
if app.mem_state.force_update.is_some() {
|
||||
app.canvas_data.mem_data = convert_mem_data_points(&app.data_collection, app.is_frozen);
|
||||
app.canvas_data.swap_data = convert_swap_data_points(&app.data_collection, app.is_frozen);
|
||||
app.mem_state.force_update = None;
|
||||
}
|
||||
|
||||
if app.net_state.force_update {
|
||||
let (rx, tx) = get_rx_tx_data_points(
|
||||
&app.data_collection,
|
||||
app.net_state.current_display_time,
|
||||
app.is_frozen,
|
||||
);
|
||||
if app.net_state.force_update.is_some() {
|
||||
let (rx, tx) = get_rx_tx_data_points(&app.data_collection, app.is_frozen);
|
||||
app.canvas_data.network_data_rx = rx;
|
||||
app.canvas_data.network_data_tx = tx;
|
||||
app.net_state.force_update = false;
|
||||
app.net_state.force_update = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_final_process_list(app: &mut App) {
|
||||
let mut filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped() {
|
||||
fn update_all_process_lists(app: &mut App) {
|
||||
let widget_ids = app
|
||||
.proc_state
|
||||
.widget_states
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
widget_ids.into_iter().for_each(|widget_id| {
|
||||
update_final_process_list(app, widget_id);
|
||||
});
|
||||
}
|
||||
|
||||
fn update_final_process_list(app: &mut App, widget_id: u64) {
|
||||
let is_invalid_or_blank = match app.proc_state.widget_states.get(&widget_id) {
|
||||
Some(process_state) => process_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_invalid_or_blank_search(),
|
||||
None => false,
|
||||
};
|
||||
|
||||
let filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped(widget_id) {
|
||||
app.canvas_data
|
||||
.grouped_process_data
|
||||
.iter()
|
||||
.filter(|process| {
|
||||
if app
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_invalid_or_blank_search()
|
||||
{
|
||||
if is_invalid_or_blank {
|
||||
return true;
|
||||
} else if let Some(matcher_result) = app.get_current_regex_matcher() {
|
||||
} else if let Some(matcher_result) = app.get_current_regex_matcher(widget_id) {
|
||||
if let Ok(matcher) = matcher_result {
|
||||
return matcher.is_match(&process.name);
|
||||
}
|
||||
@ -613,20 +581,20 @@ fn update_final_process_list(app: &mut App) {
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
let is_searching_with_pid = match app.proc_state.widget_states.get(&widget_id) {
|
||||
Some(process_state) => process_state.process_search_state.is_searching_with_pid,
|
||||
None => false,
|
||||
};
|
||||
|
||||
app.canvas_data
|
||||
.process_data
|
||||
.iter()
|
||||
.filter_map(|(_pid, process)| {
|
||||
let mut result = true;
|
||||
|
||||
if !app
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_invalid_or_blank_search()
|
||||
{
|
||||
if let Some(matcher_result) = app.get_current_regex_matcher() {
|
||||
if !is_invalid_or_blank {
|
||||
if let Some(matcher_result) = app.get_current_regex_matcher(widget_id) {
|
||||
if let Ok(matcher) = matcher_result {
|
||||
if app.process_search_state.is_searching_with_pid {
|
||||
if is_searching_with_pid {
|
||||
result = matcher.is_match(&process.pid.to_string());
|
||||
} else {
|
||||
result = matcher.is_match(&process.name);
|
||||
@ -650,31 +618,84 @@ fn update_final_process_list(app: &mut App) {
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
sort_process_data(&mut filtered_process_data, app);
|
||||
app.canvas_data.finalized_process_data = filtered_process_data;
|
||||
// Quick fix for tab updating the table headers
|
||||
if let Some(proc_widget_state) = app.proc_state.widget_states.get_mut(&widget_id) {
|
||||
if let data_harvester::processes::ProcessSorting::PID =
|
||||
proc_widget_state.process_sorting_type
|
||||
{
|
||||
if proc_widget_state.is_grouped {
|
||||
proc_widget_state.process_sorting_type =
|
||||
data_harvester::processes::ProcessSorting::CPU; // Go back to default, negate PID for group
|
||||
proc_widget_state.process_sorting_reverse = true;
|
||||
}
|
||||
}
|
||||
|
||||
let mut resulting_processes = filtered_process_data;
|
||||
sort_process_data(&mut resulting_processes, proc_widget_state);
|
||||
|
||||
if proc_widget_state.scroll_state.current_scroll_position
|
||||
>= resulting_processes.len() as u64
|
||||
{
|
||||
proc_widget_state.scroll_state.current_scroll_position =
|
||||
if resulting_processes.len() > 1 {
|
||||
resulting_processes.len() as u64 - 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
proc_widget_state.scroll_state.previous_scroll_position = 0;
|
||||
proc_widget_state.scroll_state.scroll_direction = app::ScrollDirection::DOWN;
|
||||
}
|
||||
|
||||
app.canvas_data
|
||||
.finalized_process_data_map
|
||||
.insert(widget_id, resulting_processes);
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_process_data(to_sort_vec: &mut Vec<ConvertedProcessData>, app: &App) {
|
||||
fn sort_process_data(
|
||||
to_sort_vec: &mut Vec<ConvertedProcessData>, proc_widget_state: &app::ProcWidgetState,
|
||||
) {
|
||||
to_sort_vec.sort_by(|a, b| utils::gen_util::get_ordering(&a.name, &b.name, false));
|
||||
|
||||
match app.process_sorting_type {
|
||||
match proc_widget_state.process_sorting_type {
|
||||
ProcessSorting::CPU => {
|
||||
to_sort_vec.sort_by(|a, b| {
|
||||
utils::gen_util::get_ordering(a.cpu_usage, b.cpu_usage, app.process_sorting_reverse)
|
||||
utils::gen_util::get_ordering(
|
||||
a.cpu_usage,
|
||||
b.cpu_usage,
|
||||
proc_widget_state.process_sorting_reverse,
|
||||
)
|
||||
});
|
||||
}
|
||||
ProcessSorting::MEM => {
|
||||
to_sort_vec.sort_by(|a, b| {
|
||||
utils::gen_util::get_ordering(a.mem_usage, b.mem_usage, app.process_sorting_reverse)
|
||||
utils::gen_util::get_ordering(
|
||||
a.mem_usage,
|
||||
b.mem_usage,
|
||||
proc_widget_state.process_sorting_reverse,
|
||||
)
|
||||
});
|
||||
}
|
||||
ProcessSorting::NAME => to_sort_vec.sort_by(|a, b| {
|
||||
utils::gen_util::get_ordering(&a.name, &b.name, app.process_sorting_reverse)
|
||||
}),
|
||||
ProcessSorting::PID => {
|
||||
if !app.is_grouped() {
|
||||
ProcessSorting::NAME => {
|
||||
// Don't repeat if false...
|
||||
if proc_widget_state.process_sorting_reverse {
|
||||
to_sort_vec.sort_by(|a, b| {
|
||||
utils::gen_util::get_ordering(a.pid, b.pid, app.process_sorting_reverse)
|
||||
utils::gen_util::get_ordering(
|
||||
&a.name,
|
||||
&b.name,
|
||||
proc_widget_state.process_sorting_reverse,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
ProcessSorting::PID => {
|
||||
if !proc_widget_state.is_grouped {
|
||||
to_sort_vec.sort_by(|a, b| {
|
||||
utils::gen_util::get_ordering(
|
||||
a.pid,
|
||||
b.pid,
|
||||
proc_widget_state.process_sorting_reverse,
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
315
src/options.rs
315
src/options.rs
@ -1,21 +1,26 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::{
|
||||
app::{data_harvester, App, AppConfigFields, CpuState, MemState, NetState, WidgetPosition},
|
||||
app::{
|
||||
data_harvester, layout_manager::*, App, AppConfigFields, BasicTableWidgetState, CpuState,
|
||||
CpuWidgetState, DiskState, DiskWidgetState, MemState, MemWidgetState, NetState,
|
||||
NetWidgetState, ProcState, ProcWidgetState, TempState, TempWidgetState,
|
||||
},
|
||||
constants::*,
|
||||
utils::error::{self, BottomError},
|
||||
};
|
||||
|
||||
// use layout_manager::*;
|
||||
use layout_options::*;
|
||||
|
||||
// mod layout_manager;
|
||||
mod layout_options;
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
pub struct Config {
|
||||
pub flags: Option<ConfigFlags>,
|
||||
pub colors: Option<ConfigColours>,
|
||||
pub row: Option<Vec<Row>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
@ -37,6 +42,8 @@ pub struct ConfigFlags {
|
||||
pub time_delta: Option<u64>,
|
||||
pub autohide_time: Option<bool>,
|
||||
pub hide_time: Option<bool>,
|
||||
pub default_widget_type: Option<String>,
|
||||
pub default_widget_count: Option<u64>,
|
||||
//disabled_cpu_cores: Option<Vec<u64>>, // TODO: [FEATURE] Enable disabling cores in config/flags
|
||||
}
|
||||
|
||||
@ -60,27 +67,135 @@ pub struct ConfigColours {
|
||||
pub graph_color: Option<String>,
|
||||
}
|
||||
|
||||
pub fn build_app(matches: &clap::ArgMatches<'static>, config: &Config) -> error::Result<App> {
|
||||
pub fn build_app(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config, widget_layout: &BottomLayout,
|
||||
default_widget_id: u64,
|
||||
) -> error::Result<App> {
|
||||
let autohide_time = get_autohide_time(&matches, &config);
|
||||
let default_time_value = get_default_time_value(&matches, &config)?;
|
||||
let default_widget = get_default_widget(&matches, &config);
|
||||
let use_basic_mode = get_use_basic_mode(&matches, &config);
|
||||
|
||||
let current_widget_selected = if use_basic_mode {
|
||||
match default_widget {
|
||||
WidgetPosition::Cpu => WidgetPosition::BasicCpu,
|
||||
WidgetPosition::Network => WidgetPosition::BasicNet,
|
||||
WidgetPosition::Mem => WidgetPosition::BasicMem,
|
||||
_ => default_widget,
|
||||
}
|
||||
// For processes
|
||||
let is_grouped = get_app_grouping(matches, config);
|
||||
let is_case_sensitive = get_app_case_sensitive(matches, config);
|
||||
let is_match_whole_word = get_app_match_whole_word(matches, config);
|
||||
let is_use_regex = get_app_use_regex(matches, config);
|
||||
|
||||
let mut widget_map = HashMap::new();
|
||||
let mut cpu_state_map: HashMap<u64, CpuWidgetState> = HashMap::new();
|
||||
let mut mem_state_map: HashMap<u64, MemWidgetState> = HashMap::new();
|
||||
let mut net_state_map: HashMap<u64, NetWidgetState> = HashMap::new();
|
||||
let mut proc_state_map: HashMap<u64, ProcWidgetState> = HashMap::new();
|
||||
let mut temp_state_map: HashMap<u64, TempWidgetState> = HashMap::new();
|
||||
let mut disk_state_map: HashMap<u64, DiskWidgetState> = HashMap::new();
|
||||
|
||||
let autohide_timer = if autohide_time {
|
||||
Some(Instant::now())
|
||||
} else {
|
||||
default_widget
|
||||
None
|
||||
};
|
||||
|
||||
let previous_basic_table_selected = if default_widget.is_widget_table() {
|
||||
default_widget
|
||||
let (default_widget_type_option, _) = get_default_widget_and_count(matches, config)?;
|
||||
let mut initial_widget_id: u64 = default_widget_id;
|
||||
let mut initial_widget_type = BottomWidgetType::Proc;
|
||||
let is_custom_layout = config.row.is_some();
|
||||
|
||||
for row in &widget_layout.rows {
|
||||
for col in &row.children {
|
||||
for col_row in &col.children {
|
||||
for widget in &col_row.children {
|
||||
widget_map.insert(widget.widget_id, widget.clone());
|
||||
if let Some(default_widget_type) = &default_widget_type_option {
|
||||
if !is_custom_layout || use_basic_mode {
|
||||
match widget.widget_type {
|
||||
BottomWidgetType::BasicCpu => {
|
||||
if let BottomWidgetType::Cpu = *default_widget_type {
|
||||
initial_widget_id = widget.widget_id;
|
||||
initial_widget_type = BottomWidgetType::Cpu;
|
||||
}
|
||||
}
|
||||
BottomWidgetType::BasicMem => {
|
||||
if let BottomWidgetType::Mem = *default_widget_type {
|
||||
initial_widget_id = widget.widget_id;
|
||||
initial_widget_type = BottomWidgetType::Cpu;
|
||||
}
|
||||
}
|
||||
BottomWidgetType::BasicNet => {
|
||||
if let BottomWidgetType::Net = *default_widget_type {
|
||||
initial_widget_id = widget.widget_id;
|
||||
initial_widget_type = BottomWidgetType::Cpu;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if *default_widget_type == widget.widget_type {
|
||||
initial_widget_id = widget.widget_id;
|
||||
initial_widget_type = widget.widget_type.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match widget.widget_type {
|
||||
BottomWidgetType::Cpu => {
|
||||
cpu_state_map.insert(
|
||||
widget.widget_id,
|
||||
CpuWidgetState::init(default_time_value, autohide_timer),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Mem => {
|
||||
mem_state_map.insert(
|
||||
widget.widget_id,
|
||||
MemWidgetState::init(default_time_value, autohide_timer),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Net => {
|
||||
net_state_map.insert(
|
||||
widget.widget_id,
|
||||
NetWidgetState::init(default_time_value, autohide_timer),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Proc => {
|
||||
proc_state_map.insert(
|
||||
widget.widget_id,
|
||||
ProcWidgetState::init(
|
||||
is_case_sensitive,
|
||||
is_match_whole_word,
|
||||
is_use_regex,
|
||||
is_grouped,
|
||||
),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Disk => {
|
||||
disk_state_map.insert(widget.widget_id, DiskWidgetState::init());
|
||||
}
|
||||
BottomWidgetType::Temp => {
|
||||
temp_state_map.insert(widget.widget_id, TempWidgetState::init());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: [MODULARITY] Don't collect if not added!
|
||||
let basic_table_widget_state = if use_basic_mode {
|
||||
Some(match initial_widget_type {
|
||||
BottomWidgetType::Proc | BottomWidgetType::Disk | BottomWidgetType::Temp => {
|
||||
BasicTableWidgetState {
|
||||
currently_displayed_widget_type: initial_widget_type,
|
||||
currently_displayed_widget_id: initial_widget_id,
|
||||
widget_id: 100,
|
||||
}
|
||||
}
|
||||
_ => BasicTableWidgetState {
|
||||
currently_displayed_widget_type: BottomWidgetType::Proc,
|
||||
currently_displayed_widget_id: DEFAULT_WIDGET_ID,
|
||||
widget_id: 100,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
WidgetPosition::Process
|
||||
None
|
||||
};
|
||||
|
||||
let app_config_fields = AppConfigFields {
|
||||
@ -98,22 +213,62 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &Config) -> error:
|
||||
autohide_time,
|
||||
};
|
||||
|
||||
let time_now = if autohide_time {
|
||||
Some(Instant::now())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(App::builder()
|
||||
.app_config_fields(app_config_fields)
|
||||
.current_widget_selected(current_widget_selected)
|
||||
.previous_basic_table_selected(previous_basic_table_selected)
|
||||
.cpu_state(CpuState::init(default_time_value, time_now))
|
||||
.mem_state(MemState::init(default_time_value, time_now))
|
||||
.net_state(NetState::init(default_time_value, time_now))
|
||||
.cpu_state(CpuState::init(cpu_state_map))
|
||||
.mem_state(MemState::init(mem_state_map))
|
||||
.net_state(NetState::init(net_state_map))
|
||||
.proc_state(ProcState::init(proc_state_map))
|
||||
.disk_state(DiskState::init(disk_state_map))
|
||||
.temp_state(TempState::init(temp_state_map))
|
||||
.basic_table_widget_state(basic_table_widget_state)
|
||||
.current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // I think the unwrap is fine here
|
||||
.widget_map(widget_map)
|
||||
.build())
|
||||
}
|
||||
|
||||
pub fn get_widget_layout(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||
) -> error::Result<(BottomLayout, u64)> {
|
||||
let left_legend = get_use_left_legend(matches, config);
|
||||
let (default_widget_type, mut default_widget_count) =
|
||||
get_default_widget_and_count(matches, config)?;
|
||||
let mut default_widget_id = 1;
|
||||
|
||||
let bottom_layout = if get_use_basic_mode(matches, config) {
|
||||
default_widget_id = DEFAULT_WIDGET_ID;
|
||||
BottomLayout::init_basic_default()
|
||||
} else if let Some(rows) = &config.row {
|
||||
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||
let mut total_height_ratio = 0;
|
||||
|
||||
let mut ret_bottom_layout = BottomLayout {
|
||||
rows: rows
|
||||
.iter()
|
||||
.map(|row| {
|
||||
row.convert_row_to_bottom_row(
|
||||
&mut iter_id,
|
||||
&mut total_height_ratio,
|
||||
&mut default_widget_id,
|
||||
&default_widget_type,
|
||||
&mut default_widget_count,
|
||||
left_legend,
|
||||
)
|
||||
})
|
||||
.collect::<error::Result<Vec<_>>>()?,
|
||||
total_row_height_ratio: total_height_ratio,
|
||||
};
|
||||
ret_bottom_layout.get_movement_mappings();
|
||||
|
||||
ret_bottom_layout
|
||||
} else {
|
||||
default_widget_id = DEFAULT_WIDGET_ID;
|
||||
BottomLayout::init_default(left_legend)
|
||||
};
|
||||
|
||||
Ok((bottom_layout, default_widget_id))
|
||||
}
|
||||
|
||||
fn get_update_rate_in_milliseconds(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||
) -> error::Result<u64> {
|
||||
@ -296,56 +451,56 @@ fn get_time_interval(matches: &clap::ArgMatches<'static>, config: &Config) -> er
|
||||
Ok(time_interval as u64)
|
||||
}
|
||||
|
||||
pub fn enable_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
|
||||
pub fn get_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("GROUP_PROCESSES") {
|
||||
app.toggle_grouping();
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(grouping) = flags.group_processes {
|
||||
if grouping {
|
||||
app.toggle_grouping();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn enable_app_case_sensitive(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App,
|
||||
) {
|
||||
pub fn get_app_case_sensitive(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("CASE_SENSITIVE") {
|
||||
app.process_search_state.search_toggle_ignore_case();
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(case_sensitive) = flags.case_sensitive {
|
||||
if case_sensitive {
|
||||
app.process_search_state.search_toggle_ignore_case();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn enable_app_match_whole_word(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App,
|
||||
) {
|
||||
pub fn get_app_match_whole_word(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("WHOLE_WORD") {
|
||||
app.process_search_state.search_toggle_whole_word();
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(whole_word) = flags.whole_word {
|
||||
if whole_word {
|
||||
app.process_search_state.search_toggle_whole_word();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn enable_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
|
||||
pub fn get_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("REGEX_DEFAULT") {
|
||||
app.process_search_state.search_toggle_regex();
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(regex) = flags.regex {
|
||||
if regex {
|
||||
app.process_search_state.search_toggle_regex();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn get_hide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
@ -375,32 +530,52 @@ fn get_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bo
|
||||
false
|
||||
}
|
||||
|
||||
fn get_default_widget(matches: &clap::ArgMatches<'static>, config: &Config) -> WidgetPosition {
|
||||
if matches.is_present("CPU_WIDGET") {
|
||||
return WidgetPosition::Cpu;
|
||||
} else if matches.is_present("MEM_WIDGET") {
|
||||
return WidgetPosition::Mem;
|
||||
} else if matches.is_present("DISK_WIDGET") {
|
||||
return WidgetPosition::Disk;
|
||||
} else if matches.is_present("TEMP_WIDGET") {
|
||||
return WidgetPosition::Temp;
|
||||
} else if matches.is_present("NET_WIDGET") {
|
||||
return WidgetPosition::Network;
|
||||
} else if matches.is_present("PROC_WIDGET") {
|
||||
return WidgetPosition::Process;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(default_widget) = &flags.default_widget {
|
||||
return match default_widget.as_str() {
|
||||
"cpu_default" => WidgetPosition::Cpu,
|
||||
"memory_default" => WidgetPosition::Mem,
|
||||
"processes_default" => WidgetPosition::Process,
|
||||
"network_default" => WidgetPosition::Network,
|
||||
"temperature_default" => WidgetPosition::Temp,
|
||||
"disk_default" => WidgetPosition::Disk,
|
||||
_ => WidgetPosition::Process,
|
||||
};
|
||||
fn get_default_widget_and_count(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||
) -> error::Result<(Option<BottomWidgetType>, u64)> {
|
||||
let widget_type = if let Some(widget_type) = matches.value_of("DEFAULT_WIDGET_TYPE") {
|
||||
let parsed_widget = widget_type.parse::<BottomWidgetType>()?;
|
||||
if let BottomWidgetType::Empty = parsed_widget {
|
||||
None
|
||||
} else {
|
||||
Some(parsed_widget)
|
||||
}
|
||||
}
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(widget_type) = &flags.default_widget_type {
|
||||
let parsed_widget = widget_type.parse::<BottomWidgetType>()?;
|
||||
if let BottomWidgetType::Empty = parsed_widget {
|
||||
None
|
||||
} else {
|
||||
Some(parsed_widget)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
WidgetPosition::Process
|
||||
if widget_type.is_some() {
|
||||
let widget_count = if let Some(widget_count) = matches.value_of("DEFAULT_WIDGET_COUNT") {
|
||||
widget_count.parse::<u128>()?
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(widget_count) = flags.default_widget_count {
|
||||
widget_count as u128
|
||||
} else {
|
||||
1 as u128
|
||||
}
|
||||
} else {
|
||||
1 as u128
|
||||
};
|
||||
|
||||
if widget_count > std::u64::MAX as u128 {
|
||||
Err(BottomError::InvalidArg(
|
||||
"Please set your widget count to be at most unsigned INT_MAX.".to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok((widget_type, widget_count as u64))
|
||||
}
|
||||
} else {
|
||||
Ok((None, 1))
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
use serde::Deserialize;
|
||||
use toml::Value;
|
||||
|
307
src/options/layout_options.rs
Normal file
307
src/options/layout_options.rs
Normal file
@ -0,0 +1,307 @@
|
||||
use crate::app::layout_manager::*;
|
||||
use crate::error::Result;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Represents a row. This has a length of some sort (optional) and a vector
|
||||
/// of children.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename = "row")]
|
||||
pub struct Row {
|
||||
pub ratio: Option<u32>,
|
||||
pub child: Option<Vec<RowChildren>>,
|
||||
}
|
||||
|
||||
impl Row {
|
||||
pub fn convert_row_to_bottom_row(
|
||||
&self, iter_id: &mut u64, total_height_ratio: &mut u32, default_widget_id: &mut u64,
|
||||
default_widget_type: &Option<BottomWidgetType>, default_widget_count: &mut u64,
|
||||
left_legend: bool,
|
||||
) -> Result<BottomRow> {
|
||||
// In the future we want to also add percentages.
|
||||
// But for MVP, we aren't going to bother.
|
||||
let row_ratio = self.ratio.unwrap_or(1);
|
||||
let mut children = Vec::new();
|
||||
|
||||
*total_height_ratio += row_ratio;
|
||||
|
||||
let mut total_col_ratio = 0;
|
||||
if let Some(row_children) = &self.child {
|
||||
for row_child in row_children {
|
||||
match row_child {
|
||||
RowChildren::Widget(widget) => {
|
||||
*iter_id += 1;
|
||||
let width_ratio = widget.ratio.unwrap_or(1);
|
||||
total_col_ratio += width_ratio;
|
||||
let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
|
||||
|
||||
if let Some(default_widget_type_val) = default_widget_type {
|
||||
if *default_widget_type_val == widget_type && *default_widget_count > 0
|
||||
{
|
||||
*default_widget_count -= 1;
|
||||
if *default_widget_count == 0 {
|
||||
*default_widget_id = *iter_id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check default flag
|
||||
if let Some(default_widget_flag) = widget.default {
|
||||
if default_widget_flag {
|
||||
*default_widget_id = *iter_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
children.push(match widget_type {
|
||||
BottomWidgetType::Cpu => {
|
||||
let iter_old_id = *iter_id;
|
||||
*iter_id += 1;
|
||||
BottomCol::builder()
|
||||
.col_width_ratio(width_ratio)
|
||||
.children(if left_legend {
|
||||
vec![BottomColRow::builder()
|
||||
.total_widget_ratio(20)
|
||||
.children(vec![
|
||||
BottomWidget::builder()
|
||||
.width_ratio(3)
|
||||
.widget_type(BottomWidgetType::CpuLegend)
|
||||
.widget_id(*iter_id)
|
||||
.canvas_handle_width(true)
|
||||
.build(),
|
||||
BottomWidget::builder()
|
||||
.width_ratio(17)
|
||||
.widget_type(BottomWidgetType::Cpu)
|
||||
.widget_id(iter_old_id)
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
])
|
||||
.build()]
|
||||
} else {
|
||||
vec![BottomColRow::builder()
|
||||
.total_widget_ratio(20)
|
||||
.children(vec![
|
||||
BottomWidget::builder()
|
||||
.width_ratio(17)
|
||||
.widget_type(BottomWidgetType::Cpu)
|
||||
.widget_id(iter_old_id)
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
BottomWidget::builder()
|
||||
.width_ratio(3)
|
||||
.widget_type(BottomWidgetType::CpuLegend)
|
||||
.widget_id(*iter_id)
|
||||
.canvas_handle_width(true)
|
||||
.build(),
|
||||
])
|
||||
.build()]
|
||||
})
|
||||
.build()
|
||||
}
|
||||
BottomWidgetType::Proc => {
|
||||
let iter_old_id = *iter_id;
|
||||
*iter_id += 1;
|
||||
BottomCol::builder()
|
||||
.total_col_row_ratio(2)
|
||||
.col_width_ratio(width_ratio)
|
||||
.children(vec![
|
||||
BottomColRow::builder()
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::Proc)
|
||||
.widget_id(iter_old_id)
|
||||
.build()])
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
BottomColRow::builder()
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::ProcSearch)
|
||||
.widget_id(*iter_id)
|
||||
.build()])
|
||||
.canvas_handle_height(true)
|
||||
.build(),
|
||||
])
|
||||
.build()
|
||||
}
|
||||
_ => BottomCol::builder()
|
||||
.col_width_ratio(width_ratio)
|
||||
.children(vec![BottomColRow::builder()
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(widget_type)
|
||||
.widget_id(*iter_id)
|
||||
.build()])
|
||||
.build()])
|
||||
.build(),
|
||||
});
|
||||
}
|
||||
RowChildren::Col { ratio, child } => {
|
||||
let col_width_ratio = ratio.unwrap_or(1);
|
||||
total_col_ratio += col_width_ratio;
|
||||
let mut total_col_row_ratio = 0;
|
||||
let mut contains_proc = false;
|
||||
|
||||
let mut col_row_children = Vec::new();
|
||||
|
||||
for widget in child {
|
||||
let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
|
||||
*iter_id += 1;
|
||||
let col_row_height_ratio = widget.ratio.unwrap_or(1);
|
||||
total_col_row_ratio += col_row_height_ratio;
|
||||
|
||||
if let Some(default_widget_type_val) = default_widget_type {
|
||||
if *default_widget_type_val == widget_type
|
||||
&& *default_widget_count > 0
|
||||
{
|
||||
*default_widget_count -= 1;
|
||||
if *default_widget_count == 0 {
|
||||
*default_widget_id = *iter_id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check default flag
|
||||
if let Some(default_widget_flag) = widget.default {
|
||||
if default_widget_flag {
|
||||
*default_widget_id = *iter_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match widget_type {
|
||||
BottomWidgetType::Cpu => {
|
||||
let iter_old_id = *iter_id;
|
||||
*iter_id += 1;
|
||||
if left_legend {
|
||||
col_row_children.push(
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(col_row_height_ratio)
|
||||
.total_widget_ratio(20)
|
||||
.children(vec![
|
||||
BottomWidget::builder()
|
||||
.width_ratio(3)
|
||||
.widget_type(BottomWidgetType::CpuLegend)
|
||||
.widget_id(*iter_id)
|
||||
.canvas_handle_width(true)
|
||||
.build(),
|
||||
BottomWidget::builder()
|
||||
.width_ratio(17)
|
||||
.widget_type(BottomWidgetType::Cpu)
|
||||
.widget_id(iter_old_id)
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
);
|
||||
} else {
|
||||
col_row_children.push(
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(col_row_height_ratio)
|
||||
.total_widget_ratio(20)
|
||||
.children(vec![
|
||||
BottomWidget::builder()
|
||||
.width_ratio(17)
|
||||
.widget_type(BottomWidgetType::Cpu)
|
||||
.widget_id(iter_old_id)
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
BottomWidget::builder()
|
||||
.width_ratio(3)
|
||||
.widget_type(BottomWidgetType::CpuLegend)
|
||||
.widget_id(*iter_id)
|
||||
.canvas_handle_width(true)
|
||||
.build(),
|
||||
])
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
}
|
||||
BottomWidgetType::Proc => {
|
||||
contains_proc = true;
|
||||
let iter_old_id = *iter_id;
|
||||
*iter_id += 1;
|
||||
col_row_children.push(
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(col_row_height_ratio)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::Proc)
|
||||
.widget_id(iter_old_id)
|
||||
.build()])
|
||||
.flex_grow(true)
|
||||
.build(),
|
||||
);
|
||||
col_row_children.push(
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(col_row_height_ratio)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(BottomWidgetType::ProcSearch)
|
||||
.widget_id(*iter_id)
|
||||
.build()])
|
||||
.canvas_handle_height(true)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
_ => col_row_children.push(
|
||||
BottomColRow::builder()
|
||||
.col_row_height_ratio(col_row_height_ratio)
|
||||
.children(vec![BottomWidget::builder()
|
||||
.widget_type(widget_type)
|
||||
.widget_id(*iter_id)
|
||||
.build()])
|
||||
.build(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if contains_proc {
|
||||
// Must adjust ratios to work with proc
|
||||
total_col_row_ratio *= 2;
|
||||
for child in &mut col_row_children {
|
||||
// Multiply all non-proc or proc-search ratios by 2
|
||||
if !child.children.is_empty() {
|
||||
match child.children[0].widget_type {
|
||||
BottomWidgetType::Proc | BottomWidgetType::ProcSearch => {}
|
||||
_ => child.col_row_height_ratio *= 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
children.push(
|
||||
BottomCol::builder()
|
||||
.total_col_row_ratio(total_col_row_ratio)
|
||||
.col_width_ratio(col_width_ratio)
|
||||
.children(col_row_children)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(BottomRow::builder()
|
||||
.total_col_ratio(total_col_ratio)
|
||||
.row_height_ratio(row_ratio)
|
||||
.children(children)
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a child of a Row - either a Col (column) or a FinalWidget.
|
||||
///
|
||||
/// A Col can also have an optional length and children. We only allow columns
|
||||
/// to have FinalWidgets as children, lest we get some amount of mutual
|
||||
/// recursion between Row and Col.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(untagged)]
|
||||
pub enum RowChildren {
|
||||
Widget(FinalWidget),
|
||||
Col {
|
||||
ratio: Option<u32>,
|
||||
child: Vec<FinalWidget>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Represents a widget.
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct FinalWidget {
|
||||
pub ratio: Option<u32>,
|
||||
#[serde(rename = "type")]
|
||||
pub widget_type: String,
|
||||
pub default: Option<bool>,
|
||||
}
|
@ -141,14 +141,28 @@ fn test_conflicting_temps() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conflicting_default_widget() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_invalid_default_widget_1() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Command::new(get_os_binary_loc())
|
||||
.arg("--cpu_default")
|
||||
.arg("--disk_default")
|
||||
.arg("--default_widget_type")
|
||||
.arg("fake_widget")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("Invalid widget type"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_default_widget_2() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Command::new(get_os_binary_loc())
|
||||
.arg("--default_widget_type")
|
||||
.arg("cpu")
|
||||
.arg("--default_widget_count")
|
||||
.arg("18446744073709551616")
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(predicate::str::contains(
|
||||
"cannot be used with one or more of the other specified arguments",
|
||||
"Please set your widget count to be at most unsigned INT_MAX",
|
||||
));
|
||||
|
||||
Ok(())
|
||||
|
Loading…
x
Reference in New Issue
Block a user