change: add scrolling to help menu

This commit is contained in:
Clement Tsang 2020-04-24 19:17:58 -04:00 committed by GitHub
parent 99fe0a1844
commit 863e780f2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 373 additions and 172 deletions

View File

@ -27,12 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `"processes"` - `"processes"`
- `"temperature"` - `"temperature"`
- Removed an (undocumented) feature in allowing modifying total RX/TX colours. This is mainly due to the legend change.
- Updated error messages to be a bit more consistent/helpful.
- [#117](https://github.com/ClementTsang/bottom/issues/117): Update tui to 0.9: - [#117](https://github.com/ClementTsang/bottom/issues/117): Update tui to 0.9:
- Removed an (undocumented) feature in allowing modifying total RX/TX colours. This is mainly due to the legend change.
- Use custom legend-hiding to stop hiding legends for memory and network widgets. - Use custom legend-hiding to stop hiding legends for memory and network widgets.
- In addition, changed to using only legends within the graph for network, as well as redesigned the legend. - In addition, changed to using only legends within the graph for network, as well as redesigned the legend.
@ -40,9 +38,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow for option to hide the header gap on tables via `--hide_table_gap` or `hide_table_gap = true`. - Allow for option to hide the header gap on tables via `--hide_table_gap` or `hide_table_gap = true`.
- Switch to stateful widget style for tables. - [#126](https://github.com/ClementTsang/bottom/pull/126): Updated error messages to be a bit more consistent/helpful.
- Switch to using tui-rs' new built in linear interpolation rather than doing it manually. - Redesigned help menu to allow for scrolling.
### Bug Fixes ### Bug Fixes
@ -51,9 +49,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed bug where a single empty row as a layout would crash without a proper warning. - Fixed bug where a single empty row as a layout would crash without a proper warning.
The behaviour now errors out with a more helpful message. The behaviour now errors out with a more helpful message.
### Other ### Development changes
- Updated tests and added config testing. - Switch to stateful widget style for tables.
- Switch to using tui-rs' new built in linear interpolation rather than doing it manually.
- Updated arg tests and added config testing.
- More refactoring.
## [0.3.0] - 2020-04-07 ## [0.3.0] - 2020-04-07

View File

@ -7,6 +7,8 @@
A cross-platform graphical process/system monitor with a customizable interface and a multitude of features. Supports Linux, macOS, and Windows. Inspired by both [gtop](https://github.com/aksakalli/gtop) and [gotop](https://github.com/cjbassi/gotop). A cross-platform graphical process/system monitor with a customizable interface and a multitude of features. Supports Linux, macOS, and Windows. Inspired by both [gtop](https://github.com/aksakalli/gtop) and [gotop](https://github.com/cjbassi/gotop).
<!--TODO: Update recording for 0.4-->
![Quick demo recording showing off searching, maximizing, and process killing.](assets/summary_and_search.gif) _Theme based on [gruvbox](https://github.com/morhetz/gruvbox) (see [sample config](./sample_configs/demo_config.toml))._ Recorded on version 0.2.0. ![Quick demo recording showing off searching, maximizing, and process killing.](assets/summary_and_search.gif) _Theme based on [gruvbox](https://github.com/morhetz/gruvbox) (see [sample config](./sample_configs/demo_config.toml))._ Recorded on version 0.2.0.
**Note**: This documentation is relevant to version 0.4.0 and may refer to in-development features, especially if you are reading this on the master branch. Please refer to [release branch](https://github.com/ClementTsang/bottom/tree/release/README.md) or [crates.io](https://crates.io/crates/bottom) for the most up-to-date _release_ documentation. **Note**: This documentation is relevant to version 0.4.0 and may refer to in-development features, especially if you are reading this on the master branch. Please refer to [release branch](https://github.com/ClementTsang/bottom/tree/release/README.md) or [crates.io](https://crates.io/crates/bottom) for the most up-to-date _release_ documentation.
@ -163,19 +165,19 @@ Run using `btm`.
| | | | | |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | -------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `q`, `Ctrl-c` | Quit bottom | | `q`, `Ctrl-c` | Quit |
| `Esc` | Close dialog windows, search, widgets, or exit maximized mode | | `Esc` | Close dialog windows, search, widgets, or exit maximized mode |
| `Ctrl-r` | Reset display and any collected data | | `Ctrl-r` | Reset display and any collected data |
| `f` | Freeze/unfreeze updating with new data | | `f` | Freeze/unfreeze updating with new data |
| `Ctrl`-arrow key<br>`Shift`-arrow key<br>`H/J/K/L` | Move to a different widget (on macOS some keybindings may conflict) | | `Ctrl`-arrow key<br>`Shift`-arrow key<br>`H/J/K/L` | Move to a different widget (on macOS some keybindings may conflict) |
| `Up`,`k` | Scroll up in tables | | `Up`,`k` | Scroll up |
| `Down`, `j` | Scroll down in tables | | `Down`, `j` | Scroll down |
| `?` | Open help menu | | `?` | Open help menu |
| `gg`, `Home` | Jump to the first entry of a table | | `gg`, `Home` | Jump to the first entry |
| `Shift-g`, `End` | Jump to the last entry of a table | | `Shift-g`, `End` | Jump to the last entry |
| `Enter` | Maximize widget | | `Enter` | Maximize the currently selected widget |
| `+` | Zoom in on a chart | | `+` | Zoom in on chart (decrease time range) |
| `-` | Zoom out on a chart | | `-` | Zoom out on chart (increase time range) |
| `=` | Reset zoom | | `=` | Reset zoom |
| Mouse scroll | Table: Scrolls through the list<br>Chart: Zooms in or out by scrolling up or down respectively | | Mouse scroll | Table: Scrolls through the list<br>Chart: Zooms in or out by scrolling up or down respectively |
@ -207,6 +209,9 @@ Run using `btm`.
| `Esc` | Close the search widget (retains the filter) | | `Esc` | Close the search widget (retains the filter) |
| `Ctrl-a` | Skip to the start of the search query | | `Ctrl-a` | Skip to the start of the search query |
| `Ctrl-e` | Skip to the end 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-c`/`F1` | Toggle matching case |
| `Alt-w`/`F2` | Toggle matching the entire word | | `Alt-w`/`F2` | Toggle matching the entire word |
| `Alt-r`/`F3` | Toggle using regex | | `Alt-r`/`F3` | Toggle using regex |

View File

@ -64,18 +64,15 @@ pub enum AppHelpCategory {
Search, Search,
} }
#[derive(Default)]
pub struct AppHelpDialogState { pub struct AppHelpDialogState {
pub is_showing_help: bool, pub is_showing_help: bool,
pub current_category: AppHelpCategory, pub scroll_state: ParagraphScrollState,
} pub general_index: u16,
pub cpu_index: u16,
impl Default for AppHelpDialogState { pub process_index: u16,
fn default() -> Self { pub search_index: u16,
AppHelpDialogState { pub battery_index: u16,
is_showing_help: false,
current_category: AppHelpCategory::General,
}
}
} }
/// AppConfigFields is meant to cover basic fields that would normally be set /// AppConfigFields is meant to cover basic fields that would normally be set
@ -481,6 +478,12 @@ impl BatteryState {
} }
} }
#[derive(Default)]
pub struct ParagraphScrollState {
pub current_scroll_index: u16,
pub max_scroll_index: u16,
}
#[derive(TypedBuilder)] #[derive(TypedBuilder)]
pub struct App { pub struct App {
#[builder(default = false, setter(skip))] #[builder(default = false, setter(skip))]
@ -517,7 +520,7 @@ pub struct App {
pub is_expanded: bool, pub is_expanded: bool,
#[builder(default = false, setter(skip))] #[builder(default = false, setter(skip))]
pub is_resized: bool, pub is_force_redraw: bool,
pub cpu_state: CpuState, pub cpu_state: CpuState,
pub mem_state: MemState, pub mem_state: MemState,
@ -581,11 +584,11 @@ impl App {
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
if self.is_in_dialog() { if self.is_in_dialog() {
self.help_dialog_state.is_showing_help = false; self.help_dialog_state.is_showing_help = false;
self.help_dialog_state.current_category = AppHelpCategory::General;
self.delete_dialog_state.is_showing_dd = false; self.delete_dialog_state.is_showing_dd = false;
self.delete_dialog_state.is_on_yes = false; self.delete_dialog_state.is_on_yes = false;
self.to_delete_process_list = None; self.to_delete_process_list = None;
self.dd_err = None; self.dd_err = None;
self.is_force_redraw = true;
} else if self.is_filtering_or_searching() { } else if self.is_filtering_or_searching() {
match self.current_widget.widget_type { match self.current_widget.widget_type {
BottomWidgetType::Cpu => { BottomWidgetType::Cpu => {
@ -603,7 +606,7 @@ impl App {
cpu_widget_state.scroll_state.current_scroll_position = new_position; cpu_widget_state.scroll_state.current_scroll_position = new_position;
cpu_widget_state.scroll_state.previous_scroll_position = 0; cpu_widget_state.scroll_state.previous_scroll_position = 0;
} }
self.is_resized = true; self.is_force_redraw = true;
} }
} }
BottomWidgetType::CpuLegend => { BottomWidgetType::CpuLegend => {
@ -621,7 +624,7 @@ impl App {
cpu_widget_state.scroll_state.current_scroll_position = new_position; cpu_widget_state.scroll_state.current_scroll_position = new_position;
cpu_widget_state.scroll_state.previous_scroll_position = 0; cpu_widget_state.scroll_state.previous_scroll_position = 0;
} }
self.is_resized = true; self.is_force_redraw = true;
} }
} }
BottomWidgetType::Proc => { BottomWidgetType::Proc => {
@ -636,7 +639,7 @@ impl App {
.search_state .search_state
.is_enabled = false; .is_enabled = false;
} }
self.is_resized = true; self.is_force_redraw = true;
} }
} }
BottomWidgetType::ProcSearch => { BottomWidgetType::ProcSearch => {
@ -652,14 +655,14 @@ impl App {
.is_enabled = false; .is_enabled = false;
self.move_widget_selection_up(); self.move_widget_selection_up();
} }
self.is_resized = true; self.is_force_redraw = true;
} }
} }
_ => {} _ => {}
} }
} else if self.is_expanded { } else if self.is_expanded {
self.is_expanded = false; self.is_expanded = false;
self.is_resized = true; self.is_force_redraw = true;
} }
} }
@ -974,7 +977,7 @@ impl App {
BottomWidgetType::ProcSearch => {} BottomWidgetType::ProcSearch => {}
_ => { _ => {
self.is_expanded = true; self.is_expanded = true;
self.is_resized = true; self.is_force_redraw = true;
} }
} }
} }
@ -1098,13 +1101,19 @@ impl App {
pub fn on_up_key(&mut self) { pub fn on_up_key(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
self.decrement_position_count(); self.decrement_position_count();
} else if self.help_dialog_state.is_showing_help {
self.help_scroll_up();
} }
self.reset_multi_tap_keys();
} }
pub fn on_down_key(&mut self) { pub fn on_down_key(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
self.increment_position_count(); self.increment_position_count();
} else if self.help_dialog_state.is_showing_help {
self.help_scroll_down();
} }
self.reset_multi_tap_keys();
} }
pub fn on_left_key(&mut self) { pub fn on_left_key(&mut self) {
@ -1428,10 +1437,16 @@ impl App {
} }
self.handle_char(caught_char); self.handle_char(caught_char);
} else if self.help_dialog_state.is_showing_help { } else if self.help_dialog_state.is_showing_help {
// TODO: Seems weird that we have it like this; it would be better to make this
// more obvious that we are separating dialog logic and normal logic IMO.
// This is even more so as most logic already checks for dialog state.
match caught_char { match caught_char {
'1' => self.help_dialog_state.current_category = AppHelpCategory::General, '1' => self.help_scroll_to_or_max(self.help_dialog_state.general_index),
'2' => self.help_dialog_state.current_category = AppHelpCategory::Process, '2' => self.help_scroll_to_or_max(self.help_dialog_state.cpu_index),
'3' => self.help_dialog_state.current_category = AppHelpCategory::Search, '3' => self.help_scroll_to_or_max(self.help_dialog_state.process_index),
'4' => self.help_scroll_to_or_max(self.help_dialog_state.search_index),
'5' => self.help_scroll_to_or_max(self.help_dialog_state.battery_index),
'j' | 'k' | 'g' | 'G' => self.handle_char(caught_char),
_ => {} _ => {}
} }
} }
@ -1478,8 +1493,8 @@ impl App {
} }
} }
'G' => self.skip_to_last(), 'G' => self.skip_to_last(),
'k' => self.decrement_position_count(), 'k' => self.on_up_key(),
'j' => self.increment_position_count(), 'j' => self.on_down_key(),
'f' => { 'f' => {
self.is_frozen = !self.is_frozen; self.is_frozen = !self.is_frozen;
if self.is_frozen { if self.is_frozen {
@ -1584,6 +1599,7 @@ impl App {
} }
'?' => { '?' => {
self.help_dialog_state.is_showing_help = true; self.help_dialog_state.is_showing_help = true;
self.is_force_redraw = true;
} }
'H' => self.move_widget_selection_left(), 'H' => self.move_widget_selection_left(),
'L' => self.move_widget_selection_right(), 'L' => self.move_widget_selection_right(),
@ -2019,6 +2035,8 @@ impl App {
_ => {} _ => {}
} }
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} else {
self.help_dialog_state.scroll_state.current_scroll_index = 0;
} }
} }
@ -2093,6 +2111,12 @@ impl App {
_ => {} _ => {}
} }
self.reset_multi_tap_keys(); self.reset_multi_tap_keys();
} else {
self.help_dialog_state.scroll_state.current_scroll_index = self
.help_dialog_state
.scroll_state
.max_scroll_index
.saturating_sub(1);
} }
} }
@ -2105,7 +2129,6 @@ impl App {
BottomWidgetType::CpuLegend => self.change_cpu_table_position(-1), BottomWidgetType::CpuLegend => self.change_cpu_table_position(-1),
_ => {} _ => {}
} }
self.reset_multi_tap_keys();
} }
} }
@ -2118,7 +2141,6 @@ impl App {
BottomWidgetType::CpuLegend => self.change_cpu_table_position(1), BottomWidgetType::CpuLegend => self.change_cpu_table_position(1),
_ => {} _ => {}
} }
self.reset_multi_tap_keys();
} }
} }
@ -2228,8 +2250,33 @@ impl App {
} }
} }
fn help_scroll_up(&mut self) {
if self.help_dialog_state.scroll_state.current_scroll_index > 0 {
self.help_dialog_state.scroll_state.current_scroll_index -= 1;
}
}
fn help_scroll_down(&mut self) {
if self.help_dialog_state.scroll_state.current_scroll_index + 1
< self.help_dialog_state.scroll_state.max_scroll_index
{
self.help_dialog_state.scroll_state.current_scroll_index += 1;
}
}
fn help_scroll_to_or_max(&mut self, new_position: u16) {
if new_position < self.help_dialog_state.scroll_state.max_scroll_index {
self.help_dialog_state.scroll_state.current_scroll_index = new_position;
} else {
self.help_dialog_state.scroll_state.current_scroll_index =
self.help_dialog_state.scroll_state.max_scroll_index - 1;
}
}
pub fn handle_scroll_up(&mut self) { pub fn handle_scroll_up(&mut self) {
if self.current_widget.widget_type.is_widget_graph() { if self.help_dialog_state.is_showing_help {
self.help_scroll_up();
} else if self.current_widget.widget_type.is_widget_graph() {
self.zoom_in(); self.zoom_in();
} else if self.current_widget.widget_type.is_widget_table() { } else if self.current_widget.widget_type.is_widget_table() {
self.decrement_position_count(); self.decrement_position_count();
@ -2237,7 +2284,9 @@ impl App {
} }
pub fn handle_scroll_down(&mut self) { pub fn handle_scroll_down(&mut self) {
if self.current_widget.widget_type.is_widget_graph() { if self.help_dialog_state.is_showing_help {
self.help_scroll_down();
} else if self.current_widget.widget_type.is_widget_graph() {
self.zoom_out(); self.zoom_out();
} else if self.current_widget.widget_type.is_widget_table() { } else if self.current_widget.widget_type.is_widget_table() {
self.increment_position_count(); self.increment_position_count();

View File

@ -59,9 +59,7 @@ pub struct Painter {
pub colours: CanvasColours, pub colours: CanvasColours,
height: u16, height: u16,
width: u16, width: u16,
styled_general_help_text: Vec<Text<'static>>, styled_help_text: Vec<Text<'static>>,
styled_process_help_text: Vec<Text<'static>>,
styled_search_help_text: Vec<Text<'static>>,
is_mac_os: bool, is_mac_os: bool,
row_constraints: Vec<Constraint>, row_constraints: Vec<Constraint>,
col_constraints: Vec<Vec<Constraint>>, col_constraints: Vec<Vec<Constraint>>,
@ -145,9 +143,7 @@ impl Painter {
colours: CanvasColours::default(), colours: CanvasColours::default(),
height: 0, height: 0,
width: 0, width: 0,
styled_general_help_text: Vec::new(), styled_help_text: Vec::new(),
styled_process_help_text: Vec::new(),
styled_search_help_text: Vec::new(),
is_mac_os: false, is_mac_os: false,
row_constraints, row_constraints,
col_constraints, col_constraints,
@ -164,44 +160,79 @@ impl Painter {
pub fn complete_painter_init(&mut self) { pub fn complete_painter_init(&mut self) {
self.is_mac_os = cfg!(target_os = "macos"); self.is_mac_os = cfg!(target_os = "macos");
if GENERAL_HELP_TEXT.len() > 1 { // Init help text:
self.styled_general_help_text.push(Text::Styled( // ToC
GENERAL_HELP_TEXT[0].into(), self.styled_help_text.extend(
self.colours.table_header_style, HELP_CONTENTS_TEXT
)); .iter()
self.styled_general_help_text.extend( .map(|&text| Text::Styled(text.into(), self.colours.text_style))
GENERAL_HELP_TEXT[1..] .collect::<Vec<_>>(),
.iter() );
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
.collect::<Vec<_>>(),
);
}
if PROCESS_HELP_TEXT.len() > 1 { // General
self.styled_process_help_text.push(Text::Styled( self.styled_help_text.push(Text::Raw("\n\n".into()));
PROCESS_HELP_TEXT[0].into(), self.styled_help_text.push(Text::Styled(
self.colours.table_header_style, GENERAL_HELP_TEXT[0].into(),
)); self.colours.table_header_style,
self.styled_process_help_text.extend( ));
PROCESS_HELP_TEXT[1..] self.styled_help_text.extend(
.iter() GENERAL_HELP_TEXT[1..]
.map(|&text| Text::Styled(text.into(), self.colours.text_style)) .iter()
.collect::<Vec<_>>(), .map(|&text| Text::Styled(text.into(), self.colours.text_style))
); .collect::<Vec<_>>(),
} );
if SEARCH_HELP_TEXT.len() > 1 { // CPU
self.styled_search_help_text.push(Text::Styled( self.styled_help_text.push(Text::Raw("\n\n".into()));
SEARCH_HELP_TEXT[0].into(), self.styled_help_text.push(Text::Styled(
self.colours.table_header_style, CPU_HELP_TEXT[0].into(),
)); self.colours.table_header_style,
self.styled_search_help_text.extend( ));
SEARCH_HELP_TEXT[1..] self.styled_help_text.extend(
.iter() CPU_HELP_TEXT[1..]
.map(|&text| Text::Styled(text.into(), self.colours.text_style)) .iter()
.collect::<Vec<_>>(), .map(|&text| Text::Styled(text.into(), self.colours.text_style))
); .collect::<Vec<_>>(),
} );
// Proc
self.styled_help_text.push(Text::Raw("\n\n".into()));
self.styled_help_text.push(Text::Styled(
PROCESS_HELP_TEXT[0].into(),
self.colours.table_header_style,
));
self.styled_help_text.extend(
PROCESS_HELP_TEXT[1..]
.iter()
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
.collect::<Vec<_>>(),
);
// Proc Search
self.styled_help_text.push(Text::Raw("\n\n".into()));
self.styled_help_text.push(Text::Styled(
SEARCH_HELP_TEXT[0].into(),
self.colours.table_header_style,
));
self.styled_help_text.extend(
SEARCH_HELP_TEXT[1..]
.iter()
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
.collect::<Vec<_>>(),
);
// Battery
self.styled_help_text.push(Text::Raw("\n\n".into()));
self.styled_help_text.push(Text::Styled(
BATTERY_HELP_TEXT[0].into(),
self.colours.table_header_style,
));
self.styled_help_text.extend(
BATTERY_HELP_TEXT[1..]
.iter()
.map(|&text| Text::Styled(text.into(), self.colours.text_style))
.collect::<Vec<_>>(),
);
} }
// TODO: [FEATURE] Auto-resizing dialog sizes. // TODO: [FEATURE] Auto-resizing dialog sizes.
@ -214,11 +245,10 @@ impl Painter {
let current_height = terminal_size.height; let current_height = terminal_size.height;
let current_width = terminal_size.width; let current_width = terminal_size.width;
if self.height == 0 && self.width == 0 { if (self.height == 0 && self.width == 0)
self.height = current_height; || (self.height != current_height || self.width != current_width)
self.width = current_width; {
} else if self.height != current_height || self.width != current_width { app_state.is_force_redraw = true;
app_state.is_resized = true;
self.height = current_height; self.height = current_height;
self.width = current_width; self.width = current_width;
} }
@ -434,7 +464,7 @@ impl Painter {
} }
} else { } else {
// Draws using the passed in (or default) layout. NOT basic so far. // Draws using the passed in (or default) layout. NOT basic so far.
if self.derived_widget_draw_locs.is_empty() || app_state.is_resized { if self.derived_widget_draw_locs.is_empty() || app_state.is_force_redraw {
let row_draw_locs = Layout::default() let row_draw_locs = Layout::default()
.margin(0) .margin(0)
.constraints(self.row_constraints.as_ref()) .constraints(self.row_constraints.as_ref())
@ -531,7 +561,7 @@ impl Painter {
} }
})?; })?;
app_state.is_resized = false; app_state.is_force_redraw = false;
Ok(()) Ok(())
} }

View File

View File

@ -1,4 +1,5 @@
use std::cmp::max; use std::cmp::max;
use unicode_width::UnicodeWidthStr;
use tui::{ use tui::{
backend::Backend, backend::Backend,
@ -7,12 +8,9 @@ use tui::{
widgets::{Block, Borders, Paragraph}, widgets::{Block, Borders, Paragraph},
}; };
use crate::{ use crate::{app::App, canvas::Painter, constants};
app::{App, AppHelpCategory},
canvas::Painter,
};
const HELP_BASE: &str = " Help ── 1: General ─── 2: Processes ─── 3: Search ─── Esc to close "; const HELP_BASE: &str = " Help ── Esc to close ";
pub trait HelpDialog { pub trait HelpDialog {
fn draw_help_dialog<B: Backend>( fn draw_help_dialog<B: Backend>(
@ -28,31 +26,121 @@ impl HelpDialog for Painter {
0, 0,
draw_loc.width as i32 - HELP_BASE.chars().count() as i32 - 2, draw_loc.width as i32 - HELP_BASE.chars().count() as i32 - 2,
); );
let help_title = format!( let help_title = format!(" Help ─{}─ Esc to close ", "".repeat(repeat_num as usize));
" Help ─{}─ 1: General ─── 2: Processes ─── 3: Search ─── Esc to close ",
"".repeat(repeat_num as usize) if app_state.is_force_redraw {
); // We must also recalculate how many lines are wrapping to properly get scrolling to work on
// small terminal sizes... oh joy.
// TODO: Make this more automated and easier to add.
let mut overflow_buffer = 0;
let paragraph_width = draw_loc.width - 2;
constants::HELP_CONTENTS_TEXT.iter().for_each(|text_line| {
overflow_buffer +=
UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16 / paragraph_width;
});
// General
app_state.help_dialog_state.general_index =
constants::HELP_CONTENTS_TEXT.len() as u16 + 1 + overflow_buffer;
constants::GENERAL_HELP_TEXT.iter().for_each(|text_line| {
overflow_buffer +=
UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16 / paragraph_width;
});
// CPU
app_state.help_dialog_state.cpu_index =
(constants::HELP_CONTENTS_TEXT.len() + constants::GENERAL_HELP_TEXT.len()) as u16
+ 2
+ overflow_buffer;
constants::CPU_HELP_TEXT.iter().for_each(|text_line| {
overflow_buffer +=
UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16 / paragraph_width;
});
// Processes
app_state.help_dialog_state.process_index = (constants::HELP_CONTENTS_TEXT.len()
+ constants::GENERAL_HELP_TEXT.len()
+ constants::CPU_HELP_TEXT.len())
as u16
+ 3
+ overflow_buffer;
constants::PROCESS_HELP_TEXT.iter().for_each(|text_line| {
overflow_buffer +=
UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16 / paragraph_width;
});
// Search
app_state.help_dialog_state.search_index = (constants::HELP_CONTENTS_TEXT.len()
+ constants::GENERAL_HELP_TEXT.len()
+ constants::CPU_HELP_TEXT.len()
+ constants::PROCESS_HELP_TEXT.len())
as u16
+ 4
+ overflow_buffer;
constants::SEARCH_HELP_TEXT.iter().for_each(|text_line| {
overflow_buffer +=
UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16 / paragraph_width;
});
// Battery
app_state.help_dialog_state.battery_index = (constants::HELP_CONTENTS_TEXT.len()
+ constants::GENERAL_HELP_TEXT.len()
+ constants::CPU_HELP_TEXT.len()
+ constants::PROCESS_HELP_TEXT.len()
+ constants::SEARCH_HELP_TEXT.len())
as u16
+ 5
+ overflow_buffer;
constants::BATTERY_HELP_TEXT.iter().for_each(|text_line| {
overflow_buffer +=
UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16 / paragraph_width;
});
app_state.help_dialog_state.scroll_state.max_scroll_index =
(self.styled_help_text.len() as u16
+ (constants::NUM_CATEGORIES - 3)
+ overflow_buffer)
.saturating_sub(draw_loc.height);
// Fix if over-scrolled
if app_state
.help_dialog_state
.scroll_state
.current_scroll_index
>= app_state.help_dialog_state.scroll_state.max_scroll_index
{
app_state
.help_dialog_state
.scroll_state
.current_scroll_index = app_state
.help_dialog_state
.scroll_state
.max_scroll_index
.saturating_sub(1);
}
}
f.render_widget( f.render_widget(
Paragraph::new( Paragraph::new(self.styled_help_text.iter())
match app_state.help_dialog_state.current_category { .block(
AppHelpCategory::General => &self.styled_general_help_text, Block::default()
AppHelpCategory::Process => &self.styled_process_help_text, .title(&help_title)
AppHelpCategory::Search => &self.styled_search_help_text, .title_style(self.colours.border_style)
} .style(self.colours.border_style)
.iter(), .borders(Borders::ALL)
) .border_style(self.colours.border_style),
.block( )
Block::default() .style(self.colours.text_style)
.title(&help_title) .alignment(Alignment::Left)
.title_style(self.colours.border_style) .wrap(true)
.style(self.colours.border_style) .scroll(
.borders(Borders::ALL) app_state
.border_style(self.colours.border_style), .help_dialog_state
) .scroll_state
.style(self.colours.text_style) .current_scroll_index,
.alignment(Alignment::Left) ),
.wrap(true),
draw_loc, draw_loc,
); );
} }

View File

@ -1,6 +1,7 @@
use crate::app; use crate::app;
use itertools::izip; use itertools::izip;
// TODO: Reverse intrinsic?
/// A somewhat jury-rigged solution to simulate a variable intrinsic layout for /// A somewhat jury-rigged solution to simulate a variable intrinsic layout for
/// table widths. Note that this will do one main pass to try to properly /// table widths. Note that this will do one main pass to try to properly
/// allocate widths. This will thus potentially cut off latter elements /// allocate widths. This will thus potentially cut off latter elements
@ -77,9 +78,9 @@ pub fn get_variable_intrinsic_widths(
pub fn get_search_start_position( pub fn get_search_start_position(
num_columns: usize, cursor_direction: &app::CursorDirection, cursor_bar: &mut usize, num_columns: usize, cursor_direction: &app::CursorDirection, cursor_bar: &mut usize,
current_cursor_position: usize, is_resized: bool, current_cursor_position: usize, is_force_redraw: bool,
) -> usize { ) -> usize {
if is_resized { if is_force_redraw {
*cursor_bar = 0; *cursor_bar = 0;
} }
@ -117,9 +118,9 @@ pub fn get_search_start_position(
pub fn get_start_position( pub fn get_start_position(
num_rows: u64, scroll_direction: &app::ScrollDirection, scroll_position_bar: &mut u64, num_rows: u64, scroll_direction: &app::ScrollDirection, scroll_position_bar: &mut u64,
currently_selected_position: u64, is_resized: bool, currently_selected_position: u64, is_force_redraw: bool,
) -> u64 { ) -> u64 {
if is_resized { if is_force_redraw {
*scroll_position_bar = 0; *scroll_position_bar = 0;
} }

View File

@ -224,7 +224,7 @@ impl CpuGraphWidget for Painter {
&cpu_widget_state.scroll_state.scroll_direction, &cpu_widget_state.scroll_state.scroll_direction,
&mut cpu_widget_state.scroll_state.previous_scroll_position, &mut cpu_widget_state.scroll_state.previous_scroll_position,
cpu_widget_state.scroll_state.current_scroll_position, cpu_widget_state.scroll_state.current_scroll_position,
app_state.is_resized, app_state.is_force_redraw,
); );
let is_on_widget = widget_id == app_state.current_widget.widget_id; let is_on_widget = widget_id == app_state.current_widget.widget_id;

View File

@ -45,7 +45,7 @@ impl DiskTableWidget for Painter {
&disk_widget_state.scroll_state.scroll_direction, &disk_widget_state.scroll_state.scroll_direction,
&mut disk_widget_state.scroll_state.previous_scroll_position, &mut disk_widget_state.scroll_state.previous_scroll_position,
disk_widget_state.scroll_state.current_scroll_position, disk_widget_state.scroll_state.current_scroll_position,
app_state.is_resized, app_state.is_force_redraw,
); );
let is_on_widget = app_state.current_widget.widget_id == widget_id; let is_on_widget = app_state.current_widget.widget_id == widget_id;
let disk_table_state = &mut disk_widget_state.scroll_state.table_state; let disk_table_state = &mut disk_widget_state.scroll_state.table_state;

View File

@ -91,7 +91,7 @@ impl ProcessTableWidget for Painter {
&proc_widget_state.scroll_state.scroll_direction, &proc_widget_state.scroll_state.scroll_direction,
&mut proc_widget_state.scroll_state.previous_scroll_position, &mut proc_widget_state.scroll_state.previous_scroll_position,
proc_widget_state.scroll_state.current_scroll_position, proc_widget_state.scroll_state.current_scroll_position,
app_state.is_resized, app_state.is_force_redraw,
); );
// Sanity check // Sanity check
@ -370,7 +370,7 @@ impl ProcessTableWidget for Painter {
.search_state .search_state
.cursor_bar, .cursor_bar,
current_cursor_position, current_cursor_position,
app_state.is_resized, app_state.is_force_redraw,
); );
let query = proc_widget_state.get_current_search_query().as_str(); let query = proc_widget_state.get_current_search_query().as_str();

View File

@ -46,7 +46,7 @@ impl TempTableWidget for Painter {
&temp_widget_state.scroll_state.scroll_direction, &temp_widget_state.scroll_state.scroll_direction,
&mut temp_widget_state.scroll_state.previous_scroll_position, &mut temp_widget_state.scroll_state.previous_scroll_position,
temp_widget_state.scroll_state.current_scroll_position, temp_widget_state.scroll_state.current_scroll_position,
app_state.is_resized, app_state.is_force_redraw,
); );
let is_on_widget = widget_id == app_state.current_widget.widget_id; let is_on_widget = widget_id == app_state.current_widget.widget_id;
let temp_table_state = &mut temp_widget_state.scroll_state.table_state; let temp_table_state = &mut temp_widget_state.scroll_state.table_state;

View File

@ -36,52 +36,76 @@ lazy_static! {
} }
// Help text // Help text
pub const NUM_CATEGORIES: u16 = 6;
pub const HELP_CONTENTS_TEXT: [&str; 6] = [
"Press the corresponding numbers to jump to the section, or scroll:\n",
"1 - General bindings\n",
"2 - CPU bindings\n",
"3 - Process bindings\n",
"4 - Process search bindings\n",
"5 - Battery bindings",
];
pub const GENERAL_HELP_TEXT: [&str; 18] = [ pub const GENERAL_HELP_TEXT: [&str; 18] = [
"General Keybindings\n\n", "1 - General bindings\n",
"q, Ctrl-c Quit bottom\n", "q, Ctrl-c Quit\n",
"Esc Close filters, dialog boxes, etc.\n", "Esc Close dialog windows, search, widgets, or exit maximized mode\n",
"Ctrl-r Reset all data\n", "Ctrl-r Reset display and any collected data\n",
"f Freeze display\n", "f Freeze/unfreeze updating with new data\n",
"Ctrl-Arrow Change your selected widget\n", "Ctrl-Arrow \n",
"Shift-Arrow Change your selected widget\n", "Shift-Arrow Move to a different widget\n",
"H/J/K/L Change your selected widget up/down/left/right\n", "H/J/K/L \n",
"Up, k Move cursor up\n", "Up, k Scroll up\n",
"Down, j Move cursor down\n", "Down, j Scroll down\n",
"? Open the help screen\n", "? Open help menu\n",
"gg Skip to the first entry of a list\n", "gg Jump to the first entry\n",
"G Skip to the last entry of a list\n", "G Jump to the last entry\n",
"Enter Maximize the currently selected widget\n", "Enter Maximize the currently selected widget\n",
"/ Filter out graph lines (only CPU at the moment)\n", "+ Zoom in on chart (decrease time range)\n",
"+ Zoom in (decrease time range)\n", "- Zoom out on chart (increase time range)\n",
"- Zoom out (increase time range)\n",
"= Reset zoom\n", "= Reset zoom\n",
"Mouse scroll Scroll through the tables or zoom in/out of charts by scrolling up/down",
];
pub const CPU_HELP_TEXT: [&str; 4] = [
"2 - CPU bindings\n",
"/ Open filtering for showing certain CPU cores\n",
"Space Toggle enabled/disabled cores\n",
"Esc Exit filtering mode",
]; ];
pub const PROCESS_HELP_TEXT: [&str; 8] = [ pub const PROCESS_HELP_TEXT: [&str; 8] = [
"Process Keybindings\n\n", "3 - Process bindings\n",
"dd, Delete Kill the highlighted process\n", "dd Kill the selected process\n",
"c Sort by CPU usage\n", "c Sort by memory usage, press again to reverse sorting order\n",
"m Sort by memory usage\n", "m Sort by memory usage\n",
"p Sort by PID\n", "p Sort by PID name, press again to reverse sorting order\n",
"n Sort by process name\n", "n Sort by process name, press again to reverse sorting order\n",
"Tab Group together processes with the same name\n", "Tab Group/un-group processes with the same name\n",
"Ctrl-f, / Open up the search widget\n", "Ctrl-f, / Open process search widget",
]; ];
pub const SEARCH_HELP_TEXT: [&str; 13] = [ pub const SEARCH_HELP_TEXT: [&str; 13] = [
"Search Keybindings\n\n", "4 - Process search bindings\n",
"Tab Toggle between searching for PID and name.\n", "Tab Toggle between searching for PID and name\n",
"Esc Close search widget\n", "Esc Close the search widget (retains the filter)\n",
"Ctrl-a Skip to the start of search widget\n", "Ctrl-a Skip to the start of the search query\n",
"Ctrl-e Skip to the end of search widget\n", "Ctrl-e Skip to the end of the search query\n",
"Ctrl-u Clear the current search query\n", "Ctrl-u Clear the current search query\n",
"Backspace Delete the character behind the cursor\n", "Backspace Delete the character behind the cursor\n",
"Delete Delete the character at 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 Move cursor left\n", "Left Move cursor left\n",
"Right Move cursor right\n", "Right Move cursor right",
"Alt-c/F1 Toggle whether to ignore case\n", ];
"Alt-w/F2 Toggle whether to match the whole word\n",
"Alt-r/F3 Toggle whether to use regex\n", pub const BATTERY_HELP_TEXT: [&str; 3] = [
"5 - Battery bindings\n",
"Left Go to previous battery\n",
"Right Go to next battery",
]; ];
// Config and flags // Config and flags

View File

@ -114,17 +114,6 @@ fn main() -> error::Result<()> {
painter.colours.generate_remaining_cpu_colours(); painter.colours.generate_remaining_cpu_colours();
painter.complete_painter_init(); painter.complete_painter_init();
// Set up up tui and crossterm
let mut stdout_val = stdout();
execute!(stdout_val, EnterAlternateScreen, EnableMouseCapture)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout_val))?;
terminal.hide_cursor()?;
// Set panic hook
panic::set_hook(Box::new(|info| panic_hook(info)));
// Set up input handling // Set up input handling
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
create_input_thread(tx.clone()); create_input_thread(tx.clone());
@ -151,6 +140,17 @@ fn main() -> error::Result<()> {
app.used_widgets.clone(), app.used_widgets.clone(),
); );
// Set up up tui and crossterm
let mut stdout_val = stdout();
execute!(stdout_val, EnterAlternateScreen, EnableMouseCapture)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout_val))?;
terminal.hide_cursor()?;
// Set panic hook
panic::set_hook(Box::new(|info| panic_hook(info)));
let mut first_run = true; let mut first_run = true;
loop { loop {
if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) { if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) {