mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 23:54:14 +02:00
refactor: finish help menu
This commit is contained in:
parent
e7b9c72912
commit
7ee85a82f7
@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Changes
|
## 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
|
## Internal Changes
|
||||||
|
|
||||||
|
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -263,6 +263,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"smol",
|
"smol",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
"textwrap 0.14.2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml",
|
||||||
"tui",
|
"tui",
|
||||||
@ -348,7 +349,7 @@ dependencies = [
|
|||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"strsim",
|
"strsim",
|
||||||
"textwrap",
|
"textwrap 0.11.0",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
@ -1410,6 +1411,12 @@ version = "1.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smawk"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smol"
|
name = "smol"
|
||||||
version = "1.2.5"
|
version = "1.2.5"
|
||||||
@ -1482,6 +1489,17 @@ dependencies = [
|
|||||||
"unicode-width",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
@ -1558,6 +1576,15 @@ version = "1.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
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]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -57,6 +57,7 @@ serde = { version = "1.0.125", features = ["derive"] }
|
|||||||
# Sysinfo is still used in Linux for the ProcessStatus
|
# Sysinfo is still used in Linux for the ProcessStatus
|
||||||
sysinfo = "0.18.2"
|
sysinfo = "0.18.2"
|
||||||
thiserror = "1.0.24"
|
thiserror = "1.0.24"
|
||||||
|
textwrap = "0.14.2"
|
||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
tui = { version = "0.16.0", features = ["crossterm"], default-features = false }
|
tui = { version = "0.16.0", features = ["crossterm"], default-features = false }
|
||||||
typed-builder = "0.9.0"
|
typed-builder = "0.9.0"
|
||||||
|
@ -42,7 +42,7 @@ Note that key bindings are generally case-sensitive.
|
|||||||
| ++q++ , ++ctrl+c++ | Quit |
|
| ++q++ , ++ctrl+c++ | Quit |
|
||||||
| ++esc++ | Close dialog windows, search, widgets, or exit expanded mode |
|
| ++esc++ | Close dialog windows, search, widgets, or exit expanded mode |
|
||||||
| ++ctrl+r++ | Resets any collected data |
|
| ++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 |
|
| ++question++ | Open help menu |
|
||||||
| ++e++ | Toggle expanding the currently selected widget |
|
| ++e++ | Toggle expanding the currently selected widget |
|
||||||
| ++ctrl+up++ <br/> ++shift+up++ <br/> ++K++ <br/> ++W++ | Select the widget above |
|
| ++ctrl+up++ <br/> ++shift+up++ <br/> ++K++ <br/> ++W++ | Select the widget above |
|
||||||
|
345
src/app.rs
345
src/app.rs
@ -13,7 +13,7 @@ use std::{
|
|||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEventKind};
|
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent};
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use indextree::{Arena, NodeId};
|
use indextree::{Arena, NodeId};
|
||||||
use unicode_segmentation::GraphemeCursor;
|
use unicode_segmentation::GraphemeCursor;
|
||||||
@ -33,7 +33,7 @@ use crate::{
|
|||||||
BottomEvent, Pid,
|
BottomEvent, Pid,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::event::{EventResult, ReturnSignal, WidgetEventResult};
|
use self::event::{ComponentEventResult, EventResult, ReturnSignal};
|
||||||
|
|
||||||
const MAX_SEARCH_LENGTH: usize = 200;
|
const MAX_SEARCH_LENGTH: usize = 200;
|
||||||
|
|
||||||
@ -140,8 +140,6 @@ pub struct AppState {
|
|||||||
// --- Eventually delete/rewrite ---
|
// --- Eventually delete/rewrite ---
|
||||||
pub delete_dialog_state: AppDeleteDialogState,
|
pub delete_dialog_state: AppDeleteDialogState,
|
||||||
|
|
||||||
pub help_dialog_state: AppHelpDialogState,
|
|
||||||
|
|
||||||
// --- TO DELETE ---
|
// --- TO DELETE ---
|
||||||
pub cpu_state: CpuState,
|
pub cpu_state: CpuState,
|
||||||
pub mem_state: MemState,
|
pub mem_state: MemState,
|
||||||
@ -172,6 +170,8 @@ pub struct AppState {
|
|||||||
pub layout_tree: Arena<LayoutNode>,
|
pub layout_tree: Arena<LayoutNode>,
|
||||||
pub layout_tree_root: NodeId,
|
pub layout_tree_root: NodeId,
|
||||||
frozen_state: FrozenState,
|
frozen_state: FrozenState,
|
||||||
|
|
||||||
|
pub help_dialog: DialogState<HelpDialog>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
@ -204,7 +204,6 @@ impl AppState {
|
|||||||
data_collection: Default::default(),
|
data_collection: Default::default(),
|
||||||
is_expanded: Default::default(),
|
is_expanded: Default::default(),
|
||||||
delete_dialog_state: Default::default(),
|
delete_dialog_state: Default::default(),
|
||||||
help_dialog_state: Default::default(),
|
|
||||||
cpu_state: Default::default(),
|
cpu_state: Default::default(),
|
||||||
mem_state: Default::default(),
|
mem_state: Default::default(),
|
||||||
net_state: Default::default(),
|
net_state: Default::default(),
|
||||||
@ -222,6 +221,7 @@ impl AppState {
|
|||||||
is_force_redraw: Default::default(),
|
is_force_redraw: Default::default(),
|
||||||
is_determining_widget_boundary: Default::default(),
|
is_determining_widget_boundary: Default::default(),
|
||||||
frozen_state: Default::default(),
|
frozen_state: Default::default(),
|
||||||
|
help_dialog: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ impl AppState {
|
|||||||
let c = c.to_ascii_lowercase();
|
let c = c.to_ascii_lowercase();
|
||||||
match c {
|
match c {
|
||||||
'q' => EventResult::Quit,
|
'q' => EventResult::Quit,
|
||||||
'e' => {
|
'e' if !self.help_dialog.is_showing() => {
|
||||||
if self.app_config_fields.use_basic_mode {
|
if self.app_config_fields.use_basic_mode {
|
||||||
EventResult::NoRedraw
|
EventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
@ -285,11 +285,11 @@ impl AppState {
|
|||||||
EventResult::Redraw
|
EventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'?' => {
|
'?' if !self.help_dialog.is_showing() => {
|
||||||
self.help_dialog_state.is_showing_help = true;
|
self.help_dialog.show();
|
||||||
EventResult::Redraw
|
EventResult::Redraw
|
||||||
}
|
}
|
||||||
'f' => {
|
'f' if !self.help_dialog.is_showing() => {
|
||||||
self.toggle_freeze();
|
self.toggle_freeze();
|
||||||
if !self.is_frozen() {
|
if !self.is_frozen() {
|
||||||
let data_collection = &self.data_collection;
|
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`].
|
/// 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() {
|
if event.modifiers.is_empty() {
|
||||||
match event.code {
|
match event.code {
|
||||||
KeyCode::Esc => {
|
KeyCode::Esc => {
|
||||||
if self.is_expanded {
|
if self.is_expanded {
|
||||||
self.is_expanded = false;
|
self.is_expanded = false;
|
||||||
EventResult::Redraw
|
EventResult::Redraw
|
||||||
} else if self.help_dialog_state.is_showing_help {
|
} else if self.help_dialog.is_showing() {
|
||||||
self.help_dialog_state.is_showing_help = false;
|
self.help_dialog.hide();
|
||||||
self.help_dialog_state.scroll_state.current_scroll_index = 0;
|
|
||||||
EventResult::Redraw
|
EventResult::Redraw
|
||||||
} else if self.delete_dialog_state.is_showing_dd {
|
} else if self.delete_dialog_state.is_showing_dd {
|
||||||
self.close_dd();
|
self.close_dd();
|
||||||
@ -350,8 +392,13 @@ impl AppState {
|
|||||||
EventResult::NoRedraw
|
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 {
|
} else if let KeyModifiers::CONTROL = event.modifiers {
|
||||||
match event.code {
|
match event.code {
|
||||||
@ -384,30 +431,55 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Quick and dirty handler to convert [`WidgetEventResult`]s to [`EventResult`]s, and handle [`ReturnSignal`]s.
|
/// Handles a [`MouseEvent`].
|
||||||
fn convert_widget_event_result(&mut self, w: WidgetEventResult) -> EventResult {
|
fn handle_mouse_event(&mut self, event: MouseEvent) -> EventResult {
|
||||||
match w {
|
if let DialogState::Shown(help_dialog) = &mut self.help_dialog {
|
||||||
WidgetEventResult::Quit => EventResult::Quit,
|
let result = help_dialog.handle_mouse_event(event);
|
||||||
WidgetEventResult::Redraw => EventResult::Redraw,
|
self.convert_widget_event_result(result)
|
||||||
WidgetEventResult::NoRedraw => EventResult::NoRedraw,
|
} else if self.is_expanded {
|
||||||
WidgetEventResult::Signal(signal) => match signal {
|
if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
|
||||||
ReturnSignal::KillProcess => {
|
let result = widget.handle_mouse_event(event);
|
||||||
todo!()
|
self.convert_widget_event_result(result)
|
||||||
}
|
} else {
|
||||||
ReturnSignal::Update => {
|
EventResult::NoRedraw
|
||||||
if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
|
}
|
||||||
match &self.frozen_state {
|
} else {
|
||||||
FrozenState::NotFrozen => {
|
let mut returned_result = EventResult::NoRedraw;
|
||||||
widget.update_data(&self.data_collection);
|
for (id, widget) in self.widget_lookup_map.iter_mut() {
|
||||||
}
|
if widget.does_border_intersect_mouse(&event) {
|
||||||
FrozenState::Frozen(frozen_data) => {
|
let result = widget.handle_mouse_event(event);
|
||||||
widget.update_data(frozen_data);
|
|
||||||
}
|
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.
|
/// whether the app now requires a redraw.
|
||||||
pub fn handle_event(&mut self, event: BottomEvent) -> EventResult {
|
pub fn handle_event(&mut self, event: BottomEvent) -> EventResult {
|
||||||
match event {
|
match event {
|
||||||
BottomEvent::KeyInput(event) => {
|
BottomEvent::KeyInput(event) => self.handle_key_event(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::MouseInput(event) => {
|
BottomEvent::MouseInput(event) => {
|
||||||
// Not great, but basically a blind lookup through the table for anything that clips the click location.
|
// 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.
|
self.handle_mouse_event(event)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
BottomEvent::Update(new_data) => {
|
BottomEvent::Update(new_data) => {
|
||||||
self.data_collection.eat_data(new_data);
|
self.data_collection.eat_data(new_data);
|
||||||
|
|
||||||
|
// TODO: Optimization for dialogs; don't redraw here.
|
||||||
|
|
||||||
if !self.is_frozen() {
|
if !self.is_frozen() {
|
||||||
let data_collection = &self.data_collection;
|
let data_collection = &self.data_collection;
|
||||||
self.widget_lookup_map
|
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 {
|
pub fn is_in_search_widget(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.current_widget.widget_type,
|
self.current_widget.widget_type,
|
||||||
@ -600,7 +533,7 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_in_dialog(&self) -> bool {
|
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 {
|
fn ignore_normal_keybinds(&self) -> bool {
|
||||||
@ -1097,7 +1030,7 @@ impl AppState {
|
|||||||
pub fn on_up_key(&mut self) {
|
pub fn on_up_key(&mut self) {
|
||||||
if !self.is_in_dialog() {
|
if !self.is_in_dialog() {
|
||||||
self.decrement_position_count();
|
self.decrement_position_count();
|
||||||
} else if self.help_dialog_state.is_showing_help {
|
} else if self.help_dialog.is_showing() {
|
||||||
self.help_scroll_up();
|
self.help_scroll_up();
|
||||||
} else if self.delete_dialog_state.is_showing_dd {
|
} else if self.delete_dialog_state.is_showing_dd {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@ -1118,7 +1051,7 @@ impl AppState {
|
|||||||
pub fn on_down_key(&mut self) {
|
pub fn on_down_key(&mut self) {
|
||||||
if !self.is_in_dialog() {
|
if !self.is_in_dialog() {
|
||||||
self.increment_position_count();
|
self.increment_position_count();
|
||||||
} else if self.help_dialog_state.is_showing_help {
|
} else if self.help_dialog.is_showing() {
|
||||||
self.help_scroll_down();
|
self.help_scroll_down();
|
||||||
} else if self.delete_dialog_state.is_showing_dd {
|
} else if self.delete_dialog_state.is_showing_dd {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@ -1596,19 +1529,9 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.handle_char(caught_char);
|
self.handle_char(caught_char);
|
||||||
} else if self.help_dialog_state.is_showing_help {
|
} else if self.help_dialog.is_showing() {
|
||||||
match caught_char {
|
match caught_char {
|
||||||
'1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
|
'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],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'j' | 'k' | 'g' | 'G' => self.handle_char(caught_char),
|
'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;
|
self.is_force_redraw = true;
|
||||||
}
|
}
|
||||||
'H' | 'A' => self.move_widget_selection(&WidgetDirection::Left),
|
'H' | 'A' => self.move_widget_selection(&WidgetDirection::Left),
|
||||||
@ -1863,7 +1786,6 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn expand_widget(&mut self) {
|
fn expand_widget(&mut self) {
|
||||||
// TODO: [BASIC] Expansion in basic mode.
|
|
||||||
if !self.ignore_normal_keybinds() && !self.app_config_fields.use_basic_mode {
|
if !self.ignore_normal_keybinds() && !self.app_config_fields.use_basic_mode {
|
||||||
// Pop-out mode. We ignore if in process search.
|
// Pop-out mode. We ignore if in process search.
|
||||||
|
|
||||||
@ -2256,7 +2178,7 @@ impl AppState {
|
|||||||
{
|
{
|
||||||
if proc_widget_state.is_sort_open {
|
if proc_widget_state.is_sort_open {
|
||||||
if let Some(proc_sort_widget) = self.widget_map.get(&new_widget_id) {
|
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();
|
self.reset_multi_tap_keys();
|
||||||
} else if self.help_dialog_state.is_showing_help {
|
} else if self.help_dialog.is_showing() {
|
||||||
self.help_dialog_state.scroll_state.current_scroll_index = 0;
|
// self.help_dialog_state.scroll_state.current_scroll_index = 0;
|
||||||
} else if self.delete_dialog_state.is_showing_dd {
|
} else if self.delete_dialog_state.is_showing_dd {
|
||||||
self.delete_dialog_state.selected_signal = KillSignal::Cancel;
|
self.delete_dialog_state.selected_signal = KillSignal::Cancel;
|
||||||
}
|
}
|
||||||
@ -2517,26 +2439,17 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn help_scroll_up(&mut self) {
|
fn help_scroll_up(&mut self) {
|
||||||
if self.help_dialog_state.scroll_state.current_scroll_index > 0 {
|
// if self.help_dialog_state.scroll_state.current_scroll_index > 0 {
|
||||||
self.help_dialog_state.scroll_state.current_scroll_index -= 1;
|
// self.help_dialog_state.scroll_state.current_scroll_index -= 1;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn help_scroll_down(&mut self) {
|
fn help_scroll_down(&mut self) {
|
||||||
if self.help_dialog_state.scroll_state.current_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.max_scroll_index
|
||||||
{
|
// {
|
||||||
self.help_dialog_state.scroll_state.current_scroll_index += 1;
|
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_plus(&mut self) {
|
fn on_plus(&mut self) {
|
||||||
|
@ -224,15 +224,12 @@ impl DataCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn eat_temp(&mut self, temperature_sensors: Vec<temperature::TempHarvest>) {
|
fn eat_temp(&mut self, temperature_sensors: Vec<temperature::TempHarvest>) {
|
||||||
// TODO: [PO] To implement
|
|
||||||
self.temp_harvest = temperature_sensors.to_vec();
|
self.temp_harvest = temperature_sensors.to_vec();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_disks(
|
fn eat_disks(
|
||||||
&mut self, disks: Vec<disks::DiskHarvest>, io: disks::IoHarvest, harvested_time: Instant,
|
&mut self, disks: Vec<disks::DiskHarvest>, io: disks::IoHarvest, harvested_time: Instant,
|
||||||
) {
|
) {
|
||||||
// TODO: [PO] To implement
|
|
||||||
|
|
||||||
let time_since_last_harvest = harvested_time
|
let time_since_last_harvest = harvested_time
|
||||||
.duration_since(self.current_instant)
|
.duration_since(self.current_instant)
|
||||||
.as_secs_f64();
|
.as_secs_f64();
|
||||||
|
@ -27,7 +27,6 @@ use std::borrow::Cow;
|
|||||||
|
|
||||||
use crate::Pid;
|
use crate::Pid;
|
||||||
|
|
||||||
// TODO: Add value so we know if it's sorted ascending or descending by default?
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum ProcessSorting {
|
pub enum ProcessSorting {
|
||||||
CpuPercent,
|
CpuPercent,
|
||||||
|
@ -57,7 +57,6 @@ fn is_temp_filtered(filter: &Option<Filter>, text: &str) -> bool {
|
|||||||
|
|
||||||
fn temp_vec_sort(temperature_vec: &mut Vec<TempHarvest>) {
|
fn temp_vec_sort(temperature_vec: &mut Vec<TempHarvest>) {
|
||||||
// By default, sort temperature, then by alphabetically!
|
// 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.
|
// 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) {
|
temperature_vec.sort_by(|a, b| match a.temperature.partial_cmp(&b.temperature) {
|
||||||
|
109
src/app/event.rs
109
src/app/event.rs
@ -1,6 +1,5 @@
|
|||||||
use std::time::{Duration, Instant};
|
pub mod multi_key;
|
||||||
|
pub use multi_key::*;
|
||||||
const MAX_TIMEOUT: Duration = Duration::from_millis(400);
|
|
||||||
|
|
||||||
/// These are "signals" that are sent along with an [`WidgetEventResult`] to signify a potential additional action
|
/// 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.
|
/// 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,
|
/// The results of a widget handling some event, like a mouse or key event,
|
||||||
/// signifying what the program should then do next.
|
/// signifying what the program should then do next.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum WidgetEventResult {
|
pub enum ComponentEventResult {
|
||||||
/// Kill the program.
|
/// The event isn't handled by the widget, and should be propagated to the parent.
|
||||||
Quit,
|
Unhandled,
|
||||||
/// Trigger a redraw.
|
/// Trigger a redraw.
|
||||||
Redraw,
|
Redraw,
|
||||||
/// Don't trigger a 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.
|
/// This event occurs if the widget did not handle the selection action; the caller must handle it.
|
||||||
NotHandled,
|
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<char>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MultiKey {
|
|
||||||
/// Creates a new [`MultiKey`] with a given pattern and timeout.
|
|
||||||
pub fn register(pattern: Vec<char>) -> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
101
src/app/event/multi_key.rs
Normal file
101
src/app/event/multi_key.rs
Normal file
@ -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<char>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultiKey {
|
||||||
|
/// Creates a new [`MultiKey`] with a given pattern and timeout.
|
||||||
|
pub fn register(pattern: Vec<char>) -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1511,7 +1511,6 @@ pub fn move_widget_selection(
|
|||||||
}
|
}
|
||||||
LayoutNode::Widget(_) => {
|
LayoutNode::Widget(_) => {
|
||||||
// Halt!
|
// Halt!
|
||||||
// TODO: How does this handle carousel?
|
|
||||||
current_id
|
current_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,10 @@ use tui::{backend::Backend, layout::Rect, widgets::TableState, Frame};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
event::{SelectionAction, WidgetEventResult},
|
event::{ComponentEventResult, SelectionAction},
|
||||||
layout_manager::BottomWidgetType,
|
layout_manager::BottomWidgetType,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
constants,
|
|
||||||
options::layout_options::LayoutRule,
|
options::layout_options::LayoutRule,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -20,12 +19,15 @@ mod tui_stuff;
|
|||||||
pub mod base;
|
pub mod base;
|
||||||
pub use base::*;
|
pub use base::*;
|
||||||
|
|
||||||
|
pub mod dialogs;
|
||||||
|
pub use dialogs::*;
|
||||||
|
|
||||||
pub mod bottom_widgets;
|
pub mod bottom_widgets;
|
||||||
pub use bottom_widgets::*;
|
pub use bottom_widgets::*;
|
||||||
|
|
||||||
use self::tui_stuff::BlockBuilder;
|
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.
|
/// A trait for things that are drawn with state.
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
@ -33,16 +35,16 @@ use super::data_farmer::DataCollection;
|
|||||||
pub trait Component {
|
pub trait Component {
|
||||||
/// Handles a [`KeyEvent`].
|
/// Handles a [`KeyEvent`].
|
||||||
///
|
///
|
||||||
/// Defaults to returning [`EventResult::NoRedraw`], indicating nothing should be done.
|
/// Defaults to returning [`ComponentEventResult::Unhandled`], indicating the component does not handle this event.
|
||||||
fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
|
fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::Unhandled
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles a [`MouseEvent`].
|
/// Handles a [`MouseEvent`].
|
||||||
///
|
///
|
||||||
/// Defaults to returning [`EventResult::Continue`], indicating nothing should be done.
|
/// Defaults to returning [`ComponentEventResult::Unhandled`], indicating the component does not handle this event.
|
||||||
fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult {
|
fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::Unhandled
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a [`Component`]'s bounding box. Note that these are defined in *global*, *absolute*
|
/// Returns a [`Component`]'s bounding box. Note that these are defined in *global*, *absolute*
|
||||||
@ -180,6 +182,41 @@ pub enum TmpBottomWidget {
|
|||||||
Empty,
|
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<D: Default + Component> {
|
||||||
|
Hidden,
|
||||||
|
Shown(D),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> Default for DialogState<D>
|
||||||
|
where
|
||||||
|
D: Default + Component,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
DialogState::Hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> DialogState<D>
|
||||||
|
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 -----
|
// ----- Old stuff below -----
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -250,7 +287,27 @@ impl Default for AppHelpDialogState {
|
|||||||
AppHelpDialogState {
|
AppHelpDialogState {
|
||||||
is_showing_help: false,
|
is_showing_help: false,
|
||||||
scroll_state: ParagraphScrollState::default(),
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use crossterm::event::{KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEve
|
|||||||
use tui::{layout::Rect, widgets::TableState};
|
use tui::{layout::Rect, widgets::TableState};
|
||||||
|
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
event::{MultiKey, MultiKeyResult, WidgetEventResult},
|
event::{ComponentEventResult, MultiKey, MultiKeyResult},
|
||||||
Component,
|
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 {
|
if self.current_index != 0 {
|
||||||
self.set_index(0);
|
self.set_index(0);
|
||||||
|
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else {
|
} 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;
|
let last_index = self.num_items - 1;
|
||||||
if self.current_index != last_index {
|
if self.current_index != last_index {
|
||||||
self.set_index(last_index);
|
self.set_index(last_index);
|
||||||
|
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves *downward* by *incrementing* the current index.
|
/// 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 {
|
if self.num_items == 0 {
|
||||||
return WidgetEventResult::NoRedraw;
|
return ComponentEventResult::NoRedraw;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_index = self.current_index + change_by;
|
let new_index = self.current_index + change_by;
|
||||||
if new_index >= self.num_items || self.current_index == new_index {
|
if new_index >= self.num_items || self.current_index == new_index {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
self.set_index(new_index);
|
self.set_index(new_index);
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves *upward* by *decrementing* the current index.
|
/// 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 {
|
if self.num_items == 0 {
|
||||||
return WidgetEventResult::NoRedraw;
|
return ComponentEventResult::NoRedraw;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_index = self.current_index.saturating_sub(change_by);
|
let new_index = self.current_index.saturating_sub(change_by);
|
||||||
if self.current_index == new_index {
|
if self.current_index == new_index {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
self.set_index(new_index);
|
self.set_index(new_index);
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ impl Scrollable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Component for 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};
|
use crossterm::event::KeyCode::{Char, Down, Up};
|
||||||
|
|
||||||
if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT {
|
if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT {
|
||||||
@ -218,18 +218,19 @@ impl Component for Scrollable {
|
|||||||
Char('k') => self.move_up(1),
|
Char('k') => self.move_up(1),
|
||||||
Char('g') => match self.gg_manager.input('g') {
|
Char('g') => match self.gg_manager.input('g') {
|
||||||
MultiKeyResult::Completed => self.skip_to_first(),
|
MultiKeyResult::Completed => self.skip_to_first(),
|
||||||
MultiKeyResult::Accepted => WidgetEventResult::NoRedraw,
|
MultiKeyResult::Accepted | MultiKeyResult::Rejected => {
|
||||||
MultiKeyResult::Rejected => WidgetEventResult::NoRedraw,
|
ComponentEventResult::NoRedraw
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Char('G') => self.skip_to_last(),
|
Char('G') => self.skip_to_last(),
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
match event.kind {
|
||||||
MouseEventKind::Down(MouseButton::Left) => {
|
MouseEventKind::Down(MouseButton::Left) => {
|
||||||
if self.does_bounds_intersect_mouse(&event) {
|
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::ScrollDown => self.move_down(1),
|
||||||
MouseEventKind::ScrollUp => self.move_up(1),
|
MouseEventKind::ScrollUp => self.move_up(1),
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ use tui::{backend::Backend, layout::Rect, Frame};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
event::WidgetEventResult, text_table::SimpleColumn, widgets::tui_stuff::BlockBuilder,
|
event::ComponentEventResult, text_table::SimpleColumn, widgets::tui_stuff::BlockBuilder,
|
||||||
Component, TextTable,
|
Component, TextTable,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
@ -69,11 +69,11 @@ impl Component for SortMenu {
|
|||||||
self.bounds = new_bounds;
|
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)
|
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)
|
self.table.handle_mouse_event(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use tui::{backend::Backend, layout::Rect, Frame};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
event::{ReturnSignal, WidgetEventResult},
|
event::{ReturnSignal, ComponentEventResult},
|
||||||
widgets::tui_stuff::BlockBuilder,
|
widgets::tui_stuff::BlockBuilder,
|
||||||
Component, TextTable,
|
Component, TextTable,
|
||||||
},
|
},
|
||||||
@ -391,12 +391,12 @@ impl<S> Component for SortableTextTable<S>
|
|||||||
where
|
where
|
||||||
S: SortableColumn,
|
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() {
|
for (index, column) in self.table.columns.iter().enumerate() {
|
||||||
if let Some((shortcut, _)) = *column.shortcut() {
|
if let Some((shortcut, _)) = *column.shortcut() {
|
||||||
if shortcut == event {
|
if shortcut == event {
|
||||||
self.set_sort_index(index);
|
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)
|
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 let MouseEventKind::Down(MouseButton::Left) = event.kind {
|
||||||
if !self.does_bounds_intersect_mouse(&event) {
|
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!
|
// 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 let Some((start, end)) = column.get_x_bounds() {
|
||||||
if x >= start && x <= end {
|
if x >= start && x <= end {
|
||||||
self.set_sort_index(index);
|
self.set_sort_index(index);
|
||||||
return WidgetEventResult::Signal(ReturnSignal::Update);
|
return ComponentEventResult::Signal(ReturnSignal::Update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ use unicode_width::UnicodeWidthStr;
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
event::{
|
event::{
|
||||||
|
ComponentEventResult::{self},
|
||||||
ReturnSignal,
|
ReturnSignal,
|
||||||
WidgetEventResult::{self},
|
|
||||||
},
|
},
|
||||||
Component,
|
Component,
|
||||||
},
|
},
|
||||||
@ -85,19 +85,19 @@ impl TextInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_text(&mut self) -> WidgetEventResult {
|
fn clear_text(&mut self) -> ComponentEventResult {
|
||||||
if self.text.is_empty() {
|
if self.text.is_empty() {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
self.text = String::default();
|
self.text = String::default();
|
||||||
self.cursor = GraphemeCursor::new(0, 0, true);
|
self.cursor = GraphemeCursor::new(0, 0, true);
|
||||||
self.window_index = Default::default();
|
self.window_index = Default::default();
|
||||||
self.cursor_direction = CursorDirection::Left;
|
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();
|
let current_index = self.cursor.cur_cursor();
|
||||||
|
|
||||||
if current_index < self.text.len() {
|
if current_index < self.text.len() {
|
||||||
@ -105,30 +105,30 @@ impl TextInput {
|
|||||||
if index > 0 {
|
if index > 0 {
|
||||||
self.cursor.set_cursor(index + current_index);
|
self.cursor.set_cursor(index + current_index);
|
||||||
self.cursor_direction = CursorDirection::Right;
|
self.cursor_direction = CursorDirection::Right;
|
||||||
return WidgetEventResult::Redraw;
|
return ComponentEventResult::Redraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.cursor.set_cursor(self.text.len());
|
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();
|
let current_index = self.cursor.cur_cursor();
|
||||||
|
|
||||||
for (index, _word) in self.text[..current_index].unicode_word_indices().rev() {
|
for (index, _word) in self.text[..current_index].unicode_word_indices().rev() {
|
||||||
if index < current_index {
|
if index < current_index {
|
||||||
self.cursor.set_cursor(index);
|
self.cursor.set_cursor(index);
|
||||||
self.cursor_direction = CursorDirection::Left;
|
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
|
// Fairly simple logic - create the word index iterator, skip the word that intersects with the current
|
||||||
// cursor location, draw the rest, update the string.
|
// cursor location, draw the rest, update the string.
|
||||||
let current_index = self.cursor.cur_cursor();
|
let current_index = self.cursor.cur_cursor();
|
||||||
@ -147,16 +147,16 @@ impl TextInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if start_delete_index == current_index {
|
if start_delete_index == current_index {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
self.text.drain(start_delete_index..current_index);
|
self.text.drain(start_delete_index..current_index);
|
||||||
self.cursor = GraphemeCursor::new(start_delete_index, self.text.len(), true);
|
self.cursor = GraphemeCursor::new(start_delete_index, self.text.len(), true);
|
||||||
self.cursor_direction = CursorDirection::Left;
|
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();
|
let current_index = self.cursor.cur_cursor();
|
||||||
|
|
||||||
if current_index > 0 {
|
if current_index > 0 {
|
||||||
@ -166,13 +166,13 @@ impl TextInput {
|
|||||||
self.cursor = GraphemeCursor::new(new_index, self.text.len(), true);
|
self.cursor = GraphemeCursor::new(new_index, self.text.len(), true);
|
||||||
self.cursor_direction = CursorDirection::Left;
|
self.cursor_direction = CursorDirection::Left;
|
||||||
|
|
||||||
WidgetEventResult::Signal(ReturnSignal::Update)
|
ComponentEventResult::Signal(ReturnSignal::Update)
|
||||||
} else {
|
} 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();
|
let current_index = self.cursor.cur_cursor();
|
||||||
|
|
||||||
if current_index < self.text.len() {
|
if current_index < self.text.len() {
|
||||||
@ -182,19 +182,19 @@ impl TextInput {
|
|||||||
self.cursor = GraphemeCursor::new(current_index, self.text.len(), true);
|
self.cursor = GraphemeCursor::new(current_index, self.text.len(), true);
|
||||||
self.cursor_direction = CursorDirection::Left;
|
self.cursor_direction = CursorDirection::Left;
|
||||||
|
|
||||||
WidgetEventResult::Signal(ReturnSignal::Update)
|
ComponentEventResult::Signal(ReturnSignal::Update)
|
||||||
} else {
|
} 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();
|
let current_index = self.cursor.cur_cursor();
|
||||||
self.text.insert(current_index, c);
|
self.text.insert(current_index, c);
|
||||||
self.cursor = GraphemeCursor::new(current_index, self.text.len(), true);
|
self.cursor = GraphemeCursor::new(current_index, self.text.len(), true);
|
||||||
self.move_forward();
|
self.move_forward();
|
||||||
|
|
||||||
WidgetEventResult::Signal(ReturnSignal::Update)
|
ComponentEventResult::Signal(ReturnSignal::Update)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the window indexes and returns the start index.
|
/// Updates the window indexes and returns the start index.
|
||||||
@ -296,29 +296,29 @@ impl Component for TextInput {
|
|||||||
self.bounds = new_bounds;
|
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() {
|
if event.modifiers.is_empty() {
|
||||||
match event.code {
|
match event.code {
|
||||||
KeyCode::Left => {
|
KeyCode::Left => {
|
||||||
let original_cursor = self.cursor.cur_cursor();
|
let original_cursor = self.cursor.cur_cursor();
|
||||||
if self.move_back() == original_cursor {
|
if self.move_back() == original_cursor {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Right => {
|
KeyCode::Right => {
|
||||||
let original_cursor = self.cursor.cur_cursor();
|
let original_cursor = self.cursor.cur_cursor();
|
||||||
if self.move_forward() == original_cursor {
|
if self.move_forward() == original_cursor {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => self.clear_previous_grapheme(),
|
KeyCode::Backspace => self.clear_previous_grapheme(),
|
||||||
KeyCode::Delete => self.clear_current_grapheme(),
|
KeyCode::Delete => self.clear_current_grapheme(),
|
||||||
KeyCode::Char(c) => self.insert_character(c),
|
KeyCode::Char(c) => self.insert_character(c),
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
} else if let KeyModifiers::CONTROL = event.modifiers {
|
} else if let KeyModifiers::CONTROL = event.modifiers {
|
||||||
match event.code {
|
match event.code {
|
||||||
@ -326,46 +326,46 @@ impl Component for TextInput {
|
|||||||
let prev_index = self.cursor.cur_cursor();
|
let prev_index = self.cursor.cur_cursor();
|
||||||
self.cursor.set_cursor(0);
|
self.cursor.set_cursor(0);
|
||||||
if self.cursor.cur_cursor() == prev_index {
|
if self.cursor.cur_cursor() == prev_index {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('e') => {
|
KeyCode::Char('e') => {
|
||||||
let prev_index = self.cursor.cur_cursor();
|
let prev_index = self.cursor.cur_cursor();
|
||||||
self.cursor.set_cursor(self.text.len());
|
self.cursor.set_cursor(self.text.len());
|
||||||
if self.cursor.cur_cursor() == prev_index {
|
if self.cursor.cur_cursor() == prev_index {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('u') => self.clear_text(),
|
KeyCode::Char('u') => self.clear_text(),
|
||||||
KeyCode::Char('w') => self.clear_word_from_cursor(),
|
KeyCode::Char('w') => self.clear_word_from_cursor(),
|
||||||
KeyCode::Char('h') => self.clear_previous_grapheme(),
|
KeyCode::Char('h') => self.clear_previous_grapheme(),
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
} else if let KeyModifiers::ALT = event.modifiers {
|
} else if let KeyModifiers::ALT = event.modifiers {
|
||||||
match event.code {
|
match event.code {
|
||||||
KeyCode::Char('b') => self.move_word_back(),
|
KeyCode::Char('b') => self.move_word_back(),
|
||||||
KeyCode::Char('f') => self.move_word_forward(),
|
KeyCode::Char('f') => self.move_word_forward(),
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
} else {
|
} 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...
|
// We are assuming this is within bounds...
|
||||||
|
|
||||||
let x = event.column;
|
let x = event.column;
|
||||||
let widget_x = self.bounds.x + 2;
|
let widget_x = self.bounds.x + 2;
|
||||||
if x >= widget_x {
|
if x >= widget_x {
|
||||||
// TODO: do this
|
// TODO: Do this at some point after refactor
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use tui::{
|
|||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{event::WidgetEventResult, widgets::tui_stuff::BlockBuilder, Component, Scrollable},
|
app::{event::ComponentEventResult, widgets::tui_stuff::BlockBuilder, Component, Scrollable},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
constants::TABLE_GAP_HEIGHT_LIMIT,
|
constants::TABLE_GAP_HEIGHT_LIMIT,
|
||||||
};
|
};
|
||||||
@ -130,7 +130,7 @@ where
|
|||||||
pub show_gap: bool,
|
pub show_gap: bool,
|
||||||
|
|
||||||
/// The bounding box of the [`TextTable`].
|
/// 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.
|
/// The bounds including the border, if there is one.
|
||||||
pub border_bounds: Rect,
|
pub border_bounds: Rect,
|
||||||
@ -492,19 +492,19 @@ impl<C> Component for TextTable<C>
|
|||||||
where
|
where
|
||||||
C: TableColumn,
|
C: TableColumn,
|
||||||
{
|
{
|
||||||
fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
|
fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
|
||||||
if self.selectable {
|
if self.selectable {
|
||||||
self.scrollable.handle_key_event(event)
|
self.scrollable.handle_key_event(event)
|
||||||
} else {
|
} 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 {
|
if self.selectable {
|
||||||
self.scrollable.handle_mouse_event(event)
|
self.scrollable.handle_mouse_event(event)
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::Unhandled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use tui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
event::WidgetEventResult,
|
event::ComponentEventResult,
|
||||||
widgets::tui_stuff::{
|
widgets::tui_stuff::{
|
||||||
custom_legend_chart::{Axis, Dataset},
|
custom_legend_chart::{Axis, Dataset},
|
||||||
TimeChart,
|
TimeChart,
|
||||||
@ -160,62 +160,62 @@ impl TimeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Handles a char `c`.
|
/// Handles a char `c`.
|
||||||
fn handle_char(&mut self, c: char) -> WidgetEventResult {
|
fn handle_char(&mut self, c: char) -> ComponentEventResult {
|
||||||
match c {
|
match c {
|
||||||
'-' => self.zoom_out(),
|
'-' => self.zoom_out(),
|
||||||
'+' => self.zoom_in(),
|
'+' => self.zoom_in(),
|
||||||
'=' => self.reset_zoom(),
|
'=' => 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);
|
let new_time = self.current_display_time.saturating_sub(self.time_interval);
|
||||||
|
|
||||||
if self.current_display_time == new_time {
|
if self.current_display_time == new_time {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else if new_time >= self.min_duration {
|
} else if new_time >= self.min_duration {
|
||||||
self.current_display_time = new_time;
|
self.current_display_time = new_time;
|
||||||
self.autohide_timer.start_display_timer();
|
self.autohide_timer.start_display_timer();
|
||||||
|
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else if new_time != self.min_duration {
|
} else if new_time != self.min_duration {
|
||||||
self.current_display_time = self.min_duration;
|
self.current_display_time = self.min_duration;
|
||||||
self.autohide_timer.start_display_timer();
|
self.autohide_timer.start_display_timer();
|
||||||
|
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else {
|
} 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;
|
let new_time = self.current_display_time + self.time_interval;
|
||||||
|
|
||||||
if self.current_display_time == new_time {
|
if self.current_display_time == new_time {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else if new_time <= self.max_duration {
|
} else if new_time <= self.max_duration {
|
||||||
self.current_display_time = new_time;
|
self.current_display_time = new_time;
|
||||||
self.autohide_timer.start_display_timer();
|
self.autohide_timer.start_display_timer();
|
||||||
|
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else if new_time != self.max_duration {
|
} else if new_time != self.max_duration {
|
||||||
self.current_display_time = self.max_duration;
|
self.current_display_time = self.max_duration;
|
||||||
self.autohide_timer.start_display_timer();
|
self.autohide_timer.start_display_timer();
|
||||||
|
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else {
|
} 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 {
|
if self.current_display_time == self.default_time_value {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
self.current_display_time = self.default_time_value;
|
self.current_display_time = self.default_time_value;
|
||||||
self.autohide_timer.start_display_timer();
|
self.autohide_timer.start_display_timer();
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,24 +307,24 @@ impl TimeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Component for 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;
|
use crossterm::event::KeyCode::Char;
|
||||||
|
|
||||||
if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT {
|
if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT {
|
||||||
match event.code {
|
match event.code {
|
||||||
Char(c) => self.handle_char(c),
|
Char(c) => self.handle_char(c),
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
match event.kind {
|
||||||
MouseEventKind::ScrollDown => self.zoom_out(),
|
MouseEventKind::ScrollDown => self.zoom_out(),
|
||||||
MouseEventKind::ScrollUp => self.zoom_in(),
|
MouseEventKind::ScrollUp => self.zoom_in(),
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,8 @@ use tui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
event::WidgetEventResult, widgets::tui_stuff::PipeGauge, Component, DataCollection, Widget,
|
event::ComponentEventResult, widgets::tui_stuff::PipeGauge, Component, DataCollection,
|
||||||
|
Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
constants::SIDE_BORDERS,
|
constants::SIDE_BORDERS,
|
||||||
@ -54,13 +55,13 @@ impl Component for BasicMem {
|
|||||||
self.bounds = new_bounds;
|
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 {
|
match event.code {
|
||||||
KeyCode::Char('%') if event.modifiers.is_empty() => {
|
KeyCode::Char('%') => {
|
||||||
self.use_percent = !self.use_percent;
|
self.use_percent = !self.use_percent;
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ use tui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
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,
|
widgets::tui_stuff::PipeGauge, Component, Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
@ -114,44 +114,44 @@ impl Component for BatteryTable {
|
|||||||
self.bounds = new_bounds;
|
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() {
|
if event.modifiers.is_empty() {
|
||||||
match event.code {
|
match event.code {
|
||||||
KeyCode::Left => {
|
KeyCode::Left => {
|
||||||
let current_index = self.selected_index;
|
let current_index = self.selected_index;
|
||||||
self.decrement_index();
|
self.decrement_index();
|
||||||
if current_index == self.selected_index {
|
if current_index == self.selected_index {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Right => {
|
KeyCode::Right => {
|
||||||
let current_index = self.selected_index;
|
let current_index = self.selected_index;
|
||||||
self.increment_index();
|
self.increment_index();
|
||||||
if current_index == self.selected_index {
|
if current_index == self.selected_index {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
} else {
|
} 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() {
|
for (itx, bound) in self.tab_bounds.iter().enumerate() {
|
||||||
if does_bound_intersect_coordinate(event.column, event.row, *bound)
|
if does_bound_intersect_coordinate(event.column, event.row, *bound)
|
||||||
&& itx < self.battery_data.len()
|
&& itx < self.battery_data.len()
|
||||||
{
|
{
|
||||||
self.selected_index = itx;
|
self.selected_index = itx;
|
||||||
return WidgetEventResult::Redraw;
|
return ComponentEventResult::Redraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::Unhandled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ impl Widget for BatteryTable {
|
|||||||
.block()
|
.block()
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.borders(self.block_border)
|
.borders(self.block_border)
|
||||||
.expanded(expanded)
|
.show_esc(expanded)
|
||||||
.build(painter, area);
|
.build(painter, area);
|
||||||
|
|
||||||
let inner_area = block.inner(area);
|
let inner_area = block.inner(area);
|
||||||
|
@ -12,7 +12,7 @@ use tui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
does_bound_intersect_coordinate, event::WidgetEventResult, Component, SelectableType,
|
does_bound_intersect_coordinate, event::ComponentEventResult, Component, SelectableType,
|
||||||
Widget,
|
Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
@ -164,7 +164,7 @@ impl Component for Carousel {
|
|||||||
self.bounds = new_bounds;
|
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 {
|
match event.kind {
|
||||||
crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left) => {
|
crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left) => {
|
||||||
let x = event.column;
|
let x = event.column;
|
||||||
@ -172,15 +172,15 @@ impl Component for Carousel {
|
|||||||
|
|
||||||
if does_bound_intersect_coordinate(x, y, self.left_button_bounds) {
|
if does_bound_intersect_coordinate(x, y, self.left_button_bounds) {
|
||||||
self.decrement_index();
|
self.decrement_index();
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else if does_bound_intersect_coordinate(x, y, self.right_button_bounds) {
|
} else if does_bound_intersect_coordinate(x, y, self.right_button_bounds) {
|
||||||
self.increment_index();
|
self.increment_index();
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::Unhandled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => WidgetEventResult::NoRedraw,
|
_ => ComponentEventResult::Unhandled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use tui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
event::{SelectionAction, WidgetEventResult},
|
event::{ComponentEventResult, SelectionAction},
|
||||||
text_table::SimpleColumn,
|
text_table::SimpleColumn,
|
||||||
time_graph::TimeGraphData,
|
time_graph::TimeGraphData,
|
||||||
AppConfigFields, AppScrollWidgetState, CanvasTableWidthState, Component, DataCollection,
|
AppConfigFields, AppScrollWidgetState, CanvasTableWidthState, Component, DataCollection,
|
||||||
@ -118,21 +118,21 @@ impl CpuGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Component for 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 {
|
match self.selected {
|
||||||
CpuGraphSelection::Graph => self.graph.handle_key_event(event),
|
CpuGraphSelection::Graph => self.graph.handle_key_event(event),
|
||||||
CpuGraphSelection::Legend => self.legend.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 self.graph.does_border_intersect_mouse(&event) {
|
||||||
if let CpuGraphSelection::Graph = self.selected {
|
if let CpuGraphSelection::Graph = self.selected {
|
||||||
self.graph.handle_mouse_event(event)
|
self.graph.handle_mouse_event(event)
|
||||||
} else {
|
} else {
|
||||||
self.selected = CpuGraphSelection::Graph;
|
self.selected = CpuGraphSelection::Graph;
|
||||||
self.graph.handle_mouse_event(event);
|
self.graph.handle_mouse_event(event);
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
} else if self.legend.does_border_intersect_mouse(&event) {
|
} else if self.legend.does_border_intersect_mouse(&event) {
|
||||||
if let CpuGraphSelection::Legend = self.selected {
|
if let CpuGraphSelection::Legend = self.selected {
|
||||||
@ -140,10 +140,10 @@ impl Component for CpuGraph {
|
|||||||
} else {
|
} else {
|
||||||
self.selected = CpuGraphSelection::Legend;
|
self.selected = CpuGraphSelection::Legend;
|
||||||
self.legend.handle_mouse_event(event);
|
self.legend.handle_mouse_event(event);
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::Unhandled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ impl Widget for CpuGraph {
|
|||||||
let legend_block = self
|
let legend_block = self
|
||||||
.block()
|
.block()
|
||||||
.selected(selected && matches!(&self.selected, CpuGraphSelection::Legend))
|
.selected(selected && matches!(&self.selected, CpuGraphSelection::Legend))
|
||||||
.expanded(expanded)
|
.show_esc(expanded)
|
||||||
.hide_title(true);
|
.hide_title(true);
|
||||||
|
|
||||||
let legend_data = self
|
let legend_data = self
|
||||||
@ -279,7 +279,7 @@ impl Widget for CpuGraph {
|
|||||||
let graph_block = self
|
let graph_block = self
|
||||||
.block()
|
.block()
|
||||||
.selected(selected && matches!(&self.selected, CpuGraphSelection::Graph))
|
.selected(selected && matches!(&self.selected, CpuGraphSelection::Graph))
|
||||||
.expanded(expanded)
|
.show_esc(expanded)
|
||||||
.build(painter, graph_block_area);
|
.build(painter, graph_block_area);
|
||||||
|
|
||||||
self.graph.draw_tui_chart(
|
self.graph.draw_tui_chart(
|
||||||
|
@ -5,7 +5,7 @@ use tui::{backend::Backend, layout::Rect, widgets::Borders, Frame};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_farmer::DataCollection, event::WidgetEventResult,
|
data_farmer::DataCollection, event::ComponentEventResult,
|
||||||
sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppScrollWidgetState,
|
sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppScrollWidgetState,
|
||||||
CanvasTableWidthState, Component, TextTable, Widget,
|
CanvasTableWidthState, Component, TextTable, Widget,
|
||||||
},
|
},
|
||||||
@ -105,11 +105,11 @@ impl DiskTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Component for 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)
|
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)
|
self.table.handle_mouse_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ impl Widget for DiskTable {
|
|||||||
.block()
|
.block()
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.borders(self.block_border)
|
.borders(self.block_border)
|
||||||
.expanded(expanded);
|
.show_esc(expanded);
|
||||||
|
|
||||||
self.table.draw_tui_table(
|
self.table.draw_tui_table(
|
||||||
painter,
|
painter,
|
||||||
|
@ -4,7 +4,7 @@ use crossterm::event::{KeyEvent, MouseEvent};
|
|||||||
use tui::{backend::Backend, layout::Rect};
|
use tui::{backend::Backend, layout::Rect};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{event::WidgetEventResult, time_graph::TimeGraphData, DataCollection},
|
app::{event::ComponentEventResult, time_graph::TimeGraphData, DataCollection},
|
||||||
app::{Component, TimeGraph, Widget},
|
app::{Component, TimeGraph, Widget},
|
||||||
data_conversion::{convert_mem_data_points, convert_mem_labels, convert_swap_data_points},
|
data_conversion::{convert_mem_data_points, convert_mem_labels, convert_swap_data_points},
|
||||||
options::layout_options::LayoutRule,
|
options::layout_options::LayoutRule,
|
||||||
@ -63,11 +63,11 @@ impl MemGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Component for 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)
|
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)
|
self.graph.handle_mouse_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ impl Widget for MemGraph {
|
|||||||
let block = self
|
let block = self
|
||||||
.block()
|
.block()
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.expanded(expanded)
|
.show_esc(expanded)
|
||||||
.build(painter, area);
|
.build(painter, area);
|
||||||
|
|
||||||
let mut chart_data = Vec::with_capacity(2);
|
let mut chart_data = Vec::with_capacity(2);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::{borrow::Cow, collections::HashMap, time::Instant};
|
use std::{borrow::Cow, collections::HashMap, time::Instant};
|
||||||
|
|
||||||
|
use crossterm::event::{KeyEvent, MouseEvent};
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
@ -8,9 +9,9 @@ use tui::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_farmer::DataCollection, text_table::SimpleColumn, time_graph::TimeGraphData,
|
data_farmer::DataCollection, event::ComponentEventResult, text_table::SimpleColumn,
|
||||||
widgets::tui_stuff::BlockBuilder, AppConfigFields, AxisScaling, Component, TextTable,
|
time_graph::TimeGraphData, widgets::tui_stuff::BlockBuilder, AppConfigFields, AxisScaling,
|
||||||
TimeGraph, Widget,
|
Component, TextTable, TimeGraph, Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
data_conversion::convert_network_data_points,
|
data_conversion::convert_network_data_points,
|
||||||
@ -497,15 +498,11 @@ impl Component for NetGraph {
|
|||||||
self.bounds = new_bounds;
|
self.bounds = new_bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(
|
fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
|
||||||
&mut self, event: crossterm::event::KeyEvent,
|
|
||||||
) -> crate::app::event::WidgetEventResult {
|
|
||||||
self.graph.handle_key_event(event)
|
self.graph.handle_key_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_mouse_event(
|
fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
|
||||||
&mut self, event: crossterm::event::MouseEvent,
|
|
||||||
) -> crate::app::event::WidgetEventResult {
|
|
||||||
self.graph.handle_mouse_event(event)
|
self.graph.handle_mouse_event(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,7 +519,7 @@ impl Widget for NetGraph {
|
|||||||
let block = self
|
let block = self
|
||||||
.block()
|
.block()
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.expanded(expanded)
|
.show_esc(expanded)
|
||||||
.build(painter, area);
|
.build(painter, area);
|
||||||
|
|
||||||
self.set_draw_cache();
|
self.set_draw_cache();
|
||||||
@ -644,15 +641,11 @@ impl Component for OldNetGraph {
|
|||||||
self.bounds = new_bounds;
|
self.bounds = new_bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(
|
fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
|
||||||
&mut self, event: crossterm::event::KeyEvent,
|
|
||||||
) -> crate::app::event::WidgetEventResult {
|
|
||||||
self.net_graph.handle_key_event(event)
|
self.net_graph.handle_key_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_mouse_event(
|
fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult {
|
||||||
&mut self, event: crossterm::event::MouseEvent,
|
|
||||||
) -> crate::app::event::WidgetEventResult {
|
|
||||||
self.net_graph.handle_mouse_event(event)
|
self.net_graph.handle_mouse_event(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use tui::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_harvester::processes::ProcessHarvest,
|
data_harvester::processes::ProcessHarvest,
|
||||||
event::{MultiKey, MultiKeyResult, ReturnSignal, SelectionAction, WidgetEventResult},
|
event::{ComponentEventResult, MultiKey, MultiKeyResult, ReturnSignal, SelectionAction},
|
||||||
query::*,
|
query::*,
|
||||||
text_table::DesiredColumnWidth,
|
text_table::DesiredColumnWidth,
|
||||||
widgets::tui_stuff::BlockBuilder,
|
widgets::tui_stuff::BlockBuilder,
|
||||||
@ -869,27 +869,27 @@ impl ProcessManager {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_search(&mut self) -> WidgetEventResult {
|
fn open_search(&mut self) -> ComponentEventResult {
|
||||||
if let ProcessManagerSelection::Search = self.selected {
|
if let ProcessManagerSelection::Search = self.selected {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
self.show_search = true;
|
self.show_search = true;
|
||||||
self.prev_selected = self.selected;
|
self.prev_selected = self.selected;
|
||||||
self.selected = ProcessManagerSelection::Search;
|
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 {
|
if let ProcessManagerSelection::Sort = self.selected {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::NoRedraw
|
||||||
} else {
|
} else {
|
||||||
self.sort_menu
|
self.sort_menu
|
||||||
.set_index(self.process_table.current_sorting_column_index());
|
.set_index(self.process_table.current_sorting_column_index());
|
||||||
self.show_sort = true;
|
self.show_sort = true;
|
||||||
self.prev_selected = self.selected;
|
self.prev_selected = self.selected;
|
||||||
self.selected = ProcessManagerSelection::Sort;
|
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() {
|
if self.is_using_command() {
|
||||||
self.process_table
|
self.process_table
|
||||||
.set_column(ProcessSortColumn::new(ProcessSortType::Name), 1);
|
.set_column(ProcessSortColumn::new(ProcessSortType::Name), 1);
|
||||||
@ -929,7 +929,7 @@ impl ProcessManager {
|
|||||||
// Invalidate row cache.
|
// Invalidate row cache.
|
||||||
self.process_table.invalidate_cached_columns();
|
self.process_table.invalidate_cached_columns();
|
||||||
|
|
||||||
WidgetEventResult::Signal(ReturnSignal::Update)
|
ComponentEventResult::Signal(ReturnSignal::Update)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_grouped(&self) -> bool {
|
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() {
|
if self.is_grouped() {
|
||||||
self.process_table
|
self.process_table
|
||||||
.set_column(ProcessSortColumn::new(ProcessSortType::Pid), 0);
|
.set_column(ProcessSortColumn::new(ProcessSortType::Pid), 0);
|
||||||
@ -965,7 +965,7 @@ impl ProcessManager {
|
|||||||
// Invalidate row cache.
|
// Invalidate row cache.
|
||||||
self.process_table.invalidate_cached_columns();
|
self.process_table.invalidate_cached_columns();
|
||||||
|
|
||||||
WidgetEventResult::Signal(ReturnSignal::Update)
|
ComponentEventResult::Signal(ReturnSignal::Update)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hide_sort(&mut self) {
|
fn hide_sort(&mut self) {
|
||||||
@ -994,7 +994,7 @@ impl Component for ProcessManager {
|
|||||||
self.bounds = new_bounds;
|
self.bounds = new_bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult {
|
fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
|
||||||
// "Global" handling:
|
// "Global" handling:
|
||||||
|
|
||||||
if let KeyCode::Esc = event.code {
|
if let KeyCode::Esc = event.code {
|
||||||
@ -1002,19 +1002,19 @@ impl Component for ProcessManager {
|
|||||||
ProcessManagerSelection::Processes => {
|
ProcessManagerSelection::Processes => {
|
||||||
if self.show_sort {
|
if self.show_sort {
|
||||||
self.hide_sort();
|
self.hide_sort();
|
||||||
return WidgetEventResult::Redraw;
|
return ComponentEventResult::Redraw;
|
||||||
} else if self.show_search {
|
} else if self.show_search {
|
||||||
self.hide_search();
|
self.hide_search();
|
||||||
return WidgetEventResult::Redraw;
|
return ComponentEventResult::Redraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProcessManagerSelection::Sort if self.show_sort => {
|
ProcessManagerSelection::Sort if self.show_sort => {
|
||||||
self.hide_sort();
|
self.hide_sort();
|
||||||
return WidgetEventResult::Redraw;
|
return ComponentEventResult::Redraw;
|
||||||
}
|
}
|
||||||
ProcessManagerSelection::Search if self.show_search => {
|
ProcessManagerSelection::Search if self.show_search => {
|
||||||
self.hide_search();
|
self.hide_search();
|
||||||
return WidgetEventResult::Redraw;
|
return ComponentEventResult::Redraw;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -1039,7 +1039,7 @@ impl Component for ProcessManager {
|
|||||||
// Kill the selected process(es)
|
// Kill the selected process(es)
|
||||||
}
|
}
|
||||||
MultiKeyResult::Accepted | MultiKeyResult::Rejected => {
|
MultiKeyResult::Accepted | MultiKeyResult::Rejected => {
|
||||||
return WidgetEventResult::NoRedraw;
|
return ComponentEventResult::NoRedraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1057,7 +1057,7 @@ impl Component for ProcessManager {
|
|||||||
}
|
}
|
||||||
KeyCode::Char('t') | KeyCode::F(5) => {
|
KeyCode::Char('t') | KeyCode::F(5) => {
|
||||||
self.in_tree_mode = !self.in_tree_mode;
|
self.in_tree_mode = !self.in_tree_mode;
|
||||||
return WidgetEventResult::Redraw;
|
return ComponentEventResult::Redraw;
|
||||||
}
|
}
|
||||||
KeyCode::Char('s') | KeyCode::F(6) => {
|
KeyCode::Char('s') | KeyCode::F(6) => {
|
||||||
return self.open_sort();
|
return self.open_sort();
|
||||||
@ -1086,7 +1086,7 @@ impl Component for ProcessManager {
|
|||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
self.process_table
|
self.process_table
|
||||||
.set_sort_index(self.sort_menu.current_index());
|
.set_sort_index(self.sort_menu.current_index());
|
||||||
return WidgetEventResult::Signal(ReturnSignal::Update);
|
return ComponentEventResult::Signal(ReturnSignal::Update);
|
||||||
}
|
}
|
||||||
KeyCode::Char('/') => {
|
KeyCode::Char('/') => {
|
||||||
return self.open_search();
|
return self.open_search();
|
||||||
@ -1115,7 +1115,7 @@ impl Component for ProcessManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let handle_output = self.search_input.handle_key_event(event);
|
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.process_filter = Some(parse_query(
|
||||||
self.search_input.query(),
|
self.search_input.query(),
|
||||||
self.is_searching_whole_word(),
|
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 {
|
match &event.kind {
|
||||||
MouseEventKind::Down(MouseButton::Left) => {
|
MouseEventKind::Down(MouseButton::Left) => {
|
||||||
if self.process_table.does_border_intersect_mouse(&event) {
|
if self.process_table.does_border_intersect_mouse(&event) {
|
||||||
@ -1139,11 +1139,10 @@ impl Component for ProcessManager {
|
|||||||
self.prev_selected = self.selected;
|
self.prev_selected = self.selected;
|
||||||
self.selected = ProcessManagerSelection::Processes;
|
self.selected = ProcessManagerSelection::Processes;
|
||||||
match self.process_table.handle_mouse_event(event) {
|
match self.process_table.handle_mouse_event(event) {
|
||||||
WidgetEventResult::Quit => WidgetEventResult::Quit,
|
ComponentEventResult::Unhandled
|
||||||
WidgetEventResult::Redraw | WidgetEventResult::NoRedraw => {
|
| ComponentEventResult::Redraw
|
||||||
WidgetEventResult::Redraw
|
| ComponentEventResult::NoRedraw => ComponentEventResult::Redraw,
|
||||||
}
|
ComponentEventResult::Signal(s) => ComponentEventResult::Signal(s),
|
||||||
WidgetEventResult::Signal(s) => WidgetEventResult::Signal(s),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.sort_menu.does_border_intersect_mouse(&event) {
|
} else if self.sort_menu.does_border_intersect_mouse(&event) {
|
||||||
@ -1153,7 +1152,7 @@ impl Component for ProcessManager {
|
|||||||
self.prev_selected = self.selected;
|
self.prev_selected = self.selected;
|
||||||
self.selected = ProcessManagerSelection::Sort;
|
self.selected = ProcessManagerSelection::Sort;
|
||||||
self.sort_menu.handle_mouse_event(event);
|
self.sort_menu.handle_mouse_event(event);
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
} else if does_bound_intersect_coordinate(
|
} else if does_bound_intersect_coordinate(
|
||||||
event.column,
|
event.column,
|
||||||
@ -1166,10 +1165,10 @@ impl Component for ProcessManager {
|
|||||||
self.prev_selected = self.selected;
|
self.prev_selected = self.selected;
|
||||||
self.selected = ProcessManagerSelection::Search;
|
self.selected = ProcessManagerSelection::Search;
|
||||||
self.search_input.handle_mouse_event(event);
|
self.search_input.handle_mouse_event(event);
|
||||||
WidgetEventResult::Redraw
|
ComponentEventResult::Redraw
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
WidgetEventResult::NoRedraw
|
ComponentEventResult::Unhandled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => match self.selected {
|
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::Sort => self.sort_menu.handle_mouse_event(event),
|
||||||
ProcessManagerSelection::Search => self.search_input.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] = [
|
let search_constraints: [Constraint; 2] = [
|
||||||
Constraint::Min(0),
|
Constraint::Min(0),
|
||||||
if self.block_border.contains(Borders::TOP) {
|
if self.block_border.contains(Borders::TOP) {
|
||||||
Constraint::Length(4)
|
Constraint::Length(5)
|
||||||
} else {
|
} else {
|
||||||
Constraint::Length(2)
|
Constraint::Length(3)
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const INTERNAL_SEARCH_CONSTRAINTS: [Constraint; 2] = [Constraint::Length(1); 2];
|
const INTERNAL_SEARCH_CONSTRAINTS: [Constraint; 2] = [Constraint::Length(1); 2];
|
||||||
@ -1277,7 +1276,7 @@ impl Widget for ProcessManager {
|
|||||||
.block()
|
.block()
|
||||||
.selected(process_selected)
|
.selected(process_selected)
|
||||||
.borders(self.block_border)
|
.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(
|
self.process_table.draw_tui_table(
|
||||||
painter,
|
painter,
|
||||||
|
@ -6,8 +6,9 @@ use tui::{backend::Backend, layout::Rect, widgets::Borders, Frame};
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_farmer::DataCollection, data_harvester::temperature::TemperatureType,
|
data_farmer::DataCollection, data_harvester::temperature::TemperatureType,
|
||||||
event::WidgetEventResult, sort_text_table::SimpleSortableColumn, text_table::TextTableData,
|
event::ComponentEventResult, sort_text_table::SimpleSortableColumn,
|
||||||
AppScrollWidgetState, CanvasTableWidthState, Component, TextTable, Widget,
|
text_table::TextTableData, AppScrollWidgetState, CanvasTableWidthState, Component,
|
||||||
|
TextTable, Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
data_conversion::convert_temp_row,
|
data_conversion::convert_temp_row,
|
||||||
@ -116,11 +117,11 @@ impl TempTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Component for 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)
|
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)
|
self.table.handle_mouse_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ impl Widget for TempTable {
|
|||||||
.block()
|
.block()
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.borders(self.block_border)
|
.borders(self.block_border)
|
||||||
.expanded(expanded);
|
.show_esc(expanded);
|
||||||
|
|
||||||
self.table.draw_tui_table(
|
self.table.draw_tui_table(
|
||||||
painter,
|
painter,
|
||||||
|
2
src/app/widgets/dialogs.rs
Normal file
2
src/app/widgets/dialogs.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod help;
|
||||||
|
pub use help::HelpDialog;
|
304
src/app/widgets/dialogs/help.rs
Normal file
304
src/app/widgets/dialogs/help.rs
Normal file
@ -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<Vec<Spans<'static>>>,
|
||||||
|
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<u32, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<usize> = 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::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
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<B: Backend>(
|
||||||
|
&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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ use crate::canvas::Painter;
|
|||||||
pub struct BlockBuilder {
|
pub struct BlockBuilder {
|
||||||
borders: Borders,
|
borders: Borders,
|
||||||
selected: bool,
|
selected: bool,
|
||||||
expanded: bool,
|
show_esc: bool,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
hide_title: bool,
|
hide_title: bool,
|
||||||
extra_text: Option<String>,
|
extra_text: Option<String>,
|
||||||
@ -22,7 +22,7 @@ impl BlockBuilder {
|
|||||||
Self {
|
Self {
|
||||||
borders: Borders::ALL,
|
borders: Borders::ALL,
|
||||||
selected: false,
|
selected: false,
|
||||||
expanded: false,
|
show_esc: false,
|
||||||
name,
|
name,
|
||||||
hide_title: false,
|
hide_title: false,
|
||||||
extra_text: None,
|
extra_text: None,
|
||||||
@ -35,9 +35,9 @@ impl BlockBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates that this block is currently expanded, and should be drawn as such.
|
/// Indicates that this block should show esc, and should be drawn as such.
|
||||||
pub fn expanded(mut self, expanded: bool) -> Self {
|
pub fn show_esc(mut self, show_esc: bool) -> Self {
|
||||||
self.expanded = expanded;
|
self.show_esc = show_esc;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,12 +64,14 @@ impl BlockBuilder {
|
|||||||
let has_title = !self.hide_title
|
let has_title = !self.hide_title
|
||||||
&& (self.borders.contains(Borders::TOP) || self.borders.contains(Borders::BOTTOM));
|
&& (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()
|
let block = Block::default()
|
||||||
.border_style(if self.selected {
|
.border_style(border_style)
|
||||||
painter.colours.highlighted_border_style
|
|
||||||
} else {
|
|
||||||
painter.colours.border_style
|
|
||||||
})
|
|
||||||
.borders(self.borders);
|
.borders(self.borders);
|
||||||
|
|
||||||
let inner_width = block.inner(area).width as usize;
|
let inner_width = block.inner(area).width as usize;
|
||||||
@ -82,12 +84,11 @@ impl BlockBuilder {
|
|||||||
let mut title_len = name.width();
|
let mut title_len = name.width();
|
||||||
let mut title = vec![name, Span::from(""), Span::from(""), Span::from("")];
|
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: &str = " Esc to go back ";
|
||||||
const EXPAND_TEXT_LEN: usize = EXPAND_TEXT.len();
|
const EXPAND_TEXT_LEN: usize = EXPAND_TEXT.len();
|
||||||
|
|
||||||
let expand_span =
|
let expand_span = Span::styled(EXPAND_TEXT, border_style);
|
||||||
Span::styled(EXPAND_TEXT, painter.colours.highlighted_border_style);
|
|
||||||
|
|
||||||
if title_len + EXPAND_TEXT_LEN <= inner_width {
|
if title_len + EXPAND_TEXT_LEN <= inner_width {
|
||||||
title_len += EXPAND_TEXT_LEN;
|
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);
|
let difference = inner_width.saturating_sub(title_len);
|
||||||
title[2] = Span::styled(
|
title[2] = Span::styled("─".repeat(difference), border_style);
|
||||||
"─".repeat(difference),
|
|
||||||
if self.selected {
|
|
||||||
painter.colours.highlighted_border_style
|
|
||||||
} else {
|
|
||||||
painter.colours.border_style
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
block.title(title)
|
block.title(title)
|
||||||
|
@ -50,7 +50,7 @@ fn main() -> Result<()> {
|
|||||||
let thread_termination_cvar = Arc::new(Condvar::new());
|
let thread_termination_cvar = Arc::new(Condvar::new());
|
||||||
|
|
||||||
// Set up input handling
|
// 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());
|
let input_thread = create_input_thread(sender.clone(), thread_termination_lock.clone());
|
||||||
|
|
||||||
// Cleaning loop
|
// Cleaning loop
|
||||||
|
@ -5,7 +5,7 @@ use indextree::{Arena, NodeId};
|
|||||||
use tui::{
|
use tui::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
text::{Span, Spans},
|
text::Span,
|
||||||
widgets::Paragraph,
|
widgets::Paragraph,
|
||||||
Frame, Terminal,
|
Frame, Terminal,
|
||||||
};
|
};
|
||||||
@ -19,7 +19,7 @@ use crate::{
|
|||||||
layout_manager::{generate_layout, ColLayout, LayoutNode, RowLayout},
|
layout_manager::{generate_layout, ColLayout, LayoutNode, RowLayout},
|
||||||
text_table::TextTableData,
|
text_table::TextTableData,
|
||||||
widgets::{Component, Widget},
|
widgets::{Component, Widget},
|
||||||
TmpBottomWidget,
|
DialogState, TmpBottomWidget,
|
||||||
},
|
},
|
||||||
constants::*,
|
constants::*,
|
||||||
data_conversion::{ConvertedBatteryData, ConvertedCpuData, ConvertedProcessData},
|
data_conversion::{ConvertedBatteryData, ConvertedCpuData, ConvertedProcessData},
|
||||||
@ -92,14 +92,12 @@ impl FromStr for ColourScheme {
|
|||||||
/// Handles the canvas' state.
|
/// Handles the canvas' state.
|
||||||
pub struct Painter {
|
pub struct Painter {
|
||||||
pub colours: CanvasColours,
|
pub colours: CanvasColours,
|
||||||
styled_help_text: Vec<Spans<'static>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Painter {
|
impl Painter {
|
||||||
pub fn init(config: &Config, colour_scheme: ColourScheme) -> anyhow::Result<Self> {
|
pub fn init(config: &Config, colour_scheme: ColourScheme) -> anyhow::Result<Self> {
|
||||||
let mut painter = Painter {
|
let mut painter = Painter {
|
||||||
colours: CanvasColours::default(),
|
colours: CanvasColours::default(),
|
||||||
styled_help_text: Vec::default(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let ColourScheme::Custom = colour_scheme {
|
if let ColourScheme::Custom = colour_scheme {
|
||||||
@ -107,7 +105,6 @@ impl Painter {
|
|||||||
} else {
|
} else {
|
||||||
painter.generate_colour_scheme(colour_scheme)?;
|
painter.generate_colour_scheme(colour_scheme)?;
|
||||||
}
|
}
|
||||||
painter.complete_painter_init();
|
|
||||||
|
|
||||||
Ok(painter)
|
Ok(painter)
|
||||||
}
|
}
|
||||||
@ -153,43 +150,6 @@ impl Painter {
|
|||||||
Ok(())
|
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::<Vec<_>>(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Not required check but it runs only a few times... so whatever ig, prevents me from
|
|
||||||
// being dumb and leaving a help text section only one line long.
|
|
||||||
if section.len() > 1 {
|
|
||||||
styled_help_spans.push(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::<Vec<_>>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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<B: Backend>(&self, f: &mut Frame<'_, B>, draw_loc: Rect) {
|
fn draw_frozen_indicator<B: Backend>(&self, f: &mut Frame<'_, B>, draw_loc: Rect) {
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(Span::styled(
|
Paragraph::new(Span::styled(
|
||||||
@ -218,7 +178,7 @@ impl Painter {
|
|||||||
let terminal_height = draw_area.height;
|
let terminal_height = draw_area.height;
|
||||||
let terminal_width = draw_area.width;
|
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 gen_help_len = GENERAL_HELP_TEXT.len() as u16 + 3;
|
||||||
let border_len = terminal_height.saturating_sub(gen_help_len) / 2;
|
let border_len = terminal_height.saturating_sub(gen_help_len) / 2;
|
||||||
let vertical_dialog_chunk = Layout::default()
|
let vertical_dialog_chunk = Layout::default()
|
||||||
@ -248,7 +208,7 @@ impl Painter {
|
|||||||
})
|
})
|
||||||
.split(vertical_dialog_chunk[1]);
|
.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 {
|
} 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!
|
// 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
|
// The main problem right now is that I cannot properly calculate the height offset since
|
||||||
|
@ -1,5 +1,2 @@
|
|||||||
pub mod dd_dialog;
|
pub mod dd_dialog;
|
||||||
pub mod help_dialog;
|
|
||||||
|
|
||||||
pub use dd_dialog::KillDialog;
|
pub use dd_dialog::KillDialog;
|
||||||
pub use help_dialog::HelpDialog;
|
|
||||||
|
@ -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<B: Backend>(
|
|
||||||
&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<B: Backend>(
|
|
||||||
&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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
296
src/constants.rs
296
src/constants.rs
@ -227,159 +227,193 @@ pub static NORD_LIGHT_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(|| ConfigC
|
|||||||
|
|
||||||
// Help text
|
// Help text
|
||||||
pub const HELP_CONTENTS_TEXT: [&str; 8] = [
|
pub const HELP_CONTENTS_TEXT: [&str; 8] = [
|
||||||
"Press the corresponding numbers to jump to the section, or scroll:",
|
"Press the corresponding numbers to jump to that section, or just scroll down:",
|
||||||
"1 - General",
|
"[1] General",
|
||||||
"2 - CPU widget",
|
"[2] CPU widget",
|
||||||
"3 - Process widget",
|
"[3] Process widget",
|
||||||
"4 - Process search widget",
|
"[4] Process search widget",
|
||||||
"5 - Process sort widget",
|
"[5] Process sort widget",
|
||||||
"6 - Battery widget",
|
"[6] Battery widget",
|
||||||
"7 - Basic memory widget",
|
"[7] Basic memory widget",
|
||||||
];
|
];
|
||||||
|
|
||||||
// TODO [Help]: Search in help?
|
pub const GENERAL_HELP_TITLE: &str = "General";
|
||||||
// TODO [Help]: Move to using tables for easier formatting?
|
pub const GENERAL_HELP_TEXT: [[&str; 2]; 21] = [
|
||||||
pub const GENERAL_HELP_TEXT: [&str; 30] = [
|
["q, Ctrl-c", "Quit"],
|
||||||
"1 - General",
|
[
|
||||||
"q, Ctrl-c Quit",
|
"Esc",
|
||||||
"Esc Close dialog windows, search, widgets, or exit expanded mode",
|
"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-r", "Resets any collected data"],
|
||||||
"Ctrl-Left, ",
|
["f", "Toggles freezing, stopping new data from being shown"],
|
||||||
"Shift-Left, Move widget selection left",
|
["Ctrl-Left\nShift-Left\nH, A", "Move widget selection left"],
|
||||||
"H, A ",
|
[
|
||||||
"Ctrl-Right, ",
|
"Ctrl-Right\nShift-Right\nL, D",
|
||||||
"Shift-Right, Move widget selection right",
|
"Move widget selection right",
|
||||||
"L, D ",
|
],
|
||||||
"Ctrl-Up, ",
|
["Ctrl-Up\nShift-Up\nK, W", "Move widget selection up"],
|
||||||
"Shift-Up, Move widget selection up",
|
["Ctrl-Down\nShift-Dow\nJ, S", "Move widget selection down"],
|
||||||
"K, W ",
|
["Left, h", "Move left within widget"],
|
||||||
"Ctrl-Down, ",
|
["Down, j", "Move down within widget"],
|
||||||
"Shift-Down, Move widget selection down",
|
["Up, k", "Move up within widget"],
|
||||||
"J, S ",
|
["Right, l", "Move right within widget"],
|
||||||
"Left, h Move left within widget",
|
["?", "Open help menu"],
|
||||||
"Down, j Move down within widget",
|
["gg", "Jump to the first entry"],
|
||||||
"Up, k Move up within widget",
|
["G", "Jump to the last entry"],
|
||||||
"Right, l Move right within widget",
|
["e", "Toggle expanding the currently selected widget"],
|
||||||
"? Open help menu",
|
["+", "Zoom in on chart (decrease time range)"],
|
||||||
"gg Jump to the first entry",
|
["-", "Zoom out on chart (increase time range)"],
|
||||||
"G Jump to the last entry",
|
["=", "Reset zoom"],
|
||||||
"e Toggle expanding the currently selected widget",
|
[
|
||||||
"+ Zoom in on chart (decrease time range)",
|
"Mouse scroll",
|
||||||
"- Zoom out on chart (increase time range)",
|
"Scroll through the tables or zoom in/out of charts by scrolling up/down",
|
||||||
"= 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",
|
"Mouse click",
|
||||||
|
"Selects the clicked widget, table entry, dialog option, or tab",
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const CPU_HELP_TEXT: [&str; 2] = [
|
pub const CPU_HELP_TITLE: &str = "CPU Widget";
|
||||||
"2 - CPU widget\n",
|
pub const CPU_HELP_TEXT: [[&str; 2]; 1] = [[
|
||||||
"Mouse scroll Scrolling over an CPU core/average shows only that entry on the chart",
|
"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] = [
|
pub const SEARCH_TEXT_HELP_TITLE: &str = "Process Search";
|
||||||
"3 - Process widget",
|
pub const SEARCH_HELP_TEXT: [[&str; 2]; 48] = [
|
||||||
"dd, F9 Kill the selected process",
|
["Tab", "Toggle between searching for PID and name"],
|
||||||
"c Sort by CPU usage, press again to reverse sorting order",
|
["Esc", "Close the search widget (retains the filter)"],
|
||||||
"m Sort by memory usage, press again to reverse sorting order",
|
["Ctrl-a", "Skip to the start of the search query"],
|
||||||
"p Sort by PID name, press again to reverse sorting order",
|
["Ctrl-e", "Skip to the end of the search query"],
|
||||||
"n Sort by process name, press again to reverse sorting order",
|
["Ctrl-u", "Clear the current search query"],
|
||||||
"Tab Group/un-group processes with the same name",
|
["Ctrl-w", "Delete a word behind the cursor"],
|
||||||
"Ctrl-f, / Open process search widget",
|
["Ctrl-h", "Delete the character behind the cursor"],
|
||||||
"P Toggle between showing the full command or just the process name",
|
["Backspace", "Delete the character behind the cursor"],
|
||||||
"s, F6 Open process sort widget",
|
["Delete", "Delete the character at the cursor"],
|
||||||
"I Invert current sort",
|
["Alt-c, F1", "Toggle matching case"],
|
||||||
"% Toggle between values and percentages for memory usage",
|
["Alt-w, F2", "Toggle matching the entire word"],
|
||||||
"t, F5 Toggle tree mode",
|
["Alt-r, F3", "Toggle using regex"],
|
||||||
"+, -, click Collapse/expand a branch while in tree mode",
|
["Left, Alt-h", "Move cursor left"],
|
||||||
"click on header Sorts the entries by that column, click again to invert the sort",
|
["Right, Alt-l", "Move cursor right"],
|
||||||
|
["\n", "\n"],
|
||||||
|
["Supported search types:", ""],
|
||||||
|
["<by name/cmd>", "ex: btm"],
|
||||||
|
["pid", "ex: pid 825"],
|
||||||
|
["cpu, cpu%", "ex: cpu > 4.2"],
|
||||||
|
["mem, mem%", "ex: mem < 4.2"],
|
||||||
|
["memb", "ex: memb < 100 kb"],
|
||||||
|
["read, r/s", "ex: read >= 1 b"],
|
||||||
|
["write, w/s", "ex: write <= 1 tb"],
|
||||||
|
["tread, t.read", "ex: tread = 1"],
|
||||||
|
["twrite, t.write", "ex: twrite = 1"],
|
||||||
|
["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, &&, <Space> ", ": 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] = [
|
pub const PROCESS_SORT_HELP_TITLE: &str = "Process Sort";
|
||||||
"4 - Process search widget",
|
pub const PROCESS_SORT_HELP_TEXT: [[&str; 2]; 5] = [
|
||||||
"Tab Toggle between searching for PID and name",
|
["Down, 'j'", "Scroll down in list"],
|
||||||
"Esc Close the search widget (retains the filter)",
|
["Up, 'k'", "Scroll up in list"],
|
||||||
"Ctrl-a Skip to the start of the search query",
|
["Mouse scroll", "Scroll through sort widget"],
|
||||||
"Ctrl-e Skip to the end of the search query",
|
["Esc", "Close the sort widget"],
|
||||||
"Ctrl-u Clear the current search query",
|
["Enter", "Sort by current selected column"],
|
||||||
"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:",
|
|
||||||
"<by name/cmd> ex: btm",
|
|
||||||
"pid ex: pid 825",
|
|
||||||
"cpu, cpu% ex: cpu > 4.2",
|
|
||||||
"mem, mem% ex: mem < 4.2",
|
|
||||||
"memb ex: memb < 100 kb",
|
|
||||||
"read, r/s ex: read >= 1 b",
|
|
||||||
"write, w/s ex: write <= 1 tb",
|
|
||||||
"tread, t.read ex: tread = 1",
|
|
||||||
"twrite, t.write ex: twrite = 1",
|
|
||||||
"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, &&, <Space> ex: btm and cpu > 1 and mem > 1",
|
|
||||||
"or, || ex: btm or firefox",
|
|
||||||
"",
|
|
||||||
"Supported units:",
|
|
||||||
"B ex: read > 1 b",
|
|
||||||
"KB ex: read > 1 kb",
|
|
||||||
"MB ex: read > 1 mb",
|
|
||||||
"TB ex: read > 1 tb",
|
|
||||||
"KiB ex: read > 1 kib",
|
|
||||||
"MiB ex: read > 1 mib",
|
|
||||||
"GiB ex: read > 1 gib",
|
|
||||||
"TiB ex: read > 1 tib",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const SORT_HELP_TEXT: [&str; 6] = [
|
pub const BATTERY_HELP_TITLE: &str = "Battery Widget";
|
||||||
"5 - Sort widget\n",
|
pub const BATTERY_HELP_TEXT: [[&str; 2]; 2] = [
|
||||||
"Down, 'j' Scroll down in list",
|
["Left", "Go to previous battery"],
|
||||||
"Up, 'k' Scroll up in list",
|
["Right", "Go to next battery"],
|
||||||
"Mouse scroll Scroll through sort widget",
|
|
||||||
"Esc Close the sort widget",
|
|
||||||
"Enter Sort by current selected column",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const BATTERY_HELP_TEXT: [&str; 3] = [
|
pub const BASIC_MEM_HELP_TITLE: &str = "Basic Memory Widget";
|
||||||
"6 - Battery widget",
|
pub const BASIC_MEM_HELP_TEXT: [[&str; 2]; 1] = [[
|
||||||
"Left Go to previous battery",
|
"%",
|
||||||
"Right Go to next battery",
|
"Toggle between values and percentages for memory usage",
|
||||||
];
|
]];
|
||||||
|
|
||||||
pub const BASIC_MEM_HELP_TEXT: [&str; 2] = [
|
pub static HELP_TEXT: Lazy<[Vec<[&'static str; 2]>; 7]> = Lazy::new(|| {
|
||||||
"7 - Basic memory widget",
|
[
|
||||||
"% Toggle between values and percentages for memory usage",
|
|
||||||
];
|
|
||||||
|
|
||||||
pub static HELP_TEXT: Lazy<Vec<Vec<&'static str>>> = Lazy::new(|| {
|
|
||||||
vec![
|
|
||||||
HELP_CONTENTS_TEXT.to_vec(),
|
|
||||||
GENERAL_HELP_TEXT.to_vec(),
|
GENERAL_HELP_TEXT.to_vec(),
|
||||||
CPU_HELP_TEXT.to_vec(),
|
CPU_HELP_TEXT.to_vec(),
|
||||||
PROCESS_HELP_TEXT.to_vec(),
|
PROCESS_HELP_TEXT.to_vec(),
|
||||||
SEARCH_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(),
|
BATTERY_HELP_TEXT.to_vec(),
|
||||||
BASIC_MEM_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
|
// Default layouts
|
||||||
pub const DEFAULT_LAYOUT: &str = r##"
|
pub const DEFAULT_LAYOUT: &str = r##"
|
||||||
[[row]]
|
[[row]]
|
||||||
|
@ -98,7 +98,7 @@ pub fn handle_key_event(
|
|||||||
KeyCode::Left => app.on_left_key(),
|
KeyCode::Left => app.on_left_key(),
|
||||||
KeyCode::Right => app.on_right_key(),
|
KeyCode::Right => app.on_right_key(),
|
||||||
KeyCode::Char(caught_char) => app.on_char_key(caught_char),
|
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::Enter => app.on_enter(),
|
||||||
KeyCode::Tab => app.on_tab(),
|
KeyCode::Tab => app.on_tab(),
|
||||||
KeyCode::Backspace => app.on_backspace(),
|
KeyCode::Backspace => app.on_backspace(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user