mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-25 14:44:39 +02:00
Merge pull request #34 from ClementTsang/alternate_alt_macos
Colours if selected and F1-3 keys for search options
This commit is contained in:
commit
cdf464f323
@ -29,7 +29,6 @@ script:
|
|||||||
- cargo clippy -- -D clippy::all
|
- cargo clippy -- -D clippy::all
|
||||||
- cargo build --verbose --target $TARGET
|
- cargo build --verbose --target $TARGET
|
||||||
- cargo test --verbose --target $TARGET
|
- cargo test --verbose --target $TARGET
|
||||||
- cargo install --target $TARGET
|
|
||||||
|
|
||||||
# Need to cache the whole `.cargo` directory to keep .crates.toml for cargo-update to work
|
# Need to cache the whole `.cargo` directory to keep .crates.toml for cargo-update to work
|
||||||
cache:
|
cache:
|
||||||
@ -46,6 +45,7 @@ notifications:
|
|||||||
on_success: never
|
on_success: never
|
||||||
|
|
||||||
before_deploy:
|
before_deploy:
|
||||||
|
- cargo install --path . --target $TARGET
|
||||||
- |
|
- |
|
||||||
if [[ $TRAVIS_OS_NAME == "windows" ]]; then
|
if [[ $TRAVIS_OS_NAME == "windows" ]]; then
|
||||||
choco install zip;
|
choco install zip;
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
24
src/app.rs
24
src/app.rs
@ -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
|
||||||
|
@ -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(®ex_text, regex_style),
|
||||||
];
|
];
|
||||||
option_text.extend(option_row);
|
option_text.extend(option_row);
|
||||||
|
|
||||||
|
@ -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",
|
||||||
];
|
];
|
||||||
|
100
src/main.rs
100
src/main.rs
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user