From 863e780f2f8f09fb7244bcf9819694f50cc1a400 Mon Sep 17 00:00:00 2001
From: Clement Tsang <34804052+ClementTsang@users.noreply.github.com>
Date: Fri, 24 Apr 2020 19:17:58 -0400
Subject: [PATCH] change: add scrolling to help menu
---
CHANGELOG.md | 20 ++--
README.md | 21 ++--
src/app.rs | 103 ++++++++++++++------
src/canvas.rs | 128 +++++++++++++++----------
src/canvas/dialogs/filter_dialog.rs | 0
src/canvas/dialogs/help_dialog.rs | 144 ++++++++++++++++++++++------
src/canvas/drawing_utils.rs | 9 +-
src/canvas/widgets/cpu_graph.rs | 2 +-
src/canvas/widgets/disk_table.rs | 2 +-
src/canvas/widgets/process_table.rs | 4 +-
src/canvas/widgets/temp_table.rs | 2 +-
src/constants.rs | 88 ++++++++++-------
src/main.rs | 22 ++---
13 files changed, 373 insertions(+), 172 deletions(-)
create mode 100644 src/canvas/dialogs/filter_dialog.rs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b2a0ef2..80f59a6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,12 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `"processes"`
- `"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:
+ - 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.
- 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`.
- - 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
@@ -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.
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
diff --git a/README.md b/README.md
index 8767a79e..a5e23fe1 100644
--- a/README.md
+++ b/README.md
@@ -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).
+
+
![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.
@@ -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 |
| `Ctrl-r` | Reset display and any collected data |
| `f` | Freeze/unfreeze updating with new data |
| `Ctrl`-arrow key
`Shift`-arrow key
`H/J/K/L` | Move to a different widget (on macOS some keybindings may conflict) |
-| `Up`,`k` | Scroll up in tables |
-| `Down`, `j` | Scroll down in tables |
+| `Up`,`k` | Scroll up |
+| `Down`, `j` | Scroll down |
| `?` | Open help menu |
-| `gg`, `Home` | Jump to the first entry of a table |
-| `Shift-g`, `End` | Jump to the last entry of a table |
-| `Enter` | Maximize widget |
-| `+` | Zoom in on a chart |
-| `-` | Zoom out on a chart |
+| `gg`, `Home` | Jump to the first entry |
+| `Shift-g`, `End` | Jump to the last entry |
+| `Enter` | Maximize the currently selected widget |
+| `+` | Zoom in on chart (decrease time range) |
+| `-` | Zoom out on chart (increase time range) |
| `=` | Reset zoom |
| Mouse scroll | Table: Scrolls through the list
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) |
| `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 |
diff --git a/src/app.rs b/src/app.rs
index 4f32b398..a71c4192 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -64,18 +64,15 @@ pub enum AppHelpCategory {
Search,
}
+#[derive(Default)]
pub struct AppHelpDialogState {
pub is_showing_help: bool,
- pub current_category: AppHelpCategory,
-}
-
-impl Default for AppHelpDialogState {
- fn default() -> Self {
- AppHelpDialogState {
- is_showing_help: false,
- current_category: AppHelpCategory::General,
- }
- }
+ pub scroll_state: ParagraphScrollState,
+ pub general_index: u16,
+ pub cpu_index: u16,
+ pub process_index: u16,
+ pub search_index: u16,
+ pub battery_index: u16,
}
/// 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)]
pub struct App {
#[builder(default = false, setter(skip))]
@@ -517,7 +520,7 @@ pub struct App {
pub is_expanded: bool,
#[builder(default = false, setter(skip))]
- pub is_resized: bool,
+ pub is_force_redraw: bool,
pub cpu_state: CpuState,
pub mem_state: MemState,
@@ -581,11 +584,11 @@ impl App {
self.reset_multi_tap_keys();
if self.is_in_dialog() {
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_on_yes = false;
self.to_delete_process_list = None;
self.dd_err = None;
+ self.is_force_redraw = true;
} else if self.is_filtering_or_searching() {
match self.current_widget.widget_type {
BottomWidgetType::Cpu => {
@@ -603,7 +606,7 @@ impl App {
cpu_widget_state.scroll_state.current_scroll_position = new_position;
cpu_widget_state.scroll_state.previous_scroll_position = 0;
}
- self.is_resized = true;
+ self.is_force_redraw = true;
}
}
BottomWidgetType::CpuLegend => {
@@ -621,7 +624,7 @@ impl App {
cpu_widget_state.scroll_state.current_scroll_position = new_position;
cpu_widget_state.scroll_state.previous_scroll_position = 0;
}
- self.is_resized = true;
+ self.is_force_redraw = true;
}
}
BottomWidgetType::Proc => {
@@ -636,7 +639,7 @@ impl App {
.search_state
.is_enabled = false;
}
- self.is_resized = true;
+ self.is_force_redraw = true;
}
}
BottomWidgetType::ProcSearch => {
@@ -652,14 +655,14 @@ impl App {
.is_enabled = false;
self.move_widget_selection_up();
}
- self.is_resized = true;
+ self.is_force_redraw = true;
}
}
_ => {}
}
} else if self.is_expanded {
self.is_expanded = false;
- self.is_resized = true;
+ self.is_force_redraw = true;
}
}
@@ -974,7 +977,7 @@ impl App {
BottomWidgetType::ProcSearch => {}
_ => {
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) {
if !self.is_in_dialog() {
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) {
if !self.is_in_dialog() {
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) {
@@ -1428,10 +1437,16 @@ impl App {
}
self.handle_char(caught_char);
} 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 {
- '1' => self.help_dialog_state.current_category = AppHelpCategory::General,
- '2' => self.help_dialog_state.current_category = AppHelpCategory::Process,
- '3' => self.help_dialog_state.current_category = AppHelpCategory::Search,
+ '1' => self.help_scroll_to_or_max(self.help_dialog_state.general_index),
+ '2' => self.help_scroll_to_or_max(self.help_dialog_state.cpu_index),
+ '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(),
- 'k' => self.decrement_position_count(),
- 'j' => self.increment_position_count(),
+ 'k' => self.on_up_key(),
+ 'j' => self.on_down_key(),
'f' => {
self.is_frozen = !self.is_frozen;
if self.is_frozen {
@@ -1584,6 +1599,7 @@ impl App {
}
'?' => {
self.help_dialog_state.is_showing_help = true;
+ self.is_force_redraw = true;
}
'H' => self.move_widget_selection_left(),
'L' => self.move_widget_selection_right(),
@@ -2019,6 +2035,8 @@ impl App {
_ => {}
}
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();
+ } 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),
_ => {}
}
- self.reset_multi_tap_keys();
}
}
@@ -2118,7 +2141,6 @@ impl App {
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) {
- 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();
} else if self.current_widget.widget_type.is_widget_table() {
self.decrement_position_count();
@@ -2237,7 +2284,9 @@ impl App {
}
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();
} else if self.current_widget.widget_type.is_widget_table() {
self.increment_position_count();
diff --git a/src/canvas.rs b/src/canvas.rs
index 9e4aaf30..ad332655 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -59,9 +59,7 @@ pub struct Painter {
pub colours: CanvasColours,
height: u16,
width: u16,
- styled_general_help_text: Vec>,
- styled_process_help_text: Vec>,
- styled_search_help_text: Vec>,
+ styled_help_text: Vec>,
is_mac_os: bool,
row_constraints: Vec,
col_constraints: Vec>,
@@ -145,9 +143,7 @@ impl Painter {
colours: CanvasColours::default(),
height: 0,
width: 0,
- styled_general_help_text: Vec::new(),
- styled_process_help_text: Vec::new(),
- styled_search_help_text: Vec::new(),
+ styled_help_text: Vec::new(),
is_mac_os: false,
row_constraints,
col_constraints,
@@ -164,44 +160,79 @@ impl Painter {
pub fn complete_painter_init(&mut self) {
self.is_mac_os = cfg!(target_os = "macos");
- if GENERAL_HELP_TEXT.len() > 1 {
- self.styled_general_help_text.push(Text::Styled(
- GENERAL_HELP_TEXT[0].into(),
- self.colours.table_header_style,
- ));
- self.styled_general_help_text.extend(
- GENERAL_HELP_TEXT[1..]
- .iter()
- .map(|&text| Text::Styled(text.into(), self.colours.text_style))
- .collect::>(),
- );
- }
+ // Init help text:
+ // ToC
+ self.styled_help_text.extend(
+ HELP_CONTENTS_TEXT
+ .iter()
+ .map(|&text| Text::Styled(text.into(), self.colours.text_style))
+ .collect::>(),
+ );
- if PROCESS_HELP_TEXT.len() > 1 {
- self.styled_process_help_text.push(Text::Styled(
- PROCESS_HELP_TEXT[0].into(),
- self.colours.table_header_style,
- ));
- self.styled_process_help_text.extend(
- PROCESS_HELP_TEXT[1..]
- .iter()
- .map(|&text| Text::Styled(text.into(), self.colours.text_style))
- .collect::>(),
- );
- }
+ // General
+ self.styled_help_text.push(Text::Raw("\n\n".into()));
+ self.styled_help_text.push(Text::Styled(
+ GENERAL_HELP_TEXT[0].into(),
+ self.colours.table_header_style,
+ ));
+ self.styled_help_text.extend(
+ GENERAL_HELP_TEXT[1..]
+ .iter()
+ .map(|&text| Text::Styled(text.into(), self.colours.text_style))
+ .collect::>(),
+ );
- if SEARCH_HELP_TEXT.len() > 1 {
- self.styled_search_help_text.push(Text::Styled(
- SEARCH_HELP_TEXT[0].into(),
- self.colours.table_header_style,
- ));
- self.styled_search_help_text.extend(
- SEARCH_HELP_TEXT[1..]
- .iter()
- .map(|&text| Text::Styled(text.into(), self.colours.text_style))
- .collect::>(),
- );
- }
+ // CPU
+ self.styled_help_text.push(Text::Raw("\n\n".into()));
+ self.styled_help_text.push(Text::Styled(
+ CPU_HELP_TEXT[0].into(),
+ self.colours.table_header_style,
+ ));
+ self.styled_help_text.extend(
+ CPU_HELP_TEXT[1..]
+ .iter()
+ .map(|&text| Text::Styled(text.into(), self.colours.text_style))
+ .collect::>(),
+ );
+
+ // 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::>(),
+ );
+
+ // 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::>(),
+ );
+
+ // 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::>(),
+ );
}
// TODO: [FEATURE] Auto-resizing dialog sizes.
@@ -214,11 +245,10 @@ impl Painter {
let current_height = terminal_size.height;
let current_width = terminal_size.width;
- if self.height == 0 && self.width == 0 {
- self.height = current_height;
- self.width = current_width;
- } else if self.height != current_height || self.width != current_width {
- app_state.is_resized = true;
+ if (self.height == 0 && self.width == 0)
+ || (self.height != current_height || self.width != current_width)
+ {
+ app_state.is_force_redraw = true;
self.height = current_height;
self.width = current_width;
}
@@ -434,7 +464,7 @@ impl Painter {
}
} else {
// 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()
.margin(0)
.constraints(self.row_constraints.as_ref())
@@ -531,7 +561,7 @@ impl Painter {
}
})?;
- app_state.is_resized = false;
+ app_state.is_force_redraw = false;
Ok(())
}
diff --git a/src/canvas/dialogs/filter_dialog.rs b/src/canvas/dialogs/filter_dialog.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/src/canvas/dialogs/help_dialog.rs b/src/canvas/dialogs/help_dialog.rs
index 8dbca876..70492ae9 100644
--- a/src/canvas/dialogs/help_dialog.rs
+++ b/src/canvas/dialogs/help_dialog.rs
@@ -1,4 +1,5 @@
use std::cmp::max;
+use unicode_width::UnicodeWidthStr;
use tui::{
backend::Backend,
@@ -7,12 +8,9 @@ use tui::{
widgets::{Block, Borders, Paragraph},
};
-use crate::{
- app::{App, AppHelpCategory},
- canvas::Painter,
-};
+use crate::{app::App, canvas::Painter, constants};
-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 {
fn draw_help_dialog(
@@ -28,31 +26,121 @@ impl HelpDialog for Painter {
0,
draw_loc.width as i32 - HELP_BASE.chars().count() as i32 - 2,
);
- let help_title = format!(
- " Help ─{}─ 1: General ─── 2: Processes ─── 3: Search ─── Esc to close ",
- "─".repeat(repeat_num as usize)
- );
+ let help_title = format!(" Help ─{}─ 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(
- Paragraph::new(
- match app_state.help_dialog_state.current_category {
- AppHelpCategory::General => &self.styled_general_help_text,
- AppHelpCategory::Process => &self.styled_process_help_text,
- AppHelpCategory::Search => &self.styled_search_help_text,
- }
- .iter(),
- )
- .block(
- Block::default()
- .title(&help_title)
- .title_style(self.colours.border_style)
- .style(self.colours.border_style)
- .borders(Borders::ALL)
- .border_style(self.colours.border_style),
- )
- .style(self.colours.text_style)
- .alignment(Alignment::Left)
- .wrap(true),
+ Paragraph::new(self.styled_help_text.iter())
+ .block(
+ Block::default()
+ .title(&help_title)
+ .title_style(self.colours.border_style)
+ .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(
+ app_state
+ .help_dialog_state
+ .scroll_state
+ .current_scroll_index,
+ ),
draw_loc,
);
}
diff --git a/src/canvas/drawing_utils.rs b/src/canvas/drawing_utils.rs
index 6c008724..2281e503 100644
--- a/src/canvas/drawing_utils.rs
+++ b/src/canvas/drawing_utils.rs
@@ -1,6 +1,7 @@
use crate::app;
use itertools::izip;
+// TODO: Reverse intrinsic?
/// 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
/// 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(
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 {
- if is_resized {
+ if is_force_redraw {
*cursor_bar = 0;
}
@@ -117,9 +118,9 @@ pub fn get_search_start_position(
pub fn get_start_position(
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 {
- if is_resized {
+ if is_force_redraw {
*scroll_position_bar = 0;
}
diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs
index cefed8a2..a49bd6ad 100644
--- a/src/canvas/widgets/cpu_graph.rs
+++ b/src/canvas/widgets/cpu_graph.rs
@@ -224,7 +224,7 @@ impl CpuGraphWidget for Painter {
&cpu_widget_state.scroll_state.scroll_direction,
&mut cpu_widget_state.scroll_state.previous_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;
diff --git a/src/canvas/widgets/disk_table.rs b/src/canvas/widgets/disk_table.rs
index 13d24358..fda54d97 100644
--- a/src/canvas/widgets/disk_table.rs
+++ b/src/canvas/widgets/disk_table.rs
@@ -45,7 +45,7 @@ impl DiskTableWidget for Painter {
&disk_widget_state.scroll_state.scroll_direction,
&mut disk_widget_state.scroll_state.previous_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 disk_table_state = &mut disk_widget_state.scroll_state.table_state;
diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs
index 0733b500..40803f9b 100644
--- a/src/canvas/widgets/process_table.rs
+++ b/src/canvas/widgets/process_table.rs
@@ -91,7 +91,7 @@ impl ProcessTableWidget for Painter {
&proc_widget_state.scroll_state.scroll_direction,
&mut proc_widget_state.scroll_state.previous_scroll_position,
proc_widget_state.scroll_state.current_scroll_position,
- app_state.is_resized,
+ app_state.is_force_redraw,
);
// Sanity check
@@ -370,7 +370,7 @@ impl ProcessTableWidget for Painter {
.search_state
.cursor_bar,
current_cursor_position,
- app_state.is_resized,
+ app_state.is_force_redraw,
);
let query = proc_widget_state.get_current_search_query().as_str();
diff --git a/src/canvas/widgets/temp_table.rs b/src/canvas/widgets/temp_table.rs
index 007d93b0..1e679904 100644
--- a/src/canvas/widgets/temp_table.rs
+++ b/src/canvas/widgets/temp_table.rs
@@ -46,7 +46,7 @@ impl TempTableWidget for Painter {
&temp_widget_state.scroll_state.scroll_direction,
&mut temp_widget_state.scroll_state.previous_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 temp_table_state = &mut temp_widget_state.scroll_state.table_state;
diff --git a/src/constants.rs b/src/constants.rs
index a94dcc78..dca06781 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -36,52 +36,76 @@ lazy_static! {
}
// 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] = [
- "General Keybindings\n\n",
- "q, Ctrl-c Quit bottom\n",
- "Esc Close filters, dialog boxes, etc.\n",
- "Ctrl-r Reset all data\n",
- "f Freeze display\n",
- "Ctrl-Arrow Change your selected widget\n",
- "Shift-Arrow Change your selected widget\n",
- "H/J/K/L Change your selected widget up/down/left/right\n",
- "Up, k Move cursor up\n",
- "Down, j Move cursor down\n",
- "? Open the help screen\n",
- "gg Skip to the first entry of a list\n",
- "G Skip to the last entry of a list\n",
+ "1 - General bindings\n",
+ "q, Ctrl-c Quit\n",
+ "Esc Close dialog windows, search, widgets, or exit maximized mode\n",
+ "Ctrl-r Reset display and any collected data\n",
+ "f Freeze/unfreeze updating with new data\n",
+ "Ctrl-Arrow \n",
+ "Shift-Arrow Move to a different widget\n",
+ "H/J/K/L \n",
+ "Up, k Scroll up\n",
+ "Down, j Scroll down\n",
+ "? Open help menu\n",
+ "gg Jump to the first entry\n",
+ "G Jump to the last entry\n",
"Enter Maximize the currently selected widget\n",
- "/ Filter out graph lines (only CPU at the moment)\n",
- "+ Zoom in (decrease time range)\n",
- "- Zoom out (increase time range)\n",
+ "+ Zoom in on chart (decrease time range)\n",
+ "- Zoom out on chart (increase time range)\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] = [
- "Process Keybindings\n\n",
- "dd, Delete Kill the highlighted process\n",
- "c Sort by CPU usage\n",
+ "3 - Process bindings\n",
+ "dd Kill the selected process\n",
+ "c Sort by memory usage, press again to reverse sorting order\n",
"m Sort by memory usage\n",
- "p Sort by PID\n",
- "n Sort by process name\n",
- "Tab Group together processes with the same name\n",
- "Ctrl-f, / Open up the search widget\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",
];
pub const SEARCH_HELP_TEXT: [&str; 13] = [
- "Search Keybindings\n\n",
- "Tab Toggle between searching for PID and name.\n",
- "Esc Close search widget\n",
- "Ctrl-a Skip to the start of search widget\n",
- "Ctrl-e Skip to the end of search widget\n",
+ "4 - Process search bindings\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 Move cursor left\n",
- "Right Move cursor right\n",
- "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",
+ "Right Move cursor right",
+];
+
+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
diff --git a/src/main.rs b/src/main.rs
index 675e8cdf..1940a544 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -114,17 +114,6 @@ fn main() -> error::Result<()> {
painter.colours.generate_remaining_cpu_colours();
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
let (tx, rx) = mpsc::channel();
create_input_thread(tx.clone());
@@ -151,6 +140,17 @@ fn main() -> error::Result<()> {
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;
loop {
if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) {