mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-22 05:12:27 +00:00
Merge pull request #102 from afonsofrancof/cleanup
Add the Search function.... again!
This commit is contained in:
commit
41078d5899
293
src/list.rs
293
src/list.rs
|
@ -9,6 +9,7 @@ use ratatui::{
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct ListNode {
|
struct ListNode {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
command: Command,
|
command: Command,
|
||||||
|
@ -28,6 +29,10 @@ pub struct CustomList {
|
||||||
/// This stores the preview windows state. If it is None, it will not be displayed.
|
/// This stores the preview windows state. If it is None, it will not be displayed.
|
||||||
/// If it is Some, we show it with the content of the selected item
|
/// If it is Some, we show it with the content of the selected item
|
||||||
preview_window_state: Option<PreviewWindowState>,
|
preview_window_state: Option<PreviewWindowState>,
|
||||||
|
// This stores the current search query
|
||||||
|
filter_query: String,
|
||||||
|
// This stores the filtered tree
|
||||||
|
filtered_items: Vec<ListNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct stores the preview window state
|
/// This struct stores the preview window state
|
||||||
|
@ -53,18 +58,6 @@ impl CustomList {
|
||||||
name: "root",
|
name: "root",
|
||||||
command: Command::None,
|
command: Command::None,
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
|
||||||
name: "Full System Update",
|
|
||||||
command: Command::LocalFile("system-update.sh"),
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Setup Bash Prompt",
|
|
||||||
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""),
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Setup Neovim",
|
|
||||||
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""),
|
|
||||||
},
|
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "System Setup",
|
name: "System Setup",
|
||||||
command: Command::None,
|
command: Command::None,
|
||||||
|
@ -84,52 +77,42 @@ impl CustomList {
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Security",
|
name: "Security",
|
||||||
command: ""
|
command: Command::None
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Firewall Baselines (CTT)",
|
name: "Firewall Baselines (CTT)",
|
||||||
command: with_common_script!("commands/security/firewall-baselines.sh"),
|
command: Command::LocalFile("security/firewall-baselines.sh"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Titus Dotfiles",
|
name: "Applications Setup",
|
||||||
command: Command::None
|
command: Command::None
|
||||||
} => {
|
} => {
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Alacritty Setup",
|
name: "Alacritty Setup",
|
||||||
command: Command::LocalFile("dotfiles/alacritty-setup.sh"),
|
command: Command::LocalFile("applications-setup/alacritty-setup.sh"),
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Bash Prompt Setup",
|
||||||
|
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Kitty Setup",
|
name: "Kitty Setup",
|
||||||
command: Command::LocalFile("dotfiles/kitty-setup.sh"),
|
command: Command::LocalFile("applications-setup/kitty-setup.sh")
|
||||||
|
},
|
||||||
|
ListNode {
|
||||||
|
name: "Neovim Setup",
|
||||||
|
command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""),
|
||||||
},
|
},
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Rofi Setup",
|
name: "Rofi Setup",
|
||||||
command: Command::LocalFile("dotfiles/rofi-setup.sh"),
|
command: Command::LocalFile("applications-setup/rofi-setup.sh"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ListNode {
|
ListNode {
|
||||||
name: "Testing category",
|
name: "Full System Update",
|
||||||
command: Command::None,
|
command: Command::LocalFile("system-update.sh"),
|
||||||
} => {
|
},
|
||||||
ListNode {
|
|
||||||
name: "Complex command",
|
|
||||||
command: Command::Raw("sleep 1 && ls -la && sleep 1 && ls -la && echo Bonus eza comming... && sleep 1 && ls -la"),
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Neovim",
|
|
||||||
command: Command::Raw("nvim"),
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Full bash",
|
|
||||||
command: Command::Raw("bash"),
|
|
||||||
},
|
|
||||||
ListNode {
|
|
||||||
name: "Running file with `source`",
|
|
||||||
command: Command::LocalFile("test/main.sh"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// We don't get a reference, but rather an id, because references are siginficantly more
|
// We don't get a reference, but rather an id, because references are siginficantly more
|
||||||
// paintfull to manage
|
// paintfull to manage
|
||||||
|
@ -140,46 +123,63 @@ impl CustomList {
|
||||||
list_state: ListState::default().with_selected(Some(0)),
|
list_state: ListState::default().with_selected(Some(0)),
|
||||||
// By default the PreviewWindowState is set to None, so it is not being shown
|
// By default the PreviewWindowState is set to None, so it is not being shown
|
||||||
preview_window_state: None,
|
preview_window_state: None,
|
||||||
|
filter_query: String::new(),
|
||||||
|
filtered_items: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw our custom widget to the frame
|
/// Draw our custom widget to the frame
|
||||||
pub fn draw(&mut self, frame: &mut Frame, area: Rect, state: &AppState) {
|
pub fn draw(&mut self, frame: &mut Frame, area: Rect, filter: String, state: &AppState) {
|
||||||
// Get the last element in the `visit_stack` vec
|
self.filter(filter);
|
||||||
let curr = self
|
|
||||||
.inner_tree
|
|
||||||
.get(*self.visit_stack.last().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
let mut items = vec![];
|
|
||||||
|
|
||||||
// If we are not at the root of our filesystem tree, we need to add `..` path, to be able
|
let item_list: Vec<Line> = if self.filter_query.is_empty() {
|
||||||
// to go up the tree
|
let mut items: Vec<Line> = vec![];
|
||||||
if !self.at_root() {
|
// If we are not at the root of our filesystem tree, we need to add `..` path, to be able
|
||||||
items.push(
|
// to go up the tree
|
||||||
Line::from(format!("{} ..", state.theme.dir_icon)).style(state.theme.dir_color),
|
// icons:
|
||||||
);
|
if !self.at_root() {
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through all the children
|
|
||||||
for node in curr.children() {
|
|
||||||
// The difference between a "directory" and a "command" is simple: if it has children,
|
|
||||||
// it's a directory and will be handled as such
|
|
||||||
if node.has_children() {
|
|
||||||
items.push(
|
items.push(
|
||||||
Line::from(format!("{} {}", state.theme.dir_icon, node.value().name))
|
Line::from(format!("{} ..", state.theme.dir_icon))
|
||||||
.style(state.theme.dir_color),
|
.style(state.theme.dir_color),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
items.push(
|
|
||||||
Line::from(format!("{} {}", state.theme.cmd_icon, node.value().name))
|
|
||||||
.style(state.theme.cmd_color),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Get the last element in the `visit_stack` vec
|
||||||
|
let curr = self
|
||||||
|
.inner_tree
|
||||||
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Iterate through all the children
|
||||||
|
for node in curr.children() {
|
||||||
|
// The difference between a "directory" and a "command" is simple: if it has children,
|
||||||
|
// it's a directory and will be handled as such
|
||||||
|
if node.has_children() {
|
||||||
|
items.push(
|
||||||
|
Line::from(format!("{} {}", state.theme.dir_icon, node.value().name))
|
||||||
|
.style(state.theme.dir_color),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
items.push(
|
||||||
|
Line::from(format!("{} {}", state.theme.cmd_icon, node.value().name))
|
||||||
|
.style(state.theme.cmd_color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items
|
||||||
|
} else {
|
||||||
|
self.filtered_items
|
||||||
|
.iter()
|
||||||
|
.map(|node| {
|
||||||
|
Line::from(format!("{} {}", state.theme.cmd_icon, node.name))
|
||||||
|
.style(state.theme.cmd_color)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
// create the normal list widget containing only item in our "working directory" / tree
|
// create the normal list widget containing only item in our "working directory" / tree
|
||||||
// node
|
// node
|
||||||
let list = List::new(items)
|
let list = List::new(item_list)
|
||||||
.highlight_style(Style::default().reversed())
|
.highlight_style(Style::default().reversed())
|
||||||
.block(Block::default().borders(Borders::ALL).title(format!(
|
.block(Block::default().borders(Borders::ALL).title(format!(
|
||||||
"Linux Toolbox - {}",
|
"Linux Toolbox - {}",
|
||||||
|
@ -218,6 +218,26 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn filter(&mut self, query: String) {
|
||||||
|
self.filter_query.clone_from(&query);
|
||||||
|
self.filtered_items.clear();
|
||||||
|
|
||||||
|
let query_lower = query.to_lowercase();
|
||||||
|
let mut stack = vec![self.inner_tree.root().id()];
|
||||||
|
|
||||||
|
while let Some(node_id) = stack.pop() {
|
||||||
|
let node = self.inner_tree.get(node_id).unwrap();
|
||||||
|
|
||||||
|
if node.value().name.to_lowercase().contains(&query_lower) && !node.has_children() {
|
||||||
|
self.filtered_items.push(node.value().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in node.children() {
|
||||||
|
stack.push(child.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle key events, we are only interested in `Press` and `Repeat` events
|
/// Handle key events, we are only interested in `Press` and `Repeat` events
|
||||||
pub fn handle_key(&mut self, event: KeyEvent, state: &AppState) -> Option<Command> {
|
pub fn handle_key(&mut self, event: KeyEvent, state: &AppState) -> Option<Command> {
|
||||||
if event.kind == KeyEventKind::Release {
|
if event.kind == KeyEventKind::Release {
|
||||||
|
@ -288,27 +308,35 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_scroll_up(&mut self) {
|
fn try_scroll_up(&mut self) {
|
||||||
self.list_state
|
self.list_state
|
||||||
.select(Some(self.list_state.selected().unwrap().saturating_sub(1)));
|
.select(Some(self.list_state.selected().unwrap().saturating_sub(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_scroll_down(&mut self) {
|
fn try_scroll_down(&mut self) {
|
||||||
let curr = self
|
let count = if self.filter_query.is_empty() {
|
||||||
.inner_tree
|
let curr = self
|
||||||
.get(*self.visit_stack.last().unwrap())
|
.inner_tree
|
||||||
.unwrap();
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
.unwrap();
|
||||||
let count = curr.children().count();
|
curr.children().count()
|
||||||
|
|
||||||
let curr_selection = self.list_state.selected().unwrap();
|
|
||||||
if self.at_root() {
|
|
||||||
self.list_state
|
|
||||||
.select(Some((curr_selection + 1).min(count - 1)));
|
|
||||||
} else {
|
} else {
|
||||||
// When we are not at the root, we have to account for 1 more "virtual" node, `..`. So
|
self.filtered_items.len()
|
||||||
// the count is 1 bigger (select is 0 based, because it's an index)
|
};
|
||||||
self.list_state
|
|
||||||
.select(Some((curr_selection + 1).min(count)));
|
if let Some(curr_selection) = self.list_state.selected() {
|
||||||
|
if self.at_root() {
|
||||||
|
self.list_state
|
||||||
|
.select(Some((curr_selection + 1).min(count - 1)));
|
||||||
|
} else {
|
||||||
|
// When we are not at the root, we have to account for 1 more "virtual" node, `..`. So
|
||||||
|
// the count is 1 bigger (select is 0 based, because it's an index)
|
||||||
|
self.list_state
|
||||||
|
.select(Some((curr_selection + 1).min(count)));
|
||||||
|
}
|
||||||
|
} else if count > 0 {
|
||||||
|
self.list_state.select(Some(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,29 +358,38 @@ impl CustomList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method return the currently selected command, or None if no command is selected.
|
/// This method returns the currently selected command, or None if no command is selected.
|
||||||
/// It was extracted from the 'handle_enter()'
|
/// It was extracted from the 'handle_enter()'
|
||||||
///
|
///
|
||||||
/// This could probably be integrated into the 'handle_enter()' method as to avoid code
|
/// This could probably be integrated into the 'handle_enter()' method to avoid code
|
||||||
/// duplication, but I don't want to make too major changes to the codebase.
|
/// duplication, but I don't want to make too major changes to the codebase.
|
||||||
fn get_selected_command(&self) -> Option<Command> {
|
fn get_selected_command(&self) -> Option<Command> {
|
||||||
let curr = self
|
|
||||||
.inner_tree
|
|
||||||
.get(*self.visit_stack.last().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
let selected = self.list_state.selected().unwrap();
|
let selected = self.list_state.selected().unwrap();
|
||||||
|
|
||||||
// If we are not at the root and the first item is selected, it's the `..` item
|
if self.filter_query.is_empty() {
|
||||||
if !self.at_root() && selected == 0 {
|
// No filter query, use the regular tree navigation
|
||||||
return None;
|
let curr = self
|
||||||
}
|
.inner_tree
|
||||||
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for (mut idx, node) in curr.children().enumerate() {
|
// If we are not at the root and the first item is selected, it's the `..` item
|
||||||
if !self.at_root() {
|
if !self.at_root() && selected == 0 {
|
||||||
idx += 1;
|
return None;
|
||||||
}
|
}
|
||||||
if idx == selected {
|
|
||||||
return Some(node.value().command.clone());
|
for (mut idx, node) in curr.children().enumerate() {
|
||||||
|
if !self.at_root() {
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
if idx == selected {
|
||||||
|
return Some(node.value().command.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Filter query is active, use the filtered items
|
||||||
|
if let Some(filtered_node) = self.filtered_items.get(selected) {
|
||||||
|
return Some(filtered_node.command.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -365,35 +402,49 @@ impl CustomList {
|
||||||
///
|
///
|
||||||
/// Returns `Some(command)` when command is selected, othervise we returns `None`
|
/// Returns `Some(command)` when command is selected, othervise we returns `None`
|
||||||
fn handle_enter(&mut self) -> Option<Command> {
|
fn handle_enter(&mut self) -> Option<Command> {
|
||||||
// Get the current node (current directory)
|
// Ensure an item is selected if none is selected
|
||||||
let curr = self
|
if self.list_state.selected().is_none() {
|
||||||
.inner_tree
|
|
||||||
.get(*self.visit_stack.last().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
let selected = self.list_state.selected().unwrap();
|
|
||||||
|
|
||||||
// if we are not at the root, and the first element is selected,
|
|
||||||
// we can be sure it's '..', so we go up the directory
|
|
||||||
if !self.at_root() && selected == 0 {
|
|
||||||
self.visit_stack.pop();
|
|
||||||
self.list_state.select(Some(0));
|
self.list_state.select(Some(0));
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (mut idx, node) in curr.children().enumerate() {
|
// Get the selected index
|
||||||
// at this point, we know that we are not on the .. item, and our indexes of the items never had ..
|
let selected = self.list_state.selected().unwrap();
|
||||||
// item. so to balance it out, in case the selection index contains .., se add 1 to our node index
|
|
||||||
if !self.at_root() {
|
if self.filter_query.is_empty() {
|
||||||
idx += 1;
|
// No filter query, use the regular tree navigation
|
||||||
|
let curr = self
|
||||||
|
.inner_tree
|
||||||
|
.get(*self.visit_stack.last().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// if we are not at the root, and the first element is selected,
|
||||||
|
// we can be sure it's '..', so we go up the directory
|
||||||
|
if !self.at_root() && selected == 0 {
|
||||||
|
self.visit_stack.pop();
|
||||||
|
self.list_state.select(Some(0));
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
if idx == selected {
|
|
||||||
if node.has_children() {
|
for (mut idx, node) in curr.children().enumerate() {
|
||||||
self.visit_stack.push(node.id());
|
// at this point, we know that we are not on the .. item, and our indexes of the items never had ..
|
||||||
self.list_state.select(Some(0));
|
// item. so to balance it out, in case the selection index contains .., se add 1 to our node index
|
||||||
return None;
|
if !self.at_root() {
|
||||||
} else {
|
idx += 1;
|
||||||
return Some(node.value().command.clone());
|
|
||||||
}
|
}
|
||||||
|
if idx == selected {
|
||||||
|
if node.has_children() {
|
||||||
|
self.visit_stack.push(node.id());
|
||||||
|
self.list_state.select(Some(0));
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
return Some(node.value().command.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Filter query is active, use the filtered items
|
||||||
|
if let Some(filtered_node) = self.filtered_items.get(selected) {
|
||||||
|
return Some(filtered_node.command.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
64
src/main.rs
64
src/main.rs
|
@ -21,6 +21,10 @@ use include_dir::include_dir;
|
||||||
use list::CustomList;
|
use list::CustomList;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::{Backend, CrosstermBackend},
|
backend::{Backend, CrosstermBackend},
|
||||||
|
layout::{Constraint, Direction, Layout},
|
||||||
|
style::{Color, Style},
|
||||||
|
text::Span,
|
||||||
|
widgets::{Block, Borders, Paragraph},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
use running_command::RunningCommand;
|
use running_command::RunningCommand;
|
||||||
|
@ -74,13 +78,47 @@ 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<()> {
|
||||||
let mut command_opt: Option<RunningCommand> = None;
|
let mut command_opt: Option<RunningCommand> = None;
|
||||||
|
|
||||||
let mut custom_list = CustomList::new();
|
let mut custom_list = CustomList::new();
|
||||||
|
let mut search_input = String::new();
|
||||||
|
let mut in_search_mode = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Always redraw
|
// Always redraw
|
||||||
terminal
|
terminal
|
||||||
.draw(|frame| {
|
.draw(|frame| {
|
||||||
custom_list.draw(frame, frame.size(), state);
|
//Split the terminal into 2 vertical chunks
|
||||||
|
//One for the search bar and one for the command list
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
|
||||||
|
.split(frame.size());
|
||||||
|
|
||||||
|
//Set the search bar text (If empty use the placeholder)
|
||||||
|
let display_text = if search_input.is_empty() {
|
||||||
|
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)
|
||||||
|
custom_list.draw(frame, chunks[1], search_input.clone(), state);
|
||||||
|
|
||||||
if let Some(ref mut command) = &mut command_opt {
|
if let Some(ref mut command) = &mut command_opt {
|
||||||
command.draw(frame, state);
|
command.draw(frame, state);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +145,27 @@ fn run<B: Backend>(terminal: &mut Terminal<B>, state: &AppState) -> io::Result<(
|
||||||
if key.code == KeyCode::Char('q') {
|
if key.code == KeyCode::Char('q') {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let Some(cmd) = custom_list.handle_key(key, state) {
|
//Activate search mode if the forward slash key gets pressed
|
||||||
|
if key.code == KeyCode::Char('/') {
|
||||||
|
// Enter search mode
|
||||||
|
in_search_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Insert user input into the search bar
|
||||||
|
if in_search_mode {
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char(c) => search_input.push(c),
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
search_input.pop();
|
||||||
|
}
|
||||||
|
KeyCode::Esc => {
|
||||||
|
search_input = String::new();
|
||||||
|
in_search_mode = false
|
||||||
|
}
|
||||||
|
KeyCode::Enter => in_search_mode = false,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else if let Some(cmd) = custom_list.handle_key(key, state) {
|
||||||
command_opt = Some(RunningCommand::new(cmd, state));
|
command_opt = Some(RunningCommand::new(cmd, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user