diff --git a/src/app.rs b/src/app.rs index 247b95ad..4f32b398 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,6 +3,8 @@ use std::{cmp::max, collections::HashMap, time::Instant}; use unicode_segmentation::GraphemeCursor; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; +use tui::widgets::TableState; + use typed_builder::*; use data_farmer::*; @@ -47,6 +49,7 @@ pub struct AppScrollWidgetState { pub current_scroll_position: u64, pub previous_scroll_position: u64, pub scroll_direction: ScrollDirection, + pub table_state: TableState, } #[derive(Default)] @@ -91,7 +94,7 @@ pub struct AppConfigFields { pub hide_time: bool, pub autohide_time: bool, pub use_old_network_legend: bool, - pub table_gap: u16 + pub table_gap: u16, } /// AppSearchState deals with generic searching (I might do this in the future). diff --git a/src/canvas.rs b/src/canvas.rs index d4db433d..9e4aaf30 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -69,10 +69,11 @@ pub struct Painter { layout_constraints: Vec>>>, widget_layout: BottomLayout, derived_widget_draw_locs: Vec>>>, + table_height_offset: i64, } impl Painter { - pub fn init(widget_layout: BottomLayout) -> Self { + pub fn init(widget_layout: BottomLayout, table_gap: u16) -> Self { // Now for modularity; we have to also initialize the base layouts! // We want to do this ONCE and reuse; after this we can just construct // based on the console size. @@ -154,6 +155,7 @@ impl Painter { layout_constraints, widget_layout, derived_widget_draw_locs: Vec::new(), + table_height_offset: 4 + table_gap as i64, } } diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs index 707c3a8f..a77c100a 100644 --- a/src/canvas/widgets/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -217,7 +217,7 @@ impl CpuGraphWidget for Painter { cpu_widget_state.is_legend_hidden = false; let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data; - let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; + let num_rows = max(0, i64::from(draw_loc.height) - self.table_height_offset) as u64; let start_position = get_start_position( num_rows, &cpu_widget_state.scroll_state.scroll_direction, @@ -225,13 +225,13 @@ impl CpuGraphWidget for Painter { cpu_widget_state.scroll_state.current_scroll_position, app_state.is_resized, ); + let is_on_widget = widget_id == app_state.current_widget.widget_id; let sliced_cpu_data = &cpu_data[start_position as usize..]; let mut offset_scroll_index = (cpu_widget_state.scroll_state.current_scroll_position - start_position) as usize; let show_disabled_data = app_state.app_config_fields.show_disabled_data; - let current_widget_id = app_state.current_widget.widget_id; let show_avg_cpu = app_state.app_config_fields.show_average_cpu; let cpu_rows = sliced_cpu_data.iter().enumerate().filter_map(|(itx, cpu)| { @@ -267,7 +267,7 @@ impl CpuGraphWidget for Painter { } else { Some(Row::StyledData( cpu_string_row.into_iter(), - if current_widget_id == widget_id { + if is_on_widget { if itx == offset_scroll_index { self.colours.currently_selected_text_style } else if show_avg_cpu && itx == 0 { @@ -315,10 +315,13 @@ impl CpuGraphWidget for Painter { "".to_string() }; - let title_and_border_style = if app_state.current_widget.widget_id == widget_id { - self.colours.highlighted_border_style + let (border_and_title_style, highlight_style) = if is_on_widget { + ( + self.colours.highlighted_border_style, + self.colours.currently_selected_text_style, + ) } else { - self.colours.border_style + (self.colours.border_style, self.colours.text_style) }; // Draw @@ -335,11 +338,12 @@ impl CpuGraphWidget for Painter { .block( Block::default() .title(&title) - .title_style(title_and_border_style) + .title_style(border_and_title_style) .borders(Borders::ALL) - .border_style(title_and_border_style), + .border_style(border_and_title_style), ) .header_style(self.colours.table_header_style) + .highlight_style(highlight_style) .widths( &(intrinsic_widths .iter() diff --git a/src/canvas/widgets/disk_table.rs b/src/canvas/widgets/disk_table.rs index 549ae153..13d24358 100644 --- a/src/canvas/widgets/disk_table.rs +++ b/src/canvas/widgets/disk_table.rs @@ -39,7 +39,7 @@ impl DiskTableWidget for Painter { ) { if let Some(disk_widget_state) = app_state.disk_state.widget_states.get_mut(&widget_id) { let disk_data: &mut [Vec] = &mut app_state.canvas_data.disk_data; - let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; + let num_rows = max(0, i64::from(draw_loc.height) - self.table_height_offset) as u64; let start_position = get_start_position( num_rows, &disk_widget_state.scroll_state.scroll_direction, @@ -47,34 +47,14 @@ impl DiskTableWidget for Painter { disk_widget_state.scroll_state.current_scroll_position, app_state.is_resized, ); + 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 - start_position) as usize, + )); let sliced_vec = &mut disk_data[start_position as usize..]; - let mut disk_counter: i64 = 0; - - let current_widget_id = app_state.current_widget.widget_id; - let disk_rows = sliced_vec.iter().map(|disk| { - Row::StyledData( - disk.iter(), - if current_widget_id == widget_id - && disk_widget_state.scroll_state.current_scroll_position >= start_position - { - if disk_counter as u64 - == disk_widget_state.scroll_state.current_scroll_position - - start_position - { - disk_counter = -1; - self.colours.currently_selected_text_style - } else { - if disk_counter >= 0 { - disk_counter += 1; - } - self.colours.text_style - } - } else { - self.colours.text_style - }, - ) - }); + let disk_rows = sliced_vec.iter().map(|disk| Row::Data(disk.iter())); // Calculate widths // TODO: [PRETTY] Ellipsis on strings? @@ -101,10 +81,13 @@ impl DiskTableWidget for Painter { " Disk ".to_string() }; - let border_and_title_style = if app_state.current_widget.widget_id == widget_id { - self.colours.highlighted_border_style + let (border_and_title_style, highlight_style) = if is_on_widget { + ( + self.colours.highlighted_border_style, + self.colours.currently_selected_text_style, + ) } else { - self.colours.border_style + (self.colours.border_style, self.colours.text_style) }; let disk_block = if draw_border { @@ -117,7 +100,7 @@ impl DiskTableWidget for Painter { }) .borders(Borders::ALL) .border_style(border_and_title_style) - } else if app_state.current_widget.widget_id == widget_id { + } else if is_on_widget { Block::default() .borders(*SIDE_BORDERS) .border_style(self.colours.highlighted_border_style) @@ -127,21 +110,17 @@ impl DiskTableWidget for Painter { let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)].as_ref()) - .horizontal_margin( - if app_state.current_widget.widget_id == widget_id || draw_border { - 0 - } else { - 1 - }, - ) + .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) .split(draw_loc); // Draw! - f.render_widget( + f.render_stateful_widget( Table::new(DISK_HEADERS.iter(), disk_rows) .block(disk_block) .header_style(self.colours.table_header_style) + .highlight_style(highlight_style) + .style(self.colours.text_style) .widths( &(intrinsic_widths .iter() @@ -150,6 +129,7 @@ impl DiskTableWidget for Painter { ) .header_gap(app_state.app_config_fields.table_gap), margined_draw_loc[0], + disk_table_state, ); } } diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs index 37f6c761..0733b500 100644 --- a/src/canvas/widgets/process_table.rs +++ b/src/canvas/widgets/process_table.rs @@ -83,7 +83,7 @@ impl ProcessTableWidget for Painter { // hit the process we've currently scrolled to. // We also need to move the list - we can // do so by hiding some elements! - let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; + let num_rows = max(0, i64::from(draw_loc.height) - self.table_height_offset) as u64; let is_on_widget = widget_id == app_state.current_widget.widget_id; let position = get_start_position( @@ -102,43 +102,32 @@ impl ProcessTableWidget for Painter { }; let sliced_vec = &process_data[start_position as usize..]; - let mut process_counter: i64 = 0; + 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 - start_position) + as usize, + )); // Draw! + let is_proc_widget_grouped = proc_widget_state.is_grouped; let process_rows = sliced_vec.iter().map(|process| { - let stringified_process_vec: Vec = vec![ - if proc_widget_state.is_grouped { - process.group_pids.len().to_string() - } else { - process.pid.to_string() - }, - process.name.clone(), - format!("{:.1}%", process.cpu_usage), - format!("{:.1}%", process.mem_usage), - process.read_per_sec.to_string(), - process.write_per_sec.to_string(), - process.total_read.to_string(), - process.total_write.to_string(), - process.process_states.to_string(), - ]; - Row::StyledData( - stringified_process_vec.into_iter(), - if is_on_widget { - if process_counter as u64 - == proc_widget_state.scroll_state.current_scroll_position - - start_position - { - process_counter = -1; - self.colours.currently_selected_text_style + Row::Data( + vec![ + if is_proc_widget_grouped { + process.group_pids.len().to_string() } else { - if process_counter >= 0 { - process_counter += 1; - } - self.colours.text_style - } - } else { - self.colours.text_style - }, + process.pid.to_string() + }, + process.name.clone(), + format!("{:.1}%", process.cpu_usage), + format!("{:.1}%", process.mem_usage), + process.read_per_sec.to_string(), + process.write_per_sec.to_string(), + process.total_read.to_string(), + process.total_write.to_string(), + process.process_states.to_string(), + ] + .into_iter(), ) }); @@ -223,10 +212,13 @@ impl ProcessTableWidget for Painter { String::default() }; - let border_and_title_style = if is_on_widget { - self.colours.highlighted_border_style + let (border_and_title_style, highlight_style) = if is_on_widget { + ( + self.colours.highlighted_border_style, + self.colours.currently_selected_text_style, + ) } else { - self.colours.border_style + (self.colours.border_style, self.colours.text_style) }; let process_block = if draw_border { @@ -253,10 +245,12 @@ impl ProcessTableWidget for Painter { .direction(Direction::Horizontal) .split(draw_loc); - f.render_widget( + f.render_stateful_widget( Table::new(process_headers.iter(), process_rows) .block(process_block) .header_style(self.colours.table_header_style) + .highlight_style(highlight_style) + .style(self.colours.text_style) .widths( &(intrinsic_widths .iter() @@ -267,6 +261,7 @@ impl ProcessTableWidget for Painter { ) .header_gap(app_state.app_config_fields.table_gap), margined_draw_loc[0], + proc_table_state, ); } } diff --git a/src/canvas/widgets/temp_table.rs b/src/canvas/widgets/temp_table.rs index 5fa5e1a2..007d93b0 100644 --- a/src/canvas/widgets/temp_table.rs +++ b/src/canvas/widgets/temp_table.rs @@ -40,7 +40,7 @@ impl TempTableWidget for Painter { if let Some(temp_widget_state) = app_state.temp_state.widget_states.get_mut(&widget_id) { let temp_sensor_data: &mut [Vec] = &mut app_state.canvas_data.temp_sensor_data; - let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; + let num_rows = max(0, i64::from(draw_loc.height) - self.table_height_offset) as u64; let start_position = get_start_position( num_rows, &temp_widget_state.scroll_state.scroll_direction, @@ -48,32 +48,14 @@ impl TempTableWidget for Painter { temp_widget_state.scroll_state.current_scroll_position, app_state.is_resized, ); + 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 - start_position) as usize, + )); let sliced_vec = &temp_sensor_data[start_position as usize..]; - let mut temp_row_counter: i64 = 0; - let current_widget_id = app_state.current_widget.widget_id; - - let temperature_rows = sliced_vec.iter().map(|temp_row| { - Row::StyledData( - temp_row.iter(), - if current_widget_id == widget_id { - if temp_row_counter as u64 - == temp_widget_state.scroll_state.current_scroll_position - - start_position - { - temp_row_counter = -1; - self.colours.currently_selected_text_style - } else { - if temp_row_counter >= 0 { - temp_row_counter += 1; - } - self.colours.text_style - } - } else { - self.colours.text_style - }, - ) - }); + let temperature_rows = sliced_vec.iter().map(|temp_row| Row::Data(temp_row.iter())); // Calculate widths let width = f64::from(draw_loc.width); @@ -100,25 +82,22 @@ impl TempTableWidget for Painter { " Temperatures ".to_string() }; + let (border_and_title_style, highlight_style) = if is_on_widget { + ( + self.colours.highlighted_border_style, + self.colours.currently_selected_text_style, + ) + } else { + (self.colours.border_style, self.colours.text_style) + }; + let temp_block = if draw_border { Block::default() .title(&title) - .title_style(if app_state.is_expanded { - if app_state.current_widget.widget_id == widget_id { - self.colours.highlighted_border_style - } else { - self.colours.border_style - } - } else { - self.colours.widget_title_style - }) + .title_style(border_and_title_style) .borders(Borders::ALL) - .border_style(if app_state.current_widget.widget_id == widget_id { - self.colours.highlighted_border_style - } else { - self.colours.border_style - }) - } else if app_state.current_widget.widget_id == widget_id { + .border_style(border_and_title_style) + } else if is_on_widget { Block::default() .borders(*SIDE_BORDERS) .border_style(self.colours.highlighted_border_style) @@ -128,21 +107,17 @@ impl TempTableWidget for Painter { let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)].as_ref()) - .horizontal_margin( - if app_state.current_widget.widget_id == widget_id || draw_border { - 0 - } else { - 1 - }, - ) + .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) .split(draw_loc); // Draw - f.render_widget( + f.render_stateful_widget( Table::new(TEMP_HEADERS.iter(), temperature_rows) .block(temp_block) .header_style(self.colours.table_header_style) + .highlight_style(highlight_style) + .style(self.colours.text_style) .widths( &(intrinsic_widths .iter() @@ -151,6 +126,7 @@ impl TempTableWidget for Painter { ) .header_gap(app_state.app_config_fields.table_gap), margined_draw_loc[0], + temp_table_state, ); } } diff --git a/src/constants.rs b/src/constants.rs index d5f165d7..7c019c22 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -151,7 +151,10 @@ pub const DEFAULT_CONFIG_CONTENT: &str = r##" #basic = false # Use the old network legend style -#use_old_network_legend = true +#use_old_network_legend = false + +# Remove space in tables +#hide_table_gap = false ########################################################## diff --git a/src/main.rs b/src/main.rs index e9855aec..42df73ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -145,7 +145,7 @@ fn main() -> error::Result<()> { app.used_widgets.clone(), ); - let mut painter = canvas::Painter::init(widget_layout); + let mut painter = canvas::Painter::init(widget_layout, app.app_config_fields.table_gap); if let Err(config_check) = generate_config_colours(&config, &mut painter) { cleanup_terminal(&mut terminal)?; return Err(config_check);