mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-25 14:44:39 +02:00
feature: Add collapsible tree entries (#304)
Adds collapsible trees to the tree mode for processes. These can be toggled via the + or - keys and the mouse by clicking on a selected entry.
This commit is contained in:
parent
e43456207b
commit
669b245367
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -48,6 +48,7 @@
|
|||||||
"cvars",
|
"cvars",
|
||||||
"czvf",
|
"czvf",
|
||||||
"denylist",
|
"denylist",
|
||||||
|
"eselect",
|
||||||
"fedoracentos",
|
"fedoracentos",
|
||||||
"fpath",
|
"fpath",
|
||||||
"fract",
|
"fract",
|
||||||
|
@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.5.1] - Unreleased
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
## [0.5.0] - Unreleased
|
## [0.5.0] - Unreleased
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
@ -137,6 +137,7 @@ sudo dnf install bottom
|
|||||||
### Gentoo
|
### Gentoo
|
||||||
|
|
||||||
Available in [dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq)
|
Available in [dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo eselect repository enable dm9pZCAq
|
sudo eselect repository enable dm9pZCAq
|
||||||
sudo emerge --sync dm9pZCAq
|
sudo emerge --sync dm9pZCAq
|
||||||
@ -232,7 +233,6 @@ Run using `btm`.
|
|||||||
--hide_time Completely hides the time scaling.
|
--hide_time Completely hides the time scaling.
|
||||||
-k, --kelvin Sets the temperature type to Kelvin.
|
-k, --kelvin Sets the temperature type to Kelvin.
|
||||||
-l, --left_legend Puts the CPU chart legend to the left side.
|
-l, --left_legend Puts the CPU chart legend to the left side.
|
||||||
--no_write Disables writing to the config file.
|
|
||||||
-r, --rate <MS> Sets a refresh rate in ms.
|
-r, --rate <MS> Sets a refresh rate in ms.
|
||||||
-R, --regex Enables regex by default.
|
-R, --regex Enables regex by default.
|
||||||
-d, --time_delta <MS> The amount in ms changed upon zooming.
|
-d, --time_delta <MS> The amount in ms changed upon zooming.
|
||||||
@ -401,6 +401,12 @@ Note that the `and` operator takes precedence over the `or` operator.
|
|||||||
| ------ | --------------------------------------------------------------------- |
|
| ------ | --------------------------------------------------------------------- |
|
||||||
| Scroll | Scrolling over an CPU core/average shows only that entry on the chart |
|
| Scroll | Scrolling over an CPU core/average shows only that entry on the chart |
|
||||||
|
|
||||||
|
#### Process bindings
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| ----- | --------------------------------------------------------------------------------------------------- |
|
||||||
|
| Click | If in tree mode and you click on a selected entry, it toggles whether the branch is expanded or not |
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
As yet _another_ process/system visualization and management application, bottom supports the typical features:
|
As yet _another_ process/system visualization and management application, bottom supports the typical features:
|
||||||
@ -742,6 +748,7 @@ Thanks to all contributors ([emoji key](https://allcontributors.org/docs/en/emoj
|
|||||||
|
|
||||||
<!-- markdownlint-enable -->
|
<!-- markdownlint-enable -->
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
129
src/app.rs
129
src/app.rs
@ -697,7 +697,7 @@ impl App {
|
|||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.is_enabled
|
.is_enabled
|
||||||
&& proc_widget_state.get_cursor_position()
|
&& proc_widget_state.get_search_cursor_position()
|
||||||
< proc_widget_state
|
< proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
@ -708,13 +708,13 @@ impl App {
|
|||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.current_search_query
|
.current_search_query
|
||||||
.remove(proc_widget_state.get_cursor_position());
|
.remove(proc_widget_state.get_search_cursor_position());
|
||||||
|
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.grapheme_cursor = GraphemeCursor::new(
|
.grapheme_cursor = GraphemeCursor::new(
|
||||||
proc_widget_state.get_cursor_position(),
|
proc_widget_state.get_search_cursor_position(),
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
@ -746,21 +746,22 @@ impl App {
|
|||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.is_enabled
|
.is_enabled
|
||||||
&& proc_widget_state.get_cursor_position() > 0
|
&& proc_widget_state.get_search_cursor_position() > 0
|
||||||
{
|
{
|
||||||
proc_widget_state.search_walk_back(proc_widget_state.get_cursor_position());
|
proc_widget_state
|
||||||
|
.search_walk_back(proc_widget_state.get_search_cursor_position());
|
||||||
|
|
||||||
let removed_char = proc_widget_state
|
let removed_char = proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.current_search_query
|
.current_search_query
|
||||||
.remove(proc_widget_state.get_cursor_position());
|
.remove(proc_widget_state.get_search_cursor_position());
|
||||||
|
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.grapheme_cursor = GraphemeCursor::new(
|
.grapheme_cursor = GraphemeCursor::new(
|
||||||
proc_widget_state.get_cursor_position(),
|
proc_widget_state.get_search_cursor_position(),
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
@ -838,15 +839,15 @@ impl App {
|
|||||||
.get_mut_widget_state(self.current_widget.widget_id - 1)
|
.get_mut_widget_state(self.current_widget.widget_id - 1)
|
||||||
{
|
{
|
||||||
if is_in_search_widget {
|
if is_in_search_widget {
|
||||||
let prev_cursor = proc_widget_state.get_cursor_position();
|
let prev_cursor = proc_widget_state.get_search_cursor_position();
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.search_walk_back(proc_widget_state.get_cursor_position());
|
.search_walk_back(proc_widget_state.get_search_cursor_position());
|
||||||
if proc_widget_state.get_cursor_position() < prev_cursor {
|
if proc_widget_state.get_search_cursor_position() < prev_cursor {
|
||||||
let str_slice = &proc_widget_state
|
let str_slice = &proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.current_search_query
|
.current_search_query
|
||||||
[proc_widget_state.get_cursor_position()..prev_cursor];
|
[proc_widget_state.get_search_cursor_position()..prev_cursor];
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
@ -905,15 +906,16 @@ impl App {
|
|||||||
.get_mut_widget_state(self.current_widget.widget_id - 1)
|
.get_mut_widget_state(self.current_widget.widget_id - 1)
|
||||||
{
|
{
|
||||||
if is_in_search_widget {
|
if is_in_search_widget {
|
||||||
let prev_cursor = proc_widget_state.get_cursor_position();
|
let prev_cursor = proc_widget_state.get_search_cursor_position();
|
||||||
proc_widget_state
|
proc_widget_state.search_walk_forward(
|
||||||
.search_walk_forward(proc_widget_state.get_cursor_position());
|
proc_widget_state.get_search_cursor_position(),
|
||||||
if proc_widget_state.get_cursor_position() > prev_cursor {
|
);
|
||||||
|
if proc_widget_state.get_search_cursor_position() > prev_cursor {
|
||||||
let str_slice = &proc_widget_state
|
let str_slice = &proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.current_search_query
|
.current_search_query
|
||||||
[prev_cursor..proc_widget_state.get_cursor_position()];
|
[prev_cursor..proc_widget_state.get_search_cursor_position()];
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
@ -1124,13 +1126,13 @@ impl App {
|
|||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.current_search_query
|
.current_search_query
|
||||||
.insert(proc_widget_state.get_cursor_position(), caught_char);
|
.insert(proc_widget_state.get_search_cursor_position(), caught_char);
|
||||||
|
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.grapheme_cursor = GraphemeCursor::new(
|
.grapheme_cursor = GraphemeCursor::new(
|
||||||
proc_widget_state.get_cursor_position(),
|
proc_widget_state.get_search_cursor_position(),
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
@ -1139,7 +1141,7 @@ impl App {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.search_walk_forward(proc_widget_state.get_cursor_position());
|
.search_walk_forward(proc_widget_state.get_search_cursor_position());
|
||||||
|
|
||||||
proc_widget_state
|
proc_widget_state
|
||||||
.process_search_state
|
.process_search_state
|
||||||
@ -1371,8 +1373,8 @@ impl App {
|
|||||||
'K' | 'W' => self.move_widget_selection(&WidgetDirection::Up),
|
'K' | 'W' => self.move_widget_selection(&WidgetDirection::Up),
|
||||||
'J' | 'S' => self.move_widget_selection(&WidgetDirection::Down),
|
'J' | 'S' => self.move_widget_selection(&WidgetDirection::Down),
|
||||||
't' => self.toggle_tree_mode(),
|
't' => self.toggle_tree_mode(),
|
||||||
'+' => self.zoom_in(),
|
'+' => self.on_plus(),
|
||||||
'-' => self.zoom_out(),
|
'-' => self.on_minus(),
|
||||||
'=' => self.reset_zoom(),
|
'=' => self.reset_zoom(),
|
||||||
'e' => self.toggle_expand_widget(),
|
'e' => self.toggle_expand_widget(),
|
||||||
's' => self.toggle_sort(),
|
's' => self.toggle_sort(),
|
||||||
@ -2058,7 +2060,9 @@ impl App {
|
|||||||
pub fn decrement_position_count(&mut self) {
|
pub fn decrement_position_count(&mut self) {
|
||||||
if !self.ignore_normal_keybinds() {
|
if !self.ignore_normal_keybinds() {
|
||||||
match self.current_widget.widget_type {
|
match self.current_widget.widget_type {
|
||||||
BottomWidgetType::Proc => self.increment_process_position(-1),
|
BottomWidgetType::Proc => {
|
||||||
|
self.increment_process_position(-1);
|
||||||
|
}
|
||||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(-1),
|
BottomWidgetType::ProcSort => self.increment_process_sort_position(-1),
|
||||||
BottomWidgetType::Temp => self.increment_temp_position(-1),
|
BottomWidgetType::Temp => self.increment_temp_position(-1),
|
||||||
BottomWidgetType::Disk => self.increment_disk_position(-1),
|
BottomWidgetType::Disk => self.increment_disk_position(-1),
|
||||||
@ -2071,7 +2075,9 @@ impl App {
|
|||||||
pub fn increment_position_count(&mut self) {
|
pub fn increment_position_count(&mut self) {
|
||||||
if !self.ignore_normal_keybinds() {
|
if !self.ignore_normal_keybinds() {
|
||||||
match self.current_widget.widget_type {
|
match self.current_widget.widget_type {
|
||||||
BottomWidgetType::Proc => self.increment_process_position(1),
|
BottomWidgetType::Proc => {
|
||||||
|
self.increment_process_position(1);
|
||||||
|
}
|
||||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(1),
|
BottomWidgetType::ProcSort => self.increment_process_sort_position(1),
|
||||||
BottomWidgetType::Temp => self.increment_temp_position(1),
|
BottomWidgetType::Temp => self.increment_temp_position(1),
|
||||||
BottomWidgetType::Disk => self.increment_disk_position(1),
|
BottomWidgetType::Disk => self.increment_disk_position(1),
|
||||||
@ -2128,7 +2134,8 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_process_position(&mut self, num_to_change_by: i64) {
|
/// Returns the new position.
|
||||||
|
fn increment_process_position(&mut self, num_to_change_by: i64) -> Option<usize> {
|
||||||
if let Some(proc_widget_state) = self
|
if let Some(proc_widget_state) = self
|
||||||
.proc_state
|
.proc_state
|
||||||
.get_mut_widget_state(self.current_widget.widget_id)
|
.get_mut_widget_state(self.current_widget.widget_id)
|
||||||
@ -2144,6 +2151,8 @@ impl App {
|
|||||||
{
|
{
|
||||||
proc_widget_state.scroll_state.current_scroll_position =
|
proc_widget_state.scroll_state.current_scroll_position =
|
||||||
(current_posn as i64 + num_to_change_by) as usize;
|
(current_posn as i64 + num_to_change_by) as usize;
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2152,7 +2161,11 @@ impl App {
|
|||||||
} else {
|
} else {
|
||||||
proc_widget_state.scroll_state.scroll_direction = ScrollDirection::Down;
|
proc_widget_state.scroll_state.scroll_direction = ScrollDirection::Down;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Some(proc_widget_state.scroll_state.current_scroll_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_temp_position(&mut self, num_to_change_by: i64) {
|
fn increment_temp_position(&mut self, num_to_change_by: i64) {
|
||||||
@ -2245,6 +2258,53 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_plus(&mut self) {
|
||||||
|
if let BottomWidgetType::Proc = self.current_widget.widget_type {
|
||||||
|
// Toggle collapsing if tree
|
||||||
|
self.toggle_collapsing_process_branch();
|
||||||
|
} else {
|
||||||
|
self.zoom_in();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_minus(&mut self) {
|
||||||
|
if let BottomWidgetType::Proc = self.current_widget.widget_type {
|
||||||
|
// Toggle collapsing if tree
|
||||||
|
self.toggle_collapsing_process_branch();
|
||||||
|
} else {
|
||||||
|
self.zoom_out();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_collapsing_process_branch(&mut self) {
|
||||||
|
if let Some(proc_widget_state) = self
|
||||||
|
.proc_state
|
||||||
|
.widget_states
|
||||||
|
.get_mut(&self.current_widget.widget_id)
|
||||||
|
{
|
||||||
|
let current_posn = proc_widget_state.scroll_state.current_scroll_position;
|
||||||
|
|
||||||
|
if let Some(displayed_process_list) = self
|
||||||
|
.canvas_data
|
||||||
|
.finalized_process_data_map
|
||||||
|
.get(&self.current_widget.widget_id)
|
||||||
|
{
|
||||||
|
if let Some(corresponding_process) = displayed_process_list.get(current_posn) {
|
||||||
|
let corresponding_pid = corresponding_process.pid;
|
||||||
|
|
||||||
|
if let Some(process_data) = self
|
||||||
|
.canvas_data
|
||||||
|
.single_process_data
|
||||||
|
.get_mut(&corresponding_pid)
|
||||||
|
{
|
||||||
|
process_data.is_collapsed_entry = !process_data.is_collapsed_entry;
|
||||||
|
self.proc_state.force_update = Some(self.current_widget.widget_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn zoom_out(&mut self) {
|
fn zoom_out(&mut self) {
|
||||||
match self.current_widget.widget_type {
|
match self.current_widget.widget_type {
|
||||||
BottomWidgetType::Cpu => {
|
BottomWidgetType::Cpu => {
|
||||||
@ -2464,11 +2524,12 @@ impl App {
|
|||||||
// Pretty dead simple - iterate through the widget map and go to the widget where the click
|
// Pretty dead simple - iterate through the widget map and go to the widget where the click
|
||||||
// is within.
|
// is within.
|
||||||
|
|
||||||
// TODO: [MOUSE] double click functionality...?
|
|
||||||
// TODO: [REFACTOR] might want to refactor this, it's ugly as sin.
|
// TODO: [REFACTOR] might want to refactor this, it's ugly as sin.
|
||||||
// TODO: [REFACTOR] Might wanna refactor ALL state things in general, currently everything
|
// TODO: [REFACTOR] Might wanna refactor ALL state things in general, currently everything
|
||||||
// is grouped up as an app state. We should separate stuff like event state and gui state and etc.
|
// is grouped up as an app state. We should separate stuff like event state and gui state and etc.
|
||||||
|
|
||||||
|
// TODO: [MOUSE] double click functionality...? We would do this above all other actions and SC if needed.
|
||||||
|
|
||||||
// Short circuit if we're in basic table... we might have to handle the basic table arrow
|
// Short circuit if we're in basic table... we might have to handle the basic table arrow
|
||||||
// case here...
|
// case here...
|
||||||
if let Some(bt) = &mut self.basic_table_widget_state {
|
if let Some(bt) = &mut self.basic_table_widget_state {
|
||||||
@ -2620,9 +2681,25 @@ impl App {
|
|||||||
if let Some(visual_index) =
|
if let Some(visual_index) =
|
||||||
proc_widget_state.scroll_state.table_state.selected()
|
proc_widget_state.scroll_state.table_state.selected()
|
||||||
{
|
{
|
||||||
self.increment_process_position(
|
// If in tree mode, also check to see if this click is on
|
||||||
|
// the same entry as the already selected one - if it is,
|
||||||
|
// then we minimize.
|
||||||
|
|
||||||
|
let previous_scroll_position =
|
||||||
|
proc_widget_state.scroll_state.current_scroll_position;
|
||||||
|
let is_tree_mode = proc_widget_state.is_tree_mode;
|
||||||
|
|
||||||
|
let new_position = self.increment_process_position(
|
||||||
offset_clicked_entry as i64 - visual_index as i64,
|
offset_clicked_entry as i64 - visual_index as i64,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if is_tree_mode {
|
||||||
|
if let Some(new_position) = new_position {
|
||||||
|
if previous_scroll_position == new_position {
|
||||||
|
self.toggle_collapsing_process_branch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -488,7 +488,7 @@ impl ProcWidgetState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cursor_position(&self) -> usize {
|
pub fn get_search_cursor_position(&self) -> usize {
|
||||||
self.process_search_state
|
self.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.grapheme_cursor
|
.grapheme_cursor
|
||||||
|
@ -25,6 +25,7 @@ use crate::{
|
|||||||
options::Config,
|
options::Config,
|
||||||
utils::error,
|
utils::error,
|
||||||
utils::error::BottomError,
|
utils::error::BottomError,
|
||||||
|
Pid,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod canvas_colours;
|
mod canvas_colours;
|
||||||
@ -46,9 +47,9 @@ pub struct DisplayableData {
|
|||||||
pub network_data_tx: Vec<Point>,
|
pub network_data_tx: Vec<Point>,
|
||||||
pub disk_data: Vec<Vec<String>>,
|
pub disk_data: Vec<Vec<String>>,
|
||||||
pub temp_sensor_data: Vec<Vec<String>>,
|
pub temp_sensor_data: Vec<Vec<String>>,
|
||||||
pub single_process_data: Vec<ConvertedProcessData>, // Contains single process data
|
pub single_process_data: HashMap<Pid, ConvertedProcessData>, // Contains single process data, key is PID
|
||||||
pub finalized_process_data_map: HashMap<u64, Vec<ConvertedProcessData>>, // What's actually displayed
|
pub finalized_process_data_map: HashMap<u64, Vec<ConvertedProcessData>>, // What's actually displayed, key is the widget ID.
|
||||||
pub stringified_process_data_map: HashMap<u64, Vec<(Vec<(String, Option<String>)>, bool)>>, // Represents the row and whether it is disabled
|
pub stringified_process_data_map: HashMap<u64, Vec<(Vec<(String, Option<String>)>, bool)>>, // Represents the row and whether it is disabled, key is the widget ID
|
||||||
pub mem_label_percent: String,
|
pub mem_label_percent: String,
|
||||||
pub swap_label_percent: String,
|
pub swap_label_percent: String,
|
||||||
pub mem_label_frac: String,
|
pub mem_label_frac: String,
|
||||||
|
@ -506,7 +506,7 @@ impl ProcessTableWidget for Painter {
|
|||||||
let search_title = "> ";
|
let search_title = "> ";
|
||||||
|
|
||||||
let num_chars_for_text = search_title.len();
|
let num_chars_for_text = search_title.len();
|
||||||
let cursor_position = proc_widget_state.get_cursor_position();
|
let cursor_position = proc_widget_state.get_search_cursor_position();
|
||||||
let current_cursor_position = proc_widget_state.get_char_cursor_position();
|
let current_cursor_position = proc_widget_state.get_char_cursor_position();
|
||||||
|
|
||||||
let start_position: usize = get_search_start_position(
|
let start_position: usize = get_search_start_position(
|
||||||
|
16
src/clap.rs
16
src/clap.rs
@ -143,13 +143,13 @@ Completely hides the time scaling from being shown.\n\n",
|
|||||||
"\
|
"\
|
||||||
Puts the CPU chart legend to the left side rather than the right side.\n\n",
|
Puts the CPU chart legend to the left side rather than the right side.\n\n",
|
||||||
);
|
);
|
||||||
let no_write = Arg::with_name("no_write")
|
// let no_write = Arg::with_name("no_write")
|
||||||
.long("no_write")
|
// .long("no_write")
|
||||||
.help("Disables writing to the config file.")
|
// .help("Disables writing to the config file.")
|
||||||
.long_help(
|
// .long_help(
|
||||||
"\
|
// "\
|
||||||
Disables config changes in-app from writing to the config file.",
|
// Disables config changes in-app from writing to the config file.",
|
||||||
);
|
// );
|
||||||
let regex = Arg::with_name("regex")
|
let regex = Arg::with_name("regex")
|
||||||
.short("R")
|
.short("R")
|
||||||
.long("regex")
|
.long("regex")
|
||||||
@ -355,7 +355,7 @@ The minimum is 1s (1000), and defaults to 15s (15000).\n\n\n",
|
|||||||
.arg(hide_table_gap)
|
.arg(hide_table_gap)
|
||||||
.arg(hide_time)
|
.arg(hide_time)
|
||||||
.arg(left_legend)
|
.arg(left_legend)
|
||||||
.arg(no_write)
|
// .arg(no_write)
|
||||||
.arg(rate)
|
.arg(rate)
|
||||||
.arg(regex)
|
.arg(regex)
|
||||||
.arg(time_delta)
|
.arg(time_delta)
|
||||||
|
@ -158,7 +158,6 @@ lazy_static! {
|
|||||||
// };
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: [HELP] I wanna update this before release... it's missing mouse too.
|
|
||||||
// Help text
|
// Help text
|
||||||
pub const HELP_CONTENTS_TEXT: [&str; 8] = [
|
pub const HELP_CONTENTS_TEXT: [&str; 8] = [
|
||||||
"Press the corresponding numbers to jump to the section, or scroll:",
|
"Press the corresponding numbers to jump to the section, or scroll:",
|
||||||
@ -171,7 +170,9 @@ pub const HELP_CONTENTS_TEXT: [&str; 8] = [
|
|||||||
"7 - Basic memory widget",
|
"7 - Basic memory widget",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const GENERAL_HELP_TEXT: [&str; 29] = [
|
// TODO [Help]: Search in help?
|
||||||
|
// TODO [Help]: Move to using tables for easier formatting?
|
||||||
|
pub const GENERAL_HELP_TEXT: [&str; 30] = [
|
||||||
"1 - General",
|
"1 - General",
|
||||||
"q, Ctrl-c Quit",
|
"q, Ctrl-c Quit",
|
||||||
"Esc Close dialog windows, search, widgets, or exit expanded mode",
|
"Esc Close dialog windows, search, widgets, or exit expanded mode",
|
||||||
@ -201,6 +202,7 @@ pub const GENERAL_HELP_TEXT: [&str; 29] = [
|
|||||||
"- Zoom out on chart (increase time range)",
|
"- Zoom out on chart (increase time range)",
|
||||||
"= Reset zoom",
|
"= Reset zoom",
|
||||||
"Mouse scroll Scroll through the tables or zoom in/out of charts by scrolling up/down",
|
"Mouse scroll Scroll through the tables or zoom in/out of charts by scrolling up/down",
|
||||||
|
"Mouse click Selects the clicked widget, table entry, dialog option, or tab",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const CPU_HELP_TEXT: [&str; 2] = [
|
pub const CPU_HELP_TEXT: [&str; 2] = [
|
||||||
@ -208,9 +210,7 @@ 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",
|
||||||
];
|
];
|
||||||
|
|
||||||
// TODO [Help]: Search in help?
|
pub const PROCESS_HELP_TEXT: [&str; 14] = [
|
||||||
// TODO [Help]: Move to using tables for easier formatting?
|
|
||||||
pub const PROCESS_HELP_TEXT: [&str; 13] = [
|
|
||||||
"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",
|
||||||
@ -224,6 +224,7 @@ pub const PROCESS_HELP_TEXT: [&str; 13] = [
|
|||||||
"I Invert current sort",
|
"I Invert current sort",
|
||||||
"% Toggle between values and percentages for memory usage",
|
"% Toggle between values and percentages for memory usage",
|
||||||
"t, F5 Toggle tree mode",
|
"t, F5 Toggle tree mode",
|
||||||
|
"+, -, click Collapse/expand a branch while in tree mode",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const SEARCH_HELP_TEXT: [&str; 46] = [
|
pub const SEARCH_HELP_TEXT: [&str; 46] = [
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use data_harvester::processes::ProcessSorting;
|
use data_harvester::processes::ProcessSorting;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
|
||||||
/// Point is of time, data
|
/// Point is of time, data
|
||||||
type Point = (f64, f64);
|
type Point = (f64, f64);
|
||||||
@ -66,6 +66,8 @@ pub struct ConvertedProcessData {
|
|||||||
pub process_description_prefix: Option<String>,
|
pub process_description_prefix: Option<String>,
|
||||||
/// Whether to mark this process entry as disabled (mostly for tree mode).
|
/// Whether to mark this process entry as disabled (mostly for tree mode).
|
||||||
pub is_disabled_entry: bool,
|
pub is_disabled_entry: bool,
|
||||||
|
/// Whether this entry is collapsed, hiding all its children (for tree mode).
|
||||||
|
pub is_collapsed_entry: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
@ -194,9 +196,8 @@ pub fn convert_cpu_data_points(
|
|||||||
for (itx, cpu) in data.cpu_data.iter().enumerate() {
|
for (itx, cpu) in data.cpu_data.iter().enumerate() {
|
||||||
// Check if the vector exists yet
|
// Check if the vector exists yet
|
||||||
if cpu_data_vector.len() <= itx {
|
if cpu_data_vector.len() <= itx {
|
||||||
let mut new_cpu_data = ConvertedCpuData::default();
|
let new_cpu_data = ConvertedCpuData {
|
||||||
new_cpu_data.cpu_name = if let Some(cpu_harvest) = current_data.cpu_harvest.get(itx)
|
cpu_name: if let Some(cpu_harvest) = current_data.cpu_harvest.get(itx) {
|
||||||
{
|
|
||||||
if let Some(cpu_count) = cpu_harvest.cpu_count {
|
if let Some(cpu_count) = cpu_harvest.cpu_count {
|
||||||
format!("{}{}", cpu_harvest.cpu_prefix, cpu_count)
|
format!("{}{}", cpu_harvest.cpu_prefix, cpu_count)
|
||||||
} else {
|
} else {
|
||||||
@ -204,9 +205,8 @@ pub fn convert_cpu_data_points(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String::default()
|
String::default()
|
||||||
};
|
},
|
||||||
new_cpu_data.short_cpu_name =
|
short_cpu_name: if let Some(cpu_harvest) = current_data.cpu_harvest.get(itx) {
|
||||||
if let Some(cpu_harvest) = current_data.cpu_harvest.get(itx) {
|
|
||||||
if let Some(cpu_count) = cpu_harvest.cpu_count {
|
if let Some(cpu_count) = cpu_harvest.cpu_count {
|
||||||
cpu_count.to_string()
|
cpu_count.to_string()
|
||||||
} else {
|
} else {
|
||||||
@ -214,7 +214,10 @@ pub fn convert_cpu_data_points(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String::default()
|
String::default()
|
||||||
|
},
|
||||||
|
..ConvertedCpuData::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
cpu_data_vector.push(new_cpu_data);
|
cpu_data_vector.push(new_cpu_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,16 +429,19 @@ pub enum ProcessNamingType {
|
|||||||
Path,
|
Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Because we needed to UPDATE data entries rather than REPLACING entries, we instead update
|
||||||
|
/// the existing vector.
|
||||||
pub fn convert_process_data(
|
pub fn convert_process_data(
|
||||||
current_data: &data_farmer::DataCollection,
|
current_data: &data_farmer::DataCollection,
|
||||||
) -> Vec<ConvertedProcessData> {
|
existing_converted_process_data: &mut HashMap<Pid, ConvertedProcessData>,
|
||||||
|
) {
|
||||||
// TODO [THREAD]: Thread highlighting and hiding support
|
// TODO [THREAD]: Thread highlighting and hiding support
|
||||||
// For macOS see https://github.com/hishamhm/htop/pull/848/files
|
// For macOS see https://github.com/hishamhm/htop/pull/848/files
|
||||||
|
|
||||||
current_data
|
let mut complete_pid_set: HashSet<Pid> =
|
||||||
.process_harvest
|
existing_converted_process_data.keys().copied().collect();
|
||||||
.iter()
|
|
||||||
.map(|process| {
|
for process in ¤t_data.process_harvest {
|
||||||
let converted_rps = get_exact_byte_values(process.read_bytes_per_sec, false);
|
let converted_rps = get_exact_byte_values(process.read_bytes_per_sec, false);
|
||||||
let converted_wps = get_exact_byte_values(process.write_bytes_per_sec, false);
|
let converted_wps = get_exact_byte_values(process.write_bytes_per_sec, false);
|
||||||
let converted_total_read = get_exact_byte_values(process.total_read_bytes, false);
|
let converted_total_read = get_exact_byte_values(process.total_read_bytes, false);
|
||||||
@ -449,6 +455,61 @@ pub fn convert_process_data(
|
|||||||
0, converted_total_write.0, converted_total_write.1
|
0, converted_total_write.0, converted_total_write.1
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Some(process_entry) = existing_converted_process_data.get_mut(&process.pid) {
|
||||||
|
complete_pid_set.remove(&process.pid);
|
||||||
|
|
||||||
|
// Very dumb way to see if there's PID reuse...
|
||||||
|
if process_entry.ppid == process.parent_pid {
|
||||||
|
process_entry.name = process.name.to_string();
|
||||||
|
process_entry.command = process.command.to_string();
|
||||||
|
process_entry.cpu_percent_usage = process.cpu_usage_percent;
|
||||||
|
process_entry.mem_percent_usage = process.mem_usage_percent;
|
||||||
|
process_entry.mem_usage_bytes = process.mem_usage_bytes;
|
||||||
|
process_entry.mem_usage_str = get_exact_byte_values(process.mem_usage_bytes, false);
|
||||||
|
process_entry.group_pids = vec![process.pid];
|
||||||
|
process_entry.read_per_sec = read_per_sec;
|
||||||
|
process_entry.write_per_sec = write_per_sec;
|
||||||
|
process_entry.total_read = total_read;
|
||||||
|
process_entry.total_write = total_write;
|
||||||
|
process_entry.rps_f64 = process.read_bytes_per_sec as f64;
|
||||||
|
process_entry.wps_f64 = process.write_bytes_per_sec as f64;
|
||||||
|
process_entry.tr_f64 = process.total_read_bytes as f64;
|
||||||
|
process_entry.tw_f64 = process.total_write_bytes as f64;
|
||||||
|
process_entry.process_state = process.process_state.to_owned();
|
||||||
|
process_entry.process_char = process.process_state_char;
|
||||||
|
process_entry.process_description_prefix = None;
|
||||||
|
process_entry.is_disabled_entry = false;
|
||||||
|
} else {
|
||||||
|
// ...I hate that I can't combine if let and an if statement in one line...
|
||||||
|
*process_entry = ConvertedProcessData {
|
||||||
|
pid: process.pid,
|
||||||
|
ppid: process.parent_pid,
|
||||||
|
is_thread: None,
|
||||||
|
name: process.name.to_string(),
|
||||||
|
command: process.command.to_string(),
|
||||||
|
cpu_percent_usage: process.cpu_usage_percent,
|
||||||
|
mem_percent_usage: process.mem_usage_percent,
|
||||||
|
mem_usage_bytes: process.mem_usage_bytes,
|
||||||
|
mem_usage_str: get_exact_byte_values(process.mem_usage_bytes, false),
|
||||||
|
group_pids: vec![process.pid],
|
||||||
|
read_per_sec,
|
||||||
|
write_per_sec,
|
||||||
|
total_read,
|
||||||
|
total_write,
|
||||||
|
rps_f64: process.read_bytes_per_sec as f64,
|
||||||
|
wps_f64: process.write_bytes_per_sec as f64,
|
||||||
|
tr_f64: process.total_read_bytes as f64,
|
||||||
|
tw_f64: process.total_write_bytes as f64,
|
||||||
|
process_state: process.process_state.to_owned(),
|
||||||
|
process_char: process.process_state_char,
|
||||||
|
process_description_prefix: None,
|
||||||
|
is_disabled_entry: false,
|
||||||
|
is_collapsed_entry: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
existing_converted_process_data.insert(
|
||||||
|
process.pid,
|
||||||
ConvertedProcessData {
|
ConvertedProcessData {
|
||||||
pid: process.pid,
|
pid: process.pid,
|
||||||
ppid: process.parent_pid,
|
ppid: process.parent_pid,
|
||||||
@ -472,9 +533,16 @@ pub fn convert_process_data(
|
|||||||
process_char: process.process_state_char,
|
process_char: process.process_state_char,
|
||||||
process_description_prefix: None,
|
process_description_prefix: None,
|
||||||
is_disabled_entry: false,
|
is_disabled_entry: false,
|
||||||
|
is_collapsed_entry: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now clean up any spare entries that weren't visited, to avoid clutter:
|
||||||
|
complete_pid_set.iter().for_each(|pid| {
|
||||||
|
existing_converted_process_data.remove(pid);
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BRANCH_ENDING: char = '└';
|
const BRANCH_ENDING: char = '└';
|
||||||
@ -483,32 +551,36 @@ const BRANCH_SPLIT: char = '├';
|
|||||||
const BRANCH_HORIZONTAL: char = '─';
|
const BRANCH_HORIZONTAL: char = '─';
|
||||||
|
|
||||||
pub fn tree_process_data(
|
pub fn tree_process_data(
|
||||||
single_process_data: &[ConvertedProcessData], is_using_command: bool,
|
filtered_process_data: &[ConvertedProcessData], is_using_command: bool,
|
||||||
sort_type: &ProcessSorting, is_sort_descending: bool,
|
sorting_type: &ProcessSorting, is_sort_descending: bool,
|
||||||
) -> Vec<ConvertedProcessData> {
|
) -> Vec<ConvertedProcessData> {
|
||||||
// FIXME: [TREE] Allow for collapsing entries.
|
|
||||||
// TODO: [TREE] Option to sort usage by total branch usage or individual value usage?
|
// TODO: [TREE] Option to sort usage by total branch usage or individual value usage?
|
||||||
|
|
||||||
// Let's first build up a (really terrible) parent -> child mapping...
|
// Let's first build up a (really terrible) parent -> child mapping...
|
||||||
// At the same time, let's make a mapping of PID -> process data!
|
// At the same time, let's make a mapping of PID -> process data!
|
||||||
let mut parent_child_mapping: HashMap<Pid, IndexSet<Pid>> = HashMap::default();
|
let mut parent_child_mapping: HashMap<Pid, IndexSet<Pid>> = HashMap::default();
|
||||||
let mut pid_process_mapping: HashMap<Pid, &ConvertedProcessData> = HashMap::default();
|
let mut pid_process_mapping: HashMap<Pid, &ConvertedProcessData> = HashMap::default(); // We actually already have this stored, but it's unfiltered... oh well.
|
||||||
let mut orphan_set: IndexSet<Pid> = IndexSet::new();
|
let mut orphan_set: IndexSet<Pid> = IndexSet::new();
|
||||||
|
let mut collapsed_set: IndexSet<Pid> = IndexSet::new();
|
||||||
|
|
||||||
single_process_data.iter().for_each(|process| {
|
filtered_process_data.iter().for_each(|process| {
|
||||||
if let Some(ppid) = process.ppid {
|
if let Some(ppid) = process.ppid {
|
||||||
orphan_set.insert(ppid);
|
orphan_set.insert(ppid);
|
||||||
}
|
}
|
||||||
orphan_set.insert(process.pid);
|
orphan_set.insert(process.pid);
|
||||||
});
|
});
|
||||||
|
|
||||||
single_process_data.iter().for_each(|process| {
|
filtered_process_data.iter().for_each(|process| {
|
||||||
// Create a mapping for the process if it DNE.
|
// Create a mapping for the process if it DNE.
|
||||||
parent_child_mapping
|
parent_child_mapping
|
||||||
.entry(process.pid)
|
.entry(process.pid)
|
||||||
.or_insert_with(IndexSet::new);
|
.or_insert_with(IndexSet::new);
|
||||||
pid_process_mapping.insert(process.pid, process);
|
pid_process_mapping.insert(process.pid, process);
|
||||||
|
|
||||||
|
if process.is_collapsed_entry {
|
||||||
|
collapsed_set.insert(process.pid);
|
||||||
|
}
|
||||||
|
|
||||||
// Insert its mapping to the process' parent if needed (create if it DNE).
|
// Insert its mapping to the process' parent if needed (create if it DNE).
|
||||||
if let Some(ppid) = process.ppid {
|
if let Some(ppid) = process.ppid {
|
||||||
orphan_set.remove(&process.pid);
|
orphan_set.remove(&process.pid);
|
||||||
@ -521,8 +593,8 @@ pub fn tree_process_data(
|
|||||||
|
|
||||||
// Keep only orphans, or promote children of orphans to a top-level orphan
|
// Keep only orphans, or promote children of orphans to a top-level orphan
|
||||||
// if their parents DNE in our pid to process mapping...
|
// if their parents DNE in our pid to process mapping...
|
||||||
#[allow(clippy::redundant_clone)]
|
let old_orphan_set = orphan_set.clone();
|
||||||
orphan_set.clone().iter().for_each(|pid| {
|
old_orphan_set.iter().for_each(|pid| {
|
||||||
if pid_process_mapping.get(pid).is_none() {
|
if pid_process_mapping.get(pid).is_none() {
|
||||||
// DNE! Promote the mapped children and remove the current parent...
|
// DNE! Promote the mapped children and remove the current parent...
|
||||||
orphan_set.remove(pid);
|
orphan_set.remove(pid);
|
||||||
@ -717,12 +789,14 @@ pub fn tree_process_data(
|
|||||||
/// the correct order to the PID tree as a vector.
|
/// the correct order to the PID tree as a vector.
|
||||||
fn build_explored_pids(
|
fn build_explored_pids(
|
||||||
current_pid: Pid, parent_child_mapping: &HashMap<Pid, IndexSet<Pid>>,
|
current_pid: Pid, parent_child_mapping: &HashMap<Pid, IndexSet<Pid>>,
|
||||||
prev_drawn_lines: &str,
|
prev_drawn_lines: &str, collapsed_set: &IndexSet<Pid>,
|
||||||
) -> (Vec<Pid>, Vec<String>) {
|
) -> (Vec<Pid>, Vec<String>) {
|
||||||
let mut explored_pids: Vec<Pid> = vec![current_pid];
|
let mut explored_pids: Vec<Pid> = vec![current_pid];
|
||||||
let mut lines: Vec<String> = vec![];
|
let mut lines: Vec<String> = vec![];
|
||||||
|
|
||||||
if let Some(children) = parent_child_mapping.get(¤t_pid) {
|
if collapsed_set.contains(¤t_pid) {
|
||||||
|
return (explored_pids, lines);
|
||||||
|
} else if let Some(children) = parent_child_mapping.get(¤t_pid) {
|
||||||
for (itx, child) in children.iter().rev().enumerate() {
|
for (itx, child) in children.iter().rev().enumerate() {
|
||||||
let new_drawn_lines = if itx == children.len() - 1 {
|
let new_drawn_lines = if itx == children.len() - 1 {
|
||||||
format!("{} ", prev_drawn_lines)
|
format!("{} ", prev_drawn_lines)
|
||||||
@ -730,8 +804,12 @@ pub fn tree_process_data(
|
|||||||
format!("{}{} ", prev_drawn_lines, BRANCH_VERTICAL)
|
format!("{}{} ", prev_drawn_lines, BRANCH_VERTICAL)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (pid_res, branch_res) =
|
let (pid_res, branch_res) = build_explored_pids(
|
||||||
build_explored_pids(*child, parent_child_mapping, new_drawn_lines.as_str());
|
*child,
|
||||||
|
parent_child_mapping,
|
||||||
|
new_drawn_lines.as_str(),
|
||||||
|
collapsed_set,
|
||||||
|
);
|
||||||
|
|
||||||
if itx == children.len() - 1 {
|
if itx == children.len() - 1 {
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
@ -769,20 +847,21 @@ pub fn tree_process_data(
|
|||||||
to_sort_vec.push((pid, *process));
|
to_sort_vec.push((pid, *process));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort_vec(&mut to_sort_vec, sort_type, is_sort_descending);
|
sort_vec(&mut to_sort_vec, sorting_type, is_sort_descending);
|
||||||
pids_to_explore = to_sort_vec.iter().map(|(pid, _proc)| *pid).collect();
|
pids_to_explore = to_sort_vec.iter().map(|(pid, _proc)| *pid).collect();
|
||||||
|
|
||||||
while let Some(current_pid) = pids_to_explore.pop_front() {
|
while let Some(current_pid) = pids_to_explore.pop_front() {
|
||||||
if !prune_disabled_pids(current_pid, &mut parent_child_mapping, &pid_process_mapping) {
|
if !prune_disabled_pids(current_pid, &mut parent_child_mapping, &pid_process_mapping) {
|
||||||
sort_remaining_pids(
|
sort_remaining_pids(
|
||||||
current_pid,
|
current_pid,
|
||||||
sort_type,
|
sorting_type,
|
||||||
is_sort_descending,
|
is_sort_descending,
|
||||||
&mut parent_child_mapping,
|
&mut parent_child_mapping,
|
||||||
&pid_process_mapping,
|
&pid_process_mapping,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (pid_res, branch_res) = build_explored_pids(current_pid, &parent_child_mapping, "");
|
let (pid_res, branch_res) =
|
||||||
|
build_explored_pids(current_pid, &parent_child_mapping, "", &collapsed_set);
|
||||||
lines.push(String::default());
|
lines.push(String::default());
|
||||||
lines.extend(branch_res);
|
lines.extend(branch_res);
|
||||||
explored_pids.extend(pid_res);
|
explored_pids.extend(pid_res);
|
||||||
@ -798,8 +877,9 @@ pub fn tree_process_data(
|
|||||||
Some(process) => {
|
Some(process) => {
|
||||||
let mut p = process.clone();
|
let mut p = process.clone();
|
||||||
p.process_description_prefix = Some(format!(
|
p.process_description_prefix = Some(format!(
|
||||||
"{}{}",
|
"{}{}{}",
|
||||||
prefix,
|
prefix,
|
||||||
|
if p.is_collapsed_entry { "+ " } else { "" }, // I do the + sign thing here because I'm kinda too lazy to do it in the prefix, tbh.
|
||||||
if is_using_command {
|
if is_using_command {
|
||||||
&p.command
|
&p.command
|
||||||
} else {
|
} else {
|
||||||
@ -953,6 +1033,7 @@ pub fn group_process_data(
|
|||||||
process_description_prefix: None,
|
process_description_prefix: None,
|
||||||
process_char: char::default(),
|
process_char: char::default(),
|
||||||
is_disabled_entry: false,
|
is_disabled_entry: false,
|
||||||
|
is_collapsed_entry: false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
19
src/lib.rs
19
src/lib.rs
@ -361,14 +361,17 @@ fn update_final_process_list(app: &mut App, widget_id: u64) {
|
|||||||
|
|
||||||
if let Some((is_invalid_or_blank, is_using_command, is_grouped, is_tree)) = process_states {
|
if let Some((is_invalid_or_blank, is_using_command, is_grouped, is_tree)) = process_states {
|
||||||
if !app.is_frozen {
|
if !app.is_frozen {
|
||||||
app.canvas_data.single_process_data = convert_process_data(&app.data_collection);
|
convert_process_data(
|
||||||
|
&app.data_collection,
|
||||||
|
&mut app.canvas_data.single_process_data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let process_filter = app.get_process_filter(widget_id);
|
let process_filter = app.get_process_filter(widget_id);
|
||||||
let filtered_process_data: Vec<ConvertedProcessData> = if is_tree {
|
let filtered_process_data: Vec<ConvertedProcessData> = if is_tree {
|
||||||
app.canvas_data
|
app.canvas_data
|
||||||
.single_process_data
|
.single_process_data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|process| {
|
.map(|(_pid, process)| {
|
||||||
let mut process_clone = process.clone();
|
let mut process_clone = process.clone();
|
||||||
if !is_invalid_or_blank {
|
if !is_invalid_or_blank {
|
||||||
if let Some(process_filter) = process_filter {
|
if let Some(process_filter) = process_filter {
|
||||||
@ -383,15 +386,19 @@ fn update_final_process_list(app: &mut App, widget_id: u64) {
|
|||||||
app.canvas_data
|
app.canvas_data
|
||||||
.single_process_data
|
.single_process_data
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|process| {
|
.filter_map(|(_pid, process)| {
|
||||||
if !is_invalid_or_blank {
|
if !is_invalid_or_blank {
|
||||||
if let Some(process_filter) = process_filter {
|
if let Some(process_filter) = process_filter {
|
||||||
process_filter.check(&process, is_using_command)
|
if process_filter.check(&process, is_using_command) {
|
||||||
|
Some(process)
|
||||||
} else {
|
} else {
|
||||||
true
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
true
|
Some(process)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(process)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -115,6 +115,7 @@ pub struct ConfigFlags {
|
|||||||
#[builder(default, setter(strip_option))]
|
#[builder(default, setter(strip_option))]
|
||||||
pub no_write: Option<bool>,
|
pub no_write: Option<bool>,
|
||||||
|
|
||||||
|
// For built-in colour palettes.
|
||||||
#[builder(default, setter(strip_option))]
|
#[builder(default, setter(strip_option))]
|
||||||
pub color: Option<String>,
|
pub color: Option<String>,
|
||||||
|
|
||||||
@ -362,7 +363,8 @@ pub fn build_app(
|
|||||||
1
|
1
|
||||||
},
|
},
|
||||||
disable_click: get_disable_click(matches, config),
|
disable_click: get_disable_click(matches, config),
|
||||||
no_write: get_no_write(matches, config),
|
// no_write: get_no_write(matches, config),
|
||||||
|
no_write: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let used_widgets = UsedWidgets {
|
let used_widgets = UsedWidgets {
|
||||||
@ -845,6 +847,7 @@ fn get_use_battery(matches: &clap::ArgMatches<'static>, config: &Config) -> bool
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn get_no_write(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
fn get_no_write(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||||
if matches.is_present("no_write") {
|
if matches.is_present("no_write") {
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user