From 18af6b01bf9c19a67deece57212b4c366d65ca16 Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sun, 5 Sep 2021 19:02:50 -0400 Subject: [PATCH] refactor: delete a bunch of old unused code --- src/app.rs | 513 +------------ src/app/widgets/bottom_widgets/battery.rs | 8 - src/app/widgets/bottom_widgets/cpu.rs | 21 +- src/app/widgets/bottom_widgets/disk.rs | 9 - src/app/widgets/bottom_widgets/mem.rs | 26 - src/app/widgets/bottom_widgets/net.rs | 42 -- src/app/widgets/bottom_widgets/process.rs | 64 -- src/canvas.rs | 6 +- src/canvas/drawing.rs | 15 - src/canvas/drawing/basic_table_arrows.rs | 154 ---- src/canvas/drawing/battery_display.rs | 197 ----- src/canvas/drawing/cpu_basic.rs | 199 ----- src/canvas/drawing/cpu_graph.rs | 527 ------------- src/canvas/drawing/disk_table.rs | 266 ------- src/canvas/drawing/mem_basic.rs | 123 ---- src/canvas/drawing/mem_graph.rs | 240 ------ src/canvas/drawing/network_basic.rs | 71 -- src/canvas/drawing/network_graph.rs | 758 ------------------- src/canvas/drawing/process_table.rs | 856 ---------------------- src/canvas/drawing/temp_table.rs | 256 ------- src/canvas/drawing_utils.rs | 211 ------ src/data_conversion.rs | 6 +- src/lib.rs | 313 +------- src/options.rs | 153 +--- src/options/layout_options.rs | 322 -------- 25 files changed, 10 insertions(+), 5346 deletions(-) delete mode 100644 src/canvas/drawing.rs delete mode 100644 src/canvas/drawing/basic_table_arrows.rs delete mode 100644 src/canvas/drawing/battery_display.rs delete mode 100644 src/canvas/drawing/cpu_basic.rs delete mode 100644 src/canvas/drawing/cpu_graph.rs delete mode 100644 src/canvas/drawing/disk_table.rs delete mode 100644 src/canvas/drawing/mem_basic.rs delete mode 100644 src/canvas/drawing/mem_graph.rs delete mode 100644 src/canvas/drawing/network_basic.rs delete mode 100644 src/canvas/drawing/network_graph.rs delete mode 100644 src/canvas/drawing/process_table.rs delete mode 100644 src/canvas/drawing/temp_table.rs delete mode 100644 src/canvas/drawing_utils.rs diff --git a/src/app.rs b/src/app.rs index 54dade87..7e2451b6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2300,87 +2300,7 @@ impl AppState { } } - pub fn skip_to_last(&mut self) { - // if !self.ignore_normal_keybinds() { - // match self.current_widget.widget_type { - // BottomWidgetType::Proc => { - // if let Some(proc_widget_state) = self - // .proc_state - // .get_mut_widget_state(self.current_widget.widget_id) - // { - // if let Some(finalized_process_data) = self - // .canvas_data - // .finalized_process_data_map - // .get(&self.current_widget.widget_id) - // { - // if !self.canvas_data.finalized_process_data_map.is_empty() { - // proc_widget_state.scroll_state.current_scroll_position = - // finalized_process_data.len() - 1; - // proc_widget_state.scroll_state.scroll_direction = - // ScrollDirection::Down; - // } - // } - // } - // } - // BottomWidgetType::ProcSort => { - // if let Some(proc_widget_state) = self - // .proc_state - // .get_mut_widget_state(self.current_widget.widget_id - 2) - // { - // proc_widget_state.columns.current_scroll_position = - // proc_widget_state.columns.get_enabled_columns_len() - 1; - // proc_widget_state.columns.scroll_direction = ScrollDirection::Down; - // } - // } - // BottomWidgetType::Temp => { - // if let Some(temp_widget_state) = self - // .temp_state - // .get_mut_widget_state(self.current_widget.widget_id) - // { - // if !self.canvas_data.temp_sensor_data.is_empty() { - // temp_widget_state.scroll_state.current_scroll_position = - // self.canvas_data.temp_sensor_data.len() - 1; - // temp_widget_state.scroll_state.scroll_direction = ScrollDirection::Down; - // } - // } - // } - // BottomWidgetType::Disk => { - // if let Some(disk_widget_state) = self - // .disk_state - // .get_mut_widget_state(self.current_widget.widget_id) - // { - // if !self.canvas_data.disk_data.is_empty() { - // disk_widget_state.scroll_state.current_scroll_position = - // self.canvas_data.disk_data.len() - 1; - // disk_widget_state.scroll_state.scroll_direction = ScrollDirection::Down; - // } - // } - // } - // BottomWidgetType::CpuLegend => { - // if let Some(cpu_widget_state) = self - // .cpu_state - // .get_mut_widget_state(self.current_widget.widget_id - 1) - // { - // let cap = self.canvas_data.cpu_data.len(); - // if cap > 0 { - // cpu_widget_state.scroll_state.current_scroll_position = cap - 1; - // cpu_widget_state.scroll_state.scroll_direction = ScrollDirection::Down; - // } - // } - // } - // _ => {} - // } - // self.reset_multi_tap_keys(); - // } else if self.help_dialog_state.is_showing_help { - // self.help_dialog_state.scroll_state.current_scroll_index = self - // .help_dialog_state - // .scroll_state - // .max_scroll_index - // .saturating_sub(1); - // } else if self.delete_dialog_state.is_showing_dd { - // self.delete_dialog_state.selected_signal = KillSignal::Kill(MAX_SIGNAL); - // } - } + pub fn skip_to_last(&mut self) {} pub fn decrement_position_count(&mut self) { if !self.ignore_normal_keybinds() { @@ -2461,35 +2381,6 @@ impl AppState { /// Returns the new position. fn increment_process_position(&mut self, _num_to_change_by: i64) -> Option { - // if let Some(proc_widget_state) = self - // .proc_state - // .get_mut_widget_state(self.current_widget.widget_id) - // { - // let current_posn = proc_widget_state.scroll_state.current_scroll_position; - // if let Some(finalized_process_data) = self - // .canvas_data - // .finalized_process_data_map - // .get(&self.current_widget.widget_id) - // { - // if current_posn as i64 + num_to_change_by >= 0 - // && current_posn as i64 + num_to_change_by < finalized_process_data.len() as i64 - // { - // proc_widget_state.scroll_state.current_scroll_position = - // (current_posn as i64 + num_to_change_by) as usize; - // } else { - // return None; - // } - // } - - // if num_to_change_by < 0 { - // proc_widget_state.scroll_state.scroll_direction = ScrollDirection::Up; - // } else { - // proc_widget_state.scroll_state.scroll_direction = ScrollDirection::Down; - // } - - // return Some(proc_widget_state.scroll_state.current_scroll_position); - // } - None } @@ -2563,40 +2454,6 @@ impl AppState { } } - pub fn handle_scroll_up(&mut self) { - if self.delete_dialog_state.is_showing_dd { - #[cfg(target_family = "unix")] - { - self.on_up_key(); - return; - } - } - if self.help_dialog_state.is_showing_help { - self.help_scroll_up(); - } else if self.current_widget.widget_type.is_widget_graph() { - self.zoom_in(); - } else if self.current_widget.widget_type.is_widget_table() { - self.decrement_position_count(); - } - } - - pub fn handle_scroll_down(&mut self) { - if self.delete_dialog_state.is_showing_dd { - #[cfg(target_family = "unix")] - { - self.on_down_key(); - return; - } - } - if self.help_dialog_state.is_showing_help { - self.help_scroll_down(); - } else if self.current_widget.widget_type.is_widget_graph() { - self.zoom_out(); - } else if self.current_widget.widget_type.is_widget_table() { - self.increment_position_count(); - } - } - fn on_plus(&mut self) { if let BottomWidgetType::Proc = self.current_widget.widget_type { // Toggle collapsing if tree @@ -2615,34 +2472,7 @@ impl AppState { } } - fn toggle_collapsing_process_branch(&mut self) { - // if let Some(proc_widget_state) = self - // .proc_state - // .widget_states - // .get_mut(&self.current_widget.widget_id) - // { - // let current_posn = proc_widget_state.scroll_state.current_scroll_position; - - // if let Some(displayed_process_list) = self - // .canvas_data - // .finalized_process_data_map - // .get(&self.current_widget.widget_id) - // { - // if let Some(corresponding_process) = displayed_process_list.get(current_posn) { - // let corresponding_pid = corresponding_process.pid; - - // if let Some(process_data) = self - // .canvas_data - // .single_process_data - // .get_mut(&corresponding_pid) - // { - // process_data.is_collapsed_entry = !process_data.is_collapsed_entry; - // self.proc_state.force_update = Some(self.current_widget.widget_id); - // } - // } - // } - // } - } + fn toggle_collapsing_process_branch(&mut self) {} fn zoom_out(&mut self) { match self.current_widget.widget_type { @@ -2856,343 +2686,4 @@ impl AppState { _ => {} } } - - /// Moves the mouse to the widget that was clicked on, then propagates the click down to be - /// handled by the widget specifically. - pub fn on_left_mouse_up(&mut self, x: u16, y: u16) { - // Pretty dead simple - iterate through the widget map and go to the widget where the click - // is within. - - // TODO: [REFACTOR] might want to refactor this, it's really ugly. - // TODO: [REFACTOR] Might wanna refactor ALL state things in general, currently everything - // is grouped up as an app state. We should separate stuff like event state and gui state and etc. - - // TODO: [MOUSE] double click functionality...? We would do this above all other actions and SC if needed. - - // Short circuit if we're in basic table... we might have to handle the basic table arrow - // case here... - - if let Some(bt) = &mut self.basic_table_widget_state { - if let ( - Some((left_tlc_x, left_tlc_y)), - Some((left_brc_x, left_brc_y)), - Some((right_tlc_x, right_tlc_y)), - Some((right_brc_x, right_brc_y)), - ) = (bt.left_tlc, bt.left_brc, bt.right_tlc, bt.right_brc) - { - if (x >= left_tlc_x && y >= left_tlc_y) && (x < left_brc_x && y < left_brc_y) { - // Case for the left "button" in the simple arrow. - if let Some(new_widget) = - self.widget_map.get(&(bt.currently_displayed_widget_id)) - { - // We have to move to the current table widget first... - self.current_widget = new_widget.clone(); - - if let BottomWidgetType::Proc = &new_widget.widget_type { - if let Some(proc_widget_state) = - self.proc_state.get_widget_state(new_widget.widget_id) - { - if proc_widget_state.is_sort_open { - self.move_widget_selection(&WidgetDirection::Left); - } - } - } - self.move_widget_selection(&WidgetDirection::Left); - return; - } - } else if (x >= right_tlc_x && y >= right_tlc_y) - && (x < right_brc_x && y < right_brc_y) - { - // Case for the right "button" in the simple arrow. - if let Some(new_widget) = - self.widget_map.get(&(bt.currently_displayed_widget_id)) - { - // We have to move to the current table widget first... - self.current_widget = new_widget.clone(); - - if let BottomWidgetType::ProcSort = &new_widget.widget_type { - if let Some(proc_widget_state) = - self.proc_state.get_widget_state(new_widget.widget_id - 2) - { - if proc_widget_state.is_sort_open { - self.move_widget_selection(&WidgetDirection::Right); - } - } - } - } - self.move_widget_selection(&WidgetDirection::Right); - // Bit extra logic to ensure you always land on a proc widget, not the sort - if let BottomWidgetType::ProcSort = &self.current_widget.widget_type { - self.move_widget_selection(&WidgetDirection::Right); - } - return; - } - } - } - - // Second short circuit --- are we in the dd dialog state? If so, only check yes/no/signals - // and bail after. - if self.is_in_dialog() { - match self.delete_dialog_state.button_positions.iter().find( - |(tl_x, tl_y, br_x, br_y, _idx)| { - (x >= *tl_x && y >= *tl_y) && (x <= *br_x && y <= *br_y) - }, - ) { - Some((_, _, _, _, 0)) => { - self.delete_dialog_state.selected_signal = KillSignal::Cancel - } - Some((_, _, _, _, idx)) => { - if *idx > 31 { - self.delete_dialog_state.selected_signal = KillSignal::Kill(*idx + 2) - } else { - self.delete_dialog_state.selected_signal = KillSignal::Kill(*idx) - } - } - _ => {} - } - return; - } - - let mut failed_to_get = true; - // TODO: [MOUSE] We could use a better data structure for this? Currently it's a blind - // traversal through a hashmap, using a 2d binary tree of sorts would be better. - // See: https://docs.rs/kdtree/0.6.0/kdtree/ - for (new_widget_id, widget) in &self.widget_map { - if let (Some((tlc_x, tlc_y)), Some((brc_x, brc_y))) = - (widget.top_left_corner, widget.bottom_right_corner) - { - if (x >= tlc_x && y >= tlc_y) && (x < brc_x && y < brc_y) { - if let Some(new_widget) = self.widget_map.get(new_widget_id) { - self.current_widget = new_widget.clone(); - - match &self.current_widget.widget_type { - BottomWidgetType::Temp - | BottomWidgetType::Proc - | BottomWidgetType::ProcSort - | BottomWidgetType::Disk - | BottomWidgetType::Battery => { - if let Some(basic_table_widget_state) = - &mut self.basic_table_widget_state - { - basic_table_widget_state.currently_displayed_widget_id = - self.current_widget.widget_id; - basic_table_widget_state.currently_displayed_widget_type = - self.current_widget.widget_type.clone(); - } - } - _ => {} - } - - failed_to_get = false; - break; - } - } - } - } - - if failed_to_get { - return; - } - - // Now handle click propagation down to widget. - if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) = ( - &self.current_widget.top_left_corner, - &self.current_widget.bottom_right_corner, - ) { - let border_offset = if self.is_drawing_border() { 1 } else { 0 }; - - // This check ensures the click isn't actually just clicking on the bottom border. - if y < (brc_y - border_offset) { - match &self.current_widget.widget_type { - BottomWidgetType::Proc - | BottomWidgetType::ProcSort - | BottomWidgetType::CpuLegend - | BottomWidgetType::Temp - | BottomWidgetType::Disk => { - // Get our index... - let clicked_entry = y - *tlc_y; - // + 1 so we start at 0. - let header_gap_offset = 1 + if self.is_drawing_gap(&self.current_widget) { - self.app_config_fields.table_gap - } else { - 0 - }; - let offset = border_offset + header_gap_offset; - if clicked_entry >= offset { - let offset_clicked_entry = clicked_entry - offset; - match &self.current_widget.widget_type { - BottomWidgetType::Proc => { - if let Some(proc_widget_state) = self - .proc_state - .get_widget_state(self.current_widget.widget_id) - { - if let Some(visual_index) = - proc_widget_state.scroll_state.table_state.selected() - { - // If in tree mode, also check to see if this click is on - // the same entry as the already selected one - if it is, - // then we minimize. - - let previous_scroll_position = proc_widget_state - .scroll_state - .current_scroll_position; - let is_tree_mode = proc_widget_state.is_tree_mode; - - let new_position = self.increment_process_position( - offset_clicked_entry as i64 - visual_index as i64, - ); - - if is_tree_mode { - if let Some(new_position) = new_position { - if previous_scroll_position == new_position { - self.toggle_collapsing_process_branch(); - } - } - } - } - } - } - BottomWidgetType::ProcSort => { - // TODO: This should sort if you double click! - if let Some(proc_widget_state) = self - .proc_state - .get_widget_state(self.current_widget.widget_id - 2) - { - if let Some(visual_index) = - proc_widget_state.columns.column_state.selected() - { - self.increment_process_sort_position( - offset_clicked_entry as i64 - visual_index as i64, - ); - } - } - } - BottomWidgetType::CpuLegend => { - if let Some(cpu_widget_state) = self - .cpu_state - .get_widget_state(self.current_widget.widget_id - 1) - { - if let Some(visual_index) = - cpu_widget_state.scroll_state.table_state.selected() - { - self.increment_cpu_legend_position( - offset_clicked_entry as i64 - visual_index as i64, - ); - } - } - } - BottomWidgetType::Temp => { - if let Some(temp_widget_state) = self - .temp_state - .get_widget_state(self.current_widget.widget_id) - { - if let Some(visual_index) = - temp_widget_state.scroll_state.table_state.selected() - { - self.increment_temp_position( - offset_clicked_entry as i64 - visual_index as i64, - ); - } - } - } - BottomWidgetType::Disk => { - if let Some(disk_widget_state) = self - .disk_state - .get_widget_state(self.current_widget.widget_id) - { - if let Some(visual_index) = - disk_widget_state.scroll_state.table_state.selected() - { - self.increment_disk_position( - offset_clicked_entry as i64 - visual_index as i64, - ); - } - } - } - _ => {} - } - } else { - // We might have clicked on a header! Check if we only exceeded the table + border offset, and - // it's implied we exceeded the gap offset. - if clicked_entry == border_offset { - #[allow(clippy::single_match)] - match &self.current_widget.widget_type { - BottomWidgetType::Proc => { - if let Some(proc_widget_state) = self - .proc_state - .get_mut_widget_state(self.current_widget.widget_id) - { - // Let's now check if it's a column header. - if let (Some(y_loc), Some(x_locs)) = ( - &proc_widget_state.columns.column_header_y_loc, - &proc_widget_state.columns.column_header_x_locs, - ) { - // debug!("x, y: {}, {}", x, y); - // debug!("y_loc: {}", y_loc); - // debug!("x_locs: {:?}", x_locs); - - if y == *y_loc { - for (itx, (x_left, x_right)) in - x_locs.iter().enumerate() - { - if x >= *x_left && x <= *x_right { - // Found our column! - proc_widget_state - .columns - .set_to_sorted_index_from_visual_index( - itx, - ); - proc_widget_state - .update_sorting_with_columns(); - self.proc_state.force_update = - Some(self.current_widget.widget_id); - break; - } - } - } - } - } - } - _ => {} - } - } - } - } - BottomWidgetType::Battery => { - if let Some(battery_widget_state) = self - .battery_state - .get_mut_widget_state(self.current_widget.widget_id) - { - if let Some(tab_spacing) = &battery_widget_state.tab_click_locs { - for (itx, ((tlc_x, tlc_y), (brc_x, brc_y))) in - tab_spacing.iter().enumerate() - { - if (x >= *tlc_x && y >= *tlc_y) && (x <= *brc_x && y <= *brc_y) - { - battery_widget_state.currently_selected_battery_index = itx; - break; - } - } - } - } - } - _ => {} - } - } - } - } - - fn is_drawing_border(&self) -> bool { - self.is_expanded || !self.app_config_fields.use_basic_mode - } - - fn is_drawing_gap(&self, widget: &BottomWidget) -> bool { - if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) = - (widget.top_left_corner, widget.bottom_right_corner) - { - brc_y - tlc_y >= constants::TABLE_GAP_HEIGHT_LIMIT - } else { - self.app_config_fields.table_gap == 0 - } - } } diff --git a/src/app/widgets/bottom_widgets/battery.rs b/src/app/widgets/bottom_widgets/battery.rs index b535d80b..d43e623b 100644 --- a/src/app/widgets/bottom_widgets/battery.rs +++ b/src/app/widgets/bottom_widgets/battery.rs @@ -35,17 +35,9 @@ pub struct BatteryState { } impl BatteryState { - pub fn init(widget_states: HashMap) -> Self { - BatteryState { widget_states } - } - pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut BatteryWidgetState> { self.widget_states.get_mut(&widget_id) } - - pub fn get_widget_state(&self, widget_id: u64) -> Option<&BatteryWidgetState> { - self.widget_states.get(&widget_id) - } } /// A table displaying battery information on a per-battery basis. diff --git a/src/app/widgets/bottom_widgets/cpu.rs b/src/app/widgets/bottom_widgets/cpu.rs index 8999f038..07310c54 100644 --- a/src/app/widgets/bottom_widgets/cpu.rs +++ b/src/app/widgets/bottom_widgets/cpu.rs @@ -28,19 +28,6 @@ pub struct CpuWidgetState { pub table_width_state: CanvasTableWidthState, } -impl CpuWidgetState { - pub fn init(current_display_time: u64, autohide_timer: Option) -> Self { - CpuWidgetState { - current_display_time, - is_legend_hidden: false, - autohide_timer, - scroll_state: AppScrollWidgetState::default(), - is_multi_graph_mode: false, - table_width_state: CanvasTableWidthState::default(), - } - } -} - #[derive(Default)] pub struct CpuState { pub force_update: Option, @@ -48,13 +35,6 @@ pub struct CpuState { } impl CpuState { - pub fn init(widget_states: HashMap) -> Self { - CpuState { - force_update: None, - widget_states, - } - } - pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut CpuWidgetState> { self.widget_states.get_mut(&widget_id) } @@ -64,6 +44,7 @@ impl CpuState { } } +/// Which part of the [`CpuGraph`] is currently selected. enum CpuGraphSelection { Graph, Legend, diff --git a/src/app/widgets/bottom_widgets/disk.rs b/src/app/widgets/bottom_widgets/disk.rs index b5decf38..e5f133be 100644 --- a/src/app/widgets/bottom_widgets/disk.rs +++ b/src/app/widgets/bottom_widgets/disk.rs @@ -24,15 +24,6 @@ pub struct DiskWidgetState { pub table_width_state: CanvasTableWidthState, } -impl DiskWidgetState { - pub fn init() -> Self { - DiskWidgetState { - scroll_state: AppScrollWidgetState::default(), - table_width_state: CanvasTableWidthState::default(), - } - } -} - #[derive(Default)] pub struct DiskState { pub widget_states: HashMap, diff --git a/src/app/widgets/bottom_widgets/mem.rs b/src/app/widgets/bottom_widgets/mem.rs index 3562790e..8b7b5444 100644 --- a/src/app/widgets/bottom_widgets/mem.rs +++ b/src/app/widgets/bottom_widgets/mem.rs @@ -19,38 +19,12 @@ pub struct MemWidgetState { pub autohide_timer: Option, } -impl MemWidgetState { - pub fn init(current_display_time: u64, autohide_timer: Option) -> Self { - MemWidgetState { - current_display_time, - autohide_timer, - } - } -} - #[derive(Default)] pub struct MemState { pub force_update: Option, pub widget_states: HashMap, } -impl MemState { - pub fn init(widget_states: HashMap) -> Self { - MemState { - force_update: None, - widget_states, - } - } - - pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut MemWidgetState> { - self.widget_states.get_mut(&widget_id) - } - - pub fn get_widget_state(&self, widget_id: u64) -> Option<&MemWidgetState> { - self.widget_states.get(&widget_id) - } -} - /// A widget that deals with displaying memory usage on a [`TimeGraph`]. Basically just a wrapper /// around [`TimeGraph`] as of now. pub struct MemGraph { diff --git a/src/app/widgets/bottom_widgets/net.rs b/src/app/widgets/bottom_widgets/net.rs index 00a93c8e..6876755c 100644 --- a/src/app/widgets/bottom_widgets/net.rs +++ b/src/app/widgets/bottom_widgets/net.rs @@ -22,31 +22,6 @@ use crate::{ pub struct NetWidgetState { pub current_display_time: u64, pub autohide_timer: Option, - // pub draw_max_range_cache: f64, - // pub draw_labels_cache: Vec, - // pub draw_time_start_cache: f64, - // TODO: Re-enable these when we move net details state-side! - // pub unit_type: DataUnitTypes, - // pub scale_type: AxisScaling, -} - -impl NetWidgetState { - pub fn init( - current_display_time: u64, - autohide_timer: Option, - // unit_type: DataUnitTypes, - // scale_type: AxisScaling, - ) -> Self { - NetWidgetState { - current_display_time, - autohide_timer, - // draw_max_range_cache: 0.0, - // draw_labels_cache: vec![], - // draw_time_start_cache: 0.0, - // unit_type, - // scale_type, - } - } } #[derive(Default)] @@ -55,23 +30,6 @@ pub struct NetState { pub widget_states: HashMap, } -impl NetState { - pub fn init(widget_states: HashMap) -> Self { - NetState { - force_update: None, - widget_states, - } - } - - pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut NetWidgetState> { - self.widget_states.get_mut(&widget_id) - } - - pub fn get_widget_state(&self, widget_id: u64) -> Option<&NetWidgetState> { - self.widget_states.get(&widget_id) - } -} - // --- NEW STUFF BELOW --- /// Returns the max data point and time given a time. diff --git a/src/app/widgets/bottom_widgets/process.rs b/src/app/widgets/bottom_widgets/process.rs index 6423ceb6..88223485 100644 --- a/src/app/widgets/bottom_widgets/process.rs +++ b/src/app/widgets/bottom_widgets/process.rs @@ -119,9 +119,6 @@ impl ProcessSearchState { pub struct ColumnInfo { pub enabled: bool, pub shortcut: Option<&'static str>, - // FIXME: Move column width logic here! - // pub hard_width: Option, - // pub max_soft_width: Option, } pub struct ProcColumn { @@ -419,59 +416,6 @@ pub struct ProcWidgetState { } impl ProcWidgetState { - pub fn init( - is_case_sensitive: bool, is_match_whole_word: bool, is_use_regex: bool, is_grouped: bool, - show_memory_as_values: bool, is_tree_mode: bool, is_using_command: bool, - ) -> Self { - let mut process_search_state = ProcessSearchState::default(); - - if is_case_sensitive { - // By default it's off - process_search_state.search_toggle_ignore_case(); - } - if is_match_whole_word { - process_search_state.search_toggle_whole_word(); - } - if is_use_regex { - process_search_state.search_toggle_regex(); - } - - let (process_sorting_type, is_process_sort_descending) = if is_tree_mode { - (processes::ProcessSorting::Pid, false) - } else { - (processes::ProcessSorting::CpuPercent, true) - }; - - // TODO: If we add customizable columns, this should pull from config - let mut columns = ProcColumn::default(); - columns.set_to_sorted_index_from_type(&process_sorting_type); - if is_grouped { - // Normally defaults to showing by PID, toggle count on instead. - columns.toggle(&ProcessSorting::Count); - columns.toggle(&ProcessSorting::Pid); - } - if show_memory_as_values { - // Normally defaults to showing by percent, toggle value on instead. - columns.toggle(&ProcessSorting::Mem); - columns.toggle(&ProcessSorting::MemPercent); - } - - ProcWidgetState { - process_search_state, - is_grouped, - scroll_state: AppScrollWidgetState::default(), - process_sorting_type, - is_process_sort_descending, - is_using_command, - current_column_index: 0, - is_sort_open: false, - columns, - is_tree_mode, - table_width_state: CanvasTableWidthState::default(), - requires_redraw: false, - } - } - /// Updates sorting when using the column list. /// ...this really should be part of the ProcColumn struct (along with the sorting fields), /// but I'm too lazy. @@ -620,14 +564,6 @@ pub struct ProcState { } impl ProcState { - pub fn init(widget_states: HashMap) -> Self { - ProcState { - widget_states, - force_update: None, - force_update_all: false, - } - } - pub fn get_mut_widget_state(&mut self, widget_id: u64) -> Option<&mut ProcWidgetState> { self.widget_states.get_mut(&widget_id) } diff --git a/src/canvas.rs b/src/canvas.rs index 7d621ac0..42c8faec 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -10,8 +10,6 @@ use tui::{ Frame, Terminal, }; -// use ordered_float::OrderedFloat; - use canvas_colours::*; use dialogs::*; @@ -33,8 +31,6 @@ use crate::{ mod canvas_colours; mod dialogs; -pub mod drawing; // TODO: Remove pub access at some point! -mod drawing_utils; /// Point is of time, data type Point = (f64, f64); @@ -191,7 +187,7 @@ impl Painter { self.styled_help_text = styled_help_spans.into_iter().map(Spans::from).collect(); } - // FIXME: [CONFIG] write this, should call painter init and any changed colour functions... + // TODO: [CONFIG] write this, should call painter init and any changed colour functions... pub fn update_painter_colours(&mut self) {} fn draw_frozen_indicator(&self, f: &mut Frame<'_, B>, draw_loc: Rect) { diff --git a/src/canvas/drawing.rs b/src/canvas/drawing.rs deleted file mode 100644 index ae786e74..00000000 --- a/src/canvas/drawing.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub mod basic_table_arrows; -pub mod battery_display; -pub mod cpu_basic; -pub mod mem_basic; -pub mod mem_graph; -pub mod network_basic; -pub mod network_graph; - -pub use basic_table_arrows::*; -pub use battery_display::*; -pub use cpu_basic::*; -pub use mem_basic::*; -pub use mem_graph::*; -pub use network_basic::*; -pub use network_graph::*; diff --git a/src/canvas/drawing/basic_table_arrows.rs b/src/canvas/drawing/basic_table_arrows.rs deleted file mode 100644 index 8e1f476e..00000000 --- a/src/canvas/drawing/basic_table_arrows.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::{ - app::{layout_manager::BottomWidgetType, AppState}, - canvas::Painter, -}; - -use tui::{ - backend::Backend, - layout::{Alignment, Constraint, Direction, Layout, Rect}, - terminal::Frame, - text::Span, - text::Spans, - widgets::{Block, Paragraph}, -}; - -pub fn draw_basic_table_arrows( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - if let Some(current_table) = app_state.widget_map.get(&widget_id) { - let current_table = if let BottomWidgetType::ProcSort = current_table.widget_type { - current_table - .right_neighbour - .map(|id| app_state.widget_map.get(&id).unwrap()) - .unwrap() - } else { - current_table - }; - - let (left_table, right_table) = ( - { - current_table - .left_neighbour - .map(|left_widget_id| { - app_state - .widget_map - .get(&left_widget_id) - .map(|left_widget| { - if left_widget.widget_type == BottomWidgetType::ProcSort { - left_widget - .left_neighbour - .map(|left_left_widget_id| { - app_state.widget_map.get(&left_left_widget_id).map( - |left_left_widget| &left_left_widget.widget_type, - ) - }) - .unwrap_or(Some(&BottomWidgetType::Temp)) - .unwrap_or(&BottomWidgetType::Temp) - } else { - &left_widget.widget_type - } - }) - .unwrap_or(&BottomWidgetType::Temp) - }) - .unwrap_or(&BottomWidgetType::Temp) - }, - { - current_table - .right_neighbour - .map(|right_widget_id| { - app_state - .widget_map - .get(&right_widget_id) - .map(|right_widget| { - if right_widget.widget_type == BottomWidgetType::ProcSort { - right_widget - .right_neighbour - .map(|right_right_widget_id| { - app_state.widget_map.get(&right_right_widget_id).map( - |right_right_widget| { - &right_right_widget.widget_type - }, - ) - }) - .unwrap_or(Some(&BottomWidgetType::Disk)) - .unwrap_or(&BottomWidgetType::Disk) - } else { - &right_widget.widget_type - } - }) - .unwrap_or(&BottomWidgetType::Disk) - }) - .unwrap_or(&BottomWidgetType::Disk) - }, - ); - - let left_name = left_table.get_pretty_name(); - let right_name = right_table.get_pretty_name(); - - let num_spaces = - usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len()); - - let left_arrow_text = vec![ - Spans::default(), - Spans::from(Span::styled( - format!("◄ {}", left_name), - painter.colours.text_style, - )), - ]; - - let right_arrow_text = vec![ - Spans::default(), - Spans::from(Span::styled( - format!("{} ►", right_name), - painter.colours.text_style, - )), - ]; - - let margined_draw_loc = Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Length(2 + left_name.len() as u16), - Constraint::Length(num_spaces as u16), - Constraint::Length(2 + right_name.len() as u16), - ]) - .horizontal_margin(1) - .split(draw_loc); - - f.render_widget( - Paragraph::new(left_arrow_text).block(Block::default()), - margined_draw_loc[0], - ); - f.render_widget( - Paragraph::new(right_arrow_text) - .block(Block::default()) - .alignment(Alignment::Right), - margined_draw_loc[2], - ); - - if app_state.should_get_widget_bounds() { - // Some explanations for future readers: - // - The "height" as of writing of this entire widget is 2. If it's 1, it occasionally doesn't draw. - // - As such, the buttons are only on the lower part of this 2-high widget. - // - So, we want to only check at one location, the `draw_loc.y + 1`, and that's it. - // - But why is it "+2" then? Well, it's because I have a REALLY ugly hack - // for mouse button checking, since most button checks are of the form `(draw_loc.y + draw_loc.height)`, - // and the same for the x and width. Unfortunately, if you check using >= and <=, the outer bound is - // actually too large - so, we assume all of them are one too big and check via < (see - // https://github.com/ClementTsang/bottom/pull/459 for details). - // - So in other words, to make it simple, we keep this to a standard and overshoot by one here. - if let Some(basic_table) = &mut app_state.basic_table_widget_state { - basic_table.left_tlc = Some((margined_draw_loc[0].x, margined_draw_loc[0].y + 1)); - basic_table.left_brc = Some(( - margined_draw_loc[0].x + margined_draw_loc[0].width, - margined_draw_loc[0].y + 2, - )); - basic_table.right_tlc = Some((margined_draw_loc[2].x, margined_draw_loc[2].y + 1)); - basic_table.right_brc = Some(( - margined_draw_loc[2].x + margined_draw_loc[2].width, - margined_draw_loc[2].y + 2, - )); - } - } - } -} diff --git a/src/canvas/drawing/battery_display.rs b/src/canvas/drawing/battery_display.rs deleted file mode 100644 index 73cbd213..00000000 --- a/src/canvas/drawing/battery_display.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::{ - app::AppState, - canvas::{drawing_utils::calculate_basic_use_bars, Painter}, - constants::*, -}; - -use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - terminal::Frame, - text::{Span, Spans}, - widgets::{Block, Borders, Cell, Paragraph, Row, Table, Tabs}, -}; -use unicode_segmentation::UnicodeSegmentation; - -pub fn draw_battery_display( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - draw_border: bool, widget_id: u64, -) { - let should_get_widget_bounds = app_state.should_get_widget_bounds(); - if let Some(battery_widget_state) = app_state.battery_state.widget_states.get_mut(&widget_id) { - let is_on_widget = widget_id == app_state.current_widget.widget_id; - let border_style = if is_on_widget { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }; - let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - 0 - } else { - app_state.app_config_fields.table_gap - }; - - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Battery ── Esc to go back "; - Spans::from(vec![ - Span::styled(" Battery ".to_string(), painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat(usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2 - )) - ), - border_style, - ), - ]) - } else { - Spans::from(Span::styled( - " Battery ".to_string(), - painter.colours.widget_title_style, - )) - }; - - let battery_block = if draw_border { - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(border_style) - } else if is_on_widget { - Block::default() - .borders(*SIDE_BORDERS) - .border_style(painter.colours.highlighted_border_style) - } else { - Block::default().borders(Borders::NONE) - }; - - let battery_names = app_state - .canvas_data - .battery_data - .iter() - .map(|battery| &battery.battery_name) - .collect::>(); - - let tab_draw_loc = Layout::default() - .constraints([ - Constraint::Length(1), - Constraint::Length(2), - Constraint::Min(0), - ]) - .direction(Direction::Vertical) - .split(draw_loc)[1]; - - f.render_widget( - Tabs::new( - battery_names - .iter() - .map(|name| Spans::from((*name).clone())) - .collect::>(), - ) - .block(Block::default()) - .divider(tui::symbols::line::VERTICAL) - .style(painter.colours.text_style) - .highlight_style(painter.colours.currently_selected_text_style) - .select(battery_widget_state.currently_selected_battery_index), - tab_draw_loc, - ); - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; - - if let Some(battery_details) = app_state - .canvas_data - .battery_data - .get(battery_widget_state.currently_selected_battery_index) - { - // Assuming a 50/50 split in width - let bar_length = usize::from((draw_loc.width.saturating_sub(2) / 2).saturating_sub(8)); - let charge_percentage = battery_details.charge_percentage; - let num_bars = calculate_basic_use_bars(charge_percentage, bar_length); - let bars = format!( - "[{}{}{:3.0}%]", - "|".repeat(num_bars), - " ".repeat(bar_length - num_bars), - charge_percentage, - ); - - let battery_rows = vec![ - Row::new(vec![ - Cell::from("Charge %").style(painter.colours.text_style), - Cell::from(bars).style(if charge_percentage < 10.0 { - painter.colours.low_battery_colour - } else if charge_percentage < 50.0 { - painter.colours.medium_battery_colour - } else { - painter.colours.high_battery_colour - }), - ]), - Row::new(vec!["Consumption", &battery_details.watt_consumption]) - .style(painter.colours.text_style), - // if let Some(duration_until_full) = &battery_details.duration_until_full { - // Row::new(vec!["Time to full", duration_until_full]) - // .style(painter.colours.text_style) - // } else if let Some(duration_until_empty) = &battery_details.duration_until_empty { - // Row::new(vec!["Time to empty", duration_until_empty]) - // .style(painter.colours.text_style) - // } else { - // Row::new(vec!["Time to full/empty", "N/A"]).style(painter.colours.text_style) - // }, - Row::new(vec!["Health %", &battery_details.health]) - .style(painter.colours.text_style), - ]; - - // Draw - f.render_widget( - Table::new(battery_rows) - .block(battery_block) - .header(Row::new(vec![""]).bottom_margin(table_gap)) - .widths(&[Constraint::Percentage(50), Constraint::Percentage(50)]), - margined_draw_loc, - ); - } else { - let mut contents = vec![Spans::default(); table_gap as usize]; - - contents.push(Spans::from(Span::styled( - "No data found for this battery", - painter.colours.text_style, - ))); - - f.render_widget( - Paragraph::new(contents).block(battery_block), - margined_draw_loc, - ); - } - - if should_get_widget_bounds { - // Tab wizardry - if !battery_names.is_empty() { - let mut current_x = tab_draw_loc.x; - let current_y = tab_draw_loc.y; - let mut tab_click_locs: Vec<((u16, u16), (u16, u16))> = vec![]; - for battery in battery_names { - // +1 because there's a space after the tab label. - let width = unicode_width::UnicodeWidthStr::width(battery.as_str()) as u16; - tab_click_locs.push(((current_x, current_y), (current_x + width, current_y))); - - // +4 because we want to go one space, then one space past to get to the '|', then 2 more - // to start at the blank space before the tab label. - current_x += width + 4; - } - battery_widget_state.tab_click_locs = Some(tab_click_locs); - } - - // Update draw loc in widget map - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y)); - widget.bottom_right_corner = Some(( - margined_draw_loc.x + margined_draw_loc.width, - margined_draw_loc.y + margined_draw_loc.height, - )); - } - } - } -} diff --git a/src/canvas/drawing/cpu_basic.rs b/src/canvas/drawing/cpu_basic.rs deleted file mode 100644 index a9dbc4b5..00000000 --- a/src/canvas/drawing/cpu_basic.rs +++ /dev/null @@ -1,199 +0,0 @@ -use std::cmp::min; - -use crate::{ - app::AppState, - canvas::{drawing_utils::*, Painter}, - constants::*, - data_conversion::ConvertedCpuData, -}; - -use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - terminal::Frame, - text::{Span, Spans}, - widgets::{Block, Paragraph}, -}; - -pub fn draw_basic_cpu( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - // Skip the first element, it's the "all" element - if app_state.canvas_data.cpu_data.len() > 1 { - let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data[1..]; - - // This is a bit complicated, but basically, we want to draw SOME number - // of columns to draw all CPUs. Ideally, as well, we want to not have - // to ever scroll. - // **General logic** - count number of elements in cpu_data. Then see how - // many rows and columns we have in draw_loc (-2 on both sides for border?). - // I think what we can do is try to fit in as many in one column as possible. - // If not, then add a new column. - // Then, from this, split the row space across ALL columns. From there, generate - // the desired lengths. - - if app_state.current_widget.widget_id == widget_id { - f.render_widget( - Block::default() - .borders(*SIDE_BORDERS) - .border_style(painter.colours.highlighted_border_style), - draw_loc, - ); - } - - let num_cpus = cpu_data.len(); - let show_avg_cpu = app_state.app_config_fields.show_average_cpu; - - if draw_loc.height > 0 { - let remaining_height = usize::from(draw_loc.height); - const REQUIRED_COLUMNS: usize = 4; - - let chunk_vec = - vec![Constraint::Percentage((100 / REQUIRED_COLUMNS) as u16); REQUIRED_COLUMNS]; - let chunks = Layout::default() - .constraints(chunk_vec) - .direction(Direction::Horizontal) - .split(draw_loc); - - const CPU_NAME_SPACE: usize = 3; - const BAR_BOUND_SPACE: usize = 2; - const PERCENTAGE_SPACE: usize = 4; - const MARGIN_SPACE: usize = 2; - - const COMBINED_SPACING: usize = - CPU_NAME_SPACE + BAR_BOUND_SPACE + PERCENTAGE_SPACE + MARGIN_SPACE; - const REDUCED_SPACING: usize = CPU_NAME_SPACE + PERCENTAGE_SPACE + MARGIN_SPACE; - let chunk_width = chunks[0].width as usize; - - // Inspired by htop. - // We do +4 as if it's too few bars in the bar length, it's kinda pointless. - let cpu_bars = if chunk_width >= COMBINED_SPACING + 4 { - let bar_length = chunk_width - COMBINED_SPACING; - (0..num_cpus) - .map(|cpu_index| { - let use_percentage = - if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() { - cpu_usage.1 - } else { - 0.0 - }; - - let num_bars = calculate_basic_use_bars(use_percentage, bar_length); - format!( - "{:3}[{}{}{:3.0}%]", - if app_state.app_config_fields.show_average_cpu { - if cpu_index == 0 { - "AVG".to_string() - } else { - (cpu_index - 1).to_string() - } - } else { - cpu_index.to_string() - }, - "|".repeat(num_bars), - " ".repeat(bar_length - num_bars), - use_percentage.round(), - ) - }) - .collect::>() - } else if chunk_width >= REDUCED_SPACING { - (0..num_cpus) - .map(|cpu_index| { - let use_percentage = - if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() { - cpu_usage.1 - } else { - 0.0 - }; - - format!( - "{:3} {:3.0}%", - if app_state.app_config_fields.show_average_cpu { - if cpu_index == 0 { - "AVG".to_string() - } else { - (cpu_index - 1).to_string() - } - } else { - cpu_index.to_string() - }, - use_percentage.round(), - ) - }) - .collect::>() - } else { - (0..num_cpus) - .map(|cpu_index| { - let use_percentage = - if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() { - cpu_usage.1 - } else { - 0.0 - }; - - format!("{:3.0}%", use_percentage.round(),) - }) - .collect::>() - }; - - let mut row_counter = num_cpus; - let mut start_index = 0; - for (itx, chunk) in chunks.iter().enumerate() { - // Explicitly check... don't want an accidental DBZ or underflow, this ensures - // to_divide is > 0 - if REQUIRED_COLUMNS > itx { - let to_divide = REQUIRED_COLUMNS - itx; - let how_many_cpus = min( - remaining_height, - (row_counter / to_divide) - + (if row_counter % to_divide == 0 { 0 } else { 1 }), - ); - row_counter -= how_many_cpus; - let end_index = min(start_index + how_many_cpus, num_cpus); - - let cpu_column = (start_index..end_index) - .map(|itx| { - Spans::from(Span { - content: (&cpu_bars[itx]).into(), - style: if show_avg_cpu { - if itx == 0 { - painter.colours.avg_colour_style - } else { - painter.colours.cpu_colour_styles - [(itx - 1) % painter.colours.cpu_colour_styles.len()] - } - } else { - painter.colours.cpu_colour_styles - [itx % painter.colours.cpu_colour_styles.len()] - }, - }) - }) - .collect::>(); - - start_index += how_many_cpus; - - let margined_loc = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(1) - .split(*chunk)[0]; - - f.render_widget( - Paragraph::new(cpu_column).block(Block::default()), - margined_loc, - ); - } - } - } - } - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((draw_loc.x, draw_loc.y)); - widget.bottom_right_corner = - Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height)); - } - } -} diff --git a/src/canvas/drawing/cpu_graph.rs b/src/canvas/drawing/cpu_graph.rs deleted file mode 100644 index 8bfb547c..00000000 --- a/src/canvas/drawing/cpu_graph.rs +++ /dev/null @@ -1,527 +0,0 @@ -use once_cell::sync::Lazy; -use unicode_segmentation::UnicodeSegmentation; - -use crate::{ - app::{layout_manager::WidgetDirection, AppState}, - canvas::{ - drawing_utils::{get_column_widths, get_start_position, interpolate_points}, - Painter, - }, - constants::*, - data_conversion::ConvertedCpuData, -}; - -use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - symbols::Marker, - terminal::Frame, - text::Span, - text::{Spans, Text}, - widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table}, -}; - -const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"]; -const AVG_POSITION: usize = 1; -const ALL_POSITION: usize = 0; - -static CPU_LEGEND_HEADER_LENS: Lazy> = Lazy::new(|| { - CPU_LEGEND_HEADER - .iter() - .map(|entry| entry.len() as u16) - .collect::>() -}); - -pub fn draw_cpu( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - if draw_loc.width as f64 * 0.15 <= 6.0 { - // Skip drawing legend - if app_state.current_widget.widget_id == (widget_id + 1) { - if app_state.app_config_fields.left_legend { - app_state.move_widget_selection(&WidgetDirection::Right); - } else { - app_state.move_widget_selection(&WidgetDirection::Left); - } - } - draw_cpu_graph(painter, f, app_state, draw_loc, widget_id); - if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) { - cpu_widget_state.is_legend_hidden = true; - } - - // Update draw loc in widget map - if app_state.should_get_widget_bounds() { - if let Some(bottom_widget) = app_state.widget_map.get_mut(&widget_id) { - bottom_widget.top_left_corner = Some((draw_loc.x, draw_loc.y)); - bottom_widget.bottom_right_corner = - Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height)); - } - } - } else { - let (graph_index, legend_index, constraints) = if app_state.app_config_fields.left_legend { - ( - 1, - 0, - [Constraint::Percentage(15), Constraint::Percentage(85)], - ) - } else { - ( - 0, - 1, - [Constraint::Percentage(85), Constraint::Percentage(15)], - ) - }; - - let partitioned_draw_loc = Layout::default() - .margin(0) - .direction(Direction::Horizontal) - .constraints(constraints) - .split(draw_loc); - - draw_cpu_graph( - painter, - f, - app_state, - partitioned_draw_loc[graph_index], - widget_id, - ); - draw_cpu_legend( - painter, - f, - app_state, - partitioned_draw_loc[legend_index], - widget_id + 1, - ); - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - if let Some(cpu_widget) = app_state.widget_map.get_mut(&widget_id) { - cpu_widget.top_left_corner = Some(( - partitioned_draw_loc[graph_index].x, - partitioned_draw_loc[graph_index].y, - )); - cpu_widget.bottom_right_corner = Some(( - partitioned_draw_loc[graph_index].x + partitioned_draw_loc[graph_index].width, - partitioned_draw_loc[graph_index].y + partitioned_draw_loc[graph_index].height, - )); - } - - if let Some(legend_widget) = app_state.widget_map.get_mut(&(widget_id + 1)) { - legend_widget.top_left_corner = Some(( - partitioned_draw_loc[legend_index].x, - partitioned_draw_loc[legend_index].y, - )); - legend_widget.bottom_right_corner = Some(( - partitioned_draw_loc[legend_index].x + partitioned_draw_loc[legend_index].width, - partitioned_draw_loc[legend_index].y - + partitioned_draw_loc[legend_index].height, - )); - } - } - } -} - -fn draw_cpu_graph( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) { - let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data; - - let display_time_labels = vec![ - Span::styled( - format!("{}s", cpu_widget_state.current_display_time / 1000), - painter.colours.graph_style, - ), - Span::styled("0s".to_string(), painter.colours.graph_style), - ]; - - let y_axis_labels = vec![ - Span::styled(" 0%", painter.colours.graph_style), - Span::styled("100%", painter.colours.graph_style), - ]; - - let time_start = -(cpu_widget_state.current_display_time as f64); - - let x_axis = if app_state.app_config_fields.hide_time - || (app_state.app_config_fields.autohide_time - && cpu_widget_state.autohide_timer.is_none()) - { - Axis::default().bounds([time_start, 0.0]) - } else if let Some(time) = cpu_widget_state.autohide_timer { - if std::time::Instant::now().duration_since(time).as_millis() - < AUTOHIDE_TIMEOUT_MILLISECONDS as u128 - { - Axis::default() - .bounds([time_start, 0.0]) - .style(painter.colours.graph_style) - .labels(display_time_labels) - } else { - cpu_widget_state.autohide_timer = None; - Axis::default().bounds([time_start, 0.0]) - } - } else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT { - Axis::default().bounds([time_start, 0.0]) - } else { - Axis::default() - .bounds([time_start, 0.0]) - .style(painter.colours.graph_style) - .labels(display_time_labels) - }; - - let y_axis = Axis::default() - .style(painter.colours.graph_style) - .bounds([0.0, 100.5]) - .labels(y_axis_labels); - - let use_dot = app_state.app_config_fields.use_dot; - let show_avg_cpu = app_state.app_config_fields.show_average_cpu; - let current_scroll_position = cpu_widget_state.scroll_state.current_scroll_position; - - let interpolated_cpu_points = cpu_data - .iter_mut() - .enumerate() - .map(|(itx, cpu)| { - let to_show = if current_scroll_position == ALL_POSITION { - true - } else { - itx == current_scroll_position - }; - - if to_show { - if let Some(end_pos) = cpu - .cpu_data - .iter() - .position(|(time, _data)| *time >= time_start) - { - if end_pos > 1 { - let start_pos = end_pos - 1; - let outside_point = cpu.cpu_data.get(start_pos); - let inside_point = cpu.cpu_data.get(end_pos); - - if let (Some(outside_point), Some(inside_point)) = - (outside_point, inside_point) - { - let old = *outside_point; - - let new_point = ( - time_start, - interpolate_points(outside_point, inside_point, time_start), - ); - - if let Some(to_replace) = cpu.cpu_data.get_mut(start_pos) { - *to_replace = new_point; - Some((start_pos, old)) - } else { - None // Failed to get mutable reference. - } - } else { - None // Point somehow doesn't exist in our data - } - } else { - None // Point is already "leftmost", no need to interpolate. - } - } else { - None // There is no point. - } - } else { - None - } - }) - .collect::>(); - - let dataset_vector: Vec> = if current_scroll_position == ALL_POSITION { - cpu_data - .iter() - .enumerate() - .rev() - .map(|(itx, cpu)| { - Dataset::default() - .marker(if use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(if show_avg_cpu && itx == AVG_POSITION { - painter.colours.avg_colour_style - } else if itx == ALL_POSITION { - painter.colours.all_colour_style - } else { - painter.colours.cpu_colour_styles[(itx - 1 // Because of the all position - - (if show_avg_cpu { - AVG_POSITION - } else { - 0 - })) - % painter.colours.cpu_colour_styles.len()] - }) - .data(&cpu.cpu_data[..]) - .graph_type(tui::widgets::GraphType::Line) - }) - .collect() - } else if let Some(cpu) = cpu_data.get(current_scroll_position) { - vec![Dataset::default() - .marker(if use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(if show_avg_cpu && current_scroll_position == AVG_POSITION { - painter.colours.avg_colour_style - } else { - painter.colours.cpu_colour_styles[(cpu_widget_state - .scroll_state - .current_scroll_position - - 1 // Because of the all position - - (if show_avg_cpu { - AVG_POSITION - } else { - 0 - })) - % painter.colours.cpu_colour_styles.len()] - }) - .data(&cpu.cpu_data[..]) - .graph_type(tui::widgets::GraphType::Line)] - } else { - vec![] - }; - - let is_on_widget = widget_id == app_state.current_widget.widget_id; - let border_style = if is_on_widget { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }; - - let title = if cfg!(target_family = "unix") { - let load_avg = app_state.canvas_data.load_avg_data; - let load_avg_str = format!( - "─ {:.2} {:.2} {:.2} ", - load_avg[0], load_avg[1], load_avg[2] - ); - let load_avg_str_size = - UnicodeSegmentation::graphemes(load_avg_str.as_str(), true).count(); - - if app_state.is_expanded { - const TITLE_BASE: &str = " CPU ── Esc to go back "; - - Spans::from(vec![ - Span::styled(" CPU ", painter.colours.widget_title_style), - Span::styled(load_avg_str, painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat(usize::from(draw_loc.width).saturating_sub( - load_avg_str_size - + UnicodeSegmentation::graphemes(TITLE_BASE, true).count() - + 2 - )) - ), - border_style, - ), - ]) - } else { - Spans::from(vec![ - Span::styled(" CPU ", painter.colours.widget_title_style), - Span::styled(load_avg_str, painter.colours.widget_title_style), - ]) - } - } else if app_state.is_expanded { - const TITLE_BASE: &str = " CPU ── Esc to go back "; - - Spans::from(vec![ - Span::styled(" CPU ", painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat(usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2 - )) - ), - border_style, - ), - ]) - } else { - Spans::from(vec![Span::styled( - " CPU ", - painter.colours.widget_title_style, - )]) - }; - - f.render_widget( - Chart::new(dataset_vector) - .block( - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(border_style), - ) - .x_axis(x_axis) - .y_axis(y_axis), - draw_loc, - ); - - // Reset interpolated points - cpu_data - .iter_mut() - .zip(interpolated_cpu_points) - .for_each(|(cpu, interpolation)| { - if let Some((index, old_value)) = interpolation { - if let Some(to_replace) = cpu.cpu_data.get_mut(index) { - *to_replace = old_value; - } - } - }); - } -} - -fn draw_cpu_legend( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - // let recalculate_column_widths = app_state.should_get_widget_bounds(); - // if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&(widget_id - 1)) { - // cpu_widget_state.is_legend_hidden = false; - // let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data; - // let cpu_table_state = &mut cpu_widget_state.scroll_state.table_state; - // let is_on_widget = widget_id == app_state.current_widget.widget_id; - // let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - // 0 - // } else { - // app_state.app_config_fields.table_gap - // }; - // let start_position = get_start_position( - // usize::from( - // (draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset), - // ), - // &cpu_widget_state.scroll_state.scroll_direction, - // &mut cpu_widget_state.scroll_state.previous_scroll_position, - // cpu_widget_state.scroll_state.current_scroll_position, - // app_state.is_force_redraw, - // ); - // cpu_table_state.select(Some( - // cpu_widget_state - // .scroll_state - // .current_scroll_position - // .saturating_sub(start_position), - // )); - - // let sliced_cpu_data = &cpu_data[start_position..]; - - // let offset_scroll_index = cpu_widget_state - // .scroll_state - // .current_scroll_position - // .saturating_sub(start_position); - // let show_avg_cpu = app_state.app_config_fields.show_average_cpu; - - // // Calculate widths - // if recalculate_column_widths { - // cpu_widget_state.table_width_state.desired_column_widths = vec![6, 4]; - // cpu_widget_state.table_width_state.calculated_column_widths = get_column_widths( - // draw_loc.width, - // &[None, None], - // &(CPU_LEGEND_HEADER_LENS - // .iter() - // .map(|width| Some(*width)) - // .collect::>()), - // &[Some(0.5), Some(0.5)], - // &(cpu_widget_state - // .table_width_state - // .desired_column_widths - // .iter() - // .map(|width| Some(*width)) - // .collect::>()), - // false, - // ); - // } - - // let dcw = &cpu_widget_state.table_width_state.desired_column_widths; - // let ccw = &cpu_widget_state.table_width_state.calculated_column_widths; - // let cpu_rows = sliced_cpu_data.iter().enumerate().map(|(itx, cpu)| { - // let mut truncated_name = - // if let (Some(desired_column_width), Some(calculated_column_width)) = - // (dcw.get(0), ccw.get(0)) - // { - // if *desired_column_width > *calculated_column_width { - // Text::raw(&cpu.short_cpu_name) - // } else { - // Text::raw(&cpu.cpu_name) - // } - // } else { - // Text::raw(&cpu.cpu_name) - // }; - - // let is_first_column_hidden = if let Some(calculated_column_width) = ccw.get(0) { - // *calculated_column_width == 0 - // } else { - // false - // }; - - // let truncated_legend = if is_first_column_hidden && cpu.legend_value.is_empty() { - // // For the case where we only have room for one column, display "All" in the normally blank area. - // Text::raw("All") - // } else { - // Text::raw(&cpu.legend_value) - // }; - - // if !is_first_column_hidden - // && itx == offset_scroll_index - // && itx + start_position == ALL_POSITION - // { - // truncated_name.patch_style(painter.colours.currently_selected_text_style); - // Row::new(vec![truncated_name, truncated_legend]) - // } else { - // let cpu_string_row = vec![truncated_name, truncated_legend]; - - // Row::new(cpu_string_row).style(if itx == offset_scroll_index { - // painter.colours.currently_selected_text_style - // } else if itx + start_position == ALL_POSITION { - // painter.colours.all_colour_style - // } else if show_avg_cpu { - // if itx + start_position == AVG_POSITION { - // painter.colours.avg_colour_style - // } else { - // painter.colours.cpu_colour_styles[(itx + start_position - AVG_POSITION - 1) - // % painter.colours.cpu_colour_styles.len()] - // } - // } else { - // painter.colours.cpu_colour_styles[(itx + start_position - ALL_POSITION - 1) - // % painter.colours.cpu_colour_styles.len()] - // }) - // } - // }); - - // // Note we don't set highlight_style, as it should always be shown for this widget. - // let border_and_title_style = if is_on_widget { - // painter.colours.highlighted_border_style - // } else { - // painter.colours.border_style - // }; - - // // Draw - // f.render_stateful_widget( - // Table::new(cpu_rows) - // .block( - // Block::default() - // .borders(Borders::ALL) - // .border_style(border_and_title_style), - // ) - // .header( - // Row::new(CPU_LEGEND_HEADER.to_vec()) - // .style(painter.colours.table_header_style) - // .bottom_margin(table_gap), - // ) - // .widths( - // &(cpu_widget_state - // .table_width_state - // .calculated_column_widths - // .iter() - // .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - // .collect::>()), - // ), - // draw_loc, - // cpu_table_state, - // ); - // } -} diff --git a/src/canvas/drawing/disk_table.rs b/src/canvas/drawing/disk_table.rs deleted file mode 100644 index 3404cb99..00000000 --- a/src/canvas/drawing/disk_table.rs +++ /dev/null @@ -1,266 +0,0 @@ -use once_cell::sync::Lazy; -use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - terminal::Frame, - text::Span, - text::{Spans, Text}, - widgets::{Block, Borders, Row, Table}, -}; - -use crate::{ - app, - canvas::{ - drawing_utils::{get_column_widths, get_start_position}, - Painter, - }, - constants::*, -}; -use unicode_segmentation::UnicodeSegmentation; - -const DISK_HEADERS: [&str; 7] = ["Disk", "Mount", "Used", "Free", "Total", "R/s", "W/s"]; - -static DISK_HEADERS_LENS: Lazy> = Lazy::new(|| { - DISK_HEADERS - .iter() - .map(|entry| entry.len() as u16) - .collect::>() -}); - -pub fn draw_disk_table( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect, - draw_border: bool, widget_id: u64, -) { - let recalculate_column_widths = app_state.should_get_widget_bounds(); - if let Some(disk_widget_state) = app_state.disk_state.widget_states.get_mut(&widget_id) { - let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - 0 - } else { - app_state.app_config_fields.table_gap - }; - let start_position = get_start_position( - usize::from( - (draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset), - ), - &disk_widget_state.scroll_state.scroll_direction, - &mut disk_widget_state.scroll_state.previous_scroll_position, - disk_widget_state.scroll_state.current_scroll_position, - app_state.is_force_redraw, - ); - let is_on_widget = app_state.current_widget.widget_id == widget_id; - let disk_table_state = &mut disk_widget_state.scroll_state.table_state; - disk_table_state.select(Some( - disk_widget_state - .scroll_state - .current_scroll_position - .saturating_sub(start_position), - )); - let sliced_vec = &app_state.canvas_data.disk_data[start_position..]; - - // Calculate widths - let hard_widths = [None, None, Some(4), Some(6), Some(6), Some(7), Some(7)]; - if recalculate_column_widths { - disk_widget_state.table_width_state.desired_column_widths = { - let mut column_widths = DISK_HEADERS_LENS.clone(); - for row in sliced_vec { - for (col, entry) in row.iter().enumerate() { - if entry.len() as u16 > column_widths[col] { - column_widths[col] = entry.len() as u16; - } - } - } - column_widths - }; - disk_widget_state.table_width_state.desired_column_widths = disk_widget_state - .table_width_state - .desired_column_widths - .iter() - .zip(&hard_widths) - .map(|(current, hard)| { - if let Some(hard) = hard { - if *hard > *current { - *hard - } else { - *current - } - } else { - *current - } - }) - .collect::>(); - - disk_widget_state.table_width_state.calculated_column_widths = get_column_widths( - draw_loc.width, - &hard_widths, - &(DISK_HEADERS_LENS - .iter() - .map(|w| Some(*w)) - .collect::>()), - &[Some(0.2), Some(0.2), None, None, None, None, None], - &(disk_widget_state - .table_width_state - .desired_column_widths - .iter() - .map(|w| Some(*w)) - .collect::>()), - true, - ); - } - - let dcw = &disk_widget_state.table_width_state.desired_column_widths; - let ccw = &disk_widget_state.table_width_state.calculated_column_widths; - let disk_rows = sliced_vec.iter().map(|disk_row| { - let truncated_data = - disk_row - .iter() - .zip(&hard_widths) - .enumerate() - .map(|(itx, (entry, width))| { - if width.is_none() { - if let (Some(desired_col_width), Some(calculated_col_width)) = - (dcw.get(itx), ccw.get(itx)) - { - if *desired_col_width > *calculated_col_width - && *calculated_col_width > 0 - { - let graphemes = - UnicodeSegmentation::graphemes(entry.as_str(), true) - .collect::>(); - - if graphemes.len() > *calculated_col_width as usize - && *calculated_col_width > 1 - { - // Truncate with ellipsis - let first_n = graphemes - [..(*calculated_col_width as usize - 1)] - .concat(); - return Text::raw(format!("{}…", first_n)); - } - } - } - } - - Text::raw(entry) - }); - - Row::new(truncated_data) - }); - - let (border_style, highlight_style) = if is_on_widget { - ( - painter.colours.highlighted_border_style, - painter.colours.currently_selected_text_style, - ) - } else { - (painter.colours.border_style, painter.colours.text_style) - }; - - let title_base = if app_state.app_config_fields.show_table_scroll_position { - let title_string = format!( - " Disk ({} of {}) ", - disk_widget_state - .scroll_state - .current_scroll_position - .saturating_add(1), - app_state.canvas_data.disk_data.len() - ); - - if title_string.len() <= draw_loc.width as usize { - title_string - } else { - " Disk ".to_string() - } - } else { - " Disk ".to_string() - }; - - let title = if app_state.is_expanded { - const ESCAPE_ENDING: &str = "── Esc to go back "; - - let (chosen_title_base, expanded_title_base) = { - let temp_title_base = format!("{}{}", title_base, ESCAPE_ENDING); - - if temp_title_base.len() > draw_loc.width as usize { - ( - " Disk ".to_string(), - format!("{}{}", " Disk ".to_string(), ESCAPE_ENDING), - ) - } else { - (title_base, temp_title_base) - } - }; - - Spans::from(vec![ - Span::styled(chosen_title_base, painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(expanded_title_base.as_str(), true) - .count() - + 2 - ) - ) - ), - border_style, - ), - ]) - } else { - Spans::from(Span::styled(title_base, painter.colours.widget_title_style)) - }; - - let disk_block = if draw_border { - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(border_style) - } else if is_on_widget { - Block::default() - .borders(*SIDE_BORDERS) - .border_style(painter.colours.highlighted_border_style) - } else { - Block::default().borders(Borders::NONE) - }; - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; - - // Draw! - f.render_stateful_widget( - Table::new(disk_rows) - .block(disk_block) - .header( - Row::new(DISK_HEADERS.to_vec()) - .style(painter.colours.table_header_style) - .bottom_margin(table_gap), - ) - .highlight_style(highlight_style) - .style(painter.colours.text_style) - .widths( - &(disk_widget_state - .table_width_state - .calculated_column_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::>()), - ), - margined_draw_loc, - disk_table_state, - ); - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y)); - widget.bottom_right_corner = Some(( - margined_draw_loc.x + margined_draw_loc.width, - margined_draw_loc.y + margined_draw_loc.height, - )); - } - } - } -} diff --git a/src/canvas/drawing/mem_basic.rs b/src/canvas/drawing/mem_basic.rs deleted file mode 100644 index e8a0d0f6..00000000 --- a/src/canvas/drawing/mem_basic.rs +++ /dev/null @@ -1,123 +0,0 @@ -use crate::{ - app::AppState, - canvas::{drawing_utils::*, Painter}, - constants::*, -}; - -use tui::{ - backend::Backend, - layout::{Constraint, Layout, Rect}, - terminal::Frame, - text::Span, - text::Spans, - widgets::{Block, Paragraph}, -}; - -pub fn draw_basic_memory( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data; - let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data; - - let margined_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(1) - .split(draw_loc); - - if app_state.current_widget.widget_id == widget_id { - f.render_widget( - Block::default() - .borders(*SIDE_BORDERS) - .border_style(painter.colours.highlighted_border_style), - draw_loc, - ); - } - - let ram_use_percentage = if let Some(mem) = mem_data.last() { - mem.1 - } else { - 0.0 - }; - let swap_use_percentage = if let Some(swap) = swap_data.last() { - swap.1 - } else { - 0.0 - }; - - const EMPTY_MEMORY_FRAC_STRING: &str = "0.0B/0.0B"; - - let trimmed_memory_frac = - if let Some((_label_percent, label_frac)) = &app_state.canvas_data.mem_labels { - label_frac.trim() - } else { - EMPTY_MEMORY_FRAC_STRING - }; - - let trimmed_swap_frac = - if let Some((_label_percent, label_frac)) = &app_state.canvas_data.swap_labels { - label_frac.trim() - } else { - EMPTY_MEMORY_FRAC_STRING - }; - - // +7 due to 3 + 2 + 2 columns for the name & space + bar bounds + margin spacing - // Then + length of fraction - let ram_bar_length = - usize::from(draw_loc.width.saturating_sub(7)).saturating_sub(trimmed_memory_frac.len()); - let swap_bar_length = - usize::from(draw_loc.width.saturating_sub(7)).saturating_sub(trimmed_swap_frac.len()); - - let num_bars_ram = calculate_basic_use_bars(ram_use_percentage, ram_bar_length); - let num_bars_swap = calculate_basic_use_bars(swap_use_percentage, swap_bar_length); - // TODO: Use different styling for the frac. - let mem_label = if app_state.basic_mode_use_percent { - format!( - "RAM[{}{}{:3.0}%]\n", - "|".repeat(num_bars_ram), - " ".repeat(ram_bar_length - num_bars_ram + trimmed_memory_frac.len() - 4), - ram_use_percentage.round() - ) - } else { - format!( - "RAM[{}{}{}]\n", - "|".repeat(num_bars_ram), - " ".repeat(ram_bar_length - num_bars_ram), - trimmed_memory_frac - ) - }; - let swap_label = if app_state.basic_mode_use_percent { - format!( - "SWP[{}{}{:3.0}%]", - "|".repeat(num_bars_swap), - " ".repeat(swap_bar_length - num_bars_swap + trimmed_swap_frac.len() - 4), - swap_use_percentage.round() - ) - } else { - format!( - "SWP[{}{}{}]", - "|".repeat(num_bars_swap), - " ".repeat(swap_bar_length - num_bars_swap), - trimmed_swap_frac - ) - }; - - let mem_text = vec![ - Spans::from(Span::styled(mem_label, painter.colours.ram_style)), - Spans::from(Span::styled(swap_label, painter.colours.swap_style)), - ]; - - f.render_widget( - Paragraph::new(mem_text).block(Block::default()), - margined_loc[0], - ); - - // Update draw loc in widget map - if app_state.should_get_widget_bounds() { - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((draw_loc.x, draw_loc.y)); - widget.bottom_right_corner = - Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height)); - } - } -} diff --git a/src/canvas/drawing/mem_graph.rs b/src/canvas/drawing/mem_graph.rs deleted file mode 100644 index 60ad989d..00000000 --- a/src/canvas/drawing/mem_graph.rs +++ /dev/null @@ -1,240 +0,0 @@ -use crate::{ - app::AppState, - canvas::{drawing_utils::interpolate_points, Painter}, - constants::*, -}; - -use tui::{ - backend::Backend, - layout::{Constraint, Rect}, - symbols::Marker, - terminal::Frame, - text::Span, - text::Spans, - widgets::{Axis, Block, Borders, Chart, Dataset}, -}; -use unicode_segmentation::UnicodeSegmentation; - -pub fn draw_memory_graph( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - if let Some(mem_widget_state) = app_state.mem_state.widget_states.get_mut(&widget_id) { - let mem_data: &mut [(f64, f64)] = &mut app_state.canvas_data.mem_data; - let swap_data: &mut [(f64, f64)] = &mut app_state.canvas_data.swap_data; - - let time_start = -(mem_widget_state.current_display_time as f64); - - let display_time_labels = vec![ - Span::styled( - format!("{}s", mem_widget_state.current_display_time / 1000), - painter.colours.graph_style, - ), - Span::styled("0s".to_string(), painter.colours.graph_style), - ]; - let y_axis_label = vec![ - Span::styled(" 0%", painter.colours.graph_style), - Span::styled("100%", painter.colours.graph_style), - ]; - - let x_axis = if app_state.app_config_fields.hide_time - || (app_state.app_config_fields.autohide_time - && mem_widget_state.autohide_timer.is_none()) - { - Axis::default().bounds([time_start, 0.0]) - } else if let Some(time) = mem_widget_state.autohide_timer { - if std::time::Instant::now().duration_since(time).as_millis() - < AUTOHIDE_TIMEOUT_MILLISECONDS as u128 - { - Axis::default() - .bounds([time_start, 0.0]) - .style(painter.colours.graph_style) - .labels(display_time_labels) - } else { - mem_widget_state.autohide_timer = None; - Axis::default().bounds([time_start, 0.0]) - } - } else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT { - Axis::default().bounds([time_start, 0.0]) - } else { - Axis::default() - .bounds([time_start, 0.0]) - .style(painter.colours.graph_style) - .labels(display_time_labels) - }; - - let y_axis = Axis::default() - .style(painter.colours.graph_style) - .bounds([0.0, 100.5]) - .labels(y_axis_label); - - // Interpolate values to avoid ugly gaps - let interpolated_mem_point = if let Some(end_pos) = mem_data - .iter() - .position(|(time, _data)| *time >= time_start) - { - if end_pos > 1 { - let start_pos = end_pos - 1; - let outside_point = mem_data.get(start_pos); - let inside_point = mem_data.get(end_pos); - - if let (Some(outside_point), Some(inside_point)) = (outside_point, inside_point) { - let old = *outside_point; - - let new_point = ( - time_start, - interpolate_points(outside_point, inside_point, time_start), - ); - - if let Some(to_replace) = mem_data.get_mut(start_pos) { - *to_replace = new_point; - Some((start_pos, old)) - } else { - None // Failed to get mutable reference. - } - } else { - None // Point somehow doesn't exist in our data - } - } else { - None // Point is already "leftmost", no need to interpolate. - } - } else { - None // There is no point. - }; - - let interpolated_swap_point = if let Some(end_pos) = swap_data - .iter() - .position(|(time, _data)| *time >= time_start) - { - if end_pos > 1 { - let start_pos = end_pos - 1; - let outside_point = swap_data.get(start_pos); - let inside_point = swap_data.get(end_pos); - - if let (Some(outside_point), Some(inside_point)) = (outside_point, inside_point) { - let old = *outside_point; - - let new_point = ( - time_start, - interpolate_points(outside_point, inside_point, time_start), - ); - - if let Some(to_replace) = swap_data.get_mut(start_pos) { - *to_replace = new_point; - Some((start_pos, old)) - } else { - None // Failed to get mutable reference. - } - } else { - None // Point somehow doesn't exist in our data - } - } else { - None // Point is already "leftmost", no need to interpolate. - } - } else { - None // There is no point. - }; - - let mut mem_canvas_vec: Vec> = vec![]; - - if let Some((label_percent, label_frac)) = &app_state.canvas_data.mem_labels { - let mem_label = format!("RAM:{}{}", label_percent, label_frac); - mem_canvas_vec.push( - Dataset::default() - .name(mem_label) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(painter.colours.ram_style) - .data(mem_data) - .graph_type(tui::widgets::GraphType::Line), - ); - } - - if let Some((label_percent, label_frac)) = &app_state.canvas_data.swap_labels { - let swap_label = format!("SWP:{}{}", label_percent, label_frac); - mem_canvas_vec.push( - Dataset::default() - .name(swap_label) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(painter.colours.swap_style) - .data(swap_data) - .graph_type(tui::widgets::GraphType::Line), - ); - } - - let is_on_widget = widget_id == app_state.current_widget.widget_id; - let border_style = if is_on_widget { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }; - - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Memory ── Esc to go back "; - Spans::from(vec![ - Span::styled(" Memory ", painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat(usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2 - )) - ), - border_style, - ), - ]) - } else { - Spans::from(Span::styled( - " Memory ".to_string(), - painter.colours.widget_title_style, - )) - }; - - f.render_widget( - Chart::new(mem_canvas_vec) - .block( - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(if app_state.current_widget.widget_id == widget_id { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }), - ) - .x_axis(x_axis) - .y_axis(y_axis) - .hidden_legend_constraints((Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))), - draw_loc, - ); - - // Now if you're done, reset any interpolated points! - if let Some((index, old_value)) = interpolated_mem_point { - if let Some(to_replace) = mem_data.get_mut(index) { - *to_replace = old_value; - } - } - - if let Some((index, old_value)) = interpolated_swap_point { - if let Some(to_replace) = swap_data.get_mut(index) { - *to_replace = old_value; - } - } - } - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((draw_loc.x, draw_loc.y)); - widget.bottom_right_corner = - Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height)); - } - } -} diff --git a/src/canvas/drawing/network_basic.rs b/src/canvas/drawing/network_basic.rs deleted file mode 100644 index e5dcf579..00000000 --- a/src/canvas/drawing/network_basic.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::{app::AppState, canvas::Painter, constants::*}; - -use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - terminal::Frame, - text::{Span, Spans}, - widgets::{Block, Paragraph}, -}; - -pub fn draw_basic_network( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - let divided_loc = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) - .split(draw_loc); - - let net_loc = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(1) - .split(divided_loc[0]); - - let total_loc = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(1) - .split(divided_loc[1]); - - if app_state.current_widget.widget_id == widget_id { - f.render_widget( - Block::default() - .borders(*SIDE_BORDERS) - .border_style(painter.colours.highlighted_border_style), - draw_loc, - ); - } - - let rx_label = format!("RX: {}", &app_state.canvas_data.rx_display); - let tx_label = format!("TX: {}", &app_state.canvas_data.tx_display); - let total_rx_label = format!("Total RX: {}", &app_state.canvas_data.total_rx_display); - let total_tx_label = format!("Total TX: {}", &app_state.canvas_data.total_tx_display); - - let net_text = vec![ - Spans::from(Span::styled(rx_label, painter.colours.rx_style)), - Spans::from(Span::styled(tx_label, painter.colours.tx_style)), - ]; - - let total_net_text = vec![ - Spans::from(Span::styled(total_rx_label, painter.colours.total_rx_style)), - Spans::from(Span::styled(total_tx_label, painter.colours.total_tx_style)), - ]; - - f.render_widget(Paragraph::new(net_text).block(Block::default()), net_loc[0]); - - f.render_widget( - Paragraph::new(total_net_text).block(Block::default()), - total_loc[0], - ); - - // Update draw loc in widget map - if app_state.should_get_widget_bounds() { - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((draw_loc.x, draw_loc.y)); - widget.bottom_right_corner = - Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height)); - } - } -} diff --git a/src/canvas/drawing/network_graph.rs b/src/canvas/drawing/network_graph.rs deleted file mode 100644 index 85901f9c..00000000 --- a/src/canvas/drawing/network_graph.rs +++ /dev/null @@ -1,758 +0,0 @@ -use once_cell::sync::Lazy; -use std::cmp::max; -use unicode_segmentation::UnicodeSegmentation; - -use crate::{ - app::{AppState, AxisScaling}, - canvas::{ - drawing_utils::{get_column_widths, interpolate_points}, - Painter, - }, - constants::*, - units::data_units::DataUnit, - utils::gen_util::*, -}; - -use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - symbols::Marker, - terminal::Frame, - text::Span, - text::{Spans, Text}, - widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table}, -}; - -const NETWORK_HEADERS: [&str; 4] = ["RX", "TX", "Total RX", "Total TX"]; - -static NETWORK_HEADERS_LENS: Lazy> = Lazy::new(|| { - NETWORK_HEADERS - .iter() - .map(|entry| entry.len() as u16) - .collect::>() -}); - -pub fn draw_network( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - if app_state.app_config_fields.use_old_network_legend { - let network_chunk = Layout::default() - .direction(Direction::Vertical) - .margin(0) - .constraints([ - Constraint::Length(max(draw_loc.height as i64 - 5, 0) as u16), - Constraint::Length(5), - ]) - .split(draw_loc); - - draw_network_graph(painter, f, app_state, network_chunk[0], widget_id, true); - draw_network_labels(painter, f, app_state, network_chunk[1], widget_id); - } else { - draw_network_graph(painter, f, app_state, draw_loc, widget_id, false); - } - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - // Note that in both cases, we always go to the same widget id so it's fine to do it like - // this lol. - if let Some(network_widget) = app_state.widget_map.get_mut(&widget_id) { - network_widget.top_left_corner = Some((draw_loc.x, draw_loc.y)); - network_widget.bottom_right_corner = - Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height)); - } - } -} - -pub fn draw_network_graph( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, hide_legend: bool, -) { - /// Point is of time, data - type Point = (f64, f64); - - /// Returns the max data point and time given a time. - fn get_max_entry( - rx: &[Point], tx: &[Point], time_start: f64, network_scale_type: &AxisScaling, - network_use_binary_prefix: bool, - ) -> (f64, f64) { - /// Determines a "fake" max value in circumstances where we couldn't find one from the data. - fn calculate_missing_max( - network_scale_type: &AxisScaling, network_use_binary_prefix: bool, - ) -> f64 { - match network_scale_type { - AxisScaling::Log => { - if network_use_binary_prefix { - LOG_KIBI_LIMIT - } else { - LOG_KILO_LIMIT - } - } - AxisScaling::Linear => { - if network_use_binary_prefix { - KIBI_LIMIT_F64 - } else { - KILO_LIMIT_F64 - } - } - } - } - - // First, let's shorten our ranges to actually look. We can abuse the fact that our rx and tx arrays - // are sorted, so we can short-circuit our search to filter out only the relevant data points... - let filtered_rx = if let (Some(rx_start), Some(rx_end)) = ( - rx.iter().position(|(time, _data)| *time >= time_start), - rx.iter().rposition(|(time, _data)| *time <= 0.0), - ) { - Some(&rx[rx_start..=rx_end]) - } else { - None - }; - - let filtered_tx = if let (Some(tx_start), Some(tx_end)) = ( - tx.iter().position(|(time, _data)| *time >= time_start), - tx.iter().rposition(|(time, _data)| *time <= 0.0), - ) { - Some(&tx[tx_start..=tx_end]) - } else { - None - }; - - // Then, find the maximal rx/tx so we know how to scale, and return it. - match (filtered_rx, filtered_tx) { - (None, None) => ( - time_start, - calculate_missing_max(network_scale_type, network_use_binary_prefix), - ), - (None, Some(filtered_tx)) => { - match filtered_tx - .iter() - .max_by(|(_, data_a), (_, data_b)| get_ordering(data_a, data_b, false)) - { - Some((best_time, max_val)) => { - if *max_val == 0.0 { - ( - time_start, - calculate_missing_max( - network_scale_type, - network_use_binary_prefix, - ), - ) - } else { - (*best_time, *max_val) - } - } - None => ( - time_start, - calculate_missing_max(network_scale_type, network_use_binary_prefix), - ), - } - } - (Some(filtered_rx), None) => { - match filtered_rx - .iter() - .max_by(|(_, data_a), (_, data_b)| get_ordering(data_a, data_b, false)) - { - Some((best_time, max_val)) => { - if *max_val == 0.0 { - ( - time_start, - calculate_missing_max( - network_scale_type, - network_use_binary_prefix, - ), - ) - } else { - (*best_time, *max_val) - } - } - None => ( - time_start, - calculate_missing_max(network_scale_type, network_use_binary_prefix), - ), - } - } - (Some(filtered_rx), Some(filtered_tx)) => { - match filtered_rx - .iter() - .chain(filtered_tx) - .max_by(|(_, data_a), (_, data_b)| get_ordering(data_a, data_b, false)) - { - Some((best_time, max_val)) => { - if *max_val == 0.0 { - ( - *best_time, - calculate_missing_max( - network_scale_type, - network_use_binary_prefix, - ), - ) - } else { - (*best_time, *max_val) - } - } - None => ( - time_start, - calculate_missing_max(network_scale_type, network_use_binary_prefix), - ), - } - } - } - } - - /// Returns the required max data point and labels. - fn adjust_network_data_point( - max_entry: f64, network_scale_type: &AxisScaling, network_unit_type: &DataUnit, - network_use_binary_prefix: bool, - ) -> (f64, Vec) { - // So, we're going with an approach like this for linear data: - // - Main goal is to maximize the amount of information displayed given a specific height. - // We don't want to drown out some data if the ranges are too far though! Nor do we want to filter - // out too much data... - // - Change the y-axis unit (kilo/kibi, mega/mebi...) dynamically based on max load. - // - // The idea is we take the top value, build our scale such that each "point" is a scaled version of that. - // So for example, let's say I use 390 Mb/s. If I drew 4 segments, it would be 97.5, 195, 292.5, 390, and - // probably something like 438.75? - // - // So, how do we do this in tui-rs? Well, if we are using intervals that tie in perfectly to the max - // value we want... then it's actually not that hard. Since tui-rs accepts a vector as labels and will - // properly space them all out... we just work with that and space it out properly. - // - // Dynamic chart idea based off of FreeNAS's chart design. - // - // === - // - // For log data, we just use the old method of log intervals (kilo/mega/giga/etc.). Keep it nice and simple. - - // Now just check the largest unit we correspond to... then proceed to build some entries from there! - - let unit_char = match network_unit_type { - DataUnit::Byte => "B", - DataUnit::Bit => "b", - }; - - match network_scale_type { - AxisScaling::Linear => { - let (k_limit, m_limit, g_limit, t_limit) = if network_use_binary_prefix { - ( - KIBI_LIMIT_F64, - MEBI_LIMIT_F64, - GIBI_LIMIT_F64, - TEBI_LIMIT_F64, - ) - } else { - ( - KILO_LIMIT_F64, - MEGA_LIMIT_F64, - GIGA_LIMIT_F64, - TERA_LIMIT_F64, - ) - }; - - let bumped_max_entry = max_entry * 1.5; // We use the bumped up version to calculate our unit type. - let (max_value_scaled, unit_prefix, unit_type): (f64, &str, &str) = - if bumped_max_entry < k_limit { - (max_entry, "", unit_char) - } else if bumped_max_entry < m_limit { - ( - max_entry / k_limit, - if network_use_binary_prefix { "Ki" } else { "K" }, - unit_char, - ) - } else if bumped_max_entry < g_limit { - ( - max_entry / m_limit, - if network_use_binary_prefix { "Mi" } else { "M" }, - unit_char, - ) - } else if bumped_max_entry < t_limit { - ( - max_entry / g_limit, - if network_use_binary_prefix { "Gi" } else { "G" }, - unit_char, - ) - } else { - ( - max_entry / t_limit, - if network_use_binary_prefix { "Ti" } else { "T" }, - unit_char, - ) - }; - - // Finally, build an acceptable range starting from there, using the given height! - // Note we try to put more of a weight on the bottom section vs. the top, since the top has less data. - - let base_unit = max_value_scaled; - let labels: Vec = vec![ - format!("0{}{}", unit_prefix, unit_type), - format!("{:.1}", base_unit * 0.5), - format!("{:.1}", base_unit), - format!("{:.1}", base_unit * 1.5), - ] - .into_iter() - .map(|s| format!("{:>5}", s)) // Pull 5 as the longest legend value is generally going to be 5 digits (if they somehow hit over 5 terabits per second) - .collect(); - - (bumped_max_entry, labels) - } - AxisScaling::Log => { - let (m_limit, g_limit, t_limit) = if network_use_binary_prefix { - (LOG_MEBI_LIMIT, LOG_GIBI_LIMIT, LOG_TEBI_LIMIT) - } else { - (LOG_MEGA_LIMIT, LOG_GIGA_LIMIT, LOG_TERA_LIMIT) - }; - - fn get_zero(network_use_binary_prefix: bool, unit_char: &str) -> String { - format!( - "{}0{}", - if network_use_binary_prefix { " " } else { " " }, - unit_char - ) - } - - fn get_k(network_use_binary_prefix: bool, unit_char: &str) -> String { - format!( - "1{}{}", - if network_use_binary_prefix { "Ki" } else { "K" }, - unit_char - ) - } - - fn get_m(network_use_binary_prefix: bool, unit_char: &str) -> String { - format!( - "1{}{}", - if network_use_binary_prefix { "Mi" } else { "M" }, - unit_char - ) - } - - fn get_g(network_use_binary_prefix: bool, unit_char: &str) -> String { - format!( - "1{}{}", - if network_use_binary_prefix { "Gi" } else { "G" }, - unit_char - ) - } - - fn get_t(network_use_binary_prefix: bool, unit_char: &str) -> String { - format!( - "1{}{}", - if network_use_binary_prefix { "Ti" } else { "T" }, - unit_char - ) - } - - fn get_p(network_use_binary_prefix: bool, unit_char: &str) -> String { - format!( - "1{}{}", - if network_use_binary_prefix { "Pi" } else { "P" }, - unit_char - ) - } - - if max_entry < m_limit { - ( - m_limit, - vec![ - get_zero(network_use_binary_prefix, unit_char), - get_k(network_use_binary_prefix, unit_char), - get_m(network_use_binary_prefix, unit_char), - ], - ) - } else if max_entry < g_limit { - ( - g_limit, - vec![ - get_zero(network_use_binary_prefix, unit_char), - get_k(network_use_binary_prefix, unit_char), - get_m(network_use_binary_prefix, unit_char), - get_g(network_use_binary_prefix, unit_char), - ], - ) - } else if max_entry < t_limit { - ( - t_limit, - vec![ - get_zero(network_use_binary_prefix, unit_char), - get_k(network_use_binary_prefix, unit_char), - get_m(network_use_binary_prefix, unit_char), - get_g(network_use_binary_prefix, unit_char), - get_t(network_use_binary_prefix, unit_char), - ], - ) - } else { - // I really doubt anyone's transferring beyond petabyte speeds... - ( - if network_use_binary_prefix { - LOG_PEBI_LIMIT - } else { - LOG_PETA_LIMIT - }, - vec![ - get_zero(network_use_binary_prefix, unit_char), - get_k(network_use_binary_prefix, unit_char), - get_m(network_use_binary_prefix, unit_char), - get_g(network_use_binary_prefix, unit_char), - get_t(network_use_binary_prefix, unit_char), - get_p(network_use_binary_prefix, unit_char), - ], - ) - } - } - } - } - - if let Some(network_widget_state) = app_state.net_state.widget_states.get_mut(&widget_id) { - let network_data_rx: &mut [(f64, f64)] = &mut app_state.canvas_data.network_data_rx; - let network_data_tx: &mut [(f64, f64)] = &mut app_state.canvas_data.network_data_tx; - - let time_start = -(network_widget_state.current_display_time as f64); - - let display_time_labels = vec![ - Span::styled( - format!("{}s", network_widget_state.current_display_time / 1000), - painter.colours.graph_style, - ), - Span::styled("0s".to_string(), painter.colours.graph_style), - ]; - let x_axis = if app_state.app_config_fields.hide_time - || (app_state.app_config_fields.autohide_time - && network_widget_state.autohide_timer.is_none()) - { - Axis::default().bounds([time_start, 0.0]) - } else if let Some(time) = network_widget_state.autohide_timer { - if std::time::Instant::now().duration_since(time).as_millis() - < AUTOHIDE_TIMEOUT_MILLISECONDS as u128 - { - Axis::default() - .bounds([time_start, 0.0]) - .style(painter.colours.graph_style) - .labels(display_time_labels) - } else { - network_widget_state.autohide_timer = None; - Axis::default().bounds([time_start, 0.0]) - } - } else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT { - Axis::default().bounds([time_start, 0.0]) - } else { - Axis::default() - .bounds([time_start, 0.0]) - .style(painter.colours.graph_style) - .labels(display_time_labels) - }; - - // Interpolate a point for rx and tx between the last value outside of the left bounds and the first value - // inside it. - // Because we assume it is all in order for... basically all our code, we can't just append it, - // and insertion in the middle seems. So instead, we swap *out* the value that is outside with our - // interpolated point, draw and do whatever calculations, then swap back in the old value! - // - // Note there is some re-used work here! For potential optimizations, we could re-use some work here in/from - // get_max_entry... - let interpolated_rx_point = if let Some(rx_end_pos) = network_data_rx - .iter() - .position(|(time, _data)| *time >= time_start) - { - if rx_end_pos > 1 { - let rx_start_pos = rx_end_pos - 1; - let outside_rx_point = network_data_rx.get(rx_start_pos); - let inside_rx_point = network_data_rx.get(rx_end_pos); - - if let (Some(outside_rx_point), Some(inside_rx_point)) = - (outside_rx_point, inside_rx_point) - { - let old = *outside_rx_point; - - let new_point = ( - time_start, - interpolate_points(outside_rx_point, inside_rx_point, time_start), - ); - - // debug!( - // "Interpolated between {:?} and {:?}, got rx for time {:?}: {:?}", - // outside_rx_point, inside_rx_point, time_start, new_point - // ); - - if let Some(to_replace) = network_data_rx.get_mut(rx_start_pos) { - *to_replace = new_point; - Some((rx_start_pos, old)) - } else { - None // Failed to get mutable reference. - } - } else { - None // Point somehow doesn't exist in our network_data_rx - } - } else { - None // Point is already "leftmost", no need to interpolate. - } - } else { - None // There is no point. - }; - - let interpolated_tx_point = if let Some(tx_end_pos) = network_data_tx - .iter() - .position(|(time, _data)| *time >= time_start) - { - if tx_end_pos > 1 { - let tx_start_pos = tx_end_pos - 1; - let outside_tx_point = network_data_tx.get(tx_start_pos); - let inside_tx_point = network_data_tx.get(tx_end_pos); - - if let (Some(outside_tx_point), Some(inside_tx_point)) = - (outside_tx_point, inside_tx_point) - { - let old = *outside_tx_point; - - let new_point = ( - time_start, - interpolate_points(outside_tx_point, inside_tx_point, time_start), - ); - - if let Some(to_replace) = network_data_tx.get_mut(tx_start_pos) { - *to_replace = new_point; - Some((tx_start_pos, old)) - } else { - None // Failed to get mutable reference. - } - } else { - None // Point somehow doesn't exist in our network_data_tx - } - } else { - None // Point is already "leftmost", no need to interpolate. - } - } else { - None // There is no point. - }; - - // TODO: Cache network results: Only update if: - // - Force update (includes time interval change) - // - Old max time is off screen - // - A new time interval is better and does not fit (check from end of vector to last checked; we only want to update if it is TOO big!) - - // Find the maximal rx/tx so we know how to scale, and return it. - - let (_best_time, max_entry) = get_max_entry( - network_data_rx, - network_data_tx, - time_start, - &app_state.app_config_fields.network_scale_type, - app_state.app_config_fields.network_use_binary_prefix, - ); - - let (max_range, labels) = adjust_network_data_point( - max_entry, - &app_state.app_config_fields.network_scale_type, - &app_state.app_config_fields.network_unit_type, - app_state.app_config_fields.network_use_binary_prefix, - ); - - // Cache results. - // network_widget_state.draw_max_range_cache = max_range; - // network_widget_state.draw_time_start_cache = best_time; - // network_widget_state.draw_labels_cache = labels; - - let y_axis_labels = labels - .iter() - .map(|label| Span::styled(label, painter.colours.graph_style)) - .collect::>(); - let y_axis = Axis::default() - .style(painter.colours.graph_style) - .bounds([0.0, max_range]) - .labels(y_axis_labels); - - let is_on_widget = widget_id == app_state.current_widget.widget_id; - let border_style = if is_on_widget { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }; - - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Network ── Esc to go back "; - Spans::from(vec![ - Span::styled(" Network ", painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat(usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2 - )) - ), - border_style, - ), - ]) - } else { - Spans::from(Span::styled( - " Network ", - painter.colours.widget_title_style, - )) - }; - - let legend_constraints = if hide_legend { - (Constraint::Ratio(0, 1), Constraint::Ratio(0, 1)) - } else { - (Constraint::Ratio(1, 1), Constraint::Ratio(3, 4)) - }; - - // TODO: Add support for clicking on legend to only show that value on chart. - let dataset = if app_state.app_config_fields.use_old_network_legend && !hide_legend { - vec![ - Dataset::default() - .name(format!("RX: {:7}", app_state.canvas_data.rx_display)) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(painter.colours.rx_style) - .data(network_data_rx) - .graph_type(tui::widgets::GraphType::Line), - Dataset::default() - .name(format!("TX: {:7}", app_state.canvas_data.tx_display)) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(painter.colours.tx_style) - .data(network_data_tx) - .graph_type(tui::widgets::GraphType::Line), - Dataset::default() - .name(format!( - "Total RX: {:7}", - app_state.canvas_data.total_rx_display - )) - .style(painter.colours.total_rx_style), - Dataset::default() - .name(format!( - "Total TX: {:7}", - app_state.canvas_data.total_tx_display - )) - .style(painter.colours.total_tx_style), - ] - } else { - vec![ - Dataset::default() - .name(&app_state.canvas_data.rx_display) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(painter.colours.rx_style) - .data(network_data_rx) - .graph_type(tui::widgets::GraphType::Line), - Dataset::default() - .name(&app_state.canvas_data.tx_display) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(painter.colours.tx_style) - .data(network_data_tx) - .graph_type(tui::widgets::GraphType::Line), - ] - }; - - f.render_widget( - Chart::new(dataset) - .block( - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(if app_state.current_widget.widget_id == widget_id { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }), - ) - .x_axis(x_axis) - .y_axis(y_axis) - .hidden_legend_constraints(legend_constraints), - draw_loc, - ); - - // Now if you're done, reset any interpolated points! - if let Some((index, old_value)) = interpolated_rx_point { - if let Some(to_replace) = network_data_rx.get_mut(index) { - *to_replace = old_value; - } - } - - if let Some((index, old_value)) = interpolated_tx_point { - if let Some(to_replace) = network_data_tx.get_mut(index) { - *to_replace = old_value; - } - } - } -} - -fn draw_network_labels( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - widget_id: u64, -) { - let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - 0 - } else { - app_state.app_config_fields.table_gap - }; - - let rx_display = &app_state.canvas_data.rx_display; - let tx_display = &app_state.canvas_data.tx_display; - let total_rx_display = &app_state.canvas_data.total_rx_display; - let total_tx_display = &app_state.canvas_data.total_tx_display; - - // Gross but I need it to work... - let total_network = vec![vec![ - Text::raw(rx_display), - Text::raw(tx_display), - Text::raw(total_rx_display), - Text::raw(total_tx_display), - ]]; - let mapped_network = total_network - .into_iter() - .map(|val| Row::new(val).style(painter.colours.text_style)); - - // Calculate widths - let intrinsic_widths = get_column_widths( - draw_loc.width, - &[None, None, None, None], - &(NETWORK_HEADERS_LENS - .iter() - .map(|s| Some(*s)) - .collect::>()), - &[Some(0.25); 4], - &(NETWORK_HEADERS_LENS - .iter() - .map(|s| Some(*s)) - .collect::>()), - true, - ); - - // Draw - f.render_widget( - Table::new(mapped_network) - .header( - Row::new(NETWORK_HEADERS.to_vec()) - .style(painter.colours.table_header_style) - .bottom_margin(table_gap), - ) - .block(Block::default().borders(Borders::ALL).border_style( - if app_state.current_widget.widget_id == widget_id { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }, - )) - .style(painter.colours.text_style) - .widths( - &(intrinsic_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::>()), - ), - draw_loc, - ); -} diff --git a/src/canvas/drawing/process_table.rs b/src/canvas/drawing/process_table.rs deleted file mode 100644 index 2104a5bd..00000000 --- a/src/canvas/drawing/process_table.rs +++ /dev/null @@ -1,856 +0,0 @@ -use crate::{ - app::AppState, - canvas::{ - drawing_utils::{get_column_widths, get_search_start_position, get_start_position}, - Painter, - }, - constants::*, -}; - -use indextree::NodeId; -use tui::{ - backend::Backend, - layout::{Alignment, Constraint, Direction, Layout, Rect}, - terminal::Frame, - text::{Span, Spans, Text}, - widgets::{Block, Borders, Paragraph, Row, Table}, -}; - -use unicode_segmentation::{GraphemeIndices, UnicodeSegmentation}; -use unicode_width::UnicodeWidthStr; - -use once_cell::sync::Lazy; - -static PROCESS_HEADERS_HARD_WIDTH_NO_GROUP: Lazy>> = Lazy::new(|| { - vec![ - Some(7), - None, - Some(8), - Some(8), - Some(8), - Some(8), - Some(7), - Some(8), - #[cfg(target_family = "unix")] - None, - None, - ] -}); -static PROCESS_HEADERS_HARD_WIDTH_GROUPED: Lazy>> = Lazy::new(|| { - vec![ - Some(7), - None, - Some(8), - Some(8), - Some(8), - Some(8), - Some(7), - Some(8), - ] -}); - -static PROCESS_HEADERS_SOFT_WIDTH_MAX_GROUPED_COMMAND: Lazy>> = - Lazy::new(|| vec![None, Some(0.7), None, None, None, None, None, None]); -static PROCESS_HEADERS_SOFT_WIDTH_MAX_GROUPED_ELSE: Lazy>> = - Lazy::new(|| vec![None, Some(0.3), None, None, None, None, None, None]); - -static PROCESS_HEADERS_SOFT_WIDTH_MAX_NO_GROUP_COMMAND: Lazy>> = Lazy::new(|| { - vec![ - None, - Some(0.7), - None, - None, - None, - None, - None, - None, - #[cfg(target_family = "unix")] - Some(0.05), - Some(0.2), - ] -}); -static PROCESS_HEADERS_SOFT_WIDTH_MAX_NO_GROUP_TREE: Lazy>> = Lazy::new(|| { - vec![ - None, - Some(0.5), - None, - None, - None, - None, - None, - None, - #[cfg(target_family = "unix")] - Some(0.05), - Some(0.2), - ] -}); -static PROCESS_HEADERS_SOFT_WIDTH_MAX_NO_GROUP_ELSE: Lazy>> = Lazy::new(|| { - vec![ - None, - Some(0.3), - None, - None, - None, - None, - None, - None, - #[cfg(target_family = "unix")] - Some(0.05), - Some(0.2), - ] -}); - -pub fn draw_process_features( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - draw_border: bool, widget_id: NodeId, -) { - if let Some(process_widget_state) = app_state.proc_state.widget_states.get(&1) { - let search_height = if draw_border { 5 } else { 3 }; - let is_sort_open = process_widget_state.is_sort_open; - let header_len = process_widget_state.columns.longest_header_len; - - let mut proc_draw_loc = draw_loc; - if process_widget_state.is_search_enabled() { - let processes_chunk = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Min(0), Constraint::Length(search_height)]) - .split(draw_loc); - proc_draw_loc = processes_chunk[0]; - - draw_search_field( - painter, - f, - app_state, - processes_chunk[1], - draw_border, - widget_id, - ); - } - - if is_sort_open { - let processes_chunk = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Length(header_len + 4), Constraint::Min(0)]) - .split(proc_draw_loc); - proc_draw_loc = processes_chunk[1]; - - draw_process_sort(painter, f, app_state, processes_chunk[0], draw_border, 1); - } - - draw_processes_table(painter, f, app_state, proc_draw_loc, draw_border, widget_id); - } -} - -fn draw_processes_table( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - draw_border: bool, widget_id: NodeId, -) { - let should_get_widget_bounds = app_state.should_get_widget_bounds(); - if let Some(proc_widget_state) = app_state.proc_state.widget_states.get_mut(&1) { - let recalculate_column_widths = - should_get_widget_bounds || proc_widget_state.requires_redraw; - if proc_widget_state.requires_redraw { - proc_widget_state.requires_redraw = false; - } - - let is_on_widget = false; - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; - - let (border_style, highlight_style) = if is_on_widget { - ( - painter.colours.highlighted_border_style, - painter.colours.currently_selected_text_style, - ) - } else { - (painter.colours.border_style, painter.colours.text_style) - }; - - let title_base = if app_state.app_config_fields.show_table_scroll_position { - if let Some(finalized_process_data) = app_state - .canvas_data - .stringified_process_data_map - .get(&widget_id) - { - let title = format!( - " Processes ({} of {}) ", - proc_widget_state - .scroll_state - .current_scroll_position - .saturating_add(1), - finalized_process_data.len() - ); - - if title.len() <= draw_loc.width as usize { - title - } else { - " Processes ".to_string() - } - } else { - " Processes ".to_string() - } - } else { - " Processes ".to_string() - }; - - let title = if app_state.is_expanded - && !proc_widget_state - .process_search_state - .search_state - .is_enabled - && !proc_widget_state.is_sort_open - { - const ESCAPE_ENDING: &str = "── Esc to go back "; - - let (chosen_title_base, expanded_title_base) = { - let temp_title_base = format!("{}{}", title_base, ESCAPE_ENDING); - - if temp_title_base.len() > draw_loc.width as usize { - ( - " Processes ".to_string(), - format!("{}{}", " Processes ".to_string(), ESCAPE_ENDING), - ) - } else { - (title_base, temp_title_base) - } - }; - - Spans::from(vec![ - Span::styled(chosen_title_base, painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(expanded_title_base.as_str(), true) - .count() - + 2 - ) - ) - ), - border_style, - ), - ]) - } else { - Spans::from(Span::styled(title_base, painter.colours.widget_title_style)) - }; - - let process_block = if draw_border { - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(border_style) - } else if is_on_widget { - Block::default() - .borders(*SIDE_BORDERS) - .border_style(painter.colours.highlighted_border_style) - } else { - Block::default().borders(Borders::NONE) - }; - - if let Some(process_data) = &app_state - .canvas_data - .stringified_process_data_map - .get(&widget_id) - { - let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - 0 - } else { - app_state.app_config_fields.table_gap - }; - let position = get_start_position( - usize::from( - (draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset), - ), - &proc_widget_state.scroll_state.scroll_direction, - &mut proc_widget_state.scroll_state.previous_scroll_position, - proc_widget_state.scroll_state.current_scroll_position, - app_state.is_force_redraw, - ); - - // Sanity check - let start_position = if position >= process_data.len() { - process_data.len().saturating_sub(1) - } else { - position - }; - - let sliced_vec = &process_data[start_position..]; - let processed_sliced_vec = sliced_vec.iter().map(|(data, disabled)| { - ( - data.iter() - .map(|(entry, _alternative)| entry) - .collect::>(), - disabled, - ) - }); - - let proc_table_state = &mut proc_widget_state.scroll_state.table_state; - proc_table_state.select(Some( - proc_widget_state - .scroll_state - .current_scroll_position - .saturating_sub(start_position), - )); - - // Draw! - let process_headers = proc_widget_state.columns.get_column_headers( - &proc_widget_state.process_sorting_type, - proc_widget_state.is_process_sort_descending, - ); - - // Calculate widths - // FIXME: See if we can move this into the recalculate block? I want to move column widths into the column widths - let hard_widths = if proc_widget_state.is_grouped { - &*PROCESS_HEADERS_HARD_WIDTH_GROUPED - } else { - &*PROCESS_HEADERS_HARD_WIDTH_NO_GROUP - }; - - if recalculate_column_widths { - let mut column_widths = process_headers - .iter() - .map(|entry| UnicodeWidthStr::width(entry.as_str()) as u16) - .collect::>(); - - let soft_widths_min = column_widths - .iter() - .map(|width| Some(*width)) - .collect::>(); - - proc_widget_state.table_width_state.desired_column_widths = { - for (row, _disabled) in processed_sliced_vec.clone() { - for (col, entry) in row.iter().enumerate() { - if let Some(col_width) = column_widths.get_mut(col) { - let grapheme_len = UnicodeWidthStr::width(entry.as_str()); - if grapheme_len as u16 > *col_width { - *col_width = grapheme_len as u16; - } - } - } - } - column_widths - }; - - proc_widget_state.table_width_state.desired_column_widths = proc_widget_state - .table_width_state - .desired_column_widths - .iter() - .zip(hard_widths) - .map(|(current, hard)| { - if let Some(hard) = hard { - if *hard > *current { - *hard - } else { - *current - } - } else { - *current - } - }) - .collect::>(); - - let soft_widths_max = if proc_widget_state.is_grouped { - // Note grouped trees are not a thing. - - if proc_widget_state.is_using_command { - &*PROCESS_HEADERS_SOFT_WIDTH_MAX_GROUPED_COMMAND - } else { - &*PROCESS_HEADERS_SOFT_WIDTH_MAX_GROUPED_ELSE - } - } else if proc_widget_state.is_using_command { - &*PROCESS_HEADERS_SOFT_WIDTH_MAX_NO_GROUP_COMMAND - } else if proc_widget_state.is_tree_mode { - &*PROCESS_HEADERS_SOFT_WIDTH_MAX_NO_GROUP_TREE - } else { - &*PROCESS_HEADERS_SOFT_WIDTH_MAX_NO_GROUP_ELSE - }; - - proc_widget_state.table_width_state.calculated_column_widths = get_column_widths( - draw_loc.width, - hard_widths, - &soft_widths_min, - soft_widths_max, - &(proc_widget_state - .table_width_state - .desired_column_widths - .iter() - .map(|width| Some(*width)) - .collect::>()), - true, - ); - - // debug!( - // "DCW: {:?}", - // proc_widget_state.table_width_state.desired_column_widths - // ); - // debug!( - // "CCW: {:?}", - // proc_widget_state.table_width_state.calculated_column_widths - // ); - } - - let dcw = &proc_widget_state.table_width_state.desired_column_widths; - let ccw = &proc_widget_state.table_width_state.calculated_column_widths; - - let process_rows = sliced_vec.iter().map(|(data, disabled)| { - let truncated_data = data.iter().zip(hard_widths).enumerate().map( - |(itx, ((entry, alternative), width))| { - if let (Some(desired_col_width), Some(calculated_col_width)) = - (dcw.get(itx), ccw.get(itx)) - { - if width.is_none() { - if *desired_col_width > *calculated_col_width - && *calculated_col_width > 0 - { - let graphemes = - UnicodeSegmentation::graphemes(entry.as_str(), true) - .collect::>(); - - if let Some(alternative) = alternative { - Text::raw(alternative) - } else if graphemes.len() > *calculated_col_width as usize - && *calculated_col_width > 1 - { - // Truncate with ellipsis - let first_n = graphemes - [..(*calculated_col_width as usize - 1)] - .concat(); - Text::raw(format!("{}…", first_n)) - } else { - Text::raw(entry) - } - } else { - Text::raw(entry) - } - } else { - Text::raw(entry) - } - } else { - Text::raw(entry) - } - }, - ); - - if *disabled { - Row::new(truncated_data).style(painter.colours.disabled_text_style) - } else { - Row::new(truncated_data) - } - }); - - f.render_stateful_widget( - Table::new(process_rows) - .header( - Row::new(process_headers) - .style(painter.colours.table_header_style) - .bottom_margin(table_gap), - ) - .block(process_block) - .highlight_style(highlight_style) - .style(painter.colours.text_style) - .widths( - &(proc_widget_state - .table_width_state - .calculated_column_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::>()), - ), - margined_draw_loc, - proc_table_state, - ); - } else { - f.render_widget(process_block, margined_draw_loc); - } - - // Check if we need to update columnar bounds... - if recalculate_column_widths - || proc_widget_state.columns.column_header_x_locs.is_none() - || proc_widget_state.columns.column_header_y_loc.is_none() - { - // y location is just the y location of the widget + border size (1 normally, 0 in basic) - proc_widget_state.columns.column_header_y_loc = - Some(draw_loc.y + if draw_border { 1 } else { 0 }); - - // x location is determined using the x locations of the widget; just offset from the left bound - // as appropriate, and use the right bound as limiter. - - let mut current_x_left = draw_loc.x + 1; - let max_x_right = draw_loc.x + draw_loc.width - 1; - - let mut x_locs = vec![]; - - for width in proc_widget_state - .table_width_state - .calculated_column_widths - .iter() - { - let right_bound = current_x_left + width; - - if right_bound < max_x_right { - x_locs.push((current_x_left, right_bound)); - current_x_left = right_bound + 1; - } else { - x_locs.push((current_x_left, max_x_right)); - break; - } - } - - proc_widget_state.columns.column_header_x_locs = Some(x_locs); - } - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - if let Some(widget) = app_state.widget_map.get_mut(&1) { - widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y)); - widget.bottom_right_corner = Some(( - margined_draw_loc.x + margined_draw_loc.width, - margined_draw_loc.y + margined_draw_loc.height, - )); - } - } - } -} - -fn draw_search_field( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - draw_border: bool, _widget_id: NodeId, -) { - fn build_query<'a>( - is_on_widget: bool, grapheme_indices: GraphemeIndices<'a>, start_position: usize, - cursor_position: usize, query: &str, currently_selected_text_style: tui::style::Style, - text_style: tui::style::Style, - ) -> Vec> { - let mut current_grapheme_posn = 0; - - if is_on_widget { - let mut res = grapheme_indices - .filter_map(|grapheme| { - current_grapheme_posn += UnicodeWidthStr::width(grapheme.1); - - if current_grapheme_posn <= start_position { - None - } else { - let styled = if grapheme.0 == cursor_position { - Span::styled(grapheme.1, currently_selected_text_style) - } else { - Span::styled(grapheme.1, text_style) - }; - Some(styled) - } - }) - .collect::>(); - - if cursor_position == query.len() { - res.push(Span::styled(" ", currently_selected_text_style)) - } - - res - } else { - // This is easier - we just need to get a range of graphemes, rather than - // dealing with possibly inserting a cursor (as none is shown!) - - vec![Span::styled(query.to_string(), text_style)] - } - } - - // TODO: Make the cursor scroll back if there's space! - if let Some(proc_widget_state) = app_state.proc_state.widget_states.get_mut(&1) { - let is_on_widget = false; - let num_columns = usize::from(draw_loc.width); - let search_title = "> "; - - let num_chars_for_text = search_title.len(); - let cursor_position = proc_widget_state.get_search_cursor_position(); - let current_cursor_position = proc_widget_state.get_char_cursor_position(); - - let start_position: usize = get_search_start_position( - num_columns - num_chars_for_text - 5, - &proc_widget_state - .process_search_state - .search_state - .cursor_direction, - &mut proc_widget_state - .process_search_state - .search_state - .cursor_bar, - current_cursor_position, - app_state.is_force_redraw, - ); - - let query = proc_widget_state.get_current_search_query().as_str(); - let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true); - - // TODO: [CURSOR] blank cursor if not selected - // TODO: [CURSOR] blinking cursor? - let query_with_cursor = build_query( - is_on_widget, - grapheme_indices, - start_position, - cursor_position, - query, - painter.colours.currently_selected_text_style, - painter.colours.text_style, - ); - - let mut search_text = vec![Spans::from({ - let mut search_vec = vec![Span::styled( - search_title, - if is_on_widget { - painter.colours.table_header_style - } else { - painter.colours.text_style - }, - )]; - search_vec.extend(query_with_cursor); - - search_vec - })]; - - // Text options shamelessly stolen from VS Code. - let case_style = if !proc_widget_state.process_search_state.is_ignoring_case { - painter.colours.currently_selected_text_style - } else { - painter.colours.text_style - }; - - let whole_word_style = if proc_widget_state - .process_search_state - .is_searching_whole_word - { - painter.colours.currently_selected_text_style - } else { - painter.colours.text_style - }; - - let regex_style = if proc_widget_state - .process_search_state - .is_searching_with_regex - { - painter.colours.currently_selected_text_style - } else { - painter.colours.text_style - }; - - // FIXME: [MOUSE] Mouse support for these in search - // FIXME: [MOVEMENT] Movement support for these in search - let option_text = Spans::from(vec![ - Span::styled( - format!("Case({})", if painter.is_mac_os { "F1" } else { "Alt+C" }), - case_style, - ), - Span::raw(" "), - Span::styled( - format!("Whole({})", if painter.is_mac_os { "F2" } else { "Alt+W" }), - whole_word_style, - ), - Span::raw(" "), - Span::styled( - format!("Regex({})", if painter.is_mac_os { "F3" } else { "Alt+R" }), - regex_style, - ), - ]); - - search_text.push(Spans::from(Span::styled( - if let Some(err) = &proc_widget_state - .process_search_state - .search_state - .error_message - { - err.as_str() - } else { - "" - }, - painter.colours.invalid_query_style, - ))); - search_text.push(option_text); - - let current_border_style = if proc_widget_state - .process_search_state - .search_state - .is_invalid_search - { - painter.colours.invalid_query_style - } else if is_on_widget { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }; - - let title = Span::styled( - if draw_border { - const TITLE_BASE: &str = " Esc to close "; - let repeat_num = - usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2); - format!("{} Esc to close ", "─".repeat(repeat_num)) - } else { - String::new() - }, - current_border_style, - ); - - let process_search_block = if draw_border { - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(current_border_style) - } else if is_on_widget { - Block::default() - .borders(*SIDE_BORDERS) - .border_style(current_border_style) - } else { - Block::default().borders(Borders::NONE) - }; - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; - - f.render_widget( - Paragraph::new(search_text) - .block(process_search_block) - .style(painter.colours.text_style) - .alignment(Alignment::Left), - margined_draw_loc, - ); - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - if let Some(widget) = app_state.widget_map.get_mut(&1) { - widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y)); - widget.bottom_right_corner = Some(( - margined_draw_loc.x + margined_draw_loc.width, - margined_draw_loc.y + margined_draw_loc.height, - )); - } - } - } -} - -fn draw_process_sort( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, - draw_border: bool, widget_id: u64, -) { - let is_on_widget = widget_id == app_state.current_widget.widget_id; - - if let Some(proc_widget_state) = app_state.proc_state.widget_states.get_mut(&(widget_id - 2)) { - let current_scroll_position = proc_widget_state.columns.current_scroll_position; - let sort_string = proc_widget_state - .columns - .ordered_columns - .iter() - .filter(|column_type| { - proc_widget_state - .columns - .column_mapping - .get(column_type) - .unwrap() - .enabled - }) - .map(|column_type| column_type.to_string()) - .collect::>(); - - let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - 0 - } else { - app_state.app_config_fields.table_gap - }; - let position = get_start_position( - usize::from( - (draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset), - ), - &proc_widget_state.columns.scroll_direction, - &mut proc_widget_state.columns.previous_scroll_position, - current_scroll_position, - app_state.is_force_redraw, - ); - - // Sanity check - let start_position = if position >= sort_string.len() { - sort_string.len().saturating_sub(1) - } else { - position - }; - - let sliced_vec = &sort_string[start_position..]; - - let sort_options = sliced_vec - .iter() - .map(|column| Row::new(vec![column.as_str()])); - - let column_state = &mut proc_widget_state.columns.column_state; - column_state.select(Some( - proc_widget_state - .columns - .current_scroll_position - .saturating_sub(start_position), - )); - let current_border_style = if proc_widget_state - .process_search_state - .search_state - .is_invalid_search - { - painter.colours.invalid_query_style - } else if is_on_widget { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }; - - let process_sort_block = if draw_border { - Block::default() - .borders(Borders::ALL) - .border_style(current_border_style) - } else if is_on_widget { - Block::default() - .borders(*SIDE_BORDERS) - .border_style(current_border_style) - } else { - Block::default().borders(Borders::NONE) - }; - - let highlight_style = if is_on_widget { - painter.colours.currently_selected_text_style - } else { - painter.colours.text_style - }; - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; - - f.render_stateful_widget( - Table::new(sort_options) - .header( - Row::new(vec!["Sort By"]) - .style(painter.colours.table_header_style) - .bottom_margin(table_gap), - ) - .block(process_sort_block) - .highlight_style(highlight_style) - .style(painter.colours.text_style) - .widths(&[Constraint::Percentage(100)]), - margined_draw_loc, - column_state, - ); - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y)); - widget.bottom_right_corner = Some(( - margined_draw_loc.x + margined_draw_loc.width, - margined_draw_loc.y + margined_draw_loc.height, - )); - } - } - } -} diff --git a/src/canvas/drawing/temp_table.rs b/src/canvas/drawing/temp_table.rs deleted file mode 100644 index 17d05842..00000000 --- a/src/canvas/drawing/temp_table.rs +++ /dev/null @@ -1,256 +0,0 @@ -use once_cell::sync::Lazy; -use tui::{ - backend::Backend, - layout::{Constraint, Direction, Layout, Rect}, - terminal::Frame, - text::Span, - text::{Spans, Text}, - widgets::{Block, Borders, Row, Table}, -}; - -use crate::{ - app, - canvas::{ - drawing_utils::{get_column_widths, get_start_position}, - Painter, - }, - constants::*, -}; -use unicode_segmentation::UnicodeSegmentation; - -const TEMP_HEADERS: [&str; 2] = ["Sensor", "Temp"]; - -static TEMP_HEADERS_LENS: Lazy> = Lazy::new(|| { - TEMP_HEADERS - .iter() - .map(|entry| entry.len() as u16) - .collect::>() -}); - -pub fn draw_temp_table( - painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect, - draw_border: bool, widget_id: u64, -) { - let recalculate_column_widths = app_state.should_get_widget_bounds(); - if let Some(temp_widget_state) = app_state.temp_state.widget_states.get_mut(&widget_id) { - let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - 0 - } else { - app_state.app_config_fields.table_gap - }; - let start_position = get_start_position( - usize::from( - (draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset), - ), - &temp_widget_state.scroll_state.scroll_direction, - &mut temp_widget_state.scroll_state.previous_scroll_position, - temp_widget_state.scroll_state.current_scroll_position, - app_state.is_force_redraw, - ); - let is_on_widget = widget_id == app_state.current_widget.widget_id; - let temp_table_state = &mut temp_widget_state.scroll_state.table_state; - temp_table_state.select(Some( - temp_widget_state - .scroll_state - .current_scroll_position - .saturating_sub(start_position), - )); - let sliced_vec = &app_state.canvas_data.temp_sensor_data[start_position..]; - - // Calculate widths - let hard_widths = [None, None]; - if recalculate_column_widths { - temp_widget_state.table_width_state.desired_column_widths = { - let mut column_widths = TEMP_HEADERS_LENS.clone(); - for row in sliced_vec { - for (col, entry) in row.iter().enumerate() { - if entry.len() as u16 > column_widths[col] { - column_widths[col] = entry.len() as u16; - } - } - } - - column_widths - }; - temp_widget_state.table_width_state.calculated_column_widths = get_column_widths( - draw_loc.width, - &hard_widths, - &(TEMP_HEADERS_LENS - .iter() - .map(|width| Some(*width)) - .collect::>()), - &[Some(0.80), Some(-1.0)], - &temp_widget_state - .table_width_state - .desired_column_widths - .iter() - .map(|width| Some(*width)) - .collect::>(), - false, - ); - } - - let dcw = &temp_widget_state.table_width_state.desired_column_widths; - let ccw = &temp_widget_state.table_width_state.calculated_column_widths; - let temperature_rows = sliced_vec.iter().map(|temp_row| { - let truncated_data = - temp_row - .iter() - .zip(&hard_widths) - .enumerate() - .map(|(itx, (entry, width))| { - if width.is_none() { - if let (Some(desired_col_width), Some(calculated_col_width)) = - (dcw.get(itx), ccw.get(itx)) - { - if *desired_col_width > *calculated_col_width - && *calculated_col_width > 0 - { - let graphemes = - UnicodeSegmentation::graphemes(entry.as_str(), true) - .collect::>(); - - if graphemes.len() > *calculated_col_width as usize - && *calculated_col_width > 1 - { - // Truncate with ellipsis - let first_n = graphemes - [..(*calculated_col_width as usize - 1)] - .concat(); - Text::raw(format!("{}…", first_n)) - } else { - Text::raw(entry) - } - } else { - Text::raw(entry) - } - } else { - Text::raw(entry) - } - } else { - Text::raw(entry) - } - }); - - Row::new(truncated_data) - }); - - let (border_style, highlight_style) = if is_on_widget { - ( - painter.colours.highlighted_border_style, - painter.colours.currently_selected_text_style, - ) - } else { - (painter.colours.border_style, painter.colours.text_style) - }; - - let title_base = if app_state.app_config_fields.show_table_scroll_position { - let title_string = format!( - " Temperatures ({} of {}) ", - temp_widget_state - .scroll_state - .current_scroll_position - .saturating_add(1), - app_state.canvas_data.temp_sensor_data.len() - ); - - if title_string.len() <= draw_loc.width as usize { - title_string - } else { - " Temperatures ".to_string() - } - } else { - " Temperatures ".to_string() - }; - - let title = if app_state.is_expanded { - const ESCAPE_ENDING: &str = "── Esc to go back "; - - let (chosen_title_base, expanded_title_base) = { - let temp_title_base = format!("{}{}", title_base, ESCAPE_ENDING); - - if temp_title_base.len() > draw_loc.width as usize { - ( - " Temperatures ".to_string(), - format!("{}{}", " Temperatures ".to_string(), ESCAPE_ENDING), - ) - } else { - (title_base, temp_title_base) - } - }; - - Spans::from(vec![ - Span::styled(chosen_title_base, painter.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(expanded_title_base.as_str(), true) - .count() - + 2 - ) - ) - ), - border_style, - ), - ]) - } else { - Spans::from(Span::styled(title_base, painter.colours.widget_title_style)) - }; - - let temp_block = if draw_border { - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(border_style) - } else if is_on_widget { - Block::default() - .borders(*SIDE_BORDERS) - .border_style(painter.colours.highlighted_border_style) - } else { - Block::default().borders(Borders::NONE) - }; - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; - - // Draw - f.render_stateful_widget( - Table::new(temperature_rows) - .header( - Row::new(TEMP_HEADERS.to_vec()) - .style(painter.colours.table_header_style) - .bottom_margin(table_gap), - ) - .block(temp_block) - .highlight_style(highlight_style) - .style(painter.colours.text_style) - .widths( - &(temp_widget_state - .table_width_state - .calculated_column_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::>()), - ), - margined_draw_loc, - temp_table_state, - ); - - if app_state.should_get_widget_bounds() { - // Update draw loc in widget map - // Note there is no difference between this and using draw_loc, but I'm too lazy to fix it. - if let Some(widget) = app_state.widget_map.get_mut(&widget_id) { - widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y)); - widget.bottom_right_corner = Some(( - margined_draw_loc.x + margined_draw_loc.width, - margined_draw_loc.y + margined_draw_loc.height, - )); - } - } - } -} diff --git a/src/canvas/drawing_utils.rs b/src/canvas/drawing_utils.rs deleted file mode 100644 index d35f93c2..00000000 --- a/src/canvas/drawing_utils.rs +++ /dev/null @@ -1,211 +0,0 @@ -use std::cmp::{max, min}; - -/// Return a (hard)-width vector for column widths. -/// -/// * `total_width` is the, well, total width available. **NOTE:** This function automatically -/// takes away 2 from the width as part of the left/right -/// bounds. -/// * `hard_widths` is inflexible column widths. Use a `None` to represent a soft width. -/// * `soft_widths_min` is the lower limit for a soft width. Use `None` if a hard width goes there. -/// * `soft_widths_max` is the upper limit for a soft width, in percentage of the total width. Use -/// `None` if a hard width goes there. -/// * `soft_widths_desired` is the desired soft width. Use `None` if a hard width goes there. -/// * `left_to_right` is a boolean whether to go from left to right if true, or right to left if -/// false. -/// -/// **NOTE:** This function ASSUMES THAT ALL PASSED SLICES ARE OF THE SAME SIZE. -/// -/// **NOTE:** The returned vector may not be the same size as the slices, this is because including -/// 0-constraints breaks tui-rs. -pub fn get_column_widths( - total_width: u16, hard_widths: &[Option], soft_widths_min: &[Option], - soft_widths_max: &[Option], soft_widths_desired: &[Option], left_to_right: bool, -) -> Vec { - debug_assert!( - hard_widths.len() == soft_widths_min.len(), - "hard width length != soft width min length!" - ); - debug_assert!( - soft_widths_min.len() == soft_widths_max.len(), - "soft width min length != soft width max length!" - ); - debug_assert!( - soft_widths_max.len() == soft_widths_desired.len(), - "soft width max length != soft width desired length!" - ); - - let initial_width = total_width - 2; - let mut total_width_left = initial_width; - let mut column_widths: Vec = vec![0; hard_widths.len()]; - let range: Vec = if left_to_right { - (0..hard_widths.len()).collect() - } else { - (0..hard_widths.len()).rev().collect() - }; - - for itx in &range { - if let Some(Some(hard_width)) = hard_widths.get(*itx) { - // Hard width... - let space_taken = min(*hard_width, total_width_left); - - // TODO [COLUMN MOVEMENT]: Remove this - if *hard_width > space_taken { - break; - } - - column_widths[*itx] = space_taken; - total_width_left -= space_taken; - total_width_left = total_width_left.saturating_sub(1); - } else if let ( - Some(Some(soft_width_max)), - Some(Some(soft_width_min)), - Some(Some(soft_width_desired)), - ) = ( - soft_widths_max.get(*itx), - soft_widths_min.get(*itx), - soft_widths_desired.get(*itx), - ) { - // Soft width... - let soft_limit = max( - if soft_width_max.is_sign_negative() { - *soft_width_desired - } else { - (*soft_width_max * initial_width as f64).ceil() as u16 - }, - *soft_width_min, - ); - let space_taken = min(min(soft_limit, *soft_width_desired), total_width_left); - - // TODO [COLUMN MOVEMENT]: Remove this - if *soft_width_min > space_taken { - break; - } - - column_widths[*itx] = space_taken; - total_width_left -= space_taken; - total_width_left = total_width_left.saturating_sub(1); - } - } - - // Redistribute remaining. - while total_width_left > 0 { - for itx in &range { - if column_widths[*itx] > 0 { - column_widths[*itx] += 1; - total_width_left -= 1; - if total_width_left == 0 { - break; - } - } - } - } - - let mut filtered_column_widths: Vec = vec![]; - let mut still_seeing_zeros = true; - column_widths.iter().rev().for_each(|width| { - if still_seeing_zeros { - if *width != 0 { - still_seeing_zeros = false; - filtered_column_widths.push(*width); - } - } else { - filtered_column_widths.push(*width); - } - }); - filtered_column_widths.reverse(); - filtered_column_widths -} - -// pub fn get_search_start_position( -// num_columns: usize, cursor_direction: &app::CursorDirection, cursor_bar: &mut usize, -// current_cursor_position: usize, is_force_redraw: bool, -// ) -> usize { -// if is_force_redraw { -// *cursor_bar = 0; -// } - -// match cursor_direction { -// app::CursorDirection::Right => { -// if current_cursor_position < *cursor_bar + num_columns { -// // If, using previous_scrolled_position, we can see the element -// // (so within that and + num_rows) just reuse the current previously scrolled position -// *cursor_bar -// } else if current_cursor_position >= num_columns { -// // Else if the current position past the last element visible in the list, omit -// // until we can see that element -// *cursor_bar = current_cursor_position - num_columns; -// *cursor_bar -// } else { -// // Else, if it is not past the last element visible, do not omit anything -// 0 -// } -// } -// app::CursorDirection::Left => { -// if current_cursor_position <= *cursor_bar { -// // If it's past the first element, then show from that element downwards -// *cursor_bar = current_cursor_position; -// } else if current_cursor_position >= *cursor_bar + num_columns { -// *cursor_bar = current_cursor_position - num_columns; -// } -// // Else, don't change what our start position is from whatever it is set to! -// *cursor_bar -// } -// } -// } - -// pub fn get_start_position( -// num_rows: usize, scroll_direction: &app::ScrollDirection, scroll_position_bar: &mut usize, -// currently_selected_position: usize, is_force_redraw: bool, -// ) -> usize { -// if is_force_redraw { -// *scroll_position_bar = 0; -// } - -// match scroll_direction { -// app::ScrollDirection::Down => { -// if currently_selected_position < *scroll_position_bar + num_rows { -// // If, using previous_scrolled_position, we can see the element -// // (so within that and + num_rows) just reuse the current previously scrolled position -// *scroll_position_bar -// } else if currently_selected_position >= num_rows { -// // Else if the current position past the last element visible in the list, omit -// // until we can see that element -// *scroll_position_bar = currently_selected_position - num_rows; -// *scroll_position_bar -// } else { -// // Else, if it is not past the last element visible, do not omit anything -// 0 -// } -// } -// app::ScrollDirection::Up => { -// if currently_selected_position <= *scroll_position_bar { -// // If it's past the first element, then show from that element downwards -// *scroll_position_bar = currently_selected_position; -// } else if currently_selected_position >= *scroll_position_bar + num_rows { -// *scroll_position_bar = currently_selected_position - num_rows; -// } -// // Else, don't change what our start position is from whatever it is set to! -// *scroll_position_bar -// } -// } -// } - -/// Calculate how many bars are to be -/// drawn within basic mode's components. -pub fn calculate_basic_use_bars(use_percentage: f64, num_bars_available: usize) -> usize { - std::cmp::min( - (num_bars_available as f64 * use_percentage / 100.0).round() as usize, - num_bars_available, - ) -} - -/// Interpolates between two points. Mainly used to help fill in tui-rs blanks in certain situations. -/// It is expected point_one is "further left" compared to point_two. -/// A point is two floats, in (x, y) form. x is time, y is value. -pub fn interpolate_points(point_one: &(f64, f64), point_two: &(f64, f64), time: f64) -> f64 { - let delta_x = point_two.0 - point_one.0; - let delta_y = point_two.1 - point_one.1; - let slope = delta_y / delta_x; - - (point_one.1 + (time - point_one.0) * slope).max(0.0) -} diff --git a/src/data_conversion.rs b/src/data_conversion.rs index 6a310f59..d75d98cc 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -739,7 +739,7 @@ const BRANCH_VERTICAL: char = '│'; const BRANCH_SPLIT: char = '├'; const BRANCH_HORIZONTAL: char = '─'; -pub fn tree_process_data( +fn tree_process_data( filtered_process_data: &[ConvertedProcessData], is_using_command: bool, sorting_type: &ProcessSorting, is_sort_descending: bool, ) -> Vec { @@ -1207,7 +1207,7 @@ pub fn tree_process_data( } // FIXME: [OPT] This is an easy target for optimization, too many to_strings! -pub fn stringify_process_data( +fn stringify_process_data( proc_widget_state: &ProcWidgetState, finalized_process_data: &[ConvertedProcessData], ) -> Vec<(Vec<(String, Option)>, bool)> { let is_proc_widget_grouped = proc_widget_state.is_grouped; @@ -1282,7 +1282,7 @@ pub fn stringify_process_data( /// Takes a set of converted process data and groups it together. /// /// To be honest, I really don't like how this is done, even though I've rewritten this like 3 times. -pub fn group_process_data( +fn group_process_data( single_process_data: &[ConvertedProcessData], is_using_command: bool, ) -> Vec { #[derive(Clone, Default, Debug)] diff --git a/src/lib.rs b/src/lib.rs index 9523ddc7..4716881c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ use std::{ use crossterm::{ event::{ - read, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, + read, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind, }, execute, @@ -30,13 +30,12 @@ use crossterm::{ }; use app::{ - data_harvester::{self, processes::ProcessSorting}, + data_harvester::{self}, event::EventResult, layout_manager::WidgetDirection, AppState, UsedWidgets, }; use constants::*; -use data_conversion::*; use options::*; use utils::error; @@ -76,24 +75,6 @@ pub enum ThreadControlEvent { UpdateUpdateTime(u64), } -pub fn handle_mouse_event(event: MouseEvent, app: &mut AppState) -> EventResult { - match event.kind { - MouseEventKind::Down(MouseButton::Left) => { - app.on_left_mouse_up(event.column, event.row); - EventResult::Redraw - } - MouseEventKind::ScrollUp => { - app.handle_scroll_up(); - EventResult::Redraw - } - MouseEventKind::ScrollDown => { - app.handle_scroll_down(); - EventResult::Redraw - } - _ => EventResult::NoRedraw, - } -} - pub fn handle_key_event( event: KeyEvent, app: &mut AppState, reset_sender: &std::sync::mpsc::Sender, ) -> EventResult { @@ -309,296 +290,6 @@ pub fn panic_hook(panic_info: &PanicInfo<'_>) { .unwrap(); } -pub fn force_redraw(app: &mut AppState) { - // Currently we use an Option... because we might want to future-proof this - // if we eventually get widget-specific redrawing! - if app.proc_state.force_update_all { - update_all_process_lists(app); - app.proc_state.force_update_all = false; - } else if let Some(widget_id) = app.proc_state.force_update { - update_final_process_list(app, widget_id); - app.proc_state.force_update = None; - } - - if app.cpu_state.force_update.is_some() { - convert_cpu_data_points( - &app.data_collection, - &mut app.canvas_data.cpu_data, - app.is_frozen, - ); - app.canvas_data.load_avg_data = app.data_collection.load_avg_harvest; - app.cpu_state.force_update = None; - } - - // FIXME: [OPT] Prefer reassignment over new vectors? - if app.mem_state.force_update.is_some() { - app.canvas_data.mem_data = convert_mem_data_points(&app.data_collection, app.is_frozen); - app.canvas_data.swap_data = convert_swap_data_points(&app.data_collection, app.is_frozen); - app.mem_state.force_update = None; - } - - if app.net_state.force_update.is_some() { - let (rx, tx) = get_rx_tx_data_points( - &app.data_collection, - app.is_frozen, - &app.app_config_fields.network_scale_type, - &app.app_config_fields.network_unit_type, - app.app_config_fields.network_use_binary_prefix, - ); - app.canvas_data.network_data_rx = rx; - app.canvas_data.network_data_tx = tx; - app.net_state.force_update = None; - } -} - -#[allow(clippy::needless_collect)] -pub fn update_all_process_lists(app: &mut AppState) { - // According to clippy, I can avoid a collect... but if I follow it, - // I end up conflicting with the borrow checker since app is used within the closure... hm. - if !app.is_frozen { - let widget_ids = app - .proc_state - .widget_states - .keys() - .cloned() - .collect::>(); - - widget_ids.into_iter().for_each(|widget_id| { - update_final_process_list(app, widget_id); - }); - } -} - -fn update_final_process_list(_app: &mut AppState, _widget_id: u64) { - // TODO: [STATE] FINISH THIS - // let process_states = app - // .proc_state - // .widget_states - // .get(&widget_id) - // .map(|process_state| { - // ( - // process_state - // .process_search_state - // .search_state - // .is_invalid_or_blank_search(), - // process_state.is_using_command, - // process_state.is_grouped, - // process_state.is_tree_mode, - // ) - // }); - - // if let Some((is_invalid_or_blank, is_using_command, is_grouped, is_tree)) = process_states { - // if !app.is_frozen { - // convert_process_data( - // &app.data_collection, - // &mut app.canvas_data.single_process_data, - // #[cfg(target_family = "unix")] - // &mut app.user_table, - // ); - // } - // let process_filter = app.get_process_filter(widget_id); - // let filtered_process_data: Vec = if is_tree { - // app.canvas_data - // .single_process_data - // .iter() - // .map(|(_pid, process)| { - // let mut process_clone = process.clone(); - // if !is_invalid_or_blank { - // if let Some(process_filter) = process_filter { - // process_clone.is_disabled_entry = - // !process_filter.check(&process_clone, is_using_command); - // } - // } - // process_clone - // }) - // .collect::>() - // } else { - // app.canvas_data - // .single_process_data - // .iter() - // .filter_map(|(_pid, process)| { - // if !is_invalid_or_blank { - // if let Some(process_filter) = process_filter { - // if process_filter.check(process, is_using_command) { - // Some(process) - // } else { - // None - // } - // } else { - // Some(process) - // } - // } else { - // Some(process) - // } - // }) - // .cloned() - // .collect::>() - // }; - - // if let Some(proc_widget_state) = app.proc_state.get_mut_widget_state(widget_id) { - // let mut finalized_process_data = if is_tree { - // tree_process_data( - // &filtered_process_data, - // is_using_command, - // &proc_widget_state.process_sorting_type, - // proc_widget_state.is_process_sort_descending, - // ) - // } else if is_grouped { - // group_process_data(&filtered_process_data, is_using_command) - // } else { - // filtered_process_data - // }; - - // // Note tree mode is sorted well before this, as it's special. - // if !is_tree { - // sort_process_data(&mut finalized_process_data, proc_widget_state); - // } - - // if proc_widget_state.scroll_state.current_scroll_position - // >= finalized_process_data.len() - // { - // proc_widget_state.scroll_state.current_scroll_position = - // finalized_process_data.len().saturating_sub(1); - // proc_widget_state.scroll_state.previous_scroll_position = 0; - // proc_widget_state.scroll_state.scroll_direction = app::ScrollDirection::Down; - // } - - // app.canvas_data.stringified_process_data_map.insert( - // widget_id, - // stringify_process_data(proc_widget_state, &finalized_process_data), - // ); - // app.canvas_data - // .finalized_process_data_map - // .insert(widget_id, finalized_process_data); - // } - // } -} - -fn _sort_process_data( - to_sort_vec: &mut Vec, proc_widget_state: &app::ProcWidgetState, -) { - to_sort_vec.sort_by_cached_key(|c| c.name.to_lowercase()); - - match &proc_widget_state.process_sorting_type { - ProcessSorting::CpuPercent => { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.cpu_percent_usage, - b.cpu_percent_usage, - proc_widget_state.is_process_sort_descending, - ) - }); - } - ProcessSorting::Mem => { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.mem_usage_bytes, - b.mem_usage_bytes, - proc_widget_state.is_process_sort_descending, - ) - }); - } - ProcessSorting::MemPercent => { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.mem_percent_usage, - b.mem_percent_usage, - proc_widget_state.is_process_sort_descending, - ) - }); - } - ProcessSorting::ProcessName => { - // Don't repeat if false... it sorts by name by default anyways. - if proc_widget_state.is_process_sort_descending { - to_sort_vec.sort_by_cached_key(|c| c.name.to_lowercase()); - if proc_widget_state.is_process_sort_descending { - to_sort_vec.reverse(); - } - } - } - ProcessSorting::Command => { - to_sort_vec.sort_by_cached_key(|c| c.command.to_lowercase()); - if proc_widget_state.is_process_sort_descending { - to_sort_vec.reverse(); - } - } - ProcessSorting::Pid => { - if !proc_widget_state.is_grouped { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.pid, - b.pid, - proc_widget_state.is_process_sort_descending, - ) - }); - } - } - ProcessSorting::ReadPerSecond => { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.rps_f64, - b.rps_f64, - proc_widget_state.is_process_sort_descending, - ) - }); - } - ProcessSorting::WritePerSecond => { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.wps_f64, - b.wps_f64, - proc_widget_state.is_process_sort_descending, - ) - }); - } - ProcessSorting::TotalRead => { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.tr_f64, - b.tr_f64, - proc_widget_state.is_process_sort_descending, - ) - }); - } - ProcessSorting::TotalWrite => { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.tw_f64, - b.tw_f64, - proc_widget_state.is_process_sort_descending, - ) - }); - } - ProcessSorting::State => { - to_sort_vec.sort_by_cached_key(|c| c.process_state.to_lowercase()); - if proc_widget_state.is_process_sort_descending { - to_sort_vec.reverse(); - } - } - ProcessSorting::User => to_sort_vec.sort_by(|a, b| match (&a.user, &b.user) { - (Some(user_a), Some(user_b)) => utils::gen_util::get_ordering( - user_a.to_lowercase(), - user_b.to_lowercase(), - proc_widget_state.is_process_sort_descending, - ), - (Some(_), None) => std::cmp::Ordering::Less, - (None, Some(_)) => std::cmp::Ordering::Greater, - (None, None) => std::cmp::Ordering::Less, - }), - ProcessSorting::Count => { - if proc_widget_state.is_grouped { - to_sort_vec.sort_by(|a, b| { - utils::gen_util::get_ordering( - a.group_pids.len(), - b.group_pids.len(), - proc_widget_state.is_process_sort_descending, - ) - }); - } - } - } -} - pub fn create_input_thread( sender: std::sync::mpsc::Sender, termination_ctrl_lock: Arc>, ) -> std::thread::JoinHandle<()> { diff --git a/src/options.rs b/src/options.rs index a3a5237d..750afbd8 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,6 +1,6 @@ use regex::Regex; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, str::FromStr}; +use std::str::FromStr; use crate::{ app::{layout_manager::*, *}, @@ -105,18 +105,6 @@ pub struct WidgetIdEnabled { enabled: bool, } -impl WidgetIdEnabled { - pub fn create_from_hashmap(hashmap: &HashMap) -> Vec { - hashmap - .iter() - .map(|(id, enabled)| WidgetIdEnabled { - id: *id, - enabled: *enabled, - }) - .collect() - } -} - #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ConfigColours { pub table_header_color: Option, @@ -275,27 +263,6 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re net_filter, }; - // Ok(AppState::builder() - // .app_config_fields(app_config_fields) - // .cpu_state(CpuState::init(cpu_state_map)) - // .mem_state(MemState::init(mem_state_map)) - // .net_state(NetState::init(net_state_map)) - // .proc_state(ProcState::init(proc_state_map)) - // .disk_state(DiskState::init(disk_state_map)) - // .temp_state(TempState::init(temp_state_map)) - // .battery_state(BatteryState::init(battery_state_map)) - // .basic_table_widget_state(basic_table_widget_state) - // .current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // TODO: [UNWRAP] - many of the unwraps are fine (like this one) but do a once-over and/or switch to expect? - // .widget_map(widget_map) - // .used_widgets(used_widgets) - // .filters(DataFilters { - // disk_filter, - // mount_filter, - // temp_filter, - // net_filter, - // }) - // .build()) - Ok(AppState::new( app_config_fields, data_filter, @@ -303,71 +270,6 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re )) } -// pub fn get_widget_layout( -// matches: &clap::ArgMatches<'static>, config: &Config, -// ) -> error::Result<(BottomLayout, u64, Option)> { -// let left_legend = get_use_left_legend(matches, config); -// let (default_widget_type, mut default_widget_count) = -// get_default_widget_and_count(matches, config)?; -// let mut default_widget_id = 1; - -// let bottom_layout = if get_use_basic_mode(matches, config) { -// default_widget_id = DEFAULT_WIDGET_ID; - -// BottomLayout::init_basic_default(get_use_battery(matches, config)) -// } else { -// let ref_row: Vec; // Required to handle reference -// let rows = match &config.row { -// Some(r) => r, -// None => { -// // This cannot (like it really shouldn't) fail! -// ref_row = toml::from_str::(if get_use_battery(matches, config) { -// DEFAULT_BATTERY_LAYOUT -// } else { -// DEFAULT_LAYOUT -// })? -// .row -// .unwrap(); -// &ref_row -// } -// }; - -// let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs* -// let mut total_height_ratio = 0; - -// let mut ret_bottom_layout = BottomLayout { -// rows: rows -// .iter() -// .map(|row| { -// row.convert_row_to_bottom_row( -// &mut iter_id, -// &mut total_height_ratio, -// &mut default_widget_id, -// &default_widget_type, -// &mut default_widget_count, -// left_legend, -// ) -// }) -// .collect::>>()?, -// total_row_height_ratio: total_height_ratio, -// }; - -// // Confirm that we have at least ONE widget left - if not, error out! -// if iter_id > 0 { -// ret_bottom_layout.get_movement_mappings(); -// // debug!("Bottom layout: {:#?}", ret_bottom_layout); - -// ret_bottom_layout -// } else { -// return Err(error::BottomError::ConfigError( -// "please have at least one widget under the '[[row]]' section.".to_string(), -// )); -// } -// }; - -// Ok((bottom_layout, default_widget_id, default_widget_type)) -// } - fn get_update_rate_in_milliseconds( matches: &clap::ArgMatches<'static>, config: &Config, ) -> error::Result { @@ -605,59 +507,6 @@ fn get_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bo false } -fn get_default_widget_and_count( - matches: &clap::ArgMatches<'static>, config: &Config, -) -> error::Result<(Option, u64)> { - let widget_type = if let Some(widget_type) = matches.value_of("default_widget_type") { - let parsed_widget = widget_type.parse::()?; - if let BottomWidgetType::Empty = parsed_widget { - None - } else { - Some(parsed_widget) - } - } else if let Some(flags) = &config.flags { - if let Some(widget_type) = &flags.default_widget_type { - let parsed_widget = widget_type.parse::()?; - if let BottomWidgetType::Empty = parsed_widget { - None - } else { - Some(parsed_widget) - } - } else { - None - } - } else { - None - }; - - let widget_count = if let Some(widget_count) = matches.value_of("default_widget_count") { - Some(widget_count.parse::()?) - } else if let Some(flags) = &config.flags { - flags - .default_widget_count - .map(|widget_count| widget_count as u128) - } else { - None - }; - - match (widget_type, widget_count) { - (Some(widget_type), Some(widget_count)) => { - if widget_count > std::u64::MAX as u128 { - Err(BottomError::ConfigError( - "set your widget count to be at most unsigned INT_MAX.".to_string(), - )) - } else { - Ok((Some(widget_type), widget_count as u64)) - } - } - (Some(widget_type), None) => Ok((Some(widget_type), 1)), - (None, Some(_widget_count)) => Err(BottomError::ConfigError( - "cannot set 'default_widget_count' by itself, it must be used with 'default_widget_type'.".to_string(), - )), - (None, None) => Ok((None, 1)) - } -} - fn get_disable_click(matches: &clap::ArgMatches<'static>, config: &Config) -> bool { if matches.is_present("disable_click") { return true; diff --git a/src/options/layout_options.rs b/src/options/layout_options.rs index a590f41d..42a4f8de 100644 --- a/src/options/layout_options.rs +++ b/src/options/layout_options.rs @@ -9,328 +9,6 @@ pub struct Row { pub ratio: Option, } -// impl Row { -// pub fn convert_row_to_bottom_row( -// &self, iter_id: &mut u64, total_height_ratio: &mut u32, default_widget_id: &mut u64, -// default_widget_type: &Option, default_widget_count: &mut u64, -// left_legend: bool, -// ) -> Result { -// // TODO: In the future we want to also add percentages. -// // But for MVP, we aren't going to bother. -// let row_ratio = self.ratio.unwrap_or(1); -// let mut children = Vec::new(); - -// *total_height_ratio += row_ratio; - -// let mut total_col_ratio = 0; -// if let Some(row_children) = &self.child { -// for row_child in row_children { -// match row_child { -// RowChildren::Widget(widget) => { -// *iter_id += 1; -// let width_ratio = widget.ratio.unwrap_or(1); -// total_col_ratio += width_ratio; -// let widget_type = widget.widget_type.parse::()?; - -// if let Some(default_widget_type_val) = default_widget_type { -// if *default_widget_type_val == widget_type && *default_widget_count > 0 -// { -// *default_widget_count -= 1; -// if *default_widget_count == 0 { -// *default_widget_id = *iter_id; -// } -// } -// } else { -// // Check default flag -// if let Some(default_widget_flag) = widget.default { -// if default_widget_flag { -// *default_widget_id = *iter_id; -// } -// } -// } - -// children.push(match widget_type { -// BottomWidgetType::Cpu => { -// let cpu_id = *iter_id; -// *iter_id += 1; -// OldBottomCol::builder() -// .col_width_ratio(width_ratio) -// .children(if left_legend { -// vec![BottomColRow::builder() -// .total_widget_ratio(20) -// .children(vec![ -// BottomWidget::builder() -// .width_ratio(3) -// .widget_type(BottomWidgetType::CpuLegend) -// .widget_id(*iter_id) -// .canvas_handle_width(true) -// .parent_reflector(Some(( -// WidgetDirection::Right, -// 1, -// ))) -// .build(), -// BottomWidget::builder() -// .width_ratio(17) -// .widget_type(BottomWidgetType::Cpu) -// .widget_id(cpu_id) -// .flex_grow(true) -// .build(), -// ]) -// .build()] -// } else { -// vec![BottomColRow::builder() -// .total_widget_ratio(20) -// .children(vec![ -// BottomWidget::builder() -// .width_ratio(17) -// .widget_type(BottomWidgetType::Cpu) -// .widget_id(cpu_id) -// .flex_grow(true) -// .build(), -// BottomWidget::builder() -// .width_ratio(3) -// .widget_type(BottomWidgetType::CpuLegend) -// .widget_id(*iter_id) -// .canvas_handle_width(true) -// .parent_reflector(Some(( -// WidgetDirection::Left, -// 1, -// ))) -// .build(), -// ]) -// .build()] -// }) -// .build() -// } -// BottomWidgetType::Proc => { -// let proc_id = *iter_id; -// let proc_search_id = *iter_id + 1; -// *iter_id += 2; -// OldBottomCol::builder() -// .total_col_row_ratio(2) -// .col_width_ratio(width_ratio) -// .children(vec![ -// BottomColRow::builder() -// .children(vec![ -// BottomWidget::builder() -// .widget_type(BottomWidgetType::ProcSort) -// .widget_id(*iter_id) -// .canvas_handle_width(true) -// .parent_reflector(Some(( -// WidgetDirection::Right, -// 2, -// ))) -// .width_ratio(1) -// .build(), -// BottomWidget::builder() -// .widget_type(BottomWidgetType::Proc) -// .widget_id(proc_id) -// .width_ratio(2) -// .build(), -// ]) -// .total_widget_ratio(3) -// .flex_grow(true) -// .build(), -// BottomColRow::builder() -// .children(vec![BottomWidget::builder() -// .widget_type(BottomWidgetType::ProcSearch) -// .widget_id(proc_search_id) -// .parent_reflector(Some((WidgetDirection::Up, 1))) -// .build()]) -// .canvas_handle_height(true) -// .build(), -// ]) -// .build() -// } -// _ => OldBottomCol::builder() -// .col_width_ratio(width_ratio) -// .children(vec![BottomColRow::builder() -// .children(vec![BottomWidget::builder() -// .widget_type(widget_type) -// .widget_id(*iter_id) -// .build()]) -// .build()]) -// .build(), -// }); -// } -// RowChildren::Col { ratio, child } => { -// let col_width_ratio = ratio.unwrap_or(1); -// total_col_ratio += col_width_ratio; -// let mut total_col_row_ratio = 0; -// let mut contains_proc = false; - -// let mut col_row_children: Vec = Vec::new(); - -// for widget in child { -// let widget_type = widget.widget_type.parse::()?; -// *iter_id += 1; -// let col_row_height_ratio = widget.ratio.unwrap_or(1); -// total_col_row_ratio += col_row_height_ratio; - -// if let Some(default_widget_type_val) = default_widget_type { -// if *default_widget_type_val == widget_type -// && *default_widget_count > 0 -// { -// *default_widget_count -= 1; -// if *default_widget_count == 0 { -// *default_widget_id = *iter_id; -// } -// } -// } else { -// // Check default flag -// if let Some(default_widget_flag) = widget.default { -// if default_widget_flag { -// *default_widget_id = *iter_id; -// } -// } -// } - -// match widget_type { -// BottomWidgetType::Cpu => { -// let cpu_id = *iter_id; -// *iter_id += 1; -// if left_legend { -// col_row_children.push( -// BottomColRow::builder() -// .col_row_height_ratio(col_row_height_ratio) -// .total_widget_ratio(20) -// .children(vec![ -// BottomWidget::builder() -// .width_ratio(3) -// .widget_type(BottomWidgetType::CpuLegend) -// .widget_id(*iter_id) -// .canvas_handle_width(true) -// .parent_reflector(Some(( -// WidgetDirection::Right, -// 1, -// ))) -// .build(), -// BottomWidget::builder() -// .width_ratio(17) -// .widget_type(BottomWidgetType::Cpu) -// .widget_id(cpu_id) -// .flex_grow(true) -// .build(), -// ]) -// .build(), -// ); -// } else { -// col_row_children.push( -// BottomColRow::builder() -// .col_row_height_ratio(col_row_height_ratio) -// .total_widget_ratio(20) -// .children(vec![ -// BottomWidget::builder() -// .width_ratio(17) -// .widget_type(BottomWidgetType::Cpu) -// .widget_id(cpu_id) -// .flex_grow(true) -// .build(), -// BottomWidget::builder() -// .width_ratio(3) -// .widget_type(BottomWidgetType::CpuLegend) -// .widget_id(*iter_id) -// .canvas_handle_width(true) -// .parent_reflector(Some(( -// WidgetDirection::Left, -// 1, -// ))) -// .build(), -// ]) -// .build(), -// ); -// } -// } -// BottomWidgetType::Proc => { -// contains_proc = true; -// let proc_id = *iter_id; -// let proc_search_id = *iter_id + 1; -// *iter_id += 2; -// col_row_children.push( -// BottomColRow::builder() -// .children(vec![ -// BottomWidget::builder() -// .widget_type(BottomWidgetType::ProcSort) -// .widget_id(*iter_id) -// .canvas_handle_width(true) -// .parent_reflector(Some(( -// WidgetDirection::Right, -// 2, -// ))) -// .width_ratio(1) -// .build(), -// BottomWidget::builder() -// .widget_type(BottomWidgetType::Proc) -// .widget_id(proc_id) -// .width_ratio(2) -// .build(), -// ]) -// .col_row_height_ratio(col_row_height_ratio) -// .total_widget_ratio(3) -// .build(), -// ); -// col_row_children.push( -// BottomColRow::builder() -// .col_row_height_ratio(col_row_height_ratio) -// .children(vec![BottomWidget::builder() -// .widget_type(BottomWidgetType::ProcSearch) -// .widget_id(proc_search_id) -// .parent_reflector(Some((WidgetDirection::Up, 1))) -// .build()]) -// .canvas_handle_height(true) -// .build(), -// ); -// } -// _ => col_row_children.push( -// BottomColRow::builder() -// .col_row_height_ratio(col_row_height_ratio) -// .children(vec![BottomWidget::builder() -// .widget_type(widget_type) -// .widget_id(*iter_id) -// .build()]) -// .build(), -// ), -// } -// } - -// if contains_proc { -// // Must adjust ratios to work with proc -// total_col_row_ratio *= 2; -// for child in &mut col_row_children { -// // Multiply all non-proc or proc-search ratios by 2 -// if !child.children.is_empty() { -// match child.children[0].widget_type { -// BottomWidgetType::ProcSearch => {} -// _ => child.col_row_height_ratio *= 2, -// } -// } -// } -// } - -// children.push( -// OldBottomCol::builder() -// .total_col_row_ratio(total_col_row_ratio) -// .col_width_ratio(col_width_ratio) -// .children(col_row_children) -// .build(), -// ); -// } -// RowChildren::Carousel { -// carousel_children: _, -// default: _, -// } => {} -// } -// } -// } - -// Ok(OldBottomRow::builder() -// .total_col_ratio(total_col_ratio) -// .row_height_ratio(row_ratio) -// .children(children) -// .build()) -// } -// } - /// Represents a child of a Row - either a Col (column) or a FinalWidget. /// /// A Col can also have an optional length and children. We only allow columns