diff options
author | ClementTsang <cjhtsang@uwaterloo.ca> | 2020-02-11 21:16:43 -0500 |
---|---|---|
committer | ClementTsang <cjhtsang@uwaterloo.ca> | 2020-02-12 00:35:37 -0500 |
commit | f0dad8f5bf4c409e568ee7a0d739b109d9bf45be (patch) | |
tree | 2e6c9e76c07ae420864157f507a82ff1bbdbf622 | |
parent | 908960f7836ae9e5589668d9b78f742624a7ba62 (diff) |
Added maximizing mode to allow users to zoom into a particular widget. Not 100% done.
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | src/app.rs | 419 | ||||
-rw-r--r-- | src/app/data_harvester.rs | 2 | ||||
-rw-r--r-- | src/canvas.rs | 176 | ||||
-rw-r--r-- | src/constants.rs | 3 | ||||
-rw-r--r-- | src/data_conversion.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 42 |
7 files changed, 452 insertions, 200 deletions
@@ -22,6 +22,10 @@ Features of bottom include: - Temperature widget to monitor detected sensors in your system. +- Config file support for custom colours and default options. + +- Maximizing of widgets of interest. + The compatibility of each widget and operating systems are, as of version 0.1.0, as follows: | OS | CPU | Memory | Disks | Temperature | Processes | Networks | @@ -137,10 +141,12 @@ Run using `btm`. - `Ctrl/Shift-Arrow` or `H/J/K/L` to navigate between widgets. **Note that on macOS, `Ctrl`-arrow keys conflicts with an existing macOS binding, use `Shift`-arrow key instead.** -- `Esc` to close a dialog window. +- `Esc` to close a dialog window or exit maximized mode. - `?` to get a help screen explaining the controls. Note all controls except `Esc` to close the dialog will be disabled while this is open. +- `Enter` on a widget to maximize the widget. + #### Scrollable Tables - `Up` or `k` and `Down` or `j` scrolls through the list if the widget is a table (Temperature, Disks, Processes). @@ -33,17 +33,31 @@ lazy_static! { regex::Regex::new(".*"); } -/// AppConfigFields is meant to cover basic fields that would normally be set -/// by config files or launch options. Don't need to be mutable (set and forget). -pub struct AppConfigFields { - pub update_rate_in_milliseconds: u64, - pub temperature_type: temperature::TemperatureType, - pub use_dot: bool, -} - /// AppScrollWidgetState deals with fields for a scrollable app's current state. +#[derive(Default)] pub struct AppScrollWidgetState { - pub widget_scroll_position: i64, + pub current_scroll_position: u64, + pub previous_scroll_position: u64, +} + +pub struct AppScrollState { + pub scroll_direction: ScrollDirection, + pub process_scroll_state: AppScrollWidgetState, + pub disk_scroll_state: AppScrollWidgetState, + pub temp_scroll_state: AppScrollWidgetState, + pub cpu_scroll_state: AppScrollWidgetState, +} + +impl Default for AppScrollState { + fn default() -> Self { + AppScrollState { + scroll_direction: ScrollDirection::DOWN, + process_scroll_state: AppScrollWidgetState::default(), + disk_scroll_state: AppScrollWidgetState::default(), + temp_scroll_state: AppScrollWidgetState::default(), + cpu_scroll_state: AppScrollWidgetState::default(), + } + } } /// AppSearchState only deals with the search's current settings and state. @@ -131,35 +145,29 @@ impl Default for AppHelpDialogState { } } -// TODO: [OPT] Group like fields together... this is kinda gross to step through +/// AppConfigFields is meant to cover basic fields that would normally be set +/// by config files or launch options. Don't need to be mutable (set and forget). +pub struct AppConfigFields { + pub update_rate_in_milliseconds: u64, + pub temperature_type: temperature::TemperatureType, + pub use_dot: bool, + pub left_legend: bool, + pub show_average_cpu: bool, + pub use_current_cpu_total: bool, +} + pub struct App { - // Sorting pub process_sorting_type: processes::ProcessSorting, pub process_sorting_reverse: bool, pub update_process_gui: bool, - // Positioning - pub scroll_direction: ScrollDirection, - pub currently_selected_process_position: u64, - pub currently_selected_disk_position: u64, - pub currently_selected_temperature_position: u64, - pub currently_selected_cpu_table_position: u64, - pub previous_disk_position: u64, - pub previous_temp_position: u64, - pub previous_process_position: u64, - pub previous_cpu_table_position: u64, - pub temperature_type: temperature::TemperatureType, - pub update_rate_in_milliseconds: u64, - pub show_average_cpu: bool, + pub app_scroll_positions: AppScrollState, pub current_widget_selected: WidgetPosition, pub data: data_harvester::Data, awaiting_second_char: bool, - second_char: char, - pub use_dot: bool, + second_char: Option<char>, pub dd_err: Option<String>, to_delete_process_list: Option<(String, Vec<u32>)>, pub is_frozen: bool, - pub left_legend: bool, - pub use_current_cpu_total: bool, last_key_press: Instant, pub canvas_data: canvas::DisplayableData, enable_grouping: bool, @@ -168,6 +176,8 @@ pub struct App { pub search_state: AppSearchState, pub delete_dialog_state: AppDeleteDialogState, pub help_dialog_state: AppHelpDialogState, + pub app_config_fields: AppConfigFields, + pub is_expanded: bool, } impl App { @@ -180,28 +190,14 @@ impl App { process_sorting_type: processes::ProcessSorting::CPU, process_sorting_reverse: true, update_process_gui: false, - temperature_type, - update_rate_in_milliseconds, - show_average_cpu, current_widget_selected: WidgetPosition::Process, - scroll_direction: ScrollDirection::DOWN, - currently_selected_process_position: 0, - currently_selected_disk_position: 0, - currently_selected_temperature_position: 0, - currently_selected_cpu_table_position: 0, - previous_process_position: 0, - previous_disk_position: 0, - previous_temp_position: 0, - previous_cpu_table_position: 0, + app_scroll_positions: AppScrollState::default(), data: data_harvester::Data::default(), awaiting_second_char: false, - second_char: ' ', - use_dot, + second_char: None, dd_err: None, to_delete_process_list: None, is_frozen: false, - left_legend, - use_current_cpu_total, last_key_press: Instant::now(), canvas_data: canvas::DisplayableData::default(), enable_grouping: false, @@ -210,6 +206,15 @@ impl App { search_state: AppSearchState::default(), delete_dialog_state: AppDeleteDialogState::default(), help_dialog_state: AppHelpDialogState::default(), + app_config_fields: AppConfigFields { + show_average_cpu, + temperature_type, + use_dot, + update_rate_in_milliseconds, + left_legend, + use_current_cpu_total, + }, + is_expanded: false, } } @@ -239,12 +244,14 @@ impl App { } else if self.enable_searching { self.current_widget_selected = WidgetPosition::Process; self.enable_searching = false; + } else if self.is_expanded { + self.is_expanded = false; } } fn reset_multi_tap_keys(&mut self) { self.awaiting_second_char = false; - self.second_char = ' '; + self.second_char = None; } fn is_in_dialog(&self) -> bool { @@ -354,8 +361,12 @@ impl App { regex::Regex::new(&final_regex_string) }; - self.previous_process_position = 0; - self.currently_selected_process_position = 0; + self.app_scroll_positions + .process_scroll_state + .previous_scroll_position = 0; + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = 0; } pub fn get_cursor_position(&self) -> usize { @@ -364,22 +375,27 @@ impl App { /// One of two functions allowed to run while in a dialog... pub fn on_enter(&mut self) { - if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes { - // If within dd... - if self.dd_err.is_none() { - // Also ensure that we didn't just fail a dd... - let dd_result = self.kill_highlighted_process(); - self.delete_dialog_state.is_on_yes = false; - - // Check if there was an issue... if so, inform the user. - if let Err(dd_err) = dd_result { - self.dd_err = Some(dd_err.to_string()); - } else { - self.delete_dialog_state.is_showing_dd = false; + if self.delete_dialog_state.is_showing_dd { + if self.delete_dialog_state.is_on_yes { + // If within dd... + if self.dd_err.is_none() { + // Also ensure that we didn't just fail a dd... + let dd_result = self.kill_highlighted_process(); + self.delete_dialog_state.is_on_yes = false; + + // Check if there was an issue... if so, inform the user. + if let Err(dd_err) = dd_result { + self.dd_err = Some(dd_err.to_string()); + } else { + self.delete_dialog_state.is_showing_dd = false; + } } + } else { + self.delete_dialog_state.is_showing_dd = false; } - } else { - self.delete_dialog_state.is_showing_dd = false; + } else if !self.is_in_dialog() { + // Pop-out mode. + self.is_expanded = true; } } @@ -490,55 +506,79 @@ impl App { } 'd' => { if let WidgetPosition::Process = self.current_widget_selected { - if self.awaiting_second_char && self.second_char == 'd' { - self.awaiting_second_char = false; - self.second_char = ' '; - - if self.currently_selected_process_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.currently_selected_process_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; + let mut is_first_d = true; + if let Some(second_char) = self.second_char { + if self.awaiting_second_char && second_char == 'd' { + is_first_d = false; + 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.currently_selected_process_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; + 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.reset_multi_tap_keys(); - } else { + if is_first_d { self.awaiting_second_char = true; - self.second_char = 'd'; + self.second_char = Some('d'); } } } 'g' => { - if self.awaiting_second_char && self.second_char == 'g' { - self.awaiting_second_char = false; - self.second_char = ' '; - self.skip_to_first(); - } else { + let mut is_first_g = true; + if let Some(second_char) = self.second_char { + if self.awaiting_second_char && second_char == 'g' { + is_first_g = false; + self.awaiting_second_char = false; + self.second_char = None; + self.skip_to_first(); + } + } + + if is_first_g { self.awaiting_second_char = true; - self.second_char = 'g'; + self.second_char = Some('g'); } } 'G' => self.skip_to_last(), @@ -558,7 +598,9 @@ impl App { } } self.update_process_gui = true; - self.currently_selected_process_position = 0; + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = 0; } 'm' => { match self.process_sorting_type { @@ -571,7 +613,9 @@ impl App { } } self.update_process_gui = true; - self.currently_selected_process_position = 0; + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = 0; } 'p' => { // Disable if grouping @@ -586,7 +630,9 @@ impl App { } } self.update_process_gui = true; - self.currently_selected_process_position = 0; + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = 0; } } 'n' => { @@ -600,15 +646,20 @@ impl App { } } self.update_process_gui = true; - self.currently_selected_process_position = 0; + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = 0; } '?' => { self.help_dialog_state.is_showing_help = true; } _ => {} } - if self.awaiting_second_char && caught_char != self.second_char { - self.awaiting_second_char = false; + + if let Some(second_char) = self.second_char { + if self.awaiting_second_char && caught_char != second_char { + self.awaiting_second_char = false; + } } } } else if self.help_dialog_state.is_showing_help { @@ -648,8 +699,8 @@ impl App { // Network -(up)> MEM, -(right)> PROC // PROC -(up)> Disk, -(down)> PROC_SEARCH, -(left)> Network // PROC_SEARCH -(up)> PROC, -(left)> Network - pub fn move_left(&mut self) { - if !self.is_in_dialog() { + pub fn move_widget_selection_left(&mut self) { + if !self.is_in_dialog() && !self.is_expanded { self.current_widget_selected = match self.current_widget_selected { WidgetPosition::Process => WidgetPosition::Network, WidgetPosition::ProcessSearch => WidgetPosition::Network, @@ -657,23 +708,25 @@ impl App { WidgetPosition::Temp => WidgetPosition::Mem, _ => self.current_widget_selected, }; - self.reset_multi_tap_keys(); } + + self.reset_multi_tap_keys(); } - pub fn move_right(&mut self) { - if !self.is_in_dialog() { + pub fn move_widget_selection_right(&mut self) { + if !self.is_in_dialog() && !self.is_expanded { self.current_widget_selected = match self.current_widget_selected { WidgetPosition::Mem => WidgetPosition::Temp, WidgetPosition::Network => WidgetPosition::Process, _ => self.current_widget_selected, }; - self.reset_multi_tap_keys(); } + + self.reset_multi_tap_keys(); } - pub fn move_up(&mut self) { - if !self.is_in_dialog() { + pub fn move_widget_selection_up(&mut self) { + if !self.is_in_dialog() && !self.is_expanded { self.current_widget_selected = match self.current_widget_selected { WidgetPosition::Mem => WidgetPosition::Cpu, WidgetPosition::Network => WidgetPosition::Mem, @@ -683,12 +736,18 @@ impl App { WidgetPosition::Disk => WidgetPosition::Temp, _ => self.current_widget_selected, }; - self.reset_multi_tap_keys(); + } else if self.is_expanded { + self.current_widget_selected = match self.current_widget_selected { + WidgetPosition::ProcessSearch => WidgetPosition::Process, + _ => self.current_widget_selected, + }; } + + self.reset_multi_tap_keys(); } - pub fn move_down(&mut self) { - if !self.is_in_dialog() { + pub fn move_widget_selection_down(&mut self) { + if !self.is_in_dialog() && !self.is_expanded { self.current_widget_selected = match self.current_widget_selected { WidgetPosition::Cpu => WidgetPosition::Mem, WidgetPosition::Mem => WidgetPosition::Network, @@ -703,21 +762,49 @@ impl App { } _ => self.current_widget_selected, }; - self.reset_multi_tap_keys(); + } else if self.is_expanded { + self.current_widget_selected = match self.current_widget_selected { + WidgetPosition::Process => { + if self.is_searching() { + WidgetPosition::ProcessSearch + } else { + WidgetPosition::Process + } + } + _ => self.current_widget_selected, + }; } + + self.reset_multi_tap_keys(); } pub fn skip_to_first(&mut self) { if !self.is_in_dialog() { match self.current_widget_selected { - WidgetPosition::Process => self.currently_selected_process_position = 0, - WidgetPosition::Temp => self.currently_selected_temperature_position = 0, - WidgetPosition::Disk => self.currently_selected_disk_position = 0, - WidgetPosition::Cpu => self.currently_selected_cpu_table_position = 0, + WidgetPosition::Process => { + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = 0 + } + WidgetPosition::Temp => { + self.app_scroll_positions + .temp_scroll_state + .current_scroll_position = 0 + } + WidgetPosition::Disk => { + self.app_scroll_positions + .disk_scroll_state + .current_scroll_position = 0 + } + WidgetPosition::Cpu => { + self.app_scroll_positions + .cpu_scroll_state + .current_scroll_position = 0 + } _ => {} } - self.scroll_direction = ScrollDirection::UP; + self.app_scroll_positions.scroll_direction = ScrollDirection::UP; self.reset_multi_tap_keys(); } } @@ -726,24 +813,28 @@ impl App { if !self.is_in_dialog() { match self.current_widget_selected { WidgetPosition::Process => { - self.currently_selected_process_position = - self.canvas_data.finalized_process_data.len() as u64 - 1 + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = self.canvas_data.finalized_process_data.len() as u64 - 1 } WidgetPosition::Temp => { - self.currently_selected_temperature_position = - self.canvas_data.temp_sensor_data.len() as u64 - 1 + self.app_scroll_positions + .temp_scroll_state + .current_scroll_position = self.canvas_data.temp_sensor_data.len() as u64 - 1 } WidgetPosition::Disk => { - self.currently_selected_disk_position = - self.canvas_data.disk_data.len() as u64 - 1 + self.app_scroll_positions + .disk_scroll_state + .current_scroll_position = self.canvas_data.disk_data.len() as u64 - 1 } WidgetPosition::Cpu => { - self.currently_selected_cpu_table_position = - self.canvas_data.cpu_data.len() as u64 - 1; + self.app_scroll_positions + .cpu_scroll_state + .current_scroll_position = self.canvas_data.cpu_data.len() as u64 - 1; } _ => {} } - self.scroll_direction = ScrollDirection::DOWN; + self.app_scroll_positions.scroll_direction = ScrollDirection::DOWN; self.reset_multi_tap_keys(); } } @@ -757,7 +848,7 @@ impl App { WidgetPosition::Cpu => self.change_cpu_table_position(-1), // TODO: [PO?] Temporary, may change if we add scaling _ => {} } - self.scroll_direction = ScrollDirection::UP; + self.app_scroll_positions.scroll_direction = ScrollDirection::UP; self.reset_multi_tap_keys(); } } @@ -771,48 +862,70 @@ impl App { WidgetPosition::Cpu => self.change_cpu_table_position(1), // TODO: [PO?] Temporary, may change if we add scaling _ => {} } - self.scroll_direction = ScrollDirection::DOWN; + self.app_scroll_positions.scroll_direction = ScrollDirection::DOWN; self.reset_multi_tap_keys(); } } fn change_cpu_table_position(&mut self, num_to_change_by: i64) { - if self.currently_selected_cpu_table_position as i64 + num_to_change_by >= 0 - && self.currently_selected_cpu_table_position as i64 + num_to_change_by - < self.canvas_data.cpu_data.len() as i64 + let current_posn = self + .app_scroll_positions + .cpu_scroll_state + .current_scroll_position; + + if current_posn as i64 + num_to_change_by >= 0 + && current_posn as i64 + num_to_change_by < self.canvas_data.cpu_data.len() as i64 { - self.currently_selected_cpu_table_position = - (self.currently_selected_cpu_table_position as i64 + num_to_change_by) as u64; + self.app_scroll_positions + .cpu_scroll_state + .current_scroll_position = (current_posn as i64 + num_to_change_by) as u64; } } fn change_process_position(&mut self, num_to_change_by: i64) { - if self.currently_selected_process_position as i64 + num_to_change_by >= 0 - && self.currently_selected_process_position as i64 + num_to_change_by + let current_posn = self + .app_scroll_positions + .process_scroll_state + .current_scroll_position; + + if current_posn as i64 + num_to_change_by >= 0 + && current_posn as i64 + num_to_change_by < self.canvas_data.finalized_process_data.len() as i64 { - self.currently_selected_process_position = - (self.currently_selected_process_position as i64 + num_to_change_by) as u64; + self.app_scroll_positions + .process_scroll_state + .current_scroll_position = (current_posn as i64 + num_to_change_by) as u64; } } fn change_temp_position(&mut self, num_to_change_by: i64) { - if self.currently_selected_temperature_position as i64 + num_to_change_by >= 0 - && self.currently_selected_temperature_position as i64 + num_to_change_by + let current_posn = self + .app_scroll_positions + .temp_scroll_state + .current_scroll_position; + + if current_posn as i64 + num_to_change_by >= 0 + && current_posn as i64 + num_to_change_by < self.canvas_data.temp_sensor_data.len() as i64 { - self.currently_selected_temperature_position = - (self.currently_selected_temperature_position as i64 + num_to_change_by) as u64; + self.app_scroll_positions + .temp_scroll_state + .current_scroll_position = (current_posn as i64 + num_to_change_by) as u64; } } fn change_disk_position(&mut self, num_to_change_by: i64) { - if self.currently_selected_disk_position as i64 + num_to_change_by >= 0 - && self.currently_selected_disk_position as i64 + num_to_change_by - < self.canvas_data.disk_data.len() as i64 + let current_posn = self + .app_scroll_positions + .disk_scroll_state + .current_scroll_position; + + if current_posn as i64 + num_to_change_by >= 0 + && current_posn as i64 + num_to_change_by < self.canvas_data.disk_data.len() as i64 { - self.currently_selected_disk_position = - (self.currently_selected_disk_position as i64 + num_to_change_by) as u64; + self.app_scroll_positions + .disk_scroll_state + .current_scroll_position = (current_posn as i64 + num_to_change_by) as u64; } } } diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs index db478038..4c6fd938 100644 --- a/src/app/data_harvester.rs +++ b/src/app/data_harvester.rs @@ -115,6 +115,8 @@ impl DataState { let current_instant = std::time::Instant::now(); + // TODO: [OPT] MT/Async the harvesting step. + // Network self.data.network = network::get_network_data( &self.sys, diff --git a/src/canvas.rs b/src/canvas.rs index c90c9829..6202afe1 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -1,5 +1,5 @@ use crate::{ - app::{self, data_harvester::processes::ProcessHarvest}, + app::{self, data_harvester::processes::ProcessHarvest, WidgetPosition}, constants::*, data_conversion::{ConvertedCpuData, ConvertedProcessData}, utils::error, @@ -146,9 +146,9 @@ impl Painter { .margin(1) .constraints( [ - Constraint::Percentage(32), - Constraint::Percentage(36), - Constraint::Percentage(32), + Constraint::Percentage(30), + Constraint::Percentage(40), + Constraint::Percentage(30), ] .as_ref(), ) @@ -319,6 +319,72 @@ impl Painter { // This is a bit nasty, but it works well... I guess. app_state.delete_dialog_state.is_showing_dd = false; } + } else if app_state.is_expanded { + // TODO: [REF] we should combine this with normal drawing tbh + + let rect = Layout::default() + .margin(1) + .constraints([Constraint::Percentage(100)].as_ref()) + .split(f.size()); + match &app_state.current_widget_selected { + WidgetPosition::Cpu => { + let cpu_chunk = Layout::default() + .direction(Direction::Horizontal) + .margin(0) + .constraints( + if app_state.app_config_fields.left_legend { + [Constraint::Percentage(15), Constraint::Percentage(85)] + } else { + [Constraint::Percentage(85), Constraint::Percentage(15)] + } + .as_ref(), + ) + .split(rect[0]); + + let legend_index = if app_state.app_config_fields.left_legend { + 0 + } else { + 1 + }; + let graph_index = if app_state.app_config_fields.left_legend { + 1 + } else { + 0 + }; + + self.draw_cpu_graph(&mut f, &app_state, cpu_chunk[graph_index]); + self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]); + } + WidgetPosition::Mem => { + self.draw_memory_graph(&mut f, &app_state, rect[0]); + } + WidgetPosition::Disk => { + self.draw_disk_table(&mut f, app_state, rect[0]); + } + WidgetPosition::Temp => { + self.draw_temp_table(&mut f, app_state, rect[0]); + } + WidgetPosition::Network => { + self.draw_network_graph(&mut f, &app_state, rect[0]); + } + WidgetPosition::Process | WidgetPosition::ProcessSearch => { + if app_state.is_searching() { + let processes_chunk = Layout::default() + .direction(Direction::Vertical) + .margin(0) + .constraints( + [Constraint::Percentage(85), Constraint::Percentage(15)] + .as_ref(), + ) + .split(rect[0]); + + self.draw_processes_table(&mut f, app_state, processes_chunk[0]); + self.draw_search_field(&mut f, app_state, processes_chunk[1]); + } else { + self.draw_processes_table(&mut f, app_state, rect[0]); + } + } + } } else { // TODO: [TUI] Change this back to a more even 33/33/34 when TUI releases let vertical_chunks = Layout::default() @@ -357,7 +423,7 @@ impl Painter { .direction(Direction::Horizontal) .margin(0) .constraints( - if app_state.left_legend { + if app_state.app_config_fields.left_legend { [Constraint::Percentage(15), Constraint::Percentage(85)] } else { [Constraint::Percentage(85), Constraint::Percentage(15)] @@ -386,8 +452,16 @@ impl Painter { .split(bottom_chunks[0]); // Default chunk index based on left or right legend setting - let legend_index = if app_state.left_legend { 0 } else { 1 }; - let graph_index = if app_state.left_legend { 1 } else { 0 }; + let legend_index = if app_state.app_config_fields.left_legend { + 0 + } else { + 1 + }; + let graph_index = if app_state.app_config_fields.left_legend { + 1 + } else { + 0 + }; // Set up blocks and their components // CPU graph @@ -468,7 +542,7 @@ impl Painter { )); } - if app_state.show_average_cpu { + if app_state.app_config_fields.show_average_cpu { if let Some(avg_cpu_entry) = cpu_data.first() { cpu_entries_vec.push(( self.colours.cpu_colour_styles[0], @@ -484,7 +558,7 @@ impl Painter { for cpu_entry in &cpu_entries_vec { dataset_vector.push( Dataset::default() - .marker(if app_state.use_dot { + .marker(if app_state.app_config_fields.use_dot { Marker::Dot } else { Marker::Braille @@ -519,9 +593,15 @@ impl Painter { let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; let start_position = get_start_position( num_rows, - &(app_state.scroll_direction), - &mut app_state.previous_cpu_table_position, - app_state.currently_selected_cpu_table_position, + &(app_state.app_scroll_positions.scroll_direction), + &mut app_state + .app_scroll_positions + .cpu_scroll_state + .previous_scroll_position, + app_state + .app_scroll_positions + .cpu_scroll_state + .current_scroll_position, ); let sliced_cpu_data = &cpu_data[start_position as usize..]; @@ -547,7 +627,10 @@ impl Painter { match app_state.current_widget_selected { app::WidgetPosition::Cpu => { if cpu_row_counter as u64 - == app_state.currently_selected_cpu_table_position - start_position + == app_state + .app_scroll_positions + .cpu_scroll_state + .current_scroll_position - start_position { cpu_row_counter = -1; self.colours.currently_selected_text_style @@ -612,7 +695,7 @@ impl Painter { let mut mem_canvas_vec: Vec<Dataset> = vec![Dataset::default() .name(&app_state.canvas_data.mem_label) - .marker(if app_state.use_dot { + .marker(if app_state.app_config_fields.use_dot { Marker::Dot } else { Marker::Braille @@ -624,7 +707,7 @@ impl Painter { mem_canvas_vec.push( Dataset::default() .name(&app_state.canvas_data.swap_label) - .marker(if app_state.use_dot { + .marker(if app_state.app_config_fields.use_dot { Marker::Dot } else { Marker::Braille @@ -682,7 +765,7 @@ impl Painter { "RX: {:7}", app_state.canvas_data.rx_display.clone() )) - .marker(if app_state.use_dot { + .marker(if app_state.app_config_fields.use_dot { Marker::Dot } else { Marker::Braille @@ -694,13 +777,21 @@ impl Painter { "TX: {:7}", app_state.canvas_data.tx_display.clone() )) - .marker(if app_state.use_dot { + .marker(if app_state.app_config_fields.use_dot { Marker::Dot } else { Marker::Braille }) .style(self.colours.tx_style) .data(&network_data_tx), + Dataset::default().name(&format!( + "Total RX: {:7}", + app_state.canvas_data.total_rx_display.clone() + )), + Dataset::default().name(&format!( + "Total TX: {:7}", + app_state.canvas_data.total_tx_display.clone() + )), ]) .render(f, draw_loc); } @@ -763,9 +854,15 @@ impl Painter { let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; let start_position = get_start_position( num_rows, - &(app_state.scroll_direction), - &mut app_state.previous_temp_position, - app_state.currently_selected_temperature_position, + &(app_state.app_scroll_positions.scroll_direction), + &mut app_state + .app_scroll_positions + .temp_scroll_state + .previous_scroll_position, + app_state + .app_scroll_positions + .temp_scroll_state + .current_scroll_position, ); let sliced_vec = &(temp_sensor_data[start_position as usize..]); @@ -777,7 +874,10 @@ impl Painter { match app_state.current_widget_selected { app::WidgetPosition::Temp => { if temp_row_counter as u64 - == app_state.currently_selected_temperature_position - start_position + == app_state + .app_scroll_positions + .temp_scroll_state + .current_scroll_position - start_position { temp_row_counter = -1; self.colours.currently_selected_text_style @@ -829,9 +929,15 @@ impl Painter { let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; let start_position = get_start_position( num_rows, - &(app_state.scroll_direction), - &mut app_state.previous_disk_position, - app_state.currently_selected_disk_position, + &(app_state.app_scroll_positions.scroll_direction), + &mut app_state + .app_scroll_positions + .disk_scroll_state + .previous_scroll_position, + app_state + .app_scroll_positions + .disk_scroll_state + .current_scroll_position, ); let sliced_vec = &disk_data[start_position as usize..]; @@ -843,7 +949,10 @@ impl Painter { match app_state.current_widget_selected { app::WidgetPosition::Disk => { if disk_counter as u64 - == app_state.currently_selected_disk_position - start_position + == app_state + .app_scroll_positions + .disk_scroll_state + .current_scroll_position - start_position { disk_counter = -1; self.colours.currently_selected_text_style @@ -1029,9 +1138,15 @@ impl Painter { let position = get_start_position( num_rows, - &(app_state.scroll_direction), - &mut app_state.previous_process_position, - app_state.currently_selected_process_position, + &(app_state.app_scroll_positions.scroll_direction), + &mut app_state + .app_scroll_positions + .process_scroll_state + . |