Merge pull request #9 from ClementTsang/add_graph_filtering
Add graph filtering
This commit is contained in:
commit
683a3d3a25
411
src/app.rs
411
src/app.rs
|
@ -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 {
|
||||
is_enabled: bool,
|
||||
current_search_query: String,
|
||||
searching_pid: bool,
|
||||
ignore_case: bool,
|
||||
current_regex: std::result::Result<regex::Regex, regex::Error>,
|
||||
current_cursor_position: usize,
|
||||
match_word: bool,
|
||||
use_regex: bool,
|
||||
pub is_invalid_or_blank_search: bool,
|
||||
}
|
||||
|
||||
impl Default for AppSearchState {
|
||||
fn default() -> Self {
|
||||
AppSearchState {
|
||||
is_enabled: false,
|
||||
current_search_query: String::default(),
|
||||
searching_pid: false,
|
||||
ignore_case: true,
|
||||
current_regex: BASE_REGEX.clone(),
|
||||
current_cursor_position: 0,
|
||||
match_word: false,
|
||||
use_regex: false,
|
||||
is_invalid_or_blank_search: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
self.ignore_case = !self.ignore_case;
|
||||
self.is_ignoring_case = !self.is_ignoring_case;
|
||||
}
|
||||
|
||||
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) {
|
||||
self.use_regex = !self.use_regex;
|
||||
self.is_searching_with_regex = !self.is_searching_with_regex;
|
||||
}
|
||||
|
||||
pub fn toggle_search_with_pid(&mut self) {
|
||||
self.searching_pid = !self.searching_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
|
||||
self.is_searching_with_pid = !self.is_searching_with_pid;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,6 +157,61 @@ pub struct AppConfigFields {
|
|||
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 process_sorting_type: processes::ProcessSorting,
|
||||
pub process_sorting_reverse: bool,
|
||||
|
@ -171,13 +227,15 @@ pub struct App {
|
|||
last_key_press: Instant,
|
||||
pub canvas_data: canvas::DisplayableData,
|
||||
enable_grouping: bool,
|
||||
enable_searching: bool,
|
||||
pub data_collection: DataCollection,
|
||||
pub search_state: AppSearchState,
|
||||
pub process_search_state: ProcessSearchState,
|
||||
pub delete_dialog_state: AppDeleteDialogState,
|
||||
pub help_dialog_state: AppHelpDialogState,
|
||||
pub app_config_fields: AppConfigFields,
|
||||
pub is_expanded: bool,
|
||||
pub cpu_state: CpuState,
|
||||
pub mem_state: MemState,
|
||||
pub net_state: NetworkState,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
@ -201,9 +259,8 @@ impl App {
|
|||
last_key_press: Instant::now(),
|
||||
canvas_data: canvas::DisplayableData::default(),
|
||||
enable_grouping: false,
|
||||
enable_searching: false,
|
||||
data_collection: DataCollection::default(),
|
||||
search_state: AppSearchState::default(),
|
||||
process_search_state: ProcessSearchState::default(),
|
||||
delete_dialog_state: AppDeleteDialogState::default(),
|
||||
help_dialog_state: AppHelpDialogState::default(),
|
||||
app_config_fields: AppConfigFields {
|
||||
|
@ -215,6 +272,9 @@ impl App {
|
|||
use_current_cpu_total,
|
||||
},
|
||||
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.help_dialog_state.is_showing_help = 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.enable_searching = false;
|
||||
self.process_search_state.search_state.is_enabled = false;
|
||||
}
|
||||
self.search_state.current_search_query = String::new();
|
||||
self.search_state.searching_pid = false;
|
||||
self.process_search_state.search_state.current_search_query = String::new();
|
||||
self.process_search_state.is_searching_with_pid = false;
|
||||
self.to_delete_process_list = None;
|
||||
self.dd_err = None;
|
||||
}
|
||||
|
@ -241,14 +301,37 @@ impl App {
|
|||
self.delete_dialog_state.is_on_yes = false;
|
||||
self.to_delete_process_list = None;
|
||||
self.dd_err = None;
|
||||
} else if self.enable_searching {
|
||||
self.current_widget_selected = WidgetPosition::Process;
|
||||
self.enable_searching = false;
|
||||
} else if self.is_filtering_or_searching() {
|
||||
match self.current_widget_selected {
|
||||
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 {
|
||||
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) {
|
||||
self.awaiting_second_char = false;
|
||||
self.second_char = None;
|
||||
|
@ -273,7 +356,7 @@ impl App {
|
|||
WidgetPosition::Process => self.toggle_grouping(),
|
||||
WidgetPosition::Disk => {}
|
||||
WidgetPosition::ProcessSearch => {
|
||||
if self.search_state.is_searching_with_pid() {
|
||||
if self.process_search_state.is_searching_with_pid {
|
||||
self.search_with_name();
|
||||
} else {
|
||||
self.search_with_pid();
|
||||
|
@ -287,21 +370,48 @@ impl App {
|
|||
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() {
|
||||
match self.current_widget_selected {
|
||||
WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||
// Toggle on
|
||||
self.enable_searching = true;
|
||||
self.process_search_state.search_state.is_enabled = true;
|
||||
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 {
|
||||
self.enable_searching
|
||||
self.process_search_state.search_state.is_enabled
|
||||
}
|
||||
|
||||
pub fn is_in_search_widget(&self) -> bool {
|
||||
|
@ -315,7 +425,7 @@ impl App {
|
|||
pub fn search_with_pid(&mut self) {
|
||||
if !self.is_in_dialog() && self.is_searching() {
|
||||
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) {
|
||||
if !self.is_in_dialog() && self.is_searching() {
|
||||
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 {
|
||||
&self.search_state.current_search_query
|
||||
&self.process_search_state.search_state.current_search_query
|
||||
}
|
||||
|
||||
pub fn toggle_ignore_case(&mut self) {
|
||||
if !self.is_in_dialog() && self.is_searching() {
|
||||
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_process_gui = true;
|
||||
}
|
||||
|
@ -343,24 +453,43 @@ impl App {
|
|||
}
|
||||
|
||||
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()
|
||||
} 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);
|
||||
}
|
||||
|
||||
if self.search_state.is_searching_whole_word() {
|
||||
if self.process_search_state.is_searching_whole_word {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
.process_scroll_state
|
||||
.previous_scroll_position = 0;
|
||||
|
@ -370,7 +499,9 @@ impl App {
|
|||
}
|
||||
|
||||
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...
|
||||
|
@ -394,18 +525,33 @@ impl App {
|
|||
self.delete_dialog_state.is_showing_dd = false;
|
||||
}
|
||||
} else if !self.is_in_dialog() {
|
||||
// Pop-out mode.
|
||||
self.is_expanded = true;
|
||||
// Pop-out mode. We ignore if in process search.
|
||||
match self.current_widget_selected {
|
||||
WidgetPosition::ProcessSearch => {}
|
||||
_ => self.is_expanded = true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_backspace(&mut self) {
|
||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||
if self.search_state.current_cursor_position > 0 {
|
||||
self.search_state.current_cursor_position -= 1;
|
||||
self.search_state
|
||||
if self
|
||||
.process_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
|
||||
.remove(self.search_state.current_cursor_position);
|
||||
.remove(
|
||||
self.process_search_state
|
||||
.search_state
|
||||
.current_cursor_position,
|
||||
);
|
||||
|
||||
self.update_regex();
|
||||
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> {
|
||||
&self.search_state.current_regex
|
||||
&self.process_search_state.search_state.current_regex
|
||||
}
|
||||
|
||||
pub fn on_up_key(&mut self) {
|
||||
|
@ -438,8 +584,15 @@ impl App {
|
|||
pub fn on_left_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||
if self.search_state.current_cursor_position > 0 {
|
||||
self.search_state.current_cursor_position -= 1;
|
||||
if self
|
||||
.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 {
|
||||
|
@ -450,10 +603,19 @@ impl App {
|
|||
pub fn on_right_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||
if self.search_state.current_cursor_position
|
||||
< self.search_state.current_search_query.len()
|
||||
if self
|
||||
.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 {
|
||||
|
@ -464,7 +626,9 @@ impl App {
|
|||
pub fn skip_cursor_beginning(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
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) {
|
||||
if !self.is_in_dialog() {
|
||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||
self.search_state.current_cursor_position =
|
||||
self.search_state.current_search_query.len();
|
||||
self.process_search_state
|
||||
.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) {
|
||||
// Forbid any char key presses when showing a dialog box...
|
||||
if !self.is_in_dialog() {
|
||||
|
@ -491,10 +701,18 @@ impl App {
|
|||
self.last_key_press = current_key_press_inst;
|
||||
|
||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||
self.search_state
|
||||
self.process_search_state
|
||||
.search_state
|
||||
.current_search_query
|
||||
.insert(self.search_state.current_cursor_position, caught_char);
|
||||
self.search_state.current_cursor_position += 1;
|
||||
.insert(
|
||||
self.process_search_state
|
||||
.search_state
|
||||
.current_cursor_position,
|
||||
caught_char,
|
||||
);
|
||||
self.process_search_state
|
||||
.search_state
|
||||
.current_cursor_position += 1;
|
||||
|
||||
self.update_regex();
|
||||
|
||||
|
@ -502,7 +720,7 @@ impl App {
|
|||
} else {
|
||||
match caught_char {
|
||||
'/' => {
|
||||
self.enable_searching();
|
||||
self.on_slash();
|
||||
}
|
||||
'd' => {
|
||||
if let WidgetPosition::Process = self.current_widget_selected {
|
||||
|
@ -513,49 +731,7 @@ impl App {
|
|||
self.awaiting_second_char = false;
|
||||
self.second_char = None;
|
||||
|
||||
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();
|
||||
self.start_dd();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -657,6 +833,7 @@ impl App {
|
|||
'L' => self.move_widget_selection_right(),
|
||||
'K' => self.move_widget_selection_up(),
|
||||
'J' => self.move_widget_selection_down(),
|
||||
' ' => self.on_space(),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
154
src/canvas.rs
154
src/canvas.rs
|
@ -23,9 +23,10 @@ use drawing_utils::*;
|
|||
|
||||
// Headers
|
||||
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 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 FORCE_MIN_THRESHOLD: usize = 5;
|
||||
|
||||
|
@ -40,6 +41,10 @@ lazy_static! {
|
|||
.iter()
|
||||
.map(|entry| max(FORCE_MIN_THRESHOLD, entry.len()))
|
||||
.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
|
||||
.iter()
|
||||
.map(|entry| max(FORCE_MIN_THRESHOLD, entry.len()))
|
||||
|
@ -99,6 +104,7 @@ impl Painter {
|
|||
/// This is to set some remaining styles and text.
|
||||
/// This bypasses some logic checks (size > 2, for example) but this
|
||||
/// assumes that you, the programmer, are sane and do not do stupid things.
|
||||
/// RIGHT?
|
||||
pub fn initialize(&mut self) {
|
||||
self.styled_general_help_text.push(Text::Styled(
|
||||
GENERAL_HELP_TEXT[0].into(),
|
||||
|
@ -532,22 +538,11 @@ impl Painter {
|
|||
let mut dataset_vector: Vec<Dataset> = Vec::new();
|
||||
let mut cpu_entries_vec: Vec<(Style, Vec<(f64, f64)>)> = Vec::new();
|
||||
|
||||
for (i, cpu) in cpu_data.iter().enumerate() {
|
||||
cpu_entries_vec.push((
|
||||
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() {
|
||||
for (itx, cpu) in cpu_data.iter().enumerate() {
|
||||
if app_state.cpu_state.core_show_vec[itx] {
|
||||
cpu_entries_vec.push((
|
||||
self.colours.cpu_colour_styles[0],
|
||||
avg_cpu_entry
|
||||
.cpu_data
|
||||
self.colours.cpu_colour_styles[(itx) % self.colours.cpu_colour_styles.len()],
|
||||
cpu.cpu_data
|
||||
.iter()
|
||||
.map(<(f64, f64)>::from)
|
||||
.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 ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
|
@ -625,44 +620,58 @@ impl Painter {
|
|||
let sliced_cpu_data = &cpu_data[start_position as usize..];
|
||||
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() {
|
||||
stringified_cpu_data.push(vec![
|
||||
let mut entry = vec![
|
||||
cpu.cpu_name.clone(),
|
||||
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
|
||||
.iter()
|
||||
.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)| {
|
||||
Row::StyledData(
|
||||
cpu_string_row.iter(),
|
||||
match app_state.current_widget_selected {
|
||||
app::WidgetPosition::Cpu => {
|
||||
if cpu_row_counter as u64
|
||||
if itx as u64
|
||||
== app_state
|
||||
.app_scroll_positions
|
||||
.cpu_scroll_state
|
||||
.current_scroll_position - start_position
|
||||
{
|
||||
cpu_row_counter = -1;
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
if cpu_row_counter >= 0 {
|
||||
cpu_row_counter += 1;
|
||||
}
|
||||
self.colours.cpu_colour_styles
|
||||
[itx % self.colours.cpu_colour_styles.len()]
|
||||
self.colours.cpu_colour_styles[itx
|
||||
+ 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[itx
|
||||
+ start_position as usize % self.colours.cpu_colour_styles.len()]
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -670,27 +679,70 @@ impl Painter {
|
|||
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
let width_ratios = vec![0.5, 0.5];
|
||||
let variable_intrinsic_results =
|
||||
get_variable_intrinsic_widths(width as u16, &width_ratios, &CPU_LEGEND_HEADER_LENS);
|
||||
let width_ratios = if app_state.cpu_state.is_showing_tray {
|
||||
vec![0.4, 0.3, 0.3]
|
||||
} 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 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
|
||||
Table::new(CPU_LEGEND_HEADER.iter(), cpu_rows)
|
||||
.block(Block::default().borders(Borders::ALL).border_style(
|
||||
match app_state.current_widget_selected {
|
||||
Table::new(
|
||||
if app_state.cpu_state.is_showing_tray {
|
||||
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,
|
||||
_ => self.colours.border_style,
|
||||
},
|
||||
))
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, draw_loc);
|
||||
}),
|
||||
)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(
|
||||
&(intrinsic_widths
|
||||
.iter()
|
||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.render(f, draw_loc);
|
||||
}
|
||||
|
||||
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(
|
||||
"Search by PID (Tab for Name): ",
|
||||
self.colours.table_header_style,
|
||||
|
@ -1164,21 +1216,21 @@ impl Painter {
|
|||
|
||||
let option_row = vec![
|
||||
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)
|
||||
} else {
|
||||
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),
|
||||
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)
|
||||
} else {
|
||||
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),
|
||||
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)
|
||||
} else {
|
||||
Text::styled("[ ]", self.colours.table_header_style)
|
||||
|
|
|
@ -37,11 +37,10 @@ pub fn gen_n_styles(num_to_gen: i32) -> Vec<Style> {
|
|||
Style::default().fg(Color::LightMagenta),
|
||||
Style::default().fg(Color::LightCyan),
|
||||
Style::default().fg(Color::Green),
|
||||
Style::default().fg(Color::Red),
|
||||
];
|
||||
|
||||
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);
|
||||
let result = hsv_to_rgb(h, 0.5, 0.95);
|
||||
colour_vec.push(Style::default().fg(Color::Rgb(result.0, result.1, result.2)));
|
||||
|
|
48
src/main.rs
48
src/main.rs
|
@ -254,6 +254,13 @@ fn main() -> error::Result<()> {
|
|||
&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
|
||||
let (single, grouped) = convert_process_data(&app.data_collection);
|
||||
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>,
|
||||
) -> bool {
|
||||
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.
|
||||
if event.code == KeyCode::Char('q') && !app.is_in_search_widget() {
|
||||
return true;
|
||||
|
@ -314,6 +319,7 @@ fn handle_key_event_or_break(
|
|||
KeyCode::Enter => app.on_enter(),
|
||||
KeyCode::Tab => app.on_tab(),
|
||||
KeyCode::Backspace => app.on_backspace(),
|
||||
KeyCode::Delete => app.start_dd(),
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
|
@ -324,7 +330,7 @@ fn handle_key_event_or_break(
|
|||
}
|
||||
|
||||
match event.code {
|
||||
KeyCode::Char('f') => app.enable_searching(),
|
||||
KeyCode::Char('f') => app.on_slash(),
|
||||
KeyCode::Left => app.move_widget_selection_left(),
|
||||
KeyCode::Right => app.move_widget_selection_right(),
|
||||
KeyCode::Up => app.move_widget_selection_up(),
|
||||
|
@ -355,19 +361,19 @@ fn handle_key_event_or_break(
|
|||
match event.code {
|
||||
KeyCode::Char('c') => {
|
||||
if app.is_in_search_widget() {
|
||||
app.search_state.toggle_ignore_case();
|
||||
app.process_search_state.toggle_ignore_case();
|
||||
app.update_regex();
|
||||
}
|
||||
}
|
||||
KeyCode::Char('w') => {
|
||||
if app.is_in_search_widget() {
|
||||
app.search_state.toggle_search_whole_word();
|
||||
app.process_search_state.toggle_search_whole_word();
|
||||
app.update_regex();
|
||||
}
|
||||
}
|
||||
KeyCode::Char('r') => {
|
||||
if app.is_in_search_widget() {
|
||||
app.search_state.toggle_search_regex();
|
||||
app.process_search_state.toggle_search_regex();
|
||||
app.update_regex();
|
||||
}
|
||||
}
|
||||
|
@ -526,11 +532,11 @@ fn enable_app_case_sensitive(
|
|||
matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App,
|
||||
) {
|
||||
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 {
|
||||
if let Some(case_sensitive) = flags.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,
|
||||
) {
|
||||
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 {
|
||||
if let Some(whole_word) = flags.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) {
|
||||
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 {
|
||||
if let Some(regex) = flags.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()
|
||||
.into_iter()
|
||||
.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)
|
||||
} else {
|
||||
true
|
||||
|
@ -747,8 +759,14 @@ fn update_final_process_list(app: &mut app::App) {
|
|||
.process_data
|
||||
.iter()
|
||||
.filter(|(_pid, process)| {
|
||||
if let Ok(matcher) = app.get_current_regex_matcher() {
|
||||
if app.search_state.is_searching_with_pid() {
|
||||
if app
|
||||
.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())
|
||||
} else {
|
||||
matcher.is_match(&process.name)
|
||||
|
|
Loading…
Reference in New Issue