feature: Allow sorting by any column
This feature allows any column to be sortable. This also adds: - Inverting sort for current column with `I` - Invoking a sort widget with `s` or `F6`. Close with same key or esc. And: - A bugfix in regards the basic menu and battery widget - A lot of refactoring
This commit is contained in:
parent
84f63f2f83
commit
f3897f0538
|
@ -31,6 +31,7 @@
|
||||||
"shilangyu",
|
"shilangyu",
|
||||||
"softirq",
|
"softirq",
|
||||||
"stime",
|
"stime",
|
||||||
|
"subwidget",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"tokei",
|
"tokei",
|
||||||
"twrite",
|
"twrite",
|
||||||
|
|
|
@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- [179](https://github.com/ClementTsang/bottom/pull/179): Show full command/process path as an option.
|
- [179](https://github.com/ClementTsang/bottom/pull/179): Show full command/process path as an option.
|
||||||
|
|
||||||
|
- [183](https://github.com/ClementTsang/bottom/pull/183): Added sorting capabilities to any column.
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
- Added `WASD` as an alternative widget movement system.
|
- Added `WASD` as an alternative widget movement system.
|
||||||
|
@ -25,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
- [183](https://github.com/ClementTsang/bottom/pull/183): Fixed bug in basic mode where the battery widget was placed incorrectly.
|
||||||
|
|
||||||
## [0.4.5] - 2020-07-08
|
## [0.4.5] - 2020-07-08
|
||||||
|
|
||||||
- No changes here, just an uptick for Crates.io using the wrong Cargo.lock.
|
- No changes here, just an uptick for Crates.io using the wrong Cargo.lock.
|
||||||
|
|
|
@ -38,6 +38,7 @@ backtrace = "0.3"
|
||||||
serde = {version = "1.0", features = ["derive"] }
|
serde = {version = "1.0", features = ["derive"] }
|
||||||
unicode-segmentation = "1.6.0"
|
unicode-segmentation = "1.6.0"
|
||||||
unicode-width = "0.1.7"
|
unicode-width = "0.1.7"
|
||||||
|
# tui = {version = "0.10.0", features = ["crossterm"], default-features = false, git = "https://github.com/fdehau/tui-rs.git"}
|
||||||
tui = {version = "0.10.0", features = ["crossterm"], default-features = false }
|
tui = {version = "0.10.0", features = ["crossterm"], default-features = false }
|
||||||
|
|
||||||
# For debugging only...
|
# For debugging only...
|
||||||
|
|
13
README.md
13
README.md
|
@ -28,6 +28,7 @@ A cross-platform graphical process/system monitor with a customizable interface
|
||||||
- [CPU bindings](#cpu-bindings)
|
- [CPU bindings](#cpu-bindings)
|
||||||
- [Process bindings](#process-bindings)
|
- [Process bindings](#process-bindings)
|
||||||
- [Process search bindings](#process-search-bindings)
|
- [Process search bindings](#process-search-bindings)
|
||||||
|
- [Process sort bindings](#process-sort-bindings)
|
||||||
- [Battery bindings](#battery-bindings)
|
- [Battery bindings](#battery-bindings)
|
||||||
- [Process searching keywords](#process-searching-keywords)
|
- [Process searching keywords](#process-searching-keywords)
|
||||||
- [Supported keywords](#supported-keywords)
|
- [Supported keywords](#supported-keywords)
|
||||||
|
@ -222,6 +223,8 @@ Run using `btm`.
|
||||||
| `Tab` | Group/un-group processes with the same name |
|
| `Tab` | Group/un-group processes with the same name |
|
||||||
| `Ctrl-f`, `/` | Open process search widget |
|
| `Ctrl-f`, `/` | Open process search widget |
|
||||||
| `P` | Toggle between showing the full path or just the process name |
|
| `P` | Toggle between showing the full path or just the process name |
|
||||||
|
| `s, F6` | Open process sort widget |
|
||||||
|
| `I` | Invert current sort |
|
||||||
|
|
||||||
#### Process search bindings
|
#### Process search bindings
|
||||||
|
|
||||||
|
@ -240,6 +243,16 @@ Run using `btm`.
|
||||||
| `Left` | Move cursor left |
|
| `Left` | Move cursor left |
|
||||||
| `Right` | Move cursor right |
|
| `Right` | Move cursor right |
|
||||||
|
|
||||||
|
### Process sort bindings
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| -------------- | ------------------------------- |
|
||||||
|
| `Down`, `j` | Scroll down in list |
|
||||||
|
| `Up`, `k` | Scroll up in list |
|
||||||
|
| `Mouse scroll` | Scroll through sort widget |
|
||||||
|
| `Esc` | Close the sort widget |
|
||||||
|
| `Enter` | Sort by current selected column |
|
||||||
|
|
||||||
#### Battery bindings
|
#### Battery bindings
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|
|
943
src/app.rs
943
src/app.rs
File diff suppressed because it is too large
Load Diff
|
@ -49,7 +49,7 @@ pub struct DataCollection {
|
||||||
pub network_harvest: network::NetworkHarvest,
|
pub network_harvest: network::NetworkHarvest,
|
||||||
pub memory_harvest: mem::MemHarvest,
|
pub memory_harvest: mem::MemHarvest,
|
||||||
pub swap_harvest: mem::MemHarvest,
|
pub swap_harvest: mem::MemHarvest,
|
||||||
pub cpu_harvest: cpu::CPUHarvest,
|
pub cpu_harvest: cpu::CpuHarvest,
|
||||||
pub process_harvest: Vec<processes::ProcessHarvest>,
|
pub process_harvest: Vec<processes::ProcessHarvest>,
|
||||||
pub disk_harvest: Vec<disks::DiskHarvest>,
|
pub disk_harvest: Vec<disks::DiskHarvest>,
|
||||||
pub io_harvest: disks::IOHarvest,
|
pub io_harvest: disks::IOHarvest,
|
||||||
|
@ -67,7 +67,7 @@ impl Default for DataCollection {
|
||||||
network_harvest: network::NetworkHarvest::default(),
|
network_harvest: network::NetworkHarvest::default(),
|
||||||
memory_harvest: mem::MemHarvest::default(),
|
memory_harvest: mem::MemHarvest::default(),
|
||||||
swap_harvest: mem::MemHarvest::default(),
|
swap_harvest: mem::MemHarvest::default(),
|
||||||
cpu_harvest: cpu::CPUHarvest::default(),
|
cpu_harvest: cpu::CpuHarvest::default(),
|
||||||
process_harvest: Vec::default(),
|
process_harvest: Vec::default(),
|
||||||
disk_harvest: Vec::default(),
|
disk_harvest: Vec::default(),
|
||||||
io_harvest: disks::IOHarvest::default(),
|
io_harvest: disks::IOHarvest::default(),
|
||||||
|
@ -84,7 +84,7 @@ impl DataCollection {
|
||||||
self.network_harvest = network::NetworkHarvest::default();
|
self.network_harvest = network::NetworkHarvest::default();
|
||||||
self.memory_harvest = mem::MemHarvest::default();
|
self.memory_harvest = mem::MemHarvest::default();
|
||||||
self.swap_harvest = mem::MemHarvest::default();
|
self.swap_harvest = mem::MemHarvest::default();
|
||||||
self.cpu_harvest = cpu::CPUHarvest::default();
|
self.cpu_harvest = cpu::CpuHarvest::default();
|
||||||
self.process_harvest = Vec::default();
|
self.process_harvest = Vec::default();
|
||||||
self.disk_harvest = Vec::default();
|
self.disk_harvest = Vec::default();
|
||||||
self.io_harvest = disks::IOHarvest::default();
|
self.io_harvest = disks::IOHarvest::default();
|
||||||
|
@ -205,7 +205,7 @@ impl DataCollection {
|
||||||
self.network_harvest = network.clone();
|
self.network_harvest = network.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_cpu(&mut self, cpu: &[cpu::CPUData], new_entry: &mut TimedData) {
|
fn eat_cpu(&mut self, cpu: &[cpu::CpuData], new_entry: &mut TimedData) {
|
||||||
// Note this only pre-calculates the data points - the names will be
|
// Note this only pre-calculates the data points - the names will be
|
||||||
// within the local copy of cpu_harvest. Since it's all sequential
|
// within the local copy of cpu_harvest. Since it's all sequential
|
||||||
// it probably doesn't matter anyways.
|
// it probably doesn't matter anyways.
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub mod temperature;
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
pub last_collection_time: Instant,
|
pub last_collection_time: Instant,
|
||||||
pub cpu: Option<cpu::CPUHarvest>,
|
pub cpu: Option<cpu::CpuHarvest>,
|
||||||
pub memory: Option<mem::MemHarvest>,
|
pub memory: Option<mem::MemHarvest>,
|
||||||
pub swap: Option<mem::MemHarvest>,
|
pub swap: Option<mem::MemHarvest>,
|
||||||
pub temperature_sensors: Option<Vec<temperature::TempHarvest>>,
|
pub temperature_sensors: Option<Vec<temperature::TempHarvest>>,
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
use sysinfo::{ProcessorExt, System, SystemExt};
|
use sysinfo::{ProcessorExt, System, SystemExt};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct CPUData {
|
pub struct CpuData {
|
||||||
pub cpu_name: String,
|
pub cpu_name: String,
|
||||||
pub cpu_usage: f64,
|
pub cpu_usage: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CPUHarvest = Vec<CPUData>;
|
pub type CpuHarvest = Vec<CpuData>;
|
||||||
|
|
||||||
pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> CPUHarvest {
|
pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> CpuHarvest {
|
||||||
let cpu_data = sys.get_processors();
|
let cpu_data = sys.get_processors();
|
||||||
let avg_cpu_usage = sys.get_global_processor_info().get_cpu_usage();
|
let avg_cpu_usage = sys.get_global_processor_info().get_cpu_usage();
|
||||||
let mut cpu_vec = vec![];
|
let mut cpu_vec = vec![];
|
||||||
|
|
||||||
if show_average_cpu {
|
if show_average_cpu {
|
||||||
cpu_vec.push(CPUData {
|
cpu_vec.push(CpuData {
|
||||||
cpu_name: "AVG".to_string(),
|
cpu_name: "AVG".to_string(),
|
||||||
cpu_usage: avg_cpu_usage as f64,
|
cpu_usage: avg_cpu_usage as f64,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (itx, cpu) in cpu_data.iter().enumerate() {
|
for (itx, cpu) in cpu_data.iter().enumerate() {
|
||||||
cpu_vec.push(CPUData {
|
cpu_vec.push(CpuData {
|
||||||
cpu_name: format!("CPU{}", itx),
|
cpu_name: format!("CPU{}", itx),
|
||||||
cpu_usage: f64::from(cpu.get_cpu_usage()),
|
cpu_usage: f64::from(cpu.get_cpu_usage()),
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,17 +12,48 @@ use std::{
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
|
use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
|
||||||
|
|
||||||
#[derive(Clone)]
|
// TODO: Add value so we know if it's sorted ascending or descending by default?
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum ProcessSorting {
|
pub enum ProcessSorting {
|
||||||
CPU,
|
CpuPercent,
|
||||||
MEM,
|
Mem,
|
||||||
PID,
|
MemPercent,
|
||||||
IDENTIFIER,
|
Pid,
|
||||||
|
ProcessName,
|
||||||
|
Command,
|
||||||
|
ReadPerSecond,
|
||||||
|
WritePerSecond,
|
||||||
|
TotalRead,
|
||||||
|
TotalWrite,
|
||||||
|
State,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ProcessSorting {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
use ProcessSorting::*;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match &self {
|
||||||
|
CpuPercent => "CPU%",
|
||||||
|
MemPercent => "Mem%",
|
||||||
|
Mem => "Mem",
|
||||||
|
ReadPerSecond => "R/s",
|
||||||
|
WritePerSecond => "W/s",
|
||||||
|
TotalRead => "Read",
|
||||||
|
TotalWrite => "Write",
|
||||||
|
State => "State",
|
||||||
|
ProcessName => "Name",
|
||||||
|
Command => "Command",
|
||||||
|
Pid => "PID",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProcessSorting {
|
impl Default for ProcessSorting {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ProcessSorting::CPU
|
ProcessSorting::CpuPercent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -541,7 +541,7 @@ impl BottomLayout {
|
||||||
.widget_id(4)
|
.widget_id(4)
|
||||||
.up_neighbour(Some(100))
|
.up_neighbour(Some(100))
|
||||||
.left_neighbour(Some(8))
|
.left_neighbour(Some(8))
|
||||||
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
.right_neighbour(Some(DEFAULT_WIDGET_ID + 2))
|
||||||
.build()])
|
.build()])
|
||||||
.build()])
|
.build()])
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -550,15 +550,29 @@ impl BottomLayout {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
BottomColRow::builder()
|
BottomColRow::builder()
|
||||||
.canvas_handle_height(true)
|
.canvas_handle_height(true)
|
||||||
.children(vec![BottomWidget::builder()
|
.total_widget_ratio(3)
|
||||||
.canvas_handle_width(true)
|
.children(vec![
|
||||||
.widget_type(BottomWidgetType::Proc)
|
BottomWidget::builder()
|
||||||
.widget_id(DEFAULT_WIDGET_ID)
|
.canvas_handle_width(true)
|
||||||
.up_neighbour(Some(100))
|
.widget_type(BottomWidgetType::ProcSort)
|
||||||
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
.widget_id(DEFAULT_WIDGET_ID + 2)
|
||||||
.left_neighbour(Some(4))
|
.up_neighbour(Some(100))
|
||||||
.right_neighbour(Some(8))
|
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
||||||
.build()])
|
.left_neighbour(Some(4))
|
||||||
|
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.width_ratio(1)
|
||||||
|
.build(),
|
||||||
|
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(DEFAULT_WIDGET_ID + 2))
|
||||||
|
.right_neighbour(Some(7))
|
||||||
|
.width_ratio(2)
|
||||||
|
.build(),
|
||||||
|
])
|
||||||
.build(),
|
.build(),
|
||||||
BottomColRow::builder()
|
BottomColRow::builder()
|
||||||
.canvas_handle_height(true)
|
.canvas_handle_height(true)
|
||||||
|
@ -614,7 +628,7 @@ impl BottomLayout {
|
||||||
.widget_id(4)
|
.widget_id(4)
|
||||||
.up_neighbour(Some(100))
|
.up_neighbour(Some(100))
|
||||||
.left_neighbour(Some(7))
|
.left_neighbour(Some(7))
|
||||||
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
.right_neighbour(Some(DEFAULT_WIDGET_ID + 2))
|
||||||
.build()])
|
.build()])
|
||||||
.build()])
|
.build()])
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -623,15 +637,26 @@ impl BottomLayout {
|
||||||
.children(vec![
|
.children(vec![
|
||||||
BottomColRow::builder()
|
BottomColRow::builder()
|
||||||
.canvas_handle_height(true)
|
.canvas_handle_height(true)
|
||||||
.children(vec![BottomWidget::builder()
|
.children(vec![
|
||||||
.canvas_handle_width(true)
|
BottomWidget::builder()
|
||||||
.widget_type(BottomWidgetType::Proc)
|
.canvas_handle_width(true)
|
||||||
.widget_id(DEFAULT_WIDGET_ID)
|
.widget_type(BottomWidgetType::ProcSort)
|
||||||
.up_neighbour(Some(100))
|
.widget_id(DEFAULT_WIDGET_ID + 2)
|
||||||
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
.up_neighbour(Some(100))
|
||||||
.left_neighbour(Some(4))
|
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
||||||
.right_neighbour(Some(7))
|
.left_neighbour(Some(4))
|
||||||
.build()])
|
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.build(),
|
||||||
|
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(DEFAULT_WIDGET_ID + 2))
|
||||||
|
.right_neighbour(Some(7))
|
||||||
|
.build(),
|
||||||
|
])
|
||||||
.build(),
|
.build(),
|
||||||
BottomColRow::builder()
|
BottomColRow::builder()
|
||||||
.canvas_handle_height(true)
|
.canvas_handle_height(true)
|
||||||
|
@ -730,180 +755,6 @@ impl BottomLayout {
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_default(left_legend: bool, use_battery: bool) -> Self {
|
|
||||||
let cpu_layout = 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))
|
|
||||||
.right_neighbour(if use_battery { Some(99) } else { None })
|
|
||||||
.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))
|
|
||||||
.right_neighbour(if use_battery { Some(99) } else { None })
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.build(),
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_row_layout = if use_battery {
|
|
||||||
vec![
|
|
||||||
BottomCol::builder()
|
|
||||||
.col_width_ratio(2)
|
|
||||||
.children(vec![BottomColRow::builder()
|
|
||||||
.total_widget_ratio(20)
|
|
||||||
.children(cpu_layout)
|
|
||||||
.build()])
|
|
||||||
.build(),
|
|
||||||
BottomCol::builder()
|
|
||||||
.col_width_ratio(1)
|
|
||||||
.children(vec![BottomColRow::builder()
|
|
||||||
.children(vec![BottomWidget::builder()
|
|
||||||
.widget_type(BottomWidgetType::Battery)
|
|
||||||
.widget_id(99)
|
|
||||||
.down_neighbour(Some(12))
|
|
||||||
.left_neighbour(Some(if left_legend { 1 } else { 2 }))
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.build()])
|
|
||||||
.build()])
|
|
||||||
.build(),
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
vec![BottomCol::builder()
|
|
||||||
.children(vec![BottomColRow::builder()
|
|
||||||
.total_widget_ratio(20)
|
|
||||||
.children(cpu_layout)
|
|
||||||
.build()])
|
|
||||||
.build()]
|
|
||||||
};
|
|
||||||
|
|
||||||
BottomLayout {
|
|
||||||
total_row_height_ratio: 100,
|
|
||||||
rows: vec![
|
|
||||||
BottomRow::builder()
|
|
||||||
.row_height_ratio(30)
|
|
||||||
.total_col_ratio(if use_battery { 3 } else { 1 })
|
|
||||||
.children(first_row_layout)
|
|
||||||
.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.
|
/// Represents a single row in the layout.
|
||||||
|
@ -961,6 +812,25 @@ pub struct BottomColRow {
|
||||||
pub flex_grow: bool,
|
pub flex_grow: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub enum WidgetDirection {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetDirection {
|
||||||
|
pub fn is_opposite(&self, other_direction: &WidgetDirection) -> bool {
|
||||||
|
match &self {
|
||||||
|
WidgetDirection::Left => *other_direction == WidgetDirection::Right,
|
||||||
|
WidgetDirection::Right => *other_direction == WidgetDirection::Left,
|
||||||
|
WidgetDirection::Up => *other_direction == WidgetDirection::Down,
|
||||||
|
WidgetDirection::Down => *other_direction == WidgetDirection::Up,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a single widget.
|
/// Represents a single widget.
|
||||||
#[derive(Debug, Default, Clone, TypedBuilder)]
|
#[derive(Debug, Default, Clone, TypedBuilder)]
|
||||||
pub struct BottomWidget {
|
pub struct BottomWidget {
|
||||||
|
@ -982,11 +852,17 @@ pub struct BottomWidget {
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
pub down_neighbour: Option<u64>,
|
pub down_neighbour: Option<u64>,
|
||||||
|
|
||||||
|
/// If set to true, the canvas will override any ratios.
|
||||||
#[builder(default = false)]
|
#[builder(default = false)]
|
||||||
pub canvas_handle_width: bool,
|
pub canvas_handle_width: bool,
|
||||||
|
|
||||||
|
/// Whether we want this widget to take up all available room (and ignore any ratios).
|
||||||
#[builder(default = false)]
|
#[builder(default = false)]
|
||||||
pub flex_grow: bool,
|
pub flex_grow: bool,
|
||||||
|
|
||||||
|
/// The value is the direction to bounce, as well as the parent offset.
|
||||||
|
#[builder(default = None)]
|
||||||
|
pub parent_reflector: Option<(WidgetDirection, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
@ -998,6 +874,7 @@ pub enum BottomWidgetType {
|
||||||
Net,
|
Net,
|
||||||
Proc,
|
Proc,
|
||||||
ProcSearch,
|
ProcSearch,
|
||||||
|
ProcSort,
|
||||||
Temp,
|
Temp,
|
||||||
Disk,
|
Disk,
|
||||||
BasicCpu,
|
BasicCpu,
|
||||||
|
@ -1011,7 +888,7 @@ impl BottomWidgetType {
|
||||||
pub fn is_widget_table(&self) -> bool {
|
pub fn is_widget_table(&self) -> bool {
|
||||||
use BottomWidgetType::*;
|
use BottomWidgetType::*;
|
||||||
match self {
|
match self {
|
||||||
Disk | Proc | Temp | CpuLegend => true,
|
Disk | Proc | ProcSort | Temp | CpuLegend => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,27 +7,27 @@ use tui::widgets::TableState;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{layout_manager::BottomWidgetType, query::*},
|
app::{layout_manager::BottomWidgetType, query::*},
|
||||||
constants,
|
constants,
|
||||||
data_harvester::processes,
|
data_harvester::processes::{self, ProcessSorting},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ScrollDirection {
|
pub enum ScrollDirection {
|
||||||
// UP means scrolling up --- this usually DECREMENTS
|
// UP means scrolling up --- this usually DECREMENTS
|
||||||
UP,
|
Up,
|
||||||
// DOWN means scrolling down --- this usually INCREMENTS
|
// DOWN means scrolling down --- this usually INCREMENTS
|
||||||
DOWN,
|
Down,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScrollDirection {
|
impl Default for ScrollDirection {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ScrollDirection::DOWN
|
ScrollDirection::Down
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CursorDirection {
|
pub enum CursorDirection {
|
||||||
LEFT,
|
Left,
|
||||||
RIGHT,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AppScrollWidgetState deals with fields for a scrollable app's current state.
|
/// AppScrollWidgetState deals with fields for a scrollable app's current state.
|
||||||
|
@ -85,7 +85,7 @@ impl Default for AppSearchState {
|
||||||
is_invalid_search: false,
|
is_invalid_search: false,
|
||||||
is_blank_search: true,
|
is_blank_search: true,
|
||||||
grapheme_cursor: GraphemeCursor::new(0, 0, true),
|
grapheme_cursor: GraphemeCursor::new(0, 0, true),
|
||||||
cursor_direction: CursorDirection::RIGHT,
|
cursor_direction: CursorDirection::Right,
|
||||||
cursor_bar: 0,
|
cursor_bar: 0,
|
||||||
char_cursor_position: 0,
|
char_cursor_position: 0,
|
||||||
query: None,
|
query: None,
|
||||||
|
@ -141,15 +141,183 @@ impl ProcessSearchState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ColumnInfo {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub shortcut: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProcColumn {
|
||||||
|
pub ordered_columns: Vec<ProcessSorting>,
|
||||||
|
pub column_mapping: HashMap<ProcessSorting, ColumnInfo>,
|
||||||
|
pub longest_header_len: u16,
|
||||||
|
pub column_state: TableState,
|
||||||
|
pub scroll_direction: ScrollDirection,
|
||||||
|
pub current_scroll_position: usize,
|
||||||
|
pub previous_scroll_position: usize,
|
||||||
|
pub backup_prev_scroll_position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProcColumn {
|
||||||
|
fn default() -> Self {
|
||||||
|
use ProcessSorting::*;
|
||||||
|
let ordered_columns = vec![
|
||||||
|
Pid,
|
||||||
|
ProcessName,
|
||||||
|
Command,
|
||||||
|
CpuPercent,
|
||||||
|
MemPercent,
|
||||||
|
ReadPerSecond,
|
||||||
|
WritePerSecond,
|
||||||
|
TotalRead,
|
||||||
|
TotalWrite,
|
||||||
|
State,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut column_mapping = HashMap::new();
|
||||||
|
let mut longest_header_len = 0;
|
||||||
|
for column in ordered_columns.clone() {
|
||||||
|
longest_header_len = std::cmp::max(longest_header_len, column.to_string().len());
|
||||||
|
match column {
|
||||||
|
CpuPercent => {
|
||||||
|
column_mapping.insert(
|
||||||
|
column,
|
||||||
|
ColumnInfo {
|
||||||
|
enabled: true,
|
||||||
|
shortcut: Some("c"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
MemPercent | Mem => {
|
||||||
|
column_mapping.insert(
|
||||||
|
column,
|
||||||
|
ColumnInfo {
|
||||||
|
enabled: true,
|
||||||
|
shortcut: Some("m"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ProcessName => {
|
||||||
|
column_mapping.insert(
|
||||||
|
column,
|
||||||
|
ColumnInfo {
|
||||||
|
enabled: true,
|
||||||
|
shortcut: Some("n"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Command => {
|
||||||
|
column_mapping.insert(
|
||||||
|
column,
|
||||||
|
ColumnInfo {
|
||||||
|
enabled: false,
|
||||||
|
shortcut: Some("n"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Pid => {
|
||||||
|
column_mapping.insert(
|
||||||
|
column,
|
||||||
|
ColumnInfo {
|
||||||
|
enabled: true,
|
||||||
|
shortcut: Some("p"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
column_mapping.insert(
|
||||||
|
column,
|
||||||
|
ColumnInfo {
|
||||||
|
enabled: true,
|
||||||
|
shortcut: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let longest_header_len = longest_header_len as u16;
|
||||||
|
|
||||||
|
ProcColumn {
|
||||||
|
ordered_columns,
|
||||||
|
column_mapping,
|
||||||
|
longest_header_len,
|
||||||
|
column_state: TableState::default(),
|
||||||
|
scroll_direction: ScrollDirection::default(),
|
||||||
|
current_scroll_position: 0,
|
||||||
|
previous_scroll_position: 0,
|
||||||
|
backup_prev_scroll_position: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcColumn {
|
||||||
|
pub fn get_enabled_columns_len(&self) -> usize {
|
||||||
|
self.ordered_columns
|
||||||
|
.iter()
|
||||||
|
.filter_map(|column_type| {
|
||||||
|
if self.column_mapping.get(&column_type).unwrap().enabled {
|
||||||
|
Some(1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_to_sorted_index(&mut self, proc_sorting_type: &ProcessSorting) {
|
||||||
|
// TODO [Custom Columns]: If we add custom columns, this may be needed! Since column indices will change, this runs the risk of OOB. So, when you change columns, CALL THIS AND ADAPT!
|
||||||
|
let mut true_index = 0;
|
||||||
|
for column in &self.ordered_columns {
|
||||||
|
if *column == *proc_sorting_type {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if self.column_mapping.get(column).unwrap().enabled {
|
||||||
|
true_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_scroll_position = true_index;
|
||||||
|
self.backup_prev_scroll_position = self.previous_scroll_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_column_headers(
|
||||||
|
&self, proc_sorting_type: &ProcessSorting, sort_reverse: bool,
|
||||||
|
) -> Vec<String> {
|
||||||
|
// TODO: Gonna have to figure out how to do left/right GUI notation if we add it.
|
||||||
|
self.ordered_columns
|
||||||
|
.iter()
|
||||||
|
.filter_map(|column_type| {
|
||||||
|
let mapping = self.column_mapping.get(&column_type).unwrap();
|
||||||
|
let mut command_str = String::default();
|
||||||
|
if let Some(command) = mapping.shortcut {
|
||||||
|
command_str = format!("({})", command);
|
||||||
|
}
|
||||||
|
|
||||||
|
if mapping.enabled {
|
||||||
|
Some(if proc_sorting_type == column_type {
|
||||||
|
column_type.to_string()
|
||||||
|
+ command_str.as_str()
|
||||||
|
+ if sort_reverse { "▼" } else { "▲" }
|
||||||
|
} else {
|
||||||
|
column_type.to_string() + command_str.as_str()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ProcWidgetState {
|
pub struct ProcWidgetState {
|
||||||
pub process_search_state: ProcessSearchState,
|
pub process_search_state: ProcessSearchState,
|
||||||
pub is_grouped: bool,
|
pub is_grouped: bool,
|
||||||
pub scroll_state: AppScrollWidgetState,
|
pub scroll_state: AppScrollWidgetState,
|
||||||
pub process_sorting_type: processes::ProcessSorting,
|
pub process_sorting_type: processes::ProcessSorting,
|
||||||
pub process_sorting_reverse: bool,
|
pub process_sorting_reverse: bool,
|
||||||
pub is_using_full_path: bool,
|
pub is_using_command: bool,
|
||||||
pub current_column_index: usize,
|
pub current_column_index: usize,
|
||||||
pub num_columns: usize,
|
pub is_sort_open: bool,
|
||||||
|
pub columns: ProcColumn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcWidgetState {
|
impl ProcWidgetState {
|
||||||
|
@ -168,18 +336,79 @@ impl ProcWidgetState {
|
||||||
process_search_state.search_toggle_regex();
|
process_search_state.search_toggle_regex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let process_sorting_type = processes::ProcessSorting::CpuPercent;
|
||||||
|
|
||||||
|
// TODO: If we add customizable columns, this should pull from config
|
||||||
|
let mut columns = ProcColumn::default();
|
||||||
|
columns.set_to_sorted_index(&process_sorting_type);
|
||||||
|
|
||||||
ProcWidgetState {
|
ProcWidgetState {
|
||||||
process_search_state,
|
process_search_state,
|
||||||
is_grouped,
|
is_grouped,
|
||||||
scroll_state: AppScrollWidgetState::default(),
|
scroll_state: AppScrollWidgetState::default(),
|
||||||
process_sorting_type: processes::ProcessSorting::CPU,
|
process_sorting_type,
|
||||||
process_sorting_reverse: true,
|
process_sorting_reverse: true,
|
||||||
is_using_full_path: false,
|
is_using_command: false,
|
||||||
current_column_index: 0,
|
current_column_index: 0,
|
||||||
num_columns: 1,
|
is_sort_open: false,
|
||||||
|
columns,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates sorting when using the column list.
|
||||||
|
/// ...this really should be part of the ProcColumn struct (along with the sorting fields),
|
||||||
|
/// but I'm too lazy.
|
||||||
|
///
|
||||||
|
/// Sorry, future me, you're gonna have to refactor this later. Too busy getting
|
||||||
|
/// the feature to work in the first place! :)
|
||||||
|
pub fn update_sorting_with_columns(&mut self) {
|
||||||
|
let mut true_index = 0;
|
||||||
|
let mut enabled_index = 0;
|
||||||
|
let target_itx = self.columns.current_scroll_position;
|
||||||
|
for column in &self.columns.ordered_columns {
|
||||||
|
let enabled = self.columns.column_mapping.get(column).unwrap().enabled;
|
||||||
|
if enabled_index == target_itx && enabled {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if enabled {
|
||||||
|
enabled_index += 1;
|
||||||
|
}
|
||||||
|
true_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(new_sort_type) = self.columns.ordered_columns.get(true_index) {
|
||||||
|
if *new_sort_type == self.process_sorting_type {
|
||||||
|
// Just reverse the search if we're reselecting!
|
||||||
|
self.process_sorting_reverse = !(self.process_sorting_reverse);
|
||||||
|
} else {
|
||||||
|
self.process_sorting_type = new_sort_type.clone();
|
||||||
|
match self.process_sorting_type {
|
||||||
|
ProcessSorting::State
|
||||||
|
| ProcessSorting::Pid
|
||||||
|
| ProcessSorting::ProcessName
|
||||||
|
| ProcessSorting::Command => {
|
||||||
|
// Also invert anything that uses alphabetical sorting by default.
|
||||||
|
self.process_sorting_reverse = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_command_and_name(&mut self, is_using_command: bool) {
|
||||||
|
self.columns
|
||||||
|
.column_mapping
|
||||||
|
.get_mut(&ProcessSorting::ProcessName)
|
||||||
|
.unwrap()
|
||||||
|
.enabled = !is_using_command;
|
||||||
|
self.columns
|
||||||
|
.column_mapping
|
||||||
|
.get_mut(&ProcessSorting::Command)
|
||||||
|
.unwrap()
|
||||||
|
.enabled = is_using_command;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cursor_position(&self) -> usize {
|
pub fn get_cursor_position(&self) -> usize {
|
||||||
self.process_search_state
|
self.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
|
|
|
@ -356,20 +356,16 @@ impl Painter {
|
||||||
app_state.current_widget.widget_id,
|
app_state.current_widget.widget_id,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
Proc => self.draw_process_and_search(
|
proc_type @ Proc | proc_type @ ProcSearch | proc_type @ ProcSort => {
|
||||||
&mut f,
|
let widget_id = app_state.current_widget.widget_id
|
||||||
app_state,
|
- match proc_type {
|
||||||
rect[0],
|
ProcSearch => 1,
|
||||||
true,
|
ProcSort => 2,
|
||||||
app_state.current_widget.widget_id,
|
_ => 0,
|
||||||
),
|
};
|
||||||
ProcSearch => self.draw_process_and_search(
|
|
||||||
&mut f,
|
self.draw_process_features(&mut f, app_state, rect[0], true, widget_id);
|
||||||
app_state,
|
}
|
||||||
rect[0],
|
|
||||||
true,
|
|
||||||
app_state.current_widget.widget_id - 1,
|
|
||||||
),
|
|
||||||
Battery => self.draw_battery_display(
|
Battery => self.draw_battery_display(
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
|
@ -430,13 +426,20 @@ impl Painter {
|
||||||
false,
|
false,
|
||||||
widget_id,
|
widget_id,
|
||||||
),
|
),
|
||||||
Proc => self.draw_process_and_search(
|
Proc | ProcSort => {
|
||||||
&mut f,
|
let wid = widget_id
|
||||||
app_state,
|
- match basic_table_widget_state.currently_displayed_widget_type {
|
||||||
vertical_chunks[4],
|
ProcSort => 2,
|
||||||
false,
|
_ => 0,
|
||||||
widget_id,
|
};
|
||||||
),
|
self.draw_process_features(
|
||||||
|
&mut f,
|
||||||
|
app_state,
|
||||||
|
vertical_chunks[4],
|
||||||
|
false,
|
||||||
|
wid,
|
||||||
|
);
|
||||||
|
}
|
||||||
Temp => self.draw_temp_table(
|
Temp => self.draw_temp_table(
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
|
@ -575,7 +578,7 @@ impl Painter {
|
||||||
Disk => {
|
Disk => {
|
||||||
self.draw_disk_table(f, app_state, *widget_draw_loc, true, widget.widget_id)
|
self.draw_disk_table(f, app_state, *widget_draw_loc, true, widget.widget_id)
|
||||||
}
|
}
|
||||||
Proc => self.draw_process_and_search(
|
Proc => self.draw_process_features(
|
||||||
f,
|
f,
|
||||||
app_state,
|
app_state,
|
||||||
*widget_draw_loc,
|
*widget_draw_loc,
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl HelpDialog for Painter {
|
||||||
|
|
||||||
app_state.help_dialog_state.scroll_state.max_scroll_index =
|
app_state.help_dialog_state.scroll_state.max_scroll_index =
|
||||||
(self.styled_help_text.len() as u16
|
(self.styled_help_text.len() as u16
|
||||||
+ (constants::HELP_TEXT.len() as u16 - 3)
|
+ (constants::HELP_TEXT.len() as u16 - 4)
|
||||||
+ overflow_buffer)
|
+ overflow_buffer)
|
||||||
.saturating_sub(draw_loc.height);
|
.saturating_sub(draw_loc.height);
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ pub fn get_search_start_position(
|
||||||
}
|
}
|
||||||
|
|
||||||
match cursor_direction {
|
match cursor_direction {
|
||||||
app::CursorDirection::RIGHT => {
|
app::CursorDirection::Right => {
|
||||||
if current_cursor_position < *cursor_bar + num_columns {
|
if current_cursor_position < *cursor_bar + num_columns {
|
||||||
// If, using previous_scrolled_position, we can see the element
|
// If, using previous_scrolled_position, we can see the element
|
||||||
// (so within that and + num_rows) just reuse the current previously scrolled position
|
// (so within that and + num_rows) just reuse the current previously scrolled position
|
||||||
|
@ -100,7 +100,7 @@ pub fn get_search_start_position(
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app::CursorDirection::LEFT => {
|
app::CursorDirection::Left => {
|
||||||
if current_cursor_position <= *cursor_bar {
|
if current_cursor_position <= *cursor_bar {
|
||||||
// If it's past the first element, then show from that element downwards
|
// If it's past the first element, then show from that element downwards
|
||||||
*cursor_bar = current_cursor_position;
|
*cursor_bar = current_cursor_position;
|
||||||
|
@ -125,7 +125,7 @@ pub fn get_start_position(
|
||||||
}
|
}
|
||||||
|
|
||||||
match scroll_direction {
|
match scroll_direction {
|
||||||
app::ScrollDirection::DOWN => {
|
app::ScrollDirection::Down => {
|
||||||
if currently_selected_position < *scroll_position_bar + num_rows {
|
if currently_selected_position < *scroll_position_bar + num_rows {
|
||||||
// If, using previous_scrolled_position, we can see the element
|
// If, using previous_scrolled_position, we can see the element
|
||||||
// (so within that and + num_rows) just reuse the current previously scrolled position
|
// (so within that and + num_rows) just reuse the current previously scrolled position
|
||||||
|
@ -140,7 +140,7 @@ pub fn get_start_position(
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app::ScrollDirection::UP => {
|
app::ScrollDirection::Up => {
|
||||||
if currently_selected_position <= *scroll_position_bar {
|
if currently_selected_position <= *scroll_position_bar {
|
||||||
// If it's past the first element, then show from that element downwards
|
// If it's past the first element, then show from that element downwards
|
||||||
*scroll_position_bar = currently_selected_position;
|
*scroll_position_bar = currently_selected_position;
|
||||||
|
|
|
@ -24,6 +24,15 @@ impl BasicTableArrows for Painter {
|
||||||
fn draw_basic_table_arrows<B: Backend>(
|
fn draw_basic_table_arrows<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect, current_table: &BottomWidget,
|
&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect, current_table: &BottomWidget,
|
||||||
) {
|
) {
|
||||||
|
let current_table = if let BottomWidgetType::ProcSort = current_table.widget_type {
|
||||||
|
current_table
|
||||||
|
.right_neighbour
|
||||||
|
.map(|id| app_state.widget_map.get(&id).unwrap())
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
current_table
|
||||||
|
};
|
||||||
|
|
||||||
// Effectively a paragraph with a ton of spacing
|
// Effectively a paragraph with a ton of spacing
|
||||||
let (left_table, right_table) = (
|
let (left_table, right_table) = (
|
||||||
{
|
{
|
||||||
|
@ -33,7 +42,21 @@ impl BasicTableArrows for Painter {
|
||||||
app_state
|
app_state
|
||||||
.widget_map
|
.widget_map
|
||||||
.get(&left_widget_id)
|
.get(&left_widget_id)
|
||||||
.map(|left_widget| &left_widget.widget_type)
|
.map(|left_widget| {
|
||||||
|
if left_widget.widget_type == BottomWidgetType::ProcSort {
|
||||||
|
left_widget
|
||||||
|
.left_neighbour
|
||||||
|
.map(|left_left_widget_id| {
|
||||||
|
app_state.widget_map.get(&left_left_widget_id).map(
|
||||||
|
|left_left_widget| &left_left_widget.widget_type,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| Some(&BottomWidgetType::Temp))
|
||||||
|
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||||
|
} else {
|
||||||
|
&left_widget.widget_type
|
||||||
|
}
|
||||||
|
})
|
||||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||||
|
@ -45,7 +68,23 @@ impl BasicTableArrows for Painter {
|
||||||
app_state
|
app_state
|
||||||
.widget_map
|
.widget_map
|
||||||
.get(&right_widget_id)
|
.get(&right_widget_id)
|
||||||
.map(|right_widget| &right_widget.widget_type)
|
.map(|right_widget| {
|
||||||
|
if right_widget.widget_type == BottomWidgetType::ProcSort {
|
||||||
|
right_widget
|
||||||
|
.right_neighbour
|
||||||
|
.map(|right_right_widget_id| {
|
||||||
|
app_state.widget_map.get(&right_right_widget_id).map(
|
||||||
|
|right_right_widget| {
|
||||||
|
&right_right_widget.widget_type
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| Some(&BottomWidgetType::Disk))
|
||||||
|
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||||
|
} else {
|
||||||
|
&right_widget.widget_type
|
||||||
|
}
|
||||||
|
})
|
||||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::{layout_manager::WidgetDirection, App},
|
||||||
canvas::{
|
canvas::{
|
||||||
drawing_utils::{get_start_position, get_variable_intrinsic_widths},
|
drawing_utils::{get_start_position, get_variable_intrinsic_widths},
|
||||||
Painter,
|
Painter,
|
||||||
|
@ -57,9 +57,9 @@ impl CpuGraphWidget for Painter {
|
||||||
// Skip drawing legend
|
// Skip drawing legend
|
||||||
if app_state.current_widget.widget_id == (widget_id + 1) {
|
if app_state.current_widget.widget_id == (widget_id + 1) {
|
||||||
if app_state.app_config_fields.left_legend {
|
if app_state.app_config_fields.left_legend {
|
||||||
app_state.move_widget_selection_right();
|
app_state.move_widget_selection(&WidgetDirection::Right);
|
||||||
} else {
|
} else {
|
||||||
app_state.move_widget_selection_left();
|
app_state.move_widget_selection(&WidgetDirection::Left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.draw_cpu_graph(f, app_state, draw_loc, widget_id);
|
self.draw_cpu_graph(f, app_state, draw_loc, widget_id);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{self, App},
|
app::App,
|
||||||
canvas::{
|
canvas::{
|
||||||
drawing_utils::{
|
drawing_utils::{
|
||||||
get_search_start_position, get_start_position, get_variable_intrinsic_widths,
|
get_search_start_position, get_start_position, get_variable_intrinsic_widths,
|
||||||
|
@ -14,43 +14,68 @@ use tui::{
|
||||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||||
terminal::Frame,
|
terminal::Frame,
|
||||||
text::{Span, Spans},
|
text::{Span, Spans},
|
||||||
widgets::{Block, Borders, Paragraph, Row, Table, Wrap},
|
widgets::{Block, Borders, Paragraph, Row, Table},
|
||||||
};
|
};
|
||||||
|
|
||||||
use unicode_segmentation::{GraphemeIndices, UnicodeSegmentation};
|
use unicode_segmentation::{GraphemeIndices, UnicodeSegmentation};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
pub trait ProcessTableWidget {
|
pub trait ProcessTableWidget {
|
||||||
fn draw_process_and_search<B: Backend>(
|
/// Draws and handles all process-related drawing. Use this.
|
||||||
|
/// - `widget_id` here represents the widget ID of the process widget itself!
|
||||||
|
fn draw_process_features<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Draws the process sort box.
|
||||||
|
/// - `widget_id` represents the widget ID of the process widget itself.
|
||||||
|
///
|
||||||
|
/// This should not be directly called.
|
||||||
fn draw_processes_table<B: Backend>(
|
fn draw_processes_table<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Draws the process sort box.
|
||||||
|
/// - `widget_id` represents the widget ID of the search box itself --- NOT the process widget
|
||||||
|
/// state that is stored.
|
||||||
|
///
|
||||||
|
/// This should not be directly called.
|
||||||
fn draw_search_field<B: Backend>(
|
fn draw_search_field<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Draws the process sort box.
|
||||||
|
/// - `widget_id` represents the widget ID of the sort box itself --- NOT the process widget
|
||||||
|
/// state that is stored.
|
||||||
|
///
|
||||||
|
/// This should not be directly called.
|
||||||
|
fn draw_process_sort<B: Backend>(
|
||||||
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
|
widget_id: u64,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessTableWidget for Painter {
|
impl ProcessTableWidget for Painter {
|
||||||
fn draw_process_and_search<B: Backend>(
|
fn draw_process_features<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
widget_id: u64,
|
widget_id: u64,
|
||||||
) {
|
) {
|
||||||
if let Some(process_widget_state) = app_state.proc_state.widget_states.get(&widget_id) {
|
if let Some(process_widget_state) = app_state.proc_state.widget_states.get(&widget_id) {
|
||||||
let search_height = if draw_border { 5 } else { 3 };
|
let search_height = if draw_border { 5 } else { 3 };
|
||||||
|
let is_sort_open = process_widget_state.is_sort_open;
|
||||||
|
let header_len = process_widget_state.columns.longest_header_len;
|
||||||
|
|
||||||
|
let mut proc_draw_loc = draw_loc;
|
||||||
if process_widget_state.is_search_enabled() {
|
if process_widget_state.is_search_enabled() {
|
||||||
let processes_chunk = Layout::default()
|
let processes_chunk = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Min(0), Constraint::Length(search_height)].as_ref())
|
.constraints([Constraint::Min(0), Constraint::Length(search_height)].as_ref())
|
||||||
.split(draw_loc);
|
.split(draw_loc);
|
||||||
|
proc_draw_loc = processes_chunk[0];
|
||||||
|
|
||||||
self.draw_processes_table(f, app_state, processes_chunk[0], draw_border, widget_id);
|
|
||||||
self.draw_search_field(
|
self.draw_search_field(
|
||||||
f,
|
f,
|
||||||
app_state,
|
app_state,
|
||||||
|
@ -58,9 +83,25 @@ impl ProcessTableWidget for Painter {
|
||||||
draw_border,
|
draw_border,
|
||||||
widget_id + 1,
|
widget_id + 1,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
self.draw_processes_table(f, app_state, draw_loc, draw_border, widget_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_sort_open {
|
||||||
|
let processes_chunk = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([Constraint::Length(header_len + 4), Constraint::Min(0)].as_ref())
|
||||||
|
.split(proc_draw_loc);
|
||||||
|
proc_draw_loc = processes_chunk[1];
|
||||||
|
|
||||||
|
self.draw_process_sort(
|
||||||
|
f,
|
||||||
|
app_state,
|
||||||
|
processes_chunk[0],
|
||||||
|
draw_border,
|
||||||
|
widget_id + 2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.draw_processes_table(f, app_state, proc_draw_loc, draw_border, widget_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,71 +168,17 @@ impl ProcessTableWidget for Painter {
|
||||||
process.write_per_sec.to_string(),
|
process.write_per_sec.to_string(),
|
||||||
process.total_read.to_string(),
|
process.total_read.to_string(),
|
||||||
process.total_write.to_string(),
|
process.total_write.to_string(),
|
||||||
process.process_states.to_string(),
|
process.process_state.to_string(),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
use app::data_harvester::processes::ProcessSorting;
|
let process_headers = proc_widget_state.columns.get_column_headers(
|
||||||
let mut pid_or_count = if proc_widget_state.is_grouped {
|
&proc_widget_state.process_sorting_type,
|
||||||
"Count"
|
proc_widget_state.process_sorting_reverse,
|
||||||
} else {
|
);
|
||||||
"PID(p)"
|
|
||||||
}
|
|
||||||
.to_string();
|
|
||||||
let mut identifier = if proc_widget_state.is_using_full_path {
|
|
||||||
"Command(n)".to_string()
|
|
||||||
} else {
|
|
||||||
"Name(n)".to_string()
|
|
||||||
};
|
|
||||||
let mut cpu = "CPU%(c)".to_string();
|
|
||||||
let mut mem = "Mem%(m)".to_string();
|
|
||||||
let rps = "R/s".to_string();
|
|
||||||
let wps = "W/s".to_string();
|
|
||||||
let total_read = "Read".to_string();
|
|
||||||
let total_write = "Write".to_string();
|
|
||||||
let process_state = "State ".to_string();
|
|
||||||
|
|
||||||
let direction_val = if proc_widget_state.process_sorting_reverse {
|
|
||||||
"▼".to_string()
|
|
||||||
} else {
|
|
||||||
"▲".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
match proc_widget_state.process_sorting_type {
|
|
||||||
ProcessSorting::CPU => cpu += &direction_val,
|
|
||||||
ProcessSorting::MEM => mem += &direction_val,
|
|
||||||
ProcessSorting::PID => pid_or_count += &direction_val,
|
|
||||||
ProcessSorting::IDENTIFIER => identifier += &direction_val,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Gonna have to figure out how to do left/right GUI notation.
|
|
||||||
let process_headers = if proc_widget_state.is_grouped {
|
|
||||||
vec![
|
|
||||||
pid_or_count,
|
|
||||||
identifier,
|
|
||||||
cpu,
|
|
||||||
mem,
|
|
||||||
rps,
|
|
||||||
wps,
|
|
||||||
total_read,
|
|
||||||
total_write,
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
vec![
|
|
||||||
pid_or_count,
|
|
||||||
identifier,
|
|
||||||
cpu,
|
|
||||||
mem,
|
|
||||||
rps,
|
|
||||||
wps,
|
|
||||||
total_read,
|
|
||||||
total_write,
|
|
||||||
process_state,
|
|
||||||
]
|
|
||||||
};
|
|
||||||
proc_widget_state.num_columns = process_headers.len();
|
|
||||||
let process_headers_lens: Vec<usize> = process_headers
|
let process_headers_lens: Vec<usize> = process_headers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| entry.len())
|
.map(|entry| entry.len())
|
||||||
|
@ -202,12 +189,12 @@ impl ProcessTableWidget for Painter {
|
||||||
|
|
||||||
// TODO: This is a ugly work-around for now.
|
// TODO: This is a ugly work-around for now.
|
||||||
let width_ratios = if proc_widget_state.is_grouped {
|
let width_ratios = if proc_widget_state.is_grouped {
|
||||||
if proc_widget_state.is_using_full_path {
|
if proc_widget_state.is_using_command {
|
||||||
vec![0.05, 0.7, 0.05, 0.05, 0.0375, 0.0375, 0.0375, 0.0375]
|
vec![0.05, 0.7, 0.05, 0.05, 0.0375, 0.0375, 0.0375, 0.0375]
|
||||||
} else {
|
} else {
|
||||||
vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.15, 0.15]
|
vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.15, 0.15]
|
||||||
}
|
}
|
||||||
} else if proc_widget_state.is_using_full_path {
|
} else if proc_widget_state.is_using_command {
|
||||||
vec![0.05, 0.7, 0.05, 0.05, 0.03, 0.03, 0.03, 0.03]
|
vec![0.05, 0.7, 0.05, 0.05, 0.03, 0.03, 0.03, 0.03]
|
||||||
} else {
|
} else {
|
||||||
vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
|
vec![0.1, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
|
||||||
|
@ -235,6 +222,7 @@ impl ProcessTableWidget for Painter {
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.is_enabled
|
.is_enabled
|
||||||
|
&& !proc_widget_state.is_sort_open
|
||||||
{
|
{
|
||||||
const TITLE_BASE: &str = " Processes ── Esc to go back ";
|
const TITLE_BASE: &str = " Processes ── Esc to go back ";
|
||||||
Span::styled(
|
Span::styled(
|
||||||
|
@ -502,10 +490,108 @@ impl ProcessTableWidget for Painter {
|
||||||
Paragraph::new(search_text)
|
Paragraph::new(search_text)
|
||||||
.block(process_search_block)
|
.block(process_search_block)
|
||||||
.style(self.colours.text_style)
|
.style(self.colours.text_style)
|
||||||
.alignment(Alignment::Left)
|
.alignment(Alignment::Left),
|
||||||
.wrap(Wrap { trim: false }),
|
|
||||||
margined_draw_loc[0],
|
margined_draw_loc[0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_process_sort<B: Backend>(
|
||||||
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
|
widget_id: u64,
|
||||||
|
) {
|
||||||
|
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||||
|
|
||||||
|
if let Some(proc_widget_state) =
|
||||||
|
app_state.proc_state.widget_states.get_mut(&(widget_id - 2))
|
||||||
|
{
|
||||||
|
let current_scroll_position = proc_widget_state.columns.current_scroll_position;
|
||||||
|
let sort_string = proc_widget_state
|
||||||
|
.columns
|
||||||
|
.ordered_columns
|
||||||
|
.iter()
|
||||||
|
.filter(|column_type| {
|
||||||
|
proc_widget_state
|
||||||
|
.columns
|
||||||
|
.column_mapping
|
||||||
|
.get(&column_type)
|
||||||
|
.unwrap()
|
||||||
|
.enabled
|
||||||
|
})
|
||||||
|
.enumerate()
|
||||||
|
.map(|(itx, column_type)| {
|
||||||
|
if current_scroll_position == itx {
|
||||||
|
(
|
||||||
|
column_type.to_string(),
|
||||||
|
self.colours.currently_selected_text_style,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(column_type.to_string(), self.colours.text_style)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let position = get_start_position(
|
||||||
|
usize::from(draw_loc.height.saturating_sub(self.table_height_offset)),
|
||||||
|
&proc_widget_state.columns.scroll_direction,
|
||||||
|
&mut proc_widget_state.columns.previous_scroll_position,
|
||||||
|
current_scroll_position,
|
||||||
|
app_state.is_force_redraw,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
let start_position = if position >= sort_string.len() {
|
||||||
|
sort_string.len().saturating_sub(1)
|
||||||
|
} else {
|
||||||
|
position
|
||||||
|
};
|
||||||
|
|
||||||
|
let sliced_vec = &sort_string[start_position..];
|
||||||
|
|
||||||
|
let sort_options = sliced_vec
|
||||||
|
.iter()
|
||||||
|
.map(|(column, style)| Row::StyledData(vec![column].into_iter(), *style));
|
||||||
|
|
||||||
|
let column_state = &mut proc_widget_state.columns.column_state;
|
||||||
|
let current_border_style = if proc_widget_state
|
||||||
|
.process_search_state
|
||||||
|
.search_state
|
||||||
|
.is_invalid_search
|
||||||
|
{
|
||||||
|
self.colours.invalid_query_style
|
||||||
|
} else if is_on_widget {
|
||||||
|
self.colours.highlighted_border_style
|
||||||
|
} else {
|
||||||
|
self.colours.border_style
|
||||||
|
};
|
||||||
|
|
||||||
|
let process_sort_block = if draw_border {
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(current_border_style)
|
||||||
|
} else if is_on_widget {
|
||||||
|
Block::default()
|
||||||
|
.borders(*SIDE_BORDERS)
|
||||||
|
.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(if is_on_widget || draw_border { 0 } else { 1 })
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.split(draw_loc);
|
||||||
|
|
||||||
|
f.render_stateful_widget(
|
||||||
|
Table::new(["Sort By"].iter(), sort_options)
|
||||||
|
.block(process_sort_block)
|
||||||
|
.header_style(self.colours.table_header_style)
|
||||||
|
.widths(&[Constraint::Percentage(100)])
|
||||||
|
.header_gap(1),
|
||||||
|
margined_draw_loc[0],
|
||||||
|
column_state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,13 +42,14 @@ lazy_static! {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Help text
|
// Help text
|
||||||
pub const HELP_CONTENTS_TEXT: [&str; 6] = [
|
pub const HELP_CONTENTS_TEXT: [&str; 7] = [
|
||||||
"Press the corresponding numbers to jump to the section, or scroll:",
|
"Press the corresponding numbers to jump to the section, or scroll:",
|
||||||
"1 - General",
|
"1 - General",
|
||||||
"2 - CPU widget",
|
"2 - CPU widget",
|
||||||
"3 - Process widget",
|
"3 - Process widget",
|
||||||
"4 - Process search widget",
|
"4 - Process search widget",
|
||||||
"5 - Battery widget",
|
"5 - Process sort widget",
|
||||||
|
"6 - Battery widget",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const GENERAL_HELP_TEXT: [&str; 29] = [
|
pub const GENERAL_HELP_TEXT: [&str; 29] = [
|
||||||
|
@ -88,7 +89,9 @@ pub const CPU_HELP_TEXT: [&str; 2] = [
|
||||||
"Mouse scroll Scrolling over an CPU core/average shows only that entry on the chart",
|
"Mouse scroll Scrolling over an CPU core/average shows only that entry on the chart",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const PROCESS_HELP_TEXT: [&str; 9] = [
|
// TODO [Help]: Search in help?
|
||||||
|
// TODO [Help]: Move to using tables for easier formatting?
|
||||||
|
pub const PROCESS_HELP_TEXT: [&str; 11] = [
|
||||||
"3 - Process widget",
|
"3 - Process widget",
|
||||||
"dd Kill the selected process",
|
"dd Kill the selected process",
|
||||||
"c Sort by CPU usage, press again to reverse sorting order",
|
"c Sort by CPU usage, press again to reverse sorting order",
|
||||||
|
@ -98,6 +101,8 @@ pub const PROCESS_HELP_TEXT: [&str; 9] = [
|
||||||
"Tab Group/un-group processes with the same name",
|
"Tab Group/un-group processes with the same name",
|
||||||
"Ctrl-f, / Open process search widget",
|
"Ctrl-f, / Open process search widget",
|
||||||
"P Toggle between showing the full path or just the process name",
|
"P Toggle between showing the full path or just the process name",
|
||||||
|
"s, F6 Open process sort widget",
|
||||||
|
"I Invert current sort",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const SEARCH_HELP_TEXT: [&str; 43] = [
|
pub const SEARCH_HELP_TEXT: [&str; 43] = [
|
||||||
|
@ -146,8 +151,17 @@ pub const SEARCH_HELP_TEXT: [&str; 43] = [
|
||||||
"TiB ex: read > 1 tib",
|
"TiB ex: read > 1 tib",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub const SORT_HELP_TEXT: [&str; 6] = [
|
||||||
|
"5 - Sort widget",
|
||||||
|
"Down, 'j' Scroll down in list",
|
||||||
|
"Up, 'k' Scroll up in list",
|
||||||
|
"Mouse scroll Scroll through sort widget",
|
||||||
|
"Esc Close the sort widget",
|
||||||
|
"Enter Sort by current selected column",
|
||||||
|
];
|
||||||
|
|
||||||
pub const BATTERY_HELP_TEXT: [&str; 3] = [
|
pub const BATTERY_HELP_TEXT: [&str; 3] = [
|
||||||
"5 - Battery widget",
|
"6 - Battery widget",
|
||||||
"Left Go to previous battery",
|
"Left Go to previous battery",
|
||||||
"Right Go to next battery",
|
"Right Go to next battery",
|
||||||
];
|
];
|
||||||
|
@ -159,10 +173,37 @@ lazy_static! {
|
||||||
CPU_HELP_TEXT.to_vec(),
|
CPU_HELP_TEXT.to_vec(),
|
||||||
PROCESS_HELP_TEXT.to_vec(),
|
PROCESS_HELP_TEXT.to_vec(),
|
||||||
SEARCH_HELP_TEXT.to_vec(),
|
SEARCH_HELP_TEXT.to_vec(),
|
||||||
|
SORT_HELP_TEXT.to_vec(),
|
||||||
BATTERY_HELP_TEXT.to_vec(),
|
BATTERY_HELP_TEXT.to_vec(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default layout
|
||||||
|
pub const DEFAULT_LAYOUT: &str = r##"
|
||||||
|
[[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
|
||||||
|
"##;
|
||||||
|
|
||||||
// Config and flags
|
// Config and flags
|
||||||
pub const DEFAULT_CONFIG_FILE_PATH: &str = "bottom/bottom.toml";
|
pub const DEFAULT_CONFIG_FILE_PATH: &str = "bottom/bottom.toml";
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub struct ConvertedProcessData {
|
||||||
pub wps_f64: f64,
|
pub wps_f64: f64,
|
||||||
pub tr_f64: f64,
|
pub tr_f64: f64,
|
||||||
pub tw_f64: f64,
|
pub tw_f64: f64,
|
||||||
pub process_states: String,
|
pub process_state: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
|
@ -409,7 +409,7 @@ pub fn convert_process_data(
|
||||||
wps_f64: process.write_bytes_per_sec as f64,
|
wps_f64: process.write_bytes_per_sec as f64,
|
||||||
tr_f64: process.total_read_bytes as f64,
|
tr_f64: process.total_read_bytes as f64,
|
||||||
tw_f64: process.total_write_bytes as f64,
|
tw_f64: process.total_write_bytes as f64,
|
||||||
process_states: process.process_state.to_owned(),
|
process_state: process.process_state.to_owned(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
@ -469,7 +469,7 @@ pub fn convert_process_data(
|
||||||
wps_f64: p.write_per_sec as f64,
|
wps_f64: p.write_per_sec as f64,
|
||||||
tr_f64: p.total_read as f64,
|
tr_f64: p.total_read as f64,
|
||||||
tw_f64: p.total_write as f64,
|
tw_f64: p.total_write as f64,
|
||||||
process_states: p.process_state,
|
process_state: p.process_state,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|
95
src/main.rs
95
src/main.rs
|
@ -28,7 +28,7 @@ use tui::{backend::CrosstermBackend, Terminal};
|
||||||
|
|
||||||
use app::{
|
use app::{
|
||||||
data_harvester::{self, processes::ProcessSorting},
|
data_harvester::{self, processes::ProcessSorting},
|
||||||
layout_manager::UsedWidgets,
|
layout_manager::{UsedWidgets, WidgetDirection},
|
||||||
App,
|
App,
|
||||||
};
|
};
|
||||||
use constants::*;
|
use constants::*;
|
||||||
|
@ -286,15 +286,10 @@ fn handle_key_event_or_break(
|
||||||
KeyCode::Tab => app.on_tab(),
|
KeyCode::Tab => app.on_tab(),
|
||||||
KeyCode::Backspace => app.on_backspace(),
|
KeyCode::Backspace => app.on_backspace(),
|
||||||
KeyCode::Delete => app.on_delete(),
|
KeyCode::Delete => app.on_delete(),
|
||||||
KeyCode::F(1) => {
|
KeyCode::F(1) => app.toggle_ignore_case(),
|
||||||
app.toggle_ignore_case();
|
KeyCode::F(2) => app.toggle_search_whole_word(),
|
||||||
}
|
KeyCode::F(3) => app.toggle_search_regex(),
|
||||||
KeyCode::F(2) => {
|
KeyCode::F(6) => app.toggle_sort(),
|
||||||
app.toggle_search_whole_word();
|
|
||||||
}
|
|
||||||
KeyCode::F(3) => {
|
|
||||||
app.toggle_search_regex();
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -315,10 +310,10 @@ fn handle_key_event_or_break(
|
||||||
|
|
||||||
match event.code {
|
match event.code {
|
||||||
KeyCode::Char('f') => app.on_slash(),
|
KeyCode::Char('f') => app.on_slash(),
|
||||||
KeyCode::Left => app.move_widget_selection_left(),
|
KeyCode::Left => app.move_widget_selection(&WidgetDirection::Left),
|
||||||
KeyCode::Right => app.move_widget_selection_right(),
|
KeyCode::Right => app.move_widget_selection(&WidgetDirection::Right),
|
||||||
KeyCode::Up => app.move_widget_selection_up(),
|
KeyCode::Up => app.move_widget_selection(&WidgetDirection::Up),
|
||||||
KeyCode::Down => app.move_widget_selection_down(),
|
KeyCode::Down => app.move_widget_selection(&WidgetDirection::Down),
|
||||||
KeyCode::Char('r') => {
|
KeyCode::Char('r') => {
|
||||||
if reset_sender.send(ResetEvent::Reset).is_ok() {
|
if reset_sender.send(ResetEvent::Reset).is_ok() {
|
||||||
app.reset();
|
app.reset();
|
||||||
|
@ -338,10 +333,10 @@ fn handle_key_event_or_break(
|
||||||
}
|
}
|
||||||
} else if let KeyModifiers::SHIFT = event.modifiers {
|
} else if let KeyModifiers::SHIFT = event.modifiers {
|
||||||
match event.code {
|
match event.code {
|
||||||
KeyCode::Left => app.move_widget_selection_left(),
|
KeyCode::Left => app.move_widget_selection(&WidgetDirection::Left),
|
||||||
KeyCode::Right => app.move_widget_selection_right(),
|
KeyCode::Right => app.move_widget_selection(&WidgetDirection::Right),
|
||||||
KeyCode::Up => app.move_widget_selection_up(),
|
KeyCode::Up => app.move_widget_selection(&WidgetDirection::Up),
|
||||||
KeyCode::Down => app.move_widget_selection_down(),
|
KeyCode::Down => app.move_widget_selection(&WidgetDirection::Down),
|
||||||
KeyCode::Char(caught_char) => app.on_char_key(caught_char),
|
KeyCode::Char(caught_char) => app.on_char_key(caught_char),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -607,7 +602,7 @@ fn update_final_process_list(app: &mut App, widget_id: u64) {
|
||||||
} else {
|
} else {
|
||||||
ProcessGroupingType::Ungrouped
|
ProcessGroupingType::Ungrouped
|
||||||
},
|
},
|
||||||
if proc_widget_state.is_using_full_path {
|
if proc_widget_state.is_using_command {
|
||||||
ProcessNamingType::Path
|
ProcessNamingType::Path
|
||||||
} else {
|
} else {
|
||||||
ProcessNamingType::Name
|
ProcessNamingType::Name
|
||||||
|
@ -636,12 +631,12 @@ fn update_final_process_list(app: &mut App, widget_id: u64) {
|
||||||
|
|
||||||
// Quick fix for tab updating the table headers
|
// Quick fix for tab updating the table headers
|
||||||
if let Some(proc_widget_state) = app.proc_state.get_mut_widget_state(widget_id) {
|
if let Some(proc_widget_state) = app.proc_state.get_mut_widget_state(widget_id) {
|
||||||
if let data_harvester::processes::ProcessSorting::PID =
|
if let data_harvester::processes::ProcessSorting::Pid =
|
||||||
proc_widget_state.process_sorting_type
|
proc_widget_state.process_sorting_type
|
||||||
{
|
{
|
||||||
if proc_widget_state.is_grouped {
|
if proc_widget_state.is_grouped {
|
||||||
proc_widget_state.process_sorting_type =
|
proc_widget_state.process_sorting_type =
|
||||||
data_harvester::processes::ProcessSorting::CPU; // Go back to default, negate PID for group
|
data_harvester::processes::ProcessSorting::CpuPercent; // Go back to default, negate PID for group
|
||||||
proc_widget_state.process_sorting_reverse = true;
|
proc_widget_state.process_sorting_reverse = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,7 +648,7 @@ fn update_final_process_list(app: &mut App, widget_id: u64) {
|
||||||
proc_widget_state.scroll_state.current_scroll_position =
|
proc_widget_state.scroll_state.current_scroll_position =
|
||||||
resulting_processes.len().saturating_sub(1);
|
resulting_processes.len().saturating_sub(1);
|
||||||
proc_widget_state.scroll_state.previous_scroll_position = 0;
|
proc_widget_state.scroll_state.previous_scroll_position = 0;
|
||||||
proc_widget_state.scroll_state.scroll_direction = app::ScrollDirection::DOWN;
|
proc_widget_state.scroll_state.scroll_direction = app::ScrollDirection::Down;
|
||||||
}
|
}
|
||||||
|
|
||||||
app.canvas_data
|
app.canvas_data
|
||||||
|
@ -670,7 +665,7 @@ fn sort_process_data(
|
||||||
});
|
});
|
||||||
|
|
||||||
match proc_widget_state.process_sorting_type {
|
match proc_widget_state.process_sorting_type {
|
||||||
ProcessSorting::CPU => {
|
ProcessSorting::CpuPercent => {
|
||||||
to_sort_vec.sort_by(|a, b| {
|
to_sort_vec.sort_by(|a, b| {
|
||||||
utils::gen_util::get_ordering(
|
utils::gen_util::get_ordering(
|
||||||
a.cpu_usage,
|
a.cpu_usage,
|
||||||
|
@ -679,7 +674,10 @@ fn sort_process_data(
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ProcessSorting::MEM => {
|
ProcessSorting::Mem => {
|
||||||
|
// TODO: Do when I do mem values in processes
|
||||||
|
}
|
||||||
|
ProcessSorting::MemPercent => {
|
||||||
to_sort_vec.sort_by(|a, b| {
|
to_sort_vec.sort_by(|a, b| {
|
||||||
utils::gen_util::get_ordering(
|
utils::gen_util::get_ordering(
|
||||||
a.mem_usage,
|
a.mem_usage,
|
||||||
|
@ -688,8 +686,8 @@ fn sort_process_data(
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ProcessSorting::IDENTIFIER => {
|
ProcessSorting::ProcessName | ProcessSorting::Command => {
|
||||||
// Don't repeat if false...
|
// Don't repeat if false... it sorts by name by default anyways.
|
||||||
if proc_widget_state.process_sorting_reverse {
|
if proc_widget_state.process_sorting_reverse {
|
||||||
to_sort_vec.sort_by(|a, b| {
|
to_sort_vec.sort_by(|a, b| {
|
||||||
utils::gen_util::get_ordering(
|
utils::gen_util::get_ordering(
|
||||||
|
@ -700,7 +698,7 @@ fn sort_process_data(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProcessSorting::PID => {
|
ProcessSorting::Pid => {
|
||||||
if !proc_widget_state.is_grouped {
|
if !proc_widget_state.is_grouped {
|
||||||
to_sort_vec.sort_by(|a, b| {
|
to_sort_vec.sort_by(|a, b| {
|
||||||
utils::gen_util::get_ordering(
|
utils::gen_util::get_ordering(
|
||||||
|
@ -711,6 +709,49 @@ fn sort_process_data(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ProcessSorting::ReadPerSecond => {
|
||||||
|
to_sort_vec.sort_by(|a, b| {
|
||||||
|
utils::gen_util::get_ordering(
|
||||||
|
a.rps_f64,
|
||||||
|
b.rps_f64,
|
||||||
|
proc_widget_state.process_sorting_reverse,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ProcessSorting::WritePerSecond => {
|
||||||
|
to_sort_vec.sort_by(|a, b| {
|
||||||
|
utils::gen_util::get_ordering(
|
||||||
|
a.wps_f64,
|
||||||
|
b.wps_f64,
|
||||||
|
proc_widget_state.process_sorting_reverse,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ProcessSorting::TotalRead => {
|
||||||
|
to_sort_vec.sort_by(|a, b| {
|
||||||
|
utils::gen_util::get_ordering(
|
||||||
|
a.tr_f64,
|
||||||
|
b.tr_f64,
|
||||||
|
proc_widget_state.process_sorting_reverse,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ProcessSorting::TotalWrite => {
|
||||||
|
to_sort_vec.sort_by(|a, b| {
|
||||||
|
utils::gen_util::get_ordering(
|
||||||
|
a.tw_f64,
|
||||||
|
b.tw_f64,
|
||||||
|
proc_widget_state.process_sorting_reverse,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ProcessSorting::State => to_sort_vec.sort_by(|a, b| {
|
||||||
|
utils::gen_util::get_ordering(
|
||||||
|
&a.process_state.to_lowercase(),
|
||||||
|
&b.process_state.to_lowercase(),
|
||||||
|
proc_widget_state.process_sorting_reverse,
|
||||||
|
)
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,8 +180,7 @@ pub fn build_app(
|
||||||
battery_state_map
|
battery_state_map
|
||||||
.insert(widget.widget_id, BatteryWidgetState::default());
|
.insert(widget.widget_id, BatteryWidgetState::default());
|
||||||
}
|
}
|
||||||
Empty | BasicCpu | BasicMem | BasicNet | BasicTables | ProcSearch
|
_ => {}
|
||||||
| CpuLegend => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,7 +261,17 @@ pub fn get_widget_layout(
|
||||||
let bottom_layout = if get_use_basic_mode(matches, config) {
|
let bottom_layout = if get_use_basic_mode(matches, config) {
|
||||||
default_widget_id = DEFAULT_WIDGET_ID;
|
default_widget_id = DEFAULT_WIDGET_ID;
|
||||||
BottomLayout::init_basic_default(get_use_battery(matches, config))
|
BottomLayout::init_basic_default(get_use_battery(matches, config))
|
||||||
} else if let Some(rows) = &config.row {
|
} else {
|
||||||
|
let ref_row: Vec<Row>; // Required to handle reference
|
||||||
|
let rows = match &config.row {
|
||||||
|
Some(r) => r,
|
||||||
|
None => {
|
||||||
|
// This cannot (like it really shouldn't) fail!
|
||||||
|
ref_row = toml::from_str::<Config>(DEFAULT_LAYOUT)?.row.unwrap();
|
||||||
|
&ref_row
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||||
let mut total_height_ratio = 0;
|
let mut total_height_ratio = 0;
|
||||||
|
|
||||||
|
@ -286,15 +295,14 @@ pub fn get_widget_layout(
|
||||||
// Confirm that we have at least ONE widget - if not, error out!
|
// Confirm that we have at least ONE widget - if not, error out!
|
||||||
if iter_id > 0 {
|
if iter_id > 0 {
|
||||||
ret_bottom_layout.get_movement_mappings();
|
ret_bottom_layout.get_movement_mappings();
|
||||||
|
// debug!("Bottom layout: {:#?}", ret_bottom_layout);
|
||||||
|
|
||||||
ret_bottom_layout
|
ret_bottom_layout
|
||||||
} else {
|
} else {
|
||||||
return Err(error::BottomError::ConfigError(
|
return Err(error::BottomError::ConfigError(
|
||||||
"invalid layout config: please have at least one widget.".to_string(),
|
"invalid layout config: please have at least one widget.".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
default_widget_id = DEFAULT_WIDGET_ID;
|
|
||||||
BottomLayout::init_default(left_legend, get_use_battery(matches, config))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((bottom_layout, default_widget_id))
|
Ok((bottom_layout, default_widget_id))
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl Row {
|
||||||
default_widget_type: &Option<BottomWidgetType>, default_widget_count: &mut u64,
|
default_widget_type: &Option<BottomWidgetType>, default_widget_count: &mut u64,
|
||||||
left_legend: bool,
|
left_legend: bool,
|
||||||
) -> Result<BottomRow> {
|
) -> Result<BottomRow> {
|
||||||
// In the future we want to also add percentages.
|
// TODO: In the future we want to also add percentages.
|
||||||
// But for MVP, we aren't going to bother.
|
// But for MVP, we aren't going to bother.
|
||||||
let row_ratio = self.ratio.unwrap_or(1);
|
let row_ratio = self.ratio.unwrap_or(1);
|
||||||
let mut children = Vec::new();
|
let mut children = Vec::new();
|
||||||
|
@ -53,7 +53,7 @@ impl Row {
|
||||||
|
|
||||||
children.push(match widget_type {
|
children.push(match widget_type {
|
||||||
BottomWidgetType::Cpu => {
|
BottomWidgetType::Cpu => {
|
||||||
let iter_old_id = *iter_id;
|
let cpu_id = *iter_id;
|
||||||
*iter_id += 1;
|
*iter_id += 1;
|
||||||
BottomCol::builder()
|
BottomCol::builder()
|
||||||
.col_width_ratio(width_ratio)
|
.col_width_ratio(width_ratio)
|
||||||
|
@ -66,11 +66,15 @@ impl Row {
|
||||||
.widget_type(BottomWidgetType::CpuLegend)
|
.widget_type(BottomWidgetType::CpuLegend)
|
||||||
.widget_id(*iter_id)
|
.widget_id(*iter_id)
|
||||||
.canvas_handle_width(true)
|
.canvas_handle_width(true)
|
||||||
|
.parent_reflector(Some((
|
||||||
|
WidgetDirection::Right,
|
||||||
|
1,
|
||||||
|
)))
|
||||||
.build(),
|
.build(),
|
||||||
BottomWidget::builder()
|
BottomWidget::builder()
|
||||||
.width_ratio(17)
|
.width_ratio(17)
|
||||||
.widget_type(BottomWidgetType::Cpu)
|
.widget_type(BottomWidgetType::Cpu)
|
||||||
.widget_id(iter_old_id)
|
.widget_id(cpu_id)
|
||||||
.flex_grow(true)
|
.flex_grow(true)
|
||||||
.build(),
|
.build(),
|
||||||
])
|
])
|
||||||
|
@ -82,7 +86,7 @@ impl Row {
|
||||||
BottomWidget::builder()
|
BottomWidget::builder()
|
||||||
.width_ratio(17)
|
.width_ratio(17)
|
||||||
.widget_type(BottomWidgetType::Cpu)
|
.widget_type(BottomWidgetType::Cpu)
|
||||||
.widget_id(iter_old_id)
|
.widget_id(cpu_id)
|
||||||
.flex_grow(true)
|
.flex_grow(true)
|
||||||
.build(),
|
.build(),
|
||||||
BottomWidget::builder()
|
BottomWidget::builder()
|
||||||
|
@ -90,6 +94,10 @@ impl Row {
|
||||||
.widget_type(BottomWidgetType::CpuLegend)
|
.widget_type(BottomWidgetType::CpuLegend)
|
||||||
.widget_id(*iter_id)
|
.widget_id(*iter_id)
|
||||||
.canvas_handle_width(true)
|
.canvas_handle_width(true)
|
||||||
|
.parent_reflector(Some((
|
||||||
|
WidgetDirection::Left,
|
||||||
|
1,
|
||||||
|
)))
|
||||||
.build(),
|
.build(),
|
||||||
])
|
])
|
||||||
.build()]
|
.build()]
|
||||||
|
@ -97,23 +105,39 @@ impl Row {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
BottomWidgetType::Proc => {
|
BottomWidgetType::Proc => {
|
||||||
let iter_old_id = *iter_id;
|
let proc_id = *iter_id;
|
||||||
*iter_id += 1;
|
let proc_search_id = *iter_id + 1;
|
||||||
|
*iter_id += 2;
|
||||||
BottomCol::builder()
|
BottomCol::builder()
|
||||||
.total_col_row_ratio(2)
|
.total_col_row_ratio(2)
|
||||||
.col_width_ratio(width_ratio)
|
.col_width_ratio(width_ratio)
|
||||||
.children(vec![
|
.children(vec![
|
||||||
BottomColRow::builder()
|
BottomColRow::builder()
|
||||||
.children(vec![BottomWidget::builder()
|
.children(vec![
|
||||||
.widget_type(BottomWidgetType::Proc)
|
BottomWidget::builder()
|
||||||
.widget_id(iter_old_id)
|
.widget_type(BottomWidgetType::ProcSort)
|
||||||
.build()])
|
.widget_id(*iter_id)
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.parent_reflector(Some((
|
||||||
|
WidgetDirection::Right,
|
||||||
|
2,
|
||||||
|
)))
|
||||||
|
.width_ratio(1)
|
||||||
|
.build(),
|
||||||
|
BottomWidget::builder()
|
||||||
|
.widget_type(BottomWidgetType::Proc)
|
||||||
|
.widget_id(proc_id)
|
||||||
|
.width_ratio(2)
|
||||||
|
.build(),
|
||||||
|
])
|
||||||
|
.total_widget_ratio(3)
|
||||||
.flex_grow(true)
|
.flex_grow(true)
|
||||||
.build(),
|
.build(),
|
||||||
BottomColRow::builder()
|
BottomColRow::builder()
|
||||||
.children(vec![BottomWidget::builder()
|
.children(vec![BottomWidget::builder()
|
||||||
.widget_type(BottomWidgetType::ProcSearch)
|
.widget_type(BottomWidgetType::ProcSearch)
|
||||||
.widget_id(*iter_id)
|
.widget_id(proc_search_id)
|
||||||
|
.parent_reflector(Some((WidgetDirection::Up, 1)))
|
||||||
.build()])
|
.build()])
|
||||||
.canvas_handle_height(true)
|
.canvas_handle_height(true)
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -137,7 +161,7 @@ impl Row {
|
||||||
let mut total_col_row_ratio = 0;
|
let mut total_col_row_ratio = 0;
|
||||||
let mut contains_proc = false;
|
let mut contains_proc = false;
|
||||||
|
|
||||||
let mut col_row_children = Vec::new();
|
let mut col_row_children: Vec<BottomColRow> = Vec::new();
|
||||||
|
|
||||||
for widget in child {
|
for widget in child {
|
||||||
let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
|
let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
|
||||||
|
@ -165,7 +189,7 @@ impl Row {
|
||||||
|
|
||||||
match widget_type {
|
match widget_type {
|
||||||
BottomWidgetType::Cpu => {
|
BottomWidgetType::Cpu => {
|
||||||
let iter_old_id = *iter_id;
|
let cpu_id = *iter_id;
|
||||||
*iter_id += 1;
|
*iter_id += 1;
|
||||||
if left_legend {
|
if left_legend {
|
||||||
col_row_children.push(
|
col_row_children.push(
|
||||||
|
@ -178,11 +202,15 @@ impl Row {
|
||||||
.widget_type(BottomWidgetType::CpuLegend)
|
.widget_type(BottomWidgetType::CpuLegend)
|
||||||
.widget_id(*iter_id)
|
.widget_id(*iter_id)
|
||||||
.canvas_handle_width(true)
|
.canvas_handle_width(true)
|
||||||
|
.parent_reflector(Some((
|
||||||
|
WidgetDirection::Right,
|
||||||
|
1,
|
||||||
|
)))
|
||||||
.build(),
|
.build(),
|
||||||
BottomWidget::builder()
|
BottomWidget::builder()
|
||||||
.width_ratio(17)
|
.width_ratio(17)
|
||||||
.widget_type(BottomWidgetType::Cpu)
|
.widget_type(BottomWidgetType::Cpu)
|
||||||
.widget_id(iter_old_id)
|
.widget_id(cpu_id)
|
||||||
.flex_grow(true)
|
.flex_grow(true)
|
||||||
.build(),
|
.build(),
|
||||||
])
|
])
|
||||||
|
@ -197,7 +225,7 @@ impl Row {
|
||||||
BottomWidget::builder()
|
BottomWidget::builder()
|
||||||
.width_ratio(17)
|
.width_ratio(17)
|
||||||
.widget_type(BottomWidgetType::Cpu)
|
.widget_type(BottomWidgetType::Cpu)
|
||||||
.widget_id(iter_old_id)
|
.widget_id(cpu_id)
|
||||||
.flex_grow(true)
|
.flex_grow(true)
|
||||||
.build(),
|
.build(),
|
||||||
BottomWidget::builder()
|
BottomWidget::builder()
|
||||||
|
@ -205,6 +233,10 @@ impl Row {
|
||||||
.widget_type(BottomWidgetType::CpuLegend)
|
.widget_type(BottomWidgetType::CpuLegend)
|
||||||
.widget_id(*iter_id)
|
.widget_id(*iter_id)
|
||||||
.canvas_handle_width(true)
|
.canvas_handle_width(true)
|
||||||
|
.parent_reflector(Some((
|
||||||
|
WidgetDirection::Left,
|
||||||
|
1,
|
||||||
|
)))
|
||||||
.build(),
|
.build(),
|
||||||
])
|
])
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -213,15 +245,30 @@ impl Row {
|
||||||
}
|
}
|
||||||
BottomWidgetType::Proc => {
|
BottomWidgetType::Proc => {
|
||||||
contains_proc = true;
|
contains_proc = true;
|
||||||
let iter_old_id = *iter_id;
|
let proc_id = *iter_id;
|
||||||
*iter_id += 1;
|
let proc_search_id = *iter_id + 1;
|
||||||
|
*iter_id += 2;
|
||||||
col_row_children.push(
|
col_row_children.push(
|
||||||
BottomColRow::builder()
|
BottomColRow::builder()
|
||||||
|
.children(vec![
|
||||||
|
BottomWidget::builder()
|
||||||
|
.widget_type(BottomWidgetType::ProcSort)
|
||||||
|
.widget_id(*iter_id)
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.parent_reflector(Some((
|
||||||
|
WidgetDirection::Right,
|
||||||
|
2,
|
||||||
|
)))
|
||||||
|
.width_ratio(1)
|
||||||
|
.build(),
|
||||||
|
BottomWidget::builder()
|
||||||
|
.widget_type(BottomWidgetType::Proc)
|
||||||
|
.widget_id(proc_id)
|
||||||
|
.width_ratio(2)
|
||||||
|
.build(),
|
||||||
|
])
|
||||||
.col_row_height_ratio(col_row_height_ratio)
|
.col_row_height_ratio(col_row_height_ratio)
|
||||||
.children(vec![BottomWidget::builder()
|
.total_widget_ratio(3)
|
||||||
.widget_type(BottomWidgetType::Proc)
|
|
||||||
.widget_id(iter_old_id)
|
|
||||||
.build()])
|
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
col_row_children.push(
|
col_row_children.push(
|
||||||
|
@ -229,7 +276,8 @@ impl Row {
|
||||||
.col_row_height_ratio(col_row_height_ratio)
|
.col_row_height_ratio(col_row_height_ratio)
|
||||||
.children(vec![BottomWidget::builder()
|
.children(vec![BottomWidget::builder()
|
||||||
.widget_type(BottomWidgetType::ProcSearch)
|
.widget_type(BottomWidgetType::ProcSearch)
|
||||||
.widget_id(*iter_id)
|
.widget_id(proc_search_id)
|
||||||
|
.parent_reflector(Some((WidgetDirection::Up, 1)))
|
||||||
.build()])
|
.build()])
|
||||||
.canvas_handle_height(true)
|
.canvas_handle_height(true)
|
||||||
.build(),
|
.build(),
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
// TODO: See if we can mock widget movements and test if they work
|
Loading…
Reference in New Issue