diff options
author | ClementTsang <cjhtsang@uwaterloo.ca> | 2021-11-20 22:45:49 -0500 |
---|---|---|
committer | ClementTsang <cjhtsang@uwaterloo.ca> | 2021-11-20 22:45:49 -0500 |
commit | cc66f1fcaea6d31581efea7dfc1b0029e4754f49 (patch) | |
tree | 4d9c2f0a6d9ddaefd73e95caddfa9d356e173e89 | |
parent | 5833cb8ad1f30d42efd9dc7f5f27bed35882bc95 (diff) |
refactor: mostly add back tree mode for process
Mouse control on collapse is not working yet, need to do some work
internally first.
26 files changed, 788 insertions, 1038 deletions
@@ -239,7 +239,7 @@ dependencies = [ "cargo-husky", "cfg-if", "clap", - "crossterm", + "crossterm 0.22.1", "ctrlc", "dirs", "enum_dispatch", @@ -247,7 +247,6 @@ dependencies = [ "float-ord", "futures", "futures-timer", - "fxhash", "heim", "indexmap", "indextree", @@ -258,6 +257,7 @@ dependencies = [ "predicates 1.0.8", "procfs", "regex", + "rustc-hash", "serde", "smol", "sysinfo", @@ -434,7 +434,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" dependencies = [ "bitflags", - "crossterm_winapi", + "crossterm_winapi 0.8.0", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c" +dependencies = [ + "bitflags", + "crossterm_winapi 0.9.0", "libc", "mio", "parking_lot", @@ -453,6 +469,15 @@ dependencies = [ ] [[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + +[[package]] name = "ctrlc" version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -685,15 +710,6 @@ dependencies = [ ] [[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] name = "getrandom" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1307,6 +1323,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1521,7 +1543,7 @@ checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23" dependencies = [ "bitflags", "cassowary", - "crossterm", + "crossterm 0.20.0", "unicode-segmentation", "unicode-width", ] @@ -25,8 +25,6 @@ doc = false [profile.release] debug = 0 lto = true -# debug = true -# lto = false opt-level = 3 codegen-units = 1 @@ -36,7 +34,7 @@ default = ["fern", "log", "battery"] [dependencies] anyhow = "1.0.40" backtrace = "0.3.59" -crossterm = "0.20.0" +crossterm = "0.22.1" ctrlc = { version = "3.1.9", features = ["termination"] } clap = "2.33" cfg-if = "1.0" @@ -45,12 +43,12 @@ enum_dispatch = "0.3.7" float-ord = "0.3.2" futures = "0.3.14" futures-timer = "3.0.2" -fxhash = "0.2.1" indexmap = "1.6.2" indextree = "4.3.1" itertools = "0.10.0" once_cell = "1.5.2" regex = "1.5.4" +rustc-hash = "1.1.0" serde = { version = "1.0.125", features = ["derive"] } # Sysinfo is still used in Linux for the ProcessStatus sysinfo = "0.18.2" diff --git a/docs/content/configuration/command-line-flags.md b/docs/content/configuration/command-line-flags.md index b5d6d3d3..25d0c1b8 100644 --- a/docs/content/configuration/command-line-flags.md +++ b/docs/content/configuration/command-line-flags.md @@ -17,8 +17,6 @@ The following flags can be provided to bottom in the command line to change the | `-C, --config <CONFIG PATH>` | Sets the location of the config file. | | `-u, --current_usage` | Sets process CPU% to be based on current CPU%. | | `-t, --default_time_value <MS>` | Default time value for graphs in ms. | -| `--default_widget_count <INT>` | Sets the n'th selected widget type as the default. | -| `--default_widget_type <WIDGET TYPE>` | Sets the default widget type, use --help for more info. | | `--disable_advanced_kill` | Hides advanced options to stop a process on Unix-like systems. | | `--disable_click` | Disables mouse clicks. | | `-m, --dot_marker` | Uses a dot marker for graphs. | diff --git a/sample_configs/default_config.toml b/sample_configs/default_config.toml index 1e2d56a4..556c131d 100644 --- a/sample_configs/default_config.toml +++ b/sample_configs/default_config.toml @@ -38,9 +38,6 @@ #time_delta = 15000 # Hides the time scale. #hide_time = false -# Override layout default widget -#default_widget_type = "proc" -#default_widget_count = 1 # Use basic mode #basic = false # Use the old network legend style diff --git a/sample_configs/demo_config.toml b/sample_configs/demo_config.toml index e6c5cf71..0874779b 100644 --- a/sample_configs/demo_config.toml +++ b/sample_configs/demo_config.toml @@ -11,5 +11,3 @@ group_processes = false case_sensitive = false whole_word = false regex = true -default_widget_type = "cpu" -default_widget_count = 1 @@ -10,8 +10,8 @@ pub mod widgets; use std::time::Instant; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent}; -use fxhash::FxHashMap; use indextree::{Arena, NodeId}; +use rustc_hash::FxHashMap; pub use data_farmer::*; use data_harvester::temperature; @@ -474,7 +474,7 @@ impl AppState { } #[cfg(target_family = "unix")] - pub fn on_number(&mut self, number_char: char) { + fn on_number(&mut self, number_char: char) { if self.delete_dialog_state.is_showing_dd { if self .delete_dialog_state @@ -507,7 +507,7 @@ impl AppState { } } - pub fn on_left_key(&mut self) { + fn on_left_key(&mut self) { // if !self.is_in_dialog() { // match self.current_widget.widget_type { // BottomWidgetType::ProcSearch => { @@ -566,7 +566,7 @@ impl AppState { // } } - pub fn on_right_key(&mut self) { + fn on_right_key(&mut self) { // if !self.is_in_dialog() { // match self.current_widget.widget_type { // BottomWidgetType::ProcSearch => { @@ -626,7 +626,7 @@ impl AppState { // } } - pub fn start_killing_process(&mut self) { + fn start_killing_process(&mut self) { todo!() // if let Some(proc_widget_state) = self @@ -666,7 +666,7 @@ impl AppState { // } } - pub fn kill_highlighted_process(&mut self) -> Result<()> { + fn kill_highlighted_process(&mut self) -> Result<()> { // if let BottomWidgetType::Proc = self.current_widget.widget_type { // if let Some(current_selected_processes) = &self.to_delete_process_list { // #[cfg(target_family = "unix")] diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index 83b53d29..75edc3ea 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; /// In charge of cleaning, processing, and managing data. I couldn't think of /// a better name for the file. Since I called data collection "harvesting", /// then this is the farmer I guess. @@ -13,6 +14,7 @@ /// memory usage and higher CPU usage - you will be trying to process more and /// more points as this is used! use once_cell::sync::Lazy; +use rustc_hash::FxHashMap; use std::{collections::HashMap, time::Instant, vec::Vec}; @@ -26,6 +28,8 @@ use crate::{ }; use regex::Regex; +use super::data_harvester::processes::ProcessHarvest; + #[derive(Clone, Debug, Default)] pub struct TimedData { pub rx_data: f64, @@ -36,6 +40,90 @@ pub struct TimedData { pub swap_data: Option<f64>, } +#[derive(Clone, Debug, Default)] +pub struct ProcessData { + /// A PID to process data map. + pub process_harvest: FxHashMap<Pid, ProcessHarvest>, + + /// A mapping from a process name to any PID with that name. + pub process_name_pid_map: HashMap<String, Vec<Pid>>, + + /// A mapping from a process command to any PID with that name. + pub process_cmd_pid_map: HashMap<String, Vec<Pid>>, + + /// A mapping between a process PID to any children process PIDs. + pub process_parent_mapping: FxHashMap<Pid, Vec<Pid>>, + + /// PIDs corresponding to processes that have no parents. + pub orphan_pids: Vec<Pid>, +} + +impl ProcessData { + fn ingest(&mut self, list_of_processes: Vec<ProcessHarvest>) { + // TODO: [Optimization] Probably more efficient to all of this in the data collection step, but it's fine for now. + self.process_name_pid_map.clear(); + self.process_cmd_pid_map.clear(); + self.process_parent_mapping.clear(); + + // Reverse as otherwise the pid mappings are in the wrong order. + list_of_processes.iter().rev().for_each(|process_harvest| { + if let Some(entry) = self.process_name_pid_map.get_mut(&process_harvest.name) { + entry.push(process_harvest.pid); + } else { + self.process_name_pid_map + .insert(process_harvest.name.to_string(), vec![process_harvest.pid]); + } + + if let Some(entry) = self.process_cmd_pid_map.get_mut(&process_harvest.command) { + entry.push(process_harvest.pid); + } else { + self.process_cmd_pid_map.insert( + process_harvest.command.to_string(), + vec![process_harvest.pid], + ); + } + + if let Some(parent_pid) = process_harvest.parent_pid { + if let Some(entry) = self.process_parent_mapping.get_mut(&parent_pid) { + entry.push(process_harvest.pid); + } else { + self.process_parent_mapping + .insert(parent_pid, vec![process_harvest.pid]); + } + } + }); + + self.process_name_pid_map.shrink_to_fit(); + self.process_cmd_pid_map.shrink_to_fit(); + self.process_parent_mapping.shrink_to_fit(); + + let process_pid_map = list_of_processes + .into_iter() + .map(|process| (process.pid, process)) + .collect(); + self.process_harvest = process_pid_map; + + // This also needs a quick sort + reverse to be in the correct order. + self.orphan_pids = self + .process_harvest + .iter() + .filter_map(|(pid, process_harvest)| { + if let Some(parent_pid) = process_harvest.parent_pid { + if self.process_harvest.contains_key(&parent_pid) { + None + } else { + Some(*pid) + } + } else { + Some(*pid) + } + }) + .sorted() + .rev() + .collect(); + } +} + /// AppCollection represents the pooled data stored within the main app /// thread. Basically stores a (occasionally cleaned) record of the data /// collected, and what is needed to convert into a displayable form. @@ -48,16 +136,14 @@ pub struct TimedData { /// not the data collector. #[derive(Clone, Debug)] pub struct DataCollection { - pub current_instant: Instant, + pub current_instant: Instant, // TODO: [Refactor] Can I get rid of this? If I could, then I could just use #[derive(Default)] too! pub timed_data_vec: Vec<(Instant, TimedData)>, pub network_harvest: network::NetworkHarvest, pub memory_harvest: memory::MemHarvest, pub swap_harvest: memory::MemHarvest, pub cpu_harvest: cpu::CpuHarvest, pub load_avg_harvest: cpu::LoadAvgHarvest, - pub process_harvest: Vec<processes::ProcessHarvest>, - pub process_name_pid_map: HashMap<String, Vec<Pid>>, - pub process_cmd_pid_map: HashMap<String, Vec<Pid>>, + pub process_data: ProcessData, pub disk_harvest: Vec<disks::DiskHarvest>, pub io_harvest: disks::IoHarvest, pub io_labels_and_prev: Vec<((u64, u64), (u64, u64))>, @@ -71,22 +157,20 @@ impl Default for DataCollection { fn default() -> Self { DataCollection { current_instant: Instant::now(), - timed_data_vec: Vec::default(), - network_harvest: network::NetworkHarvest::default(), - memory_harvest: memory::MemHarvest::default(), - swap_harvest: memory::MemHarvest::default(), - cpu_harvest: cpu::CpuHarvest::default(), - load_avg_harvest: cpu::LoadAvgHarvest::default(), - process_harvest: Vec::default(), - process_name_pid_map: HashMap::default(), - process_cmd_pid_map: HashMap::default(), - disk_harvest: Vec::default(), - io_harvest: disks::IoHarvest::default(), - io_labels_and_prev: Vec::default(), - io_labels: Vec::default(), - temp_harvest: Vec::default(), + timed_data_vec: Default::default(), + network_harvest: Default::default(), + memory_harvest: Default::default(), + swap_harvest: Default::default(), + cpu_harvest: Default::default(), + load_avg_harvest: Default::default(), + process_data: Default::default(), + disk_harvest: Default::default(), + io_harvest: Default::default(), + io_labels_and_prev: Default::default(), + io_labels: Default::default(), + temp_harvest: Default::default(), #[cfg(feature = "battery")] - battery_harvest: Vec::default(), + battery_harvest: Default::default(), } } } @@ -98,9 +182,7 @@ impl DataCollection { self.memory_harvest = Default::default(); self.swap_harvest = Default::default(); self.cpu_harvest = Default::default(); - self.process_harvest = Default::default(); - self.process_name_pid_map = Default::default(); - self.process_cmd_pid_map = Default::default(); + self.process_data = Default::default(); self.disk_harvest = Default::default(); self.io_harvest = Default::default(); self.io_labels_and_prev = Default::default(); @@ -132,8 +214,6 @@ impl DataCollection { pub fn eat_data(&mut self, harvested_data: Box<Data>) { let harvested_time = harvested_data.last_collection_time; - // trace!("Harvested time: {:?}", harvested_time); - // trace!("New current instant: {:?}", self.current_instant); let mut new_entry = TimedData::default(); // Network @@ -316,28 +396,7 @@ impl DataCollection { } fn eat_proc(&mut self, list_of_processes: Vec<processes::ProcessHarvest>) { - // TODO: [Optimization] Probably more efficient to do this in the data collection step, but it's fine for now. - self.process_name_pid_map.clear(); - self.process_cmd_pid_map.clear(); - list_of_processes.iter().for_each(|process_harvest| { - if let Some(entry) = self.process_name_pid_map.get_mut(&process_harvest.name) { - entry.push(process_harvest.pid); - } else { - self.process_name_pid_map - .insert(process_harvest.name.to_string(), vec![process_harvest.pid]); - } - - if let Some(entry) = self.process_cmd_pid_map.get_mut(&process_harvest.command) { - entry.push(process_harvest.pid); - } else { - self.process_cmd_pid_map.insert( - process_harvest.command.to_string(), - vec![process_harvest.pid], - ); - } - }); - - self.process_harvest = list_of_processes; + self.process_data.ingest(list_of_processes); } #[cfg(feature = "battery")] diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs index 2672022d..75d5b4b6 100644 --- a/src/app/data_harvester.rs +++ b/src/app/data_harvester.rs @@ -3,7 +3,7 @@ use std::time::Instant; #[cfg(target_os = "linux")] -use fxhash::FxHashMap; +use rustc_hash::FxHashMap; #[cfg(not(target_os = "linux"))] use sysinfo::{System, SystemExt}; diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes.rs index 1ee60123..bcbc97e0 100644 --- a/src/app/data_harvester/processes.rs +++ b/src/app/data_harvester/processes.rs @@ -45,41 +45,10 @@ pub enum ProcessSorting { Count, } -impl std::fmt::Display for ProcessSorting { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match &self { - ProcessSorting::CpuPercent => "CPU%", - ProcessSorting::MemPercent => "Mem%", - ProcessSorting::Mem => "Mem", - ProcessSorting::ReadPerSecond => "R/s", - ProcessSorting::WritePerSecond => "W/s", - ProcessSorting::TotalRead => "T.Read", - ProcessSorting::TotalWrite => "T.Write", - ProcessSorting::State => "State", - ProcessSorting::ProcessName => "Name", - ProcessSorting::Command => "Command", - ProcessSorting::Pid => "PID", - ProcessSorting::Count => "Count", - ProcessSorting::User => "User", - } - ) - } -} - -impl Default for ProcessSorting { - fn default() -> Self { - ProcessSorting::CpuPercent - } -} - #[derive(Debug, Clone, Default)] pub struct ProcessHarvest { pub pid: Pid, pub parent_pid: Option<Pid>, - pub children_pids: Vec<Pid>, pub cpu_usage_percent: f64, pub mem_usage_percent: f64, pub mem_usage_bytes: u64, @@ -102,3 +71,15 @@ pub struct ProcessHarvest { #[cfg(target_family = "unix")] pub user: Cow<'static, str>, } + +impl ProcessHarvest { + pub(crate) fn add(&mut self, rhs: &ProcessHarvest) { + self.cpu_usage_percent += rhs.cpu_usage_percent; + self.mem_usage_bytes += rhs.mem_usage_bytes; + self.mem_usage_percent += rhs.mem_usage_percent; + self.read_bytes_per_sec += rhs.read_bytes_per_sec; + self.write_bytes_per_sec += rhs.write_bytes_per_sec; + self.total_read_bytes += rhs.total_read_bytes; + self.total_write_bytes += rhs.total_write_bytes; + } +} diff --git a/src/app/data_harvester/processes/linux.rs b/src/app/data_harvester/processes/linux.rs index 5d11ca7b..63158297 100644 --- a/src/app/data_harvester/processes/linux.rs +++ b/src/app/data_harvester/processes/linux.rs @@ -11,7 +11,7 @@ use sysinfo::ProcessStatus; use procfs::process::{Process, Stat}; -use fxhash::{FxHashMap, FxHashSet}; +use rustc_hash::{FxHashMap, FxHashSet}; /// Maximum character length of a /proc/<PID>/stat process name. /// If it's equal or greater, then we instead refer to the command for the name. @@ -203,7 +203,6 @@ fn read_proc( ProcessHarvest { pid: process.pid, parent_pid, - children_pids: vec![], cpu_usage_percent, mem_usage_percent, mem_usage_bytes, diff --git a/src/app/data_harvester/processes/macos.rs b/src/app/data_harvester/processes/macos.rs index d5dffe85..a65f2627 100644 --- a/src/app/data_harvester/processes/macos.rs +++ b/src/app/data_harvester/processes/macos.rs @@ -89,7 +89,6 @@ pub fn get_process_data( process_vector.push(ProcessHarvest { pid: process_val.pid(), parent_pid: process_val.parent(), - children_pids: vec![], name, command, mem_usage_percent: if mem_total_kb > 0 { diff --git a/src/app/data_harvester/processes/windows.rs b/src/app/data_harvester/processes/windows.rs index 763f9cf8..08cde500 100644 --- a/src/app/data_harvester/processes/windows.rs +++ b/src/app/data_harvester/processes/windows.rs @@ -58,7 +58,6 @@ pub fn get_process_data( process_vector.push(ProcessHarvest { pid: process_val.pid(), parent_pid: process_val.parent(), - children_pids: vec![], name, command, mem_usage_percent: if mem_total_kb > 0 { diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs index 7f0dd663..b5d54480 100644 --- a/src/app/layout_manager.rs +++ b/src/app/layout_manager.rs @@ -9,8 +9,8 @@ use crate::{ ProcessDefaults, }, }; -use fxhash::FxHashMap; use indextree::{Arena, NodeId}; +use rustc_hash::FxHashMap; use std::cmp::min; use tui::layout::Rect; diff --git a/src/app/widgets/base/sort_text_table.rs b/src/app/widgets/base/sort_text_table.rs index 2c2d9e21..001624a4 100644 --- a/src/app/widgets/base/sort_text_table.rs +++ b/src/app/widgets/base/sort_text_table.rs @@ -288,11 +288,16 @@ where self.table.current_scroll_index() } - /// Returns the current column the table is sorting by. + /// Returns a reference to the current column the table is sorting by. pub fn current_sorting_column(&self) -> &S { &self.table.columns[self.sort_index] } + /// Returns a mutable reference to the current column the table is sorting by. + pub fn current_mut_sorting_column(&mut self) -> &mut S { + &mut self.table.columns[self.sort_index] + } + /// Returns the current column index the table is sorting by. pub fn current_sorting_column_index(&self) -> usize { self.sort_index diff --git a/src/app/widgets/base/text_table.rs b/src/app/widgets/base/text_table.rs index d3c9c601..896eda85 100644 --- a/src/app/widgets/base/text_table.rs +++ b/src/app/widgets/base/text_table.rs @@ -39,8 +39,9 @@ pub trait TableColumn { fn set_x_bounds(&mut self, x_bounds: Option<(u16, u16)>); } -pub type TextTableData = Vec<Vec<(Cow<'static, str>, Option<Cow<'static, str>>, Option<Style>)>>; -pub type TextTableDataRef = [Vec<(Cow<'static, str>, Option<Cow<'static, str>>, Option<Style>)>]; +pub(crate) type TextTableRow = Vec<(Cow<'static, str>, Option<Cow<'static, str>>, Option<Style>)>; +pub(crate) type TextTableData = Vec<TextTableRow>; +pub(crate) type TextTableDataRef = [TextTableRow]; /// A [`SimpleColumn`] represents some column in a [`TextTable`]. #[derive(Debug)] @@ -468,14 +469,13 @@ where .style(painter.colours.table_header_style) .bottom_margin(table_gap); - let table = Table::new(rows) + let mut table = Table::new(rows) .header(header) - .style(painter.colours.text_style) - .highlight_style(if show_selected_entry { - painter.colours.currently_selected_text_style - } else { - painter.colours.text_style - }); + .style(painter.colours.text_style); + + if show_selected_entry { + table = table.highlight_style(painter.colours.currently_selected_text_style); + } if self.selectable { f.render_stateful_widget( diff --git a/src/app/widgets/bottom_widgets/process.rs b/src/app/widgets/bottom_widgets/process.rs index 8ca3a02e..154f4fb7 100644 --- a/src/app/widgets/bottom_widgets/process.rs +++ b/src/app/widgets/bottom_widgets/process.rs @@ -1,13 +1,15 @@ -use std::{borrow::Cow, collections::HashMap}; +use std::{borrow::Cow, cell::RefCell, collections::HashMap}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}; use float_ord::FloatOrd; use itertools::{Either, Itertools}; use once_cell::unsync::Lazy; +use rustc_hash::{FxHashMap, FxHashSet}; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, + style::Style, text::{Span, Spans}, widgets::{Borders, Paragraph}, Frame, @@ -18,14 +20,15 @@ use crate::{ data_harvester::processes::ProcessHarvest, event::{ComponentEventResult, MultiKey, MultiKeyResult, ReturnSignal, SelectionAction}, query::*, - text_table::DesiredColumnWidth, + text_table::{DesiredColumnWidth, TextTableRow}, widgets::tui_stuff::BlockBuilder, - AppConfigFields, DataCollection, + AppConfigFields, DataCollection, ProcessData, }, canvas::Painter, - data_conversion::get_string_with_bytes, + data_conversion::{get_string_with_bytes, get_string_with_bytes_per_second}, options::{layout_options::LayoutRule, ProcessDefaults}, utils::error::BottomError, + Pid, }; use crate::app::{ @@ -140,7 +143,7 @@ impl ProcessSortType { ProcessSortType::Wps => Hard(Some(8)), ProcessSortType::TotalRead => Hard(Some(7)), ProcessSortType::TotalWrite => Hard(Some(8)), - ProcessSortType::User => Flex(0.08), + ProcessSortType::User => Flex(0.08), // FIXME: [URGENT] adjust this scaling ProcessSortType::State => Hard(Some(8)), } } @@ -211,7 +214,7 @@ impl SortableColumn for ProcessSortColumn { } fn default_descending(&self) -> bool { - self.sortable_column.default_descending() + self.sortable_column.default_descending() // TODO: [Behaviour] not sure if I like this behaviour tbh } fn sorting_status(&self) -> SortStatus { @@ -239,35 +242,37 @@ impl SortableColumn for ProcessSortColumn { } } +#[derive(Default)] +struct TreeData { + user_collapsed_pids: FxHashSet<Pid>, + sorted_pids: RefCell<Vec<Pid>>, +} + +enum ManagerMode { + Normal, + Grouped, + Tree(TreeData), +} + /// A searchable, sortable table to manage processes. pub struct ProcessManager { bounds: Rect, process_table: SortableTextTable<ProcessSortColumn>, sort_menu: SortMenu, search_block_bounds: Rect, - search_input: TextInput, - dd_multi: MultiKey, - selected: ProcessManagerSelection, prev_selected: ProcessManagerSelection, - - in_tree_mode: bool, + manager_mode: ManagerMode, show_sort: bool, show_search: bool, - search_modifiers: SearchModifiers, - display_data: TextTableData, - process_filter: Option<Result<Query, BottomError>>, - block_border: Borders, - width: LayoutRule, height: LayoutRule, - show_scroll_index: bool, } @@ -299,7 +304,7 @@ impl ProcessManager { dd_multi: MultiKey::register(vec!['d', 'd']), // TODO: [Optimization] Maybe use something static/const/arrayvec?... selected: ProcessManagerSelection::Processes, prev_selected: ProcessManagerSelection::Processes, - in_tree_mode: false, + manager_mode: ManagerMode::Normal, show_sort: false, show_search: false, s |