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?
This commit is contained in:
Clement Tsang 2020-09-26 20:04:34 -04:00 committed by ClementTsang
parent 6db76029e2
commit 750d8f3cb7
20 changed files with 488 additions and 548 deletions

View File

@ -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

6
Cargo.lock generated
View File

@ -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",
]

View File

@ -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"

View File

@ -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!

View File

@ -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...

View File

@ -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],

View File

@ -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,
);
}

View File

@ -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);

View File

@ -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],

View File

@ -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() {

View File

@ -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,
);
}

View File

@ -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),
)

View File

@ -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)

View File

@ -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],
);

View File

@ -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

View File

@ -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],
);

View File

@ -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

View File

@ -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),

View File

@ -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)

View File

@ -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",
];