refactor: cover almost all keybinds except killing processes

This commit is contained in:
ClementTsang 2021-09-26 00:30:16 -04:00
parent abcca77c1d
commit 35ec66eaa7
12 changed files with 196 additions and 919 deletions

View File

@ -198,7 +198,6 @@ Note that key bindings are generally case-sensitive.
| ------------------------------------- | -------------------------------------------- |
| ++left++ <br/> ++h++ <br/> ++alt+h++ | Moves the cursor left |
| ++right++ <br/> ++l++ <br/> ++alt+l++ | Moves the cursor right |
| ++tab++ | Toggle between searching by PID or name |
| ++esc++ | Close the search widget (retains the filter) |
| ++ctrl+a++ | Skip to the start of the search query |
| ++ctrl+e++ | Skip to the end of the search query |

View File

@ -308,26 +308,36 @@ impl AppState {
/// Moves to a widget.
fn move_to_widget(&mut self, direction: MovementDirection) -> EventResult {
let layout_tree = &mut self.layout_tree;
let previous_selected = self.selected_widget;
if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
match move_widget_selection(layout_tree, widget, self.selected_widget, direction) {
MoveWidgetResult::ForceRedraw(new_widget_id) => {
self.selected_widget = new_widget_id;
EventResult::Redraw
}
MoveWidgetResult::NodeId(new_widget_id) => {
self.selected_widget = new_widget_id;
match if self.is_expanded {
move_expanded_widget_selection(
&mut self.widget_lookup_map,
self.selected_widget,
direction,
)
} else {
let layout_tree = &mut self.layout_tree;
if previous_selected != self.selected_widget {
EventResult::Redraw
} else {
EventResult::NoRedraw
}
move_widget_selection(
layout_tree,
&mut self.widget_lookup_map,
self.selected_widget,
direction,
)
} {
MoveWidgetResult::ForceRedraw(new_widget_id) => {
self.selected_widget = new_widget_id;
EventResult::Redraw
}
MoveWidgetResult::NodeId(new_widget_id) => {
let previous_selected = self.selected_widget;
self.selected_widget = new_widget_id;
if previous_selected != self.selected_widget {
EventResult::Redraw
} else {
EventResult::NoRedraw
}
}
} else {
EventResult::NoRedraw
}
}
@ -448,33 +458,29 @@ impl AppState {
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;
let was_id_already_selected = self.selected_widget == *id;
self.selected_widget = *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;
}
}
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 {
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;
}
}
}

View File

@ -44,8 +44,8 @@ pub enum ComponentEventResult {
/// How a widget should handle a widget selection request.
pub enum SelectionAction {
/// This event occurs if the widget internally handled the selection action. A redraw is required.
/// This occurs if the widget internally handled the selection action. A redraw is required.
Handled,
/// This event occurs if the widget did not handle the selection action; the caller must handle it.
/// This occurs if the widget did not handle the selection action; the caller must handle it.
NotHandled,
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
use std::time::Instant;
use std::{fmt::Debug, time::Instant};
use crossterm::event::{KeyEvent, MouseEvent};
use enum_dispatch::enum_dispatch;
use indextree::NodeId;
use tui::{backend::Backend, layout::Rect, widgets::TableState, Frame};
use crate::{
@ -160,7 +159,6 @@ pub trait Widget {
pub enum SelectableType {
Selectable,
Unselectable,
Redirect(NodeId),
}
/// The "main" widgets that are used by bottom to display information!
@ -182,6 +180,26 @@ pub enum TmpBottomWidget {
Empty,
}
impl Debug for TmpBottomWidget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MemGraph(_) => write!(f, "MemGraph"),
Self::TempTable(_) => write!(f, "TempTable"),
Self::DiskTable(_) => write!(f, "DiskTable"),
Self::CpuGraph(_) => write!(f, "CpuGraph"),
Self::NetGraph(_) => write!(f, "NetGraph"),
Self::OldNetGraph(_) => write!(f, "OldNetGraph"),
Self::ProcessManager(_) => write!(f, "ProcessManager"),
Self::BatteryTable(_) => write!(f, "BatteryTable"),
Self::BasicCpu(_) => write!(f, "BasicCpu"),
Self::BasicMem(_) => write!(f, "BasicMem"),
Self::BasicNet(_) => write!(f, "BasicNet"),
Self::Carousel(_) => write!(f, "Carousel"),
Self::Empty(_) => write!(f, "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.

View File

@ -285,6 +285,24 @@ impl TextInput {
area,
);
}
fn move_left(&mut self) -> ComponentEventResult {
let original_cursor = self.cursor.cur_cursor();
if self.move_back() == original_cursor {
ComponentEventResult::NoRedraw
} else {
ComponentEventResult::Redraw
}
}
fn move_right(&mut self) -> ComponentEventResult {
let original_cursor = self.cursor.cur_cursor();
if self.move_forward() == original_cursor {
ComponentEventResult::NoRedraw
} else {
ComponentEventResult::Redraw
}
}
}
impl Component for TextInput {
@ -299,22 +317,8 @@ impl Component for TextInput {
fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
if event.modifiers.is_empty() || event.modifiers == KeyModifiers::SHIFT {
match event.code {
KeyCode::Left => {
let original_cursor = self.cursor.cur_cursor();
if self.move_back() == original_cursor {
ComponentEventResult::NoRedraw
} else {
ComponentEventResult::Redraw
}
}
KeyCode::Right => {
let original_cursor = self.cursor.cur_cursor();
if self.move_forward() == original_cursor {
ComponentEventResult::NoRedraw
} else {
ComponentEventResult::Redraw
}
}
KeyCode::Left => self.move_left(),
KeyCode::Right => self.move_right(),
KeyCode::Backspace => self.clear_previous_grapheme(),
KeyCode::Delete => self.clear_current_grapheme(),
KeyCode::Char(c) => self.insert_character(c),
@ -349,6 +353,8 @@ impl Component for TextInput {
match event.code {
KeyCode::Char('b') => self.move_word_back(),
KeyCode::Char('f') => self.move_word_forward(),
KeyCode::Char('h') => self.move_left(),
KeyCode::Char('l') => self.move_right(),
_ => ComponentEventResult::Unhandled,
}
} else {

View File

@ -12,14 +12,15 @@ use tui::{
use crate::{
app::{
does_bound_intersect_coordinate, event::ComponentEventResult, Component, SelectableType,
Widget,
does_bound_intersect_coordinate,
event::{ComponentEventResult, SelectionAction},
Component, Widget,
},
canvas::Painter,
options::layout_options::LayoutRule,
};
/// A container that "holds"" multiple [`BottomWidget`]s through their [`NodeId`]s.
/// A container that "holds" multiple [`BottomWidget`]s through their [`NodeId`]s.
#[derive(PartialEq, Eq)]
pub struct Carousel {
index: usize,
@ -109,7 +110,7 @@ impl Carousel {
.direction(tui::layout::Direction::Vertical)
.split(area);
self.set_bounds(split_area[0]);
self.set_bounds(area);
if let Some((_prev_id, prev_element_name)) = self.get_prev() {
let prev_arrow_text = Spans::from(Span::styled(
@ -198,11 +199,15 @@ impl Widget for Carousel {
self.height
}
fn selectable_type(&self) -> SelectableType {
if let Some(node) = self.get_currently_selected() {
SelectableType::Redirect(node)
} else {
SelectableType::Unselectable
}
fn handle_widget_selection_left(&mut self) -> SelectionAction {
// Override to move to the left widget
self.decrement_index();
SelectionAction::Handled
}
fn handle_widget_selection_right(&mut self) -> SelectionAction {
// Override to move to the right widget
self.increment_index();
SelectionAction::Handled
}
}

View File

@ -1,7 +1,7 @@
use tui::layout::Rect;
use crate::{
app::{Component, Widget},
app::{Component, SelectableType, Widget},
options::layout_options::LayoutRule,
};
@ -35,8 +35,6 @@ impl Empty {
impl Component for Empty {
fn bounds(&self) -> Rect {
// TODO: Maybe think of how to store this without making it available for clicking. Separate bounds out to the layout? Might
// need to keep the bounds calculations for some components, so maybe implement it specifically for them.
Rect::default()
}
@ -55,4 +53,8 @@ impl Widget for Empty {
fn height(&self) -> LayoutRule {
self.height
}
fn selectable_type(&self) -> SelectableType {
SelectableType::Unselectable
}
}

View File

@ -984,6 +984,24 @@ impl ProcessManager {
ComponentEventResult::Signal(ReturnSignal::Update)
}
fn toggle_memory(&mut self) -> ComponentEventResult {
if matches!(
self.process_table.columns()[3].sort_type,
ProcessSortType::MemPercent
) {
self.process_table
.set_column(ProcessSortColumn::new(ProcessSortType::Mem), 3);
} else {
self.process_table
.set_column(ProcessSortColumn::new(ProcessSortType::MemPercent), 3);
}
// Invalidate row cache.
self.process_table.invalidate_cached_columns(); // TODO: This should be automatically called somehow after sets/removes to avoid forgetting it - maybe do a queue system?
ComponentEventResult::Signal(ReturnSignal::Update)
}
fn hide_sort(&mut self) {
self.show_sort = false;
if let ProcessManagerSelection::Sort = self.selected {
@ -1081,7 +1099,7 @@ impl Component for ProcessManager {
return self.open_search();
}
KeyCode::Char('%') => {
// Handle switching memory usage type
return self.toggle_memory();
}
KeyCode::Char('+') => {
// Expand a branch

View File

@ -215,6 +215,8 @@ impl Painter {
// line-wrapping is NOT the same as taking the width of the text and dividing by width.
// So, I need the height AFTER wrapping.
// See: https://github.com/fdehau/tui-rs/pull/349. Land this after this pushes to release.
//
// ADDENDUM: I could probably use the same textwrap trick I did with the help menu for this.
let dd_text = self.get_dd_spans(app_state);
@ -334,15 +336,6 @@ impl Painter {
);
if let Some(widget) = lookup_map.get_mut(&node) {
// debug!(
// "Original bound: {:?}, offset_x: {}, offset_y: {}, area: {:?}, widget: {}",
// bound,
// offset_x,
// offset_y,
// area,
// widget.get_pretty_name()
// );
if let TmpBottomWidget::Carousel(carousel) = widget {
let remaining_area: Rect =
carousel.draw_carousel(painter, f, area);
@ -357,7 +350,7 @@ impl Painter {
painter,
f,
remaining_area,
selected_id == to_draw_node,
selected_id == node,
false,
);
}

View File

@ -140,6 +140,7 @@ impl KillDialog for Painter {
} else {
#[cfg(target_family = "unix")]
{
// TODO: Can probably make this const.
let signal_text;
#[cfg(target_os = "linux")]
{

View File

@ -1,9 +1,6 @@
use crate::options::ConfigColours;
use once_cell::sync::Lazy;
// Default widget ID
pub const DEFAULT_WIDGET_ID: u64 = 56709;
// How long to store data.
pub const STALE_MAX_MILLISECONDS: u64 = 600 * 1000; // Keep 10 minutes of data.
@ -320,8 +317,7 @@ pub const PROCESS_HELP_TEXT: [[&str; 2]; 14] = [
];
pub const SEARCH_TEXT_HELP_TITLE: &str = "Process Search";
pub const SEARCH_HELP_TEXT: [[&str; 2]; 48] = [
["Tab", "Toggle between searching for PID and name"],
pub const SEARCH_HELP_TEXT: [[&str; 2]; 47] = [
["Esc", "Close the search widget (retains the filter)"],
["Ctrl-a", "Skip to the start of the search query"],
["Ctrl-e", "Skip to the end of the search query"],