Colours if selected and F1-3 keys for search options

Added different colours to search options if selected; added F1-3 keys as an alternative for searching.  Both are available, but on macOS F1-3 will be suggested instead.
This commit is contained in:
ClementTsang 2020-02-25 21:24:26 -05:00
parent 46b695d575
commit 0660184099
5 changed files with 133 additions and 69 deletions

View File

@ -184,11 +184,11 @@ Run using `btm`.
- `Left` and `Right` arrow keys to move the cursor within the search bar. - `Left` and `Right` arrow keys to move the cursor within the search bar.
- `Alt-c` to toggle ignoring case. - `Alt-c/F1` to toggle ignoring case.
- `Alt-m` to toggle matching the entire word. - `Alt-w/F2` to toggle matching the entire word.
- `Alt-r` to toggle using regex. - `Alt-r/F3` to toggle using regex.
Note that `q` is disabled while in the search widget. Note that `q` is disabled while in the search widget.

View File

@ -106,15 +106,15 @@ impl Default for ProcessSearchState {
} }
impl ProcessSearchState { impl ProcessSearchState {
pub fn toggle_ignore_case(&mut self) { pub fn search_toggle_ignore_case(&mut self) {
self.is_ignoring_case = !self.is_ignoring_case; self.is_ignoring_case = !self.is_ignoring_case;
} }
pub fn toggle_search_whole_word(&mut self) { pub fn search_toggle_whole_word(&mut self) {
self.is_searching_whole_word = !self.is_searching_whole_word; self.is_searching_whole_word = !self.is_searching_whole_word;
} }
pub fn toggle_search_regex(&mut self) { pub fn search_toggle_regex(&mut self) {
self.is_searching_with_regex = !self.is_searching_with_regex; self.is_searching_with_regex = !self.is_searching_with_regex;
} }
} }
@ -463,6 +463,24 @@ impl App {
&self.process_search_state.search_state.current_search_query &self.process_search_state.search_state.current_search_query
} }
pub fn toggle_ignore_case(&mut self) {
self.process_search_state.search_toggle_ignore_case();
self.update_regex();
self.update_process_gui = true;
}
pub fn toggle_search_whole_word(&mut self) {
self.process_search_state.search_toggle_whole_word();
self.update_regex();
self.update_process_gui = true;
}
pub fn toggle_search_regex(&mut self) {
self.process_search_state.search_toggle_regex();
self.update_regex();
self.update_process_gui = true;
}
pub fn update_regex(&mut self) { pub fn update_regex(&mut self) {
if self if self
.process_search_state .process_search_state

View File

@ -97,6 +97,7 @@ pub struct Painter {
pub styled_general_help_text: Vec<Text<'static>>, pub styled_general_help_text: Vec<Text<'static>>,
pub styled_process_help_text: Vec<Text<'static>>, pub styled_process_help_text: Vec<Text<'static>>,
pub styled_search_help_text: Vec<Text<'static>>, pub styled_search_help_text: Vec<Text<'static>>,
is_mac_os: bool,
} }
impl Painter { impl Painter {
@ -106,6 +107,8 @@ impl Painter {
/// assumes that you, the programmer, are sane and do not do stupid things. /// assumes that you, the programmer, are sane and do not do stupid things.
/// RIGHT? /// RIGHT?
pub fn initialize(&mut self) { pub fn initialize(&mut self) {
self.is_mac_os = cfg!(target_os = "macos");
self.styled_general_help_text.push(Text::Styled( self.styled_general_help_text.push(Text::Styled(
GENERAL_HELP_TEXT[0].into(), GENERAL_HELP_TEXT[0].into(),
self.colours.table_header_style, self.colours.table_header_style,
@ -1222,27 +1225,60 @@ impl Painter {
option_text.push(Text::raw("\n")); option_text.push(Text::raw("\n"));
} }
let option_row = vec![ let case_style = if !app_state.process_search_state.is_ignoring_case {
Text::styled("Match Case (Alt+C)", self.colours.table_header_style), self.colours.currently_selected_text_style
} else {
self.colours.text_style
};
let whole_word_style = if app_state.process_search_state.is_searching_whole_word {
self.colours.currently_selected_text_style
} else {
self.colours.text_style
};
let regex_style = if app_state.process_search_state.is_searching_with_regex {
self.colours.currently_selected_text_style
} else {
self.colours.text_style
};
let case_text = format!(
"Match Case ({})[{}]",
if self.is_mac_os { "F1" } else { "Alt+C" },
if !app_state.process_search_state.is_ignoring_case { if !app_state.process_search_state.is_ignoring_case {
Text::styled("[*]", self.colours.table_header_style) "*"
} else { } else {
Text::styled("[ ]", self.colours.table_header_style) " "
}, }
Text::styled(" ", self.colours.table_header_style), );
Text::styled("Match Whole Word (Alt+W)", self.colours.table_header_style),
let whole_text = format!(
"Match Whole Word ({})[{}]",
if self.is_mac_os { "F2" } else { "Alt+W" },
if app_state.process_search_state.is_searching_whole_word { if app_state.process_search_state.is_searching_whole_word {
Text::styled("[*]", self.colours.table_header_style) "*"
} else { } else {
Text::styled("[ ]", self.colours.table_header_style) " "
}, }
Text::styled(" ", self.colours.table_header_style), );
Text::styled("Use Regex (Alt+R)", self.colours.table_header_style),
let regex_text = format!(
"Use Regex ({})[{}]",
if self.is_mac_os { "F3" } else { "Alt+R" },
if app_state.process_search_state.is_searching_with_regex { if app_state.process_search_state.is_searching_with_regex {
Text::styled("[*]", self.colours.table_header_style) "*"
} else { } else {
Text::styled("[ ]", self.colours.table_header_style) " "
}, }
);
let option_row = vec![
Text::styled(&case_text, case_style),
Text::raw(" "),
Text::styled(&whole_text, whole_word_style),
Text::raw(" "),
Text::styled(&regex_text, regex_style),
]; ];
option_text.extend(option_row); option_text.extend(option_row);

View File

@ -50,7 +50,7 @@ pub const SEARCH_HELP_TEXT: [&str; 13] = [
"Delete Delete the character at the cursor\n", "Delete Delete the character at the cursor\n",
"Left Move cursor left\n", "Left Move cursor left\n",
"Right Move cursor right\n", "Right Move cursor right\n",
"Alt-c Toggle whether to ignore case\n", "Alt-c/F1 Toggle whether to ignore case\n",
"Alt-m Toggle whether to match the whole word\n", "Alt-w/F2 Toggle whether to match the whole word\n",
"Alt-r Toggle whether to use regex\n", "Alt-r/F3 Toggle whether to use regex\n",
]; ];

View File

@ -42,7 +42,10 @@ mod canvas;
mod constants; mod constants;
mod data_conversion; mod data_conversion;
use app::data_harvester::{self, processes::ProcessSorting}; use app::{
data_harvester::{self, processes::ProcessSorting},
App,
};
use constants::*; use constants::*;
use data_conversion::*; use data_conversion::*;
use utils::error::{self, BottomError}; use utils::error::{self, BottomError};
@ -155,7 +158,7 @@ fn main() -> error::Result<()> {
let show_disabled_data = get_show_disabled_data_option(&matches, &config); let show_disabled_data = get_show_disabled_data_option(&matches, &config);
// Create "app" struct, which will control most of the program and store settings/state // Create "app" struct, which will control most of the program and store settings/state
let mut app = app::App::new( let mut app = App::new(
show_average_cpu, show_average_cpu,
temperature_type, temperature_type,
update_rate_in_milliseconds as u64, update_rate_in_milliseconds as u64,
@ -301,7 +304,7 @@ fn main() -> error::Result<()> {
Ok(()) Ok(())
} }
fn handle_mouse_event(event: MouseEvent, app: &mut app::App) { fn handle_mouse_event(event: MouseEvent, app: &mut App) {
match event { match event {
MouseEvent::ScrollUp(_x, _y, _modifiers) => app.decrement_position_count(), MouseEvent::ScrollUp(_x, _y, _modifiers) => app.decrement_position_count(),
MouseEvent::ScrollDown(_x, _y, _modifiers) => app.increment_position_count(), MouseEvent::ScrollDown(_x, _y, _modifiers) => app.increment_position_count(),
@ -310,7 +313,7 @@ fn handle_mouse_event(event: MouseEvent, app: &mut app::App) {
} }
fn handle_key_event_or_break( fn handle_key_event_or_break(
event: KeyEvent, app: &mut app::App, rtx: &std::sync::mpsc::Sender<ResetEvent>, event: KeyEvent, app: &mut App, rtx: &std::sync::mpsc::Sender<ResetEvent>,
) -> bool { ) -> bool {
if event.modifiers.is_empty() { if event.modifiers.is_empty() {
// Required catch for searching - otherwise you couldn't search with q. // Required catch for searching - otherwise you couldn't search with q.
@ -331,11 +334,45 @@ fn handle_key_event_or_break(
KeyCode::Tab => app.on_tab(), KeyCode::Tab => app.on_tab(),
KeyCode::Backspace => app.on_backspace(), KeyCode::Backspace => app.on_backspace(),
KeyCode::Delete => app.on_delete(), KeyCode::Delete => app.on_delete(),
KeyCode::F(1) => {
if app.is_in_search_widget() {
app.toggle_ignore_case();
}
}
KeyCode::F(2) => {
if app.is_in_search_widget() {
app.toggle_search_whole_word();
}
}
KeyCode::F(3) => {
if app.is_in_search_widget() {
app.toggle_search_regex();
}
}
_ => {} _ => {}
} }
} else { } else {
// Otherwise, track the modifier as well... // Otherwise, track the modifier as well...
if let KeyModifiers::CONTROL = event.modifiers { if let KeyModifiers::ALT = event.modifiers {
match event.code {
KeyCode::Char('c') | KeyCode::Char('C') => {
if app.is_in_search_widget() {
app.toggle_ignore_case();
}
}
KeyCode::Char('w') | KeyCode::Char('W') => {
if app.is_in_search_widget() {
app.toggle_search_whole_word();
}
}
KeyCode::Char('r') | KeyCode::Char('R') => {
if app.is_in_search_widget() {
app.toggle_search_regex();
}
}
_ => {}
}
} else if let KeyModifiers::CONTROL = event.modifiers {
if event.code == KeyCode::Char('c') { if event.code == KeyCode::Char('c') {
return true; return true;
} }
@ -367,31 +404,6 @@ fn handle_key_event_or_break(
KeyCode::Char(caught_char) => app.on_char_key(caught_char), KeyCode::Char(caught_char) => app.on_char_key(caught_char),
_ => {} _ => {}
} }
} else if let KeyModifiers::ALT = event.modifiers {
match event.code {
KeyCode::Char('c') | KeyCode::Char('C') => {
if app.is_in_search_widget() {
app.process_search_state.toggle_ignore_case();
app.update_regex();
app.update_process_gui = true;
}
}
KeyCode::Char('w') | KeyCode::Char('W') => {
if app.is_in_search_widget() {
app.process_search_state.toggle_search_whole_word();
app.update_regex();
app.update_process_gui = true;
}
}
KeyCode::Char('r') | KeyCode::Char('R') => {
if app.is_in_search_widget() {
app.process_search_state.toggle_search_regex();
app.update_regex();
app.update_process_gui = true;
}
}
_ => {}
}
} }
} }
@ -554,7 +566,7 @@ fn get_show_disabled_data_option(matches: &clap::ArgMatches<'static>, config: &C
false false
} }
fn enable_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App) { fn enable_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
if matches.is_present("GROUP_PROCESSES") { if matches.is_present("GROUP_PROCESSES") {
app.toggle_grouping(); app.toggle_grouping();
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
@ -566,41 +578,39 @@ fn enable_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config, app
} }
} }
fn enable_app_case_sensitive( fn enable_app_case_sensitive(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App,
) {
if matches.is_present("CASE_SENSITIVE") { if matches.is_present("CASE_SENSITIVE") {
app.process_search_state.toggle_ignore_case(); app.process_search_state.search_toggle_ignore_case();
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
if let Some(case_sensitive) = flags.case_sensitive { if let Some(case_sensitive) = flags.case_sensitive {
if case_sensitive { if case_sensitive {
app.process_search_state.toggle_ignore_case(); app.process_search_state.search_toggle_ignore_case();
} }
} }
} }
} }
fn enable_app_match_whole_word( fn enable_app_match_whole_word(
matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App, matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App,
) { ) {
if matches.is_present("WHOLE_WORD") { if matches.is_present("WHOLE_WORD") {
app.process_search_state.toggle_search_whole_word(); app.process_search_state.search_toggle_whole_word();
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
if let Some(whole_word) = flags.whole_word { if let Some(whole_word) = flags.whole_word {
if whole_word { if whole_word {
app.process_search_state.toggle_search_whole_word(); app.process_search_state.search_toggle_whole_word();
} }
} }
} }
} }
fn enable_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut app::App) { fn enable_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config, app: &mut App) {
if matches.is_present("REGEX_DEFAULT") { if matches.is_present("REGEX_DEFAULT") {
app.process_search_state.toggle_search_regex(); app.process_search_state.search_toggle_regex();
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
if let Some(regex) = flags.regex { if let Some(regex) = flags.regex {
if regex { if regex {
app.process_search_state.toggle_search_regex(); app.process_search_state.search_toggle_regex();
} }
} }
} }
@ -650,7 +660,7 @@ fn get_default_widget(matches: &clap::ArgMatches<'static>, config: &Config) -> a
fn try_drawing( fn try_drawing(
terminal: &mut tui::terminal::Terminal<tui::backend::CrosstermBackend<std::io::Stdout>>, terminal: &mut tui::terminal::Terminal<tui::backend::CrosstermBackend<std::io::Stdout>>,
app: &mut app::App, painter: &mut canvas::Painter, app: &mut App, painter: &mut canvas::Painter,
) -> error::Result<()> { ) -> error::Result<()> {
if let Err(err) = painter.draw_data(terminal, app) { if let Err(err) = painter.draw_data(terminal, app) {
cleanup_terminal(terminal)?; cleanup_terminal(terminal)?;
@ -777,7 +787,7 @@ fn panic_hook(panic_info: &PanicInfo<'_>) {
.unwrap(); .unwrap();
} }
fn update_final_process_list(app: &mut app::App) { fn update_final_process_list(app: &mut App) {
let mut filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped() { let mut filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped() {
app.canvas_data app.canvas_data
.grouped_process_data .grouped_process_data
@ -841,7 +851,7 @@ fn update_final_process_list(app: &mut app::App) {
app.canvas_data.finalized_process_data = filtered_process_data; app.canvas_data.finalized_process_data = filtered_process_data;
} }
fn sort_process_data(to_sort_vec: &mut Vec<ConvertedProcessData>, app: &app::App) { fn sort_process_data(to_sort_vec: &mut Vec<ConvertedProcessData>, app: &App) {
to_sort_vec.sort_by(|a, b| utils::gen_util::get_ordering(&a.name, &b.name, false)); to_sort_vec.sort_by(|a, b| utils::gen_util::get_ordering(&a.name, &b.name, false));
match app.process_sorting_type { match app.process_sorting_type {