refactor: add process search conditions and error

This commit is contained in:
ClementTsang 2021-09-24 23:19:08 -04:00
parent 7ee85a82f7
commit abcca77c1d
5 changed files with 157 additions and 34 deletions

View File

@ -297,7 +297,7 @@ impl Component for TextInput {
} }
fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult { fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult {
if event.modifiers.is_empty() { if event.modifiers.is_empty() || event.modifiers == KeyModifiers::SHIFT {
match event.code { match event.code {
KeyCode::Left => { KeyCode::Left => {
let original_cursor = self.cursor.cur_cursor(); let original_cursor = self.cursor.cur_cursor();
@ -356,16 +356,17 @@ impl Component for TextInput {
} }
} }
fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult { 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 at some point after refactor // // TODO: Do this at some point after refactor
ComponentEventResult::Redraw // ComponentEventResult::Redraw
} else { // } else {
ComponentEventResult::NoRedraw // ComponentEventResult::NoRedraw
} // }
ComponentEventResult::Unhandled
} }
} }

View File

@ -165,7 +165,7 @@ impl TimeGraph {
'-' => self.zoom_out(), '-' => self.zoom_out(),
'+' => self.zoom_in(), '+' => self.zoom_in(),
'=' => self.reset_zoom(), '=' => self.reset_zoom(),
_ => ComponentEventResult::NoRedraw, _ => ComponentEventResult::Unhandled,
} }
} }

View File

@ -3,12 +3,14 @@ use std::{borrow::Cow, collections::HashMap};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
use float_ord::FloatOrd; use float_ord::FloatOrd;
use itertools::{Either, Itertools}; use itertools::{Either, Itertools};
use once_cell::unsync::Lazy;
use unicode_segmentation::GraphemeCursor; use unicode_segmentation::GraphemeCursor;
use tui::{ use tui::{
backend::Backend, backend::Backend,
layout::{Constraint, Direction, Layout, Rect}, layout::{Constraint, Direction, Layout, Rect},
widgets::{Borders, TableState}, text::{Span, Spans},
widgets::{Borders, Paragraph, TableState},
Frame, Frame,
}; };
@ -590,6 +592,20 @@ struct SearchModifiers {
enable_regex: bool, enable_regex: bool,
} }
impl SearchModifiers {
fn toggle_case_sensitive(&mut self) {
self.enable_case_sensitive = !self.enable_case_sensitive;
}
fn toggle_whole_word(&mut self) {
self.enable_whole_word = !self.enable_whole_word;
}
fn toggle_regex(&mut self) {
self.enable_regex = !self.enable_regex;
}
}
enum FlexColumn { enum FlexColumn {
Flex(f64), Flex(f64),
Hard(Option<u16>), Hard(Option<u16>),
@ -983,6 +999,24 @@ impl ProcessManager {
self.selected = ProcessManagerSelection::Processes; self.selected = ProcessManagerSelection::Processes;
} }
} }
/// Toggles the search case-sensitivity status for the [`ProcessManager`].
fn toggle_search_case_sensitive(&mut self) -> ComponentEventResult {
self.search_modifiers.toggle_case_sensitive();
ComponentEventResult::Signal(ReturnSignal::Update)
}
/// Toggle whether to search for the whole word for the [`ProcessManager`].
fn toggle_search_whole_word(&mut self) -> ComponentEventResult {
self.search_modifiers.toggle_whole_word();
ComponentEventResult::Signal(ReturnSignal::Update)
}
/// Toggle whether to search with regex for the [`ProcessManager`].
fn toggle_search_regex(&mut self) -> ComponentEventResult {
self.search_modifiers.toggle_regex();
ComponentEventResult::Signal(ReturnSignal::Update)
}
} }
impl Component for ProcessManager { impl Component for ProcessManager {
@ -1100,28 +1134,44 @@ impl Component for ProcessManager {
ProcessManagerSelection::Search => { ProcessManagerSelection::Search => {
if event.modifiers.is_empty() { if event.modifiers.is_empty() {
match event.code { match event.code {
KeyCode::F(1) => {} KeyCode::F(1) => {
KeyCode::F(2) => {} return self.toggle_search_case_sensitive();
KeyCode::F(3) => {} }
KeyCode::F(2) => {
return self.toggle_search_whole_word();
}
KeyCode::F(3) => {
return self.toggle_search_regex();
}
_ => {} _ => {}
} }
} else if let KeyModifiers::ALT = event.modifiers { } else if let KeyModifiers::ALT = event.modifiers {
match event.code { match event.code {
KeyCode::Char('c') | KeyCode::Char('C') => {} KeyCode::Char('c') | KeyCode::Char('C') => {
KeyCode::Char('w') | KeyCode::Char('W') => {} return self.toggle_search_case_sensitive();
KeyCode::Char('r') | KeyCode::Char('R') => {} }
KeyCode::Char('w') | KeyCode::Char('W') => {
return self.toggle_search_whole_word();
}
KeyCode::Char('r') | KeyCode::Char('R') => {
return self.toggle_search_regex();
}
_ => {} _ => {}
} }
} }
let handle_output = self.search_input.handle_key_event(event); let handle_output = self.search_input.handle_key_event(event);
if let ComponentEventResult::Signal(ReturnSignal::Update) = handle_output { if let ComponentEventResult::Signal(ReturnSignal::Update) = handle_output {
self.process_filter = Some(parse_query( if !self.search_input.query().is_empty() {
self.search_input.query(), self.process_filter = Some(parse_query(
self.is_searching_whole_word(), self.search_input.query(),
!self.is_case_sensitive(), self.is_searching_whole_word(),
self.is_searching_with_regex(), !self.is_case_sensitive(),
)); self.is_searching_with_regex(),
));
} else {
self.process_filter = None;
}
} }
handle_output handle_output
@ -1190,7 +1240,7 @@ impl Widget for ProcessManager {
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool, &mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool, expanded: bool,
) { ) {
let area = if self.show_search { let draw_area = if self.show_search {
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) {
@ -1199,7 +1249,7 @@ impl Widget for ProcessManager {
Constraint::Length(3) Constraint::Length(3)
}, },
]; ];
const INTERNAL_SEARCH_CONSTRAINTS: [Constraint; 2] = [Constraint::Length(1); 2]; const INTERNAL_SEARCH_CONSTRAINTS: [Constraint; 3] = [Constraint::Length(1); 3];
let vertical_split_area = Layout::default() let vertical_split_area = Layout::default()
.margin(0) .margin(0)
@ -1224,7 +1274,7 @@ impl Widget for ProcessManager {
.constraints(INTERNAL_SEARCH_CONSTRAINTS) .constraints(INTERNAL_SEARCH_CONSTRAINTS)
.split(search_block.inner(vertical_split_area[1])); .split(search_block.inner(vertical_split_area[1]));
if !internal_split_area.is_empty() { if internal_split_area[0].height > 0 {
self.search_input.draw_text_input( self.search_input.draw_text_input(
painter, painter,
f, f,
@ -1233,8 +1283,80 @@ impl Widget for ProcessManager {
); );
} }
if internal_split_area.len() == 2 { if internal_split_area[1].height > 0 {
// TODO: Draw buttons if let Some(Err(err)) = &self.process_filter {
f.render_widget(
Paragraph::new(tui::text::Span::styled(
err.to_string(),
painter.colours.invalid_query_style,
)),
internal_split_area[1],
);
}
}
if internal_split_area[2].height > 0 {
let case_text: Lazy<String> = Lazy::new(|| {
format!(
"Case({})",
if cfg!(target_os = "macos") {
"F1"
} else {
"Alt+C"
}
)
});
let whole_word_text: Lazy<String> = Lazy::new(|| {
format!(
"Whole({})",
if cfg!(target_os = "macos") {
"F2"
} else {
"Alt+W"
}
)
});
let regex_text: Lazy<String> = Lazy::new(|| {
format!(
"Regex({})",
if cfg!(target_os = "macos") {
"F3"
} else {
"Alt+R"
}
)
});
let case_style = if self.is_case_sensitive() {
painter.colours.currently_selected_text_style
} else {
painter.colours.text_style
};
let whole_word_style = if self.is_searching_whole_word() {
painter.colours.currently_selected_text_style
} else {
painter.colours.text_style
};
let regex_style = if self.is_searching_with_regex() {
painter.colours.currently_selected_text_style
} else {
painter.colours.text_style
};
f.render_widget(
Paragraph::new(Spans::from(vec![
Span::styled(&*case_text, case_style),
Span::raw(" "), // TODO: Smartly space it out in the future...
Span::styled(&*whole_word_text, whole_word_style),
Span::raw(" "),
Span::styled(&*regex_text, regex_style),
])),
internal_split_area[2],
)
} }
f.render_widget(search_block, vertical_split_area[1]); f.render_widget(search_block, vertical_split_area[1]);
@ -1244,14 +1366,14 @@ impl Widget for ProcessManager {
area area
}; };
let area = if self.show_sort { let draw_area = if self.show_sort {
const SORT_CONSTRAINTS: [Constraint; 2] = [Constraint::Length(10), Constraint::Min(0)]; const SORT_CONSTRAINTS: [Constraint; 2] = [Constraint::Length(10), Constraint::Min(0)];
let horizontal_split_area = Layout::default() let horizontal_split_area = Layout::default()
.margin(0) .margin(0)
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints(SORT_CONSTRAINTS) .constraints(SORT_CONSTRAINTS)
.split(area); .split(draw_area);
let sort_block = self let sort_block = self
.block() .block()
@ -1267,7 +1389,7 @@ impl Widget for ProcessManager {
horizontal_split_area[1] horizontal_split_area[1]
} else { } else {
area draw_area
}; };
let process_selected = let process_selected =
@ -1283,7 +1405,7 @@ impl Widget for ProcessManager {
f, f,
&self.display_data, &self.display_data,
process_block, process_block,
area, draw_area,
process_selected, process_selected,
self.show_scroll_index, self.show_scroll_index,
); );

View File

View File

@ -404,7 +404,7 @@ pub static HELP_TEXT: Lazy<[Vec<[&'static str; 2]>; 7]> = Lazy::new(|| {
] ]
}); });
pub const HELP_TITLES: [&'static str; 7] = [ pub const HELP_TITLES: [&str; 7] = [
GENERAL_HELP_TITLE, GENERAL_HELP_TITLE,
CPU_HELP_TITLE, CPU_HELP_TITLE,
PROCESS_HELP_TITLE, PROCESS_HELP_TITLE,