diff --git a/Cargo.lock b/Cargo.lock index 4f6793ca2..b7eeba58c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -620,9 +620,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", @@ -1278,9 +1278,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 596c91ad7..c3fc7690a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ crossterm = "0.28.1" ctrlc = { version = "3.4.5", features = ["termination"] } dirs = "5.0.1" # Maybe consider https://github.com/rust-lang/rustc-hash for some cases too? -hashbrown = "0.15.0" +hashbrown = "0.15.2" humantime = "2.1.0" indexmap = "2.6.0" indoc = "2.0.5" @@ -104,7 +104,7 @@ time = { version = "0.3.36", features = ["local-offset", "formatting", "macros"] # These are just used for JSON schema generation. schemars = { version = "0.8.21", optional = true } -serde_json = { version = "1.0.132", optional = true } +serde_json = { version = "1.0.133", optional = true } strum = { version = "0.26.3", features = ["derive"], optional = true } [target.'cfg(unix)'.dependencies] diff --git a/schema/nightly/bottom.json b/schema/nightly/bottom.json index 9ff9ec9f4..b41322d9e 100644 --- a/schema/nightly/bottom.json +++ b/schema/nightly/bottom.json @@ -955,6 +955,15 @@ } ] }, + "WidgetBorderType": { + "type": "string", + "enum": [ + "Default", + "Rounded", + "Double", + "Thick" + ] + }, "WidgetStyle": { "description": "General styling for generic widgets.", "type": "object", @@ -1014,6 +1023,17 @@ } ] }, + "widget_border_type": { + "description": "Widget borders type.", + "anyOf": [ + { + "$ref": "#/definitions/WidgetBorderType" + }, + { + "type": "null" + } + ] + }, "widget_title": { "description": "Text styling for a widget's title.", "anyOf": [ diff --git a/src/canvas.rs b/src/canvas.rs index c65fc1fec..a7090ef23 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -18,12 +18,12 @@ use crate::{ App, }, constants::*, - options::config::style::ColourPalette, + options::config::style::Styles, }; /// Handles the canvas' state. pub struct Painter { - pub colours: ColourPalette, + pub styles: Styles, previous_height: u16, previous_width: u16, @@ -47,7 +47,7 @@ pub enum LayoutConstraint { } impl Painter { - pub fn init(layout: BottomLayout, styling: ColourPalette) -> anyhow::Result { + pub fn init(layout: BottomLayout, styling: Styles) -> anyhow::Result { // 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. @@ -131,7 +131,7 @@ impl Painter { }); let painter = Painter { - colours: styling, + styles: styling, previous_height: 0, previous_width: 0, row_constraints, @@ -149,9 +149,9 @@ impl Painter { pub fn get_border_style(&self, widget_id: u64, selected_widget_id: u64) -> tui::style::Style { let is_on_widget = widget_id == selected_widget_id; if is_on_widget { - self.colours.highlighted_border_style + self.styles.highlighted_border_style } else { - self.colours.border_style + self.styles.border_style } } @@ -159,7 +159,7 @@ impl Painter { f.render_widget( Paragraph::new(Span::styled( "Frozen, press 'f' to unfreeze", - self.colours.selected_text_style, + self.styles.selected_text_style, )), Layout::default() .horizontal_margin(1) @@ -333,15 +333,11 @@ impl Painter { _ => 0, }; - self.draw_process(f, app_state, rect[0], true, widget_id); + self.draw_process(f, app_state, rect[0], widget_id); + } + Battery => { + self.draw_battery(f, app_state, rect[0], app_state.current_widget.widget_id) } - Battery => self.draw_battery( - f, - app_state, - rect[0], - true, - app_state.current_widget.widget_id, - ), _ => {} } } else if app_state.app_config_fields.use_basic_mode { @@ -444,18 +440,14 @@ impl Painter { ProcSort => 2, _ => 0, }; - self.draw_process(f, app_state, vertical_chunks[3], false, wid); + self.draw_process(f, app_state, vertical_chunks[3], wid); } Temp => { self.draw_temp_table(f, app_state, vertical_chunks[3], widget_id) } - Battery => self.draw_battery( - f, - app_state, - vertical_chunks[3], - false, - widget_id, - ), + Battery => { + self.draw_battery(f, app_state, vertical_chunks[3], widget_id) + } _ => {} } } @@ -729,8 +721,8 @@ impl Painter { Net => self.draw_network(f, app_state, *draw_loc, widget.widget_id), Temp => self.draw_temp_table(f, app_state, *draw_loc, widget.widget_id), Disk => self.draw_disk_table(f, app_state, *draw_loc, widget.widget_id), - Proc => self.draw_process(f, app_state, *draw_loc, true, widget.widget_id), - Battery => self.draw_battery(f, app_state, *draw_loc, true, widget.widget_id), + Proc => self.draw_process(f, app_state, *draw_loc, widget.widget_id), + Battery => self.draw_battery(f, app_state, *draw_loc, widget.widget_id), _ => {} } } diff --git a/src/canvas/components.rs b/src/canvas/components.rs index 05a63112b..d349352d0 100644 --- a/src/canvas/components.rs +++ b/src/canvas/components.rs @@ -3,6 +3,7 @@ pub mod data_table; pub mod time_graph; mod tui_widget; + pub mod widget_carousel; pub use tui_widget::*; diff --git a/src/canvas/components/data_table/draw.rs b/src/canvas/components/data_table/draw.rs index 44d7b50a1..8dc39c81a 100644 --- a/src/canvas/components/data_table/draw.rs +++ b/src/canvas/components/data_table/draw.rs @@ -7,10 +7,9 @@ use concat_string::concat_string; use tui::{ layout::{Constraint, Direction, Layout, Rect}, text::{Line, Span, Text}, - widgets::{Block, Borders, Row, Table}, + widgets::{Block, Row, Table}, Frame, }; -use unicode_segmentation::UnicodeSegmentation; use super::{ CalculateColumnWidths, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataToCell, @@ -18,8 +17,8 @@ use super::{ }; use crate::{ app::layout_manager::BottomWidget, - canvas::Painter, - constants::{SIDE_BORDERS, TABLE_GAP_HEIGHT_LIMIT}, + canvas::{drawing_utils::widget_block, Painter}, + constants::TABLE_GAP_HEIGHT_LIMIT, utils::strings::truncate_to_text, }; @@ -68,46 +67,41 @@ where C: DataTableColumn, { fn block<'a>(&self, draw_info: &'a DrawInfo, data_len: usize) -> Block<'a> { - let border_style = match draw_info.selection_state { - SelectionState::NotSelected => self.styling.border_style, - SelectionState::Selected | SelectionState::Expanded => { - self.styling.highlighted_border_style - } + let is_selected = match draw_info.selection_state { + SelectionState::NotSelected => false, + SelectionState::Selected | SelectionState::Expanded => true, }; - if !self.props.is_basic { - let block = Block::default() - .borders(Borders::ALL) - .border_style(border_style); + let border_style = if is_selected { + self.styling.highlighted_border_style + } else { + self.styling.border_style + }; - if let Some(title) = self.generate_title(draw_info, data_len) { - block.title(title) - } else { - block + let mut block = widget_block(self.props.is_basic, is_selected, self.styling.border_type) + .border_style(border_style); + + if let Some((left_title, right_title)) = self.generate_title(draw_info, data_len) { + if !self.props.is_basic { + block = block.title_top(left_title); + } + + if let Some(right_title) = right_title { + block = block.title_top(right_title); } - } else if draw_info.is_on_widget() { - // Implies it is basic mode but selected. - Block::default() - .borders(SIDE_BORDERS) - .border_style(border_style) - } else { - Block::default().borders(Borders::NONE) } + + block } /// Generates a title, given the available space. - pub fn generate_title<'a>( - &self, draw_info: &'a DrawInfo, total_items: usize, - ) -> Option> { + fn generate_title( + &self, draw_info: &'_ DrawInfo, total_items: usize, + ) -> Option<(Line<'static>, Option>)> { self.props.title.as_ref().map(|title| { let current_index = self.state.current_index.saturating_add(1); let draw_loc = draw_info.loc; let title_style = self.styling.title_style; - let border_style = if draw_info.is_on_widget() { - self.styling.highlighted_border_style - } else { - self.styling.border_style - }; let title = if self.props.show_table_scroll_position { let pos = current_index.to_string(); @@ -123,19 +117,15 @@ where title.to_string() }; - if draw_info.is_expanded() { - let title_base = concat_string!(title, "── Esc to go back "); - let lines = "─".repeat(usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(title_base.as_str(), true).count() + 2, - )); - let esc = concat_string!("─", lines, "─ Esc to go back "); - Line::from(vec![ - Span::styled(title, title_style), - Span::styled(esc, border_style), - ]) + let left_title = Line::from(Span::styled(title, title_style)).left_aligned(); + + let right_title = if draw_info.is_expanded() { + Some(Line::from(" Esc to go back ").right_aligned()) } else { - Line::from(Span::styled(title, title_style)) - } + None + }; + + (left_title, right_title) }) } @@ -202,8 +192,9 @@ where if !self.data.is_empty() || !self.first_draw { if self.first_draw { - self.first_draw = false; // TODO: Doing it this way is fine, but it could be done better (e.g. showing - // custom no results/entries message) + // TODO: Doing it this way is fine, but it could be done better (e.g. showing + // custom no results/entries message) + self.first_draw = false; if let Some(first_index) = self.first_index { self.set_position(first_index); } diff --git a/src/canvas/components/data_table/styling.rs b/src/canvas/components/data_table/styling.rs index 0006e8ec1..1fd63a027 100644 --- a/src/canvas/components/data_table/styling.rs +++ b/src/canvas/components/data_table/styling.rs @@ -1,11 +1,12 @@ -use tui::style::Style; +use tui::{style::Style, widgets::BorderType}; -use crate::options::config::style::ColourPalette; +use crate::options::config::style::Styles; #[derive(Default)] pub struct DataTableStyling { pub header_style: Style, pub border_style: Style, + pub border_type: BorderType, pub highlighted_border_style: Style, pub text_style: Style, pub highlighted_text_style: Style, @@ -13,14 +14,15 @@ pub struct DataTableStyling { } impl DataTableStyling { - pub fn from_palette(colours: &ColourPalette) -> Self { + pub fn from_palette(styles: &Styles) -> Self { Self { - header_style: colours.table_header_style, - border_style: colours.border_style, - highlighted_border_style: colours.highlighted_border_style, - text_style: colours.text_style, - highlighted_text_style: colours.selected_text_style, - title_style: colours.widget_title_style, + header_style: styles.table_header_style, + border_style: styles.border_style, + border_type: styles.border_type, + highlighted_border_style: styles.highlighted_border_style, + text_style: styles.text_style, + highlighted_text_style: styles.selected_text_style, + title_style: styles.widget_title_style, } } } diff --git a/src/canvas/components/time_graph.rs b/src/canvas/components/time_graph.rs index 3d4b1221b..7dc85334d 100644 --- a/src/canvas/components/time_graph.rs +++ b/src/canvas/components/time_graph.rs @@ -6,10 +6,11 @@ use tui::{ style::Style, symbols::Marker, text::{Line, Span}, - widgets::{Block, Borders, GraphType}, + widgets::{BorderType, GraphType}, Frame, }; -use unicode_segmentation::UnicodeSegmentation; + +use crate::canvas::drawing_utils::widget_block; use super::time_chart::{ Axis, Dataset, LegendPosition, Point, TimeChart, DEFAULT_LEGEND_CONSTRAINTS, @@ -42,9 +43,15 @@ pub struct TimeGraph<'a> { /// The border style. pub border_style: Style, + /// The border type. + pub border_type: BorderType, + /// The graph title. pub title: Cow<'a, str>, + /// Whether this graph is selected. + pub is_selected: bool, + /// Whether this graph is expanded. pub is_expanded: bool, @@ -100,29 +107,6 @@ impl TimeGraph<'_> { ) } - /// Generates a title for the [`TimeGraph`] widget, given the available - /// space. - fn generate_title(&self, draw_loc: Rect) -> Line<'_> { - if self.is_expanded { - let title_base = concat_string!(self.title, "── Esc to go back "); - Line::from(vec![ - Span::styled(self.title.as_ref(), self.title_style), - Span::styled( - concat_string!( - "─", - "─".repeat(usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(title_base.as_str(), true).count() + 2 - )), - "─ Esc to go back " - ), - self.border_style, - ), - ]) - } else { - Line::from(Span::styled(self.title.as_ref(), self.title_style)) - } - } - /// Draws a time graph at [`Rect`] location provided by `draw_loc`. A time /// graph is used to display data points throughout time in the x-axis. /// @@ -139,10 +123,18 @@ impl TimeGraph<'_> { // This is some ugly manual loop unswitching. Maybe unnecessary. // TODO: Optimize this step. Cut out unneeded points. let data = graph_data.iter().map(create_dataset).collect(); - let block = Block::default() - .title(self.generate_title(draw_loc)) - .borders(Borders::ALL) - .border_style(self.border_style); + + let block = { + let mut b = widget_block(false, self.is_selected, self.border_type) + .border_style(self.border_style) + .title_top(Line::styled(self.title.as_ref(), self.title_style)); + + if self.is_expanded { + b = b.title_top(Line::styled(" Esc to go back ", self.title_style).right_aligned()) + } + + b + }; f.render_widget( TimeChart::new(data) @@ -186,10 +178,10 @@ mod test { use std::borrow::Cow; use tui::{ - layout::Rect, style::{Color, Style}, symbols::Marker, - text::{Line, Span}, + text::Span, + widgets::BorderType, }; use super::TimeGraph; @@ -210,6 +202,8 @@ mod test { y_labels: &Y_LABELS, graph_style: Style::default().fg(Color::Red), border_style: Style::default().fg(Color::Blue), + border_type: BorderType::Plain, + is_selected: false, is_expanded: false, title_style: Style::default().fg(Color::Cyan), legend_position: None, @@ -252,26 +246,4 @@ mod test { assert_eq!(y_axis.labels, actual.labels); assert_eq!(y_axis.style, actual.style); } - - #[test] - fn time_graph_gen_title() { - let mut time_graph = create_time_graph(); - let draw_loc = Rect::new(0, 0, 32, 100); - - let title = time_graph.generate_title(draw_loc); - assert_eq!( - title, - Line::from(Span::styled(" Network ", Style::default().fg(Color::Cyan))) - ); - - time_graph.is_expanded = true; - let title = time_graph.generate_title(draw_loc); - assert_eq!( - title, - Line::from(vec![ - Span::styled(" Network ", Style::default().fg(Color::Cyan)), - Span::styled("───── Esc to go back ", Style::default().fg(Color::Blue)) - ]) - ); - } } diff --git a/src/canvas/components/widget_carousel.rs b/src/canvas/components/widget_carousel.rs index b6911cc10..c1bdb12e1 100644 --- a/src/canvas/components/widget_carousel.rs +++ b/src/canvas/components/widget_carousel.rs @@ -84,26 +84,25 @@ impl Painter { }, ); + // TODO: I can do this text effect as just a border now! let left_name = left_table.get_pretty_name(); let right_name = right_table.get_pretty_name(); - let num_spaces = usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len()); + let carousel_text_style = if widget_id == app_state.current_widget.widget_id { + self.styles.highlighted_border_style + } else { + self.styles.text_style + }; let left_arrow_text = vec![ Line::default(), - Line::from(Span::styled( - format!("◄ {left_name}"), - self.colours.text_style, - )), + Line::from(Span::styled(format!("◄ {left_name}"), carousel_text_style)), ]; let right_arrow_text = vec![ Line::default(), - Line::from(Span::styled( - format!("{right_name} ►"), - self.colours.text_style, - )), + Line::from(Span::styled(format!("{right_name} ►"), carousel_text_style)), ]; let margined_draw_loc = Layout::default() diff --git a/src/canvas/dialogs/dd_dialog.rs b/src/canvas/dialogs/dd_dialog.rs index 3659ba112..1052a153f 100644 --- a/src/canvas/dialogs/dd_dialog.rs +++ b/src/canvas/dialogs/dd_dialog.rs @@ -4,19 +4,16 @@ use std::cmp::min; use tui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, text::{Line, Span, Text}, - widgets::{Block, Borders, Paragraph, Wrap}, + widgets::{Block, Paragraph, Wrap}, Frame, }; use crate::{ app::{App, KillSignal, MAX_PROCESS_SIGNAL}, - canvas::Painter, + canvas::{drawing_utils::dialog_block, Painter}, widgets::ProcWidgetMode, }; -const DD_BASE: &str = " Confirm Kill Process ── Esc to close "; -const DD_ERROR_BASE: &str = " Error ── Esc to close "; - cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { const SIGNAL_TEXT: [&str; 63] = [ @@ -211,12 +208,12 @@ impl Painter { if MAX_PROCESS_SIGNAL == 1 || !app_state.app_config_fields.is_advanced_kill { let (yes_button, no_button) = match app_state.delete_dialog_state.selected_signal { KillSignal::Kill(_) => ( - Span::styled("Yes", self.colours.selected_text_style), - Span::styled("No", self.colours.text_style), + Span::styled("Yes", self.styles.selected_text_style), + Span::styled("No", self.styles.text_style), ), KillSignal::Cancel => ( - Span::styled("Yes", self.colours.text_style), - Span::styled("No", self.colours.selected_text_style), + Span::styled("Yes", self.styles.text_style), + Span::styled("No", self.styles.selected_text_style), ), }; @@ -322,11 +319,11 @@ impl Painter { let mut buttons = SIGNAL_TEXT [scroll_offset + 1..min((layout.len()) + scroll_offset, SIGNAL_TEXT.len())] .iter() - .map(|text| Span::styled(*text, self.colours.text_style)) + .map(|text| Span::styled(*text, self.styles.text_style)) .collect::>>(); - buttons.insert(0, Span::styled(SIGNAL_TEXT[0], self.colours.text_style)); + buttons.insert(0, Span::styled(SIGNAL_TEXT[0], self.styles.text_style)); buttons[selected - scroll_offset] = - Span::styled(SIGNAL_TEXT[selected], self.colours.selected_text_style); + Span::styled(SIGNAL_TEXT[selected], self.styles.selected_text_style); app_state.delete_dialog_state.button_positions = layout .iter() @@ -354,45 +351,24 @@ impl Painter { ) -> bool { if let Some(dd_text) = dd_text { let dd_title = if app_state.dd_err.is_some() { - Line::from(vec![ - Span::styled(" Error ", self.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to close ", - "─".repeat( - usize::from(draw_loc.width) - .saturating_sub(DD_ERROR_BASE.chars().count() + 2) - ) - ), - self.colours.border_style, - ), - ]) + Line::styled(" Error ", self.styles.widget_title_style) } else { - Line::from(vec![ - Span::styled(" Confirm Kill Process ", self.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to close ", - "─".repeat( - usize::from(draw_loc.width) - .saturating_sub(DD_BASE.chars().count() + 2) - ) - ), - self.colours.border_style, - ), - ]) + Line::styled(" Confirm Kill Process ", self.styles.widget_title_style) }; f.render_widget( Paragraph::new(dd_text) .block( - Block::default() - .title(dd_title) - .style(self.colours.border_style) - .borders(Borders::ALL) - .border_style(self.colours.border_style), + dialog_block(self.styles.border_type) + .title_top(dd_title) + .title_top( + Line::styled(" Esc to close ", self.styles.widget_title_style) + .right_aligned(), + ) + .style(self.styles.border_style) + .border_style(self.styles.border_style), ) - .style(self.colours.text_style) + .style(self.styles.text_style) .alignment(Alignment::Center) .wrap(Wrap { trim: true }), draw_loc, diff --git a/src/canvas/dialogs/help_dialog.rs b/src/canvas/dialogs/help_dialog.rs index ee3fa3c60..ea7ca3150 100644 --- a/src/canvas/dialogs/help_dialog.rs +++ b/src/canvas/dialogs/help_dialog.rs @@ -3,19 +3,17 @@ use std::cmp::{max, min}; use tui::{ layout::{Alignment, Rect}, text::{Line, Span}, - widgets::{Block, Borders, Paragraph, Wrap}, + widgets::{Paragraph, Wrap}, Frame, }; use unicode_width::UnicodeWidthStr; use crate::{ app::App, - canvas::Painter, + canvas::{drawing_utils::dialog_block, Painter}, constants::{self, HELP_TEXT}, }; -const HELP_BASE: &str = " Help ── Esc to close "; - // TODO: [REFACTOR] Make generic dialog boxes to build off of instead? impl Painter { fn help_text_lines(&self) -> Vec> { @@ -28,12 +26,12 @@ impl Painter { if itx > 0 { if let Some(header) = section.next() { styled_help_spans.push(Span::default()); - styled_help_spans.push(Span::styled(*header, self.colours.table_header_style)); + styled_help_spans.push(Span::styled(*header, self.styles.table_header_style)); } } section.for_each(|&text| { - styled_help_spans.push(Span::styled(text, self.colours.text_style)) + styled_help_spans.push(Span::styled(text, self.styles.text_style)) }); }); @@ -43,24 +41,12 @@ impl Painter { pub fn draw_help_dialog(&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect) { let styled_help_text = self.help_text_lines(); - let help_title = Line::from(vec![ - Span::styled(" Help ", self.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to close ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2) - ) - ), - self.colours.border_style, - ), - ]); - - let block = Block::default() - .title(help_title) - .style(self.colours.border_style) - .borders(Borders::ALL) - .border_style(self.colours.border_style); + let block = dialog_block(self.styles.border_type) + .border_style(self.styles.border_style) + .title_top(Line::styled(" Help ", self.styles.widget_title_style)) + .title_top( + Line::styled(" Esc to close ", self.styles.widget_title_style).right_aligned(), + ); if app_state.should_get_widget_bounds() { // We must also recalculate how many lines are wrapping to properly get @@ -116,7 +102,7 @@ impl Painter { f.render_widget( Paragraph::new(styled_help_text.clone()) .block(block) - .style(self.colours.text_style) + .style(self.styles.text_style) .alignment(Alignment::Left) .wrap(Wrap { trim: true }) .scroll(( diff --git a/src/canvas/drawing_utils.rs b/src/canvas/drawing_utils.rs index 8a6589294..5f243f0b6 100644 --- a/src/canvas/drawing_utils.rs +++ b/src/canvas/drawing_utils.rs @@ -1,6 +1,11 @@ use std::{cmp::min, time::Instant}; -use tui::layout::Rect; +use tui::{ + layout::Rect, + widgets::{Block, BorderType, Borders}, +}; + +use super::SIDE_BORDERS; /// Calculate how many bars are to be drawn within basic mode's components. pub fn calculate_basic_use_bars(use_percentage: f64, num_bars_available: usize) -> usize { @@ -30,6 +35,30 @@ pub fn should_hide_x_label( } } +/// Return a widget block. +pub fn widget_block(is_basic: bool, is_selected: bool, border_type: BorderType) -> Block<'static> { + let mut block = Block::default().border_type(border_type); + + if is_basic { + if is_selected { + block = block.borders(SIDE_BORDERS); + } else { + block = block.borders(Borders::empty()); + } + } else { + block = block.borders(Borders::all()); + } + + block +} + +/// Return a dialog block. +pub fn dialog_block(border_type: BorderType) -> Block<'static> { + Block::default() + .border_type(border_type) + .borders(Borders::all()) +} + #[cfg(test)] mod test { diff --git a/src/canvas/widgets/battery_display.rs b/src/canvas/widgets/battery_display.rs index ae329b901..2007b624f 100644 --- a/src/canvas/widgets/battery_display.rs +++ b/src/canvas/widgets/battery_display.rs @@ -1,23 +1,24 @@ use tui::{ layout::{Constraint, Direction, Layout, Rect}, text::{Line, Span}, - widgets::{Block, Borders, Cell, Paragraph, Row, Table, Tabs}, + widgets::{Cell, Paragraph, Row, Table, Tabs}, Frame, }; -use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; use crate::{ app::App, - canvas::{drawing_utils::calculate_basic_use_bars, Painter}, + canvas::{ + drawing_utils::{calculate_basic_use_bars, widget_block}, + Painter, + }, constants::*, data_conversion::BatteryDuration, }; impl Painter { pub fn draw_battery( - &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool, - widget_id: u64, + &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64, ) { let should_get_widget_bounds = app_state.should_get_widget_bounds(); if let Some(battery_widget_state) = app_state @@ -28,9 +29,9 @@ impl Painter { { let is_on_widget = widget_id == app_state.current_widget.widget_id; let border_style = if is_on_widget { - self.colours.highlighted_border_style + self.styles.highlighted_border_style } else { - self.colours.border_style + self.styles.border_style }; let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT { 0 @@ -38,35 +39,23 @@ impl Painter { app_state.app_config_fields.table_gap }; - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Battery ── Esc to go back "; - Line::from(vec![ - Span::styled(" Battery ", self.colours.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat(usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2 - )) - ), - border_style, - ), - ]) - } else { - Line::from(Span::styled(" Battery ", self.colours.widget_title_style)) - }; + let block = { + let mut block = widget_block( + app_state.app_config_fields.use_basic_mode, + is_on_widget, + self.styles.border_type, + ) + .border_style(border_style) + .title_top(Line::styled(" Battery ", self.styles.widget_title_style)); + + if app_state.is_expanded { + block = block.title_top( + Line::styled(" Esc to go back ", self.styles.widget_title_style) + .right_aligned(), + ) + } - let battery_block = if draw_border { - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(border_style) - } else if is_on_widget { - Block::default() - .borders(SIDE_BORDERS) - .border_style(self.colours.highlighted_border_style) - } else { - Block::default().borders(Borders::NONE) + block }; if app_state.converted_data.battery_data.len() > 1 { @@ -95,8 +84,8 @@ impl Painter { .collect::>(), ) .divider(tui::symbols::line::VERTICAL) - .style(self.colours.text_style) - .highlight_style(self.colours.selected_text_style) + .style(self.styles.text_style) + .highlight_style(self.styles.selected_text_style) .select(battery_widget_state.currently_selected_battery_index), tab_draw_loc, ); @@ -120,9 +109,11 @@ impl Painter { } } + let is_basic = app_state.app_config_fields.use_basic_mode; + let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) - .horizontal_margin(u16::from(!(is_on_widget || draw_border))) + .horizontal_margin(u16::from(!(is_on_widget || is_basic))) .direction(Direction::Horizontal) .split(draw_loc)[0]; @@ -144,15 +135,15 @@ impl Painter { let mut battery_charge_rows = Vec::with_capacity(2); battery_charge_rows.push(Row::new([ - Cell::from("Charge").style(self.colours.text_style) + Cell::from("Charge").style(self.styles.text_style) ])); battery_charge_rows.push(Row::new([Cell::from(bars).style( if charge_percentage < 10.0 { - self.colours.low_battery + self.styles.low_battery } else if charge_percentage < 50.0 { - self.colours.medium_battery + self.styles.medium_battery } else { - self.colours.high_battery + self.styles.high_battery }, )])); @@ -160,16 +151,16 @@ impl Painter { battery_rows.push(Row::new([""]).bottom_margin(table_gap + 1)); battery_rows.push( Row::new(["Rate", &battery_details.watt_consumption]) - .style(self.colours.text_style), + .style(self.styles.text_style), ); battery_rows.push( - Row::new(["State", &battery_details.state]).style(self.colours.text_style), + Row::new(["State", &battery_details.state]).style(self.styles.text_style), ); let mut time: String; // Keep string lifetime in scope. { - let style = self.colours.text_style; + let style = self.styles.text_style; match &battery_details.battery_duration { BatteryDuration::ToEmpty(secs) => { time = long_time(*secs); @@ -198,7 +189,7 @@ impl Painter { } battery_rows.push( - Row::new(["Health", &battery_details.health]).style(self.colours.text_style), + Row::new(["Health", &battery_details.health]).style(self.styles.text_style), ); let header = if app_state.converted_data.battery_data.len() > 1 { @@ -210,7 +201,7 @@ impl Painter { // Draw bar f.render_widget( Table::new(battery_charge_rows, [Constraint::Percentage(100)]) - .block(battery_block.clone()) + .block(block.clone()) .header(header.clone()), margined_draw_loc, ); @@ -221,7 +212,7 @@ impl Painter { battery_rows, [Constraint::Percentage(50), Constraint::Percentage(50)], ) - .block(battery_block) + .block(block) .header(header), margined_draw_loc, ); @@ -230,13 +221,10 @@ impl Painter { contents.push(Line::from(Span::styled( "No data found for this battery", - self.colours.text_style, + self.styles.text_style, ))); - f.render_widget( - Paragraph::new(contents).block(battery_block), - margined_draw_loc, - ); + f.render_widget(Paragraph::new(contents).block(block), margined_draw_loc); } if should_get_widget_bounds { @@ -253,7 +241,6 @@ impl Painter { } } -#[inline] fn get_hms(secs: i64) -> (i64, i64, i64) { let hours = secs / (60 * 60); let minutes = (secs / 60) - hours * 60; @@ -266,23 +253,16 @@ fn long_time(secs: i64) -> String { let (hours, minutes, seconds) = get_hms(secs); if hours > 0 { - format!( - "{} hour{}, {} minute{}, {} second{}", - hours, - if hours == 1 { "" } else { "s" }, - minutes, - if minutes == 1 { "" } else { "s" }, - seconds, - if seconds == 1 { "" } else { "s" }, - ) + let h = if hours == 1 { "hour" } else { "hours" }; + let m = if minutes == 1 { "minute" } else { "minutes" }; + let s = if seconds == 1 { "second" } else { "seconds" }; + + format!("{hours} {h}, {minutes} {m}, {seconds} {s}") } else { - format!( - "{} minute{}, {} second{}", - minutes, - if minutes == 1 { "" } else { "s" }, - seconds, - if seconds == 1 { "" } else { "s" }, - ) + let m = if minutes == 1 { "minute" } else { "minutes" }; + let s = if seconds == 1 { "second" } else { "seconds" }; + + format!("{minutes} {m}, {seconds} {s}") } } diff --git a/src/canvas/widgets/cpu_basic.rs b/src/canvas/widgets/cpu_basic.rs index e0bc0e863..874f0ed53 100644 --- a/src/canvas/widgets/cpu_basic.rs +++ b/src/canvas/widgets/cpu_basic.rs @@ -2,7 +2,6 @@ use std::cmp::min; use tui::{ layout::{Constraint, Direction, Layout, Rect}, - widgets::Block, Frame, }; @@ -10,9 +9,9 @@ use crate::{ app::App, canvas::{ components::pipe_gauge::{LabelLimit, PipeGauge}, + drawing_utils::widget_block, Painter, }, - constants::*, data_collection::cpu::CpuDataType, data_conversion::CpuWidgetData, }; @@ -38,9 +37,8 @@ impl Painter { if app_state.current_widget.widget_id == widget_id { f.render_widget( - Block::default() - .borders(SIDE_BORDERS) - .border_style(self.colours.highlighted_border_style), + widget_block(true, true, self.styles.border_type) + .border_style(self.styles.highlighted_border_style), draw_loc, ); } @@ -156,10 +154,10 @@ impl Painter { }; let (outer, style) = match data_type { - CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_cpu_colour), + CpuDataType::Avg => ("AVG".to_string(), self.styles.avg_cpu_colour), CpuDataType::Cpu(index) => ( format!("{index:<3}",), - self.colours.cpu_colour_styles[index % self.colours.cpu_colour_styles.len()], + self.styles.cpu_colour_styles[index % self.styles.cpu_colour_styles.len()], ), }; let inner = format!("{:>3.0}%", last_entry.round()); diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs index 80946e85c..1d416ee88 100644 --- a/src/canvas/widgets/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -136,13 +136,13 @@ impl Painter { CpuWidgetData::All => None, CpuWidgetData::Entry { data, .. } => { let style = if show_avg_cpu && itx == AVG_POSITION { - self.colours.avg_cpu_colour + self.styles.avg_cpu_colour } else if itx == ALL_POSITION { - self.colours.all_cpu_colour + self.styles.all_cpu_colour } 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()] + self.styles.cpu_colour_styles[(offset_position - show_avg_offset) + % self.styles.cpu_colour_styles.len()] }; Some(GraphData { @@ -158,11 +158,11 @@ impl Painter { cpu_data.get(current_scroll_position) { let style = if show_avg_cpu && current_scroll_position == AVG_POSITION { - self.colours.avg_cpu_colour + self.styles.avg_cpu_colour } 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()] + self.styles.cpu_colour_styles + [(offset_position - show_avg_offset) % self.styles.cpu_colour_styles.len()] }; vec![GraphData { @@ -228,11 +228,13 @@ impl Painter { hide_x_labels, y_bounds: Y_BOUNDS, y_labels: &Y_LABELS, - graph_style: self.colours.graph_style, + graph_style: self.styles.graph_style, border_style, + border_type: self.styles.border_type, title, + is_selected: app_state.current_widget.widget_id == widget_id, is_expanded: app_state.is_expanded, - title_style: self.colours.widget_title_style, + title_style: self.styles.widget_title_style, legend_position: None, legend_constraints: None, marker, diff --git a/src/canvas/widgets/mem_basic.rs b/src/canvas/widgets/mem_basic.rs index 2250a836b..bee7d18dd 100644 --- a/src/canvas/widgets/mem_basic.rs +++ b/src/canvas/widgets/mem_basic.rs @@ -1,13 +1,11 @@ use tui::{ layout::{Constraint, Direction, Layout, Rect}, - widgets::Block, Frame, }; use crate::{ app::App, - canvas::{components::pipe_gauge::PipeGauge, Painter}, - constants::*, + canvas::{components::pipe_gauge::PipeGauge, drawing_utils::widget_block, Painter}, }; impl Painter { @@ -19,9 +17,8 @@ impl Painter { if app_state.current_widget.widget_id == widget_id { f.render_widget( - Block::default() - .borders(SIDE_BORDERS) - .border_style(self.colours.highlighted_border_style), + widget_block(true, true, self.styles.border_type) + .border_style(self.styles.highlighted_border_style), draw_loc, ); } @@ -50,8 +47,8 @@ impl Painter { .ratio(ram_percentage / 100.0) .start_label("RAM") .inner_label(memory_fraction_label) - .label_style(self.colours.ram_style) - .gauge_style(self.colours.ram_style), + .label_style(self.styles.ram_style) + .gauge_style(self.styles.ram_style), ); #[cfg(not(target_os = "windows"))] @@ -75,8 +72,8 @@ impl Painter { .ratio(cache_percentage / 100.0) .start_label("CHE") .inner_label(cache_fraction_label) - .label_style(self.colours.cache_style) - .gauge_style(self.colours.cache_style), + .label_style(self.styles.cache_style) + .gauge_style(self.styles.cache_style), ); } } @@ -100,8 +97,8 @@ impl Painter { .ratio(swap_percentage / 100.0) .start_label("SWP") .inner_label(swap_fraction_label) - .label_style(self.colours.swap_style) - .gauge_style(self.colours.swap_style), + .label_style(self.styles.swap_style) + .gauge_style(self.styles.swap_style), ); } @@ -124,8 +121,8 @@ impl Painter { .ratio(arc_percentage / 100.0) .start_label("ARC") .inner_label(arc_fraction_label) - .label_style(self.colours.arc_style) - .gauge_style(self.colours.arc_style), + .label_style(self.styles.arc_style) + .gauge_style(self.styles.arc_style), ); } } @@ -133,7 +130,7 @@ impl Painter { #[cfg(feature = "gpu")] { if let Some(gpu_data) = &app_state.converted_data.gpu_data { - let gpu_styles = &self.colours.gpu_colours; + let gpu_styles = &self.styles.gpu_colours; let mut color_index = 0; gpu_data.iter().for_each(|gpu_data_vec| { diff --git a/src/canvas/widgets/mem_graph.rs b/src/canvas/widgets/mem_graph.rs index 699fc6508..bb82f6fdb 100644 --- a/src/canvas/widgets/mem_graph.rs +++ b/src/canvas/widgets/mem_graph.rs @@ -55,7 +55,7 @@ impl Painter { let mem_label = format!("RAM:{label_percent}{label_frac}"); points.push(GraphData { points: &app_state.converted_data.mem_data, - style: self.colours.ram_style, + style: self.styles.ram_style, name: Some(mem_label.into()), }); } @@ -64,7 +64,7 @@ impl Painter { let cache_label = format!("CHE:{label_percent}{label_frac}"); points.push(GraphData { points: &app_state.converted_data.cache_data, - style: self.colours.cache_style, + style: self.styles.cache_style, name: Some(cache_label.into()), }); } @@ -72,7 +72,7 @@ impl Painter { let swap_label = format!("SWP:{label_percent}{label_frac}"); points.push(GraphData { points: &app_state.converted_data.swap_data, - style: self.colours.swap_style, + style: self.styles.swap_style, name: Some(swap_label.into()), }); } @@ -81,7 +81,7 @@ impl Painter { let arc_label = format!("ARC:{label_percent}{label_frac}"); points.push(GraphData { points: &app_state.converted_data.arc_data, - style: self.colours.arc_style, + style: self.styles.arc_style, name: Some(arc_label.into()), }); } @@ -89,7 +89,7 @@ impl Painter { { if let Some(gpu_data) = &app_state.converted_data.gpu_data { let mut color_index = 0; - let gpu_styles = &self.colours.gpu_colours; + let gpu_styles = &self.styles.gpu_colours; gpu_data.iter().for_each(|gpu| { let gpu_label = format!("{}:{}{}", gpu.name, gpu.mem_percent, gpu.mem_total); @@ -128,11 +128,13 @@ impl Painter { hide_x_labels, y_bounds: Y_BOUNDS, y_labels: &Y_LABELS, - graph_style: self.colours.graph_style, + graph_style: self.styles.graph_style, border_style, + border_type: self.styles.border_type, title: " Memory ".into(), + is_selected: app_state.current_widget.widget_id == widget_id, is_expanded: app_state.is_expanded, - title_style: self.colours.widget_title_style, + title_style: self.styles.widget_title_style, legend_position: app_state.app_config_fields.memory_legend_position, legend_constraints: Some((Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))), marker, diff --git a/src/canvas/widgets/network_basic.rs b/src/canvas/widgets/network_basic.rs index 5dd2b7493..6451fecf6 100644 --- a/src/canvas/widgets/network_basic.rs +++ b/src/canvas/widgets/network_basic.rs @@ -5,7 +5,10 @@ use tui::{ Frame, }; -use crate::{app::App, canvas::Painter, constants::*}; +use crate::{ + app::App, + canvas::{drawing_utils::widget_block, Painter}, +}; impl Painter { pub fn draw_basic_network( @@ -30,9 +33,8 @@ impl Painter { if app_state.current_widget.widget_id == widget_id { f.render_widget( - Block::default() - .borders(SIDE_BORDERS) - .border_style(self.colours.highlighted_border_style), + widget_block(true, true, self.styles.border_type) + .border_style(self.styles.highlighted_border_style), draw_loc, ); } @@ -43,13 +45,13 @@ impl Painter { let total_tx_label = format!("Total TX: {}", app_state.converted_data.total_tx_display); let net_text = vec![ - Line::from(Span::styled(rx_label, self.colours.rx_style)), - Line::from(Span::styled(tx_label, self.colours.tx_style)), + Line::from(Span::styled(rx_label, self.styles.rx_style)), + Line::from(Span::styled(tx_label, self.styles.tx_style)), ]; let total_net_text = vec![ - Line::from(Span::styled(total_rx_label, self.colours.total_rx_style)), - Line::from(Span::styled(total_tx_label, self.colours.total_tx_style)), + Line::from(Span::styled(total_rx_label, self.styles.total_rx_style)), + Line::from(Span::styled(total_tx_label, self.styles.total_tx_style)), ]; f.render_widget(Paragraph::new(net_text).block(Block::default()), net_loc[0]); diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs index 30cb67597..5b9441b57 100644 --- a/src/canvas/widgets/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -107,17 +107,17 @@ impl Painter { vec![ GraphData { points: network_data_rx, - style: self.colours.rx_style, + style: self.styles.rx_style, name: Some(format!("RX: {:7}", app_state.converted_data.rx_display).into()), }, GraphData { points: network_data_tx, - style: self.colours.tx_style, + style: self.styles.tx_style, name: Some(format!("TX: {:7}", app_state.converted_data.tx_display).into()), }, GraphData { points: &[], - style: self.colours.total_rx_style, + style: self.styles.total_rx_style, name: Some( format!("Total RX: {:7}", app_state.converted_data.total_rx_display) .into(), @@ -125,7 +125,7 @@ impl Painter { }, GraphData { points: &[], - style: self.colours.total_tx_style, + style: self.styles.total_tx_style, name: Some( format!("Total TX: {:7}", app_state.converted_data.total_tx_display) .into(), @@ -136,12 +136,12 @@ impl Painter { vec![ GraphData { points: network_data_rx, - style: self.colours.rx_style, + style: self.styles.rx_style, name: Some((&app_state.converted_data.rx_display).into()), }, GraphData { points: network_data_tx, - style: self.colours.tx_style, + style: self.styles.tx_style, name: Some((&app_state.converted_data.tx_display).into()), }, ] @@ -158,11 +158,13 @@ impl Painter { hide_x_labels, y_bounds, y_labels: &y_labels, - graph_style: self.colours.graph_style, + graph_style: self.styles.graph_style, border_style, + border_type: self.styles.border_type, title: " Network ".into(), + is_selected: app_state.current_widget.widget_id == widget_id, is_expanded: app_state.is_expanded, - title_style: self.colours.widget_title_style, + title_style: self.styles.widget_title_style, legend_position: app_state.app_config_fields.network_legend_position, legend_constraints: Some(legend_constraints), marker, @@ -183,10 +185,10 @@ impl Painter { // Gross but I need it to work... let total_network = vec![Row::new([ - Text::styled(rx_display, self.colours.rx_style), - Text::styled(tx_display, self.colours.tx_style), - Text::styled(total_rx_display, self.colours.total_rx_style), - Text::styled(total_tx_display, self.colours.total_tx_style), + Text::styled(rx_display, self.styles.rx_style), + Text::styled(tx_display, self.styles.tx_style), + Text::styled(total_rx_display, self.styles.total_rx_style), + Text::styled(total_tx_display, self.styles.total_tx_style), ])]; // Draw @@ -198,15 +200,15 @@ impl Painter { .map(Constraint::Length) .collect::>()), ) - .header(Row::new(NETWORK_HEADERS).style(self.colours.table_header_style)) + .header(Row::new(NETWORK_HEADERS).style(self.styles.table_header_style)) .block(Block::default().borders(Borders::ALL).border_style( if app_state.current_widget.widget_id == widget_id { - self.colours.highlighted_border_style + self.styles.highlighted_border_style } else { - self.colours.border_style + self.styles.border_style }, )) - .style(self.colours.text_style), + .style(self.styles.text_style), draw_loc, ); } diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs index 375a7dd50..c19d3875b 100644 --- a/src/canvas/widgets/process_table.rs +++ b/src/canvas/widgets/process_table.rs @@ -2,7 +2,7 @@ use tui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::Style, text::{Line, Span}, - widgets::{Block, Borders, Paragraph}, + widgets::Paragraph, Frame, }; use unicode_segmentation::UnicodeSegmentation; @@ -11,9 +11,9 @@ use crate::{ app::{App, AppSearchState}, canvas::{ components::data_table::{DrawInfo, SelectionState}, + drawing_utils::widget_block, Painter, }, - constants::*, }; const SORT_MENU_WIDTH: u16 = 7; @@ -23,11 +23,11 @@ impl Painter { /// - `widget_id` here represents the widget ID of the process widget /// itself! pub fn draw_process( - &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool, - widget_id: u64, + &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64, ) { if let Some(proc_widget_state) = app_state.states.proc_state.widget_states.get(&widget_id) { - let search_height = if draw_border { 5 } else { 3 }; + let is_basic = app_state.app_config_fields.use_basic_mode; + let search_height = if !is_basic { 5 } else { 3 }; let is_sort_open = proc_widget_state.is_sort_open; let mut proc_draw_loc = draw_loc; @@ -38,13 +38,7 @@ impl Painter { .split(draw_loc); proc_draw_loc = processes_chunk[0]; - self.draw_search_field( - f, - app_state, - processes_chunk[1], - draw_border, - widget_id + 1, - ); + self.draw_search_field(f, app_state, processes_chunk[1], widget_id + 1); } if is_sort_open { @@ -110,8 +104,7 @@ impl Painter { /// - `widget_id` represents the widget ID of the search box itself --- NOT /// the process widget state that is stored. fn draw_search_field( - &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool, - widget_id: u64, + &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64, ) { fn build_query_span( search_state: &AppSearchState, available_width: usize, is_on_widget: bool, @@ -157,16 +150,18 @@ impl Painter { } } + let is_basic = app_state.app_config_fields.use_basic_mode; + if let Some(proc_widget_state) = app_state .states .proc_state .widget_states .get_mut(&(widget_id - 1)) { - let is_on_widget = widget_id == app_state.current_widget.widget_id; + let is_selected = widget_id == app_state.current_widget.widget_id; let num_columns = usize::from(draw_loc.width); const SEARCH_TITLE: &str = "> "; - let offset = if draw_border { 4 } else { 2 }; // width of 3 removed for >_| + let offset = 4; let available_width = if num_columns > (offset + 3) { num_columns - offset } else { @@ -182,18 +177,18 @@ impl Painter { let query_with_cursor = build_query_span( &proc_widget_state.proc_search.search_state, available_width, - is_on_widget, - self.colours.selected_text_style, - self.colours.text_style, + is_selected, + self.styles.selected_text_style, + self.styles.text_style, ); let mut search_text = vec![Line::from({ let mut search_vec = vec![Span::styled( SEARCH_TITLE, - if is_on_widget { - self.colours.table_header_style + if is_selected { + self.styles.table_header_style } else { - self.colours.text_style + self.styles.text_style }, )]; search_vec.extend(query_with_cursor); @@ -203,21 +198,21 @@ impl Painter { // Text options shamelessly stolen from VS Code. let case_style = if !proc_widget_state.proc_search.is_ignoring_case { - self.colours.selected_text_style + self.styles.selected_text_style } else { - self.colours.text_style + self.styles.text_style }; let whole_word_style = if proc_widget_state.proc_search.is_searching_whole_word { - self.colours.selected_text_style + self.styles.selected_text_style } else { - self.colours.text_style + self.styles.text_style }; let regex_style = if proc_widget_state.proc_search.is_searching_with_regex { - self.colours.selected_text_style + self.styles.selected_text_style } else { - self.colours.text_style + self.styles.text_style }; // TODO: [MOUSE] Mouse support for these in search @@ -245,54 +240,42 @@ impl Painter { } else { "" }, - self.colours.invalid_query_style, + self.styles.invalid_query_style, ))); search_text.push(option_text); let current_border_style = if proc_widget_state.proc_search.search_state.is_invalid_search { - self.colours.invalid_query_style - } else if is_on_widget { - self.colours.highlighted_border_style + self.styles.invalid_query_style + } else if is_selected { + self.styles.highlighted_border_style } else { - self.colours.border_style + self.styles.border_style }; - let title = Span::styled( - if draw_border { - const TITLE_BASE: &str = " Esc to close "; - let repeat_num = - usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2); - format!("{} Esc to close ", "─".repeat(repeat_num)) - } else { - String::new() - }, - current_border_style, - ); + let process_search_block = { + let mut block = widget_block(is_basic, is_selected, self.styles.border_type) + .border_style(current_border_style); - let process_search_block = if draw_border { - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(current_border_style) - } else if is_on_widget { - Block::default() - .borders(SIDE_BORDERS) - .border_style(current_border_style) - } else { - Block::default().borders(Borders::NONE) + if !is_basic { + block = block.title_top( + Line::styled(" Esc to close ", current_border_style).right_aligned(), + ) + } + + block }; let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) - .horizontal_margin(u16::from(!(is_on_widget || draw_border))) + .horizontal_margin(u16::from(is_basic && !is_selected)) .direction(Direction::Horizontal) .split(draw_loc)[0]; f.render_widget( Paragraph::new(search_text) .block(process_search_block) - .style(self.colours.text_style) + .style(self.styles.text_style) .alignment(Alignment::Left), margined_draw_loc, ); diff --git a/src/options.rs b/src/options.rs index df6dc5b52..15c710268 100644 --- a/src/options.rs +++ b/src/options.rs @@ -16,7 +16,7 @@ use std::{ }; use anyhow::{Context, Result}; -use config::style::ColourPalette; +use config::style::Styles; pub use config::Config; pub(crate) use error::{OptionError, OptionResult}; use hashbrown::{HashMap, HashSet}; @@ -144,7 +144,7 @@ fn create_config_at_path(path: &Path) -> anyhow::Result { /// - If the user does NOT pass in a path explicitly, then just show a warning, /// but continue. This is in case they do not want to write a default config file at /// the XDG locations, for example. -pub fn get_or_create_config(config_path: Option<&Path>) -> anyhow::Result { +pub(crate) fn get_or_create_config(config_path: Option<&Path>) -> anyhow::Result { let adjusted_config_path = get_config_path(config_path); match &adjusted_config_path { @@ -196,9 +196,7 @@ pub fn get_or_create_config(config_path: Option<&Path>) -> anyhow::Result Result<(App, BottomLayout, ColourPalette)> { +pub(crate) fn init_app(args: BottomArgs, config: Config) -> Result<(App, BottomLayout, Styles)> { use BottomWidgetType::*; // Since everything takes a reference, but we want to take ownership here to @@ -206,7 +204,7 @@ pub(crate) fn init_app( let args = &args; let config = &config; - let styling = ColourPalette::new(args, config)?; + let styling = Styles::new(args, config)?; let (widget_layout, default_widget_id, default_widget_type_option) = get_widget_layout(args, config) diff --git a/src/options/config/style.rs b/src/options/config/style.rs index 74db09088..5374604d6 100644 --- a/src/options/config/style.rs +++ b/src/options/config/style.rs @@ -1,6 +1,7 @@ //! Config options around styling. mod battery; +mod borders; mod cpu; mod graphs; mod memory; @@ -19,7 +20,7 @@ use memory::MemoryStyle; use network::NetworkStyle; use serde::{Deserialize, Serialize}; use tables::TableStyle; -use tui::style::Style; +use tui::{style::Style, widgets::BorderType}; use utils::{opt, set_colour, set_colour_list, set_style}; use widgets::WidgetStyle; @@ -92,45 +93,47 @@ pub(crate) struct StyleConfig { pub(crate) widgets: Option, } -/// The actual internal representation of the configured colours, -/// as a "palette". +/// The actual internal representation of the configured styles. #[derive(Debug)] -pub struct ColourPalette { - pub ram_style: Style, +pub struct Styles { + pub(crate) ram_style: Style, #[cfg(not(target_os = "windows"))] - pub cache_style: Style, - pub swap_style: Style, - pub arc_style: Style, - pub gpu_colours: Vec