Skip to content

Commit

Permalink
feature: Adds tree view (#223)
Browse files Browse the repository at this point in the history
Adds a tree process view to bottom.

Currently uses a pretty jank method of column width setting, should get fixed in #225.
  • Loading branch information
ClementTsang authored Sep 7, 2020
1 parent 0d8572c commit eb8295c
Show file tree
Hide file tree
Showing 22 changed files with 713 additions and 236 deletions.
2 changes: 2 additions & 0 deletions .cargo-husky/hooks/pre-push
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/sh

set -e

echo "Running pre-push hook:"

echo "Executing: cargo +nightly clippy -- -D clippy::all"
Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@
"fract",
"gnueabihf",
"gotop",
"gotop's",
"gtop",
"haase",
"heim",
"hjkl",
"htop",
"indexmap",
"libc",
"markdownlint",
"memb",
Expand All @@ -62,6 +65,7 @@
"nvme",
"paren",
"pmem",
"ppid",
"prepush",
"processthreadsapi",
"regexes",
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
47 changes: 33 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
Binary file added assets/trees_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/trees_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/trees_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 67 additions & 44 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub use states::*;
use crate::{
canvas, constants,
utils::error::{BottomError, Result},
Pid,
};

pub mod data_farmer;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}
}
_ => {}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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 =
Expand All @@ -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);
Expand All @@ -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(),
Expand Down Expand Up @@ -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()
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/data_harvester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
Loading

0 comments on commit eb8295c

Please sign in to comment.