diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c0a1e5e..0fb9fb69 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Changes
-- [#557](https://github.com/ClementTsang/bottom/pull/557): Add '/s' to network usage legend.
+- [#557](https://github.com/ClementTsang/bottom/pull/557): Add '/s' to network usage legend to indicate "per second".
## Internal Changes
diff --git a/Cargo.lock b/Cargo.lock
index f8dbb36c..0c75735d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -263,6 +263,7 @@ dependencies = [
"serde",
"smol",
"sysinfo",
+ "textwrap 0.14.2",
"thiserror",
"toml",
"tui",
@@ -348,7 +349,7 @@ dependencies = [
"atty",
"bitflags",
"strsim",
- "textwrap",
+ "textwrap 0.11.0",
"unicode-width",
"vec_map",
]
@@ -1410,6 +1411,12 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
+[[package]]
+name = "smawk"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
+
[[package]]
name = "smol"
version = "1.2.5"
@@ -1482,6 +1489,17 @@ dependencies = [
"unicode-width",
]
+[[package]]
+name = "textwrap"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
+dependencies = [
+ "smawk",
+ "unicode-linebreak",
+ "unicode-width",
+]
+
[[package]]
name = "thiserror"
version = "1.0.24"
@@ -1558,6 +1576,15 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f"
+dependencies = [
+ "regex",
+]
+
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
diff --git a/Cargo.toml b/Cargo.toml
index c2370011..58ac69e9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -57,6 +57,7 @@ serde = { version = "1.0.125", features = ["derive"] }
# Sysinfo is still used in Linux for the ProcessStatus
sysinfo = "0.18.2"
thiserror = "1.0.24"
+textwrap = "0.14.2"
toml = "0.5.8"
tui = { version = "0.16.0", features = ["crossterm"], default-features = false }
typed-builder = "0.9.0"
diff --git a/docs/content/usage/general-usage.md b/docs/content/usage/general-usage.md
index eb457aef..de134a69 100644
--- a/docs/content/usage/general-usage.md
+++ b/docs/content/usage/general-usage.md
@@ -42,7 +42,7 @@ Note that key bindings are generally case-sensitive.
| ++q++ , ++ctrl+c++ | Quit |
| ++esc++ | Close dialog windows, search, widgets, or exit expanded mode |
| ++ctrl+r++ | Resets any collected data |
-| ++f++ | Toggles freezing, which stops new data from being shown |
+| ++f++ | Toggles freezing, stopping new data from being shown |
| ++question++ | Open help menu |
| ++e++ | Toggle expanding the currently selected widget |
| ++ctrl+up++
++shift+up++
++K++
++W++ | Select the widget above |
diff --git a/src/app.rs b/src/app.rs
index b3409d3f..3b95a536 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -13,7 +13,7 @@ use std::{
time::Instant,
};
-use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEventKind};
+use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent};
use fxhash::FxHashMap;
use indextree::{Arena, NodeId};
use unicode_segmentation::GraphemeCursor;
@@ -33,7 +33,7 @@ use crate::{
BottomEvent, Pid,
};
-use self::event::{EventResult, ReturnSignal, WidgetEventResult};
+use self::event::{ComponentEventResult, EventResult, ReturnSignal};
const MAX_SEARCH_LENGTH: usize = 200;
@@ -140,8 +140,6 @@ pub struct AppState {
// --- Eventually delete/rewrite ---
pub delete_dialog_state: AppDeleteDialogState,
- pub help_dialog_state: AppHelpDialogState,
-
// --- TO DELETE ---
pub cpu_state: CpuState,
pub mem_state: MemState,
@@ -172,6 +170,8 @@ pub struct AppState {
pub layout_tree: Arena,
pub layout_tree_root: NodeId,
frozen_state: FrozenState,
+
+ pub help_dialog: DialogState,
}
impl AppState {
@@ -204,7 +204,6 @@ impl AppState {
data_collection: Default::default(),
is_expanded: Default::default(),
delete_dialog_state: Default::default(),
- help_dialog_state: Default::default(),
cpu_state: Default::default(),
mem_state: Default::default(),
net_state: Default::default(),
@@ -222,6 +221,7 @@ impl AppState {
is_force_redraw: Default::default(),
is_determining_widget_boundary: Default::default(),
frozen_state: Default::default(),
+ help_dialog: Default::default(),
}
}
@@ -277,7 +277,7 @@ impl AppState {
let c = c.to_ascii_lowercase();
match c {
'q' => EventResult::Quit,
- 'e' => {
+ 'e' if !self.help_dialog.is_showing() => {
if self.app_config_fields.use_basic_mode {
EventResult::NoRedraw
} else {
@@ -285,11 +285,11 @@ impl AppState {
EventResult::Redraw
}
}
- '?' => {
- self.help_dialog_state.is_showing_help = true;
+ '?' if !self.help_dialog.is_showing() => {
+ self.help_dialog.show();
EventResult::Redraw
}
- 'f' => {
+ 'f' if !self.help_dialog.is_showing() => {
self.toggle_freeze();
if !self.is_frozen() {
let data_collection = &self.data_collection;
@@ -331,17 +331,59 @@ impl AppState {
}
}
+ /// Quick and dirty handler to convert [`ComponentEventResult`]s to [`EventResult`]s, and handle [`ReturnSignal`]s.
+ fn convert_widget_event_result(&mut self, w: ComponentEventResult) -> EventResult {
+ match w {
+ ComponentEventResult::Unhandled => EventResult::NoRedraw,
+ ComponentEventResult::Redraw => EventResult::Redraw,
+ ComponentEventResult::NoRedraw => EventResult::NoRedraw,
+ ComponentEventResult::Signal(signal) => match signal {
+ ReturnSignal::KillProcess => {
+ todo!()
+ }
+ ReturnSignal::Update => {
+ if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ match &self.frozen_state {
+ FrozenState::NotFrozen => {
+ widget.update_data(&self.data_collection);
+ }
+ FrozenState::Frozen(frozen_data) => {
+ widget.update_data(frozen_data);
+ }
+ }
+ }
+ EventResult::Redraw
+ }
+ },
+ }
+ }
+
+ /// Handles a [`KeyEvent`], and returns an [`EventResult`].
+ fn handle_key_event(&mut self, event: KeyEvent) -> EventResult {
+ let result = if let DialogState::Shown(help_dialog) = &mut self.help_dialog {
+ help_dialog.handle_key_event(event)
+ } else if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ widget.handle_key_event(event)
+ } else {
+ ComponentEventResult::Unhandled
+ };
+
+ match result {
+ ComponentEventResult::Unhandled => self.handle_global_key_event(event),
+ _ => self.convert_widget_event_result(result),
+ }
+ }
+
/// Handles a global [`KeyEvent`], and returns an [`EventResult`].
- fn handle_global_shortcut(&mut self, event: KeyEvent) -> EventResult {
+ fn handle_global_key_event(&mut self, event: KeyEvent) -> EventResult {
if event.modifiers.is_empty() {
match event.code {
KeyCode::Esc => {
if self.is_expanded {
self.is_expanded = false;
EventResult::Redraw
- } else if self.help_dialog_state.is_showing_help {
- self.help_dialog_state.is_showing_help = false;
- self.help_dialog_state.scroll_state.current_scroll_index = 0;
+ } else if self.help_dialog.is_showing() {
+ self.help_dialog.hide();
EventResult::Redraw
} else if self.delete_dialog_state.is_showing_dd {
self.close_dd();
@@ -350,8 +392,13 @@ impl AppState {
EventResult::NoRedraw
}
}
- KeyCode::Char(c) => self.handle_global_char(c),
- _ => EventResult::NoRedraw,
+ _ => {
+ if let KeyCode::Char(c) = event.code {
+ self.handle_global_char(c)
+ } else {
+ EventResult::NoRedraw
+ }
+ }
}
} else if let KeyModifiers::CONTROL = event.modifiers {
match event.code {
@@ -384,30 +431,55 @@ impl AppState {
}
}
- /// Quick and dirty handler to convert [`WidgetEventResult`]s to [`EventResult`]s, and handle [`ReturnSignal`]s.
- fn convert_widget_event_result(&mut self, w: WidgetEventResult) -> EventResult {
- match w {
- WidgetEventResult::Quit => EventResult::Quit,
- WidgetEventResult::Redraw => EventResult::Redraw,
- WidgetEventResult::NoRedraw => EventResult::NoRedraw,
- WidgetEventResult::Signal(signal) => match signal {
- ReturnSignal::KillProcess => {
- todo!()
- }
- ReturnSignal::Update => {
- if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
- match &self.frozen_state {
- FrozenState::NotFrozen => {
- widget.update_data(&self.data_collection);
- }
- FrozenState::Frozen(frozen_data) => {
- widget.update_data(frozen_data);
- }
+ /// Handles a [`MouseEvent`].
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> EventResult {
+ if let DialogState::Shown(help_dialog) = &mut self.help_dialog {
+ let result = help_dialog.handle_mouse_event(event);
+ self.convert_widget_event_result(result)
+ } else if self.is_expanded {
+ if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ let result = widget.handle_mouse_event(event);
+ self.convert_widget_event_result(result)
+ } else {
+ EventResult::NoRedraw
+ }
+ } else {
+ let mut returned_result = EventResult::NoRedraw;
+ for (id, widget) in self.widget_lookup_map.iter_mut() {
+ if widget.does_border_intersect_mouse(&event) {
+ let result = widget.handle_mouse_event(event);
+
+ let new_id;
+ match widget.selectable_type() {
+ SelectableType::Selectable => {
+ new_id = *id;
+ }
+ SelectableType::Unselectable => {
+ let result = widget.handle_mouse_event(event);
+ return self.convert_widget_event_result(result);
+ }
+ SelectableType::Redirect(redirected_id) => {
+ new_id = redirected_id;
}
}
- EventResult::Redraw
+
+ let was_id_already_selected = self.selected_widget == new_id;
+ self.selected_widget = new_id;
+
+ if was_id_already_selected {
+ returned_result = self.convert_widget_event_result(result);
+ break;
+ } else {
+ // If the weren't equal, *force* a redraw, and correct the layout tree.
+ correct_layout_last_selections(&mut self.layout_tree, self.selected_widget);
+ let _ = self.convert_widget_event_result(result);
+ returned_result = EventResult::Redraw;
+ break;
+ }
}
- },
+ }
+
+ returned_result
}
}
@@ -415,83 +487,16 @@ impl AppState {
/// whether the app now requires a redraw.
pub fn handle_event(&mut self, event: BottomEvent) -> EventResult {
match event {
- BottomEvent::KeyInput(event) => {
- if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
- let result = widget.handle_key_event(event);
- match self.convert_widget_event_result(result) {
- EventResult::Quit => EventResult::Quit,
- EventResult::Redraw => EventResult::Redraw,
- EventResult::NoRedraw => self.handle_global_shortcut(event),
- }
- } else {
- self.handle_global_shortcut(event)
- }
- }
+ BottomEvent::KeyInput(event) => self.handle_key_event(event),
BottomEvent::MouseInput(event) => {
// Not great, but basically a blind lookup through the table for anything that clips the click location.
- // TODO: Would be cool to use a kd-tree or something like that in the future.
-
- match &event.kind {
- MouseEventKind::Down(MouseButton::Left) => {
- if self.is_expanded {
- if let Some(widget) =
- self.widget_lookup_map.get_mut(&self.selected_widget)
- {
- let result = widget.handle_mouse_event(event);
- return self.convert_widget_event_result(result);
- }
- } else {
- for (id, widget) in self.widget_lookup_map.iter_mut() {
- if widget.does_border_intersect_mouse(&event) {
- let result = widget.handle_mouse_event(event);
-
- let new_id;
- match widget.selectable_type() {
- SelectableType::Selectable => {
- new_id = *id;
- }
- SelectableType::Unselectable => {
- let result = widget.handle_mouse_event(event);
- return self.convert_widget_event_result(result);
- }
- SelectableType::Redirect(redirected_id) => {
- new_id = redirected_id;
- }
- }
-
- let was_id_already_selected = self.selected_widget == new_id;
- self.selected_widget = new_id;
-
- if was_id_already_selected {
- return self.convert_widget_event_result(result);
- } else {
- // If the weren't equal, *force* a redraw, and correct the layout tree.
- correct_layout_last_selections(
- &mut self.layout_tree,
- self.selected_widget,
- );
- let _ = self.convert_widget_event_result(result);
- return EventResult::Redraw;
- }
- }
- }
- }
- }
- MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => {
- if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget)
- {
- let result = widget.handle_mouse_event(event);
- return self.convert_widget_event_result(result);
- }
- }
- _ => {}
- }
-
- EventResult::NoRedraw
+ self.handle_mouse_event(event)
}
BottomEvent::Update(new_data) => {
self.data_collection.eat_data(new_data);
+ // TODO: Optimization for dialogs; don't redraw here.
+
if !self.is_frozen() {
let data_collection = &self.data_collection;
self.widget_lookup_map
@@ -515,78 +520,6 @@ impl AppState {
}
}
- pub fn on_esc(&mut self) {
- self.reset_multi_tap_keys();
- if self.is_in_dialog() {
- if self.help_dialog_state.is_showing_help {
- self.help_dialog_state.is_showing_help = false;
- self.help_dialog_state.scroll_state.current_scroll_index = 0;
- } else {
- self.close_dd();
- }
-
- self.is_force_redraw = true;
- } else {
- match self.current_widget.widget_type {
- BottomWidgetType::Proc => {
- if let Some(current_proc_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id)
- {
- if current_proc_state.is_search_enabled() || current_proc_state.is_sort_open
- {
- current_proc_state
- .process_search_state
- .search_state
- .is_enabled = false;
-
- current_proc_state.is_sort_open = false;
- self.is_force_redraw = true;
- return;
- }
- }
- }
- BottomWidgetType::ProcSearch => {
- if let Some(current_proc_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id - 1)
- {
- if current_proc_state.is_search_enabled() {
- current_proc_state
- .process_search_state
- .search_state
- .is_enabled = false;
- self.move_widget_selection(&WidgetDirection::Up);
- self.is_force_redraw = true;
- return;
- }
- }
- }
- BottomWidgetType::ProcSort => {
- if let Some(current_proc_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id - 2)
- {
- if current_proc_state.is_sort_open {
- current_proc_state.columns.current_scroll_position =
- current_proc_state.columns.backup_prev_scroll_position;
- current_proc_state.is_sort_open = false;
- self.move_widget_selection(&WidgetDirection::Right);
- self.is_force_redraw = true;
- return;
- }
- }
- }
- _ => {}
- }
-
- if self.is_expanded {
- self.is_expanded = false;
- self.is_force_redraw = true;
- }
- }
- }
-
pub fn is_in_search_widget(&self) -> bool {
matches!(
self.current_widget.widget_type,
@@ -600,7 +533,7 @@ impl AppState {
}
fn is_in_dialog(&self) -> bool {
- self.help_dialog_state.is_showing_help || self.delete_dialog_state.is_showing_dd
+ self.delete_dialog_state.is_showing_dd
}
fn ignore_normal_keybinds(&self) -> bool {
@@ -1097,7 +1030,7 @@ impl AppState {
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 {
+ } else if self.help_dialog.is_showing() {
self.help_scroll_up();
} else if self.delete_dialog_state.is_showing_dd {
#[cfg(target_os = "windows")]
@@ -1118,7 +1051,7 @@ impl AppState {
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 {
+ } else if self.help_dialog.is_showing() {
self.help_scroll_down();
} else if self.delete_dialog_state.is_showing_dd {
#[cfg(target_os = "windows")]
@@ -1596,19 +1529,9 @@ impl AppState {
}
}
self.handle_char(caught_char);
- } else if self.help_dialog_state.is_showing_help {
+ } else if self.help_dialog.is_showing() {
match caught_char {
- '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
- let potential_index = caught_char.to_digit(10);
- if let Some(potential_index) = potential_index {
- if (potential_index as usize) < self.help_dialog_state.index_shortcuts.len()
- {
- self.help_scroll_to_or_max(
- self.help_dialog_state.index_shortcuts[potential_index as usize],
- );
- }
- }
- }
+ '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {}
'j' | 'k' | 'g' | 'G' => self.handle_char(caught_char),
_ => {}
}
@@ -1792,7 +1715,7 @@ impl AppState {
}
}
'?' => {
- self.help_dialog_state.is_showing_help = true;
+ self.help_dialog.show();
self.is_force_redraw = true;
}
'H' | 'A' => self.move_widget_selection(&WidgetDirection::Left),
@@ -1863,7 +1786,6 @@ impl AppState {
}
fn expand_widget(&mut self) {
- // TODO: [BASIC] Expansion in basic mode.
if !self.ignore_normal_keybinds() && !self.app_config_fields.use_basic_mode {
// Pop-out mode. We ignore if in process search.
@@ -2256,7 +2178,7 @@ impl AppState {
{
if proc_widget_state.is_sort_open {
if let Some(proc_sort_widget) = self.widget_map.get(&new_widget_id) {
- self.current_widget = proc_sort_widget.clone(); // TODO: Could I remove this clone w/ static references?
+ self.current_widget = proc_sort_widget.clone();
}
}
}
@@ -2378,8 +2300,8 @@ impl AppState {
_ => {}
}
self.reset_multi_tap_keys();
- } else if self.help_dialog_state.is_showing_help {
- self.help_dialog_state.scroll_state.current_scroll_index = 0;
+ } else if self.help_dialog.is_showing() {
+ // self.help_dialog_state.scroll_state.current_scroll_index = 0;
} else if self.delete_dialog_state.is_showing_dd {
self.delete_dialog_state.selected_signal = KillSignal::Cancel;
}
@@ -2517,26 +2439,17 @@ impl AppState {
}
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;
- }
+ // 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;
- }
+ // 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 on_plus(&mut self) {
diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs
index c2747289..dc6ee1ab 100644
--- a/src/app/data_farmer.rs
+++ b/src/app/data_farmer.rs
@@ -224,15 +224,12 @@ impl DataCollection {
}
fn eat_temp(&mut self, temperature_sensors: Vec) {
- // TODO: [PO] To implement
self.temp_harvest = temperature_sensors.to_vec();
}
fn eat_disks(
&mut self, disks: Vec, io: disks::IoHarvest, harvested_time: Instant,
) {
- // TODO: [PO] To implement
-
let time_since_last_harvest = harvested_time
.duration_since(self.current_instant)
.as_secs_f64();
diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes.rs
index 3d8d7d5c..151d3a55 100644
--- a/src/app/data_harvester/processes.rs
+++ b/src/app/data_harvester/processes.rs
@@ -27,7 +27,6 @@ use std::borrow::Cow;
use crate::Pid;
-// TODO: Add value so we know if it's sorted ascending or descending by default?
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum ProcessSorting {
CpuPercent,
diff --git a/src/app/data_harvester/temperature.rs b/src/app/data_harvester/temperature.rs
index 8f3b776e..3426a053 100644
--- a/src/app/data_harvester/temperature.rs
+++ b/src/app/data_harvester/temperature.rs
@@ -57,7 +57,6 @@ fn is_temp_filtered(filter: &Option, text: &str) -> bool {
fn temp_vec_sort(temperature_vec: &mut Vec) {
// By default, sort temperature, then by alphabetically!
- // TODO: [TEMPS] Allow users to control this.
// Note we sort in reverse here; we want greater temps to be higher priority.
temperature_vec.sort_by(|a, b| match a.temperature.partial_cmp(&b.temperature) {
diff --git a/src/app/event.rs b/src/app/event.rs
index 9201e2c0..f727050d 100644
--- a/src/app/event.rs
+++ b/src/app/event.rs
@@ -1,6 +1,5 @@
-use std::time::{Duration, Instant};
-
-const MAX_TIMEOUT: Duration = Duration::from_millis(400);
+pub mod multi_key;
+pub use multi_key::*;
/// These are "signals" that are sent along with an [`WidgetEventResult`] to signify a potential additional action
/// that the caller must do, along with the "core" result of either drawing or redrawing.
@@ -32,9 +31,9 @@ pub enum EventResult {
/// The results of a widget handling some event, like a mouse or key event,
/// signifying what the program should then do next.
#[derive(Debug)]
-pub enum WidgetEventResult {
- /// Kill the program.
- Quit,
+pub enum ComponentEventResult {
+ /// The event isn't handled by the widget, and should be propagated to the parent.
+ Unhandled,
/// Trigger a redraw.
Redraw,
/// Don't trigger a redraw.
@@ -50,101 +49,3 @@ pub enum SelectionAction {
/// This event occurs if the widget did not handle the selection action; the caller must handle it.
NotHandled,
}
-
-/// The states a [`MultiKey`] can be in.
-enum MultiKeyState {
- /// Currently not waiting on any next input.
- Idle,
- /// Waiting for the next input, with a given trigger [`Instant`].
- Waiting {
- /// When it was triggered.
- trigger_instant: Instant,
-
- /// What part of the pattern it is at.
- checked_index: usize,
- },
-}
-
-/// The possible outcomes of calling [`MultiKey::input`].
-pub enum MultiKeyResult {
- /// Returned when a character was *accepted*, but has not completed the sequence required.
- Accepted,
- /// Returned when a character is accepted and completes the sequence.
- Completed,
- /// Returned if a character breaks the sequence or if it has already timed out.
- Rejected,
-}
-
-/// A struct useful for managing multi-key keybinds.
-pub struct MultiKey {
- state: MultiKeyState,
- pattern: Vec,
-}
-
-impl MultiKey {
- /// Creates a new [`MultiKey`] with a given pattern and timeout.
- pub fn register(pattern: Vec) -> Self {
- Self {
- state: MultiKeyState::Idle,
- pattern,
- }
- }
-
- /// Resets to an idle state.
- fn reset(&mut self) {
- self.state = MultiKeyState::Idle;
- }
-
- /// Handles a char input and returns the current status of the [`MultiKey`] after, which is one of:
- /// - Accepting the char and moving to the next state
- /// - Completing the multi-key pattern
- /// - Rejecting it
- ///
- /// Note that if a [`MultiKey`] only "times out" upon calling this - if it has timed out, it will first reset
- /// before trying to check the char.
- pub fn input(&mut self, c: char) -> MultiKeyResult {
- match &mut self.state {
- MultiKeyState::Idle => {
- if let Some(first) = self.pattern.first() {
- if *first == c {
- self.state = MultiKeyState::Waiting {
- trigger_instant: Instant::now(),
- checked_index: 0,
- };
-
- return MultiKeyResult::Accepted;
- }
- }
-
- MultiKeyResult::Rejected
- }
- MultiKeyState::Waiting {
- trigger_instant,
- checked_index,
- } => {
- if trigger_instant.elapsed() > MAX_TIMEOUT {
- // Just reset and recursively call (putting it into Idle).
- self.reset();
- self.input(c)
- } else if let Some(next) = self.pattern.get(*checked_index + 1) {
- if *next == c {
- *checked_index += 1;
-
- if *checked_index == self.pattern.len() - 1 {
- self.reset();
- MultiKeyResult::Completed
- } else {
- MultiKeyResult::Accepted
- }
- } else {
- self.reset();
- MultiKeyResult::Rejected
- }
- } else {
- self.reset();
- MultiKeyResult::Rejected
- }
- }
- }
- }
-}
diff --git a/src/app/event/multi_key.rs b/src/app/event/multi_key.rs
new file mode 100644
index 00000000..1ab2c1b7
--- /dev/null
+++ b/src/app/event/multi_key.rs
@@ -0,0 +1,101 @@
+use std::time::{Duration, Instant};
+
+const MAX_TIMEOUT: Duration = Duration::from_millis(400);
+
+/// The states a [`MultiKey`] can be in.
+enum MultiKeyState {
+ /// Currently not waiting on any next input.
+ Idle,
+ /// Waiting for the next input, with a given trigger [`Instant`].
+ Waiting {
+ /// When it was triggered.
+ trigger_instant: Instant,
+
+ /// What part of the pattern it is at.
+ checked_index: usize,
+ },
+}
+
+/// The possible outcomes of calling [`MultiKey::input`].
+pub enum MultiKeyResult {
+ /// Returned when a character was *accepted*, but has not completed the sequence required.
+ Accepted,
+ /// Returned when a character is accepted and completes the sequence.
+ Completed,
+ /// Returned if a character breaks the sequence or if it has already timed out.
+ Rejected,
+}
+
+/// A struct useful for managing multi-key keybinds.
+pub struct MultiKey {
+ state: MultiKeyState,
+ pattern: Vec,
+}
+
+impl MultiKey {
+ /// Creates a new [`MultiKey`] with a given pattern and timeout.
+ pub fn register(pattern: Vec) -> Self {
+ Self {
+ state: MultiKeyState::Idle,
+ pattern,
+ }
+ }
+
+ /// Resets to an idle state.
+ fn reset(&mut self) {
+ self.state = MultiKeyState::Idle;
+ }
+
+ /// Handles a char input and returns the current status of the [`MultiKey`] after, which is one of:
+ /// - Accepting the char and moving to the next state
+ /// - Completing the multi-key pattern
+ /// - Rejecting it
+ ///
+ /// Note that if a [`MultiKey`] only "times out" upon calling this - if it has timed out, it will first reset
+ /// before trying to check the char.
+ pub fn input(&mut self, c: char) -> MultiKeyResult {
+ match &mut self.state {
+ MultiKeyState::Idle => {
+ if let Some(first) = self.pattern.first() {
+ if *first == c {
+ self.state = MultiKeyState::Waiting {
+ trigger_instant: Instant::now(),
+ checked_index: 0,
+ };
+
+ return MultiKeyResult::Accepted;
+ }
+ }
+
+ MultiKeyResult::Rejected
+ }
+ MultiKeyState::Waiting {
+ trigger_instant,
+ checked_index,
+ } => {
+ if trigger_instant.elapsed() > MAX_TIMEOUT {
+ // Just reset and recursively call (putting it into Idle).
+ self.reset();
+ self.input(c)
+ } else if let Some(next) = self.pattern.get(*checked_index + 1) {
+ if *next == c {
+ *checked_index += 1;
+
+ if *checked_index == self.pattern.len() - 1 {
+ self.reset();
+ MultiKeyResult::Completed
+ } else {
+ MultiKeyResult::Accepted
+ }
+ } else {
+ self.reset();
+ MultiKeyResult::Rejected
+ }
+ } else {
+ self.reset();
+ MultiKeyResult::Rejected
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs
index 61d17924..9f2c287b 100644
--- a/src/app/layout_manager.rs
+++ b/src/app/layout_manager.rs
@@ -1511,7 +1511,6 @@ pub fn move_widget_selection(
}
LayoutNode::Widget(_) => {
// Halt!
- // TODO: How does this handle carousel?
current_id
}
}
diff --git a/src/app/widgets.rs b/src/app/widgets.rs
index ffc08cd6..f49a3890 100644
--- a/src/app/widgets.rs
+++ b/src/app/widgets.rs
@@ -7,11 +7,10 @@ use tui::{backend::Backend, layout::Rect, widgets::TableState, Frame};
use crate::{
app::{
- event::{SelectionAction, WidgetEventResult},
+ event::{ComponentEventResult, SelectionAction},
layout_manager::BottomWidgetType,
},
canvas::Painter,
- constants,
options::layout_options::LayoutRule,
};
@@ -20,12 +19,15 @@ mod tui_stuff;
pub mod base;
pub use base::*;
+pub mod dialogs;
+pub use dialogs::*;
+
pub mod bottom_widgets;
pub use bottom_widgets::*;
use self::tui_stuff::BlockBuilder;
-use super::data_farmer::DataCollection;
+use super::{data_farmer::DataCollection, event::EventResult};
/// A trait for things that are drawn with state.
#[enum_dispatch]
@@ -33,16 +35,16 @@ use super::data_farmer::DataCollection;
pub trait Component {
/// Handles a [`KeyEvent`].
///
- /// Defaults to returning [`EventResult::NoRedraw`], indicating nothing should be done.
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
- WidgetEventResult::NoRedraw
+ /// Defaults to returning [`ComponentEventResult::Unhandled`], indicating the component does not handle this event.
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
+ ComponentEventResult::Unhandled
}
/// Handles a [`MouseEvent`].
///
- /// Defaults to returning [`EventResult::Continue`], indicating nothing should be done.
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
- WidgetEventResult::NoRedraw
+ /// Defaults to returning [`ComponentEventResult::Unhandled`], indicating the component does not handle this event.
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
+ ComponentEventResult::Unhandled
}
/// Returns a [`Component`]'s bounding box. Note that these are defined in *global*, *absolute*
@@ -180,6 +182,41 @@ pub enum TmpBottomWidget {
Empty,
}
+/// The states a dialog can be in. Consists of either:
+/// - [`DialogState::Hidden`] - the dialog is currently not showing.
+/// - [`DialogState::Shown`] - the dialog is showing.
+#[derive(Debug)]
+pub enum DialogState {
+ Hidden,
+ Shown(D),
+}
+
+impl Default for DialogState
+where
+ D: Default + Component,
+{
+ fn default() -> Self {
+ DialogState::Hidden
+ }
+}
+
+impl DialogState
+where
+ D: Default + Component,
+{
+ pub fn is_showing(&self) -> bool {
+ matches!(self, DialogState::Shown(_))
+ }
+
+ pub fn hide(&mut self) {
+ *self = DialogState::Hidden;
+ }
+
+ pub fn show(&mut self) {
+ *self = DialogState::Shown(D::default());
+ }
+}
+
// ----- Old stuff below -----
#[derive(Debug)]
@@ -250,7 +287,27 @@ impl Default for AppHelpDialogState {
AppHelpDialogState {
is_showing_help: false,
scroll_state: ParagraphScrollState::default(),
- index_shortcuts: vec![0; constants::HELP_TEXT.len()],
+ index_shortcuts: vec![],
+ }
+ }
+}
+
+impl AppHelpDialogState {
+ pub fn increment(&mut self) -> EventResult {
+ if self.scroll_state.current_scroll_index < self.scroll_state.max_scroll_index {
+ self.scroll_state.current_scroll_index += 1;
+ EventResult::Redraw
+ } else {
+ EventResult::NoRedraw
+ }
+ }
+
+ pub fn decrement(&mut self) -> EventResult {
+ if self.scroll_state.current_scroll_index > 0 {
+ self.scroll_state.current_scroll_index -= 1;
+ EventResult::Redraw
+ } else {
+ EventResult::NoRedraw
}
}
}
diff --git a/src/app/widgets/base/scrollable.rs b/src/app/widgets/base/scrollable.rs
index ba4faa5f..b8652a9f 100644
--- a/src/app/widgets/base/scrollable.rs
+++ b/src/app/widgets/base/scrollable.rs
@@ -4,7 +4,7 @@ use crossterm::event::{KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEve
use tui::{layout::Rect, widgets::TableState};
use crate::app::{
- event::{MultiKey, MultiKeyResult, WidgetEventResult},
+ event::{ComponentEventResult, MultiKey, MultiKeyResult},
Component,
};
@@ -138,54 +138,54 @@ impl Scrollable {
}
}
- fn skip_to_first(&mut self) -> WidgetEventResult {
+ fn skip_to_first(&mut self) -> ComponentEventResult {
if self.current_index != 0 {
self.set_index(0);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
}
- fn skip_to_last(&mut self) -> WidgetEventResult {
+ fn skip_to_last(&mut self) -> ComponentEventResult {
let last_index = self.num_items - 1;
if self.current_index != last_index {
self.set_index(last_index);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
}
/// Moves *downward* by *incrementing* the current index.
- fn move_down(&mut self, change_by: usize) -> WidgetEventResult {
+ fn move_down(&mut self, change_by: usize) -> ComponentEventResult {
if self.num_items == 0 {
- return WidgetEventResult::NoRedraw;
+ return ComponentEventResult::NoRedraw;
}
let new_index = self.current_index + change_by;
if new_index >= self.num_items || self.current_index == new_index {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
self.set_index(new_index);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
/// Moves *upward* by *decrementing* the current index.
- fn move_up(&mut self, change_by: usize) -> WidgetEventResult {
+ fn move_up(&mut self, change_by: usize) -> ComponentEventResult {
if self.num_items == 0 {
- return WidgetEventResult::NoRedraw;
+ return ComponentEventResult::NoRedraw;
}
let new_index = self.current_index.saturating_sub(change_by);
if self.current_index == new_index {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
self.set_index(new_index);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
@@ -207,7 +207,7 @@ impl Scrollable {
}
impl Component for Scrollable {
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
use crossterm::event::KeyCode::{Char, Down, Up};
if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT {
@@ -218,18 +218,19 @@ impl Component for Scrollable {
Char('k') => self.move_up(1),
Char('g') => match self.gg_manager.input('g') {
MultiKeyResult::Completed => self.skip_to_first(),
- MultiKeyResult::Accepted => WidgetEventResult::NoRedraw,
- MultiKeyResult::Rejected => WidgetEventResult::NoRedraw,
+ MultiKeyResult::Accepted | MultiKeyResult::Rejected => {
+ ComponentEventResult::NoRedraw
+ }
},
Char('G') => self.skip_to_last(),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
match event.kind {
MouseEventKind::Down(MouseButton::Left) => {
if self.does_bounds_intersect_mouse(&event) {
@@ -256,11 +257,11 @@ impl Component for Scrollable {
}
}
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
MouseEventKind::ScrollDown => self.move_down(1),
MouseEventKind::ScrollUp => self.move_up(1),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
}
diff --git a/src/app/widgets/base/sort_menu.rs b/src/app/widgets/base/sort_menu.rs
index b893a418..548bbf77 100644
--- a/src/app/widgets/base/sort_menu.rs
+++ b/src/app/widgets/base/sort_menu.rs
@@ -3,7 +3,7 @@ use tui::{backend::Backend, layout::Rect, Frame};
use crate::{
app::{
- event::WidgetEventResult, text_table::SimpleColumn, widgets::tui_stuff::BlockBuilder,
+ event::ComponentEventResult, text_table::SimpleColumn, widgets::tui_stuff::BlockBuilder,
Component, TextTable,
},
canvas::Painter,
@@ -69,11 +69,11 @@ impl Component for SortMenu {
self.bounds = new_bounds;
}
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
self.table.handle_key_event(event)
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
self.table.handle_mouse_event(event)
}
}
diff --git a/src/app/widgets/base/sort_text_table.rs b/src/app/widgets/base/sort_text_table.rs
index 64766041..f1f3b0e4 100644
--- a/src/app/widgets/base/sort_text_table.rs
+++ b/src/app/widgets/base/sort_text_table.rs
@@ -5,7 +5,7 @@ use tui::{backend::Backend, layout::Rect, Frame};
use crate::{
app::{
- event::{ReturnSignal, WidgetEventResult},
+ event::{ReturnSignal, ComponentEventResult},
widgets::tui_stuff::BlockBuilder,
Component, TextTable,
},
@@ -391,12 +391,12 @@ impl Component for SortableTextTable
where
S: SortableColumn,
{
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
for (index, column) in self.table.columns.iter().enumerate() {
if let Some((shortcut, _)) = *column.shortcut() {
if shortcut == event {
self.set_sort_index(index);
- return WidgetEventResult::Signal(ReturnSignal::Update);
+ return ComponentEventResult::Signal(ReturnSignal::Update);
}
}
}
@@ -404,10 +404,10 @@ where
self.table.scrollable.handle_key_event(event)
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
if let MouseEventKind::Down(MouseButton::Left) = event.kind {
if !self.does_bounds_intersect_mouse(&event) {
- return WidgetEventResult::NoRedraw;
+ return ComponentEventResult::NoRedraw;
}
// Note these are representing RELATIVE coordinates! They *need* the above intersection check for validity!
@@ -419,7 +419,7 @@ where
if let Some((start, end)) = column.get_x_bounds() {
if x >= start && x <= end {
self.set_sort_index(index);
- return WidgetEventResult::Signal(ReturnSignal::Update);
+ return ComponentEventResult::Signal(ReturnSignal::Update);
}
}
}
diff --git a/src/app/widgets/base/text_input.rs b/src/app/widgets/base/text_input.rs
index f3480c0e..47a5cb7b 100644
--- a/src/app/widgets/base/text_input.rs
+++ b/src/app/widgets/base/text_input.rs
@@ -13,8 +13,8 @@ use unicode_width::UnicodeWidthStr;
use crate::{
app::{
event::{
+ ComponentEventResult::{self},
ReturnSignal,
- WidgetEventResult::{self},
},
Component,
},
@@ -85,19 +85,19 @@ impl TextInput {
}
}
- fn clear_text(&mut self) -> WidgetEventResult {
+ fn clear_text(&mut self) -> ComponentEventResult {
if self.text.is_empty() {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
self.text = String::default();
self.cursor = GraphemeCursor::new(0, 0, true);
self.window_index = Default::default();
self.cursor_direction = CursorDirection::Left;
- WidgetEventResult::Signal(ReturnSignal::Update)
+ ComponentEventResult::Signal(ReturnSignal::Update)
}
}
- fn move_word_forward(&mut self) -> WidgetEventResult {
+ fn move_word_forward(&mut self) -> ComponentEventResult {
let current_index = self.cursor.cur_cursor();
if current_index < self.text.len() {
@@ -105,30 +105,30 @@ impl TextInput {
if index > 0 {
self.cursor.set_cursor(index + current_index);
self.cursor_direction = CursorDirection::Right;
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
}
}
self.cursor.set_cursor(self.text.len());
}
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
- fn move_word_back(&mut self) -> WidgetEventResult {
+ fn move_word_back(&mut self) -> ComponentEventResult {
let current_index = self.cursor.cur_cursor();
for (index, _word) in self.text[..current_index].unicode_word_indices().rev() {
if index < current_index {
self.cursor.set_cursor(index);
self.cursor_direction = CursorDirection::Left;
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
}
}
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
- fn clear_word_from_cursor(&mut self) -> WidgetEventResult {
+ fn clear_word_from_cursor(&mut self) -> ComponentEventResult {
// Fairly simple logic - create the word index iterator, skip the word that intersects with the current
// cursor location, draw the rest, update the string.
let current_index = self.cursor.cur_cursor();
@@ -147,16 +147,16 @@ impl TextInput {
}
if start_delete_index == current_index {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
self.text.drain(start_delete_index..current_index);
self.cursor = GraphemeCursor::new(start_delete_index, self.text.len(), true);
self.cursor_direction = CursorDirection::Left;
- WidgetEventResult::Signal(ReturnSignal::Update)
+ ComponentEventResult::Signal(ReturnSignal::Update)
}
}
- fn clear_previous_grapheme(&mut self) -> WidgetEventResult {
+ fn clear_previous_grapheme(&mut self) -> ComponentEventResult {
let current_index = self.cursor.cur_cursor();
if current_index > 0 {
@@ -166,13 +166,13 @@ impl TextInput {
self.cursor = GraphemeCursor::new(new_index, self.text.len(), true);
self.cursor_direction = CursorDirection::Left;
- WidgetEventResult::Signal(ReturnSignal::Update)
+ ComponentEventResult::Signal(ReturnSignal::Update)
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
}
- fn clear_current_grapheme(&mut self) -> WidgetEventResult {
+ fn clear_current_grapheme(&mut self) -> ComponentEventResult {
let current_index = self.cursor.cur_cursor();
if current_index < self.text.len() {
@@ -182,19 +182,19 @@ impl TextInput {
self.cursor = GraphemeCursor::new(current_index, self.text.len(), true);
self.cursor_direction = CursorDirection::Left;
- WidgetEventResult::Signal(ReturnSignal::Update)
+ ComponentEventResult::Signal(ReturnSignal::Update)
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
}
- fn insert_character(&mut self, c: char) -> WidgetEventResult {
+ fn insert_character(&mut self, c: char) -> ComponentEventResult {
let current_index = self.cursor.cur_cursor();
self.text.insert(current_index, c);
self.cursor = GraphemeCursor::new(current_index, self.text.len(), true);
self.move_forward();
- WidgetEventResult::Signal(ReturnSignal::Update)
+ ComponentEventResult::Signal(ReturnSignal::Update)
}
/// Updates the window indexes and returns the start index.
@@ -296,29 +296,29 @@ impl Component for TextInput {
self.bounds = new_bounds;
}
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
if event.modifiers.is_empty() {
match event.code {
KeyCode::Left => {
let original_cursor = self.cursor.cur_cursor();
if self.move_back() == original_cursor {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
KeyCode::Right => {
let original_cursor = self.cursor.cur_cursor();
if self.move_forward() == original_cursor {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
KeyCode::Backspace => self.clear_previous_grapheme(),
KeyCode::Delete => self.clear_current_grapheme(),
KeyCode::Char(c) => self.insert_character(c),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
} else if let KeyModifiers::CONTROL = event.modifiers {
match event.code {
@@ -326,46 +326,46 @@ impl Component for TextInput {
let prev_index = self.cursor.cur_cursor();
self.cursor.set_cursor(0);
if self.cursor.cur_cursor() == prev_index {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
KeyCode::Char('e') => {
let prev_index = self.cursor.cur_cursor();
self.cursor.set_cursor(self.text.len());
if self.cursor.cur_cursor() == prev_index {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
KeyCode::Char('u') => self.clear_text(),
KeyCode::Char('w') => self.clear_word_from_cursor(),
KeyCode::Char('h') => self.clear_previous_grapheme(),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
} else if let KeyModifiers::ALT = event.modifiers {
match event.code {
KeyCode::Char('b') => self.move_word_back(),
KeyCode::Char('f') => self.move_word_forward(),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
// We are assuming this is within bounds...
let x = event.column;
let widget_x = self.bounds.x + 2;
if x >= widget_x {
- // TODO: do this
- WidgetEventResult::Redraw
+ // TODO: Do this at some point after refactor
+ ComponentEventResult::Redraw
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
}
}
diff --git a/src/app/widgets/base/text_table.rs b/src/app/widgets/base/text_table.rs
index 0814555c..e6d42001 100644
--- a/src/app/widgets/base/text_table.rs
+++ b/src/app/widgets/base/text_table.rs
@@ -15,7 +15,7 @@ use tui::{
use unicode_segmentation::UnicodeSegmentation;
use crate::{
- app::{event::WidgetEventResult, widgets::tui_stuff::BlockBuilder, Component, Scrollable},
+ app::{event::ComponentEventResult, widgets::tui_stuff::BlockBuilder, Component, Scrollable},
canvas::Painter,
constants::TABLE_GAP_HEIGHT_LIMIT,
};
@@ -130,7 +130,7 @@ where
pub show_gap: bool,
/// The bounding box of the [`TextTable`].
- pub bounds: Rect, // TODO: Consider moving bounds to something else???
+ pub bounds: Rect, // TODO: Consider moving bounds to something else?
/// The bounds including the border, if there is one.
pub border_bounds: Rect,
@@ -492,19 +492,19 @@ impl Component for TextTable
where
C: TableColumn,
{
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
if self.selectable {
self.scrollable.handle_key_event(event)
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
if self.selectable {
self.scrollable.handle_mouse_event(event)
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
diff --git a/src/app/widgets/base/time_graph.rs b/src/app/widgets/base/time_graph.rs
index 94e5f847..81ca9e15 100644
--- a/src/app/widgets/base/time_graph.rs
+++ b/src/app/widgets/base/time_graph.rs
@@ -15,7 +15,7 @@ use tui::{
use crate::{
app::{
- event::WidgetEventResult,
+ event::ComponentEventResult,
widgets::tui_stuff::{
custom_legend_chart::{Axis, Dataset},
TimeChart,
@@ -160,62 +160,62 @@ impl TimeGraph {
}
/// Handles a char `c`.
- fn handle_char(&mut self, c: char) -> WidgetEventResult {
+ fn handle_char(&mut self, c: char) -> ComponentEventResult {
match c {
'-' => self.zoom_out(),
'+' => self.zoom_in(),
'=' => self.reset_zoom(),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::NoRedraw,
}
}
- fn zoom_in(&mut self) -> WidgetEventResult {
+ fn zoom_in(&mut self) -> ComponentEventResult {
let new_time = self.current_display_time.saturating_sub(self.time_interval);
if self.current_display_time == new_time {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else if new_time >= self.min_duration {
self.current_display_time = new_time;
self.autohide_timer.start_display_timer();
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else if new_time != self.min_duration {
self.current_display_time = self.min_duration;
self.autohide_timer.start_display_timer();
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
}
- fn zoom_out(&mut self) -> WidgetEventResult {
+ fn zoom_out(&mut self) -> ComponentEventResult {
let new_time = self.current_display_time + self.time_interval;
if self.current_display_time == new_time {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else if new_time <= self.max_duration {
self.current_display_time = new_time;
self.autohide_timer.start_display_timer();
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else if new_time != self.max_duration {
self.current_display_time = self.max_duration;
self.autohide_timer.start_display_timer();
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
}
}
- fn reset_zoom(&mut self) -> WidgetEventResult {
+ fn reset_zoom(&mut self) -> ComponentEventResult {
if self.current_display_time == self.default_time_value {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
self.current_display_time = self.default_time_value;
self.autohide_timer.start_display_timer();
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
@@ -307,24 +307,24 @@ impl TimeGraph {
}
impl Component for TimeGraph {
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
use crossterm::event::KeyCode::Char;
if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT {
match event.code {
Char(c) => self.handle_char(c),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
match event.kind {
MouseEventKind::ScrollDown => self.zoom_out(),
MouseEventKind::ScrollUp => self.zoom_in(),
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
}
diff --git a/src/app/widgets/bottom_widgets/basic_mem.rs b/src/app/widgets/bottom_widgets/basic_mem.rs
index d70d0e04..3658ee7c 100644
--- a/src/app/widgets/bottom_widgets/basic_mem.rs
+++ b/src/app/widgets/bottom_widgets/basic_mem.rs
@@ -8,7 +8,8 @@ use tui::{
use crate::{
app::{
- event::WidgetEventResult, widgets::tui_stuff::PipeGauge, Component, DataCollection, Widget,
+ event::ComponentEventResult, widgets::tui_stuff::PipeGauge, Component, DataCollection,
+ Widget,
},
canvas::Painter,
constants::SIDE_BORDERS,
@@ -54,13 +55,13 @@ impl Component for BasicMem {
self.bounds = new_bounds;
}
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
match event.code {
- KeyCode::Char('%') if event.modifiers.is_empty() => {
+ KeyCode::Char('%') => {
self.use_percent = !self.use_percent;
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
}
}
diff --git a/src/app/widgets/bottom_widgets/battery.rs b/src/app/widgets/bottom_widgets/battery.rs
index 1d0c9c8b..8c1d0721 100644
--- a/src/app/widgets/bottom_widgets/battery.rs
+++ b/src/app/widgets/bottom_widgets/battery.rs
@@ -14,7 +14,7 @@ use tui::{
use crate::{
app::{
- data_farmer::DataCollection, does_bound_intersect_coordinate, event::WidgetEventResult,
+ data_farmer::DataCollection, does_bound_intersect_coordinate, event::ComponentEventResult,
widgets::tui_stuff::PipeGauge, Component, Widget,
},
canvas::Painter,
@@ -114,44 +114,44 @@ impl Component for BatteryTable {
self.bounds = new_bounds;
}
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
if event.modifiers.is_empty() {
match event.code {
KeyCode::Left => {
let current_index = self.selected_index;
self.decrement_index();
if current_index == self.selected_index {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
KeyCode::Right => {
let current_index = self.selected_index;
self.increment_index();
if current_index == self.selected_index {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
for (itx, bound) in self.tab_bounds.iter().enumerate() {
if does_bound_intersect_coordinate(event.column, event.row, *bound)
&& itx < self.battery_data.len()
{
self.selected_index = itx;
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
}
}
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
@@ -183,7 +183,7 @@ impl Widget for BatteryTable {
.block()
.selected(selected)
.borders(self.block_border)
- .expanded(expanded)
+ .show_esc(expanded)
.build(painter, area);
let inner_area = block.inner(area);
diff --git a/src/app/widgets/bottom_widgets/carousel.rs b/src/app/widgets/bottom_widgets/carousel.rs
index 9c8356fb..92d3614c 100644
--- a/src/app/widgets/bottom_widgets/carousel.rs
+++ b/src/app/widgets/bottom_widgets/carousel.rs
@@ -12,7 +12,7 @@ use tui::{
use crate::{
app::{
- does_bound_intersect_coordinate, event::WidgetEventResult, Component, SelectableType,
+ does_bound_intersect_coordinate, event::ComponentEventResult, Component, SelectableType,
Widget,
},
canvas::Painter,
@@ -164,7 +164,7 @@ impl Component for Carousel {
self.bounds = new_bounds;
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
match event.kind {
crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left) => {
let x = event.column;
@@ -172,15 +172,15 @@ impl Component for Carousel {
if does_bound_intersect_coordinate(x, y, self.left_button_bounds) {
self.decrement_index();
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else if does_bound_intersect_coordinate(x, y, self.right_button_bounds) {
self.increment_index();
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
}
}
diff --git a/src/app/widgets/bottom_widgets/cpu.rs b/src/app/widgets/bottom_widgets/cpu.rs
index 9397a7e1..6e5e18a5 100644
--- a/src/app/widgets/bottom_widgets/cpu.rs
+++ b/src/app/widgets/bottom_widgets/cpu.rs
@@ -9,7 +9,7 @@ use tui::{
use crate::{
app::{
- event::{SelectionAction, WidgetEventResult},
+ event::{ComponentEventResult, SelectionAction},
text_table::SimpleColumn,
time_graph::TimeGraphData,
AppConfigFields, AppScrollWidgetState, CanvasTableWidthState, Component, DataCollection,
@@ -118,21 +118,21 @@ impl CpuGraph {
}
impl Component for CpuGraph {
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
match self.selected {
CpuGraphSelection::Graph => self.graph.handle_key_event(event),
CpuGraphSelection::Legend => self.legend.handle_key_event(event),
}
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
if self.graph.does_border_intersect_mouse(&event) {
if let CpuGraphSelection::Graph = self.selected {
self.graph.handle_mouse_event(event)
} else {
self.selected = CpuGraphSelection::Graph;
self.graph.handle_mouse_event(event);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
} else if self.legend.does_border_intersect_mouse(&event) {
if let CpuGraphSelection::Legend = self.selected {
@@ -140,10 +140,10 @@ impl Component for CpuGraph {
} else {
self.selected = CpuGraphSelection::Legend;
self.legend.handle_mouse_event(event);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
@@ -193,7 +193,7 @@ impl Widget for CpuGraph {
let legend_block = self
.block()
.selected(selected && matches!(&self.selected, CpuGraphSelection::Legend))
- .expanded(expanded)
+ .show_esc(expanded)
.hide_title(true);
let legend_data = self
@@ -279,7 +279,7 @@ impl Widget for CpuGraph {
let graph_block = self
.block()
.selected(selected && matches!(&self.selected, CpuGraphSelection::Graph))
- .expanded(expanded)
+ .show_esc(expanded)
.build(painter, graph_block_area);
self.graph.draw_tui_chart(
diff --git a/src/app/widgets/bottom_widgets/disk.rs b/src/app/widgets/bottom_widgets/disk.rs
index 1f5383da..74b63f86 100644
--- a/src/app/widgets/bottom_widgets/disk.rs
+++ b/src/app/widgets/bottom_widgets/disk.rs
@@ -5,7 +5,7 @@ use tui::{backend::Backend, layout::Rect, widgets::Borders, Frame};
use crate::{
app::{
- data_farmer::DataCollection, event::WidgetEventResult,
+ data_farmer::DataCollection, event::ComponentEventResult,
sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppScrollWidgetState,
CanvasTableWidthState, Component, TextTable, Widget,
},
@@ -105,11 +105,11 @@ impl DiskTable {
}
impl Component for DiskTable {
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
self.table.handle_key_event(event)
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
self.table.handle_mouse_event(event)
}
@@ -135,7 +135,7 @@ impl Widget for DiskTable {
.block()
.selected(selected)
.borders(self.block_border)
- .expanded(expanded);
+ .show_esc(expanded);
self.table.draw_tui_table(
painter,
diff --git a/src/app/widgets/bottom_widgets/mem.rs b/src/app/widgets/bottom_widgets/mem.rs
index 11880421..465e3ab9 100644
--- a/src/app/widgets/bottom_widgets/mem.rs
+++ b/src/app/widgets/bottom_widgets/mem.rs
@@ -4,7 +4,7 @@ use crossterm::event::{KeyEvent, MouseEvent};
use tui::{backend::Backend, layout::Rect};
use crate::{
- app::{event::WidgetEventResult, time_graph::TimeGraphData, DataCollection},
+ app::{event::ComponentEventResult, time_graph::TimeGraphData, DataCollection},
app::{Component, TimeGraph, Widget},
data_conversion::{convert_mem_data_points, convert_mem_labels, convert_swap_data_points},
options::layout_options::LayoutRule,
@@ -63,11 +63,11 @@ impl MemGraph {
}
impl Component for MemGraph {
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
self.graph.handle_key_event(event)
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
self.graph.handle_mouse_event(event)
}
@@ -92,7 +92,7 @@ impl Widget for MemGraph {
let block = self
.block()
.selected(selected)
- .expanded(expanded)
+ .show_esc(expanded)
.build(painter, area);
let mut chart_data = Vec::with_capacity(2);
diff --git a/src/app/widgets/bottom_widgets/net.rs b/src/app/widgets/bottom_widgets/net.rs
index b02b957f..1449aa6a 100644
--- a/src/app/widgets/bottom_widgets/net.rs
+++ b/src/app/widgets/bottom_widgets/net.rs
@@ -1,5 +1,6 @@
use std::{borrow::Cow, collections::HashMap, time::Instant};
+use crossterm::event::{KeyEvent, MouseEvent};
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
@@ -8,9 +9,9 @@ use tui::{
use crate::{
app::{
- data_farmer::DataCollection, text_table::SimpleColumn, time_graph::TimeGraphData,
- widgets::tui_stuff::BlockBuilder, AppConfigFields, AxisScaling, Component, TextTable,
- TimeGraph, Widget,
+ data_farmer::DataCollection, event::ComponentEventResult, text_table::SimpleColumn,
+ time_graph::TimeGraphData, widgets::tui_stuff::BlockBuilder, AppConfigFields, AxisScaling,
+ Component, TextTable, TimeGraph, Widget,
},
canvas::Painter,
data_conversion::convert_network_data_points,
@@ -497,15 +498,11 @@ impl Component for NetGraph {
self.bounds = new_bounds;
}
- fn handle_key_event(
- &mut self, event: crossterm::event::KeyEvent,
- ) -> crate::app::event::WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
self.graph.handle_key_event(event)
}
- fn handle_mouse_event(
- &mut self, event: crossterm::event::MouseEvent,
- ) -> crate::app::event::WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
self.graph.handle_mouse_event(event)
}
}
@@ -522,7 +519,7 @@ impl Widget for NetGraph {
let block = self
.block()
.selected(selected)
- .expanded(expanded)
+ .show_esc(expanded)
.build(painter, area);
self.set_draw_cache();
@@ -644,15 +641,11 @@ impl Component for OldNetGraph {
self.bounds = new_bounds;
}
- fn handle_key_event(
- &mut self, event: crossterm::event::KeyEvent,
- ) -> crate::app::event::WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
self.net_graph.handle_key_event(event)
}
- fn handle_mouse_event(
- &mut self, event: crossterm::event::MouseEvent,
- ) -> crate::app::event::WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
self.net_graph.handle_mouse_event(event)
}
}
diff --git a/src/app/widgets/bottom_widgets/process.rs b/src/app/widgets/bottom_widgets/process.rs
index eeff0b54..ba1dcf2e 100644
--- a/src/app/widgets/bottom_widgets/process.rs
+++ b/src/app/widgets/bottom_widgets/process.rs
@@ -15,7 +15,7 @@ use tui::{
use crate::{
app::{
data_harvester::processes::ProcessHarvest,
- event::{MultiKey, MultiKeyResult, ReturnSignal, SelectionAction, WidgetEventResult},
+ event::{ComponentEventResult, MultiKey, MultiKeyResult, ReturnSignal, SelectionAction},
query::*,
text_table::DesiredColumnWidth,
widgets::tui_stuff::BlockBuilder,
@@ -869,27 +869,27 @@ impl ProcessManager {
self
}
- fn open_search(&mut self) -> WidgetEventResult {
+ fn open_search(&mut self) -> ComponentEventResult {
if let ProcessManagerSelection::Search = self.selected {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
self.show_search = true;
self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Search;
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
- fn open_sort(&mut self) -> WidgetEventResult {
+ fn open_sort(&mut self) -> ComponentEventResult {
if let ProcessManagerSelection::Sort = self.selected {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::NoRedraw
} else {
self.sort_menu
.set_index(self.process_table.current_sorting_column_index());
self.show_sort = true;
self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Sort;
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
}
@@ -917,7 +917,7 @@ impl ProcessManager {
)
}
- fn toggle_command(&mut self) -> WidgetEventResult {
+ fn toggle_command(&mut self) -> ComponentEventResult {
if self.is_using_command() {
self.process_table
.set_column(ProcessSortColumn::new(ProcessSortType::Name), 1);
@@ -929,7 +929,7 @@ impl ProcessManager {
// Invalidate row cache.
self.process_table.invalidate_cached_columns();
- WidgetEventResult::Signal(ReturnSignal::Update)
+ ComponentEventResult::Signal(ReturnSignal::Update)
}
fn is_grouped(&self) -> bool {
@@ -939,7 +939,7 @@ impl ProcessManager {
)
}
- fn toggle_grouped(&mut self) -> WidgetEventResult {
+ fn toggle_grouped(&mut self) -> ComponentEventResult {
if self.is_grouped() {
self.process_table
.set_column(ProcessSortColumn::new(ProcessSortType::Pid), 0);
@@ -965,7 +965,7 @@ impl ProcessManager {
// Invalidate row cache.
self.process_table.invalidate_cached_columns();
- WidgetEventResult::Signal(ReturnSignal::Update)
+ ComponentEventResult::Signal(ReturnSignal::Update)
}
fn hide_sort(&mut self) {
@@ -994,7 +994,7 @@ impl Component for ProcessManager {
self.bounds = new_bounds;
}
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
// "Global" handling:
if let KeyCode::Esc = event.code {
@@ -1002,19 +1002,19 @@ impl Component for ProcessManager {
ProcessManagerSelection::Processes => {
if self.show_sort {
self.hide_sort();
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
} else if self.show_search {
self.hide_search();
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
}
}
ProcessManagerSelection::Sort if self.show_sort => {
self.hide_sort();
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
}
ProcessManagerSelection::Search if self.show_search => {
self.hide_search();
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
}
_ => {}
}
@@ -1039,7 +1039,7 @@ impl Component for ProcessManager {
// Kill the selected process(es)
}
MultiKeyResult::Accepted | MultiKeyResult::Rejected => {
- return WidgetEventResult::NoRedraw;
+ return ComponentEventResult::NoRedraw;
}
}
}
@@ -1057,7 +1057,7 @@ impl Component for ProcessManager {
}
KeyCode::Char('t') | KeyCode::F(5) => {
self.in_tree_mode = !self.in_tree_mode;
- return WidgetEventResult::Redraw;
+ return ComponentEventResult::Redraw;
}
KeyCode::Char('s') | KeyCode::F(6) => {
return self.open_sort();
@@ -1086,7 +1086,7 @@ impl Component for ProcessManager {
KeyCode::Enter => {
self.process_table
.set_sort_index(self.sort_menu.current_index());
- return WidgetEventResult::Signal(ReturnSignal::Update);
+ return ComponentEventResult::Signal(ReturnSignal::Update);
}
KeyCode::Char('/') => {
return self.open_search();
@@ -1115,7 +1115,7 @@ impl Component for ProcessManager {
}
let handle_output = self.search_input.handle_key_event(event);
- if let WidgetEventResult::Signal(ReturnSignal::Update) = handle_output {
+ if let ComponentEventResult::Signal(ReturnSignal::Update) = handle_output {
self.process_filter = Some(parse_query(
self.search_input.query(),
self.is_searching_whole_word(),
@@ -1129,7 +1129,7 @@ impl Component for ProcessManager {
}
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
match &event.kind {
MouseEventKind::Down(MouseButton::Left) => {
if self.process_table.does_border_intersect_mouse(&event) {
@@ -1139,11 +1139,10 @@ impl Component for ProcessManager {
self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Processes;
match self.process_table.handle_mouse_event(event) {
- WidgetEventResult::Quit => WidgetEventResult::Quit,
- WidgetEventResult::Redraw | WidgetEventResult::NoRedraw => {
- WidgetEventResult::Redraw
- }
- WidgetEventResult::Signal(s) => WidgetEventResult::Signal(s),
+ ComponentEventResult::Unhandled
+ | ComponentEventResult::Redraw
+ | ComponentEventResult::NoRedraw => ComponentEventResult::Redraw,
+ ComponentEventResult::Signal(s) => ComponentEventResult::Signal(s),
}
}
} else if self.sort_menu.does_border_intersect_mouse(&event) {
@@ -1153,7 +1152,7 @@ impl Component for ProcessManager {
self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Sort;
self.sort_menu.handle_mouse_event(event);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
} else if does_bound_intersect_coordinate(
event.column,
@@ -1166,10 +1165,10 @@ impl Component for ProcessManager {
self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Search;
self.search_input.handle_mouse_event(event);
- WidgetEventResult::Redraw
+ ComponentEventResult::Redraw
}
} else {
- WidgetEventResult::NoRedraw
+ ComponentEventResult::Unhandled
}
}
MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => match self.selected {
@@ -1177,7 +1176,7 @@ impl Component for ProcessManager {
ProcessManagerSelection::Sort => self.sort_menu.handle_mouse_event(event),
ProcessManagerSelection::Search => self.search_input.handle_mouse_event(event),
},
- _ => WidgetEventResult::NoRedraw,
+ _ => ComponentEventResult::Unhandled,
}
}
}
@@ -1195,9 +1194,9 @@ impl Widget for ProcessManager {
let search_constraints: [Constraint; 2] = [
Constraint::Min(0),
if self.block_border.contains(Borders::TOP) {
- Constraint::Length(4)
+ Constraint::Length(5)
} else {
- Constraint::Length(2)
+ Constraint::Length(3)
},
];
const INTERNAL_SEARCH_CONSTRAINTS: [Constraint; 2] = [Constraint::Length(1); 2];
@@ -1277,7 +1276,7 @@ impl Widget for ProcessManager {
.block()
.selected(process_selected)
.borders(self.block_border)
- .expanded(expanded && !self.show_sort && !self.show_search);
+ .show_esc(expanded && !self.show_sort && !self.show_search);
self.process_table.draw_tui_table(
painter,
diff --git a/src/app/widgets/bottom_widgets/temp.rs b/src/app/widgets/bottom_widgets/temp.rs
index 9d5ef4f9..be8623f6 100644
--- a/src/app/widgets/bottom_widgets/temp.rs
+++ b/src/app/widgets/bottom_widgets/temp.rs
@@ -6,8 +6,9 @@ use tui::{backend::Backend, layout::Rect, widgets::Borders, Frame};
use crate::{
app::{
data_farmer::DataCollection, data_harvester::temperature::TemperatureType,
- event::WidgetEventResult, sort_text_table::SimpleSortableColumn, text_table::TextTableData,
- AppScrollWidgetState, CanvasTableWidthState, Component, TextTable, Widget,
+ event::ComponentEventResult, sort_text_table::SimpleSortableColumn,
+ text_table::TextTableData, AppScrollWidgetState, CanvasTableWidthState, Component,
+ TextTable, Widget,
},
canvas::Painter,
data_conversion::convert_temp_row,
@@ -116,11 +117,11 @@ impl TempTable {
}
impl Component for TempTable {
- fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
self.table.handle_key_event(event)
}
- fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
self.table.handle_mouse_event(event)
}
@@ -146,7 +147,7 @@ impl Widget for TempTable {
.block()
.selected(selected)
.borders(self.block_border)
- .expanded(expanded);
+ .show_esc(expanded);
self.table.draw_tui_table(
painter,
diff --git a/src/app/widgets/dialogs.rs b/src/app/widgets/dialogs.rs
new file mode 100644
index 00000000..e2dc26bb
--- /dev/null
+++ b/src/app/widgets/dialogs.rs
@@ -0,0 +1,2 @@
+pub mod help;
+pub use help::HelpDialog;
diff --git a/src/app/widgets/dialogs/help.rs b/src/app/widgets/dialogs/help.rs
new file mode 100644
index 00000000..fba8a77c
--- /dev/null
+++ b/src/app/widgets/dialogs/help.rs
@@ -0,0 +1,304 @@
+use std::cmp::min;
+
+use crossterm::event::{KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
+use fxhash::FxHashMap;
+use itertools::{EitherOrBoth, Itertools};
+use tui::{
+ backend::Backend,
+ layout::{Constraint, Layout, Rect},
+ text::{Span, Spans},
+ widgets::{Borders, Paragraph},
+ Frame,
+};
+use unicode_width::UnicodeWidthStr;
+
+use crate::{
+ app::{
+ event::{ComponentEventResult, MultiKey, MultiKeyResult},
+ widgets::tui_stuff::BlockBuilder,
+ Component,
+ },
+ canvas::Painter,
+ constants::*,
+};
+
+pub struct HelpDialog {
+ current_index: usize,
+ max_index: usize,
+ bounds: Rect,
+ wrapped_text: Vec>>,
+ left_column_width: Constraint,
+ right_column_width: Constraint,
+
+ /// Manages the `gg` double-tap shortcut.
+ gg_manager: MultiKey,
+
+ /// A jury-rigged solution for shortcut indices.
+ /// TODO: THIS DOES NOT SCALE WELL!
+ shortcut_indices: FxHashMap,
+}
+
+impl Default for HelpDialog {
+ fn default() -> Self {
+ Self {
+ current_index: Default::default(),
+ max_index: Default::default(),
+ bounds: Default::default(),
+ left_column_width: Constraint::Length(0),
+ right_column_width: Constraint::Length(0),
+ wrapped_text: Default::default(),
+ gg_manager: MultiKey::register(vec!['g', 'g']),
+ shortcut_indices: FxHashMap::default(),
+ }
+ }
+}
+
+impl HelpDialog {
+ pub fn rebuild_wrapped_text(&mut self, painter: &Painter) {
+ let left_column_width = HELP_TEXT
+ .iter()
+ .map(|text| {
+ text.iter()
+ .map(|[labels, _details]| {
+ labels
+ .lines()
+ .map(|line| UnicodeWidthStr::width(line))
+ .max()
+ .unwrap_or(0)
+ })
+ .max()
+ .unwrap_or(0)
+ })
+ .max()
+ .unwrap_or(0)
+ + 2;
+ let right_column_width = (self.bounds.width as usize).saturating_sub(left_column_width);
+
+ self.left_column_width = Constraint::Length(left_column_width as u16);
+ self.right_column_width = Constraint::Length(right_column_width as u16);
+
+ let mut shortcut_index = 1;
+ let mut current_index = HELP_TITLES.len() + 2;
+
+ // let mut section_indices: Vec = Vec::with_capacity(HELP_TITLES.len());
+ // let mut help_title_index = HELP_CONTENTS_TEXT.len() + 1;
+ // Behold, this monstrosity of an iterator (I'm sorry).
+ // Be warned, for when you stare into the iterator, the iterator stares back.
+
+ let wrapped_details_iter = HELP_TEXT.iter().map(|text| {
+ text.iter()
+ .map(|[labels, details]| {
+ let labels = textwrap::fill(labels, left_column_width);
+ let details = textwrap::fill(details, right_column_width);
+
+ labels
+ .lines()
+ .zip_longest(details.lines())
+ .map(|z| match z {
+ EitherOrBoth::Both(a, b) => vec![
+ Spans::from(Span::styled(
+ a.to_string(),
+ painter.colours.text_style,
+ )),
+ Spans::from(Span::styled(
+ b.to_string(),
+ painter.colours.text_style,
+ )),
+ ],
+ EitherOrBoth::Left(s) => {
+ vec![Spans::from(Span::styled(
+ s.to_string(),
+ painter.colours.text_style,
+ ))]
+ }
+ EitherOrBoth::Right(s) => vec![
+ Spans::default(),
+ Spans::from(Span::styled(
+ s.to_string(),
+ painter.colours.text_style,
+ )),
+ ],
+ })
+ .collect::>()
+ })
+ .flatten()
+ .collect::>()
+ });
+
+ self.wrapped_text = HELP_CONTENTS_TEXT
+ .iter()
+ .map(|t| vec![Spans::from(Span::styled(*t, painter.colours.text_style))])
+ .chain(
+ HELP_TITLES
+ .iter()
+ .zip(wrapped_details_iter)
+ .map(|(title, text)| {
+ self.shortcut_indices.insert(shortcut_index, current_index);
+ shortcut_index += 1;
+ current_index += 2 + text.len();
+ std::iter::once(vec![Spans::default()])
+ .chain(std::iter::once(vec![Spans::from(Span::styled(
+ *title,
+ painter.colours.highlighted_border_style,
+ ))]))
+ .chain(text)
+ })
+ .flatten(),
+ )
+ .collect();
+
+ self.max_index = self
+ .wrapped_text
+ .len()
+ .saturating_sub(self.bounds.height as usize);
+
+ for value in self.shortcut_indices.values_mut() {
+ *value = min(*value, self.max_index);
+ }
+
+ if self.current_index > self.max_index {
+ self.current_index = self.max_index;
+ }
+ }
+
+ pub fn draw_help(
+ &mut self, painter: &Painter, f: &mut Frame<'_, B>, block_area: Rect,
+ ) {
+ let block = BlockBuilder::new("Help")
+ .borders(Borders::all())
+ .show_esc(true)
+ .build(painter, block_area);
+
+ let inner_area = block.inner(block_area);
+ if inner_area != self.bounds {
+ self.bounds = inner_area;
+ self.rebuild_wrapped_text(painter);
+ }
+ let end_index = self.current_index + inner_area.height as usize;
+
+ let split_area = Layout::default()
+ .constraints(vec![Constraint::Length(1); inner_area.height as usize])
+ .direction(tui::layout::Direction::Vertical)
+ .split(inner_area);
+
+ self.wrapped_text[self.current_index..end_index]
+ .iter()
+ .zip(split_area)
+ .for_each(|(row, area)| {
+ if row.len() > 1 {
+ let row_split_area = Layout::default()
+ .constraints(vec![self.left_column_width, self.right_column_width])
+ .direction(tui::layout::Direction::Horizontal)
+ .split(area);
+
+ let left_area = row_split_area[0];
+ let right_area = row_split_area[1];
+
+ f.render_widget(Paragraph::new(row[0].clone()), left_area);
+ f.render_widget(Paragraph::new(row[1].clone()), right_area);
+ } else if let Some(line) = row.get(0) {
+ f.render_widget(Paragraph::new(line.clone()), area);
+ }
+ });
+
+ f.render_widget(block, block_area);
+ }
+
+ fn move_up(&mut self, amount: usize) -> ComponentEventResult {
+ let new_index = self.current_index.saturating_sub(amount);
+ if self.current_index == new_index {
+ ComponentEventResult::NoRedraw
+ } else {
+ self.current_index = new_index;
+ ComponentEventResult::Redraw
+ }
+ }
+
+ fn move_down(&mut self, amount: usize) -> ComponentEventResult {
+ let new_index = self.current_index + amount;
+ if new_index > self.max_index || self.current_index == new_index {
+ ComponentEventResult::NoRedraw
+ } else {
+ self.current_index = new_index;
+ ComponentEventResult::Redraw
+ }
+ }
+
+ fn skip_to_first(&mut self) -> ComponentEventResult {
+ if self.current_index == 0 {
+ ComponentEventResult::NoRedraw
+ } else {
+ self.current_index = 0;
+ ComponentEventResult::Redraw
+ }
+ }
+
+ fn skip_to_last(&mut self) -> ComponentEventResult {
+ if self.current_index == self.max_index {
+ ComponentEventResult::NoRedraw
+ } else {
+ self.current_index = self.max_index;
+ ComponentEventResult::Redraw
+ }
+ }
+}
+
+impl Component for HelpDialog {
+ fn bounds(&self) -> Rect {
+ self.bounds
+ }
+
+ fn set_bounds(&mut self, new_bounds: Rect) {
+ self.bounds = new_bounds;
+ }
+
+ fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
+ use crossterm::event::KeyCode::{Char, Down, Up};
+
+ if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT {
+ match event.code {
+ Down if event.modifiers == KeyModifiers::NONE => self.move_down(1),
+ Up if event.modifiers == KeyModifiers::NONE => self.move_up(1),
+ Char(c) => match c {
+ 'j' => self.move_down(1),
+ 'k' => self.move_up(1),
+ 'g' => match self.gg_manager.input('g') {
+ MultiKeyResult::Completed => self.skip_to_first(),
+ MultiKeyResult::Accepted | MultiKeyResult::Rejected => {
+ ComponentEventResult::NoRedraw
+ }
+ },
+ 'G' => self.skip_to_last(),
+ '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
+ if let Some(potential_index) = c.to_digit(10) {
+ if let Some(&new_index) = self.shortcut_indices.get(&potential_index) {
+ if new_index != self.current_index {
+ self.current_index = new_index;
+ ComponentEventResult::Redraw
+ } else {
+ ComponentEventResult::NoRedraw
+ }
+ } else {
+ ComponentEventResult::Unhandled
+ }
+ } else {
+ ComponentEventResult::Unhandled
+ }
+ }
+ _ => ComponentEventResult::Unhandled,
+ },
+ _ => ComponentEventResult::Unhandled,
+ }
+ } else {
+ ComponentEventResult::Unhandled
+ }
+ }
+
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
+ match event.kind {
+ MouseEventKind::ScrollDown => self.move_down(1),
+ MouseEventKind::ScrollUp => self.move_up(1),
+ _ => ComponentEventResult::Unhandled,
+ }
+ }
+}
diff --git a/src/app/widgets/tui_stuff/block_builder.rs b/src/app/widgets/tui_stuff/block_builder.rs
index 6f9801c8..cec835e3 100644
--- a/src/app/widgets/tui_stuff/block_builder.rs
+++ b/src/app/widgets/tui_stuff/block_builder.rs
@@ -10,7 +10,7 @@ use crate::canvas::Painter;
pub struct BlockBuilder {
borders: Borders,
selected: bool,
- expanded: bool,
+ show_esc: bool,
name: &'static str,
hide_title: bool,
extra_text: Option,
@@ -22,7 +22,7 @@ impl BlockBuilder {
Self {
borders: Borders::ALL,
selected: false,
- expanded: false,
+ show_esc: false,
name,
hide_title: false,
extra_text: None,
@@ -35,9 +35,9 @@ impl BlockBuilder {
self
}
- /// Indicates that this block is currently expanded, and should be drawn as such.
- pub fn expanded(mut self, expanded: bool) -> Self {
- self.expanded = expanded;
+ /// Indicates that this block should show esc, and should be drawn as such.
+ pub fn show_esc(mut self, show_esc: bool) -> Self {
+ self.show_esc = show_esc;
self
}
@@ -64,12 +64,14 @@ impl BlockBuilder {
let has_title = !self.hide_title
&& (self.borders.contains(Borders::TOP) || self.borders.contains(Borders::BOTTOM));
+ let border_style = if self.selected {
+ painter.colours.highlighted_border_style
+ } else {
+ painter.colours.border_style
+ };
+
let block = Block::default()
- .border_style(if self.selected {
- painter.colours.highlighted_border_style
- } else {
- painter.colours.border_style
- })
+ .border_style(border_style)
.borders(self.borders);
let inner_width = block.inner(area).width as usize;
@@ -82,12 +84,11 @@ impl BlockBuilder {
let mut title_len = name.width();
let mut title = vec![name, Span::from(""), Span::from(""), Span::from("")];
- if self.expanded {
+ if self.show_esc {
const EXPAND_TEXT: &str = " Esc to go back ";
const EXPAND_TEXT_LEN: usize = EXPAND_TEXT.len();
- let expand_span =
- Span::styled(EXPAND_TEXT, painter.colours.highlighted_border_style);
+ let expand_span = Span::styled(EXPAND_TEXT, border_style);
if title_len + EXPAND_TEXT_LEN <= inner_width {
title_len += EXPAND_TEXT_LEN;
@@ -107,16 +108,9 @@ impl BlockBuilder {
}
}
- if self.expanded {
+ if self.show_esc {
let difference = inner_width.saturating_sub(title_len);
- title[2] = Span::styled(
- "─".repeat(difference),
- if self.selected {
- painter.colours.highlighted_border_style
- } else {
- painter.colours.border_style
- },
- );
+ title[2] = Span::styled("─".repeat(difference), border_style);
}
block.title(title)
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 358b95be..84b88d0b 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -50,7 +50,7 @@ fn main() -> Result<()> {
let thread_termination_cvar = Arc::new(Condvar::new());
// Set up input handling
- let (sender, receiver) = mpsc::channel(); // FIXME: Make this bounded, prevents overloading.
+ let (sender, receiver) = mpsc::channel();
let input_thread = create_input_thread(sender.clone(), thread_termination_lock.clone());
// Cleaning loop
diff --git a/src/canvas.rs b/src/canvas.rs
index 0c91978d..a494f898 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -5,7 +5,7 @@ use indextree::{Arena, NodeId};
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
- text::{Span, Spans},
+ text::Span,
widgets::Paragraph,
Frame, Terminal,
};
@@ -19,7 +19,7 @@ use crate::{
layout_manager::{generate_layout, ColLayout, LayoutNode, RowLayout},
text_table::TextTableData,
widgets::{Component, Widget},
- TmpBottomWidget,
+ DialogState, TmpBottomWidget,
},
constants::*,
data_conversion::{ConvertedBatteryData, ConvertedCpuData, ConvertedProcessData},
@@ -92,14 +92,12 @@ impl FromStr for ColourScheme {
/// Handles the canvas' state.
pub struct Painter {
pub colours: CanvasColours,
- styled_help_text: Vec>,
}
impl Painter {
pub fn init(config: &Config, colour_scheme: ColourScheme) -> anyhow::Result {
let mut painter = Painter {
colours: CanvasColours::default(),
- styled_help_text: Vec::default(),
};
if let ColourScheme::Custom = colour_scheme {
@@ -107,7 +105,6 @@ impl Painter {
} else {
painter.generate_colour_scheme(colour_scheme)?;
}
- painter.complete_painter_init();
Ok(painter)
}
@@ -153,43 +150,6 @@ impl Painter {
Ok(())
}
- /// Must be run once before drawing, but after setting colours.
- /// This is to set some remaining styles and text.
- fn complete_painter_init(&mut self) {
- let mut styled_help_spans = Vec::new();
-
- // Init help text:
- (*HELP_TEXT).iter().enumerate().for_each(|(itx, section)| {
- if itx == 0 {
- styled_help_spans.extend(
- section
- .iter()
- .map(|&text| Span::styled(text, self.colours.text_style))
- .collect::>(),
- );
- } 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(Span::raw(""));
- styled_help_spans
- .push(Span::styled(section[0], self.colours.table_header_style));
- styled_help_spans.extend(
- section[1..]
- .iter()
- .map(|&text| Span::styled(text, self.colours.text_style))
- .collect::>(),
- );
- }
- }
- });
-
- self.styled_help_text = styled_help_spans.into_iter().map(Spans::from).collect();
- }
-
- // TODO: [CONFIG] write this, should call painter init and any changed colour functions...
- pub fn update_painter_colours(&mut self) {}
-
fn draw_frozen_indicator(&self, f: &mut Frame<'_, B>, draw_loc: Rect) {
f.render_widget(
Paragraph::new(Span::styled(
@@ -218,7 +178,7 @@ impl Painter {
let terminal_height = draw_area.height;
let terminal_width = draw_area.width;
- if app_state.help_dialog_state.is_showing_help {
+ if let DialogState::Shown(help_dialog) = &mut app_state.help_dialog {
let gen_help_len = GENERAL_HELP_TEXT.len() as u16 + 3;
let border_len = terminal_height.saturating_sub(gen_help_len) / 2;
let vertical_dialog_chunk = Layout::default()
@@ -248,7 +208,7 @@ impl Painter {
})
.split(vertical_dialog_chunk[1]);
- self.draw_help_dialog(&mut f, app_state, middle_dialog_chunk[1]);
+ help_dialog.draw_help(&self, f, middle_dialog_chunk[1]);
} else if app_state.delete_dialog_state.is_showing_dd {
// TODO: This needs the paragraph wrap feature from tui-rs to be pushed to complete... but for now it's pretty close!
// The main problem right now is that I cannot properly calculate the height offset since
diff --git a/src/canvas/dialogs.rs b/src/canvas/dialogs.rs
index 7a2a7e20..af15e931 100644
--- a/src/canvas/dialogs.rs
+++ b/src/canvas/dialogs.rs
@@ -1,5 +1,2 @@
pub mod dd_dialog;
-pub mod help_dialog;
-
pub use dd_dialog::KillDialog;
-pub use help_dialog::HelpDialog;
diff --git a/src/canvas/dialogs/help_dialog.rs b/src/canvas/dialogs/help_dialog.rs
deleted file mode 100644
index 54b47000..00000000
--- a/src/canvas/dialogs/help_dialog.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-use unicode_width::UnicodeWidthStr;
-
-use crate::{app::AppState, canvas::Painter, constants};
-use tui::{
- backend::Backend,
- layout::{Alignment, Rect},
- terminal::Frame,
- text::Span,
- text::Spans,
- widgets::{Block, Borders, Paragraph, Wrap},
-};
-
-const HELP_BASE: &str = " Help ── Esc to close ";
-
-pub trait HelpDialog {
- fn draw_help_dialog(
- &self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
- );
-}
-
-// TODO: [REFACTOR] Make generic dialog boxes to build off of instead?
-impl HelpDialog for Painter {
- fn draw_help_dialog(
- &self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
- ) {
- let help_title = Spans::from(vec![
- Span::styled(" Help ", self.colours.widget_title_style),
- Span::styled(
- format!(
- "─{}─ Esc to close ",
- "─".repeat(
- usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2)
- )
- ),
- self.colours.border_style,
- ),
- ]);
-
- if app_state.should_get_widget_bounds() {
- // We must also recalculate how many lines are wrapping to properly get scrolling to work on
- // small terminal sizes... oh joy.
-
- let mut overflow_buffer = 0;
- let paragraph_width = std::cmp::max(draw_loc.width.saturating_sub(2), 1);
- let mut prev_section_len = 0;
-
- constants::HELP_TEXT
- .iter()
- .enumerate()
- .for_each(|(itx, section)| {
- let mut buffer = 0;
-
- if itx == 0 {
- section.iter().for_each(|text_line| {
- buffer += UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16
- / paragraph_width;
- });
-
- app_state.help_dialog_state.index_shortcuts[itx] = 0;
- } else {
- section.iter().for_each(|text_line| {
- buffer += UnicodeWidthStr::width(*text_line).saturating_sub(1) as u16
- / paragraph_width;
- });
-
- app_state.help_dialog_state.index_shortcuts[itx] =
- app_state.help_dialog_state.index_shortcuts[itx - 1]
- + 1
- + prev_section_len;
- }
- prev_section_len = section.len() as u16 + buffer;
- overflow_buffer += buffer;
- });
-
- app_state.help_dialog_state.scroll_state.max_scroll_index =
- (self.styled_help_text.len() as u16
- + (constants::HELP_TEXT.len() as u16 - 5)
- + 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(self.styled_help_text.clone())
- .block(
- Block::default()
- .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(Wrap { trim: true })
- .scroll((
- app_state
- .help_dialog_state
- .scroll_state
- .current_scroll_index,
- 0,
- )),
- draw_loc,
- );
- }
-}
diff --git a/src/constants.rs b/src/constants.rs
index 636616c7..5db55def 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -227,159 +227,193 @@ pub static NORD_LIGHT_COLOUR_PALETTE: Lazy = Lazy::new(|| ConfigC
// Help text
pub const HELP_CONTENTS_TEXT: [&str; 8] = [
- "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",
+ "Press the corresponding numbers to jump to that section, or just scroll down:",
+ "[1] General",
+ "[2] CPU widget",
+ "[3] Process widget",
+ "[4] Process search widget",
+ "[5] Process sort widget",
+ "[6] Battery widget",
+ "[7] Basic memory widget",
];
-// TODO [Help]: Search in help?
-// TODO [Help]: Move to using tables for easier formatting?
-pub const GENERAL_HELP_TEXT: [&str; 30] = [
- "1 - General",
- "q, Ctrl-c Quit",
- "Esc Close dialog windows, search, widgets, or exit expanded mode",
- "Ctrl-r Resets any collected data",
- "f Toggles freezing, which stops new data from being shown",
- "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",
- "Mouse click Selects the clicked widget, table entry, dialog option, or tab",
+pub const GENERAL_HELP_TITLE: &str = "General";
+pub const GENERAL_HELP_TEXT: [[&str; 2]; 21] = [
+ ["q, Ctrl-c", "Quit"],
+ [
+ "Esc",
+ "Close dialog windows, search, widgets, or exit expanded mode",
+ ],
+ ["Ctrl-r", "Resets any collected data"],
+ ["f", "Toggles freezing, stopping new data from being shown"],
+ ["Ctrl-Left\nShift-Left\nH, A", "Move widget selection left"],
+ [
+ "Ctrl-Right\nShift-Right\nL, D",
+ "Move widget selection right",
+ ],
+ ["Ctrl-Up\nShift-Up\nK, W", "Move widget selection up"],
+ ["Ctrl-Down\nShift-Dow\nJ, S", "Move widget selection down"],
+ ["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",
+ ],
+ [
+ "Mouse click",
+ "Selects the clicked widget, table entry, dialog option, or tab",
+ ],
];
-pub const CPU_HELP_TEXT: [&str; 2] = [
- "2 - CPU widget\n",
- "Mouse scroll Scrolling over an CPU core/average shows only that entry on the chart",
+pub const CPU_HELP_TITLE: &str = "CPU Widget";
+pub const CPU_HELP_TEXT: [[&str; 2]; 1] = [[
+ "Mouse scroll",
+ "Scrolling over an CPU core/average shows only that entry on the chart",
+]];
+
+pub const PROCESS_HELP_TITLE: &str = "Process Widget";
+pub const PROCESS_HELP_TEXT: [[&str; 2]; 14] = [
+ ["dd, F9", "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"],
+ ["+, -, click", "Collapse/expand a branch while in tree mode"],
+ [
+ "click on header",
+ "Sorts the entries by that column, click again to invert the sort",
+ ],
];
-pub const PROCESS_HELP_TEXT: [&str; 15] = [
- "3 - Process widget",
- "dd, F9 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",
- "+, -, click Collapse/expand a branch while in tree mode",
- "click on header Sorts the entries by that column, click again to invert the sort",
+pub const SEARCH_TEXT_HELP_TITLE: &str = "Process Search";
+pub const SEARCH_HELP_TEXT: [[&str; 2]; 48] = [
+ ["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"],
+ ["Ctrl-w", "Delete a word behind the cursor"],
+ ["Ctrl-h", "Delete the character behind the cursor"],
+ ["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"],
+ ["\n", "\n"],
+ ["Supported search types:", ""],
+ ["", "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"],
+ ["user", "ex: user = root"],
+ ["state", "ex: state = running"],
+ ["\n", "\n"],
+ ["Comparison operators:", ""],
+ ["=", "ex: cpu = 1"],
+ [">", "ex: cpu > 1"],
+ ["<", "ex: cpu < 1"],
+ [">=", "ex: cpu >= 1"],
+ ["<=", "ex: cpu <= 1"],
+ ["\n", "\n"],
+ ["Logical operators:", ""],
+ ["and, &&, ", ": btm and cpu > 1 and mem > 1"],
+ ["or, ||", "ex: btm or firefox"],
+ ["\n", "\n"],
+ ["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 SEARCH_HELP_TEXT: [&str; 49] = [
- "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",
- "Ctrl-w Delete a word behind the cursor",
- "Ctrl-h Delete the character behind the cursor",
- "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:",
- " 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",
- "user ex: user = root",
- "state ex: state = running",
- "",
- "Comparison operators:",
- "= ex: cpu = 1",
- "> ex: cpu > 1",
- "< ex: cpu < 1",
- ">= ex: cpu >= 1",
- "<= ex: cpu <= 1",
- "",
- "Logical operators:",
- "and, &&, 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 PROCESS_SORT_HELP_TITLE: &str = "Process Sort";
+pub const PROCESS_SORT_HELP_TEXT: [[&str; 2]; 5] = [
+ ["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 SORT_HELP_TEXT: [&str; 6] = [
- "5 - 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_TITLE: &str = "Battery Widget";
+pub const BATTERY_HELP_TEXT: [[&str; 2]; 2] = [
+ ["Left", "Go to previous battery"],
+ ["Right", "Go to next battery"],
];
-pub const BATTERY_HELP_TEXT: [&str; 3] = [
- "6 - Battery widget",
- "Left Go to previous battery",
- "Right Go to next battery",
-];
+pub const BASIC_MEM_HELP_TITLE: &str = "Basic Memory Widget";
+pub const BASIC_MEM_HELP_TEXT: [[&str; 2]; 1] = [[
+ "%",
+ "Toggle between values and percentages for memory usage",
+]];
-pub const BASIC_MEM_HELP_TEXT: [&str; 2] = [
- "7 - Basic memory widget",
- "% Toggle between values and percentages for memory usage",
-];
-
-pub static HELP_TEXT: Lazy>> = Lazy::new(|| {
- vec![
- HELP_CONTENTS_TEXT.to_vec(),
+pub static HELP_TEXT: Lazy<[Vec<[&'static str; 2]>; 7]> = Lazy::new(|| {
+ [
GENERAL_HELP_TEXT.to_vec(),
CPU_HELP_TEXT.to_vec(),
PROCESS_HELP_TEXT.to_vec(),
SEARCH_HELP_TEXT.to_vec(),
- SORT_HELP_TEXT.to_vec(),
+ PROCESS_SORT_HELP_TEXT.to_vec(),
BATTERY_HELP_TEXT.to_vec(),
BASIC_MEM_HELP_TEXT.to_vec(),
]
});
+pub const HELP_TITLES: [&'static str; 7] = [
+ GENERAL_HELP_TITLE,
+ CPU_HELP_TITLE,
+ PROCESS_HELP_TITLE,
+ SEARCH_TEXT_HELP_TITLE,
+ PROCESS_SORT_HELP_TITLE,
+ BATTERY_HELP_TITLE,
+ BASIC_MEM_HELP_TITLE,
+];
+
// Default layouts
pub const DEFAULT_LAYOUT: &str = r##"
[[row]]
diff --git a/src/lib.rs b/src/lib.rs
index 05718df6..cf963426 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -98,7 +98,7 @@ pub fn handle_key_event(
KeyCode::Left => app.on_left_key(),
KeyCode::Right => app.on_right_key(),
KeyCode::Char(caught_char) => app.on_char_key(caught_char),
- KeyCode::Esc => app.on_esc(),
+ // KeyCode::Esc => app.on_esc(),
KeyCode::Enter => app.on_enter(),
KeyCode::Tab => app.on_tab(),
KeyCode::Backspace => app.on_backspace(),