mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 23:54:14 +02:00
refactor: add process search conditions and error
This commit is contained in:
parent
7ee85a82f7
commit
abcca77c1d
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
|
0
src/app/widgets/dialogs/process_kill.rs
Normal file
0
src/app/widgets/dialogs/process_kill.rs
Normal 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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user