From f0dad8f5bf4c409e568ee7a0d739b109d9bf45be Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Tue, 11 Feb 2020 21:16:43 -0500 Subject: [PATCH] Added maximizing mode to allow users to zoom into a particular widget. Not 100% done. --- README.md | 8 +- src/app.rs | 409 ++++++++++++++++++++++++-------------- src/app/data_harvester.rs | 2 + src/canvas.rs | 176 +++++++++++++--- src/constants.rs | 3 +- src/data_conversion.rs | 2 +- src/main.rs | 42 ++-- 7 files changed, 447 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index bda2fc70..bc3b6d3e 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/src/app.rs b/src/app.rs index 270aa823..99ea5ee7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -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 current_scroll_position: u64, + pub previous_scroll_position: u64, } -/// AppScrollWidgetState deals with fields for a scrollable app's current state. -pub struct AppScrollWidgetState { - pub widget_scroll_position: i64, +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, pub dd_err: Option, to_delete_process_list: Option<(String, Vec)>, 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; + 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; + // 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 = ' '; + 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.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; + 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()); + 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; + 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]) - }; + 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.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 = 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 + .previous_scroll_position, + app_state + .app_scroll_positions + .process_scroll_state + .current_scroll_position, ); // Sanity check @@ -1061,7 +1176,10 @@ impl Painter { match app_state.current_widget_selected { app::WidgetPosition::Process => { if process_counter as u64 - == app_state.currently_selected_process_position - start_position + == app_state + .app_scroll_positions + .process_scroll_state + .current_scroll_position - start_position { process_counter = -1; self.colours.currently_selected_text_style diff --git a/src/constants.rs b/src/constants.rs index 2bfc89f5..aab1ce02 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -12,7 +12,7 @@ pub const DEFAULT_UNIX_CONFIG_FILE_PATH: &str = "~/.config/btm/btm.toml"; pub const DEFAULT_WINDOWS_CONFIG_FILE_PATH: &str = ""; // Help text -pub const GENERAL_HELP_TEXT: [&str; 15] = [ +pub const GENERAL_HELP_TEXT: [&str; 16] = [ "General Keybindings\n\n", "Esc Close dialog box\n", "q, Ctrl-c Quit bottom\n", @@ -28,6 +28,7 @@ pub const GENERAL_HELP_TEXT: [&str; 15] = [ "? Open the help screen\n", "gg Skip to the first entry of a list\n", "G Skip to the last entry of a list\n", + "Enter Maximize the currently selected widget\n", ]; pub const PROCESS_HELP_TEXT: [&str; 8] = [ diff --git a/src/data_conversion.rs b/src/data_conversion.rs index ec4373e9..50a8313e 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -62,7 +62,7 @@ pub fn convert_temp_row(app: &App) -> Vec> { let mut sensor_vector: Vec> = Vec::new(); let current_data = &app.data_collection; - let temp_type = &app.temperature_type; + let temp_type = &app.app_config_fields.temperature_type; if current_data.temp_harvest.is_empty() { sensor_vector.push(vec!["No Sensors Found".to_string(), "".to_string()]) diff --git a/src/main.rs b/src/main.rs index a63858f4..45815701 100644 --- a/src/main.rs +++ b/src/main.rs @@ -182,7 +182,7 @@ fn main() -> error::Result<()> { rrx, use_current_cpu_total, update_rate_in_milliseconds as u64, - app.temperature_type.clone(), + app.app_config_fields.temperature_type.clone(), ); let mut painter = canvas::Painter::default(); @@ -214,6 +214,8 @@ fn main() -> error::Result<()> { // Convert all data into tui-compliant components + // TODO: [OPT] MT the conversion step. + // Network let network_data = convert_network_data_points(&app.data_collection); app.canvas_data.network_data_rx = network_data.rx; @@ -236,8 +238,10 @@ fn main() -> error::Result<()> { app.canvas_data.swap_label = memory_and_swap_labels.1; // CPU - app.canvas_data.cpu_data = - convert_cpu_data_points(app.show_average_cpu, &app.data_collection); + app.canvas_data.cpu_data = convert_cpu_data_points( + app.app_config_fields.show_average_cpu, + &app.data_collection, + ); // Processes let (single, grouped) = convert_process_data(&app.data_collection); @@ -294,10 +298,10 @@ fn handle_key_event_or_break( KeyCode::Down => app.on_down_key(), KeyCode::Left => app.on_left_key(), KeyCode::Right => app.on_right_key(), - KeyCode::Char('H') => app.move_left(), - KeyCode::Char('L') => app.move_right(), - KeyCode::Char('K') => app.move_up(), - KeyCode::Char('J') => app.move_down(), + KeyCode::Char('H') => app.move_widget_selection_left(), + KeyCode::Char('L') => app.move_widget_selection_right(), + KeyCode::Char('K') => app.move_widget_selection_up(), + KeyCode::Char('J') => app.move_widget_selection_down(), KeyCode::Char(character) => app.on_char_key(character), KeyCode::Esc => app.on_esc(), KeyCode::Enter => app.on_enter(), @@ -314,10 +318,10 @@ fn handle_key_event_or_break( match event.code { KeyCode::Char('f') => app.enable_searching(), - KeyCode::Left => app.move_left(), - KeyCode::Right => app.move_right(), - KeyCode::Up => app.move_up(), - KeyCode::Down => app.move_down(), + KeyCode::Left => app.move_widget_selection_left(), + KeyCode::Right => app.move_widget_selection_right(), + KeyCode::Up => app.move_widget_selection_up(), + KeyCode::Down => app.move_widget_selection_down(), KeyCode::Char('r') => { if rtx.send(ResetEvent::Reset).is_ok() { app.reset(); @@ -329,10 +333,18 @@ fn handle_key_event_or_break( } } else if let KeyModifiers::SHIFT = event.modifiers { match event.code { - KeyCode::Left | KeyCode::Char('h') | KeyCode::Char('H') => app.move_left(), - KeyCode::Right | KeyCode::Char('l') | KeyCode::Char('L') => app.move_right(), - KeyCode::Up | KeyCode::Char('k') | KeyCode::Char('K') => app.move_up(), - KeyCode::Down | KeyCode::Char('j') | KeyCode::Char('J') => app.move_down(), + KeyCode::Left | KeyCode::Char('h') | KeyCode::Char('H') => { + app.move_widget_selection_left() + } + KeyCode::Right | KeyCode::Char('l') | KeyCode::Char('L') => { + app.move_widget_selection_right() + } + KeyCode::Up | KeyCode::Char('k') | KeyCode::Char('K') => { + app.move_widget_selection_up() + } + KeyCode::Down | KeyCode::Char('j') | KeyCode::Char('J') => { + app.move_widget_selection_down() + } KeyCode::Char('/') | KeyCode::Char('?') => app.on_char_key('?'), _ => {} }