diff options
author | ClementTsang <cjhtsang@uwaterloo.ca> | 2020-03-07 23:47:53 -0500 |
---|---|---|
committer | ClementTsang <cjhtsang@uwaterloo.ca> | 2020-03-07 23:47:53 -0500 |
commit | 03ec52c5b16d8968e14e1a648bd2ef4b9489380e (patch) | |
tree | 2de0ee608bbf72255be03b3fab35f87ab14fd11a /src/canvas.rs | |
parent | 132a5a2170294f0d5c463ecba77de1aab32183db (diff) |
Split up widgets to make it a bit easier to work with.
Diffstat (limited to 'src/canvas.rs')
-rw-r--r-- | src/canvas.rs | 1394 |
1 files changed, 4 insertions, 1390 deletions
diff --git a/src/canvas.rs b/src/canvas.rs index 8c0c9545..aa7e8478 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -1,19 +1,16 @@ -use std::cmp::{max, min}; +use std::cmp::max; use std::collections::HashMap; use tui::{ backend::Backend, layout::{Alignment, Constraint, Direction, Layout, Rect}, - style::{Color, Style}, terminal::Frame, - widgets::{Axis, Block, Borders, Chart, Dataset, Marker, Paragraph, Row, Table, Text, Widget}, + widgets::{Block, Borders, Paragraph, Text, Widget}, Terminal, }; -use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthStr; use canvas_colours::*; -use drawing_utils::*; +use widgets::*; use crate::{ app::{self, data_harvester::processes::ProcessHarvest, WidgetPosition}, @@ -24,45 +21,7 @@ use crate::{ mod canvas_colours; mod drawing_utils; - -// Headers -const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"]; -const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show (Space)"]; -const DISK_HEADERS: [&str; 7] = ["Disk", "Mount", "Used", "Free", "Total", "R/s", "W/s"]; -const TEMP_HEADERS: [&str; 2] = ["Sensor", "Temp"]; -const MEM_HEADERS: [&str; 3] = ["Mem", "Usage", "Use%"]; -const NETWORK_HEADERS: [&str; 4] = ["RX", "TX", "Total RX", "Total TX"]; -const FORCE_MIN_THRESHOLD: usize = 5; - -lazy_static! { - static ref SIDE_BORDERS: Borders = Borders::from_bits_truncate(20); - static ref DEFAULT_TEXT_STYLE: Style = Style::default().fg(Color::Gray); - static ref DEFAULT_HEADER_STYLE: Style = Style::default().fg(Color::LightBlue); - static ref DISK_HEADERS_LENS: Vec<usize> = DISK_HEADERS - .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) - .collect::<Vec<_>>(); - static ref CPU_LEGEND_HEADER_LENS: Vec<usize> = CPU_LEGEND_HEADER - .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) - .collect::<Vec<_>>(); - static ref CPU_SELECT_LEGEND_HEADER_LENS: Vec<usize> = CPU_SELECT_LEGEND_HEADER - .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) - .collect::<Vec<_>>(); - static ref TEMP_HEADERS_LENS: Vec<usize> = TEMP_HEADERS - .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) - .collect::<Vec<_>>(); - static ref MEM_HEADERS_LENS: Vec<usize> = MEM_HEADERS - .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) - .collect::<Vec<_>>(); - static ref NETWORK_HEADERS_LENS: Vec<usize> = NETWORK_HEADERS - .iter() - .map(|entry| max(FORCE_MIN_THRESHOLD, entry.len())) - .collect::<Vec<_>>(); -} +mod widgets; #[derive(Default)] pub struct DisplayableData { @@ -585,1349 +544,4 @@ impl Painter { Ok(()) } - - fn draw_process_and_search<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool, - ) { - let search_width = if draw_border { 5 } else { 3 }; - - if app_state.is_searching() { - let processes_chunk = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Min(0), Constraint::Length(search_width)].as_ref()) - .split(draw_loc); - - self.draw_processes_table(f, app_state, processes_chunk[0], draw_border); - self.draw_search_field(f, app_state, processes_chunk[1], draw_border); - } else { - self.draw_processes_table(f, app_state, draw_loc, draw_border); - } - } - - fn draw_cpu_graph<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect, - ) { - let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data; - - // CPU usage graph - let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]); - let y_axis = Axis::default() - .style(self.colours.graph_style) - .labels_style(self.colours.graph_style) - .bounds([-0.5, 100.5]) - .labels(&["0%", "100%"]); - - let dataset_vector: Vec<Dataset<'_>> = cpu_data - .iter() - .enumerate() - .rev() - .filter_map(|(itx, cpu)| { - if app_state.cpu_state.core_show_vec[itx] { - Some( - Dataset::default() - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style( - if app_state.app_config_fields.show_average_cpu && itx == 0 { - self.colours.avg_colour_style - } else { - self.colours.cpu_colour_styles - [itx % self.colours.cpu_colour_styles.len()] - }, - ) - .data(&cpu.cpu_data[..]), - ) - } else { - None - } - }) - .collect(); - - let title = if app_state.is_expanded && !app_state.cpu_state.is_showing_tray { - const TITLE_BASE: &str = " CPU ── Esc to go back "; - let repeat_num = max( - 0, - draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2, - ); - let result_title = - format!(" CPU ─{}─ Esc to go back ", "─".repeat(repeat_num as usize)); - - result_title - } else { - " CPU ".to_string() - }; - - Chart::default() - .block( - Block::default() - .title(&title) - .title_style(if app_state.is_expanded { - self.colours.highlighted_border_style - } else { - self.colours.widget_title_style - }) - .borders(Borders::ALL) - .border_style(match app_state.current_widget_selected { - WidgetPosition::Cpu | WidgetPosition::BasicCpu => { - self.colours.highlighted_border_style - } - _ => self.colours.border_style, - }), - ) - .x_axis(x_axis) - .y_axis(y_axis) - .datasets(&dataset_vector) - .render(f, draw_loc); - } - - fn draw_cpu_legend<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, - ) { - let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data; - - let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; - let start_position = get_start_position( - num_rows, - &app_state.app_scroll_positions.scroll_direction, - &mut app_state - .app_scroll_positions - .cpu_scroll_state - .previous_scroll_position, - app_state - .app_scroll_positions - .cpu_scroll_state - .current_scroll_position, - app_state.is_resized, - ); - - let sliced_cpu_data = &cpu_data[start_position as usize..]; - let mut stringified_cpu_data: Vec<Vec<String>> = Vec::new(); - - for (itx, cpu) in sliced_cpu_data.iter().enumerate() { - if app_state.cpu_state.is_showing_tray { - stringified_cpu_data.push(vec![ - cpu.cpu_name.clone(), - if app_state.cpu_state.core_show_vec[itx + start_position as usize] { - "[*]".to_string() - } else { - "[ ]".to_string() - }, - ]); - } else if let Some(cpu_data) = cpu.cpu_data.last() { - if app_state.app_config_fields.show_disabled_data - || app_state.cpu_state.core_show_vec[itx] - { - stringified_cpu_data.push(vec![ - cpu.cpu_name.clone(), - format!("{:.0}%", cpu_data.1.round()), - ]); - } - } - } - - let cpu_rows = stringified_cpu_data - .iter() - .enumerate() - .map(|(itx, cpu_string_row)| { - Row::StyledData( - cpu_string_row.iter(), - match app_state.current_widget_selected { - WidgetPosition::Cpu => { - if itx as u64 - == app_state - .app_scroll_positions - .cpu_scroll_state - .current_scroll_position - - start_position - { - self.colours.currently_selected_text_style - } else if app_state.app_config_fields.show_average_cpu && itx == 0 { - self.colours.avg_colour_style - } else { - self.colours.cpu_colour_styles[itx - + start_position as usize - % self.colours.cpu_colour_styles.len()] - } - } - _ => { - if app_state.app_config_fields.show_average_cpu && itx == 0 { - self.colours.avg_colour_style - } else { - self.colours.cpu_colour_styles[itx - + start_position as usize - % self.colours.cpu_colour_styles.len()] - } - } - }, - ) - }); - - // Calculate widths - let width = f64::from(draw_loc.width); - let width_ratios = vec![0.5, 0.5]; - - let variable_intrinsic_results = get_variable_intrinsic_widths( - width as u16, - &width_ratios, - if app_state.cpu_state.is_showing_tray { - &CPU_SELECT_LEGEND_HEADER_LENS - } else { - &CPU_LEGEND_HEADER_LENS - }, - ); - let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; - - let title = if app_state.cpu_state.is_showing_tray { - const TITLE_BASE: &str = " Esc to close "; - let repeat_num = max( - 0, - draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2, - ); - let result_title = format!("{} Esc to close ", "─".repeat(repeat_num as usize)); - - result_title - } else { - "".to_string() - }; - - // Draw - Table::new( - if app_state.cpu_state.is_showing_tray { - CPU_SELECT_LEGEND_HEADER - } else { - CPU_LEGEND_HEADER - } - .iter(), - cpu_rows, - ) - .block( - Block::default() - .title(&title) - .title_style(if app_state.is_expanded { - self.colours.highlighted_border_style - } else { - match app_state.current_widget_selected { - WidgetPosition::Cpu => self.colours.highlighted_border_style, - _ => self.colours.border_style, - } - }) - .borders(Borders::ALL) - .border_style(match app_state.current_widget_selected { - WidgetPosition::Cpu => self.colours.highlighted_border_style, - _ => self.colours.border_style, - }), - ) - .header_style(self.colours.table_header_style) - .widths( - &(intrinsic_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::<Vec<_>>()), - ) - .render(f, draw_loc); - } - - fn draw_memory_graph<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect, - ) { - let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data; - let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data; - - let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]); - - // Offset as the zero value isn't drawn otherwise... - let y_axis: Axis<'_, &str> = Axis::default() - .style(self.colours.graph_style) - .labels_style(self.colours.graph_style) - .bounds([-0.5, 100.5]) - .labels(&["0%", "100%"]); - - let mem_canvas_vec: Vec<Dataset<'_>> = vec![ - Dataset::default() - .name(&app_state.canvas_data.mem_label) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(self.colours.ram_style) - .data(&mem_data), - Dataset::default() - .name(&app_state.canvas_data.swap_label) - .marker(if app_state.app_config_fields.use_dot { - Marker::Dot - } else { - Marker::Braille - }) - .style(self.colours.swap_style) - .data(&swap_data), - ]; - - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Memory ── Esc to go back "; - let repeat_num = max( - 0, - draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2, - ); - let result_title = format!( - " Memory ─{}─ Esc to go back ", - "─".repeat(repeat_num as usize) - ); - - result_title - } else { - " Memory ".to_string() - }; - - Chart::default() - .block( - Block::default() - .title(&title) - .title_style(if app_state.is_expanded { - self.colours.highlighted_border_style - } else { - self.colours.widget_title_style - }) - .borders(Borders::ALL) - .border_style(match app_state.current_widget_selected { - WidgetPosition::Mem | WidgetPosition::BasicMem => { - self.colours.highlighted_border_style - } - _ => self.colours.border_style, - }), - ) - .x_axis(x_axis) - .y_axis(y_axis) - .datasets(&mem_canvas_vec) - .render(f, draw_loc); - } - - fn draw_network_graph<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect, - ) { - let network_data_rx: &[(f64, f64)] = &app_state.canvas_data.network_data_rx; - let network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx; - - let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, 60_000.0]); - let y_axis: Axis<'_, &str> = Axis::default() - .style(self.colours.graph_style) - .labels_style(self.colours.graph_style) - .bounds([-0.5, 30_f64]) - .labels(&["0B", "1KiB", "1MiB", "1GiB"]); - - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Network ── Esc to go back "; - let repeat_num = max( - 0, - draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2, - ); - let result_title = format!( - " Network ─{}─ Esc to go back ", - "─".repeat(repeat_num as usize) - ); - - result_title - } else { - " Network ".to_string() - }; - - Chart::default() - .block( - Block::default() - .title(&title) - .title_style(if app_state.is_expanded { - self.colours.highlighted_border_style - } else { - self.colours.widget_title_style - }) - .borders(Borders::ALL) - .border_style(match app_state.current_widget_selected { - WidgetPosition::Network | WidgetPosition::BasicNet => { - self.colours.highlighted_border_style - } - _ => self.colours.border_style, - }), - ) - .x_axis(x_axis) - .y_axis(y_axis) - .datasets(&[ - 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(self.colours.rx_style) - .data(&network_data_rx), - 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(self.colours.tx_style) - .data(&network_data_tx), - Dataset::default() - .name(&format!( - "Total RX: {:7}", - app_state.canvas_data.total_rx_display - )) - .style(self.colours.total_rx_style), - Dataset::default() - .name(&format!( - "Total TX: {:7}", - app_state.canvas_data.total_tx_display - )) - .style(self.colours.total_tx_style), - ]) - .render(f, draw_loc); - } - - fn draw_network_labels<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, - ) { - 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![ - rx_display, - tx_display, - total_rx_display, - total_tx_display, - ]]; - let mapped_network = total_network - .iter() - .map(|val| Row::StyledData(val.iter(), self.colours.text_style)); - - // Calculate widths - let width_ratios: Vec<f64> = vec![0.25, 0.25, 0.25, 0.25]; - let lens: &[usize] = &NETWORK_HEADERS_LENS; - let width = f64::from(draw_loc.width); - - let variable_intrinsic_results = - get_variable_intrinsic_widths(width as u16, &width_ratios, lens); - let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; - - // Draw - Table::new(NETWORK_HEADERS.iter(), mapped_network) - .block(Block::default().borders(Borders::ALL).border_style( - match app_state.current_widget_selected { - WidgetPosition::Network => self.colours.highlighted_border_style, - _ => self.colours.border_style, - }, - )) - .header_style(self.colours.table_header_style) - .style(self.colours.text_style) - .widths( - &(intrinsic_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::<Vec<_>>()), - ) - .render(f, draw_loc); - } - - fn draw_temp_table<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool, - ) { - let temp_sensor_data: &[Vec<String>] = &app_state.canvas_data.temp_sensor_data; - - let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; - let start_position = get_start_position( - num_rows, - &app_state.app_scroll_positions.scroll_direction, - &mut app_state - .app_scroll_positions - .temp_scroll_state - .previous_scroll_position, - app_state - .app_scroll_positions - .temp_scroll_state - .current_scroll_position, - app_state.is_resized, - ); - - let sliced_vec = &temp_sensor_data[start_position as usize..]; - let mut temp_row_counter: i64 = 0; - - let temperature_rows = sliced_vec.iter().map(|temp_row| { - Row::StyledData( - temp_row.iter(), - match app_state.current_widget_selected { - WidgetPosition::Temp => { - if temp_row_counter as u64 - == app_state - .app_scroll_positions - .temp_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 - } - } - _ => self.colours.text_style, - }, - ) - }); - - // Calculate widths - let width = f64::from(draw_loc.width); - let width_ratios = [0.5, 0.5]; - let variable_intrinsic_results = - get_variable_intrinsic_widths(width as u16, &width_ratios, &TEMP_HEADERS_LENS); - let intrinsic_widths = &(variable_intrinsic_results.0)[0..variable_intrinsic_results.1]; - - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Temperatures ── Esc to go back "; - let repeat_num = max( - 0, - draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2, - ); - let result_title = format!( - " Temperatures ─{}─ Esc to go back ", - "─".repeat(repeat_num as usize) - ); - - result_title - } else if app_state.app_config_fields.use_basic_mode { - String::new() - } else { - " Temperatures ".to_string() - }; - - let temp_block = if draw_border { - Block::default() - .title(&title) - .title_style(if app_state.is_expanded { - match app_state.current_widget_selected { - WidgetPosition::Temp => self.colours.highlighted_border_style, - _ => self.colours.border_style, - } - } else { - self.colours.widget_title_style - }) - .borders(Borders::ALL) - .border_style(match app_state.current_widget_selected { - WidgetPosition::Temp => self.colours.highlighted_border_style, - _ => self.colours.border_style, - }) - } else { - match app_state.current_widget_selected { - WidgetPosition::Temp => Block::default() - .borders(*SIDE_BORDERS) - .border_style(self.colours.highlighted_border_style), - _ => Block::default().borders(Borders::NONE), - } - }; - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)].as_ref()) - .horizontal_margin(match app_state.current_widget_selected { - WidgetPosition::Temp => 0, - _ if !draw_border => 1, - _ => 0, - }) - .direction(Direction::Horizontal) - .split(draw_loc); - - // Draw - Table::new(TEMP_HEADERS.iter(), temperature_rows) - .block(temp_block) - .header_style(self.colours.table_header_style) - .widths( - &(intrinsic_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::<Vec<_>>()), - ) - .render(f, margined_draw_loc[0]); - } - - fn draw_disk_table<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool, - ) { - let disk_data: &[Vec<String>] = &app_state.canvas_data.disk_data; - let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; - let start_position = get_start_position( - num_rows, - &app_state.app_scroll_positions.scroll_direction, - &mut app_state - .app_scroll_positions - .disk_scroll_state - .previous_scroll_position, - app_state - .app_scroll_positions - .disk_scroll_state - .current_scroll_position, - app_state.is_resized, - ); - - let sliced_vec = &disk_data[start_position as usize..]; - let mut disk_counter: i64 = 0; - - let disk_rows = sliced_vec.iter().map(|disk| { - Row::StyledData( - disk.iter(), - match app_state.current_widget_selected { - WidgetPosition::Disk => { - if disk_counter as u64 - == app_state - .app_scroll_positions - .disk_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 - } - } - _ => self.colours.text_style, - }, - ) - }); - - // Calculate widths - // TODO: [PRETTY] Ellipsis on strings? - let width = f64::from(draw_loc.width); - let width_ratios = [0.2, 0.15, 0.13, 0.13, 0.13, 0.13, 0.13]; - let variable_intrinsic_results = - get_variable_intrinsic_widths(width as u16, &width_ratios, &DISK_HEADERS_LENS); - let intrinsic_widths = &variable_intrinsic_results.0[0..variable_intrinsic_results.1]; - - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Disk ── Esc to go back "; - let repeat_num = max( - 0, - draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2, - ); - let result_title = format!( - " Disk ─{}─ Esc to go back ", - "─".repeat(repeat_num as usize) - ); - result_title - } else if app_state.app_config_fields.use_basic_mode { - String::new() - } else { - " Disk ".to_string() - }; - - let disk_block = if draw_border { - Block::default() - .title(&title) - .title_style(if app_state.is_expanded { - match app_state.current_widget_selected { - WidgetPosition::Disk => self.colours.highlighted_border_style, - _ => self.colours.border_style, - } - } else { - self.colours.widget_title_style - }) - .borders(Borders::ALL) - .border_style(match app_state.current_widget_selected { - WidgetPosition::Disk => self.colours.highlighted_border_style, - _ => self.colours.border_style, - }) - } else { - match app_state.current_widget_selected { - WidgetPosition::Disk => Block::default() - .borders(*SIDE_BORDERS) - .border_style(self.colours.highlighted_border_style), - _ => Block::default().borders(Borders::NONE), - } - }; - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)].as_ref()) - .horizontal_margin(match app_state.current_widget_selected { - WidgetPosition::Disk => 0, - _ if !draw_border => 1, - _ => 0, - }) - .direction(Direction::Horizontal) - .split(draw_loc); - - // Draw! - Table::new(DISK_HEADERS.iter(), disk_rows) - .block(disk_block) - .header_style(self.colours.table_header_style) - .widths( - &(intrinsic_widths - .iter() - .map(|calculated_width| Constraint::Length(*calculated_width as u16)) - .collect::<Vec<_>>()), - ) - .render(f, margined_draw_loc[0]); - } - - fn draw_search_field<B: Backend>( - &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool, - ) { - let pid_search_text = "Search by PID (Tab for Name): "; - let name_search_text = "Search by Name (Tab for PID): "; - let grouped_search_text = "Search by Name: "; - let num_columns = draw_loc.width as usize; - - let chosen_text = if app_state.is_grouped() { - grouped_search_text - } else if app_state.process_search_state.is_searching_with_pid { - pid_search_text - } else { - name_search_text - }; - - let search_title: &str = if chosen_text.len() == min(num_columns / 2, chosen_text.len()) { - chosen_text - } else if chosen_text.is_empty() { - "" - } else { - "> " - }; - - let num_chars_for_text = search_title.len(); - - let mut search_text = vec![Text::styled(search_title, self.colours.table_header_style)]; - - let cursor_position = app_state.get_cursor_position(); - let current_cursor_position = app_state.get_char_cursor_position(); - - let start_position: usize = get_search_start_position( - num_columns - num_chars_for_text - 5, - &app_state.process_search_state.search_state.cursor_direction, - &mut app_state.process_search_state.search_state.cursor_bar, - current_cursor_position, - app_state.is_resized, - ); - - let query = app_state.get_current_search_query().as_str(); - let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true); - let mut current_grapheme_posn = 0; - let query_with_cursor: Vec<Text<'_>> = - if let WidgetPosition::ProcessSearch = app_state.current_widget_selected { - 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 { - Text::styled(grapheme.1, self.colours.currently_selected_text_style) - } else { - Text::styled(grapheme.1, self.colours.text_style) - }; - Some(styled) - } - }) - .collect::<Vec<_>>(); - - if cursor_position >= query.len() { - res.push(Text::styled( - " ", - self.colours.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!) - grapheme_indices - .filter_map(|grapheme| { - current_grapheme_posn += UnicodeWidthStr::width(grapheme.1); - if current_grapheme_posn <= start_position { - None - } else { - let styled = Text::styled(grapheme.1, self.colours.text_style); - Some(styled) - } - }) - .collect::<Vec<_>>() - }; - - // Text options shamelessly stolen from VS Code. - let mut option_text = vec![]; - let case_style = if !app_state.process_search_state.is_ignoring_case { - self.colours.currently_selected_text_style - } else { - self.colours.text_style - }; - - let whole_word_style = if app_state.process_search_state.is_searching_whole_word { - self.colours.currently_selected_text_style - } else { - self.colours.text_style - }; - - let regex_style = if app_state.process_search_state.is_searching_with_regex { - self.colours.currently_selected_text_style - } else { - self.colours.text_style - }; - - let case_text = format!( - "Match Case ({})[{}]", - if self.is_mac_os { "F1" } else { "Alt+C" }, - if !app_state.process_search_state.is_ignoring_case { - "*" - } else { - " " - } - ); - - let whole_text = format!( - "Match Whole Word ({})[{}]", - if self.is_mac_os { "F2" } else { "Alt+W" }, - if app_state.process_search_state.is_searching_whole_word { - "*" - } else { - " " - } - ); - - let regex_text = format!( - "Use Regex ({})[{}]", - if self.is_mac_os { "F3" } else { "Alt+R" }, - if app_state.process_search_state.is_searching_with_regex { - "*" - } else { - " " - } - ); - - let option_row = vec![ - Text::raw("\n\n"), - Text::styled(&case_text, case_style), - Text::raw(" "), - Text::styled(&whole_text, whole_word_style), - Text::raw(" "), - Text::styled(®ex_text, regex_style), - ]; - option_text.extend(option_row); - - search_text.extend(query_with_cursor); - search_text.extend(option_text); - - let current_border_style: Style = if app_state - .process_search_state - .search_state - .is_invalid_search - { - Style::default().fg(Color::Rgb(255, 0, 0)) - } els |