diff --git a/src/app/states.rs b/src/app/states.rs index 5d3ace9c..ebb28996 100644 --- a/src/app/states.rs +++ b/src/app/states.rs @@ -117,9 +117,7 @@ impl TableComponentState { /// Calculates widths for the columns for this table. /// - /// * `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. + /// * `total_width` is the, well, total width available. /// * `left_to_right` is a boolean whether to go from left to right if true, or right to left if /// false. /// @@ -128,71 +126,68 @@ impl TableComponentState { use itertools::Either; use std::cmp::{max, min}; - if total_width > 2 { - let initial_width = total_width - 2; - let mut total_width_left = initial_width; + let mut total_width_left = total_width; - let column_widths = &mut self.calculated_widths; - *column_widths = vec![0; self.columns.len()]; + let column_widths = &mut self.calculated_widths; + *column_widths = vec![0; self.columns.len()]; - let columns = if left_to_right { - Either::Left(self.columns.iter().enumerate()) - } else { - Either::Right(self.columns.iter().enumerate().rev()) - }; + let columns = if left_to_right { + Either::Left(self.columns.iter().enumerate()) + } else { + Either::Right(self.columns.iter().enumerate().rev()) + }; - for (itx, column) in columns { - match &column.width_bounds { - WidthBounds::Soft { - min_width, - desired, - max_percentage, - } => { - let soft_limit = max( - if let Some(max_percentage) = max_percentage { - // Rust doesn't have an `into()` or `try_into()` for floats to integers??? - ((*max_percentage * f32::from(initial_width)).ceil()) as u16 - } else { - *desired - }, - *min_width, - ); - let space_taken = min(min(soft_limit, *desired), total_width_left); - - if *min_width > space_taken { - break; + for (itx, column) in columns { + match &column.width_bounds { + WidthBounds::Soft { + min_width, + desired, + max_percentage, + } => { + let soft_limit = max( + if let Some(max_percentage) = max_percentage { + // Rust doesn't have an `into()` or `try_into()` for floats to integers??? + ((*max_percentage * f32::from(total_width)).ceil()) as u16 } else { - total_width_left = total_width_left.saturating_sub(space_taken + 1); - column_widths[itx] = space_taken; - } + *desired + }, + *min_width, + ); + let space_taken = min(min(soft_limit, *desired), total_width_left); + + if *min_width > space_taken { + break; + } else { + total_width_left = total_width_left.saturating_sub(space_taken + 1); + column_widths[itx] = space_taken; } - WidthBounds::Hard(width) => { - let space_taken = min(*width, total_width_left); + } + WidthBounds::Hard(width) => { + let space_taken = min(*width, total_width_left); - if *width > space_taken { - break; - } else { - total_width_left = total_width_left.saturating_sub(space_taken + 1); - column_widths[itx] = space_taken; - } + if *width > space_taken { + break; + } else { + total_width_left = total_width_left.saturating_sub(space_taken + 1); + column_widths[itx] = space_taken; } } } + } - while let Some(0) = column_widths.last() { - column_widths.pop(); - } + while let Some(0) = column_widths.last() { + column_widths.pop(); + } - if !column_widths.is_empty() { - // Redistribute remaining. - let amount_per_slot = total_width_left / column_widths.len() as u16; - total_width_left %= column_widths.len() as u16; - for (index, width) in column_widths.iter_mut().enumerate() { - if index < total_width_left.into() { - *width += amount_per_slot + 1; - } else { - *width += amount_per_slot; - } + if !column_widths.is_empty() { + // Redistribute remaining. + let amount_per_slot = total_width_left / column_widths.len() as u16; + total_width_left %= column_widths.len() as u16; + for (index, width) in column_widths.iter_mut().enumerate() { + if index < total_width_left.into() { + *width += amount_per_slot + 1; + } else { + *width += amount_per_slot; } } } diff --git a/src/canvas/components/text_table.rs b/src/canvas/components/text_table.rs index 767b8e6c..86cc5d1b 100644 --- a/src/canvas/components/text_table.rs +++ b/src/canvas/components/text_table.rs @@ -19,7 +19,6 @@ use crate::{ pub struct TextTable<'a> { pub table_gap: u16, - pub table_height_offset: u16, pub is_force_redraw: bool, pub recalculate_column_widths: bool, @@ -100,71 +99,23 @@ impl<'a> TextTable<'a> { &self, f: &mut Frame<'_, B>, draw_loc: Rect, state: &mut TableComponentState, table_data: &TableData, ) -> Rect { - let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { - 0 - } else { - self.table_gap - }; - - let sliced_vec = { - let num_rows = usize::from( - (draw_loc.height + 1 - table_gap).saturating_sub(self.table_height_offset), - ); - let start = get_start_position( - num_rows, - &state.scroll_direction, - &mut state.scroll_bar, - state.current_scroll_position, - self.is_force_redraw, - ); - let end = min(table_data.data.len(), start + num_rows + 1); - state - .table_state - .select(Some(state.current_scroll_position.saturating_sub(start))); - &table_data.data[start..end] - }; - - // Calculate widths - if self.recalculate_column_widths { - state - .columns - .iter_mut() - .zip(&table_data.row_widths) - .for_each(|(column, data_width)| match &mut column.width_bounds { - app::WidthBounds::Soft { - min_width: _, - desired, - max_percentage: _, - } => { - *desired = std::cmp::max(column.name.len(), *data_width) as u16; - } - app::WidthBounds::Hard(_width) => {} - }); - - state.calculate_column_widths(draw_loc.width, self.left_to_right); - } - - let columns = &state.columns; - let widths = &state.calculated_widths; - // TODO: Maybe truncate this too? - let header = Row::new(columns.iter().map(|c| Text::raw(c.name.as_ref()))) - .style(self.header_style) - .bottom_margin(table_gap); - let disk_rows = sliced_vec.iter().map(|row| { - Row::new( - row.iter() - .zip(widths) - .map(|(cell, width)| truncate_text(cell, (*width).into())), - ) - }); - - let title = self.generate_title( - draw_loc, - state.current_scroll_position.saturating_add(1), - table_data.data.len(), - ); + let margined_draw_loc = Layout::default() + .constraints([Constraint::Percentage(100)]) + .horizontal_margin(if self.is_on_widget || self.draw_border { + 0 + } else { + 1 + }) + .direction(Direction::Horizontal) + .split(draw_loc)[0]; let disk_block = if self.draw_border { + let title = self.generate_title( + draw_loc, + state.current_scroll_position.saturating_add(1), + table_data.data.len(), + ); + Block::default() .title(title) .borders(Borders::ALL) @@ -177,34 +128,99 @@ impl<'a> TextTable<'a> { Block::default().borders(Borders::NONE) }; - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)]) - .horizontal_margin(if self.is_on_widget || self.draw_border { + let (inner_width, inner_height) = { + let inner = disk_block.inner(margined_draw_loc); + (inner.width, inner.height) + }; + + if inner_width == 0 || inner_height == 0 { + f.render_widget(disk_block, margined_draw_loc); + margined_draw_loc + } else { + let show_header = inner_height > 1; + let header_height = if show_header { 1 } else { 0 }; + let table_gap = if !show_header || draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { 0 } else { - 1 - }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; + self.table_gap + }; - // Draw! - f.render_stateful_widget( - Table::new(disk_rows) - .block(disk_block) - .header(header) - .highlight_style(self.highlighted_text_style) - .style(self.text_style) - .widths( + let sliced_vec = { + let num_rows = usize::from(inner_height.saturating_sub(table_gap + header_height)); + let start = get_start_position( + num_rows, + &state.scroll_direction, + &mut state.scroll_bar, + state.current_scroll_position, + self.is_force_redraw, + ); + let end = min(table_data.data.len(), start + num_rows + 1); + state + .table_state + .select(Some(state.current_scroll_position.saturating_sub(start))); + &table_data.data[start..end] + }; + + // Calculate widths + if self.recalculate_column_widths { + state + .columns + .iter_mut() + .zip(&table_data.row_widths) + .for_each(|(column, data_width)| match &mut column.width_bounds { + app::WidthBounds::Soft { + min_width: _, + desired, + max_percentage: _, + } => { + *desired = std::cmp::max(column.name.len(), *data_width) as u16; + } + app::WidthBounds::Hard(_width) => {} + }); + + state.calculate_column_widths(inner_width, self.left_to_right); + } + + let columns = &state.columns; + let widths = &state.calculated_widths; + // TODO: Maybe truncate this too? + let header = Row::new(columns.iter().map(|c| Text::raw(c.name.as_ref()))) + .style(self.header_style) + .bottom_margin(table_gap); + let disk_rows = sliced_vec.iter().map(|row| { + Row::new( + row.iter() + .zip(widths) + .map(|(cell, width)| truncate_text(cell, (*width).into())), + ) + }); + + let widget = { + let mut table = Table::new(disk_rows) + .block(disk_block) + .highlight_style(self.highlighted_text_style) + .style(self.text_style); + + if show_header { + table = table.header(header); + } + + table + }; + + f.render_stateful_widget( + widget.widths( &(widths .iter() .map(|w| Constraint::Length(*w)) .collect::>()), ), - margined_draw_loc, - &mut state.table_state, - ); + margined_draw_loc, + &mut state.table_state, + ); - margined_draw_loc + margined_draw_loc + } } } diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs index fcb7daa2..36ba33e8 100644 --- a/src/canvas/widgets/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use crate::{ - app::{layout_manager::WidgetDirection, App}, + app::{layout_manager::WidgetDirection, App, CpuWidgetState}, canvas::{ components::{GraphData, TimeGraph}, drawing_utils::{get_column_widths, get_start_position, should_hide_x_label}, @@ -115,6 +115,56 @@ impl Painter { } } + fn generate_points<'a>( + &self, cpu_widget_state: &CpuWidgetState, cpu_data: &'a [ConvertedCpuData], + show_avg_cpu: bool, + ) -> Vec> { + let show_avg_offset = if show_avg_cpu { AVG_POSITION } else { 0 }; + + let current_scroll_position = cpu_widget_state.scroll_state.current_scroll_position; + if current_scroll_position == ALL_POSITION { + // This case ensures the other cases cannot have the position be equal to 0. + cpu_data + .iter() + .enumerate() + .rev() + .map(|(itx, cpu)| { + let style = if show_avg_cpu && itx == AVG_POSITION { + self.colours.avg_colour_style + } else if itx == ALL_POSITION { + self.colours.all_colour_style + } else { + let offset_position = itx - 1; // Because of the all position + self.colours.cpu_colour_styles[(offset_position - show_avg_offset) + % self.colours.cpu_colour_styles.len()] + }; + + GraphData { + points: &cpu.cpu_data[..], + style, + name: None, + } + }) + .collect::>() + } else if let Some(cpu) = cpu_data.get(current_scroll_position) { + let style = if show_avg_cpu && current_scroll_position == AVG_POSITION { + self.colours.avg_colour_style + } else { + let offset_position = current_scroll_position - 1; // Because of the all position + self.colours.cpu_colour_styles + [(offset_position - show_avg_offset) % self.colours.cpu_colour_styles.len()] + }; + + vec![GraphData { + points: &cpu.cpu_data[..], + style, + name: None, + }] + } else { + vec![] + } + } + fn draw_cpu_graph( &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64, ) { @@ -131,52 +181,12 @@ impl Painter { &mut cpu_widget_state.autohide_timer, draw_loc, ); - let show_avg_cpu = app_state.app_config_fields.show_average_cpu; - let show_avg_offset = if show_avg_cpu { AVG_POSITION } else { 0 }; - let points = { - let current_scroll_position = cpu_widget_state.scroll_state.current_scroll_position; - if current_scroll_position == ALL_POSITION { - // This case ensures the other cases cannot have the position be equal to 0. - cpu_data - .iter() - .enumerate() - .rev() - .map(|(itx, cpu)| { - let style = if show_avg_cpu && itx == AVG_POSITION { - self.colours.avg_colour_style - } else if itx == ALL_POSITION { - self.colours.all_colour_style - } else { - let offset_position = itx - 1; // Because of the all position - self.colours.cpu_colour_styles[(offset_position - show_avg_offset) - % self.colours.cpu_colour_styles.len()] - }; - GraphData { - points: &cpu.cpu_data[..], - style, - name: None, - } - }) - .collect::>() - } else if let Some(cpu) = cpu_data.get(current_scroll_position) { - let style = if show_avg_cpu && current_scroll_position == AVG_POSITION { - self.colours.avg_colour_style - } else { - let offset_position = current_scroll_position - 1; // Because of the all position - self.colours.cpu_colour_styles[(offset_position - show_avg_offset) - % self.colours.cpu_colour_styles.len()] - }; - - vec![GraphData { - points: &cpu.cpu_data[..], - style, - name: None, - }] - } else { - vec![] - } - }; + let points = self.generate_points( + &cpu_widget_state, + cpu_data, + app_state.app_config_fields.show_average_cpu, + ); // TODO: Maybe hide load avg if too long? Or maybe the CPU part. let title = if cfg!(target_family = "unix") { diff --git a/src/canvas/widgets/disk_table.rs b/src/canvas/widgets/disk_table.rs index e897a770..fd667290 100644 --- a/src/canvas/widgets/disk_table.rs +++ b/src/canvas/widgets/disk_table.rs @@ -24,7 +24,6 @@ impl Painter { }; let margined_draw_loc = TextTable { table_gap: app_state.app_config_fields.table_gap, - table_height_offset: self.table_height_offset, is_force_redraw: app_state.is_force_redraw, recalculate_column_widths, header_style: self.colours.table_header_style, diff --git a/src/canvas/widgets/temp_table.rs b/src/canvas/widgets/temp_table.rs index 890411e2..bdd30134 100644 --- a/src/canvas/widgets/temp_table.rs +++ b/src/canvas/widgets/temp_table.rs @@ -24,7 +24,6 @@ impl Painter { }; let margined_draw_loc = TextTable { table_gap: app_state.app_config_fields.table_gap, - table_height_offset: self.table_height_offset, is_force_redraw: app_state.is_force_redraw, recalculate_column_widths, header_style: self.colours.table_header_style,