Merge pull request #120 from afonsofrancof:cleanup_search

Move search bar widget to a separate file
This commit is contained in:
Chris Titus 2024-08-15 14:51:53 -05:00 committed by GitHub
commit fe76cfcf51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 102 additions and 52 deletions

View File

@ -2,9 +2,11 @@ mod float;
mod floating_text; mod floating_text;
mod list; mod list;
mod running_command; mod running_command;
mod search;
pub mod state; pub mod state;
mod theme; mod theme;
use crate::search::SearchBar;
use std::{ use std::{
io::{self, stdout}, io::{self, stdout},
time::Duration, time::Duration,
@ -24,9 +26,6 @@ use list::CustomList;
use ratatui::{ use ratatui::{
backend::{Backend, CrosstermBackend}, backend::{Backend, CrosstermBackend},
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
style::{Color, Style},
text::Span,
widgets::{Block, Borders, Paragraph},
Terminal, Terminal,
}; };
use running_command::RunningCommand; use running_command::RunningCommand;
@ -79,13 +78,12 @@ fn main() -> std::io::Result<()> {
} }
fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<()> { fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<()> {
//Create the search field
let mut search_input = String::new();
//Create the command list //Create the command list
let mut custom_list = CustomList::new(); let mut custom_list = CustomList::new();
//Create the float to hold command output //Create the float to hold command output
let mut command_float = Float::new(60, 60); let mut command_float = Float::new(60, 60);
let mut in_search_mode = false; //Create the search bar
let mut search_bar = SearchBar::new();
loop { loop {
// Always redraw // Always redraw
@ -98,29 +96,8 @@ fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<(
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref()) .constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
.split(frame.size()); .split(frame.size());
//Set the search bar text (If empty use the placeholder) //Render the search bar
let display_text = if search_input.is_empty() { search_bar.draw(frame, chunks[0], state);
if in_search_mode {
Span::raw("")
} else {
Span::raw("Press / to search")
}
} else {
Span::raw(&search_input)
};
//Create the search bar widget
let mut search_bar = Paragraph::new(display_text)
.block(Block::default().borders(Borders::ALL).title("Search"))
.style(Style::default().fg(Color::DarkGray));
//Change the color if in search mode
if in_search_mode {
search_bar = search_bar.clone().style(Style::default().fg(Color::Blue));
}
//Render the search bar (First chunk of the screen)
frame.render_widget(search_bar, chunks[0]);
//Render the command list (Second chunk of the screen) //Render the command list (Second chunk of the screen)
custom_list.draw(frame, chunks[1], state); custom_list.draw(frame, chunks[1], state);
//Render the command float in the custom_list chunk //Render the command float in the custom_list chunk
@ -146,28 +123,14 @@ fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<(
//If that's the case, don't propagate input to other widgets //If that's the case, don't propagate input to other widgets
if !command_float.handle_key_event(&key) { if !command_float.handle_key_event(&key) {
//Insert user input into the search bar //Insert user input into the search bar
if in_search_mode { //Send the keys to the search bar
match key.code { if search_bar.is_search_active() {
KeyCode::Char(c) => { let search_query = search_bar.handle_key(key);
search_input.push(c); custom_list.reset_selection();
custom_list.filter(search_input.clone()); custom_list.filter(search_query);
} }
KeyCode::Backspace => { // Else, send them to the list
search_input.pop(); else if let Some(cmd) = custom_list.handle_key(key, state) {
custom_list.filter(search_input.clone());
}
KeyCode::Esc => {
search_input = String::new();
custom_list.filter(search_input.clone());
in_search_mode = false
}
KeyCode::Enter => {
in_search_mode = false;
custom_list.reset_selection();
}
_ => {}
}
} else if let Some(cmd) = custom_list.handle_key(key, state) {
command_float.set_content(Some(RunningCommand::new(cmd, state))); command_float.set_content(Some(RunningCommand::new(cmd, state)));
} else { } else {
// Handle keys while not in search mode // Handle keys while not in search mode
@ -176,7 +139,7 @@ fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<(
KeyCode::Char('q') => return Ok(()), KeyCode::Char('q') => return Ok(()),
//Activate search mode if the forward slash key gets pressed //Activate search mode if the forward slash key gets pressed
KeyCode::Char('/') => { KeyCode::Char('/') => {
in_search_mode = true; search_bar.activate_search();
continue; continue;
} }
_ => {} _ => {}

81
src/search.rs Normal file
View File

@ -0,0 +1,81 @@
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{
layout::Rect,
style::Style,
text::Span,
widgets::{Block, Borders, Paragraph},
Frame,
};
use crate::state::AppState;
pub struct SearchBar {
search_input: String,
in_search_mode: bool,
}
impl SearchBar {
pub fn new() -> Self {
SearchBar {
search_input: String::new(),
in_search_mode: false,
}
}
pub fn activate_search(&mut self) {
self.in_search_mode = true;
}
pub fn deactivate_search(&mut self) {
self.in_search_mode = false;
}
pub fn is_search_active(&self) -> bool {
self.in_search_mode
}
pub fn draw(&self, frame: &mut Frame, area: Rect, state: &AppState) {
//Set the search bar text (If empty use the placeholder)
let display_text = if !self.in_search_mode && self.search_input.is_empty() {
Span::raw("Press / to search")
} else {
Span::raw(&self.search_input)
};
//Create the search bar widget
let mut search_bar = Paragraph::new(display_text)
.block(Block::default().borders(Borders::ALL).title("Search"))
.style(Style::default().fg(state.theme.unfocused_color));
//Change the color if in search mode
if self.in_search_mode {
search_bar = search_bar
.clone()
.style(Style::default().fg(state.theme.focused_color));
}
//Render the search bar (First chunk of the screen)
frame.render_widget(search_bar, area);
}
pub fn handle_key(&mut self, event: KeyEvent) -> String {
//Insert user input into the search bar
match event.code {
KeyCode::Char(c) => {
self.search_input.push(c);
}
KeyCode::Backspace => {
self.search_input.pop();
}
KeyCode::Esc => {
self.search_input = String::new();
self.in_search_mode = false;
}
KeyCode::Enter => {
self.in_search_mode = false;
}
_ => {}
}
self.search_input.clone()
}
}

View File

@ -8,6 +8,8 @@ pub struct Theme {
pub cmd_icon: &'static str, pub cmd_icon: &'static str,
pub success_color: Color, pub success_color: Color,
pub fail_color: Color, pub fail_color: Color,
pub focused_color: Color,
pub unfocused_color: Color,
} }
pub const THEMES: [Theme; 2] = [ pub const THEMES: [Theme; 2] = [
@ -18,6 +20,8 @@ pub const THEMES: [Theme; 2] = [
cmd_icon: "[CMD]", cmd_icon: "[CMD]",
success_color: Color::Green, success_color: Color::Green,
fail_color: Color::Red, fail_color: Color::Red,
focused_color: Color::LightBlue,
unfocused_color: Color::Gray,
}, },
Theme { Theme {
dir_color: Color::Blue, dir_color: Color::Blue,
@ -26,5 +30,7 @@ pub const THEMES: [Theme; 2] = [
cmd_icon: "", cmd_icon: "",
fail_color: Color::Rgb(199, 55, 44), fail_color: Color::Rgb(199, 55, 44),
success_color: Color::Rgb(5, 255, 55), success_color: Color::Rgb(5, 255, 55),
focused_color: Color::LightBlue,
unfocused_color: Color::Gray,
}, },
]; ];