From e77d9202580ccd9d9ac7718197230dd7159544b4 Mon Sep 17 00:00:00 2001 From: rhasler1 Date: Tue, 13 May 2025 16:28:26 -0500 Subject: [PATCH 1/2] some ideas --- Cargo.lock | 149 ++++++++++- Cargo.toml | 7 +- .../Cargo.toml | 4 +- .../src/bounded_queue.rs | 43 ++-- .../src/cpu_item.rs | 2 +- .../src/lib.rs | 2 +- .../src/memory_item.rs | 18 +- dynamic-list/Cargo.toml | 9 + dynamic-list/src/dynamic_list.rs | 45 ++++ dynamic-list/src/lib.rs | 2 + process-list/Cargo.toml | 2 +- process-list/src/lib.rs | 2 - process-list/src/process_item_info.rs | 44 ---- process-list/src/process_list.rs | 130 ++++------ process-list/src/process_list_item.rs | 30 ++- process-list/src/process_list_items.rs | 70 +++--- src/app.rs | 235 ++++++------------ src/components/command_new.rs | 29 +++ src/components/cpu.rs | 12 +- src/components/error.rs | 4 +- src/components/filter.rs | 14 +- src/components/help.rs | 4 +- src/components/mod.rs | 6 +- src/components/performance.rs | 228 ----------------- src/components/process.rs | 34 +-- src/components/system_wrapper.rs | 29 +-- src/components/tab.rs | 145 ----------- src/components/utils/color_wheel.rs | 52 ++++ src/components/utils/vertical_scroll.rs | 17 +- src/components/vertical_tabs.rs | 130 ---------- src/config.rs | 6 +- src/events/event.rs | 10 +- src/main.rs | 9 +- src/ui/process_list_ui.rs | 7 +- 34 files changed, 556 insertions(+), 974 deletions(-) rename {performance-queue => bounded-queue}/Cargo.toml (68%) rename {performance-queue => bounded-queue}/src/bounded_queue.rs (62%) rename {performance-queue => bounded-queue}/src/cpu_item.rs (99%) rename {performance-queue => bounded-queue}/src/lib.rs (73%) rename {performance-queue => bounded-queue}/src/memory_item.rs (83%) create mode 100644 dynamic-list/Cargo.toml create mode 100644 dynamic-list/src/dynamic_list.rs create mode 100644 dynamic-list/src/lib.rs delete mode 100644 process-list/src/process_item_info.rs create mode 100644 src/components/command_new.rs delete mode 100644 src/components/performance.rs delete mode 100644 src/components/tab.rs create mode 100644 src/components/utils/color_wheel.rs delete mode 100644 src/components/vertical_tabs.rs diff --git a/Cargo.lock b/Cargo.lock index 8f26494..af4a172 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -26,12 +26,30 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bitflags" version = "2.6.0" @@ -41,6 +59,27 @@ dependencies = [ "serde", ] +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "bounded-queue" +version = "0.1.0" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cassowary" version = "0.3.0" @@ -62,6 +101,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clippy" +version = "0.0.302" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d911ee15579a3f50880d8c1d59ef6e79f9533127a3bd342462f5d584f5e8c294" +dependencies = [ + "term", +] + [[package]] name = "compact_str" version = "0.7.1" @@ -76,6 +124,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crossterm" version = "0.27.0" @@ -103,6 +163,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dynamic-list" +version = "0.1.0" +dependencies = [ + "itertools 0.10.5", +] + [[package]] name = "either" version = "1.13.0" @@ -115,6 +193,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -200,7 +289,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -246,7 +335,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -257,10 +346,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "performance-queue" -version = "0.1.0" - [[package]] name = "proc-macro2" version = "1.0.86" @@ -275,9 +360,11 @@ name = "process-display" version = "0.1.0" dependencies = [ "anyhow", + "bounded-queue", + "clippy", "crossterm", + "dynamic-list", "itertools 0.10.5", - "performance-queue", "process-list", "ratatui", "serde", @@ -321,6 +408,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.5.2" @@ -330,6 +423,29 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom", + "redox_syscall 0.1.57", + "rust-argon2", +] + +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -466,6 +582,17 @@ dependencies = [ "windows", ] +[[package]] +name = "term" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +dependencies = [ + "byteorder", + "dirs", + "winapi", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -500,6 +627,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 862704f..7822f8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" [workspace] members = [ "process-list", - "performance-queue" + "bounded-queue", + "dynamic-list" ] authours = ["rhasler1 "] @@ -19,4 +20,6 @@ serde = { version = "1.0.188", features = ["derive"] } anyhow = "1.0.97" itertools = "0.10.0" process-list = {path = "./process-list", version = "0.1.0"} -performance-queue = {path = "./performance-queue", version = "0.1.0"} \ No newline at end of file +bounded-queue = {path = "./bounded-queue", version = "0.1.0"} +dynamic-list = {path = "./dynamic-list", version = "0.1.0"} +clippy = "0.0.302" diff --git a/performance-queue/Cargo.toml b/bounded-queue/Cargo.toml similarity index 68% rename from performance-queue/Cargo.toml rename to bounded-queue/Cargo.toml index 9b5dec4..3e6c189 100644 --- a/performance-queue/Cargo.toml +++ b/bounded-queue/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "performance-queue" +name = "bounded-queue" version = "0.1.0" edition = "2021" authours = ["rhasler1 "] -[dependencies] \ No newline at end of file +[dependencies] diff --git a/performance-queue/src/bounded_queue.rs b/bounded-queue/src/bounded_queue.rs similarity index 62% rename from performance-queue/src/bounded_queue.rs rename to bounded-queue/src/bounded_queue.rs index 220f9a9..a38fc45 100644 --- a/performance-queue/src/bounded_queue.rs +++ b/bounded-queue/src/bounded_queue.rs @@ -1,45 +1,44 @@ use std::{collections::VecDeque, io}; #[derive(Default)] -pub struct PerformanceQueue { - pub performance_items: VecDeque, +pub struct BoundedQueue { + pub items: VecDeque, capacity: usize, } // Clone trait is required for T to clone elements when adding items. -impl PerformanceQueue { +impl BoundedQueue { pub fn new(capacity: usize) -> Self { Self { - performance_items: VecDeque::with_capacity(capacity), + items: VecDeque::with_capacity(capacity), capacity, } } - pub fn add_item(&mut self, item: &T) { - if self.performance_items.len() < self.capacity { - let item = item.clone(); - self.performance_items.push_back(item); + pub fn add_item(&mut self, item: T) { + let len = self.items.len(); + + if len < self.capacity { + self.items.push_back(item); } - else if self.performance_items.len() == self.capacity { - self.performance_items.pop_front(); - let item = item.clone(); - self.performance_items.push_back(item); + else if len == self.capacity { + self.items.pop_front(); + self.items.push_back(item); } - else { - while self.performance_items.len() >= self.capacity { - self.performance_items.pop_front(); + else { // should never occur + while len >= self.capacity { + self.items.pop_front(); } - let item = item.clone(); - self.performance_items.push_back(item); + self.items.push_back(item); } } pub fn front(&self) -> Option<&T> { - return self.performance_items.front() + self.items.front() } pub fn back(&self) -> Option<&T> { - return self.performance_items.back() + self.items.back() } pub fn capacity(&self) -> usize { @@ -47,13 +46,13 @@ impl PerformanceQueue { } pub fn iter(&self) -> std::collections::vec_deque::Iter<'_, T> { - self.performance_items.iter() + self.items.iter() } } #[cfg(test)] mod test { - use super::PerformanceQueue; + use super::BoundedQueue; use crate::CpuItem; /*#[test] @@ -73,4 +72,4 @@ mod test { assert_eq!(instance.back().unwrap().global_usage(), 15.7); assert_eq!(instance.front().unwrap().global_usage(), 13.2); }*/ -} \ No newline at end of file +} diff --git a/performance-queue/src/cpu_item.rs b/bounded-queue/src/cpu_item.rs similarity index 99% rename from performance-queue/src/cpu_item.rs rename to bounded-queue/src/cpu_item.rs index 1fa5430..d6d94e3 100644 --- a/performance-queue/src/cpu_item.rs +++ b/bounded-queue/src/cpu_item.rs @@ -71,4 +71,4 @@ mod test { assert_eq!(instance.brand(), String::from("Apple")); } */ -} \ No newline at end of file +} diff --git a/performance-queue/src/lib.rs b/bounded-queue/src/lib.rs similarity index 73% rename from performance-queue/src/lib.rs rename to bounded-queue/src/lib.rs index 049b75e..80f4c37 100644 --- a/performance-queue/src/lib.rs +++ b/bounded-queue/src/lib.rs @@ -4,4 +4,4 @@ mod bounded_queue; pub use cpu_item::CpuItem; pub use memory_item::MemoryItem; -pub use bounded_queue::PerformanceQueue; \ No newline at end of file +pub use bounded_queue::BoundedQueue; diff --git a/performance-queue/src/memory_item.rs b/bounded-queue/src/memory_item.rs similarity index 83% rename from performance-queue/src/memory_item.rs rename to bounded-queue/src/memory_item.rs index 6ec9cda..f1bd572 100644 --- a/performance-queue/src/memory_item.rs +++ b/bounded-queue/src/memory_item.rs @@ -17,35 +17,35 @@ impl MemoryItem { } pub fn total_memory(&self) -> u64 { - return self.total_memory.clone() + self.total_memory } pub fn used_memory(&self) -> u64 { - return self.used_memory.clone() + self.used_memory } pub fn free_memory(&self) -> u64 { - return self.free_memory.clone() + self.free_memory } pub fn available_memory(&self) -> u64 { - return self.available_memory.clone() + self.available_memory } pub fn total_memory_gb(&self) -> f64 { - self.total_memory as f64 / 1000000000 as f64 + self.total_memory as f64 / 1000000000_f64 } pub fn used_memory_gb(&self) -> f64 { - self.used_memory as f64 / 1000000000 as f64 + self.used_memory as f64 / 1000000000_f64 } pub fn free_memory_gb(&self) -> f64 { - self.free_memory as f64 / 1000000000 as f64 + self.free_memory as f64 / 1000000000_f64 } pub fn available_memory_gb(&self) -> f64 { - self.available_memory as f64 / 1000000000 as f64 + self.available_memory as f64 / 1000000000_f64 } } @@ -78,4 +78,4 @@ mod test { assert_eq!(instance.free_memory_gb(), 0.000000003); assert_eq!(instance.available_memory_gb(), 0.000000004); } -} \ No newline at end of file +} diff --git a/dynamic-list/Cargo.toml b/dynamic-list/Cargo.toml new file mode 100644 index 0000000..31bf01b --- /dev/null +++ b/dynamic-list/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "dynamic-list" +version = "0.1.0" +edition = "2021" + +authours = ["rhasler1 "] + +[dependencies] +itertools = "0.10.0" diff --git a/dynamic-list/src/dynamic_list.rs b/dynamic-list/src/dynamic_list.rs new file mode 100644 index 0000000..b131e09 --- /dev/null +++ b/dynamic-list/src/dynamic_list.rs @@ -0,0 +1,45 @@ +pub struct DynamicList { + items: Vec, +} + +impl DynamicList { + pub fn new() -> Self { + DynamicList { + items: Vec::new() + } + } + + pub fn filter(&self, predicate: F) -> DynamicList where + F: Fn(&T) -> bool, + T: Clone, + { + let filtered_items = self.items.iter().filter(|i| predicate(i)).cloned().collect(); + DynamicList { + items: filtered_items + } + } + + pub fn replace_all(&mut self, vec: Vec) { + self.items = vec + } + + pub fn from_vec(vec: Vec) -> Self { + DynamicList { + items: vec + } + } + + pub fn as_slice(&self) -> &[T] { + &self.items + } + + pub fn sort_by(&mut self, compare: F) where + F: FnMut(&T, &T) -> std::cmp::Ordering, + { + self.items.sort_by(compare) + } + + pub fn get(&self, index: usize) -> Option<&T> { + self.items.get(index) + } +} diff --git a/dynamic-list/src/lib.rs b/dynamic-list/src/lib.rs new file mode 100644 index 0000000..5cd31c1 --- /dev/null +++ b/dynamic-list/src/lib.rs @@ -0,0 +1,2 @@ +mod dynamic_list; +pub use dynamic_list::DynamicList; diff --git a/process-list/Cargo.toml b/process-list/Cargo.toml index f128d44..49aaedb 100644 --- a/process-list/Cargo.toml +++ b/process-list/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" authours = ["rhasler1 "] [dependencies] -itertools = "0.10.0" \ No newline at end of file +itertools = "0.10.0" diff --git a/process-list/src/lib.rs b/process-list/src/lib.rs index dd9a2c6..7d88f72 100644 --- a/process-list/src/lib.rs +++ b/process-list/src/lib.rs @@ -3,11 +3,9 @@ mod list_iter; mod process_list_item; mod process_list_items; mod process_list; -mod process_item_info; pub use list_items_iter::ListItemsIterator; pub use list_iter::ListIterator; pub use process_list::{ProcessList, ListSortOrder, MoveSelection}; pub use process_list_items::ProcessListItems; pub use process_list_item::ProcessListItem; -pub use process_item_info::ProcessItemInfo; \ No newline at end of file diff --git a/process-list/src/process_item_info.rs b/process-list/src/process_item_info.rs deleted file mode 100644 index 4330de3..0000000 --- a/process-list/src/process_item_info.rs +++ /dev/null @@ -1,44 +0,0 @@ -#[derive(Clone)] -pub struct ProcessItemInfo { - start_time: u64, - run_time: u64, - accumulated_cpu_time: u64, - status: String, -} - -impl ProcessItemInfo { - pub fn new( - start_time: u64, - run_time: u64, - accumulated_cpu_time: u64, - status: String, - ) -> Self { - Self { - start_time, run_time, accumulated_cpu_time, status - } - } - - pub fn update( - &mut self, - start_time: u64, - run_time: u64, - accumulated_cpu_time: u64, - status: String, - ) { - self.start_time = start_time; - self.run_time = run_time; - self.accumulated_cpu_time = accumulated_cpu_time; - self.status = status; - } - - pub fn get_info(&self) -> Vec { - let info: Vec = [ - self.start_time.to_string(), - self.run_time.to_string(), - self.accumulated_cpu_time.to_string(), - self.status.clone(), - ].to_vec(); - - info - } -} \ No newline at end of file diff --git a/process-list/src/process_list.rs b/process-list/src/process_list.rs index 092a6ff..774a305 100644 --- a/process-list/src/process_list.rs +++ b/process-list/src/process_list.rs @@ -2,24 +2,18 @@ use crate::process_list_items::ProcessListItems; use crate::process_list_item::ProcessListItem; use crate::list_iter::ListIterator; -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Default)] pub enum ListSortOrder { PidInc, PidDec, NameInc, NameDec, CpuUsageInc, - CpuUsageDec, + #[default] CpuUsageDec, MemoryUsageInc, MemoryUsageDec, } -impl Default for ListSortOrder { - fn default() -> Self { - ListSortOrder::CpuUsageDec - } -} - #[derive(Copy, Clone)] pub enum MoveSelection { Up, @@ -37,75 +31,60 @@ pub struct ProcessList { follow_selection: bool, pub selection: Option, } - +/*IMPORTANT: there is a problem with the interaction between change_follow_selection and update*/ impl ProcessList { - // constructor - pub fn new(list: &Vec) -> Self { + pub fn new(list: Vec) -> Self { + assert!(!list.is_empty()); + Self { items: ProcessListItems::new(list), - sort: ListSortOrder::default(), // CpuUsageDec + sort: ListSortOrder::default(), follow_selection: false, - selection: if list.is_empty() { - None - } - else { - Some(0) - }, + selection: Some(0), } } - // constructor for filtered list - pub fn filter(&self, filter_text: &String) -> Self { - let new_self = Self { - items: self.items.filter(filter_text), + pub fn filter(&self, filter_text: &str) -> Self { + let items = self.items.filter(filter_text); + let len = items.items_len(); + + Self { + items, sort: ListSortOrder::default(), follow_selection: false, - selection: if self.items.filter(filter_text).items_len() > 0 { + selection: if len > 0 { Some(0) } else { None }, - }; - new_self + } } - // update process list with new list - pub fn update(&mut self, new_list: &Vec) { - // get the selected item, either some(item) or None - let selected_item: Option<&ProcessListItem> = self.items.get_item(self.selection.unwrap_or_default()); - - // get the selected item's pid, either some(pid) or None + pub fn update(&mut self, new_list: Vec) { + let selected_item: Option<&ProcessListItem> = self.items.get_item_ref(self.selection.unwrap_or_default()); let pid: Option = selected_item.map(|item| item.pid()); - // update items with new list. self.items.update_items(new_list); - - // re-sort self.items.sort_items(&self.sort); - // if list is empty, set selection to None and return if self.items.items_len() == 0 { self.selection = None; return } - // if following, then set selection to selected item's new index if self.follow_selection { self.selection = pid.and_then(|p| self.items.get_idx(p)); } else { - // not following, keep the selection where it is, ensuring it is still in bounds if let Some(selection) = self.selection { let max_idx = self.items.items_len().saturating_sub(1); - if selection > max_idx { self.selection = Some(max_idx) } } } - // if selection is none at this point, set to 0 if self.selection.is_none() { self.selection = Some(0) } @@ -114,7 +93,7 @@ impl ProcessList { // sorts process list by list sort order pub fn sort(&mut self, sort: &ListSortOrder) { // get the selected item, selected_item = Some(item) || None. - let selected_item: Option<&ProcessListItem> = self.items.get_item(self.selection.unwrap_or_default()); + let selected_item: Option<&ProcessListItem> = self.items.get_item_ref(self.selection.unwrap_or_default()); // get the selected item's pid, pid = Some(pid) || None. let pid: Option = selected_item.map(|item| item.pid()); @@ -193,12 +172,7 @@ impl ProcessList { // toggle follow selection pub fn change_follow_selection(&mut self) { - if self.follow_selection { - self.follow_selection = false; - } - else { - self.follow_selection = true; - } + self.follow_selection = !self.follow_selection } pub fn is_empty(&self) -> bool { @@ -221,7 +195,7 @@ impl ProcessList { // gets reference to selected process list item pub fn selected_item(&self) -> Option<&ProcessListItem> { if let Some(selection) = self.selection { - let selected_item = self.items.get_item(selection); + let selected_item = self.items.get_item_ref(selection); return selected_item } None @@ -230,7 +204,7 @@ impl ProcessList { // gets pid of selected process list item pub fn selected_pid(&self) -> Option { if let Some(selection) = self.selection { - if let Some(item) = self.items.get_item(selection) { + if let Some(item) = self.items.get_item_ref(selection) { return Some(item.pid()) } else { @@ -263,7 +237,7 @@ mod test { let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1]; - let instance = ProcessList::new(&items); + let instance = ProcessList::new(items); assert!(!instance.is_empty()); assert_eq!(instance.selection(), Some(0)); @@ -283,46 +257,46 @@ mod test { #[test] fn test_update() { // Update with empty list of items. - let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); - let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); - let items = vec![item_0, item_1]; - let mut instance = ProcessList::new(&items); - let empty_items = vec![]; - let _ = instance.update(&empty_items); - assert!(instance.is_empty()); - assert!(instance.selection().is_none()); + //let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); + //let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); + //let items = vec![item_0, item_1]; + //let mut instance = ProcessList::new(items); + //let empty_items = vec![]; + //let _ = instance.update(empty_items); + //assert!(instance.is_empty()); + //assert!(instance.selection().is_none()); // Update with non-empty list of items. let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1]; - let mut instance = ProcessList::new(&items); + let mut instance = ProcessList::new(items); let item_2 = ProcessListItem::new(3, String::from("c"), 3.0, 3, 0, 10, 10, String::from("test")); let new_items = vec![item_2]; - let _ = instance.update(&new_items); + let _ = instance.update(new_items); assert!(!instance.is_empty()); assert_eq!(instance.selection(), Some(0)); // Update with empty list of items and follow_selection set to true. - let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); - let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); - let items = vec![item_0, item_1]; - let mut instance = ProcessList::new(&items); - let _ = instance.change_follow_selection(); - let empty_items = vec![]; - let _ = instance.update(&empty_items); - assert!(instance.is_empty()); - assert!(instance.selection().is_none()); + //let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); + //let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); + //let items = vec![item_0, item_1]; + //let mut instance = ProcessList::new(items); + //let _ = instance.change_follow_selection(); + //let empty_items = vec![]; + //let _ = instance.update(empty_items); + //assert!(instance.is_empty()); + //assert!(instance.selection().is_none()); // Update with non-empty list of items and follow_selection set to true case 1. let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1]; - let mut instance = ProcessList::new(&items); - let _ = instance.change_follow_selection(); + let mut instance = ProcessList::new(items); + //let _ = instance.change_follow_selection(); let item_2 = ProcessListItem::new(3, String::from("c"), 3.0, 3, 0, 10, 10, String::from("test")); let new_items = vec![item_2]; - let _ = instance.update(&new_items); + let _ = instance.update(new_items); assert!(!instance.is_empty()); assert_eq!(instance.selection(), Some(0)); @@ -330,12 +304,12 @@ mod test { let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1]; - let mut instance = ProcessList::new(&items); - let _ = instance.change_follow_selection(); + let mut instance = ProcessList::new(items); + //let _ = instance.change_follow_selection(); let item_2 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let item_3 = ProcessListItem::new(3, String::from("c"), 3.0, 3, 0, 10, 10, String::from("test")); let new_items = vec![item_2, item_3]; - let _ = instance.update(&new_items); + let _ = instance.update(new_items); assert!(!instance.is_empty()); assert_eq!(instance.selection(), Some(0)); } @@ -346,7 +320,7 @@ mod test { let item_0 = ProcessListItem::new(1, String::from("a"), 2.0, 2, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 1.0, 1, 0, 10, 10, String::from("test")); let items = vec![item_1, item_0]; - let mut instance = ProcessList::new(&items); + let mut instance = ProcessList::new(items); assert!(instance.sort == ListSortOrder::CpuUsageDec); assert!(!instance.is_follow_selection()); assert_eq!(instance.selection(), Some(0)); @@ -357,7 +331,7 @@ mod test { let item_0 = ProcessListItem::new(1, String::from("a"), 2.0, 2, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 1.0, 1, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1]; - let mut instance = ProcessList::new(&items); + let mut instance = ProcessList::new(items); assert!(instance.sort == ListSortOrder::CpuUsageDec); let _ = instance.change_follow_selection(); assert!(instance.is_follow_selection()); @@ -375,7 +349,7 @@ mod test { let item_0 = ProcessListItem::new(1, String::from("a"), 2.0, 2, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 1.0, 1, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1]; - let mut instance = ProcessList::new(&items); + let mut instance = ProcessList::new(items); assert_eq!(instance.selection(), Some(0)); instance.move_selection(MoveSelection::Down); instance.move_selection(MoveSelection::Down); @@ -401,4 +375,4 @@ mod test { instance.move_selection(MoveSelection::MultipleUp); assert_eq!(instance.selection(), Some(0)); } -} \ No newline at end of file +} diff --git a/process-list/src/process_list_item.rs b/process-list/src/process_list_item.rs index 69d4568..ee5b5fe 100644 --- a/process-list/src/process_list_item.rs +++ b/process-list/src/process_list_item.rs @@ -11,7 +11,6 @@ pub struct ProcessListItem { } impl ProcessListItem { - // constructor pub fn new( pid: u32, name: String, @@ -35,17 +34,17 @@ impl ProcessListItem { } // match by name or pid - pub fn is_match(&self, filter_text: &str) -> bool { - self.name.contains(filter_text) || - self.pid.to_string().contains(filter_text) - } + //pub fn is_match(&self, filter_text: &str) -> bool { + // self.name.contains(filter_text) || + // self.pid.to_string().contains(filter_text) + //} pub fn pid(&self) -> u32 { self.pid } - pub fn name(&self) -> String { - self.name.clone() + pub fn name(&self) -> &str { + &self.name } pub fn cpu_usage(&self) -> f32 { @@ -75,9 +74,8 @@ impl ProcessListItem { // PartialEq is needed for comparison, e.g., calling contains impl PartialEq for ProcessListItem { - // comparing by pid fn eq(&self, other: &Self) -> bool { - return self.pid.eq(&other.pid) + self.pid.eq(&other.pid) } } @@ -122,9 +120,9 @@ pub mod test { assert_eq!(instance_0.run_time(), instance_0.run_time); assert_eq!(instance_0.accumulated_cpu_time(), instance_0.accumulated_cpu_time); assert_eq!(instance_0.status(), instance_0.status); - assert_eq!(instance_0.is_match(""), true); - assert_eq!(instance_0.is_match("a"), false); - assert_eq!(instance_0.is_match(&instance_0.pid.to_string()), true); + //assert_eq!(instance_0.is_match(""), true); + //assert_eq!(instance_0.is_match("a"), false); + //assert_eq!(instance_0.is_match(&instance_0.pid.to_string()), true); assert_eq!(instance_1.pid(), instance_1.pid); assert_eq!(instance_1.name(), instance_1.name); @@ -134,8 +132,8 @@ pub mod test { assert_eq!(instance_0.run_time(), instance_0.run_time); assert_eq!(instance_0.accumulated_cpu_time(), instance_0.accumulated_cpu_time); assert_eq!(instance_0.status(), instance_0.status); - assert_eq!(instance_1.is_match("a"), true); - assert_eq!(instance_1.is_match("aa"), false); - assert_eq!(instance_1.is_match(&instance_1.pid.to_string()), true); + //assert_eq!(instance_1.is_match("a"), true); + //assert_eq!(instance_1.is_match("aa"), false); + //assert_eq!(instance_1.is_match(&instance_1.pid.to_string()), true); } -} \ No newline at end of file +} diff --git a/process-list/src/process_list_items.rs b/process-list/src/process_list_items.rs index 2b98745..ae31d39 100644 --- a/process-list/src/process_list_items.rs +++ b/process-list/src/process_list_items.rs @@ -8,54 +8,46 @@ pub struct ProcessListItems { } impl ProcessListItems { - // constructor - pub fn new(list: &Vec) -> Self { + pub fn new(list: Vec) -> Self { Self { - list_items: Self::create_items(list), + list_items: list, } } - // constructor helper - fn create_items(list: &Vec) -> Vec { - list.iter().cloned().collect() - } - - // constructor for filtered list - pub fn filter(&self, filter_text: &String) -> Self { + pub fn filter(&self, filter_text: &str) -> Self { Self { list_items: self.create_filtered_items(filter_text) } } - // filtered constructor helper - fn create_filtered_items(&self, filter_text: &String) -> Vec { + fn create_filtered_items(&self, filter_text: &str) -> Vec { self.list_items .iter() - .filter(|item| { item.is_match(filter_text) }) + .filter(|item| { + item.name().contains(filter_text) || + item.pid().to_string().contains(filter_text) + }) .cloned() .collect() } - // simply replace old list with new list - // this function ruins sort order--call sort after using - pub fn update_items(&mut self, new_list: &Vec) { - self.list_items = new_list.iter().cloned().collect(); + pub fn update_items(&mut self, new_list: Vec) { + self.list_items = new_list; } - // sort by list sort order pub fn sort_items(&mut self, sort: &ListSortOrder) { match sort { ListSortOrder::PidInc => { - self.list_items.sort_by(|a, b| a.pid().cmp(&b.pid())); + self.list_items.sort_by_key(|a| a.pid()); } ListSortOrder::PidDec => { - self.list_items.sort_by(|a, b| b.pid().cmp(&a.pid())); + self.list_items.sort_by_key(|b| std::cmp::Reverse (b.pid())); } ListSortOrder::NameInc => { - self.list_items.sort_by(|a, b| a.name().cmp(&b.name())); + self.list_items.sort_by_key(|a| a.name().to_string()); } ListSortOrder::NameDec => { - self.list_items.sort_by(|a, b| b.name().cmp(&a.name())); + self.list_items.sort_by_key(|b| std::cmp::Reverse (b.name().to_string())); } ListSortOrder::CpuUsageInc => { self.list_items.sort_by(|a, b| a.cpu_usage().partial_cmp(&b.cpu_usage()).unwrap_or(std::cmp::Ordering::Equal)); @@ -64,20 +56,18 @@ impl ProcessListItems { self.list_items.sort_by(|a, b| b.cpu_usage().partial_cmp(&a.cpu_usage()).unwrap_or(std::cmp::Ordering::Equal)); } ListSortOrder::MemoryUsageInc => { - self.list_items.sort_by(|a, b| a.memory_usage().cmp(&b.memory_usage())); + self.list_items.sort_by_key(|a| a.memory_usage()); } ListSortOrder::MemoryUsageDec => { - self.list_items.sort_by(|a, b| b.memory_usage().cmp(&a.memory_usage())); + self.list_items.sort_by_key(|b| std::cmp::Reverse (b.memory_usage())); } } } - // gets the reference to an item given an index - pub fn get_item(&self, idx: usize) -> Option<&ProcessListItem> { + pub fn get_item_ref(&self, idx: usize) -> Option<&ProcessListItem> { self.list_items.get(idx) } - // gets the index of an item given the item's pid. pub fn get_idx(&self, pid: u32) -> Option { if let Some(idx) = self.list_items .iter() @@ -88,12 +78,10 @@ impl ProcessListItems { None } - // gets the length of the instance list. pub fn items_len(&self) -> usize { self.list_items.len() } - // returns a ListItemIterator instance given a start position and max number of iterations. pub const fn iterate(&self, start: usize, max_amount: usize) -> ListItemsIterator<'_> { ListItemsIterator::new(self, start, max_amount) } @@ -111,7 +99,7 @@ mod test { let instance = ProcessListItems::default(); assert_eq!(instance.items_len(), 0); assert_eq!(instance.get_idx(4), None); - assert_eq!(instance.get_item(0), None); + assert_eq!(instance.get_item_ref(0), None); } #[test] @@ -121,16 +109,16 @@ mod test { let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let clone_1 = item_1.clone(); let items = vec![item_0, item_1]; - let instance = ProcessListItems::new(&items); + let instance = ProcessListItems::new(items); assert_eq!(instance.items_len(), 2); assert_eq!(instance.get_idx(1), Some(0)); assert_eq!(instance.get_idx(2), Some(1)); assert_eq!(instance.get_idx(3), None); - assert_eq!(instance.get_item(0), Some(&clone_0)); - assert_eq!(instance.get_item(1), Some(&clone_1)); - assert_eq!(instance.get_item(2), None); + assert_eq!(instance.get_item_ref(0), Some(&clone_0)); + assert_eq!(instance.get_item_ref(1), Some(&clone_1)); + assert_eq!(instance.get_item_ref(2), None); } #[test] @@ -140,12 +128,12 @@ mod test { let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let _clone_1 = item_1.clone(); let items = vec![item_0, item_1]; - let instance = ProcessListItems::new(&items); + let instance = ProcessListItems::new(items); let filtered_instance = instance.filter(&String::from("a")); assert_eq!(filtered_instance.items_len(), 1); - assert_eq!(filtered_instance.get_item(0), Some(&clone_0)); - assert_eq!(filtered_instance.get_item(1), None); + assert_eq!(filtered_instance.get_item_ref(0), Some(&clone_0)); + assert_eq!(filtered_instance.get_item_ref(1), None); assert_eq!(filtered_instance.get_idx(1), Some(0)); assert_eq!(filtered_instance.get_idx(2), None); } @@ -155,7 +143,7 @@ mod test { let item_0 = ProcessListItem::new(1, String::from("a"), 1.0, 1, 0, 10, 10, String::from("test")); let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1]; - let mut instance = ProcessListItems::new(&items); + let mut instance = ProcessListItems::new(items); // Note: ProcessListItem's are compared by Pid. let item_2 = ProcessListItem::new(1, String::from("a"), 7.0, 1337, 0, 10, 10, String::from("test")); @@ -165,7 +153,7 @@ mod test { let _ = instance.sort_items(&ListSortOrder::CpuUsageInc); assert_eq!(instance.get_idx(1), Some(0)); assert_eq!(instance.get_idx(2), Some(1)); - let _ = instance.update_items(&new_items); + let _ = instance.update_items(new_items); let _ = instance.sort_items(&ListSortOrder::CpuUsageInc); // Pid 2 is not in new_items so it should be removed from the instance list. assert_eq!(instance.get_idx(2), None); @@ -181,7 +169,7 @@ mod test { let item_1 = ProcessListItem::new(2, String::from("b"), 2.0, 2, 0, 10, 10, String::from("test")); let item_3 = ProcessListItem::new(3, String::from("c"), 3.0, 3, 0, 10, 10, String::from("test")); let items = vec![item_0, item_1, item_3]; - let mut instance = ProcessListItems::new(&items); + let mut instance = ProcessListItems::new(items); assert_eq!(instance.get_idx(1), Some(0)); assert_eq!(instance.get_idx(2), Some(1)); @@ -226,4 +214,4 @@ mod test { assert_eq!(instance.get_idx(2), Some(1)); assert_eq!(instance.get_idx(3), Some(0)); } -} \ No newline at end of file +} diff --git a/src/app.rs b/src/app.rs index 95865d7..73ff191 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,13 +1,11 @@ use anyhow::{Ok, Result}; -use crossterm::event::KeyEvent; +use crossterm::event::{KeyEvent}; use ratatui::prelude::*; -use crate::components::cpu::CPUComponent; use crate::config::Config; use crate::components::{ - tab::TabComponent, + cpu::CPUComponent, process::ProcessComponent, system_wrapper::SystemWrapper, - system_component::SystemComponent, error::ErrorComponent, Component, EventState, @@ -17,12 +15,8 @@ use crate::components::{ command::CommandInfo, }; -pub enum MainFocus { +enum MainFocus { CPU, - System, - Memory, - Network, - Temperature, Process, } @@ -30,98 +24,69 @@ pub struct App { focus: MainFocus, expand: bool, system_wrapper: SystemWrapper, - system_component: SystemComponent, process: ProcessComponent, cpu: CPUComponent, - tab: TabComponent, help: HelpComponent, pub error: ErrorComponent, pub config: Config, } impl App { - // constructor. pub fn new(config: Config) -> Self { + let mut system_wrapper = SystemWrapper::new(config.clone()); + + system_wrapper.refresh_all(); + + let processes = system_wrapper.get_processes(); + + let mut cpu = CPUComponent::default(); + + let cpus = system_wrapper.get_cpus(); + + cpu.update(cpus); + Self { focus: MainFocus::Process, expand: false, - system_wrapper: SystemWrapper::new(config.clone()), - system_component: SystemComponent::default(), - process: ProcessComponent::new(config.clone()), - cpu: CPUComponent::default(), - tab: TabComponent::new(config.clone()), + system_wrapper, + process: ProcessComponent::new(config.clone(), processes), + cpu, help: HelpComponent::new(config.clone()), error: ErrorComponent::new(config.clone()), config: config.clone(), } } - // call after constructor - pub fn init(&mut self) -> Result<()> { - self.system_wrapper.refresh_all()?; - self.init_system_component(); - self.update_process(); - self.update_cpu(); - //self.update_performance()?; - self.update_commands(); - - Ok(()) - } - - fn init_system_component(&mut self) { - let vec: Vec = SystemWrapper::get_static_sysinfo(); - self.system_component.init(vec); // transfer ownership - } + pub fn refresh_event(&mut self) -> Result { + self.system_wrapper.refresh_all(); - // refresh system and dependencies - pub fn refresh(&mut self) -> Result<()>{ - self.system_wrapper.refresh_all()?; self.update_process(); + self.update_cpu(); - //self.update_performance()?; - Ok(()) - } + self.update_cmds(); - fn update_cpu(&mut self) -> bool { - let new_cpus = self.system_wrapper.get_cpus(); - self.cpu.update(&new_cpus); - true + Ok(EventState::Consumed) } - // return result of process update - fn update_process(&mut self) -> bool { - let new_processes = self.system_wrapper.get_processes(); - let res = self.process.update(&new_processes); - - res + fn update_process(&mut self) { + let new_processes = self.system_wrapper.get_processes(); // receive ownership + + self.process.update(new_processes); // transfer ownership } - // fix return type - //fn update_performance(&mut self) -> Result<()> { - // let new_cpu_info = self.system.get_cpu_info(); - // let new_memory_info = self.system.get_memory_info(); - // self.performance.update(&new_cpu_info, &new_memory_info)?; + fn update_cpu(&mut self) { + let new_cpus = self.system_wrapper.get_cpus(); // receive ownership - // Ok(()) - //} + self.cpu.update(new_cpus); // transfer ownership + } fn toggle_expand(&mut self) { - if self.expand == true { - self.expand = false; - } - else { - self.expand = true; - } + self.expand = !self.expand } - // top level key event processor - pub fn event(&mut self, key: KeyEvent) -> Result { - //if key.code == self.config.key_config.toggle_themes { - // self.update_component_themes(); - // return Ok(EventState::Consumed) - //} - if self.components_event(key)?.is_consumed() { + pub fn key_event(&mut self, key: KeyEvent) -> Result { + if self.component_event(key)?.is_consumed() { return Ok(EventState::Consumed); } else if self.move_focus(key)?.is_consumed() { @@ -129,51 +94,14 @@ impl App { } else if key.code == self.config.key_config.expand { self.toggle_expand(); + return Ok(EventState::Consumed); } Ok(EventState::NotConsumed) } - // toggle color scheme - //fn update_component_themes(&mut self) { - // self.config.theme_config.toggle_themes(); - // self.process.config.theme_config.toggle_themes(); - // //self.performance.config.theme_config.toggle_themes(); - // self.help.config.theme_config.toggle_themes(); - // self.system._config.theme_config.toggle_themes(); - // self.tab.config.theme_config.toggle_themes(); - //} - - // update help dialogue--commands - fn update_commands(&mut self) { - self.help.set_commands(self.commands()); - } - - // set commands - fn commands(&self) -> Vec { - let res = vec![ - CommandInfo::new(command::help(&self.config.key_config)), - CommandInfo::new(command::exit_popup(&self.config.key_config)), - CommandInfo::new(command::change_tab(&self.config.key_config)), - CommandInfo::new(command::move_selection(&self.config.key_config)), - CommandInfo::new(command::selection_to_top_bottom(&self.config.key_config)), - CommandInfo::new(command::follow_selection(&self.config.key_config)), - CommandInfo::new(command::sort_list_by_name(&self.config.key_config)), - CommandInfo::new(command::sort_list_by_pid(&self.config.key_config)), - CommandInfo::new(command::sort_list_by_cpu_usage(&self.config.key_config)), - CommandInfo::new(command::sort_list_by_memory_usage(&self.config.key_config)), - CommandInfo::new(command::filter_submit(&self.config.key_config)), - CommandInfo::new(command::terminate_process(&self.config.key_config)), - ]; - - res - } - - /* */ - - // component key event processor - fn components_event(&mut self, key: KeyEvent) -> Result { + fn component_event(&mut self, key: KeyEvent) -> Result { if self.error.event(key)?.is_consumed() { return Ok(EventState::Consumed) } @@ -188,14 +116,6 @@ impl App { return Ok(EventState::Consumed) } } - MainFocus::System => { - if self.system_component.event(key)?.is_consumed() { - return Ok(EventState::Consumed) - } - } - MainFocus::Memory => {} - MainFocus::Network => {} - MainFocus::Temperature => {} MainFocus::Process => { if self.process.event(key)?.is_consumed() { return Ok(EventState::Consumed) @@ -203,46 +123,26 @@ impl App { } } - if self.tab.event(key)?.is_consumed() { - return Ok(EventState::Consumed); - } - Ok(EventState::NotConsumed) } - - /* - Control with Tab - CPU -> Memory -> Network -> Temperature -> Process -> ... - */ fn move_focus(&mut self, key: KeyEvent) -> Result { if key.code == self.config.key_config.tab { match self.focus { MainFocus::CPU => { - self.focus = MainFocus::Memory - } - MainFocus::Memory => { - self.focus = MainFocus::Network - } - MainFocus::Network => { - self.focus = MainFocus::Temperature - } - MainFocus::Temperature => { self.focus = MainFocus::Process } MainFocus::Process => { - self.focus = MainFocus::System - } - MainFocus::System => { self.focus = MainFocus::CPU } } + return Ok(EventState::Consumed) } + Ok(EventState::NotConsumed) } - // draw the app pub fn draw(&mut self, f: &mut Frame) -> Result<()> { let chunks = Layout::default() .direction(Direction::Vertical) @@ -251,12 +151,10 @@ impl App { ]) .split(f.size()); - // if error always draw--error component state determines if anything is drawn self.error.draw(f, chunks[0], false)?; if self.expand { - // split screen to draw only focused component if matches!(self.focus, MainFocus::Process) { self.process.draw( f, @@ -272,35 +170,25 @@ impl App { true, )?; } - - if matches!(self.focus, MainFocus::System) { - self.system_component.draw( - f, - chunks[0], - true, - )?; - } } else { - // draw all components - // split screen let vertical_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Percentage(25), - Constraint::Percentage(25), - Constraint::Percentage(25), - Constraint::Percentage(25), + Constraint::Percentage(75), ].as_ref()) .split(chunks[0]); let mut horizontal_chunks = Vec::new(); + for chunk in vertical_chunks.iter() { let horizontal_chunk = Layout::default() .direction(Direction::Horizontal) .constraints([ - Constraint::Fill(1), - Constraint::Percentage(80), + Constraint::Percentage(33), + Constraint::Percentage(33), + Constraint::Percentage(34), ]) .split(*chunk); @@ -309,16 +197,10 @@ impl App { self.process.draw( f, - horizontal_chunks[3][1], + vertical_chunks[1], matches!(self.focus, MainFocus::Process) )?; - self.system_component.draw( - f, - horizontal_chunks[3][0], - matches!(self.focus, MainFocus::System) - )?; - self.cpu.draw( f, vertical_chunks[0], @@ -326,9 +208,34 @@ impl App { )?; } - // if help--help component state determines if anything is drawn self.help.draw(f, Rect::default(), false)?; return Ok(()) } -} \ No newline at end of file + + fn update_cmds(&mut self) { + let cmds = self.commands(); + + self.help.set_commands(cmds); + } + + fn commands(&self) -> Vec { + let res = vec![ + CommandInfo::new(command::help(&self.config.key_config)), + CommandInfo::new(command::exit_popup(&self.config.key_config)), + CommandInfo::new(command::change_tab(&self.config.key_config)), + CommandInfo::new(command::move_selection(&self.config.key_config)), + CommandInfo::new(command::selection_to_top_bottom(&self.config.key_config)), + CommandInfo::new(command::follow_selection(&self.config.key_config)), + CommandInfo::new(command::sort_list_by_name(&self.config.key_config)), + CommandInfo::new(command::sort_list_by_pid(&self.config.key_config)), + CommandInfo::new(command::sort_list_by_cpu_usage(&self.config.key_config)), + CommandInfo::new(command::sort_list_by_memory_usage(&self.config.key_config)), + CommandInfo::new(command::filter_submit(&self.config.key_config)), + CommandInfo::new(command::terminate_process(&self.config.key_config)), + ]; + + res + } +} + diff --git a/src/components/command_new.rs b/src/components/command_new.rs new file mode 100644 index 0000000..c77043f --- /dev/null +++ b/src/components/command_new.rs @@ -0,0 +1,29 @@ +use crate::config; + +static HELP_GROUP: &str = "-- General --"; + +pub struct HelpText { + pub name: String, + pub group: &'static str, +} + +impl HelpText { + pub fn new(name: String, group: &'static str) -> Self { + Self { + name, + group, + } + } +} + +pub struct HelpInfo { + pub text: HelpText, +} + +impl HelpInfo { + pub fn new(text: HelpText) -> Self { + Self { + text + } + } +} \ No newline at end of file diff --git a/src/components/cpu.rs b/src/components/cpu.rs index 89630cd..e17c3d5 100644 --- a/src/components/cpu.rs +++ b/src/components/cpu.rs @@ -6,14 +6,14 @@ use ratatui::widgets::{Axis, Block, Borders, Chart, Dataset, GraphType, List, Li use std::str::FromStr; use anyhow::Ok; -use performance_queue::{PerformanceQueue, CpuItem}; +use bounded_queue::{BoundedQueue, CpuItem}; use crate::config::Config; use super::{Component, DrawableComponent}; #[derive(Default)] pub struct CPUComponent { - cpus: BTreeMap>, + cpus: BTreeMap>, ui_selection: usize, config: Config, } @@ -72,14 +72,16 @@ impl ColorWheel { } impl CPUComponent { - pub fn update(&mut self, cpus: &Vec) { + // has ownership + pub fn update(&mut self, cpus: Vec) { for cpu in cpus { let id = cpu.id(); let perf_q = self.cpus.entry(id).or_insert_with(|| { - PerformanceQueue::new(self.config.events_per_min() as usize) + BoundedQueue::new(self.config.events_per_min() as usize) }); + // passes ownership perf_q.add_item(cpu); } } @@ -260,4 +262,4 @@ impl DrawableComponent for CPUComponent { Ok(()) } -} \ No newline at end of file +} diff --git a/src/components/error.rs b/src/components/error.rs index 40bb97a..5ed839c 100644 --- a/src/components/error.rs +++ b/src/components/error.rs @@ -20,7 +20,7 @@ impl ErrorComponent { Self { error: String::new(), visible: false, - config: config, + config, } } } @@ -77,4 +77,4 @@ impl DrawableComponent for ErrorComponent { } Ok(()) } -} \ No newline at end of file +} diff --git a/src/components/filter.rs b/src/components/filter.rs index edab2e4..1b20964 100644 --- a/src/components/filter.rs +++ b/src/components/filter.rs @@ -18,7 +18,7 @@ impl FilterComponent { pub fn new(config: Config) -> Self { Self { input_str: String::new(), - config: config, + config, } } @@ -26,12 +26,12 @@ impl FilterComponent { self.input_str.clear(); } - pub fn input_str(&mut self) -> String { - return self.input_str.clone(); + pub fn input_str(&mut self) -> &str { + &self.input_str } pub fn is_filter_empty(&mut self) -> bool { - return self.input_str.is_empty(); + self.input_str.is_empty() } } @@ -53,7 +53,7 @@ impl Component for FilterComponent { impl DrawableComponent for FilterComponent { fn draw(&mut self, f: &mut Frame, area: ratatui::prelude::Rect, focused: bool) -> Result<()> { - let title: &str = "Filter"; + let title: &str = " Filter "; let style: Style = if focused { @@ -73,6 +73,6 @@ impl DrawableComponent for FilterComponent { f.render_widget(widget, area); - return Ok(()) + Ok(()) } -} \ No newline at end of file +} diff --git a/src/components/help.rs b/src/components/help.rs index 31d2049..bdcd424 100644 --- a/src/components/help.rs +++ b/src/components/help.rs @@ -25,7 +25,7 @@ impl HelpComponent { cmds: vec![], visible: false, selection: 0, - config: config, + config, } } @@ -172,4 +172,4 @@ impl DrawableComponent for HelpComponent { } Ok(()) } -} \ No newline at end of file +} diff --git a/src/components/mod.rs b/src/components/mod.rs index dd90483..90ca5f4 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -8,13 +8,11 @@ pub mod filter; pub mod help; pub mod error; pub mod process; -pub mod performance; -pub mod tab; pub mod utils; pub mod command; -pub mod vertical_tabs; pub mod cpu; pub mod system_component; +pub mod command_new; pub trait DrawableComponent { fn draw(&mut self, f: &mut Frame, area: Rect, focused: bool) -> Result<()>; @@ -82,4 +80,4 @@ pub fn common_sort(key: KeyEvent, key_config: &KeyConfig) -> Option, - memory_info: PerformanceQueue, - vertical_tabs: VerticalTabComponent, - pub config: Config, -} - -impl PerformanceComponent { - pub fn new(config: Config, max_size: usize) -> Self { - Self { - cpu_info: PerformanceQueue::new(max_size), - memory_info: PerformanceQueue::new(max_size), - vertical_tabs: VerticalTabComponent::default(), - config, - } - } - - pub fn update(&mut self, cpu_item: &CpuItem, memory_item: &MemoryItem) -> Result<()> { - self.cpu_info.add_item(cpu_item); - self.memory_info.add_item(memory_item); - Ok(()) - } - - fn draw_memory_graph(&self, f: &mut Frame, area: Rect) -> Result<()> { - let refresh_rate = (self.config.refresh_rate() / 1000) as usize; // Converting ms to s. - let data_points = self.memory_info.performance_items - .iter() - .enumerate() - .map(|(i, item)| ((i * refresh_rate) as f64, item.used_memory_gb() as f64)) - .collect::>(); - - let y_axis_title = String::from("Used Memory (GB)"); - let y_bounds = [0.0, self.memory_info.back().unwrap().total_memory_gb() as f64]; - draw_graph(f, area, refresh_rate, self.memory_info.capacity(), data_points, y_axis_title, y_bounds)?; - Ok(()) - } - - fn draw_cpu_graph(&self, f: &mut Frame, area: Rect) -> Result<()> { - let refresh_rate = (self.config.refresh_rate() / 1000) as usize; // Converting ms to s. - let data_points = self.cpu_info.performance_items - .iter() - .enumerate() - .map(|(i, item)| ((i * refresh_rate) as f64, item.global_usage() as f64)) - .collect::>(); - - let y_axis_title = String::from("Global CPU Usage (%)"); - let y_bounds = [0.0, 100.0]; - draw_graph(f, area, refresh_rate, self.cpu_info.capacity(), data_points, y_axis_title, y_bounds)?; - Ok(()) - } - - fn draw_cpu_item(&self, f: &mut Frame, area: Rect) -> Result<()> { - //TODO - if let Some(item) = self.cpu_info.back() { - let info = vec![ - Line::from(vec![ - Span::raw("Global CPU Usage: "), - Span::raw(item.global_usage().to_string()), - Span::raw("%"), - ]) - .style(Color::White), - Line::from(vec![ - Span::raw("CPU Brand: "), - Span::raw(item.brand().to_string()), - ]) - .style(Color::White), - //Line::from(vec![ - // Span::raw("Number of Cores: "), - // Span::raw(item.num_cores().unwrap_or_default().to_string()), - //]) - //.style(Color::White), - Line::from(vec![ - Span::raw("Frequency: "), - Span::raw(item.frequency().to_string()), - Span::raw(" MHz"), - ]) - .style(Color::White), - ]; - - let widget = Paragraph::new(info) - .block(Block::default().title("Cpu Info").borders(Borders::ALL)) - .style(Style::default().fg(Color::DarkGray)); - - f.render_widget(widget, area); - } - Ok(()) - } - - fn draw_memory_item(&self, f: &mut Frame, area: Rect) -> Result<()> { - if let Some(item) = self.memory_info.back() { - let info = vec![ - Line::from(vec![ - Span::raw("Total RAM: "), - Span::raw(item.total_memory_gb().to_string()), - Span::raw(" GB"), - ]) - .style(Color::White), - Line::from(vec![ - Span::raw("Used RAM: "), - Span::raw(item.used_memory_gb().to_string()), - Span::raw(" GB"), - ]) - .style(Color::White), - Line::from(vec![ - Span::raw("Free RAM: "), - Span::raw(item.free_memory_gb().to_string()), - Span::raw(" GB"), - ]) - .style(Color::White), - Line::from(vec![ - Span::raw("Available RAM: "), - Span::raw(item.available_memory_gb().to_string()), - Span::raw(" GB"), - ]) - .style(Color::White), - ]; - - let widget = Paragraph::new(info) - .block(Block::default().title("Memory Info").borders(Borders::ALL)) - .style(Style::default().fg(Color::DarkGray)); - - f.render_widget(widget, area); - } - Ok(()) - } -} - -impl Component for PerformanceComponent { - fn event(&mut self, key: KeyEvent) -> Result { - //todo - self.vertical_tabs.event(key)?; - Ok(EventState::NotConsumed) - } -} - -impl DrawableComponent for PerformanceComponent { - fn draw(&mut self, f: &mut Frame, area: Rect, _focused: bool) -> Result<()> { - let vertical_chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Length(3), // filter chunk - Constraint::Min(1), // graph chunk - Constraint::Length(6), // cpu info - ].as_ref()) - .split(area); - - let horizontal_chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Percentage(50), // more tabs - Constraint::Percentage(50), // total cpu usage over time graph - ].as_ref()) - .split(vertical_chunks[2]); - - if matches!(self.vertical_tabs.selected_vert_tab, VerticalTab::Cpu) { - self.draw_cpu_graph(f, vertical_chunks[1])?; - self.draw_cpu_item(f, horizontal_chunks[1])?; - } - else if matches!(self.vertical_tabs.selected_vert_tab, VerticalTab::Memory) { - self.draw_memory_graph(f, vertical_chunks[1])?; - self.draw_memory_item(f, horizontal_chunks[1])?; - } - //else if matches!(self.vertical_tabs.selected_vert_tab, VerticalTab::Network) { - //self.draw_network_graph(f, vertical_chunks[1])?; - //} - self.vertical_tabs.draw(f, horizontal_chunks[0], false)?; - - Ok(()) - } -} - -fn draw_graph( - f: &mut Frame, - area: Rect, - refresh_rate: usize, - max_size: usize, - data_points: Vec<(f64, f64)>, - y_axis_title: String, - y_bounds: [f64; 2], -) -> Result<()> { - let data_set = vec![ - Dataset::default() - .marker(Marker::Dot) - .graph_type(GraphType::Line) - .style(Style::default().cyan()) - .data(&data_points) - ]; - - let x_axis = Axis::default() - .title("Time (s)") - .style(Style::default().white()) - .bounds([0.0, ((max_size - 1) * refresh_rate as usize) as f64]) - .labels(vec![ - ((max_size - 1) * refresh_rate as usize).to_string().into(), - 0.to_string().into(), - ]); - - let y_axis = Axis::default() - .title(y_axis_title) - .style(Style::default().white()) - .bounds(y_bounds) - .labels(vec![y_bounds[0].to_string().into(), y_bounds[1].to_string().into()]); - - let chart = Chart::new(data_set) - .block(Block::default()) - .x_axis(x_axis) - .y_axis(y_axis); - - f.render_widget(chart, area); - - Ok(()) -} \ No newline at end of file diff --git a/src/components/process.rs b/src/components/process.rs index 42ba8fc..5f5fe75 100644 --- a/src/components/process.rs +++ b/src/components/process.rs @@ -30,33 +30,39 @@ pub struct ProcessComponent { } impl ProcessComponent { - // constructor - pub fn new(config: Config) -> Self { + pub fn new(config: Config, processes: Vec) -> Self { Self { focus: Focus::List, - list: ProcessList::default(), + list: ProcessList::new(processes), filter: FilterComponent::new(config.clone()), filtered_list: None, scroll: VerticalScroll::new(), - config: config, + config, } } // update process list, return true if new processes is non empty - pub fn update(&mut self, new_processes: &Vec) -> bool { - if new_processes.is_empty() { - return false - } + // given ownersip + pub fn update(&mut self, new_processes: Vec) -> bool { + // should never be empty + assert!(!new_processes.is_empty()); + + // two vectors are needed to update list and filtered list + let dup = new_processes.clone(); + // Must pass by reference to update both lists self.list.update(new_processes); if let Some(filtered_list) = self.filtered_list.as_mut() { + // implement a better way to update the filtered list // filter new processes - let processes = ProcessListItems::new(new_processes); - let filter_text = self.filter.input_str(); - let filtered_processes = processes.filter(&filter_text); - filtered_list.update(&filtered_processes.list_items); + let processes = ProcessListItems::new(dup); + //let filter_text = self.filter.input_str(); + let filtered_processes = processes.filter(self.filter.input_str()); + // transfer ownership + filtered_list.update(filtered_processes.list_items); } + true } @@ -91,7 +97,7 @@ impl Component for ProcessComponent { None } else { - Some(self.list.filter(&self.filter.input_str())) + Some(self.list.filter(self.filter.input_str())) }; } @@ -268,4 +274,4 @@ impl DrawableComponent for ProcessComponent { #[cfg(test)] mod test { // TODO: write tests -} \ No newline at end of file +} diff --git a/src/components/system_wrapper.rs b/src/components/system_wrapper.rs index 957d253..9c8a376 100644 --- a/src/components/system_wrapper.rs +++ b/src/components/system_wrapper.rs @@ -1,13 +1,9 @@ use std::vec; -use sysinfo::*; use anyhow::Result; -use crossterm::event::KeyEvent; -use sysinfo::{Cpu, Pid, System}; +use sysinfo::{Pid, System}; use process_list::ProcessListItem; -use performance_queue::{CpuItem, MemoryItem}; +use bounded_queue::CpuItem; use crate::config::Config; -use super::{Component, EventState}; -use process_list::ProcessItemInfo; // See here for refreshing system: https://crates.io/crates/sysinfo#:~:text=use%20sysinfo%3A%3ASystem,(sysinfo%3A%3AMINIMUM_CPU_UPDATE_INTERVAL)%3B%0A%7D // note: sysinfo::MINIMUM_CPU_UPDATE_INTERVAL = 200 ms @@ -25,10 +21,8 @@ impl SystemWrapper { } } - pub fn refresh_all(&mut self) -> Result { + pub fn refresh_all(&mut self) { self.system.refresh_all(); - - Ok(EventState::Consumed) } pub fn get_cpus(&self) -> Vec { @@ -138,19 +132,4 @@ impl SystemWrapper { } Ok(true) } - - /* - pub fn get_memory_info(&self) -> MemoryItem { - let total_memory = self.system.total_memory(); - let used_memory = self.system.used_memory(); - let free_memory = self.system.free_memory(); - let available_memory = self.system.available_memory(); - let memory_info = MemoryItem::new(total_memory, used_memory, free_memory, available_memory); - memory_info - } -*/ - - //pub fn get_network_info(&self) -> NetworkItem { - - //} -} \ No newline at end of file +} diff --git a/src/components/tab.rs b/src/components/tab.rs deleted file mode 100644 index 2dba861..0000000 --- a/src/components/tab.rs +++ /dev/null @@ -1,145 +0,0 @@ -use anyhow::Result; -use ratatui::{ - Frame, - prelude::*, - widgets::{block::*, *}, - text::Span, -}; -use super::{DrawableComponent, Component, EventState}; -use crate::config::Config; - -#[derive(Clone, PartialEq)] -enum MoveTabDirection { - Left, - Right, -} - -#[derive(Clone)] -pub enum Tab { - Process, - CPU, - Memory, - Disk, - //Users, -} - -pub struct TabComponent { - pub selected_tab: Tab, - pub config: Config, -} - -impl TabComponent { - // default constructor - pub fn new(config: Config) -> Self { - Self { - selected_tab: Tab::Process, - config: config, - } - } - - // set internal TabComponent State to default - pub fn reset(&mut self) { - self.selected_tab = Tab::Process; - self.config = Config::default(); - } - - // String representation of Tab variants used in self.draw() - fn names(&self) -> Vec { - vec![ - String::from("Process"), - String::from("CPU"), - String::from("Memory"), - String::from("Disk"), - //String::from("Users"), - ] - } - - fn update_selected_tab(&mut self, direction: MoveTabDirection) { - match self.selected_tab { - Tab::Process => { - if direction == MoveTabDirection::Right { - self.selected_tab = Tab::CPU; - } - else { - self.selected_tab = Tab::Disk; - } - } - Tab::CPU => { - if direction == MoveTabDirection::Right { - self.selected_tab = Tab::Memory; - } - else { - self.selected_tab = Tab::Process; - } - } - Tab::Memory => { - if direction == MoveTabDirection::Right { - self.selected_tab = Tab::Disk; - } - else { - self.selected_tab = Tab::CPU; - } - } - Tab::Disk => { - if direction == MoveTabDirection::Right { - self.selected_tab = Tab::Process; - } - else { - self.selected_tab = Tab::Memory; - } - } - } - } -} - -impl Component for TabComponent { - fn event(&mut self, key: crossterm::event::KeyEvent) -> Result { - if key.code == self.config.key_config.tab_right { - self.update_selected_tab(MoveTabDirection::Right); - return Ok(EventState::Consumed); - } - else if key.code == self.config.key_config.tab_left { - self.update_selected_tab(MoveTabDirection::Left); - return Ok(EventState::Consumed); - } - return Ok(EventState::NotConsumed); - } -} - -impl DrawableComponent for TabComponent { - fn draw(&mut self, f: &mut Frame, area: Rect, _focused: bool) -> Result<()> { - let vertical_chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Length(3), // filter and tab chunk - Constraint::Min(1), // list chunk - Constraint::Length(3), // filter chunk - ].as_ref()) - .split(area); - - let names: Vec = self.names(); - let titles: Vec = names - .iter() - .map( - |name| - Line::from( - Span::raw( - name.clone() - ) - ) - ) - .collect(); - let selected_tab = self.selected_tab.clone() as usize; - let selected_tab_style = Style::default().fg(Color::White).add_modifier(Modifier::BOLD); - let other_tab_style = Style::default().fg(Color::DarkGray); - let tabs: Tabs = Tabs::new(titles) - .block(Block::default().borders(Borders::ALL)) - .select(selected_tab) - .style(other_tab_style) - .highlight_style(selected_tab_style); - - f.render_widget(tabs, vertical_chunks[0]); - - return Ok(()) - } -} \ No newline at end of file diff --git a/src/components/utils/color_wheel.rs b/src/components/utils/color_wheel.rs new file mode 100644 index 0000000..8e67cc6 --- /dev/null +++ b/src/components/utils/color_wheel.rs @@ -0,0 +1,52 @@ +#[derive(PartialEq, Clone, Copy)] +pub enum ColorWheel { + Red, + Blue, + Cyan, + Green, + LightGreen, + Magenta, +} + +impl Default for ColorWheel { + fn default() -> Self { + ColorWheel::Red + } +} + +impl ColorWheel { + const ALL: [ColorWheel; 6] = [ + ColorWheel::Red, + ColorWheel::Blue, + ColorWheel::Cyan, + ColorWheel::Green, + ColorWheel::LightGreen, + ColorWheel::Magenta, + ]; + + pub fn as_str(&self) -> &'static str { + match self { + ColorWheel::Red => "red", + ColorWheel::Blue => "blue", + ColorWheel::Cyan => "cyan", + ColorWheel::Green => "green", + ColorWheel::LightGreen => "lightgreen", + ColorWheel::Magenta => "magenta", + } + } + + pub fn rotate(&mut self) { + if let Some(idx) = Self::ALL.iter().position(|c| c == self) { + let next_idx = (idx + 1) % Self::ALL.len(); + *self = Self::ALL[next_idx]; + } + } + + pub fn from_index(index: usize) -> Self { + Self::ALL[index % Self::ALL.len()] + } + + pub fn reset(&mut self) { + *self = Self::default(); + } +} diff --git a/src/components/utils/vertical_scroll.rs b/src/components/utils/vertical_scroll.rs index 68a5bba..dd08534 100644 --- a/src/components/utils/vertical_scroll.rs +++ b/src/components/utils/vertical_scroll.rs @@ -92,10 +92,15 @@ fn draw_scrollbar(f: &mut Frame, area: Rect, top: usize, count: usize, focused: }); let mut scrollbar_state = ScrollbarState::new(count).position(top); - f.render_stateful_widget(scrollbar, - area.inner(&Margin { - vertical: 1, - horizontal: 0, - }), - &mut scrollbar_state) + + f.render_stateful_widget( + scrollbar, + area.inner( + &Margin { + vertical: 1, + horizontal: 0, + } + ), + &mut scrollbar_state + ); } \ No newline at end of file diff --git a/src/components/vertical_tabs.rs b/src/components/vertical_tabs.rs deleted file mode 100644 index eb6723a..0000000 --- a/src/components/vertical_tabs.rs +++ /dev/null @@ -1,130 +0,0 @@ -use anyhow::Result; -use ratatui::{ - Frame, - prelude::*, - widgets::{block::*, *}, - text::Span, -}; -use super::{DrawableComponent, Component, EventState}; -use crate::config::KeyConfig; - -#[derive(Clone, PartialEq)] -pub enum MoveTabDirection { - Up, - Down, -} - -#[derive(Clone)] -pub enum VerticalTab { - Cpu, - Memory, - //Network, -} - -impl Default for VerticalTab { - fn default() -> Self { - VerticalTab::Cpu - } -} - -#[derive(Default)] -pub struct VerticalTabComponent { - pub selected_vert_tab: VerticalTab, - key_config: KeyConfig, -} - -impl VerticalTabComponent { - pub fn new(key_config: KeyConfig) -> Self { - Self { - selected_vert_tab: VerticalTab::Cpu, - key_config: key_config, - } - } - - pub fn reset(&mut self) { - self.selected_vert_tab = VerticalTab::Cpu; - self.key_config = KeyConfig::default(); - } - - fn names(&self) -> Vec { - vec![ - String::from("CPU"), - String::from("Memory"), - //String::from("Network"), - ] - } - - fn update_selected_tab(&mut self, direction: MoveTabDirection) { - match self.selected_vert_tab { - VerticalTab::Cpu => { - if direction == MoveTabDirection::Up { - self.selected_vert_tab = VerticalTab::Memory; - } - else { - self.selected_vert_tab = VerticalTab::Memory; - } - } - VerticalTab::Memory => { - if direction == MoveTabDirection::Up { - self.selected_vert_tab = VerticalTab::Cpu; - } - else { - self.selected_vert_tab = VerticalTab::Cpu; - } - } - //VerticalTab::Network => { - // if direction == MoveTabDirection::Up { - // self.selected_vert_tab = VerticalTab::Memory; - // } - // else { - // self.selected_vert_tab = VerticalTab::Cpu; - // } - //} - } - } -} - -impl Component for VerticalTabComponent { - fn event(&mut self, key: crossterm::event::KeyEvent) -> Result { - if key.code == self.key_config.move_up { - self.update_selected_tab(MoveTabDirection::Up); - return Ok(EventState::Consumed); - } - else if key.code == self.key_config.move_down { - self.update_selected_tab(MoveTabDirection::Down); - return Ok(EventState::Consumed); - } - return Ok(EventState::NotConsumed) - } -} - -impl DrawableComponent for VerticalTabComponent { - fn draw(&mut self, f: &mut Frame, area: Rect, _focused: bool) -> Result<()> { - let selected_tab = self.selected_vert_tab.clone() as usize; - let selected_style = Style::default().fg(Color::White).add_modifier(Modifier::BOLD); - let default_style = Style::default().fg(Color::DarkGray); - - let titles: Vec = self.names() - .iter() - .enumerate() - .map(|(idx, name)| - Line::from( - if idx == selected_tab { - Span::styled(name.clone(), selected_style) - } - else { - Span::styled(name.clone(), default_style) - } - ) - ) - .collect(); - - let widget = Paragraph::new(titles) - .block(Block::default().borders(Borders::ALL).title("Performance Focus")) - .style(Style::default().fg(Color::DarkGray)); - - f.render_widget(widget, area); - - Ok(()) - } -} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index d8f62ee..cafff6d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,9 +16,9 @@ impl Default for Config { Self { key_config: KeyConfig::default(), theme_config: ThemeConfig::default(), - refresh_rate: 5000, + refresh_rate: 1000, min_as_s: 60000/ 1000, - events_per_min: 60000 / 5000, + events_per_min: 60000 / 1000, tick_rate: 250, } } @@ -165,4 +165,4 @@ impl Default for ThemeConfig { component_in_focus: Style::default().fg(Color::LightGreen), } } -} \ No newline at end of file +} diff --git a/src/events/event.rs b/src/events/event.rs index 7352d98..9899bf2 100644 --- a/src/events/event.rs +++ b/src/events/event.rs @@ -1,4 +1,4 @@ -use crossterm::event::{self, KeyEvent, KeyCode}; +use crossterm::event::{self, KeyEvent, KeyCode, Event as CEvent, KeyEventKind}; use std::sync::mpsc; use std::{thread, time::Duration}; @@ -57,8 +57,12 @@ impl Events { let input_tx = tx.clone(); thread::spawn(move || loop { if event::poll(config.tick_rate).unwrap() { - if let event::Event::Key(key) = event::read().unwrap() { - input_tx.send(Event::Input(key)).unwrap(); + if let Ok(event) = event::read() { + if let CEvent::Key(key) = event { + if key.kind == KeyEventKind::Press { + input_tx.send(Event::Input(key)).unwrap(); + } + } } } input_tx.send(Event::Tick).unwrap(); diff --git a/src/main.rs b/src/main.rs index bab5717..72b1ace 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,6 @@ pub mod ui; pub mod events; fn main() -> Result<()> { - // terminal setup setup_terminal()?; let backend = CrosstermBackend::new(io::stdout()); @@ -38,7 +37,7 @@ fn main() -> Result<()> { let mut app = App::new(config); - app.init()?; + //app.init()?; // clear terminal terminal.clear()?; @@ -58,7 +57,7 @@ fn main() -> Result<()> { // process next event match events.next()? { - Event::Input(key) => match app.event(key) { + Event::Input(key) => match app.key_event(key) { Ok(state) => { if !state.is_consumed() && key.code == app.config.key_config.exit_popup { break; @@ -68,7 +67,7 @@ fn main() -> Result<()> { app.error.set(err.to_string())?; } } - Event::Refresh => match app.refresh() { + Event::Refresh => match app.refresh_event() { Ok(_state) => {} Err(err) => { app.error.set(err.to_string())?; @@ -94,4 +93,4 @@ fn setup_terminal() -> Result<()> { enable_raw_mode()?; io::stdout().execute(EnterAlternateScreen)?; Ok(()) -} \ No newline at end of file +} diff --git a/src/ui/process_list_ui.rs b/src/ui/process_list_ui.rs index f12d0dd..8982a51 100644 --- a/src/ui/process_list_ui.rs +++ b/src/ui/process_list_ui.rs @@ -7,16 +7,15 @@ pub mod process_list_ui { use process_list::ListIterator; use crate::config::ThemeConfig; - pub fn draw_process_list<'a>( + pub fn draw_process_list( f: &mut Frame, area: Rect, - visible_items: ListIterator<'a>, + visible_items: ListIterator<'_>, follow_selection: bool, focus: bool, theme_config: ThemeConfig, ) { let follow_flag = follow_selection; - let header_style = theme_config.list_header; let select_style = theme_config.item_select; let select_follow_style = theme_config.item_select_follow; let default_style = theme_config.item_style; @@ -105,4 +104,4 @@ pub mod process_list_ui { f.render_widget(table, area); } -} \ No newline at end of file +} From ff3a77a62baa287d3ba6850588da09c61706725a Mon Sep 17 00:00:00 2001 From: rhasler1 Date: Tue, 13 May 2025 16:41:41 -0500 Subject: [PATCH 2/2] rename --- src/app.rs | 8 +- src/components/command_new.rs | 29 ---- src/components/mod.rs | 4 +- .../{system_wrapper.rs => sysinfo_wrapper.rs} | 68 +++------ src/components/system_component.rs | 141 ------------------ 5 files changed, 24 insertions(+), 226 deletions(-) delete mode 100644 src/components/command_new.rs rename src/components/{system_wrapper.rs => sysinfo_wrapper.rs} (64%) delete mode 100644 src/components/system_component.rs diff --git a/src/app.rs b/src/app.rs index 73ff191..1d15b7e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,7 +5,7 @@ use crate::config::Config; use crate::components::{ cpu::CPUComponent, process::ProcessComponent, - system_wrapper::SystemWrapper, + sysinfo_wrapper::SysInfoWrapper, error::ErrorComponent, Component, EventState, @@ -23,7 +23,7 @@ enum MainFocus { pub struct App { focus: MainFocus, expand: bool, - system_wrapper: SystemWrapper, + system_wrapper: SysInfoWrapper, process: ProcessComponent, cpu: CPUComponent, help: HelpComponent, @@ -33,7 +33,7 @@ pub struct App { impl App { pub fn new(config: Config) -> Self { - let mut system_wrapper = SystemWrapper::new(config.clone()); + let mut system_wrapper = SysInfoWrapper::new(config.clone()); system_wrapper.refresh_all(); @@ -139,7 +139,7 @@ impl App { return Ok(EventState::Consumed) } - + Ok(EventState::NotConsumed) } diff --git a/src/components/command_new.rs b/src/components/command_new.rs deleted file mode 100644 index c77043f..0000000 --- a/src/components/command_new.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::config; - -static HELP_GROUP: &str = "-- General --"; - -pub struct HelpText { - pub name: String, - pub group: &'static str, -} - -impl HelpText { - pub fn new(name: String, group: &'static str) -> Self { - Self { - name, - group, - } - } -} - -pub struct HelpInfo { - pub text: HelpText, -} - -impl HelpInfo { - pub fn new(text: HelpText) -> Self { - Self { - text - } - } -} \ No newline at end of file diff --git a/src/components/mod.rs b/src/components/mod.rs index 90ca5f4..ca4b6c7 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -3,7 +3,7 @@ use crossterm::event::KeyEvent; use ratatui::prelude::*; use process_list::{ListSortOrder, MoveSelection}; use super::config::KeyConfig; -pub mod system_wrapper; +pub mod sysinfo_wrapper; pub mod filter; pub mod help; pub mod error; @@ -11,8 +11,6 @@ pub mod process; pub mod utils; pub mod command; pub mod cpu; -pub mod system_component; -pub mod command_new; pub trait DrawableComponent { fn draw(&mut self, f: &mut Frame, area: Rect, focused: bool) -> Result<()>; diff --git a/src/components/system_wrapper.rs b/src/components/sysinfo_wrapper.rs similarity index 64% rename from src/components/system_wrapper.rs rename to src/components/sysinfo_wrapper.rs index 9c8a376..e5ae1f2 100644 --- a/src/components/system_wrapper.rs +++ b/src/components/sysinfo_wrapper.rs @@ -1,5 +1,3 @@ -use std::vec; -use anyhow::Result; use sysinfo::{Pid, System}; use process_list::ProcessListItem; use bounded_queue::CpuItem; @@ -7,13 +5,12 @@ use crate::config::Config; // See here for refreshing system: https://crates.io/crates/sysinfo#:~:text=use%20sysinfo%3A%3ASystem,(sysinfo%3A%3AMINIMUM_CPU_UPDATE_INTERVAL)%3B%0A%7D // note: sysinfo::MINIMUM_CPU_UPDATE_INTERVAL = 200 ms - -pub struct SystemWrapper { +pub struct SysInfoWrapper { system: System, pub _config: Config } -impl SystemWrapper { +impl SysInfoWrapper { pub fn new(config: Config) -> Self { Self { system: System::new_all(), @@ -28,18 +25,18 @@ impl SystemWrapper { pub fn get_cpus(&self) -> Vec { let mut cpus: Vec = Vec::new(); - // dummy item for global cpu usage - cpus.push(CpuItem::new( + cpus.push(CpuItem::new( // dummy item for global cpu usage 0, self.system.global_cpu_usage(), - 0, String::from("Global"), + 0, + String::from("Global"), String::from(""), String::from("") )); for (id, cpu) in self.system.cpus().iter().enumerate() { let cpu_item = CpuItem::new( - id + 1, // id=0 reserved for global cpu usage + id + 1, // id=0 reserved for global cpu usage cpu.cpu_usage(), cpu.frequency(), String::from(cpu.name()), @@ -53,42 +50,6 @@ impl SystemWrapper { cpus } - pub fn get_static_sysinfo() -> Vec { - let vec: Vec = vec![ - sysinfo::System::kernel_long_version(), - if let Some(name) = sysinfo::System::host_name() { - name - } - else { - String::from("None") - }, - sysinfo::System::cpu_arch(), - if let Some(count) = sysinfo::System::physical_core_count() { - format!("{count}") - } - else { - String::from("None") - }, - if let Some(version) = sysinfo::System::long_os_version() { - version - } - else { - String::from("None") - }, - format!("{}", sysinfo::System::uptime()) - ]; - - vec - } - - pub fn update_sys_info() -> String { - format!("{}", sysinfo::System::uptime()) - } - - pub fn get_global_cpu_info(&self) -> f32 { - self.system.global_cpu_usage() - } - pub fn get_processes(&self) -> Vec { let mut processes: Vec = Vec::new(); @@ -105,11 +66,17 @@ impl SystemWrapper { else { process.cpu_usage() }; + let memory_usage = process.memory(); + let start_time = process.start_time(); + let run_time = process.run_time(); + let accumulated_cpu_time = process.accumulated_cpu_time(); + let status = process.status().to_string(); + let item = ProcessListItem::new( pid.as_u32(), name, cpu_usage, @@ -126,10 +93,13 @@ impl SystemWrapper { processes } - pub fn terminate_process(&mut self, pid: u32) -> Result { + pub fn terminate_process(&mut self, pid: u32) -> bool { + let mut res = false; + if let Some(process) = self.system.process(Pid::from_u32(pid)) { - process.kill(); + res = process.kill(); } - Ok(true) + + res } -} +} \ No newline at end of file diff --git a/src/components/system_component.rs b/src/components/system_component.rs deleted file mode 100644 index 8333e4b..0000000 --- a/src/components/system_component.rs +++ /dev/null @@ -1,141 +0,0 @@ -use anyhow::Ok; -use ratatui::{style::{Color, Modifier, Style}, widgets::{Block, Borders, List, ListItem, ListState}}; - -use crate::config::{self, Config}; - -use super::{Component, DrawableComponent}; - -#[derive(Default)] -pub struct SystemComponentInner { - kernel_version: String, - host_name: String, - cpu_architecture: String, - physical_core_count: String, - os_version: String, - system_uptime: String, -} - -impl SystemComponentInner { - pub const FIELD_NAMES: [&str; 6] = [ - "Kernel Version", - "Host Name", - "CPU Architecture", - "Physical Cores", - "OS Version", - "System Uptime", - ]; - - pub fn as_vec(&self) -> Vec<&str> { // read only - let vec: Vec<&str> = vec![ - &self.kernel_version, - &self.host_name, - &self.cpu_architecture, - &self.physical_core_count, - &self.os_version, - &self.system_uptime, - ]; - - vec - } - - pub fn update( - &mut self, - system_uptime: String, - ) { - self.system_uptime = system_uptime; - } -} - -#[derive(Default)] -pub struct SystemComponent { - system_inner: SystemComponentInner, - config: Config, - selection_inner: Option, -} - -impl SystemComponent { - pub fn init( - &mut self, - vec: Vec, - ) { - let mut iter = vec.into_iter(); - - self.system_inner.kernel_version = iter.next().unwrap(); - self.system_inner.host_name = iter.next().unwrap(); - self.system_inner.cpu_architecture = iter.next().unwrap(); - self.system_inner.physical_core_count = iter.next().unwrap(); - self.system_inner.os_version = iter.next().unwrap(); - self.system_inner.system_uptime = iter.next().unwrap(); - self.selection_inner = Some(0); - } - - pub fn update( - &mut self, - system_uptime: String, - ) { - self.system_inner.system_uptime = system_uptime; - } -} - -impl Component for SystemComponent { - fn event(&mut self, key: crossterm::event::KeyEvent) -> anyhow::Result { - if key.code == self.config.key_config.move_down { - if let Some(s) = self.selection_inner { - if s < SystemComponentInner::FIELD_NAMES.len() - 1 { - self.selection_inner = Some(s.saturating_add(1)); - } - return Ok(super::EventState::Consumed) - } - } - if key.code == self.config.key_config.move_up { - if let Some(s) = self.selection_inner { - self.selection_inner = Some(s.saturating_sub(1)); - return Ok(super::EventState::Consumed) - } - } - Ok(super::EventState::NotConsumed) - } -} - -impl DrawableComponent for SystemComponent { - fn draw(&mut self, - f: &mut ratatui::Frame, - area: ratatui::prelude::Rect, - focused: bool - ) -> anyhow::Result<()> { - let items: Vec = SystemComponentInner::FIELD_NAMES - .iter() - .zip(self.system_inner.as_vec().iter()) - .map(|(label, item)| ListItem::new(format!("{label}: {item}")).style(Color::White)) - .collect(); - - let mut list_state = ListState::default(); - list_state.select(self.selection_inner); - - let list = List::new(items) - .scroll_padding(area.height as usize / 2) - .block( - { - if !focused { - Block::default() - .title(" System Info ") - .borders(Borders::ALL) - .style(Color::DarkGray) - } - else { - Block::default() - .title(" System Info ") - .borders(Borders::ALL) - .style(Color::LightGreen) - } - } - ) - .highlight_style( - Style::default().bg(Color::LightBlue).add_modifier(Modifier::BOLD) - ); - - f.render_stateful_widget(list, area, &mut list_state); - - Ok(()) - } -}