Merge pull request #9 from ClementTsang/add_graph_filtering

Add graph filtering
This commit is contained in:
Clement Tsang 2020-02-16 19:50:34 -05:00 committed by GitHub
commit 683a3d3a25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 431 additions and 185 deletions

View File

@ -60,62 +60,63 @@ impl Default for AppScrollState {
} }
} }
/// AppSearchState only deals with the search's current settings and state. /// AppSearchState deals with generic searching (I might do this in the future).
pub struct AppSearchState { pub struct AppSearchState {
is_enabled: bool,
current_search_query: String, current_search_query: String,
searching_pid: bool,
ignore_case: bool,
current_regex: std::result::Result<regex::Regex, regex::Error>, current_regex: std::result::Result<regex::Regex, regex::Error>,
current_cursor_position: usize, current_cursor_position: usize,
match_word: bool, pub is_invalid_or_blank_search: bool,
use_regex: bool,
} }
impl Default for AppSearchState { impl Default for AppSearchState {
fn default() -> Self { fn default() -> Self {
AppSearchState { AppSearchState {
is_enabled: false,
current_search_query: String::default(), current_search_query: String::default(),
searching_pid: false,
ignore_case: true,
current_regex: BASE_REGEX.clone(), current_regex: BASE_REGEX.clone(),
current_cursor_position: 0, current_cursor_position: 0,
match_word: false, is_invalid_or_blank_search: true,
use_regex: false,
} }
} }
} }
impl AppSearchState { /// ProcessSearchState only deals with process' search's current settings and state.
pub struct ProcessSearchState {
pub search_state: AppSearchState,
pub is_searching_with_pid: bool,
pub is_ignoring_case: bool,
pub is_searching_whole_word: bool,
pub is_searching_with_regex: bool,
}
impl Default for ProcessSearchState {
fn default() -> Self {
ProcessSearchState {
search_state: AppSearchState::default(),
is_searching_with_pid: false,
is_ignoring_case: true,
is_searching_whole_word: false,
is_searching_with_regex: false,
}
}
}
impl ProcessSearchState {
pub fn toggle_ignore_case(&mut self) { pub fn toggle_ignore_case(&mut self) {
self.ignore_case = !self.ignore_case; self.is_ignoring_case = !self.is_ignoring_case;
} }
pub fn toggle_search_whole_word(&mut self) { pub fn toggle_search_whole_word(&mut self) {
self.match_word = !self.match_word; self.is_searching_whole_word = !self.is_searching_whole_word;
} }
pub fn toggle_search_regex(&mut self) { pub fn toggle_search_regex(&mut self) {
self.use_regex = !self.use_regex; self.is_searching_with_regex = !self.is_searching_with_regex;
} }
pub fn toggle_search_with_pid(&mut self) { pub fn toggle_search_with_pid(&mut self) {
self.searching_pid = !self.searching_pid; self.is_searching_with_pid = !self.is_searching_with_pid;
}
pub fn is_ignoring_case(&self) -> bool {
self.ignore_case
}
pub fn is_searching_whole_word(&self) -> bool {
self.match_word
}
pub fn is_searching_with_regex(&self) -> bool {
self.use_regex
}
pub fn is_searching_with_pid(&self) -> bool {
self.searching_pid
} }
} }
@ -156,6 +157,61 @@ pub struct AppConfigFields {
pub use_current_cpu_total: bool, pub use_current_cpu_total: bool,
} }
/// Network specific
pub struct NetworkState {
pub is_showing_tray: bool,
pub is_showing_rx: bool,
pub is_showing_tx: bool,
pub zoom_level: f64,
}
impl Default for NetworkState {
fn default() -> Self {
NetworkState {
is_showing_tray: false,
is_showing_rx: true,
is_showing_tx: true,
zoom_level: 100.0,
}
}
}
/// CPU specific
pub struct CpuState {
pub is_showing_tray: bool,
pub zoom_level: f64,
pub core_show_vec: Vec<bool>,
}
impl Default for CpuState {
fn default() -> Self {
CpuState {
is_showing_tray: false,
zoom_level: 100.0,
core_show_vec: Vec::new(),
}
}
}
/// Memory specific
pub struct MemState {
pub is_showing_tray: bool,
pub is_showing_ram: bool,
pub is_showing_swap: bool,
pub zoom_level: f64,
}
impl Default for MemState {
fn default() -> Self {
MemState {
is_showing_tray: false,
is_showing_ram: true,
is_showing_swap: true,
zoom_level: 100.0,
}
}
}
pub struct App { pub struct App {
pub process_sorting_type: processes::ProcessSorting, pub process_sorting_type: processes::ProcessSorting,
pub process_sorting_reverse: bool, pub process_sorting_reverse: bool,
@ -171,13 +227,15 @@ pub struct App {
last_key_press: Instant, last_key_press: Instant,
pub canvas_data: canvas::DisplayableData, pub canvas_data: canvas::DisplayableData,
enable_grouping: bool, enable_grouping: bool,
enable_searching: bool,
pub data_collection: DataCollection, pub data_collection: DataCollection,
pub search_state: AppSearchState, pub process_search_state: ProcessSearchState,
pub delete_dialog_state: AppDeleteDialogState, pub delete_dialog_state: AppDeleteDialogState,
pub help_dialog_state: AppHelpDialogState, pub help_dialog_state: AppHelpDialogState,
pub app_config_fields: AppConfigFields, pub app_config_fields: AppConfigFields,
pub is_expanded: bool, pub is_expanded: bool,
pub cpu_state: CpuState,
pub mem_state: MemState,
pub net_state: NetworkState,
} }
impl App { impl App {
@ -201,9 +259,8 @@ impl App {
last_key_press: Instant::now(), last_key_press: Instant::now(),
canvas_data: canvas::DisplayableData::default(), canvas_data: canvas::DisplayableData::default(),
enable_grouping: false, enable_grouping: false,
enable_searching: false,
data_collection: DataCollection::default(), data_collection: DataCollection::default(),
search_state: AppSearchState::default(), process_search_state: ProcessSearchState::default(),
delete_dialog_state: AppDeleteDialogState::default(), delete_dialog_state: AppDeleteDialogState::default(),
help_dialog_state: AppHelpDialogState::default(), help_dialog_state: AppHelpDialogState::default(),
app_config_fields: AppConfigFields { app_config_fields: AppConfigFields {
@ -215,6 +272,9 @@ impl App {
use_current_cpu_total, use_current_cpu_total,
}, },
is_expanded: false, is_expanded: false,
cpu_state: CpuState::default(),
mem_state: MemState::default(),
net_state: NetworkState::default(),
} }
} }
@ -222,12 +282,12 @@ impl App {
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
self.help_dialog_state.is_showing_help = false; self.help_dialog_state.is_showing_help = false;
self.delete_dialog_state.is_showing_dd = false; self.delete_dialog_state.is_showing_dd = false;
if self.enable_searching { if self.process_search_state.search_state.is_enabled {
self.current_widget_selected = WidgetPosition::Process; self.current_widget_selected = WidgetPosition::Process;
self.enable_searching = false; self.process_search_state.search_state.is_enabled = false;
} }
self.search_state.current_search_query = String::new(); self.process_search_state.search_state.current_search_query = String::new();
self.search_state.searching_pid = false; self.process_search_state.is_searching_with_pid = false;
self.to_delete_process_list = None; self.to_delete_process_list = None;
self.dd_err = None; self.dd_err = None;
} }
@ -241,14 +301,37 @@ impl App {
self.delete_dialog_state.is_on_yes = false; self.delete_dialog_state.is_on_yes = false;
self.to_delete_process_list = None; self.to_delete_process_list = None;
self.dd_err = None; self.dd_err = None;
} else if self.enable_searching { } else if self.is_filtering_or_searching() {
self.current_widget_selected = WidgetPosition::Process; match self.current_widget_selected {
self.enable_searching = false; WidgetPosition::Process | WidgetPosition::ProcessSearch => {
if self.process_search_state.search_state.is_enabled {
self.current_widget_selected = WidgetPosition::Process;
self.process_search_state.search_state.is_enabled = false;
}
}
WidgetPosition::Cpu => {
self.cpu_state.is_showing_tray = false;
}
WidgetPosition::Mem => {
self.mem_state.is_showing_tray = false;
}
WidgetPosition::Network => {
self.net_state.is_showing_tray = false;
}
_ => {}
}
} else if self.is_expanded { } else if self.is_expanded {
self.is_expanded = false; self.is_expanded = false;
} }
} }
fn is_filtering_or_searching(&self) -> bool {
self.cpu_state.is_showing_tray
|| self.mem_state.is_showing_tray
|| self.net_state.is_showing_tray
|| self.process_search_state.search_state.is_enabled
}
fn reset_multi_tap_keys(&mut self) { fn reset_multi_tap_keys(&mut self) {
self.awaiting_second_char = false; self.awaiting_second_char = false;
self.second_char = None; self.second_char = None;
@ -273,7 +356,7 @@ impl App {
WidgetPosition::Process => self.toggle_grouping(), WidgetPosition::Process => self.toggle_grouping(),
WidgetPosition::Disk => {} WidgetPosition::Disk => {}
WidgetPosition::ProcessSearch => { WidgetPosition::ProcessSearch => {
if self.search_state.is_searching_with_pid() { if self.process_search_state.is_searching_with_pid {
self.search_with_name(); self.search_with_name();
} else { } else {
self.search_with_pid(); self.search_with_pid();
@ -287,21 +370,48 @@ impl App {
self.enable_grouping self.enable_grouping
} }
pub fn enable_searching(&mut self) { pub fn on_space(&mut self) {
match self.current_widget_selected {
WidgetPosition::Cpu => {
let curr_posn = self
.app_scroll_positions
.cpu_scroll_state
.current_scroll_position;
if self.cpu_state.is_showing_tray
&& curr_posn < self.data_collection.cpu_harvest.len() as u64
{
self.cpu_state.core_show_vec[curr_posn as usize] =
!self.cpu_state.core_show_vec[curr_posn as usize];
}
}
_ => {}
}
}
pub fn on_slash(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
match self.current_widget_selected { match self.current_widget_selected {
WidgetPosition::Process | WidgetPosition::ProcessSearch => { WidgetPosition::Process | WidgetPosition::ProcessSearch => {
// Toggle on // Toggle on
self.enable_searching = true; self.process_search_state.search_state.is_enabled = true;
self.current_widget_selected = WidgetPosition::ProcessSearch; self.current_widget_selected = WidgetPosition::ProcessSearch;
} }
WidgetPosition::Cpu => {
self.cpu_state.is_showing_tray = true;
}
// WidgetPosition::Mem => {
// self.mem_state.is_showing_tray = true;
// }
// WidgetPosition::Network => {
// self.net_state.is_showing_tray = true;
// }
_ => {} _ => {}
} }
} }
} }
pub fn is_searching(&self) -> bool { pub fn is_searching(&self) -> bool {
self.enable_searching self.process_search_state.search_state.is_enabled
} }
pub fn is_in_search_widget(&self) -> bool { pub fn is_in_search_widget(&self) -> bool {
@ -315,7 +425,7 @@ impl App {
pub fn search_with_pid(&mut self) { pub fn search_with_pid(&mut self) {
if !self.is_in_dialog() && self.is_searching() { if !self.is_in_dialog() && self.is_searching() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.search_state.searching_pid = true; self.process_search_state.is_searching_with_pid = true;
} }
} }
} }
@ -323,19 +433,19 @@ impl App {
pub fn search_with_name(&mut self) { pub fn search_with_name(&mut self) {
if !self.is_in_dialog() && self.is_searching() { if !self.is_in_dialog() && self.is_searching() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.search_state.searching_pid = false; self.process_search_state.is_searching_with_pid = false;
} }
} }
} }
pub fn get_current_search_query(&self) -> &String { pub fn get_current_search_query(&self) -> &String {
&self.search_state.current_search_query &self.process_search_state.search_state.current_search_query
} }
pub fn toggle_ignore_case(&mut self) { pub fn toggle_ignore_case(&mut self) {
if !self.is_in_dialog() && self.is_searching() { if !self.is_in_dialog() && self.is_searching() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.search_state.toggle_ignore_case(); self.process_search_state.toggle_ignore_case();
self.update_regex(); self.update_regex();
self.update_process_gui = true; self.update_process_gui = true;
} }
@ -343,24 +453,43 @@ impl App {
} }
pub fn update_regex(&mut self) { pub fn update_regex(&mut self) {
self.search_state.current_regex = if self.search_state.current_search_query.is_empty() { self.process_search_state.search_state.current_regex = if self
.process_search_state
.search_state
.current_search_query
.is_empty()
{
self.process_search_state
.search_state
.is_invalid_or_blank_search = true;
BASE_REGEX.clone() BASE_REGEX.clone()
} else { } else {
let mut final_regex_string = self.search_state.current_search_query.clone(); let mut final_regex_string = self
.process_search_state
.search_state
.current_search_query
.clone();
if !self.search_state.is_searching_with_regex() { if !self.process_search_state.is_searching_with_regex {
final_regex_string = regex::escape(&final_regex_string); final_regex_string = regex::escape(&final_regex_string);
} }
if self.search_state.is_searching_whole_word() { if self.process_search_state.is_searching_whole_word {
final_regex_string = format!("^{}$", final_regex_string); final_regex_string = format!("^{}$", final_regex_string);
} }
if self.search_state.is_ignoring_case() { if self.process_search_state.is_ignoring_case {
final_regex_string = format!("(?i){}", final_regex_string); final_regex_string = format!("(?i){}", final_regex_string);
} }
regex::Regex::new(&final_regex_string) regex::Regex::new(&final_regex_string)
}; };
self.process_search_state
.search_state
.is_invalid_or_blank_search = self
.process_search_state
.search_state
.current_regex
.is_err();
self.app_scroll_positions self.app_scroll_positions
.process_scroll_state .process_scroll_state
.previous_scroll_position = 0; .previous_scroll_position = 0;
@ -370,7 +499,9 @@ impl App {
} }
pub fn get_cursor_position(&self) -> usize { pub fn get_cursor_position(&self) -> usize {
self.search_state.current_cursor_position self.process_search_state
.search_state
.current_cursor_position
} }
/// One of two functions allowed to run while in a dialog... /// One of two functions allowed to run while in a dialog...
@ -394,18 +525,33 @@ impl App {
self.delete_dialog_state.is_showing_dd = false; self.delete_dialog_state.is_showing_dd = false;
} }
} else if !self.is_in_dialog() { } else if !self.is_in_dialog() {
// Pop-out mode. // Pop-out mode. We ignore if in process search.
self.is_expanded = true; match self.current_widget_selected {
WidgetPosition::ProcessSearch => {}
_ => self.is_expanded = true,
}
} }
} }
pub fn on_backspace(&mut self) { pub fn on_backspace(&mut self) {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
if self.search_state.current_cursor_position > 0 { if self
self.search_state.current_cursor_position -= 1; .process_search_state
self.search_state .search_state
.current_cursor_position
> 0
{
self.process_search_state
.search_state
.current_cursor_position -= 1;
self.process_search_state
.search_state
.current_search_query .current_search_query
.remove(self.search_state.current_cursor_position); .remove(
self.process_search_state
.search_state
.current_cursor_position,
);
self.update_regex(); self.update_regex();
self.update_process_gui = true; self.update_process_gui = true;
@ -414,7 +560,7 @@ impl App {
} }
pub fn get_current_regex_matcher(&self) -> &std::result::Result<regex::Regex, regex::Error> { pub fn get_current_regex_matcher(&self) -> &std::result::Result<regex::Regex, regex::Error> {
&self.search_state.current_regex &self.process_search_state.search_state.current_regex
} }
pub fn on_up_key(&mut self) { pub fn on_up_key(&mut self) {
@ -438,8 +584,15 @@ impl App {
pub fn on_left_key(&mut self) { pub fn on_left_key(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
if self.search_state.current_cursor_position > 0 { if self
self.search_state.current_cursor_position -= 1; .process_search_state
.search_state
.current_cursor_position
> 0
{
self.process_search_state
.search_state
.current_cursor_position -= 1;
} }
} }
} else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes { } else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes {
@ -450,10 +603,19 @@ impl App {
pub fn on_right_key(&mut self) { pub fn on_right_key(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
if self.search_state.current_cursor_position if self
< self.search_state.current_search_query.len() .process_search_state
.search_state
.current_cursor_position
< self
.process_search_state
.search_state
.current_search_query
.len()
{ {
self.search_state.current_cursor_position += 1; self.process_search_state
.search_state
.current_cursor_position += 1;
} }
} }
} else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes { } else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes {
@ -464,7 +626,9 @@ impl App {
pub fn skip_cursor_beginning(&mut self) { pub fn skip_cursor_beginning(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.search_state.current_cursor_position = 0; self.process_search_state
.search_state
.current_cursor_position = 0;
} }
} }
} }
@ -472,12 +636,58 @@ impl App {
pub fn skip_cursor_end(&mut self) { pub fn skip_cursor_end(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.search_state.current_cursor_position = self.process_search_state
self.search_state.current_search_query.len(); .search_state
.current_cursor_position = self
.process_search_state
.search_state
.current_search_query
.len();
} }
} }
} }
pub fn start_dd(&mut self) {
if self
.app_scroll_positions
.process_scroll_state
.current_scroll_position
< self.canvas_data.finalized_process_data.len() as u64
{
let current_process = if self.is_grouped() {
let group_pids = &self.canvas_data.finalized_process_data[self
.app_scroll_positions
.process_scroll_state
.current_scroll_position
as usize]
.group_pids;
let mut ret = ("".to_string(), group_pids.clone());
for pid in group_pids {
if let Some(process) = self.canvas_data.process_data.get(&pid) {
ret.0 = process.name.clone();
break;
}
}
ret
} else {
let process = self.canvas_data.finalized_process_data[self
.app_scroll_positions
.process_scroll_state
.current_scroll_position
as usize]
.clone();
(process.name.clone(), vec![process.pid])
};
self.to_delete_process_list = Some(current_process);
self.delete_dialog_state.is_showing_dd = true;
}
self.reset_multi_tap_keys();
}
pub fn on_char_key(&mut self, caught_char: char) { pub fn on_char_key(&mut self, caught_char: char) {
// Forbid any char key presses when showing a dialog box... // Forbid any char key presses when showing a dialog box...
if !self.is_in_dialog() { if !self.is_in_dialog() {
@ -491,10 +701,18 @@ impl App {
self.last_key_press = current_key_press_inst; self.last_key_press = current_key_press_inst;
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.search_state self.process_search_state
.search_state
.current_search_query .current_search_query
.insert(self.search_state.current_cursor_position, caught_char); .insert(
self.search_state.current_cursor_position += 1; self.process_search_state
.search_state
.current_cursor_position,
caught_char,
);
self.process_search_state
.search_state
.current_cursor_position += 1;
self.update_regex(); self.update_regex();
@ -502,7 +720,7 @@ impl App {
} else { } else {
match caught_char { match caught_char {
'/' => { '/' => {
self.enable_searching(); self.on_slash();
} }
'd' => { 'd' => {
if let WidgetPosition::Process = self.current_widget_selected { if let WidgetPosition::Process = self.current_widget_selected {
@ -513,49 +731,7 @@ impl App {
self.awaiting_second_char = false; self.awaiting_second_char = false;
self.second_char = None; self.second_char = None;
if self self.start_dd();
.app_scroll_positions
.process_scroll_state
.current_scroll_position < self
.canvas_data
.finalized_process_data
.len() as u64
{
let current_process = if self.is_grouped() {
let group_pids = &self
.canvas_data
.finalized_process_data[self
.app_scroll_positions
.process_scroll_state
.current_scroll_position as usize]
.group_pids;
let mut ret = ("".to_string(), group_pids.clone());
for pid in group_pids {
if let Some(process) =
self.canvas_data.process_data.get(&pid)
{
ret.0 = process.name.clone();
break;
}
}
ret
} else {
let process = self.canvas_data.finalized_process_data
[self
.app_scroll_positions
.process_scroll_state
.current_scroll_position as usize]
.clone();
(process.name.clone(), vec![process.pid])
};
self.to_delete_process_list = Some(current_process);
self.delete_dialog_state.is_showing_dd = true;
}
self.reset_multi_tap_keys();
} }
} }
@ -657,6 +833,7 @@ impl App {
'L' => self.move_widget_selection_right(), 'L' => self.move_widget_selection_right(),
'K' => self.move_widget_selection_up(), 'K' => self.move_widget_selection_up(),
'J' => self.move_widget_selection_down(), 'J' => self.move_widget_selection_down(),
' ' => self.on_space(),
_ => {} _ => {}
} }

View File

@ -23,9 +23,10 @@ use drawing_utils::*;
// Headers // Headers
const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"]; const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"];
const CPU_SELECT_LEGEND_HEADER: [&str; 3] = ["CPU", "Use%", "Show"];
const DISK_HEADERS: [&str; 7] = ["Disk", "Mount", "Used", "Free", "Total", "R/s", "W/s"]; const DISK_HEADERS: [&str; 7] = ["Disk", "Mount", "Used", "Free", "Total", "R/s", "W/s"];
const TEMP_HEADERS: [&str; 2] = ["Sensor", "Temp"]; const TEMP_HEADERS: [&str; 2] = ["Sensor", "Temp"];
const MEM_HEADERS: [&str; 3] = ["Mem", "Usage", "Usage%"]; const MEM_HEADERS: [&str; 3] = ["Mem", "Usage", "Use%"];
const NETWORK_HEADERS: [&str; 4] = ["RX", "TX", "Total RX", "Total TX"]; const NETWORK_HEADERS: [&str; 4] = ["RX", "TX", "Total RX", "Total TX"];
const FORCE_MIN_THRESHOLD: usize = 5; const FORCE_MIN_THRESHOLD: usize = 5;
@ -40,6 +41,10 @@ lazy_static! {
.iter() .iter()
.map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
static ref CPU_SELECT_LEGEND_HEADER_LENS: Vec<usize> = CPU_SELECT_LEGEND_HEADER
.iter()
.map(|entry| max(FORCE_MIN_THRESHOLD, entry.len()))
.collect::<Vec<_>>();
static ref TEMP_HEADERS_LENS: Vec<usize> = TEMP_HEADERS static ref TEMP_HEADERS_LENS: Vec<usize> = TEMP_HEADERS
.iter() .iter()
.map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len()))
@ -99,6 +104,7 @@ impl Painter {
/// This is to set some remaining styles and text. /// This is to set some remaining styles and text.
/// This bypasses some logic checks (size > 2, for example) but this /// This bypasses some logic checks (size > 2, for example) but this
/// assumes that you, the programmer, are sane and do not do stupid things. /// assumes that you, the programmer, are sane and do not do stupid things.
/// RIGHT?
pub fn initialize(&mut self) { pub fn initialize(&mut self) {
self.styled_general_help_text.push(Text::Styled( self.styled_general_help_text.push(Text::Styled(
GENERAL_HELP_TEXT[0].into(), GENERAL_HELP_TEXT[0].into(),
@ -532,22 +538,11 @@ impl Painter {
let mut dataset_vector: Vec<Dataset> = Vec::new(); let mut dataset_vector: Vec<Dataset> = Vec::new();
let mut cpu_entries_vec: Vec<(Style, Vec<(f64, f64)>)> = Vec::new(); let mut cpu_entries_vec: Vec<(Style, Vec<(f64, f64)>)> = Vec::new();
for (i, cpu) in cpu_data.iter().enumerate() { for (itx, cpu) in cpu_data.iter().enumerate() {
cpu_entries_vec.push(( if app_state.cpu_state.core_show_vec[itx] {
self.colours.cpu_colour_styles[(i) % self.colours.cpu_colour_styles.len()],
cpu.cpu_data
.iter()
.map(<(f64, f64)>::from)
.collect::<Vec<_>>(),
));
}
if app_state.app_config_fields.show_average_cpu {
if let Some(avg_cpu_entry) = cpu_data.first() {
cpu_entries_vec.push(( cpu_entries_vec.push((
self.colours.cpu_colour_styles[0], self.colours.cpu_colour_styles[(itx) % self.colours.cpu_colour_styles.len()],
avg_cpu_entry cpu.cpu_data
.cpu_data
.iter() .iter()
.map(<(f64, f64)>::from) .map(<(f64, f64)>::from)
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
@ -568,7 +563,7 @@ impl Painter {
); );
} }
let title = if app_state.is_expanded { let title = if app_state.is_expanded && !app_state.cpu_state.is_showing_tray {
const TITLE_BASE: &str = " CPU ── Esc to go back "; const TITLE_BASE: &str = " CPU ── Esc to go back ";
let repeat_num = max( let repeat_num = max(
0, 0,
@ -625,44 +620,58 @@ impl Painter {
let sliced_cpu_data = &cpu_data[start_position as usize..]; let sliced_cpu_data = &cpu_data[start_position as usize..];
let mut stringified_cpu_data: Vec<Vec<String>> = Vec::new(); let mut stringified_cpu_data: Vec<Vec<String>> = Vec::new();
for cpu in sliced_cpu_data { for (itx, cpu) in sliced_cpu_data.iter().enumerate() {
if let Some(cpu_data) = cpu.cpu_data.last() { if let Some(cpu_data) = cpu.cpu_data.last() {
stringified_cpu_data.push(vec![ let mut entry = vec![
cpu.cpu_name.clone(), cpu.cpu_name.clone(),
format!("{:.0}%", cpu_data.usage.round()), format!("{:.0}%", cpu_data.usage.round()),
]); ];
if app_state.cpu_state.is_showing_tray {
entry.push(
if app_state.cpu_state.core_show_vec[itx + start_position as usize] {
"[*]".to_string()
} else {
"[ ]".to_string()
},
)
}
stringified_cpu_data.push(entry);
} }
} }
let mut cpu_row_counter: i64 = 0;
let cpu_rows = stringified_cpu_data let cpu_rows = stringified_cpu_data
.iter() .iter()
.enumerate() .enumerate()
.filter(|(itx, _)| {
if app_state.cpu_state.is_showing_tray {
true
} else {
app_state.cpu_state.core_show_vec[*itx]
}
})
.map(|(itx, cpu_string_row)| { .map(|(itx, cpu_string_row)| {
Row::StyledData( Row::StyledData(
cpu_string_row.iter(), cpu_string_row.iter(),
match app_state.current_widget_selected { match app_state.current_widget_selected {
app::WidgetPosition::Cpu => { app::WidgetPosition::Cpu => {
if cpu_row_counter as u64 if itx as u64
== app_state == app_state
.app_scroll_positions .app_scroll_positions
.cpu_scroll_state .cpu_scroll_state
.current_scroll_position - start_position .current_scroll_position - start_position
{ {
cpu_row_counter = -1;
self.colours.currently_selected_text_style self.colours.currently_selected_text_style
} else { } else {
if cpu_row_counter >= 0 { self.colours.cpu_colour_styles[itx
cpu_row_counter += 1; + start_position as usize
} % self.colours.cpu_colour_styles.len()]
self.colours.cpu_colour_styles
[itx % self.colours.cpu_colour_styles.len()]
} }
} }
_ => { _ => {
self.colours.cpu_colour_styles self.colours.cpu_colour_styles[itx
[itx % self.colours.cpu_colour_styles.len()] + start_position as usize % self.colours.cpu_colour_styles.len()]
} }
}, },
) )
@ -670,27 +679,70 @@ impl Painter {
// Calculate widths // Calculate widths
let width = f64::from(draw_loc.width); let width = f64::from(draw_loc.width);
let width_ratios = vec![0.5, 0.5]; let width_ratios = if app_state.cpu_state.is_showing_tray {
let variable_intrinsic_results = vec![0.4, 0.3, 0.3]
get_variable_intrinsic_widths(width as u16, &width_ratios, &CPU_LEGEND_HEADER_LENS); } else {
vec![0.5, 0.5]
};
let variable_intrinsic_results = get_variable_intrinsic_widths(
width as u16,
&width_ratios,
if app_state.cpu_state.is_showing_tray {
&CPU_SELECT_LEGEND_HEADER_LENS
} else {
&CPU_LEGEND_HEADER_LENS
},
);
let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
let title = if app_state.cpu_state.is_showing_tray {
const TITLE_BASE: &str = " Esc to go close ";
let repeat_num = max(
0,
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
);
let result_title = format!("{} Esc to go close ", "".repeat(repeat_num as usize));
result_title
} else {
"".to_string()
};
// Draw // Draw
Table::new(CPU_LEGEND_HEADER.iter(), cpu_rows) Table::new(
.block(Block::default().borders(Borders::ALL).border_style( if app_state.cpu_state.is_showing_tray {
match app_state.current_widget_selected { CPU_SELECT_LEGEND_HEADER.to_vec()
} else {
CPU_LEGEND_HEADER.to_vec()
}
.iter(),
cpu_rows,
)
.block(
Block::default()
.title(&title)
.title_style(if app_state.is_expanded {
self.colours.highlighted_border_style
} else {
match app_state.current_widget_selected {
app::WidgetPosition::Cpu => self.colours.highlighted_border_style,
_ => self.colours.border_style,
}
})
.borders(Borders::ALL)
.border_style(match app_state.current_widget_selected {
app::WidgetPosition::Cpu => self.colours.highlighted_border_style, app::WidgetPosition::Cpu => self.colours.highlighted_border_style,
_ => self.colours.border_style, _ => self.colours.border_style,
}, }),
)) )
.header_style(self.colours.table_header_style) .header_style(self.colours.table_header_style)
.widths( .widths(
&(intrinsic_widths &(intrinsic_widths
.iter() .iter()
.map(|calculated_width| Constraint::Length(*calculated_width as u16)) .map(|calculated_width| Constraint::Length(*calculated_width as u16))
.collect::<Vec<_>>()), .collect::<Vec<_>>()),
) )
.render(f, draw_loc); .render(f, draw_loc);
} }
fn draw_memory_graph<B: backend::Backend>( fn draw_memory_graph<B: backend::Backend>(
@ -1144,7 +1196,7 @@ impl Painter {
)] )]
}; };
let mut search_text = vec![if app_state.search_state.is_searching_with_pid() { let mut search_text = vec![if app_state.process_search_state.is_searching_with_pid {
Text::styled( Text::styled(
"Search by PID (Tab for Name): ", "Search by PID (Tab for Name): ",
self.colours.table_header_style, self.colours.table_header_style,
@ -1164,21 +1216,21 @@ impl Painter {
let option_row = vec![ let option_row = vec![
Text::styled("Match Case (Alt+C)", self.colours.table_header_style), Text::styled("Match Case (Alt+C)", self.colours.table_header_style),
if !app_state.search_state.is_ignoring_case() { if !app_state.process_search_state.is_ignoring_case {
Text::styled("[*]", self.colours.table_header_style) Text::styled("[*]", self.colours.table_header_style)
} else { } else {
Text::styled("[ ]", self.colours.table_header_style) Text::styled("[ ]", self.colours.table_header_style)
}, },
Text::styled(" ", self.colours.table_header_style), Text::styled(" ", self.colours.table_header_style),
Text::styled("Match Whole Word (Alt+W)", self.colours.table_header_style), Text::styled("Match Whole Word (Alt+W)", self.colours.table_header_style),
if app_state.search_state.is_searching_whole_word() { if app_state.process_search_state.is_searching_whole_word {
Text::styled("[*]", self.colours.table_header_style) Text::styled("[*]", self.colours.table_header_style)
} else { } else {
Text::styled("[ ]", self.colours.table_header_style) Text::styled("[ ]", self.colours.table_header_style)
}, },
Text::styled(" ", self.colours.table_header_style), Text::styled(" ", self.colours.table_header_style),
Text::styled("Use Regex (Alt+R)", self.colours.table_header_style), Text::styled("Use Regex (Alt+R)", self.colours.table_header_style),
if app_state.search_state.is_searching_with_regex() { if app_state.process_search_state.is_searching_with_regex {
Text::styled("[*]", self.colours.table_header_style) Text::styled("[*]", self.colours.table_header_style)
} else { } else {
Text::styled("[ ]", self.colours.table_header_style) Text::styled("[ ]", self.colours.table_header_style)

View File

@ -37,11 +37,10 @@ pub fn gen_n_styles(num_to_gen: i32) -> Vec<Style> {
Style::default().fg(Color::LightMagenta), Style::default().fg(Color::LightMagenta),
Style::default().fg(Color::LightCyan), Style::default().fg(Color::LightCyan),
Style::default().fg(Color::Green), Style::default().fg(Color::Green),
Style::default().fg(Color::Red),
]; ];
let mut h: f32 = 0.4; // We don't need random colours... right? let mut h: f32 = 0.4; // We don't need random colours... right?
for _i in 0..(num_to_gen - 6) { for _i in 0..(num_to_gen - 5) {
h = gen_hsv(h); h = gen_hsv(h);
let result = hsv_to_rgb(h, 0.5, 0.95); let result = hsv_to_rgb(h, 0.5, 0.95);
colour_vec.push(Style::default().fg(Color::Rgb(result.0, result.1, result.2))); colour_vec.push(Style::default().fg(Color::Rgb(result.0, result.1, result.2)));

View File

@ -254,6 +254,13 @@ fn main() -> error::Result<()> {
&app.data_collection, &app.data_collection,
); );
// Pre-fill CPU if needed
for itx in 0..app.canvas_data.cpu_data.len() {
if app.cpu_state.core_show_vec.len() <= itx {
app.cpu_state.core_show_vec.push(true);
}
}
// Processes // Processes
let (single, grouped) = convert_process_data(&app.data_collection); let (single, grouped) = convert_process_data(&app.data_collection);
app.canvas_data.process_data = single; app.canvas_data.process_data = single;
@ -295,8 +302,6 @@ fn handle_key_event_or_break(
event: KeyEvent, app: &mut app::App, rtx: &std::sync::mpsc::Sender<ResetEvent>, event: KeyEvent, app: &mut app::App, rtx: &std::sync::mpsc::Sender<ResetEvent>,
) -> bool { ) -> bool {
if event.modifiers.is_empty() { if event.modifiers.is_empty() {
// If only a code, and no modifiers, don't bother...
// Required catch for searching - otherwise you couldn't search with q. // Required catch for searching - otherwise you couldn't search with q.
if event.code == KeyCode::Char('q') && !app.is_in_search_widget() { if event.code == KeyCode::Char('q') && !app.is_in_search_widget() {
return true; return true;
@ -314,6 +319,7 @@ fn handle_key_event_or_break(
KeyCode::Enter => app.on_enter(), KeyCode::Enter => app.on_enter(),
KeyCode::Tab => app.on_tab(), KeyCode::Tab => app.on_tab(),
KeyCode::Backspace => app.on_backspace(), KeyCode::Backspace => app.on_backspace(),
KeyCode::Delete => app.start_dd(),
_ => {} _ => {}
} }
} else { } else {
@ -324,7 +330,7 @@ fn handle_key_event_or_break(
} }
match event.code { match event.code {
KeyCode::Char('f') => app.enable_searching(), KeyCode::Char('f') => app.on_slash(),
KeyCode::Left => app.move_widget_selection_left(), KeyCode::Left => app.move_widget_selection_left(),
KeyCode::Right => app.move_widget_selection_right(), KeyCode::Right => app.move_widget_selection_right(),
KeyCode::Up => app.move_widget_selection_up(), KeyCode::Up => app.move_widget_selection_up(),
@ -355,19 +361,19 @@ fn handle_key_event_or_break(
match event.code { match event.code {
KeyCode::Char('c') => { KeyCode::Char('c') => {
if app.is_in_search_widget() { if app.is_in_search_widget() {
app.search_state.toggle_ignore_case(); app.process_search_state.toggle_ignore_case();
app.update_regex(); app.update_regex();
} }
} }
KeyCode::Char('w') => { KeyCode::Char('w') => {
if app.is_in_search_widget() { if app.is_in_search_widget() {
app.search_state.toggle_search_whole_word(); app.process_search_state.toggle_search_whole_word();
app.update_regex(); app.update_regex();
} }
} }
KeyCode::Char('r') => { KeyCode::Char('r') => {
if app.is_in_search_widget() { if app.is_in_search_widget() {
app.search_state.toggle_search_regex(); app.process_search_state.toggle_search_regex();
app.update_regex(); app.update_regex();
} }
} }
@ -526,11 +532,11 @@ fn enable_app_case_sensitive(
matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App, matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App,
) { ) {
if matches.is_present("CASE_SENSITIVE") { if matches.is_present("CASE_SENSITIVE") {
app.search_state.toggle_ignore_case(); app.process_search_state.toggle_ignore_case();
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
if let Some(case_sensitive) = flags.case_sensitive { if let Some(case_sensitive) = flags.case_sensitive {
if case_sensitive { if case_sensitive {
app.search_state.toggle_ignore_case(); app.process_search_state.toggle_ignore_case();
} }
} }
} }
@ -540,11 +546,11 @@ fn enable_app_match_whole_word(
matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App, matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App,
) { ) {
if matches.is_present("WHOLE_WORD") { if matches.is_present("WHOLE_WORD") {
app.search_state.toggle_search_whole_word(); app.process_search_state.toggle_search_whole_word();
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
if let Some(whole_word) = flags.whole_word { if let Some(whole_word) = flags.whole_word {
if whole_word { if whole_word {
app.search_state.toggle_search_whole_word(); app.process_search_state.toggle_search_whole_word();
} }
} }
} }
@ -552,11 +558,11 @@ fn enable_app_match_whole_word(
fn enable_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App) { fn enable_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App) {
if matches.is_present("REGEX_DEFAULT") { if matches.is_present("REGEX_DEFAULT") {
app.search_state.toggle_search_regex(); app.process_search_state.toggle_search_regex();
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
if let Some(regex) = flags.regex { if let Some(regex) = flags.regex {
if regex { if regex {
app.search_state.toggle_search_regex(); app.process_search_state.toggle_search_regex();
} }
} }
} }
@ -735,7 +741,13 @@ fn update_final_process_list(app: &mut app::App) {
.clone() .clone()
.into_iter() .into_iter()
.filter(|process| { .filter(|process| {
if let Ok(matcher) = app.get_current_regex_matcher() { if app
.process_search_state
.search_state
.is_invalid_or_blank_search
{
true
} else if let Ok(matcher) = app.get_current_regex_matcher() {
matcher.is_match(&process.name) matcher.is_match(&process.name)
} else { } else {
true true
@ -747,8 +759,14 @@ fn update_final_process_list(app: &mut app::App) {
.process_data .process_data
.iter() .iter()
.filter(|(_pid, process)| { .filter(|(_pid, process)| {
if let Ok(matcher) = app.get_current_regex_matcher() { if app
if app.search_state.is_searching_with_pid() { .process_search_state
.search_state
.is_invalid_or_blank_search
{
true
} else if let Ok(matcher) = app.get_current_regex_matcher() {
if app.process_search_state.is_searching_with_pid {
matcher.is_match(&process.pid.to_string()) matcher.is_match(&process.pid.to_string())
} else { } else {
matcher.is_match(&process.name) matcher.is_match(&process.name)