From 750d8f3cb747103cca7a2a4afe8ff41ff1318b6f Mon Sep 17 00:00:00 2001 From: Clement Tsang <34804052+ClementTsang@users.noreply.github.com> Date: Sat, 26 Sep 2020 20:04:34 -0400 Subject: [PATCH] refactor: tui-rs 0.11.0 refactor (#253) Refactors tui-rs usage to the new 0.11.0 release. This release also fixes the highlighting bug from #249, and now, expanding a widget no longer overrides the widget title colour. This commit also introduces #255, but that seems to be easy to bandaid so hopefully it will get fixed soon? --- CHANGELOG.md | 6 +- Cargo.lock | 6 +- Cargo.toml | 3 +- src/app.rs | 1 + src/canvas.rs | 15 +- src/canvas/dialogs/dd_dialog.rs | 106 +++++------- src/canvas/dialogs/help_dialog.rs | 36 ++--- src/canvas/screens/config_screen.rs | 6 +- src/canvas/widgets/basic_table_arrows.rs | 22 ++- src/canvas/widgets/battery_display.rs | 154 ++++++------------ src/canvas/widgets/cpu_basic.rs | 20 +-- src/canvas/widgets/cpu_graph.rs | 64 ++++---- src/canvas/widgets/disk_table.rs | 67 +++----- src/canvas/widgets/mem_basic.rs | 12 +- src/canvas/widgets/mem_graph.rs | 70 ++++---- src/canvas/widgets/network_basic.rs | 26 ++- src/canvas/widgets/network_graph.rs | 65 ++++---- src/canvas/widgets/process_table.rs | 120 +++++++------- src/canvas/widgets/temp_table.rs | 39 ++--- src/constants.rs | 198 +++++++++++------------ 20 files changed, 488 insertions(+), 548 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ebdd407..0d1bb1df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#223](https://github.com/ClementTsang/bottom/pull/223): Add tree mode for processes. -- [](): Add in-app configuration. - ### Changes - [#213](https://github.com/ClementTsang/bottom/pull/213), [#214](https://github.com/ClementTsang/bottom/pull/214): Updated help descriptions, added auto-complete generation. @@ -33,6 +31,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#224](https://github.com/ClementTsang/bottom/pull/224): Implements sorting by count. It previously did absolutely nothing. +- [#253](https://github.com/ClementTsang/bottom/pull/253): Fix highlighted entries being stuck in another colour when the widget is not selected. + +- [#253](https://github.com/ClementTsang/bottom/pull/253): Expanding a widget no longer overrides the widget title colour. + ## [0.4.7] - 2020-08-26 ### Bug Fixes diff --git a/Cargo.lock b/Cargo.lock index 7893cdbb..ebef0f0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1363,15 +1363,13 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" [[package]] name = "tui" -version = "0.9.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9533d39bef0ae8f510e8a99d78702e68d1bbf0b98a78ec9740509d287010ae1e" +checksum = "36626dee5ede9fd34015e9fb4fd7eedf3f3d05bdf1436aaef15b7d0a24233778" dependencies = [ "bitflags", "cassowary", "crossterm", - "either", - "itertools", "unicode-segmentation", "unicode-width", ] diff --git a/Cargo.toml b/Cargo.toml index d019a7ba..90a877a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,8 @@ serde = {version = "1.0", features = ["derive"] } sysinfo = "0.15.1" thiserror = "1.0.20" toml = "0.5.6" -tui = {version = "0.9.5", features = ["crossterm"], default-features = false } +tui = {version = "0.11.0", features = ["crossterm"], default-features = false } +# tui = {version = "0.11.0", features = ["crossterm"], default-features = false, path="../tui-rs" } typed-builder = "0.7.0" unicode-segmentation = "1.6.0" unicode-width = "0.1" diff --git a/src/app.rs b/src/app.rs index 33c11ebd..3eda03ad 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1276,6 +1276,7 @@ impl App { } /// Call this whenever the config value is updated! + #[allow(dead_code)] //FIXME: Remove this fn update_config_file(&mut self) -> anyhow::Result<()> { if self.app_config_fields.no_write { // Don't write! diff --git a/src/canvas.rs b/src/canvas.rs index f4707507..bc142194 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, - widgets::Text, + text::{Span, Spans}, Frame, Terminal, }; @@ -63,7 +63,7 @@ pub struct Painter { pub colours: CanvasColours, height: u16, width: u16, - styled_help_text: Vec<Text<'static>>, + styled_help_text: Vec<Spans<'static>>, is_mac_os: bool, row_constraints: Vec<Constraint>, col_constraints: Vec<Vec<Constraint>>, @@ -289,28 +289,27 @@ impl Painter { styled_help_spans.extend( section .iter() - .map(|&text| Text::styled(text, self.colours.text_style)) + .map(|&text| Span::styled(text, self.colours.text_style)) .collect::<Vec<_>>(), ); } else { // Not required check but it runs only a few times... so whatever ig, prevents me from // being dumb and leaving a help text section only one line long. if section.len() > 1 { - styled_help_spans.push(Text::raw("\n\n")); + styled_help_spans.push(Span::raw("")); styled_help_spans - .push(Text::styled(section[0], self.colours.table_header_style)); + .push(Span::styled(section[0], self.colours.table_header_style)); styled_help_spans.extend( section[1..] .iter() - .map(|&text| Text::styled(text, self.colours.text_style)) + .map(|&text| Span::styled(text, self.colours.text_style)) .collect::<Vec<_>>(), ); } } }); - // self.styled_help_text = styled_help_spans.into_iter().map(Spans::from).collect(); - self.styled_help_text = styled_help_spans; + self.styled_help_text = styled_help_spans.into_iter().map(Spans::from).collect(); } // FIXME: [CONFIG] write this, should call painter init and any changed colour functions... diff --git a/src/canvas/dialogs/dd_dialog.rs b/src/canvas/dialogs/dd_dialog.rs index 11d0df5f..50dbc7ef 100644 --- a/src/canvas/dialogs/dd_dialog.rs +++ b/src/canvas/dialogs/dd_dialog.rs @@ -2,7 +2,8 @@ use tui::{ backend::Backend, layout::{Alignment, Constraint, Direction, Layout, Rect}, terminal::Frame, - widgets::{Block, Borders, Paragraph, Text}, + text::{Span, Spans, Text}, + widgets::{Block, Borders, Paragraph, Wrap}, }; use crate::{app::App, canvas::Painter}; @@ -11,46 +12,46 @@ const DD_BASE: &str = " Confirm Kill Process ── Esc to close "; const DD_ERROR_BASE: &str = " Error ── Esc to close "; pub trait KillDialog { - fn get_dd_spans(&self, app_state: &App) -> Option<Vec<Text<'_>>>; + fn get_dd_spans(&self, app_state: &App) -> Option<Text<'_>>; fn draw_dd_dialog<B: Backend>( - &self, f: &mut Frame<'_, B>, dd_text: Option<Vec<Text<'_>>>, app_state: &mut App, - draw_loc: Rect, + &self, f: &mut Frame<'_, B>, dd_text: Option<Text<'_>>, app_state: &mut App, draw_loc: Rect, ) -> bool; } impl KillDialog for Painter { - fn get_dd_spans(&self, app_state: &App) -> Option<Vec<Text<'_>>> { + fn get_dd_spans(&self, app_state: &App) -> Option<Text<'_>> { if let Some(dd_err) = &app_state.dd_err { - return Some(vec![ - Text::raw("\n"), - Text::raw(format!("Failed to kill process.\n{}\n", dd_err)), - Text::raw("Please press ENTER or ESC to close this dialog."), - ]); + return Some(Text::from(vec![ + Spans::default(), + Spans::from("Failed to kill process."), + Spans::from(dd_err.clone()), + Spans::from("Please press ENTER or ESC to close this dialog."), + ])); } else if let Some(to_kill_processes) = app_state.get_to_delete_processes() { if let Some(first_pid) = to_kill_processes.1.first() { - return Some(vec![ - Text::raw("\n"), + return Some(Text::from(vec![ + Spans::from(""), if app_state.is_grouped(app_state.current_widget.widget_id) { if to_kill_processes.1.len() != 1 { - Text::raw(format!( + Spans::from(format!( "Kill {} processes with the name \"{}\"? Press ENTER to confirm.", to_kill_processes.1.len(), to_kill_processes.0 )) } else { - Text::raw(format!( + Spans::from(format!( "Kill 1 process with the name \"{}\"? Press ENTER to confirm.", to_kill_processes.0 )) } } else { - Text::raw(format!( + Spans::from(format!( "Kill process \"{}\" with PID {}? Press ENTER to confirm.", to_kill_processes.0, first_pid )) }, - ]); + ])); } } @@ -58,63 +59,44 @@ impl KillDialog for Painter { } fn draw_dd_dialog<B: Backend>( - &self, f: &mut Frame<'_, B>, dd_text: Option<Vec<Text<'_>>>, app_state: &mut App, - draw_loc: Rect, + &self, f: &mut Frame<'_, B>, dd_text: Option<Text<'_>>, app_state: &mut App, draw_loc: Rect, ) -> bool { if let Some(dd_text) = dd_text { - // let dd_title = if app_state.dd_err.is_some() { - // Text::styled( - // format!( - // " Error ─{}─ Esc to close ", - // "─".repeat( - // usize::from(draw_loc.width) - // .saturating_sub(DD_ERROR_BASE.chars().count() + 2) - // ) - // ), - // self.colours.border_style, - // ) - // } else { - // Text::styled( - // format!( - // " Confirm Kill Process ─{}─ Esc to close ", - // "─".repeat( - // usize::from(draw_loc.width).saturating_sub(DD_BASE.chars().count() + 2) - // ) - // ), - // self.colours.border_style, - // ) - // }; - let dd_title = if app_state.dd_err.is_some() { - format!( - " Error ─{}─ Esc to close ", - "─".repeat( - usize::from(draw_loc.width) - .saturating_sub(DD_ERROR_BASE.chars().count() + 2) - ) + Span::styled( + format!( + " Error ─{}─ Esc to close ", + "─".repeat( + usize::from(draw_loc.width) + .saturating_sub(DD_ERROR_BASE.chars().count() + 2) + ) + ), + self.colours.border_style, ) } else { - format!( - " Confirm Kill Process ─{}─ Esc to close ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub(DD_BASE.chars().count() + 2) - ) + Span::styled( + format!( + " Confirm Kill Process ─{}─ Esc to close ", + "─".repeat( + usize::from(draw_loc.width).saturating_sub(DD_BASE.chars().count() + 2) + ) + ), + self.colours.border_style, ) }; f.render_widget( - Paragraph::new(dd_text.iter()) + Paragraph::new(dd_text) .block( Block::default() - .title(&dd_title) - .title_style(self.colours.border_style) + .title(dd_title) .style(self.colours.border_style) .borders(Borders::ALL) .border_style(self.colours.border_style), ) .style(self.colours.text_style) .alignment(Alignment::Center) - .wrap(true), + .wrap(Wrap { trim: true }), draw_loc, ); @@ -135,13 +117,13 @@ impl KillDialog for Painter { if let Some(button_draw_loc) = split_draw_loc.get(1) { let (yes_button, no_button) = if app_state.delete_dialog_state.is_on_yes { ( - Text::styled("Yes", self.colours.currently_selected_text_style), - Text::raw("No"), + Span::styled("Yes", self.colours.currently_selected_text_style), + Span::raw("No"), ) } else { ( - Text::raw("Yes"), - Text::styled("No", self.colours.currently_selected_text_style), + Span::raw("Yes"), + Span::styled("No", self.colours.currently_selected_text_style), ) }; @@ -158,13 +140,13 @@ impl KillDialog for Painter { .split(*button_draw_loc); f.render_widget( - Paragraph::new([yes_button].iter()) + Paragraph::new(yes_button) .block(Block::default()) .alignment(Alignment::Right), button_layout[0], ); f.render_widget( - Paragraph::new([no_button].iter()) + Paragraph::new(no_button) .block(Block::default()) .alignment(Alignment::Left), button_layout[2], diff --git a/src/canvas/dialogs/help_dialog.rs b/src/canvas/dialogs/help_dialog.rs index fe0f1c52..752dc858 100644 --- a/src/canvas/dialogs/help_dialog.rs +++ b/src/canvas/dialogs/help_dialog.rs @@ -5,7 +5,8 @@ use tui::{ backend::Backend, layout::{Alignment, Rect}, terminal::Frame, - widgets::{Block, Borders, Paragraph}, + text::Span, + widgets::{Block, Borders, Paragraph, Wrap}, }; const HELP_BASE: &str = " Help ── Esc to close "; @@ -21,19 +22,14 @@ impl HelpDialog for Painter { fn draw_help_dialog<B: Backend>( &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, ) { - // let help_title = Text::styled( - // format!( - // " Help ─{}─ Esc to close ", - // "─".repeat( - // usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2) - // ) - // ), - // self.colours.border_style, - // ); - - let help_title = format!( - " Help ─{}─ Esc to close ", - "─".repeat(usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2)) + let help_title = Span::styled( + format!( + " Help ─{}─ Esc to close ", + "─".repeat( + usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2) + ) + ), + self.colours.border_style, ); if app_state.should_get_widget_bounds() { @@ -99,24 +95,24 @@ impl HelpDialog for Painter { } f.render_widget( - Paragraph::new(self.styled_help_text.iter()) + Paragraph::new(self.styled_help_text.clone()) .block( Block::default() - .title(&help_title) - .title_style(self.colours.border_style) + .title(help_title) .style(self.colours.border_style) .borders(Borders::ALL) .border_style(self.colours.border_style), ) .style(self.colours.text_style) .alignment(Alignment::Left) - .wrap(true) - .scroll( + .wrap(Wrap { trim: true }) + .scroll(( app_state .help_dialog_state .scroll_state .current_scroll_index, - ), + 0, + )), draw_loc, ); } diff --git a/src/canvas/screens/config_screen.rs b/src/canvas/screens/config_screen.rs index 6aa22493..161e6ca6 100644 --- a/src/canvas/screens/config_screen.rs +++ b/src/canvas/screens/config_screen.rs @@ -1,3 +1,5 @@ +#![allow(unused_variables)] //FIXME: Remove this +#![allow(unused_imports)] //FIXME: Remove this use crate::{app::App, canvas::Painter, constants}; use tui::{ backend::Backend, @@ -6,6 +8,7 @@ use tui::{ layout::Layout, layout::{Alignment, Rect}, terminal::Frame, + text::Span, widgets::{Block, Borders, Paragraph}, }; @@ -20,8 +23,7 @@ impl ConfigScreen for Painter { &self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, ) { let config_block = Block::default() - .title(&" Config ") - .title_style(self.colours.border_style) + .title(" Config ") // FIXME: [Config] missing title styling .style(self.colours.border_style) .borders(Borders::ALL) .border_style(self.colours.border_style); diff --git a/src/canvas/widgets/basic_table_arrows.rs b/src/canvas/widgets/basic_table_arrows.rs index fd64e8dc..bce169e1 100644 --- a/src/canvas/widgets/basic_table_arrows.rs +++ b/src/canvas/widgets/basic_table_arrows.rs @@ -7,7 +7,9 @@ use tui::{ backend::Backend, layout::{Alignment, Constraint, Direction, Layout, Rect}, terminal::Frame, - widgets::{Block, Paragraph, Text}, + text::Span, + text::Spans, + widgets::{Block, Paragraph}, }; pub trait BasicTableArrows { @@ -97,13 +99,19 @@ impl BasicTableArrows for Painter { usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len()); let left_arrow_text = vec![ - Text::raw("\n"), - Text::styled(format!("◄ {}", left_name), self.colours.text_style), + Spans::default(), + Spans::from(Span::styled( + format!("◄ {}", left_name), + self.colours.text_style, + )), ]; let right_arrow_text = vec![ - Text::raw("\n"), - Text::styled(format!("{} ►", right_name), self.colours.text_style), + Spans::default(), + Spans::from(Span::styled( + format!("{} ►", right_name), + self.colours.text_style, + )), ]; let margined_draw_loc = Layout::default() @@ -120,11 +128,11 @@ impl BasicTableArrows for Painter { .split(draw_loc); f.render_widget( - Paragraph::new(left_arrow_text.iter()).block(Block::default()), + Paragraph::new(left_arrow_text).block(Block::default()), margined_draw_loc[0], ); f.render_widget( - Paragraph::new(right_arrow_text.iter()) + Paragraph::new(right_arrow_text) .block(Block::default()) .alignment(Alignment::Right), margined_draw_loc[2], diff --git a/src/canvas/widgets/battery_display.rs b/src/canvas/widgets/battery_display.rs index e50ed515..7364044b 100644 --- a/src/canvas/widgets/battery_display.rs +++ b/src/canvas/widgets/battery_display.rs @@ -8,8 +8,10 @@ use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, - widgets::{Block, Borders, Paragraph, Row, Table, Tabs, Text}, + text::{Span, Spans}, + widgets::{Block, Borders, Paragraph, Row, Table, Tabs}, }; +use unicode_segmentation::UnicodeSegmentation; pub trait BatteryDisplayWidget { fn draw_battery_display<B: Backend>( @@ -28,53 +30,38 @@ impl BatteryDisplayWidget for Painter { app_state.battery_state.widget_states.get_mut(&widget_id) { let is_on_widget = widget_id == app_state.current_widget.widget_id; - let border_and_title_style = if is_on_widget { + let border_style = if is_on_widget { self.colours.highlighted_border_style } else { self.colours.border_style }; - // let title = if app_state.is_expanded { - // const TITLE_BASE: &str = " Battery ── Esc to go back "; - // Span::styled( - // format!( - // " Battery ─{}─ Esc to go back ", - // "─".repeat( - // usize::from(draw_loc.width) - // .saturating_sub(TITLE_BASE.chars().count() + 2) - // ) - // ), - // border_and_title_style, - // ) - // } else { - // Span::styled(" Battery ".to_string(), self.colours.widget_title_style) - // }; - let title = if app_state.is_expanded { const TITLE_BASE: &str = " Battery ── Esc to go back "; - - format!( - " Battery ─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2) - ) - ) + Spans::from(vec![ + Span::styled(" Battery ".to_string(), 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 { - " Battery ".to_string() - }; - - let title_style = if app_state.is_expanded { - border_and_title_style - } else { - self.colours.widget_title_style + Spans::from(Span::styled( + " Battery ".to_string(), + self.colours.widget_title_style, + )) }; let battery_block = if draw_border { Block::default() - .title(&title) - .title_style(title_style) + .title(title) .borders(Borders::ALL) - .border_style(border_and_title_style) + .border_style(border_style) } else if is_on_widget { Block::default() .borders(*SIDE_BORDERS) @@ -83,38 +70,12 @@ impl BatteryDisplayWidget for Painter { Block::default().borders(Borders::NONE) }; - // f.render_widget( - // // Tabs::new( - // // (app_state - // // .canvas_data - // // .battery_data - // // .iter() - // // .map(|battery| Spans::from(battery.battery_name.clone()))) - // // .collect::<Vec<_>>(), - // // ) - // Tabs::default() - // .titles( - // app_state - // .canvas_data - // .battery_data - // .iter() - // .map(|battery| &battery.battery_name) - // .collect::<Vec<_>>() - // .as_ref(), - // ) - // .block(battery_block) - // .divider(tui::symbols::line::VERTICAL) - // .style(self.colours.text_style) - // .highlight_style(self.colours.currently_selected_text_style) - // .select(battery_widget_state.currently_selected_battery_index), - // draw_loc, - // ); - - let margined_draw_loc = Layout::default() - .constraints([Constraint::Percentage(100)].as_ref()) - .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) - .direction(Direction::Horizontal) - .split(draw_loc)[0]; + let battery_names = app_state + .canvas_data + .battery_data + .iter() + .map(|battery| &battery.battery_name) + .collect::<Vec<_>>(); let tab_draw_loc = Layout::default() .constraints( @@ -128,6 +89,27 @@ impl BatteryDisplayWidget for Painter { .direction(Direction::Vertical) .split(draw_loc)[1]; + f.render_widget( + Tabs::new( + battery_names + .iter() + .map(|name| Spans::from((*name).clone())) + .collect::<Vec<_>>(), + ) + .block(Block::default()) + .divider(tui::symbols::line::VERTICAL) + .style(self.colours.text_style) + .highlight_style(self.colours.currently_selected_text_style) + .select(battery_widget_state.currently_selected_battery_index), + tab_draw_loc, + ); + + let margined_draw_loc = Layout::default() + .constraints([Constraint::Percentage(100)].as_ref()) + .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) + .direction(Direction::Horizontal) + .split(draw_loc)[0]; + if let Some(battery_details) = app_state .canvas_data .battery_data @@ -189,45 +171,15 @@ impl BatteryDisplayWidget for Painter { ); } else { f.render_widget( - Paragraph::new( - [Text::styled( - "No data found for this battery", - self.colours.text_style, - )] - .iter(), - ) + Paragraph::new(Span::styled( + "No data found for this battery", + self.colours.text_style, + )) .block(battery_block), margined_draw_loc, ); } - let battery_names = app_state - .canvas_data - .battery_data - .iter() - .map(|battery| &battery.battery_name) - .collect::<Vec<_>>(); - - // Has to be placed AFTER for tui 0.9, place BEFORE for 0.10. - f.render_widget( - // Tabs::new( - // (app_state - // .canvas_data - // .battery_data - // .iter() - // .map(|battery| Spans::from(battery.battery_name.clone()))) - // .collect::<Vec<_>>(), - // ) - Tabs::default() - .titles(battery_names.as_ref()) - .block(Block::default()) - .divider(tui::symbols::line::VERTICAL) - .style(self.colours.text_style) - .highlight_style(self.colours.currently_selected_text_style) //FIXME: [HIGHLIGHT] THIS IS BROKEN ON TUI's SIDE, override this with your own style... - .select(battery_widget_state.currently_selected_battery_index), - tab_draw_loc, - ); - if should_get_widget_bounds { // Tab wizardry if !battery_names.is_empty() { diff --git a/src/canvas/widgets/cpu_basic.rs b/src/canvas/widgets/cpu_basic.rs index 7f9381f9..2f7574b6 100644 --- a/src/canvas/widgets/cpu_basic.rs +++ b/src/canvas/widgets/cpu_basic.rs @@ -11,7 +11,8 @@ use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, - widgets::{Block, Paragraph, Text}, + text::{Span, Spans}, + widgets::{Block, Paragraph}, }; pub trait CpuBasicWidget { @@ -76,7 +77,7 @@ impl CpuBasicWidget for Painter { let num_bars = calculate_basic_use_bars(use_percentage, bar_length); format!( - "{:3}[{}{}{:3.0}%]\n", + "{:3}[{}{}{:3.0}%]", if app_state.app_config_fields.show_average_cpu { if cpu_index == 0 { "AVG".to_string() @@ -108,16 +109,11 @@ impl CpuBasicWidget for Painter { let end_index = min(start_index + how_many_cpus, num_cpus); let cpu_column = (start_index..end_index) .map(|cpu_index| { - // Spans::from(Span { - // content: (&cpu_bars[cpu_index]).into(), - // style: self.colours.cpu_colour_styles - // [cpu_index % self.colours.cpu_colour_styles.len()], - // }) - Text::styled( - &cpu_bars[cpu_index], - self.colours.cpu_colour_styles + Spans::from(Span { + content: (&cpu_bars[cpu_index]).into(), + style: self.colours.cpu_colour_styles [cpu_index % self.colours.cpu_colour_styles.len()], - ) + }) }) .collect::<Vec<_>>(); @@ -130,7 +126,7 @@ impl CpuBasicWidget for Painter { .split(*chunk)[0]; f.render_widget( - Paragraph::new(cpu_column.iter()).block(Block::default()), + Paragraph::new(cpu_column).block(Block::default()), margined_loc, ); } diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs index 97ba064d..07fdfaf0 100644 --- a/src/canvas/widgets/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -1,5 +1,6 @@ use lazy_static::lazy_static; use std::borrow::Cow; +use unicode_segmentation::UnicodeSegmentation; use crate::{ app::{layout_manager::WidgetDirection, App}, @@ -16,6 +17,8 @@ use tui::{ layout::{Constraint, Direction, Layout, Rect}, symbols::Marker, terminal::Frame, + text::Span, + text::Spans, widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table}, }; @@ -135,17 +138,17 @@ impl CpuGraphWidget for Painter { if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) { let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data; - // let display_time_labels = vec![ - // Text::styled( - // format!("{}s", cpu_widget_state.current_display_time / 1000), - // self.colours.graph_style, - // ), - // Text::styled("0s".to_string(), self.colours.graph_style), - // ]; + let display_time_labels = vec![ + Span::styled( + format!("{}s", cpu_widget_state.current_display_time / 1000), + self.colours.graph_style, + ), + Span::styled("0s".to_string(), self.colours.graph_style), + ]; - let display_time_labels = [ - format!("{}s", cpu_widget_state.current_display_time / 1000), - "0s".to_string(), + let y_axis_labels = vec![ + Span::styled("0%", self.colours.graph_style), + Span::styled("100%", self.colours.graph_style), ]; let x_axis = if app_state.app_config_fields.hide_time @@ -160,8 +163,7 @@ impl CpuGraphWidget for Painter { Axis::default() .bounds([-(cpu_widget_state.current_display_time as f64), 0.0]) .style(self.colours.graph_style) - .labels(&display_time_labels) - .labels_style(self.colours.graph_style) + .labels(display_time_labels) } else { cpu_widget_state.autohide_timer = None; Axis::default().bounds([-(cpu_widget_state.current_display_time as f64), 0.0]) @@ -172,15 +174,13 @@ impl CpuGraphWidget for Painter { Axis::default() .bounds([-(cpu_widget_state.current_display_time as f64), 0.0]) .style(self.colours.graph_style) - .labels(&display_time_labels) - .labels_style(self.colours.graph_style) + .labels(display_time_labels) }; let y_axis = Axis::default() .style(self.colours.graph_style) .bounds([0.0, 100.5]) - .labels_style(self.colours.graph_style) - .labels(&["0%", "100%"]); + .labels(y_axis_labels); let use_dot = app_state.app_config_fields.use_dot; let show_avg_cpu = app_state.app_config_fields.show_average_cpu; @@ -228,32 +228,36 @@ impl CpuGraphWidget for Painter { vec![] }; - let border_style = if app_state.current_widget.widget_id == widget_id { + let is_on_widget = widget_id == app_state.current_widget.widget_id; + let border_style = if is_on_widget { self.colours.highlighted_border_style } else { self.colours.border_style }; - // let title = if app_state.is_expanded { - // Span::styled(" CPU ".to_string(), border_style) - // } else { - // Span::styled(" CPU ".to_string(), self.colours.widget_title_style) - // }; - let title = " CPU "; - let title_style = if app_state.is_expanded { - border_style + let title = if app_state.is_expanded { + const TITLE_BASE: &str = " CPU ── Esc to go back "; + Spans::from(vec![ + Span::styled(" CPU ", 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 { - self.colours.widget_title_style + Spans::from(Span::styled(" CPU ", self.colours.widget_title_style)) }; f.render_widget( - // Chart::new(dataset_vector) - Chart::default() - .datasets(&dataset_vector) + Chart::new(dataset_vector) .block( Block::default() .title(title) - .title_style(title_style) .borders(Borders::ALL) .border_style(border_style), ) diff --git a/src/canvas/widgets/disk_table.rs b/src/canvas/widgets/disk_table.rs index 440687df..091b58c9 100644 --- a/src/canvas/widgets/disk_table.rs +++ b/src/canvas/widgets/disk_table.rs @@ -3,6 +3,8 @@ use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, + text::Span, + text::Spans, widgets::{Block, Borders, Row, Table}, }; @@ -139,19 +141,13 @@ impl DiskTableWidget for Painter { let first_n = graphemes [..(*calculated_col_width as usize - 1)] .concat(); - Cow::Owned(format!("{}…", first_n)) - } else { - Cow::Borrowed(entry) + return Cow::Owned(format!("{}…", first_n)); } - } else { - Cow::Borrowed(entry) } - } else { - Cow::Borrowed(entry) } - } else { - Cow::Borrowed(entry) } + + Cow::Borrowed(entry) }, ); @@ -159,7 +155,7 @@ impl DiskTableWidget for Painter { }); // TODO: This seems to be bugged? The selected text style gets "stuck"? I think this gets fixed with tui 0.10? - let (border_and_title_style, highlight_style) = if is_on_widget { + let (border_style, highlight_style) = if is_on_widget { ( self.colours.highlighted_border_style, self.colours.currently_selected_text_style, @@ -168,50 +164,29 @@ impl DiskTableWidget for Painter { (self.colours.border_style, self.colours.text_style) }; - // let title = if app_state.is_expanded { - // const TITLE_BASE: &str = " Disk ── Esc to go back "; - // Span::styled( - // format!( - // " Disk ─{}─ Esc to go back ", - // "─".repeat( - // usize::from(draw_loc.width) - // .saturating_sub(TITLE_BASE.chars().count() + 2) - // ) - // ), - // border_and_title_style, - // ) - // } else if app_state.app_config_fields.use_basic_mode { - // Span::from(String::new()) - // } else { - // Span::styled(" Disk ".to_string(), self.colours.widget_title_style) - // }; - let title = if app_state.is_expanded { const TITLE_BASE: &str = " Disk ── Esc to go back "; - format!( - " Disk ─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2) - ) - ) - } else if app_state.app_config_fields.use_basic_mode { - String::new() + Spans::from(vec![ + Span::styled(" Disk ", 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 { - " Disk ".to_string() - }; - - let title_style = if app_state.is_expanded { - border_and_title_style - } else { - self.colours.widget_title_style + Spans::from(Span::styled(" Disk ", self.colours.widget_title_style)) }; let disk_block = if draw_border { Block::default() - .title(&title) - .title_style(title_style) + .title(title) .borders(Borders::ALL) - .border_style(border_and_title_style) + .border_style(border_style) } else if is_on_widget { Block::default() .borders(*SIDE_BORDERS) diff --git a/src/canvas/widgets/mem_basic.rs b/src/canvas/widgets/mem_basic.rs index 89f96dd5..38ce9dfc 100644 --- a/src/canvas/widgets/mem_basic.rs +++ b/src/canvas/widgets/mem_basic.rs @@ -8,7 +8,9 @@ use tui::{ backend::Backend, layout::{Constraint, Layout, Rect}, terminal::Frame, - widgets::{Block, Paragraph, Text}, + text::Span, + text::Spans, + widgets::{Block, Paragraph}, }; pub trait MemBasicWidget { @@ -106,13 +108,13 @@ impl MemBasicWidget for Painter { ) }; - let mem_text = [ - Text::styled(mem_label, self.colours.ram_style), - Text::styled(swap_label, self.colours.swap_style), + let mem_text = vec![ + Spans::from(Span::styled(mem_label, self.colours.ram_style)), + Spans::from(Span::styled(swap_label, self.colours.swap_style)), ]; f.render_widget( - Paragraph::new(mem_text.iter()).block(Block::default()), + Paragraph::new(mem_text).block(Block::default()), margined_loc[0], ); diff --git a/src/canvas/widgets/mem_graph.rs b/src/canvas/widgets/mem_graph.rs index bdc0b0b7..c3f62197 100644 --- a/src/canvas/widgets/mem_graph.rs +++ b/src/canvas/widgets/mem_graph.rs @@ -5,8 +5,11 @@ use tui::{ layout::{Constraint, Rect}, symbols::Marker, terminal::Frame, + text::Span, + text::Spans, widgets::{Axis, Block, Borders, Chart, Dataset}, }; +use unicode_segmentation::UnicodeSegmentation; pub trait MemGraphWidget { fn draw_memory_graph<B: Backend>( @@ -22,10 +25,18 @@ impl MemGraphWidget for Painter { let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data; let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data; - let display_time_labels = [ - format!("{}s", mem_widget_state.current_display_time / 1000), - "0s".to_string(), + let display_time_labels = vec![ + Span::styled( + format!("{}s", mem_widget_state.current_display_time / 1000), + self.colours.graph_style, + ), + Span::styled("0s".to_string(), self.colours.graph_style), ]; + let y_axis_label = vec![ + Span::styled("0%", self.colours.graph_style), + Span::styled("100%", self.colours.graph_style), + ]; + let x_axis = if app_state.app_config_fields.hide_time || (app_state.app_config_fields.autohide_time && mem_widget_state.autohide_timer.is_none()) @@ -38,8 +49,7 @@ impl MemGraphWidget for Painter { Axis::default() .bounds([-(mem_widget_state.current_display_time as f64), 0.0]) .style(self.colours.graph_style) - .labels(&display_time_labels) - .labels_style(self.colours.graph_style) + .labels(display_time_labels) } else { mem_widget_state.autohide_timer = None; Axis::default().bounds([-(mem_widget_state.current_display_time as f64), 0.0]) @@ -50,15 +60,13 @@ impl MemGraphWidget for Painter { Axis::default() .bounds([-(mem_widget_state.current_display_time as f64), 0.0]) .style(self.colours.graph_style) - .labels(&display_time_labels) - .labels_style(self.colours.graph_style) + .labels(display_time_labels) }; let y_axis = Axis::default() .style(self.colours.graph_style) .bounds([0.0, 100.5]) - .labels(&["0%", "100%"]) - .labels_style(self.colours.graph_style); + .labels(y_axis_label); let mut mem_canvas_vec: Vec<Dataset<'_>> = vec![]; let mem_label = format!( @@ -96,31 +104,39 @@ impl MemGraphWidget for Painter { .graph_type(tui::widgets::GraphType::Line), ); - let title = if app_state.is_expanded { - const TITLE_BASE: &str = " Memory ── Esc to go back "; - format!( - " Memory ─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2) - ) - ) - } else { - " Memory ".to_string() - }; - let title_style = if app_state.is_expanded { + let is_on_widget = widget_id == app_state.current_widget.widget_id; + let border_style = if is_on_widget { self.colours.highlighted_border_style } else { - self.colours.widget_title_style + self.colours.border_style + }; + + let title = if app_state.is_expanded { + const TITLE_BASE: &str = " Memory ── Esc to go back "; + Spans::from(vec![ + Span::styled(" Memory ", 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 { + Spans::from(Span::styled( + " Memory ".to_string(), + self.colours.widget_title_style, + )) }; f.render_widget( - // Chart::new(mem_canvas_vec) - Chart::default() - .datasets(&mem_canvas_vec) + Chart::new(mem_canvas_vec) .block( Block::default() - .title(&title) - .title_style(title_style) + .title(title) .borders(Borders::ALL) .border_style(if app_state.current_widget.widget_id == widget_id { self.colours.highlighted_border_style diff --git a/src/canvas/widgets/network_basic.rs b/src/canvas/widgets/network_basic.rs index 126c8ae0..e96d2cf6 100644 --- a/src/canvas/widgets/network_basic.rs +++ b/src/canvas/widgets/network_basic.rs @@ -4,7 +4,8 @@ use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, - widgets::{Block, Paragraph, Text}, + text::{Span, Spans, }, + widgets::{Block, Paragraph}, }; pub trait NetworkBasicWidget { @@ -43,28 +44,25 @@ impl NetworkBasicWidget for Painter { ); } - let rx_label = format!("RX: {}\n", &app_state.canvas_data.rx_display); + let rx_label = format!("RX: {}", &app_state.canvas_data.rx_display); let tx_label = format!("TX: {}", &app_state.canvas_data.tx_display); - let total_rx_label = format!("Total RX: {}\n", &app_state.canvas_data.total_rx_display); + let total_rx_label = format!("Total RX: {}", &app_state.canvas_data.total_rx_display); let total_tx_label = format!("Total TX: {}", &app_state.canvas_data.total_tx_display); - let net_text = [ - Text::styled(rx_label, self.colours.rx_style), - Text::styled(tx_label, self.colours.tx_style), + let net_text = vec![ + Spans::from(Span::styled(rx_label, self.colours.rx_style)), + Spans::from(Span::styled(tx_label, self.colours.tx_style)), ]; - let total_net_text = [ - Text::styled(total_rx_label, self.colours.total_rx_style), - Text::styled(total_tx_label, self.colours.total_tx_style), + let total_net_text = vec![ + Spans::from(Span::styled(total_rx_label, self.colours.total_rx_style)), + Spans::from(Span::styled(total_tx_label, self.colours.total_tx_style)), ]; - f.render_widget( - Paragraph::new(net_text.iter()).block(Block::default()), - net_loc[0], - ); + f.render_widget(Paragraph::new(net_text).block(Block::default()), net_loc[0]); f.render_widget( - Paragraph::new(total_net_text.iter()).block(Block::default()), + Paragraph::new(total_net_text).block(Block::default()), total_loc[0], ); diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs index 8050046b..95dfe304 100644 --- a/src/canvas/widgets/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -1,5 +1,6 @@ use lazy_static::lazy_static; use std::cmp::max; +use unicode_segmentation::UnicodeSegmentation; use crate::{ app::App, @@ -13,6 +14,8 @@ use tui::{ layout::{Constraint, Direction, Layout, Rect}, symbols::Marker, terminal::Frame, + text::Span, + text::Spans, widgets::{Axis, Block, Borders, Chart, Dataset, Row, Table}, }; @@ -179,9 +182,12 @@ impl NetworkGraphWidget for Painter { -(network_widget_state.current_display_time as f64), 0.0, ); - let display_time_labels = [ - format!("{}s", network_widget_state.current_display_time / 1000), - "0s".to_string(), + let display_time_labels = vec![ + Span::styled( + format!("{}s", network_widget_state.current_display_time / 1000), + self.colours.graph_style, + ), + Span::styled("0s".to_string(), self.colours.graph_style), ]; let x_axis = if app_state.app_config_fields.hide_time || (app_state.app_config_fields.autohide_time @@ -195,8 +201,7 @@ impl NetworkGraphWidget for Painter { Axis::default() .bounds([-(network_widget_state.current_display_time as f64), 0.0]) .style(self.colours.graph_style) - .labels(&display_time_labels) - .labels_style(self.colours.graph_style) + .labels(display_time_labels) } else { network_widget_state.autohide_timer = None; Axis::default() @@ -208,32 +213,41 @@ impl NetworkGraphWidget for Painter { Axis::default() .bounds([-(network_widget_state.current_display_time as f64), 0.0]) .style(self.colours.graph_style) - .labels(&display_time_labels) - .labels_style(self.colours.graph_style) + .labels(display_time_labels) }; - let y_axis_labels = labels; + let y_axis_labels = labels + .iter() + .map(|label| Span::styled(label, self.colours.graph_style)) + .collect::<Vec<_>>(); let y_axis = Axis::default() .style(self.colours.graph_style) .bounds([0.0, max_range]) - .labels(&y_axis_labels) - .labels_style(self.colours.graph_style); + .labels(y_axis_labels); + + let is_on_widget = widget_id == app_state.current_widget.widget_id; + let border_style = if is_on_widget { + self.colours.highlighted_border_style + } else { + self.colours.border_style + }; let title = if app_state.is_expanded { const TITLE_BASE: &str = " Network ── Esc to go back "; - format!( - " Network ─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2) - ) - ) + Spans::from(vec![ + Span::styled(" Network ", 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 { - " Network ".to_string() - }; - let title_style = if app_state.is_expanded { - self.colours.highlighted_border_style - } else { - self.colours.widget_title_style + Spans::from(Span::styled(" Network ", self.colours.widget_title_style)) }; let legend_constraints = if hide_legend { @@ -321,13 +335,10 @@ impl NetworkGraphWidget for Painter { }; f.render_widget( - // Chart::new(dataset) - Chart::default() - .datasets(&dataset) + Chart::new(dataset) .block( Block::default() - .title(&title) - .title_style(title_style) + .title(title) .borders(Borders::ALL) .border_style(if app_state.current_widget.widget_id == widget_id { self.colours.highlighted_border_style diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs index 89a27ba9..970dcda1 100644 --- a/src/canvas/widgets/process_table.rs +++ b/src/canvas/widgets/process_table.rs @@ -11,7 +11,8 @@ use tui::{ backend::Backend, layout::{Alignment, Constraint, Direction, Layout, Rect}, terminal::Frame, - widgets::{Block, Borders, Paragraph, Row, Table, Text}, + text::{Span, Spans}, + widgets::{Block, Borders, Paragraph, Row, Table}, }; use std::borrow::Cow; @@ -122,7 +123,7 @@ impl ProcessTableWidget for Painter { .direction(Direction::Horizontal) .split(draw_loc)[0]; - let (border_and_title_style, highlight_style) = if is_on_widget { + let (border_style, highlight_style) = if is_on_widget { ( self.colours.highlighted_border_style, self.colours.currently_selected_text_style, @@ -131,41 +132,35 @@ impl ProcessTableWidget for Painter { (self.colours.border_style, self.colours.text_style) }; - let title = if draw_border { - if app_state.is_expanded - && !proc_widget_state - .process_search_state - .search_state - .is_enabled - && !proc_widget_state.is_sort_open - { - const TITLE_BASE: &str = " Processes ── Esc to go back "; - format!( - " Processes ─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width) - .saturating_sub(TITLE_BASE.chars().count() + 2) - ) - ) - } else { - " Processes ".to_string() - } + let title = if app_state.is_expanded + && !proc_widget_state + .process_search_state + .search_state + .is_enabled + && !proc_widget_state.is_sort_open + { + const TITLE_BASE: &str = " Processes ── Esc to go back "; + Spans::from(vec![ + Span::styled(" Processes ", 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 { - String::default() - }; - - let title_style = if app_state.is_expanded { - border_and_title_style - } else { - self.colours.widget_title_style + Spans::from(Span::styled(" Processes ", self.colours.widget_title_style)) }; let process_block = if draw_border { Block::default() - .title(&title) - .title_style(title_style) + .title(title) .borders(Borders::ALL) - .border_style(border_and_title_style) + .border_style(border_style) } else if is_on_widget { Block::default() .borders(*SIDE_BORDERS) @@ -459,7 +454,7 @@ impl ProcessTableWidget for Painter { is_on_widget: bool, grapheme_indices: GraphemeIndices<'a>, start_position: usize, cursor_position: usize, query: &str, currently_selected_text_style: tui::style::Style, text_style: tui::style::Style, - ) -> Vec<Text<'a>> { + ) -> Vec<Span<'a>> { let mut current_grapheme_posn = 0; if is_on_widget { @@ -471,9 +466,9 @@ impl ProcessTableWidget for Painter { None } else { let styled = if grapheme.0 == cursor_position { - Text::styled(grapheme.1, currently_selected_text_style) + Span::styled(grapheme.1, currently_selected_text_style) } else { - Text::styled(grapheme.1, text_style) + Span::styled(grapheme.1, text_style) }; Some(styled) } @@ -481,7 +476,7 @@ impl ProcessTableWidget for Painter { .collect::<Vec<_>>(); if cursor_position >= query.len() { - res.push(Text::styled(" ", currently_selected_text_style)) + res.push(Span::styled(" ", currently_selected_text_style)) } res @@ -495,7 +490,7 @@ impl ProcessTableWidget for Painter { if current_grapheme_posn <= start_position { None } else { - let styled = Text::styled(grapheme.1, text_style); + let styled = Span::styled(grapheme.1, text_style); Some(styled) } }) @@ -543,8 +538,8 @@ impl ProcessTableWidget for Painter { self.colours.text_style, ); - let mut search_text = { - let mut search_vec = vec![Text::styled( + let mut search_text = vec![Spans::from({ + let mut search_vec = vec![Span::styled( search_title, if is_on_widget { self.colours.table_header_style @@ -554,7 +549,7 @@ impl ProcessTableWidget for Painter { )]; search_vec.extend(query_with_cursor); search_vec - }; + })]; // Text options shamelessly stolen from VS Code. let case_style = if !proc_widget_state.process_search_state.is_ignoring_case { @@ -583,26 +578,24 @@ impl ProcessTableWidget for Painter { // FIXME: [MOUSE] Mouse support for these in search // FIXME: [MOVEMENT] Movement support for these in search - let option_text = vec![ - Text::raw("\n"), - Text::styled( + let option_text = Spans::from(vec![ + Span::styled( format!("Case({})", if self.is_mac_os { "F1" } else { "Alt+C" }), case_style, ), - Text::raw(" "), - Text::styled( + Span::raw(" "), + Span::styled( format!("Whole({})", if self.is_mac_os { "F2" } else { "Alt+W" }), whole_word_style, ), - Text::raw(" "), - Text::styled( + Span::raw(" "), + Span::styled( format!("Regex({})", if self.is_mac_os { "F3" } else { "Alt+R" }), regex_style, ), - ]; + ]); - search_text.push(Text::raw("\n")); - search_text.push(Text::styled( + search_text.push(Spans::from(Span::styled( if let Some(err) = &proc_widget_state .process_search_state .search_state @@ -613,8 +606,8 @@ impl ProcessTableWidget for Painter { "" }, self.colours.invalid_query_style, - )); - search_text.extend(option_text); + ))); + search_text.push(option_text); let current_border_style = if proc_widget_state .process_search_state @@ -628,20 +621,21 @@ impl ProcessTableWidget for Painter { self.colours.border_style }; - let title = 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() - }; + 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 = if draw_border { Block::default() - .title(&title) - .title_style(current_border_style) + .title(title) .borders(Borders::ALL) .border_style(current_border_style) } else if is_on_widget { @@ -659,7 +653,7 @@ impl ProcessTableWidget for Painter { .split(draw_loc)[0]; f.render_widget( - Paragraph::new(search_text.iter()) + Paragraph::new(search_text) .block(process_search_block) .style(self.colours.text_style) .alignment(Alignment::Left), diff --git a/src/canvas/widgets/temp_table.rs b/src/canvas/widgets/temp_table.rs index b17bb0e0..960efac6 100644 --- a/src/canvas/widgets/temp_table.rs +++ b/src/canvas/widgets/temp_table.rs @@ -3,6 +3,8 @@ use tui::{ backend::Backend, layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, + text::Span, + text::Spans, widgets::{Block, Borders, Row, Table}, }; @@ -141,7 +143,7 @@ impl TempTableWidget for Painter { Row::Data(truncated_data) }); - let (border_and_title_style, highlight_style) = if is_on_widget { + let (border_style, highlight_style) = if is_on_widget { ( self.colours.highlighted_border_style, self.colours.currently_selected_text_style, @@ -152,29 +154,30 @@ impl TempTableWidget for Painter { let title = if app_state.is_expanded { const TITLE_BASE: &str = " Temperatures ── Esc to go back "; - format!( - " Temperatures ─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2) - ) - ) - } else if app_state.app_config_fields.use_basic_mode { - String::new() + Spans::from(vec![ + Span::styled(" Temperatures ", 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 { - " Temperatures ".to_string() - }; - let title_style = if app_state.is_expanded { - border_and_title_style - } else { - self.colours.widget_title_style + Spans::from(Span::styled( + " Temperatures ", + self.colours.widget_title_style, + )) }; let temp_block = if draw_border { Block::default() - .title(&title) - .title_style(title_style) + .title(title) .borders(Borders::ALL) - .border_style(border_and_title_style) + .border_style(border_style) } else if is_on_widget { Block::default() .borders(*SIDE_BORDERS) diff --git a/src/constants.rs b/src/constants.rs index 3044d023..40f8e646 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -40,45 +40,45 @@ lazy_static! { // FIXME: [HELP] I wanna update this before release... it's missing mouse too. // Help text pub const HELP_CONTENTS_TEXT: [&str; 8] = [ - "Press the corresponding numbers to jump to the section, or scroll:\n", - "1 - General\n", - "2 - CPU widget\n", - "3 - Process widget\n", - "4 - Process search widget\n", - "5 - Process sort widget\n", - "6 - Battery widget\n", + "Press the corresponding numbers to jump to the section, or scroll:", + "1 - General", + "2 - CPU widget", + "3 - Process widget", + "4 - Process search widget", + "5 - Process sort widget", + "6 - Battery widget", "7 - Basic memory widget", ]; pub const GENERAL_HELP_TEXT: [&str; 29] = [ - "1 - General\n", - "q, Ctrl-c Quit\n", - "Esc Close dialog windows, search, widgets, or exit expanded mode\n", - "Ctrl-r Reset display and any collected data\n", - "f Freeze/unfreeze updating with new data\n", - "Ctrl-Left, \n", - "Shift-Left, Move widget selection left\n", - "H, A \n", - "Ctrl-Right, \n", - "Shift-Right, Move widget selection right\n", - "L, D \n", - "Ctrl-Up, \n", - "Shift-Up, Move widget selection up\n", - "K, W \n", - "Ctrl-Down, \n", - "Shift-Down, Move widget selection down\n", - "J, S \n", - "Left, h Move left within widget\n", - "Down, j Move down within widget\n", - "Up, k Move up within widget\n", - "Right, l Move right within widget\n", - "? Open help menu\n", - "gg Jump to the first entry\n", - "G Jump to the last entry\n", - "e Toggle expanding the currently selected widget\n", - "+ Zoom in on chart (decrease time range)\n", - "- Zoom out on chart (increase time range)\n", - "= Reset zoom\n", + "1 - General", + "q, Ctrl-c Quit", + "Esc Close dialog windows, search, widgets, or exit expanded mode", + "Ctrl-r Reset display and any collected data", + "f Freeze/unfreeze updating with new data", + "Ctrl-Left, ", + "Shift-Left, Move widget selection left", + "H, A ", + "Ctrl-Right, ", + "Shift-Right, Move widget selection right", + "L, D ", + "Ctrl-Up, ", + "Shift-Up, Move widget selection up", + "K, W ", + "Ctrl-Down, ", + "Shift-Down, Move widget selection down", + "J, S ", + "Left, h Move left within widget", + "Down, j Move down within widget", + "Up, k Move up within widget", + "Right, l Move right within widget", + "? Open help menu", + "gg Jump to the first entry", + "G Jump to the last entry", + "e Toggle expanding the currently selected widget", + "+ Zoom in on chart (decrease time range)", + "- Zoom out on chart (increase time range)", + "= Reset zoom", "Mouse scroll Scroll through the tables or zoom in/out of charts by scrolling up/down", ]; @@ -90,87 +90,87 @@ pub const CPU_HELP_TEXT: [&str; 2] = [ // TODO [Help]: Search in help? // TODO [Help]: Move to using tables for easier formatting? pub const PROCESS_HELP_TEXT: [&str; 13] = [ - "3 - Process widget\n", - "dd Kill the selected process\n", - "c Sort by CPU usage, press again to reverse sorting order\n", - "m Sort by memory usage, press again to reverse sorting order\n", - "p Sort by PID name, press again to reverse sorting order\n", - "n Sort by process name, press again to reverse sorting order\n", - "Tab Group/un-group processes with the same name\n", - "Ctrl-f, / Open process search widget\n", - "P Toggle between showing the full command or just the process name\n", - "s, F6 Open process sort widget\n", - "I Invert current sort\n", - "% Toggle between values and percentages for memory usage\n", + "3 - Process widget", + "dd Kill the selected process", + "c Sort by CPU usage, press again to reverse sorting order", + "m Sort by memory usage, press again to reverse sorting order", + "p Sort by PID name, press again to reverse sorting order", + "n Sort by process name, press again to reverse sorting order", + "Tab Group/un-group processes with the same name", + "Ctrl-f, / Open process search widget", + "P Toggle between showing the full command or just the process name", + "s, F6 Open process sort widget", + "I Invert current sort", + "% Toggle between values and percentages for memory usage", "t, F5 Toggle tree mode", ]; pub const SEARCH_HELP_TEXT: [&str; 46] = [ - "4 - Process search widget\n", - "Tab Toggle between searching for PID and name\n", - "Esc Close the search widget (retains the filter)\n", - "Ctrl-a Skip to the start of the search query\n", - "Ctrl-e Skip to the end of the search query\n", - "Ctrl-u Clear the current search query\n", - "Backspace Delete the character behind the cursor\n", - "Delete Delete the character at the cursor\n", - "Alt-c, F1 Toggle matching case\n", - "Alt-w, F2 Toggle matching the entire word\n", - "Alt-r, F3 Toggle using regex\n", - "Left, Alt-h Move cursor left\n", - "Right, Alt-l Move cursor right\n", - "\n", - "Supported search types:\n", - "<by name/cmd> ex: btm\n", - "pid ex: pid 825\n", - "cpu, cpu% ex: cpu > 4.2\n", - "mem, mem% ex: mem < 4.2\n", - "memb ex: memb < 100 kb\n", - "read, r/s ex: read >= 1 b\n", - "write, w/s ex: write <= 1 tb\n", - "tread, t.read ex: tread = 1\n", - "twrite, t.write ex: twrite = 1\n", - "state ex: state = running\n", - "\n", - "Comparison operators:\n", - "= ex: cpu = 1\n", - "> ex: cpu > 1\n", - "< ex: cpu < 1\n", - ">= ex: cpu >= 1\n", - "<= ex: cpu <= 1\n", - "\n", - "Logical operators:\n", - "and, &&, <Space> ex: btm and cpu > 1 and mem > 1\n", - "or, || ex: btm or firefox\n", - "\n", - "Supported units:\n", - "B ex: read > 1 b\n", - "KB ex: read > 1 kb\n", - "MB ex: read > 1 mb\n", - "TB ex: read > 1 tb\n", - "KiB ex: read > 1 kib\n", - "MiB ex: read > 1 mib\n", - "GiB ex: read > 1 gib\n", + "4 - Process search widget", + "Tab Toggle between searching for PID and name", + "Esc Close the search widget (retains the filter)", + "Ctrl-a Skip to the start of the search query", + "Ctrl-e Skip to the end of the search query", + "Ctrl-u Clear the current search query", + "Backspace Delete the character behind the cursor", + "Delete Delete the character at the cursor", + "Alt-c, F1 Toggle matching case", + "Alt-w, F2 Toggle matching the entire word", + "Alt-r, F3 Toggle using regex", + "Left, Alt-h Move cursor left", + "Right, Alt-l Move cursor right", + "", + "Supported search types:", + "<by name/cmd> ex: btm", + "pid ex: pid 825", + "cpu, cpu% ex: cpu > 4.2", + "mem, mem% ex: mem < 4.2", + "memb ex: memb < 100 kb", + "read, r/s ex: read >= 1 b", + "write, w/s ex: write <= 1 tb", + "tread, t.read ex: tread = 1", + "twrite, t.write ex: twrite = 1", + "state ex: state = running", + "", + "Comparison operators:", + "= ex: cpu = 1", + "> ex: cpu > 1", + "< ex: cpu < 1", + ">= ex: cpu >= 1", + "<= ex: cpu <= 1", + "", + "Logical operators:", + "and, &&, <Space> ex: btm and cpu > 1 and mem > 1", + "or, || ex: btm or firefox", + "", + "Supported units:", + "B ex: read > 1 b", + "KB ex: read > 1 kb", + "MB ex: read > 1 mb", + "TB ex: read > 1 tb", + "KiB ex: read > 1 kib", + "MiB ex: read > 1 mib", + "GiB ex: read > 1 gib", "TiB ex: read > 1 tib", ]; pub const SORT_HELP_TEXT: [&str; 6] = [ "5 - Sort widget\n", - "Down, 'j' Scroll down in list\n", - "Up, 'k' Scroll up in list\n", - "Mouse scroll Scroll through sort widget\n", - "Esc Close the sort widget\n", + "Down, 'j' Scroll down in list", + "Up, 'k' Scroll up in list", + "Mouse scroll Scroll through sort widget", + "Esc Close the sort widget", "Enter Sort by current selected column", ]; pub const BATTERY_HELP_TEXT: [&str; 3] = [ - "6 - Battery widget\n", - "Left Go to previous battery\n", + "6 - Battery widget", + "Left Go to previous battery", "Right Go to next battery", ]; pub const BASIC_MEM_HELP_TEXT: [&str; 2] = [ - "7 - Basic memory widget\n", + "7 - Basic memory widget", "% Toggle between values and percentages for memory usage", ];