bug: fix a variety of bugs

Bugs squashed:
- Incorrect column sizing for flex cases
- Case where the sort menu bounds were still existing despite being
  hidden
- Proc widget not actually taking into account the calculated row widths
  in some cases during data conversion.
This commit is contained in:
ClementTsang 2022-05-16 04:53:39 -04:00
parent 0831a56341
commit cc6d7b8ad7
8 changed files with 124 additions and 117 deletions

View File

@ -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();
}
}
}

View File

@ -400,20 +400,18 @@ impl<H: TableComponentHeader> TableComponentState<H> {
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<H: TableComponentHeader> TableComponentState<H> {
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]

View File

@ -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();
}
}
}

View File

@ -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 => {

View File

@ -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::<Vec<_>>(),
);
let row_widths = state
.columns
.iter()
.filter_map(|c| {
if c.calculated_width == 0 {
None
} else {
Some(c.calculated_width)
}
})
.collect::<Vec<_>>();
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::<Vec<_>>()),
),
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::<Vec<_>>()),
),
margined_draw_loc,
&mut state.table_state,
);
} else {
f.render_widget(block, margined_draw_loc);
}
}
}
}

View File

@ -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(

View File

@ -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<B: Backend>(
@ -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 {

View File

@ -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;
}
}