diff --git a/src/app.rs b/src/app.rs index 5df1aa9d..d139f60f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -217,26 +217,25 @@ impl App { } else { match self.current_widget.widget_type { BottomWidgetType::Proc => { - if let Some(current_proc_state) = self + if let Some(pws) = self .proc_state .get_mut_widget_state(self.current_widget.widget_id) { - if current_proc_state.is_search_enabled() || current_proc_state.is_sort_open - { - current_proc_state.proc_search.search_state.is_enabled = false; - current_proc_state.is_sort_open = false; + if pws.is_search_enabled() || pws.is_sort_open { + pws.proc_search.search_state.is_enabled = false; + pws.is_sort_open = false; self.is_force_redraw = true; return; } } } BottomWidgetType::ProcSearch => { - if let Some(current_proc_state) = self + if let Some(pws) = self .proc_state .get_mut_widget_state(self.current_widget.widget_id - 1) { - if current_proc_state.is_search_enabled() { - current_proc_state.proc_search.search_state.is_enabled = false; + if pws.is_search_enabled() { + pws.proc_search.search_state.is_enabled = false; self.move_widget_selection(&WidgetDirection::Up); self.is_force_redraw = true; return; @@ -244,12 +243,12 @@ impl App { } } BottomWidgetType::ProcSort => { - if let Some(current_proc_state) = self + if let Some(pws) = self .proc_state .get_mut_widget_state(self.current_widget.widget_id - 2) { - if current_proc_state.is_sort_open { - current_proc_state.is_sort_open = false; + if pws.is_sort_open { + pws.is_sort_open = false; self.move_widget_selection(&WidgetDirection::Right); self.is_force_redraw = true; return; @@ -345,11 +344,12 @@ impl App { if let Some(pws) = self.proc_state.get_mut_widget_state(widget_id) { pws.is_sort_open = !pws.is_sort_open; - pws.force_update = true; + pws.force_rerender = true; // If the sort is now open, move left. Otherwise, if the proc sort was selected, force move right. if pws.is_sort_open { if let SortState::Sortable(st) = &pws.table_state.sort_state { + pws.sort_table_state.scroll_bar = 0; pws.sort_table_state.current_scroll_position = st .current_index .clamp(0, pws.num_enabled_columns().saturating_sub(1)); @@ -358,6 +358,7 @@ impl App { } else if let BottomWidgetType::ProcSort = self.current_widget.widget_type { self.move_widget_selection(&WidgetDirection::Right); } + self.is_force_redraw = true; } } @@ -376,7 +377,7 @@ impl App { &mut proc_widget_state.table_state.sort_state { state.toggle_order(); - proc_widget_state.force_update = true; + proc_widget_state.force_data_update(); } } } @@ -396,7 +397,7 @@ impl App { .get_mut(&self.current_widget.widget_id) { proc_widget_state.toggle_mem_percentage(); - proc_widget_state.force_update = true; + proc_widget_state.force_data_update(); } } _ => {} @@ -414,7 +415,6 @@ impl App { if is_in_search_widget && proc_widget_state.is_search_enabled() { proc_widget_state.proc_search.search_toggle_ignore_case(); proc_widget_state.update_query(); - proc_widget_state.force_update = true; // Remember, it's the opposite (ignoring case is case "in"sensitive) is_case_sensitive = Some(!proc_widget_state.proc_search.is_ignoring_case); @@ -465,7 +465,6 @@ impl App { if is_in_search_widget && proc_widget_state.is_search_enabled() { proc_widget_state.proc_search.search_toggle_whole_word(); proc_widget_state.update_query(); - proc_widget_state.force_update = true; is_searching_whole_word = Some(proc_widget_state.proc_search.is_searching_whole_word); @@ -520,7 +519,6 @@ impl App { if is_in_search_widget && proc_widget_state.is_search_enabled() { proc_widget_state.proc_search.search_toggle_regex(); proc_widget_state.update_query(); - proc_widget_state.force_update = true; is_searching_with_regex = Some(proc_widget_state.proc_search.is_searching_with_regex); @@ -569,13 +567,13 @@ impl App { match proc_widget_state.mode { ProcWidgetMode::Tree { .. } => { proc_widget_state.mode = ProcWidgetMode::Normal; - proc_widget_state.force_update = true; + proc_widget_state.force_rerender_and_update(); } ProcWidgetMode::Normal => { proc_widget_state.mode = ProcWidgetMode::Tree { collapsed_pids: Default::default(), }; - proc_widget_state.force_update = true; + proc_widget_state.force_rerender_and_update(); } ProcWidgetMode::Grouped => {} } @@ -617,6 +615,7 @@ impl App { { proc_widget_state.use_sort_table_value(); self.move_widget_selection(&WidgetDirection::Right); + self.is_force_redraw = true; } } } @@ -662,7 +661,6 @@ impl App { ); proc_widget_state.update_query(); - proc_widget_state.force_update = true; } } else { self.start_killing_process() @@ -714,7 +712,6 @@ impl App { CursorDirection::Left; proc_widget_state.update_query(); - proc_widget_state.force_update = true; } } } @@ -1077,7 +1074,6 @@ impl App { .get_mut(&(self.current_widget.widget_id - 1)) { proc_widget_state.clear_search(); - proc_widget_state.force_update = true; } } } @@ -1141,7 +1137,6 @@ impl App { proc_widget_state.proc_search.search_state.cursor_direction = CursorDirection::Left; proc_widget_state.update_query(); - proc_widget_state.force_update = true; } } } @@ -1245,7 +1240,6 @@ impl App { UnicodeWidthChar::width(caught_char).unwrap_or(0); proc_widget_state.update_query(); - proc_widget_state.force_update = true; proc_widget_state.proc_search.search_state.cursor_direction = CursorDirection::Right; @@ -2555,7 +2549,6 @@ impl App { 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 @@ -2716,7 +2709,7 @@ impl App { &mut proc_widget_state.table_state.sort_state { if st.try_select_location(x, y).is_some() { - proc_widget_state.force_update = true; + proc_widget_state.force_data_update(); } } } diff --git a/src/app/states/table_state.rs b/src/app/states/table_state.rs index fea36464..c6ce3b46 100644 --- a/src/app/states/table_state.rs +++ b/src/app/states/table_state.rs @@ -400,20 +400,18 @@ impl TableComponentState { continue; } - let space_taken = min( - max( - if let Some(max_percentage) = max_percentage { - // TODO: Rust doesn't have an `into()` or `try_into()` for floats to integers. - ((*max_percentage * f32::from(total_width)).ceil()) as u16 - } else { - *desired - }, - min_width, - ), - total_width_left, + let soft_limit = max( + if let Some(max_percentage) = max_percentage { + // TODO: Rust doesn't have an `into()` or `try_into()` for floats to integers. + ((*max_percentage * f32::from(total_width)).ceil()) as u16 + } else { + *desired + }, + min_width, ); + let space_taken = min(min(soft_limit, *desired), total_width_left); - if min_width == 0 { + if min_width > space_taken || min_width == 0 { skip_iter = true; } else if space_taken > 0 { total_width_left = total_width_left.saturating_sub(space_taken + 1); @@ -452,6 +450,7 @@ impl TableComponentState { let mut num_dist = num_columns; let amount_per_slot = total_width_left / num_dist; total_width_left %= num_dist; + for column in self.columns.iter_mut() { if num_dist == 0 { break; @@ -607,7 +606,7 @@ mod test { test_calculation(&mut state, 8, vec![1, 1, 4]); test_calculation(&mut state, 14, vec![2, 2, 7]); test_calculation(&mut state, 20, vec![2, 4, 11]); - test_calculation(&mut state, 100, vec![12, 24, 61]); + test_calculation(&mut state, 100, vec![27, 35, 35]); state.sort_state = SortState::Sortable(SortableState::new(1, SortOrder::Ascending, vec![])); @@ -622,7 +621,7 @@ mod test { test_calculation(&mut state, 8, vec![3, 3]); test_calculation(&mut state, 14, vec![2, 2, 7]); test_calculation(&mut state, 20, vec![3, 4, 10]); - test_calculation(&mut state, 100, vec![13, 24, 60]); + test_calculation(&mut state, 100, vec![27, 35, 35]); } #[test] diff --git a/src/app/widgets/process_widget.rs b/src/app/widgets/process_widget.rs index dcd1503c..86a8c25e 100644 --- a/src/app/widgets/process_widget.rs +++ b/src/app/widgets/process_widget.rs @@ -311,7 +311,8 @@ pub struct ProcWidget { pub sort_table_state: TableComponentState, pub is_sort_open: bool, - pub force_update: bool, + pub force_rerender: bool, + pub force_update_data: bool, pub table_data: TableData, } @@ -396,7 +397,8 @@ impl ProcWidget { sort_table_state, is_sort_open: false, mode, - force_update: false, + force_rerender: true, + force_update_data: false, table_data: TableData::default(), } } @@ -418,7 +420,6 @@ impl ProcWidget { /// This function *only* updates the displayed process data. If there is a need to update the actual *stored* data, /// call it before this function. pub fn update_displayed_process_data(&mut self, data_collection: &DataCollection) { - // Now update everything else. let search_query = if self.proc_search.search_state.is_invalid_or_blank_search() { &None } else { @@ -821,14 +822,7 @@ impl ProcWidget { let cmd_pid_map = &data_collection.process_data.cmd_pid_map; let name_pid_map = &data_collection.process_data.name_pid_map; - let mut col_widths = vec![ - 0; - self.table_state - .columns - .iter() - .filter(|c| c.is_skipped()) - .count() - ]; + let mut col_widths = vec![0; self.table_state.columns.len()]; let data = process_data .iter() @@ -858,10 +852,23 @@ impl ProcWidget { if let Some(ProcWidgetColumn::Memory { show_percentage }) = self.get_mut_proc_col(Self::MEM) { *show_percentage = !*show_percentage; - self.force_update = true; + self.force_data_update(); } } + /// Forces an update of the data stored. + #[inline] + pub fn force_data_update(&mut self) { + self.force_update_data = true; + } + + /// Forces an entire rerender and update of the data stored. + #[inline] + pub fn force_rerender_and_update(&mut self) { + self.force_rerender = true; + self.force_update_data = true; + } + /// Marks the selected column as hidden, and automatically resets the selected column if currently selected. fn hide_column(&mut self, index: usize) { if let Some(col) = self.table_state.columns.get_mut(index) { @@ -887,7 +894,7 @@ impl ProcWidget { pub fn select_column(&mut self, new_sort_index: usize) { if let SortState::Sortable(state) = &mut self.table_state.sort_state { state.update_sort_index(new_sort_index); - self.force_update = true; + self.force_data_update(); } } @@ -902,7 +909,7 @@ impl ProcWidget { if !collapsed_pids.remove(&pid) { collapsed_pids.insert(pid); } - self.force_update = true; + self.force_data_update(); } } } @@ -924,7 +931,7 @@ impl ProcWidget { } } - self.force_update = true; + self.force_rerender_and_update(); } } } @@ -958,7 +965,7 @@ impl ProcWidget { self.show_column(Self::STATE); self.mode = ProcWidgetMode::Normal; } - self.force_update = true; + self.force_rerender_and_update(); } } } @@ -1011,10 +1018,13 @@ impl ProcWidget { } self.table_state.scroll_bar = 0; self.table_state.current_scroll_position = 0; + + self.force_data_update(); } pub fn clear_search(&mut self) { self.proc_search.search_state.reset(); + self.force_data_update(); } pub fn search_walk_forward(&mut self, start_position: usize) { @@ -1064,7 +1074,7 @@ impl ProcWidget { st.update_sort_index(self.sort_table_state.current_scroll_position); self.is_sort_open = false; - self.force_update = true; + self.force_rerender_and_update(); } } } diff --git a/src/bin/main.rs b/src/bin/main.rs index 6ae223eb..e3a86315 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -130,11 +130,11 @@ fn main() -> Result<()> { if handle_key_event_or_break(event, &mut app, &collection_thread_ctrl_sender) { break; } - handle_force_redraws(&mut app); + update_data(&mut app); } BottomEvent::MouseInput(event) => { handle_mouse_event(event, &mut app); - handle_force_redraws(&mut app); + update_data(&mut app); } BottomEvent::Update(data) => { app.data_collection.eat_data(data); @@ -207,7 +207,7 @@ fn main() -> Result<()> { // Processes if app.used_widgets.use_proc { for proc in app.proc_state.widget_states.values_mut() { - proc.force_update = true; + proc.force_data_update(); } } @@ -220,7 +220,7 @@ fn main() -> Result<()> { } } - handle_force_redraws(&mut app); + update_data(&mut app); } } BottomEvent::Clean => { diff --git a/src/canvas/components/text_table.rs b/src/canvas/components/text_table.rs index fe94ffa5..f5fc31e7 100644 --- a/src/canvas/components/text_table.rs +++ b/src/canvas/components/text_table.rs @@ -184,7 +184,10 @@ impl<'a> TextTable<'a> { desired, max_percentage: _, } => { - *desired = max(column.header.header_text().len(), *data_width) as u16; + *desired = max( + *desired, + max(column.header.header_text().len(), *data_width) as u16, + ); } WidthBounds::CellWidth => {} WidthBounds::Hard(_width) => {} @@ -193,20 +196,19 @@ impl<'a> TextTable<'a> { state.calculate_column_widths(inner_width, self.left_to_right); if let SortState::Sortable(st) = &mut state.sort_state { - st.update_visual_index( - inner_rect, - &state - .columns - .iter() - .filter_map(|c| { - if c.calculated_width == 0 { - None - } else { - Some(c.calculated_width) - } - }) - .collect::>(), - ); + let row_widths = state + .columns + .iter() + .filter_map(|c| { + if c.calculated_width == 0 { + None + } else { + Some(c.calculated_width) + } + }) + .collect::>(); + + st.update_visual_index(inner_rect, &row_widths); } // Update draw loc in widget map @@ -236,35 +238,39 @@ impl<'a> TextTable<'a> { })) }); - let widget = { - let mut table = Table::new(table_rows) - .block(block) - .highlight_style(self.highlighted_text_style) - .style(self.text_style); + if !table_data.data.is_empty() { + let widget = { + let mut table = Table::new(table_rows) + .block(block) + .highlight_style(self.highlighted_text_style) + .style(self.text_style); - if show_header { - table = table.header(header); - } + if show_header { + table = table.header(header); + } - table - }; + table + }; - f.render_stateful_widget( - widget.widths( - &(columns - .iter() - .filter_map(|c| { - if c.calculated_width == 0 { - None - } else { - Some(Constraint::Length(c.calculated_width)) - } - }) - .collect::>()), - ), - margined_draw_loc, - &mut state.table_state, - ); + f.render_stateful_widget( + widget.widths( + &(columns + .iter() + .filter_map(|c| { + if c.calculated_width == 0 { + None + } else { + Some(Constraint::Length(c.calculated_width)) + } + }) + .collect::>()), + ), + margined_draw_loc, + &mut state.table_state, + ); + } else { + f.render_widget(block, margined_draw_loc); + } } } } diff --git a/src/canvas/components/time_graph.rs b/src/canvas/components/time_graph.rs index 723483be..7448ff5c 100644 --- a/src/canvas/components/time_graph.rs +++ b/src/canvas/components/time_graph.rs @@ -143,14 +143,14 @@ impl<'a> TimeGraph<'a> { .collect() }; + let block = Block::default() + .title(self.generate_title(draw_loc)) + .borders(Borders::ALL) + .border_style(self.border_style); + f.render_widget( TimeChart::new(data) - .block( - Block::default() - .title(self.generate_title(draw_loc)) - .borders(Borders::ALL) - .border_style(self.border_style), - ) + .block(block) .x_axis(x_axis) .y_axis(y_axis) .hidden_legend_constraints( diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs index 306aac20..b38d09cb 100644 --- a/src/canvas/widgets/process_table.rs +++ b/src/canvas/widgets/process_table.rs @@ -65,14 +65,14 @@ impl Painter { if let Some(proc_widget_state) = app_state.proc_state.widget_states.get_mut(&widget_id) { // Reset redraw marker. - if proc_widget_state.force_update { - proc_widget_state.force_update = false; + if proc_widget_state.force_rerender { + proc_widget_state.force_rerender = false; } } } /// Draws the process sort box. - /// - `widget_id` represents the widget ID of the process widget itself. + /// - `widget_id` represents the widget ID of the process widget itself.an /// /// This should not be directly called. fn draw_processes_table( @@ -81,9 +81,8 @@ impl Painter { ) { 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(&widget_id) { - // TODO: [PROC] this might be too aggressive... let recalculate_column_widths = - should_get_widget_bounds || proc_widget_state.force_update; + should_get_widget_bounds || proc_widget_state.force_rerender; let is_on_widget = widget_id == app_state.current_widget.widget_id; let (border_style, highlighted_text_style) = if is_on_widget { @@ -350,7 +349,7 @@ impl Painter { app_state.proc_state.widget_states.get_mut(&(widget_id - 2)) { let recalculate_column_widths = - should_get_widget_bounds || proc_widget_state.force_update; + should_get_widget_bounds || proc_widget_state.force_rerender; let is_on_widget = widget_id == app_state.current_widget.widget_id; let (border_style, highlighted_text_style) = if is_on_widget { diff --git a/src/lib.rs b/src/lib.rs index 4042226b..735ace77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -302,11 +302,11 @@ pub fn panic_hook(panic_info: &PanicInfo<'_>) { .unwrap(); } -pub fn handle_force_redraws(app: &mut App) { +pub fn update_data(app: &mut App) { for proc in app.proc_state.widget_states.values_mut() { - if proc.force_update { - // NB: Currently, the "force update" gets fixed in the draw. + if proc.force_update_data { proc.update_displayed_process_data(&app.data_collection); + proc.force_update_data = false; } }