diff --git a/src/app.rs b/src/app.rs index 2eb94efa..df20ac84 100644 --- a/src/app.rs +++ b/src/app.rs @@ -68,6 +68,7 @@ pub struct App { searching_pid: bool, pub use_simple: bool, current_regex: std::result::Result, + current_cursor_position: usize, } impl App { @@ -112,6 +113,7 @@ impl App { searching_pid: false, use_simple: false, current_regex: BASE_REGEX.clone(), //TODO: [OPT] seems like a thing we can switch to lifetimes to avoid cloning + current_cursor_position: 0, } } @@ -235,6 +237,10 @@ impl App { } } + pub fn get_cursor_position(&self) -> usize { + self.current_cursor_position + } + /// One of two functions allowed to run while in a dialog... pub fn on_enter(&mut self) { if self.show_dd { @@ -254,7 +260,18 @@ impl App { pub fn on_backspace(&mut self) { if let ApplicationPosition::ProcessSearch = self.current_application_position { - self.current_search_query.pop(); + if self.current_cursor_position > 0 { + self.current_cursor_position -= 1; + self.current_search_query + .remove(self.current_cursor_position); + + // TODO: [OPT] this runs even while in simple... consider making this only run if they toggle back to regex! + self.current_regex = if self.current_search_query.is_empty() { + BASE_REGEX.clone() + } else { + regex::Regex::new(&(self.current_search_query)) + }; + } } } @@ -262,6 +279,44 @@ impl App { &self.current_regex } + pub fn on_up_key(&mut self) { + if !self.is_in_dialog() { + if let ApplicationPosition::ProcessSearch = self.current_application_position { + } else { + self.decrement_position_count(); + } + } + } + + pub fn on_down_key(&mut self) { + if !self.is_in_dialog() { + if let ApplicationPosition::ProcessSearch = self.current_application_position { + } else { + self.increment_position_count(); + } + } + } + + pub fn on_left_key(&mut self) { + if !self.is_in_dialog() { + if let ApplicationPosition::ProcessSearch = self.current_application_position { + if self.current_cursor_position > 0 { + self.current_cursor_position -= 1; + } + } + } + } + + pub fn on_right_key(&mut self) { + if !self.is_in_dialog() { + if let ApplicationPosition::ProcessSearch = self.current_application_position { + if self.current_cursor_position < self.current_search_query.len() { + self.current_cursor_position += 1; + } + } + } + } + pub fn on_char_key(&mut self, caught_char: char) { // Forbid any char key presses when showing a dialog box... if !self.is_in_dialog() { @@ -275,8 +330,11 @@ impl App { self.last_key_press = current_key_press_inst; if let ApplicationPosition::ProcessSearch = self.current_application_position { - self.current_search_query.push(caught_char); + self.current_search_query + .insert(self.current_cursor_position, caught_char); + self.current_cursor_position += 1; + // TODO: [OPT] this runs even while in simple... consider making this only run if they toggle back to regex! self.current_regex = if self.current_search_query.is_empty() { BASE_REGEX.clone() } else { @@ -432,7 +490,7 @@ impl App { // Network -(up)> MEM, -(right)> PROC // PROC -(up)> Disk OR PROC_SEARCH if enabled, -(left)> Network // PROC_SEARCH -(up)> Disk, -(down)> PROC, -(left)> Network - pub fn on_left(&mut self) { + pub fn move_left(&mut self) { if !self.is_in_dialog() { self.current_application_position = match self.current_application_position { ApplicationPosition::Process => ApplicationPosition::Network, @@ -445,7 +503,7 @@ impl App { } } - pub fn on_right(&mut self) { + pub fn move_right(&mut self) { if !self.is_in_dialog() { self.current_application_position = match self.current_application_position { ApplicationPosition::Mem => ApplicationPosition::Temp, @@ -456,7 +514,7 @@ impl App { } } - pub fn on_up(&mut self) { + pub fn move_up(&mut self) { if !self.is_in_dialog() { self.current_application_position = match self.current_application_position { ApplicationPosition::Mem => ApplicationPosition::Cpu, @@ -477,7 +535,7 @@ impl App { } } - pub fn on_down(&mut self) { + pub fn move_down(&mut self) { if !self.is_in_dialog() { self.current_application_position = match self.current_application_position { ApplicationPosition::Cpu => ApplicationPosition::Mem, diff --git a/src/canvas.rs b/src/canvas.rs index 7a9f74f7..1d7a5fcc 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -900,7 +900,7 @@ fn draw_disk_table( fn draw_search_field( f: &mut Frame, app_state: &mut app::App, draw_loc: Rect, ) { - let width = draw_loc.width - 18; // TODO [SEARCH] this is hardcoded... ew + let width = draw_loc.width - 18; // TODO [SEARCH] this is hard-coded... ew let query = app_state.get_current_search_query(); let shrunk_query = if query.len() < width as usize { query @@ -908,7 +908,32 @@ fn draw_search_field( &query[(query.len() - width as usize)..] }; - let search_text = [ + // TODO: [SEARCH] Consider making this look prettier + let cursor_position = app_state.get_cursor_position(); + + // TODO: [SEARCH] This can be optimized... + let mut query_with_cursor: Vec = shrunk_query + .chars() + .enumerate() + .map(|(itx, c)| { + if itx == cursor_position { + Text::styled( + c.to_string(), + Style::default().fg(TEXT_COLOUR).bg(TABLE_HEADER_COLOUR), + ) + } else { + Text::styled(c.to_string(), Style::default().fg(TEXT_COLOUR)) + } + }) + .collect::>(); + if cursor_position >= query.len() { + query_with_cursor.push(Text::styled( + " ".to_string(), + Style::default().fg(TEXT_COLOUR).bg(TABLE_HEADER_COLOUR), + )) + } + + let mut search_text = vec![ if app_state.is_searching_with_pid() { Text::styled("\nPID", Style::default().fg(TABLE_HEADER_COLOUR)) } else { @@ -919,8 +944,10 @@ fn draw_search_field( } else { Text::styled(" (Regex): ", Style::default().fg(TABLE_HEADER_COLOUR)) }, - Text::raw(shrunk_query), ]; + + search_text.extend(query_with_cursor); + // TODO: [SEARCH] Gotta make this easier to understand... it's pretty ugly cramming controls like this Paragraph::new(search_text.iter()) .block( diff --git a/src/main.rs b/src/main.rs index 96a08bdc..bf0b89c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -227,8 +227,10 @@ fn main() -> error::Result<()> { match event.code { KeyCode::End => app.skip_to_last(), KeyCode::Home => app.skip_to_first(), - KeyCode::Up => app.decrement_position_count(), - KeyCode::Down => app.increment_position_count(), + KeyCode::Up => app.on_up_key(), + KeyCode::Down => app.on_down_key(), + KeyCode::Left => app.on_left_key(), + KeyCode::Right => app.on_right_key(), KeyCode::Char(character) => app.on_char_key(character), KeyCode::Esc => app.on_esc(), KeyCode::Enter => app.on_enter(), @@ -242,10 +244,10 @@ fn main() -> error::Result<()> { match event.code { KeyCode::Char('c') => break, KeyCode::Char('f') => app.toggle_searching(), // Note that this is fine for now, assuming '/' does not do anything other than search. - KeyCode::Left | KeyCode::Char('h') => app.on_left(), - KeyCode::Right | KeyCode::Char('l') => app.on_right(), - KeyCode::Up | KeyCode::Char('k') => app.on_up(), - KeyCode::Down | KeyCode::Char('j') => app.on_down(), + KeyCode::Left | KeyCode::Char('h') => app.move_left(), + KeyCode::Right | KeyCode::Char('l') => app.move_right(), + KeyCode::Up | KeyCode::Char('k') => app.move_up(), + KeyCode::Down | KeyCode::Char('j') => app.move_down(), KeyCode::Char('p') => app.search_with_pid(), KeyCode::Char('n') => app.search_with_name(), KeyCode::Char('r') => {