summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.cargo-husky/hooks/pre-push2
-rw-r--r--.vscode/settings.json4
-rw-r--r--CHANGELOG.md2
-rw-r--r--Cargo.lock17
-rw-r--r--Cargo.toml1
-rw-r--r--README.md47
-rw-r--r--assets/trees_1.pngbin0 -> 369812 bytes
-rw-r--r--assets/trees_2.pngbin0 -> 233142 bytes
-rw-r--r--assets/trees_3.pngbin0 -> 53619 bytes
-rw-r--r--src/app.rs111
-rw-r--r--src/app/data_harvester.rs2
-rw-r--r--src/app/data_harvester/processes.rs23
-rw-r--r--src/app/process_killer.rs11
-rw-r--r--src/app/states.rs12
-rw-r--r--src/canvas.rs1
-rw-r--r--src/canvas/canvas_colours.rs4
-rw-r--r--src/canvas/widgets/cpu_graph.rs12
-rw-r--r--src/canvas/widgets/process_table.rs80
-rw-r--r--src/constants.rs15
-rw-r--r--src/data_conversion.rs437
-rw-r--r--src/lib.rs162
-rw-r--r--src/utils/gen_util.rs6
22 files changed, 713 insertions, 236 deletions
diff --git a/.cargo-husky/hooks/pre-push b/.cargo-husky/hooks/pre-push
index a1e3616f..50105aa1 100755
--- a/.cargo-husky/hooks/pre-push
+++ b/.cargo-husky/hooks/pre-push
@@ -1,5 +1,7 @@
#!/bin/sh
+set -e
+
echo "Running pre-push hook:"
echo "Executing: cargo +nightly clippy -- -D clippy::all"
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8653ef8a..9a9b4df4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -46,10 +46,13 @@
"fract",
"gnueabihf",
"gotop",
+ "gotop's",
"gtop",
"haase",
"heim",
"hjkl",
+ "htop",
+ "indexmap",
"libc",
"markdownlint",
"memb",
@@ -62,6 +65,7 @@
"nvme",
"paren",
"pmem",
+ "ppid",
"prepush",
"processthreadsapi",
"regexes",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b3357bd..2fbcb362 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#220](https://github.com/ClementTsang/bottom/pull/220): Add ability to hide specific temperature and disk entries via config.
+- [#223](https://github.com/ClementTsang/bottom/pull/223): Add tree mode for processes.
+
### Changes
- [#213](https://github.com/ClementTsang/bottom/pull/213), [#214](https://github.com/ClementTsang/bottom/pull/214): Updated help descriptions, added auto-complete generation.
diff --git a/Cargo.lock b/Cargo.lock
index 20f2221f..7893cdbb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -150,6 +150,7 @@ dependencies = [
"fern",
"futures",
"heim",
+ "indexmap",
"itertools",
"lazy_static",
"libc",
@@ -535,6 +536,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
[[package]]
+name = "hashbrown"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
+
+[[package]]
name = "heim"
version = "0.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -724,6 +731,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e"
[[package]]
+name = "indexmap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index d8a83071..d2ec0dd0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,6 +32,7 @@ ctrlc = {version = "3.1", features = ["termination"]}
clap = "2.33"
dirs = "3.0.1"
futures = "0.3.5"
+indexmap = "1.6.0"
itertools = "0.9.0"
libc = "0.2"
regex = "1.3"
diff --git a/README.md b/README.md
index e504ea58..aa38c6a0 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ A cross-platform graphical process/system monitor with a customizable interface
- [Processes](#processes)
- [Process searching](#process-searching)
- [Process sorting](#process-sorting)
+ - [Tree mode](#tree-mode)
- [Zoom](#zoom)
- [Expanding](#expanding)
- [Basic mode](#basic-mode)
@@ -246,23 +247,24 @@ Run using `btm`.
| `s, F6` | Open process sort widget |
| `I` | Invert current sort |
| `%` | Toggle between values and percentages for memory usage |
+| `t`, `F5` | Toggle tree mode |
#### Process search bindings
-| | |
-| ------------ | -------------------------------------------- |
-| `Tab` | Toggle between searching by PID or name |
-| `Esc` | Close the search widget (retains the filter) |
-| `Ctrl-a` | Skip to the start of the search query |
-| `Ctrl-e` | Skip to the end of the search query |
-| `Ctrl-u` | Clear the current search query |
-| `Backspace` | Delete the character behind the cursor |
-| `Delete` | Delete the character at the cursor |
-| `Alt-c`/`F1` | Toggle matching case |
-| `Alt-w`/`F2` | Toggle matching the entire word |
-| `Alt-r`/`F3` | Toggle using regex |
-| `Left` | Move cursor left |
-| `Right` | Move cursor right |
+| | |
+| ------------- | -------------------------------------------- |
+| `Tab` | Toggle between searching by PID or name |
+| `Esc` | Close the search widget (retains the filter) |
+| `Ctrl-a` | Skip to the start of the search query |
+| `Ctrl-e` | Skip to the end of the search query |
+| `Ctrl-u` | Clear the current search query |
+| `Backspace` | Delete the character behind the cursor |
+| `Delete` | Delete the character at the cursor |
+| `Alt-c`, `F1` | Toggle matching case |
+| `Alt-w`, `F2` | Toggle matching the entire word |
+| `Alt-r`, `F3` | Toggle using regex |
+| `Left` | Move cursor left |
+| `Right` | Move cursor right |
### Process sort bindings
@@ -424,6 +426,23 @@ You can sort the processes list by any column you want by pressing `s` while on
![sorting](assets/sort.png)
+#### Tree mode
+
+Use `t` or `F5` to toggle tree mode in a process widget. This is somewhat similar to htop's tree
+mode.
+
+![Standard tree](assets/trees_1.png)
+
+Sorting works as well, but it is done per groups of siblings. For example, by CPU%:
+
+![Standard tree](assets/trees_2.png)
+
+You can also still filter processes. Branches that entirely do not match the query are pruned out,
+but if a branch contains an element that does match the query, any non-matching elements will instead
+just be greyed out, so the tree structure is still maintained:
+
+![Standard tree](assets/trees_3.png)
+
### Zoom
Using the `+`/`-` keys or the scroll wheel will move the current time intervals of the currently selected widget, and `=` to reset the zoom levels to the default.
diff --git a/assets/trees_1.png b/assets/trees_1.png
new file mode 100644
index 00000000..cfe7e6b2
--- /dev/null
+++ b/assets/trees_1.png
Binary files differ
diff --git a/assets/trees_2.png b/assets/trees_2.png
new file mode 100644
index 00000000..e6c1063c
--- /dev/null
+++ b/assets/trees_2.png
Binary files differ
diff --git a/assets/trees_3.png b/assets/trees_3.png
new file mode 100644
index 00000000..741116b1
--- /dev/null
+++ b/assets/trees_3.png
Binary files differ
diff --git a/src/app.rs b/src/app.rs
index 1aa390f7..4d07f4d0 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -13,6 +13,7 @@ pub use states::*;
use crate::{
canvas, constants,
utils::error::{BottomError, Result},
+ Pid,
};
pub mod data_farmer;
@@ -67,7 +68,7 @@ pub struct App {
pub dd_err: Option<String>,
#[builder(default, setter(skip))]
- to_delete_process_list: Option<(String, Vec<u32>)>,
+ to_delete_process_list: Option<(String, Vec<Pid>)>,
#[builder(default = false, setter(skip))]
pub is_frozen: bool,
@@ -265,37 +266,40 @@ impl App {
.proc_state
.get_mut_widget_state(self.current_widget.widget_id)
{
- // Toggles process widget grouping state
- proc_widget_state.is_grouped = !(proc_widget_state.is_grouped);
-
- // Forcefully switch off column if we were on it...
- if (proc_widget_state.is_grouped
- && proc_widget_state.process_sorting_type
- == data_harvester::processes::ProcessSorting::Pid)
- || (!proc_widget_state.is_grouped
+ // Do NOT allow when in tree mode!
+ if !proc_widget_state.is_tree_mode {
+ // Toggles process widget grouping state
+ proc_widget_state.is_grouped = !(proc_widget_state.is_grouped);
+
+ // Forcefully switch off column if we were on it...
+ if (proc_widget_state.is_grouped
&& proc_widget_state.process_sorting_type
- == data_harvester::processes::ProcessSorting::Count)
- {
- proc_widget_state.process_sorting_type =
- data_harvester::processes::ProcessSorting::CpuPercent; // Go back to default, negate PID for group
- proc_widget_state.process_sorting_reverse = true;
- }
+ == data_harvester::processes::ProcessSorting::Pid)
+ || (!proc_widget_state.is_grouped
+ && proc_widget_state.process_sorting_type
+ == data_harvester::processes::ProcessSorting::Count)
+ {
+ proc_widget_state.process_sorting_type =
+ data_harvester::processes::ProcessSorting::CpuPercent; // Go back to default, negate PID for group
+ proc_widget_state.is_process_sort_descending = true;
+ }
- proc_widget_state
- .columns
- .column_mapping
- .get_mut(&processes::ProcessSorting::State)
- .unwrap()
- .enabled = !(proc_widget_state.is_grouped);
+ proc_widget_state
+ .columns
+ .column_mapping
+ .get_mut(&processes::ProcessSorting::State)
+ .unwrap()
+ .enabled = !(proc_widget_state.is_grouped);
- proc_widget_state
- .columns
- .toggle(&processes::ProcessSorting::Count);
- proc_widget_state
- .columns
- .toggle(&processes::ProcessSorting::Pid);
+ proc_widget_state
+ .columns
+ .toggle(&processes::ProcessSorting::Count);
+ proc_widget_state
+ .columns
+ .toggle(&processes::ProcessSorting::Pid);
- self.proc_state.force_update = Some(self.current_widget.widget_id);
+ self.proc_state.force_update = Some(self.current_widget.widget_id);
+ }
}
}
_ => {}
@@ -384,8 +388,8 @@ impl App {
};
if let Some(proc_widget_state) = self.proc_state.get_mut_widget_state(widget_id) {
- proc_widget_state.process_sorting_reverse =
- !proc_widget_state.process_sorting_reverse;
+ proc_widget_state.is_process_sort_descending =
+ !proc_widget_state.is_process_sort_descending;
self.proc_state.force_update = Some(widget_id);
}
@@ -483,6 +487,24 @@ impl App {
}
}
+ pub fn toggle_tree_mode(&mut self) {
+ if let Some(proc_widget_state) = self
+ .proc_state
+ .widget_states
+ .get_mut(&(self.current_widget.widget_id))
+ {
+ proc_widget_state.is_tree_mode = !proc_widget_state.is_tree_mode;
+
+ if proc_widget_state.is_tree_mode {
+ // We enabled... set PID sort type to ascending.
+ proc_widget_state.process_sorting_type = processes::ProcessSorting::Pid;
+ proc_widget_state.is_process_sort_descending = false;
+ }
+
+ self.proc_state.force_update = Some(self.current_widget.widget_id);
+ }
+ }
+
/// One of two functions allowed to run while in a dialog...
pub fn on_enter(&mut self) {
if self.delete_dialog_state.is_showing_dd {
@@ -889,7 +911,7 @@ impl App {
if proc_widget_state.scroll_state.current_scroll_position
< corresponding_filtered_process_list.len()
{
- let current_process: (String, Vec<u32>);
+ let current_process: (String, Vec<Pid>);
if self.is_grouped(self.current_widget.widget_id) {
if let Some(process) = &corresponding_filtered_process_list
.get(proc_widget_state.scroll_state.current_scroll_position)
@@ -1069,13 +1091,13 @@ impl App {
{
match proc_widget_state.process_sorting_type {
processes::ProcessSorting::CpuPercent => {
- proc_widget_state.process_sorting_reverse =
- !proc_widget_state.process_sorting_reverse
+ proc_widget_state.is_process_sort_descending =
+ !proc_widget_state.is_process_sort_descending
}
_ => {
proc_widget_state.process_sorting_type =
processes::ProcessSorting::CpuPercent;
- proc_widget_state.process_sorting_reverse = true;
+ proc_widget_state.is_process_sort_descending = true;
}
}
self.proc_state.force_update = Some(self.current_widget.widget_id);
@@ -1092,13 +1114,13 @@ impl App {
{
match proc_widget_state.process_sorting_type {
processes::ProcessSorting::MemPercent => {
- proc_widget_state.process_sorting_reverse =
- !proc_widget_state.process_sorting_reverse
+ proc_widget_state.is_process_sort_descending =
+ !proc_widget_state.is_process_sort_descending
}
_ => {
proc_widget_state.process_sorting_type =
processes::ProcessSorting::MemPercent;
- proc_widget_state.process_sorting_reverse = true;
+ proc_widget_state.is_process_sort_descending = true;
}
}
self.proc_state.force_update = Some(self.current_widget.widget_id);
@@ -1116,13 +1138,13 @@ impl App {
if !proc_widget_state.is_grouped {
match proc_widget_state.process_sorting_type {
processes::ProcessSorting::Pid => {
- proc_widget_state.process_sorting_reverse =
- !proc_widget_state.process_sorting_reverse
+ proc_widget_state.is_process_sort_descending =
+ !proc_widget_state.is_process_sort_descending
}
_ => {
proc_widget_state.process_sorting_type =
processes::ProcessSorting::Pid;
- proc_widget_state.process_sorting_reverse = false;
+ proc_widget_state.is_process_sort_descending = false;
}
}
self.proc_state.force_update = Some(self.current_widget.widget_id);
@@ -1168,8 +1190,8 @@ impl App {
match proc_widget_state.process_sorting_type {
processes::ProcessSorting::ProcessName
| processes::ProcessSorting::Command => {
- proc_widget_state.process_sorting_reverse =
- !proc_widget_state.process_sorting_reverse
+ proc_widget_state.is_process_sort_descending =
+ !proc_widget_state.is_process_sort_descending
}
_ => {
proc_widget_state.process_sorting_type =
@@ -1178,7 +1200,7 @@ impl App {
} else {
processes::ProcessSorting::ProcessName
};
- proc_widget_state.process_sorting_reverse = false;
+ proc_widget_state.is_process_sort_descending = false;
}
}
self.proc_state.force_update = Some(self.current_widget.widget_id);
@@ -1194,6 +1216,7 @@ impl App {
'L' | 'D' => self.move_widget_selection(&WidgetDirection::Right),
'K' | 'W' => self.move_widget_selection(&WidgetDirection::Up),
'J' | 'S' => self.move_widget_selection(&WidgetDirection::Down),
+ 't' => self.toggle_tree_mode(),
'+' => self.zoom_in(),
'-' => self.zoom_out(),
'=' => self.reset_zoom(),
@@ -1228,7 +1251,7 @@ impl App {
}
}
- pub fn get_to_delete_processes(&self) -> Option<(String, Vec<u32>)> {
+ pub fn get_to_delete_processes(&self) -> Option<(String, Vec<Pid>)> {
self.to_delete_process_list.clone()
}
diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs
index eb5b0add..e7deca52 100644
--- a/src/app/data_harvester.rs
+++ b/src/app/data_harvester.rs
@@ -72,7 +72,7 @@ pub struct DataCollector {
pub data: Data,
sys: System,
#[cfg(target_os = "linux")]
- pid_mapping: HashMap<u32, processes::PrevProcDetails>,
+ pid_mapping: HashMap<crate::Pid, processes::PrevProcDetails>,
#[cfg(target_os = "linux")]
prev_idle: f64,
#[cfg(target_os = "linux")]
diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes.rs
index dcd6d647..8604bf8f 100644
--- a/src/app/data_harvester/processes.rs
+++ b/src/app/data_harvester/processes.rs
@@ -1,3 +1,4 @@
+use crate::Pid;
use std::path::PathBuf;
use sysinfo::ProcessStatus;
@@ -59,7 +60,8 @@ impl Default for ProcessSorting {
#[derive(Debug, Clone, Default)]
pub struct ProcessHarvest {
- pub pid: u32,
+ pub pid: Pid,
+ pub parent_pid: Option<Pid>, // Remember, parent_pid 0 is root...
pub cpu_usage_percent: f64,
pub mem_usage_percent: f64,
pub mem_usage_bytes: u64,
@@ -89,7 +91,7 @@ pub struct PrevProcDetails {
}
impl PrevProcDetails {
- pub fn new(pid: u32) -> Self {
+ pub fn new(pid: Pid) -> Self {
PrevProcDetails {
proc_io_path: PathBuf::from(format!("/proc/{}/io", pid)),
proc_exe_path: PathBuf::from(format!("/proc/{}/exe", pid)),
@@ -200,7 +202,7 @@ fn read_path_contents(path: &PathBuf) -> std::io::Result<String> {
#[cfg(target_os = "linux")]
fn get_linux_process_state(stat: &[&str]) -> (char, String) {
- // The -2 offset is because of us cutting off name + pid
+ // The -2 offset is because of us cutting off name + pid, normally it's 2
if let Some(first_char) = stat[0].chars().collect::<Vec<char>>().first() {
(
*first_char,
@@ -241,8 +243,8 @@ fn get_linux_cpu_usage(
#[allow(clippy::too_many_arguments)]
#[cfg(target_os = "linux")]
fn read_proc<S: core::hash::BuildHasher>(
- pid: u32, cpu_usage: f64, cpu_fraction: f64,
- pid_mapping: &mut HashMap<u32, PrevProcDetails, S>, use_current_cpu_total: bool,
+ pid: Pid, cpu_usage: f64, cpu_fraction: f64,
+ pid_mapping: &mut HashMap<Pid, PrevProcDetails, S>, use_current_cpu_total: bool,
time_difference_in_secs: u64, mem_total_kb: u64, page_file_kb: u64,
) -> error::Result<ProcessHarvest> {
let pid_stat = pid_mapping
@@ -282,6 +284,7 @@ fn read_proc<S: core::hash::BuildHasher>(
&mut pid_stat.cpu_time,
use_current_cpu_total,
)?;
+ let parent_pid = stat[1].parse::<Pid>().ok();
let (_vsize, rss) = get_linux_process_vsize_rss(&stat);
let mem_usage_kb = rss * page_file_kb;
let mem_usage_percent = mem_usage_kb as f64 / mem_total_kb as f64 * 100.0;
@@ -320,6 +323,7 @@ fn read_proc<S: core::hash::BuildHasher>(
Ok(ProcessHarvest {
pid,
+ parent_pid,
name,
command,
mem_usage_percent,
@@ -337,14 +341,16 @@ fn read_proc<S: core::hash::BuildHasher>(
#[cfg(target_os = "linux")]
pub fn linux_get_processes_list(
prev_idle: &mut f64, prev_non_idle: &mut f64,
- pid_mapping: &mut HashMap<u32, PrevProcDetails, RandomState>, use_current_cpu_total: bool,
+ pid_mapping: &mut HashMap<Pid, PrevProcDetails, RandomState>, use_current_cpu_total: bool,
time_difference_in_secs: u64, mem_total_kb: u64, page_file_kb: u64,
) -> crate::utils::error::Result<Vec<ProcessHarvest>> {
+ // TODO: [PROC THREADS] Add threads
+
if let Ok((cpu_usage, cpu_fraction)) = cpu_usage_calculation(prev_idle, prev_non_idle) {
let process_vector: Vec<ProcessHarvest> = std::fs::read_dir("/proc")?
.filter_map(|dir| {
if let Ok(dir) = dir {
- let pid = dir.file_name().to_string_lossy().trim().parse::<u32>();
+ let pid = dir.file_name().to_string_lossy().trim().parse::<Pid>();
if let Ok(pid) = pid {
// I skip checking if the path is also a directory, it's not needed I think?
if let Ok(process_object) = read_proc(
@@ -424,7 +430,8 @@ pub fn windows_macos_get_processes_list(
let disk_usage = process_val.disk_usage();
process_vector.push(ProcessHarvest {
- pid: process_val.pid() as u32,
+ pid: process_val.pid(),
+ parent_pid: process_val.parent(),
name,
command,
mem_usage_percent: if mem_total_kb > 0 {
diff --git a/src/app/process_killer.rs b/src/app/process_killer.rs
index cb0662bc..e367d99c 100644
--- a/src/app/process_killer.rs
+++ b/src/app/process_killer.rs
@@ -10,6 +10,7 @@ use winapi::{
/// This file is meant to house (OS specific) implementations on how to kill processes.
use crate::utils::error::BottomError;
+use crate::Pid;
#[cfg(target_os = "windows")]
struct Process(HANDLE);
@@ -31,9 +32,9 @@ impl Process {
}
/// Kills a process, given a PID.
-pub fn kill_process_given_pid(pid: u32) -> crate::utils::error::Result<()> {
- if cfg!(target_os = "linux") || cfg!(target_os = "macos") {
- #[cfg(any(target_os = "linux", target_os = "macos"))]
+pub fn kill_process_given_pid(pid: Pid) -> crate::utils::error::Result<()> {
+ if cfg!(target_family = "unix") {
+ #[cfg(any(target_family = "unix"))]
{
let output = unsafe { libc::kill(pid as i32, libc::SIGTERM) };
if output != 0 {
@@ -59,8 +60,8 @@ pub fn kill_process_given_pid(pid: u32) -> crate::utils::error::Result<()> {
};
}
}
- } else if cfg!(target_os = "windows") {
- #[cfg(target_os = "windows")]
+ } else if cfg!(target_family = "windows") {
+ #[cfg(target_family = "windows")]
{
let process = Process::open(pid as DWORD)?;
process.kill()?;
diff --git a/src/app/states.rs b/src/app/states.rs
index cf9123db..318b2175 100644
--- a/src/app/states.rs
+++ b/src/app/states.rs
@@ -9,6 +9,7 @@ use crate::{
constants,
data_harvester::processes::{self, ProcessSorting},
};
+use ProcessSorting::*;
#[derive(Debug)]
pub enum ScrollDirection {
@@ -159,7 +160,6 @@ pub struct ProcColumn {
impl Default for ProcColumn {
fn default() -> Self {
- use ProcessSorting::*;
let ordered_columns = vec![
Count,
Pid,
@@ -352,11 +352,12 @@ pub struct ProcWidgetState {
pub is_grouped: bool,
pub scroll_state: AppScrollWidgetState,
pub process_sorting_type: processes::ProcessSorting,
- pub process_sorting_reverse: bool,
+ pub is_process_sort_descending: bool,
pub is_using_command: bool,
pub current_column_index: usize,
pub is_sort_open: bool,
pub columns: ProcColumn,
+ pub is_tree_mode: bool,
}
impl ProcWidgetState {
@@ -390,11 +391,12 @@ impl ProcWidgetState {
is_grouped,
scroll_state: AppScrollWidgetState::default(),
process_sorting_type,
- process_sorting_reverse: true,
+ is_process_sort_descending: true,
is_using_command: false,
current_column_index: 0,
is_sort_open: false,
columns,
+ is_tree_mode: false,
}
}
@@ -422,7 +424,7 @@ impl ProcWidgetState {
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);
+ self.is_process_sort_descending = !(self.is_process_sort_descending);
} else {
self.process_sorting_type = new_sort_type.clone();
match self.process_sorting_type {
@@ -431,7 +433,7 @@ impl ProcWidgetState {
| ProcessSorting::ProcessName
| ProcessSorting::Command => {
// Also invert anything that uses alphabetical sorting by default.
- self.process_sorting_reverse = false;
+ self.is_process_sort_descending = false;
}
_ => {}
}
diff --git a/src/canvas.rs b/src/canvas.rs
index 61f74e4f..e276e097 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -42,7 +42,6 @@ pub struct DisplayableData {
pub disk_data: Vec<Vec<String>>,
pub temp_sensor_data: Vec<Vec<String>>,
pub single_process_data: Vec<ConvertedProcessData>, // Contains single process data
- pub process_data: Vec<ConvertedProcessData>, // Not the final value, may be grouped or single
pub finalized_process_data_map: HashMap<u64, Vec<ConvertedProcessData>>, // What's actually displayed
pub mem_label_percent: String,
pub swap_label_percent: String,
diff --git a/src/canvas/canvas_colours.rs b/src/canvas/canvas_colours.rs
index 98932d5d..2876e24b 100644
--- a/src/canvas/canvas_colours.rs
+++ b/src/canvas/canvas_colours.rs
@@ -28,6 +28,7 @@ pub struct CanvasColours {
// Full, Medium, Low
pub battery_bar_styles: Vec<Style>,
pub invalid_query_style: Style,
+ pub disabled_text_style: Style,
}
impl Default for CanvasColours {
@@ -63,7 +64,8 @@ impl Default for CanvasColours {
Style::default().fg(Color::Green),
Style::default().fg(Color::Green),
],
- invalid_query_style: tui::style::Style::default().fg(tui::style::Color::Red),
+ invalid_query_style: Style::default().fg(tui::style::Color::Red),
+ disabled_text_style: Style::default().fg(Color::DarkGray),
}
}
}
diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs
index e000b66f..86999345 100644
--- a/src/canvas/widgets/cpu_graph.rs
+++ b/src/canvas/widgets/cpu_graph.rs
@@ -349,13 +349,11 @@ impl CpuGraphWidget for Painter {
get_variable_intrinsic_widths(width as u16, &width_ratios, &CPU_LEGEND_HEADER_LENS);
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
- let (border_and_title_style, highlight_style) = if is_on_widget {
- (
- self.colours.highlighted_border_style,
- self.colours.currently_selected_text_style,
- )
+ // Note we don't set highlight_style, as it should always be shown for this widget.
+ let border_and_title_style = if is_on_widget {
+ self.colours.highlighted_border_style
} else {
- (self.colours.border_style, self.colours.text_style)
+ self.colours.border_style
};
// Draw
@@ -367,7 +365,7 @@ impl CpuGraphWidget for Painter {
.border_style(border_and_title_style),
)
.header_style(self.colours.table_header_style)
- .highlight_style(highlight_style)
+ .highlight_style(self.colours.currently_selected_text_style)
.widths(
&(intrinsic_widths
.iter()
diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs
index 8d6ea0bb..cce7835c 100644
--- a/src/canvas/widgets/process_table.rs
+++ b/src/canvas/widgets/process_table.rs
@@ -36,7 +36,7 @@ pub trait ProcessTableWidget {
widget_id: u64,
);
- /// Draws the process sort box.
+ /// Draws the process search field.
/// - `widget_id` represents the widget ID of the search box itself --- NOT the process widget
/// state that is stored.
///
@@ -173,15 +173,6 @@ impl ProcessTableWidget for Painter {
.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 table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
0
} else {
@@ -217,39 +208,52 @@ impl ProcessTableWidget for Painter {
// Draw!
let is_proc_widget_grouped = proc_widget_state.is_grouped;
let is_using_command = proc_widget_state.is_using_command;
+ let is_tree = proc_widget_state.is_tree_mode;
let mem_enabled = proc_widget_state.columns.is_enabled(&ProcessSorting::Mem);
+
+ // FIXME: [PROC OPTIMIZE] This can definitely be optimized; string references work fine here!
let process_rows = sliced_vec.iter().map(|process| {
- Row::Data(
- vec![
- if is_proc_widget_grouped {
- process.group_pids.len().to_string()
- } else {
- process.pid.to_string()
- },
- if is_using_command {
- process.command.clone()
- } else {
-