From f0e67ba3137ea9ea9c9c0eed9769c6f178187731 Mon Sep 17 00:00:00 2001 From: afonsofrancof Date: Tue, 6 Aug 2024 17:21:47 +0100 Subject: [PATCH 1/8] Made float.rs generic. It can show anything inside it now --- src/float.rs | 140 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 53 deletions(-) diff --git a/src/float.rs b/src/float.rs index 2cbc8173..5048f1b8 100644 --- a/src/float.rs +++ b/src/float.rs @@ -1,59 +1,93 @@ -use ratatui::layout::{Constraint, Direction, Layout, Rect}; +use crossterm::event::{KeyCode, KeyEvent}; +use ratatui::{ + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Style, Stylize}, + widgets::Block, + Frame, +}; -/// This function just makes a given area smaller by 20 % in each direction, creating a kind of -/// "floating window". And you don't actually need all the constraints, and layouts to do that, its -/// very easy to calculate it directly, but I chose to use the ratatui API -pub fn floating_window(size: Rect) -> Rect { - // If the terminal window is small enough, just take up all the space for the command - if size.width < 85 || size.height < 25 { - return size; - } - let hor_float = Layout::default() - .constraints([ - Constraint::Percentage(20), - Constraint::Percentage(60), - Constraint::Percentage(20), - ]) - .direction(Direction::Horizontal) - .split(size)[1]; - Layout::default() - .constraints([ - Constraint::Percentage(20), - Constraint::Percentage(60), - Constraint::Percentage(20), - ]) - .direction(Direction::Vertical) - .split(hor_float)[1] +pub trait FloatContent { + fn draw(&mut self, frame: &mut Frame, area: Rect); + fn handle_key_event(&mut self, key: &KeyEvent) -> bool; + fn is_finished(&self) -> bool; } -/// Here is how would a purely math based function look like: -/// But it might break on smaller numbers -fn _unused_manual_floating_window(size: Rect) -> Rect { - // If the terminal window is small enough, just take up all the space for the command - if size.width < 85 || size.height < 25 { - return size; - } - let new_width = size.width * 60 / 100; - let new_height = size.height * 60 / 100; - let new_x = size.x + size.width * 20 / 100; - let new_y = size.y + size.height * 20 / 100; - Rect { - width: new_width, - height: new_height, - x: new_x, - y: new_y, - } +pub struct Float { + content: Option, + width_percent: u16, + height_percent: u16, } -#[test] -fn test_floating() { - let rect = Rect { - x: 10, - y: 2, - width: 100, - height: 200, - }; - let res1 = floating_window(rect); - let res2 = floating_window(rect); - assert_eq!(res1, res2); +impl Float { + pub fn new(width_percent: u16, height_percent: u16) -> Self { + Self { + content: None, + width_percent, + height_percent, + } + } + + fn floating_window(&self, size: Rect) -> Rect { + let hor_float = Layout::default() + .constraints([ + Constraint::Percentage((100 - self.width_percent) / 2), + Constraint::Percentage(self.width_percent), + Constraint::Percentage((100 - self.width_percent) / 2), + ]) + .direction(Direction::Horizontal) + .split(size)[1]; + + Layout::default() + .constraints([ + Constraint::Percentage((100 - self.height_percent) / 2), + Constraint::Percentage(self.height_percent), + Constraint::Percentage((100 - self.height_percent) / 2), + ]) + .direction(Direction::Vertical) + .split(hor_float)[1] + } + + pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) { + let popup_area = self.floating_window(parent_area); + + if let Some(content) = &mut self.content { + let content_area = Rect { + x: popup_area.x, + y: popup_area.y, + width: popup_area.width, + height: popup_area.height, + }; + + content.draw(frame, content_area); + } + } + + // Returns true if the key was processed by this Float. + pub fn handle_key_event(&mut self, key: &KeyEvent) -> bool { + if let Some(content) = &mut self.content { + match key.code { + KeyCode::Enter | KeyCode::Char('p') | KeyCode::Esc | KeyCode::Char('q') => { + if content.is_finished() { + self.content = None; + } else { + content.handle_key_event(key); + } + } + _ => { + content.handle_key_event(key); + } + } + true + } else { + false + } + } + + pub fn get_content(&self) -> &Option { + &self.content + } + + pub fn set_content(&mut self, content: Option) { + self.content = content; + } } From 65b7246099f5fb4664f117b029c9f621290b03e2 Mon Sep 17 00:00:00 2001 From: afonsofrancof Date: Tue, 6 Aug 2024 17:22:15 +0100 Subject: [PATCH 2/8] Made running_command.rs implement FloatContent so it can be shown inside a float --- src/running_command.rs | 199 ++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 102 deletions(-) diff --git a/src/running_command.rs b/src/running_command.rs index ad3196ae..06aa5b91 100644 --- a/src/running_command.rs +++ b/src/running_command.rs @@ -1,28 +1,26 @@ -use std::{ - io::Write, - sync::{Arc, Mutex}, - thread::JoinHandle, -}; - +use crate::{float::FloatContent, state::AppState}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use oneshot::{channel, Receiver}; use portable_pty::{ ChildKiller, CommandBuilder, ExitStatus, MasterPty, NativePtySystem, PtySize, PtySystem, }; use ratatui::{ - layout::Size, - style::{Style, Stylize}, + layout::{Rect, Size}, + style::{Color, Style, Stylize}, text::{Line, Span}, widgets::{Block, Borders}, Frame, }; +use std::{ + io::Write, + sync::{Arc, Mutex}, + thread::JoinHandle, +}; use tui_term::{ vt100::{self, Screen}, widget::PseudoTerminal, }; -use crate::{float::floating_window, state::AppState}; - #[derive(Clone)] pub enum Command { Raw(&'static str), @@ -30,36 +28,107 @@ pub enum Command { None, // Directory } -/// This is a struct for storing everything connected to a running command -// Create a new instance on every new command you want to run pub struct RunningCommand { - /// A buffer to save all the command output (accumulates, untill the command exits) + /// A buffer to save all the command output (accumulates, until the command exits) buffer: Arc>>, - - /// A handle of the tread where the command is being executed + /// A handle for the thread running the command command_thread: Option>, - - /// A handle to kill the running process, it's an option because it can only be used once + /// A handle to kill the running process; it's an option because it can only be used once child_killer: Option>>, - - /// A join handle for the thread that is reading all the command output and sending it to the - /// main thread + /// A join handle for the thread that reads command output and sends it to the main thread _reader_thread: JoinHandle<()>, - /// Virtual terminal (pty) handle, used for resizing the pty pty_master: Box, - /// Used for sending keys to the emulated terminal writer: Box, - /// Only set after the process has ended status: Option, } +impl FloatContent for RunningCommand { + fn draw(&mut self, frame: &mut Frame, area: Rect) { + // Calculate the inner size of the terminal area, considering borders + let inner_size = Size { + width: area.width - 2, // Adjust for border width + height: area.height - 2, + }; + + // Define the block for the terminal display + let block = if !self.is_finished() { + // Display a block indicating the command is running + Block::default() + .borders(Borders::ALL) + .title_top(Line::from("Running the command....").centered()) + .title_style(Style::default().reversed()) + .title_bottom(Line::from("Press Ctrl-C to KILL the command")) + } else { + // Display a block with the command's exit status + let mut title_line = if self.get_exit_status().success() { + Line::from( + Span::default() + .content("SUCCESS!") + .style(Style::default().fg(Color::Green).reversed()), + ) + } else { + Line::from( + Span::default() + .content("FAILED!") + .style(Style::default().fg(Color::Red).reversed()), + ) + }; + + title_line.push_span( + Span::default() + .content(" press to close this window ") + .style(Style::default()), + ); + + Block::default() + .borders(Borders::ALL) + .title_top(title_line.centered()) + }; + + // Process the buffer and create the pseudo-terminal widget + let screen = self.screen(inner_size); + let pseudo_term = PseudoTerminal::new(&screen).block(block); + + // Render the widget on the frame + frame.render_widget(pseudo_term, area); + } + + /// Handle key events of the running command "window". Returns true when the "window" should be + /// closed + fn handle_key_event(&mut self, key: &KeyEvent) -> bool { + match key.code { + // Handle Ctrl-C to kill the command + KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => { + self.kill_child(); + } + // Close the window when Enter is pressed and the command is finished + KeyCode::Enter if self.is_finished() => { + return true; + } + // Pass other key events to the terminal + _ => self.handle_passthrough_key_event(key), + } + false + } + + fn is_finished(&self) -> bool { + // Check if the command thread has finished + if let Some(command_thread) = &self.command_thread { + command_thread.is_finished() + } else { + true + } + } +} + impl RunningCommand { pub fn new(command: Command, state: &AppState) -> Self { let pty_system = NativePtySystem::default(); + // Build the command based on the provided Command enum variant let mut cmd = CommandBuilder::new("sh"); match command { Command::Raw(prompt) => { @@ -74,10 +143,11 @@ impl RunningCommand { cmd.cwd(&state.temp_path); + // Open a pseudo-terminal with initial size let pair = pty_system .openpty(PtySize { - rows: 24, // Set the initial size of the emulated terminal - cols: 80, // We will update this later, if resized + rows: 24, // Initial number of rows (will be updated dynamically) + cols: 80, // Initial number of columns (will be updated dynamically) pixel_width: 0, pixel_height: 0, }) @@ -129,6 +199,7 @@ impl RunningCommand { status: None, } } + fn screen(&mut self, size: Size) -> Screen { // Resize the emulated pty self.pty_master @@ -149,13 +220,7 @@ impl RunningCommand { parser.process(buffer); parser.screen().clone() } - pub fn is_finished(&mut self) -> bool { - if let Some(command_thread) = &self.command_thread { - command_thread.is_finished() - } else { - true - } - } + /// This function will block if the command is not finished fn get_exit_status(&mut self) -> ExitStatus { if self.command_thread.is_some() { @@ -168,57 +233,6 @@ impl RunningCommand { } } - pub fn draw(&mut self, frame: &mut Frame, state: &AppState) { - // Funny name - let floater = floating_window(frame.size()); - - let inner_size = Size { - width: floater.width - 2, // Because we add a `Block` with a border - height: floater.height - 2, - }; - - // When the command is running - let term_border = if !self.is_finished() { - Block::default() - .borders(Borders::ALL) - .title_top(Line::from("Running the command....").centered()) - .title_style(Style::default().reversed()) - .title_bottom(Line::from("Press Ctrl-C to KILL the command")) - } else { - // This portion is just for pretty colors. - // You can use multiple `Span`s with different styles each, to construct a line, - // which can be used as a list item, or in this case a `Block` title - - let mut title_line = if self.get_exit_status().success() { - Line::from( - Span::default() - .content("SUCCESS!") - .style(Style::default().fg(state.theme.success_color).reversed()), - ) - } else { - Line::from( - Span::default() - .content("FAILED!") - .style(Style::default().fg(state.theme.fail_color).reversed()), - ) - }; - - title_line.push_span( - Span::default() - .content(" press to close this window ") - .style(Style::default()), - ); - - Block::default() - .borders(Borders::ALL) - .title_top(title_line.centered()) - }; - let screen = self.screen(inner_size); // when the terminal is changing a lot, there - // will be 1 frame of lag on resizing - let pseudo_term = PseudoTerminal::new(&screen).block(term_border); - frame.render_widget(pseudo_term, floater); - } - /// Send SIGHUB signal, *not* SIGKILL or SIGTERM, to the child process pub fn kill_child(&mut self) { if !self.is_finished() { @@ -227,22 +241,6 @@ impl RunningCommand { } } - /// Handle key events of the running command "window". Returns true when the "window" should be - /// closed - pub fn handle_key_event(&mut self, key: &KeyEvent) -> bool { - match key.code { - KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => { - self.kill_child() - } - - KeyCode::Enter if self.is_finished() => { - return true; - } - _ => self.handle_passthrough_key_event(key), - }; - false - } - /// Convert the KeyEvent to pty key codes, and send them to the virtual terminal fn handle_passthrough_key_event(&mut self, key: &KeyEvent) { let input_bytes = match key.code { @@ -260,9 +258,6 @@ impl RunningCommand { '6' | '^' => send = vec![30], '7' | '-' | '_' => send = vec![31], char if ('A'..='_').contains(&char) => { - // Since A == 65, - // we can safely subtract 64 to get - // the corresponding control character let ascii_val = char as u8; let ascii_to_send = ascii_val - 64; send = vec![ascii_to_send]; From 72c10c5ef87538e6ff631a9c88e0cac1f5a9146b Mon Sep 17 00:00:00 2001 From: afonsofrancof Date: Tue, 6 Aug 2024 17:22:32 +0100 Subject: [PATCH 3/8] Removed out of place handling of float window in list.rs --- src/list.rs | 169 +++++++++++++++------------------------------------- 1 file changed, 47 insertions(+), 122 deletions(-) diff --git a/src/list.rs b/src/list.rs index 5e094107..a60f89d1 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,4 +1,6 @@ -use crate::{float::floating_window, running_command::Command, state::AppState}; +use crate::{ + float::Float, preview_content::PreviewContent, running_command::Command, state::AppState, +}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use ego_tree::{tree, NodeId}; use ratatui::{ @@ -26,28 +28,12 @@ pub struct CustomList { /// This is the state asociated with the list widget, used to display the selection in the /// widget list_state: ListState, - /// 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 - preview_window_state: Option, // This stores the current search query filter_query: String, // This stores the filtered tree filtered_items: Vec, -} - -/// This struct stores the preview window state -struct PreviewWindowState { - /// The text inside the window - text: Vec, - /// The current line scroll - scroll: usize, -} - -impl PreviewWindowState { - /// Create a new PreviewWindowState - pub fn new(text: Vec) -> Self { - Self { text, scroll: 0 } - } + // This is the preview window for the commands + preview_float: Float, } impl CustomList { @@ -192,10 +178,9 @@ impl CustomList { inner_tree: tree, visit_stack: vec![root_id], list_state: ListState::default().with_selected(Some(0)), - // By default the PreviewWindowState is set to None, so it is not being shown - preview_window_state: None, filter_query: String::new(), filtered_items: vec![], + preview_float: Float::new(80, 80), } } @@ -259,32 +244,8 @@ impl CustomList { // Render it frame.render_stateful_widget(list, area, &mut self.list_state); - // Draw the preview window if it's active - if let Some(pw_state) = &self.preview_window_state { - // Set the window to be floating - let floating_area = floating_window(area); - - // Draw the preview windows lines - let lines: Vec = pw_state - .text - .iter() - .skip(pw_state.scroll) - .take(floating_area.height as usize) - .map(|line| Line::from(line.as_str())) - .collect(); - - // Create list widget - let list = List::new(lines) - .block( - Block::default() - .borders(Borders::ALL) - .title("Action preview"), - ) - .highlight_style(Style::default().reversed()); - - // Finally render the preview window - frame.render_widget(list, floating_area); - } + //Render the preview window + self.preview_float.draw(frame, area); } pub fn filter(&mut self, query: String) { @@ -323,98 +284,32 @@ impl CustomList { if event.kind == KeyEventKind::Release { return None; } + + if self.preview_float.handle_key_event(&event) { + return None; // If the key event was handled by the preview, don't propagate it further + } + match event.code { // Damm you Up arrow, use vim lol KeyCode::Char('j') | KeyCode::Down => { - // If the preview window is active, scroll down and consume the scroll action, - // so the scroll does not happen in the main window as well - if self.preview_window_state.is_some() { - self.scroll_preview_window_down(); - } else { - self.list_state.select_next(); - } - + self.list_state.select_next(); None } KeyCode::Char('k') | KeyCode::Up => { - // If the preview window is active, scroll up and consume the scroll action, - // so the scroll does not happen in the main window as well - if self.preview_window_state.is_some() { - self.scroll_preview_window_up(); - } else { - self.list_state.select_previous(); - } - + self.list_state.select_previous(); None } - // The 'p' key toggles the preview on and off KeyCode::Char('p') => { self.toggle_preview_window(state); None } - KeyCode::Enter if self.preview_window_state.is_none() => self.handle_enter(), + KeyCode::Enter => self.handle_enter(), _ => None, } } - fn toggle_preview_window(&mut self, state: &AppState) { - // If the preview window is active, disable it - if self.preview_window_state.is_some() { - self.preview_window_state = None; - } else { - // If the preview windows is not active, show it - // Get the selected command - if let Some(selected_command) = self.get_selected_command() { - let lines = match selected_command { - Command::Raw(cmd) => { - // Reconstruct the line breaks and file formatting after the - // 'include_str!()' call in the node - cmd.lines().map(|line| line.to_string()).collect() - } - Command::LocalFile(file_path) => { - let mut full_path = state.temp_path.clone(); - full_path.push(file_path); - let file_contents = std::fs::read_to_string(&full_path) - .map_err(|_| format!("File not found: {:?}", &full_path)) - .unwrap(); - file_contents.lines().map(|line| line.to_string()).collect() - } - // If command is a folder, we don't display a preview - Command::None => return, - }; - - // Show the preview window with the text lines - self.preview_window_state = Some(PreviewWindowState::new(lines)); - } - } - } - - /// Scroll the preview window down - fn scroll_preview_window_down(&mut self) { - if let Some(pw_state) = &mut self.preview_window_state { - if pw_state.scroll + 1 < pw_state.text.len() { - pw_state.scroll += 1; - } - } - } - - /// Scroll the preview window up - fn scroll_preview_window_up(&mut self) { - if let Some(pw_state) = &mut self.preview_window_state { - if pw_state.scroll > 0 { - pw_state.scroll = pw_state.scroll.saturating_sub(1); - } - } - } - - /// This method returns the currently selected command, or None if no command is selected. - /// It was extracted from the 'handle_enter()' - /// - /// 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. fn get_selected_command(&self) -> Option { let selected_index = self.list_state.selected().unwrap_or(0); - println!("Selected Index: {}", selected_index); if self.filter_query.is_empty() { // No filter query, use the regular tree navigation @@ -440,7 +335,6 @@ impl CustomList { } else { // Filter query is active, use the filtered items if let Some(filtered_node) = self.filtered_items.get(selected_index) { - println!("Filtered Node Name: {}", filtered_node.name); return Some(filtered_node.command.clone()); } } @@ -496,6 +390,37 @@ impl CustomList { None } + fn toggle_preview_window(&mut self, state: &AppState) { + if self.preview_float.get_content().is_some() { + // If the preview window is active, disable it + self.preview_float.set_content(None); + } else { + // If the preview window is not active, show it + + // Get the selected command + if let Some(selected_command) = self.get_selected_command() { + let lines = match selected_command { + Command::Raw(cmd) => cmd.lines().map(|line| line.to_string()).collect(), + Command::LocalFile(file_path) => { + if file_path.is_empty() { + return; + } + let mut full_path = state.temp_path.clone(); + full_path.push(file_path); + let file_contents = std::fs::read_to_string(&full_path) + .map_err(|_| format!("File not found: {:?}", &full_path)) + .unwrap(); + file_contents.lines().map(|line| line.to_string()).collect() + } + Command::None => return, + }; + + self.preview_float + .set_content(Some(PreviewContent::new(lines))); + } + } + } + /// Checks weather the current tree node is the root node (can we go up the tree or no) /// Returns `true` if we can't go up the tree (we are at the tree root) /// else returns `false` From 6e0540c68a887372a77a983f754cd82711e51703 Mon Sep 17 00:00:00 2001 From: afonsofrancof Date: Tue, 6 Aug 2024 17:29:57 +0100 Subject: [PATCH 4/8] Made main.rs use new float logic --- src/main.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index f00382fb..ee104b1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod float; mod list; +mod preview_content; mod running_command; pub mod state; mod theme; @@ -17,6 +18,7 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, }; +use float::Float; use include_dir::include_dir; use list::CustomList; use ratatui::{ @@ -77,9 +79,12 @@ fn main() -> std::io::Result<()> { } fn run(terminal: &mut Terminal, state: &AppState) -> io::Result<()> { - let mut command_opt: Option = None; - let mut custom_list = CustomList::new(); + //Create the search field let mut search_input = String::new(); + //Create the command list + let mut custom_list = CustomList::new(); + //Create the float to hold command output + let mut command_float = Float::new(60, 60); let mut in_search_mode = false; loop { @@ -118,10 +123,8 @@ fn run(terminal: &mut Terminal, state: &AppState) -> io::Result<( frame.render_widget(search_bar, chunks[0]); //Render the command list (Second chunk of the screen) custom_list.draw(frame, chunks[1], state); - - if let Some(ref mut command) = &mut command_opt { - command.draw(frame, state); - } + //Render the command float in the custom_list chunk + command_float.draw(frame, chunks[1]); }) .unwrap(); From ab2cc1340f05197f93ba65e3850da4468aee41aa Mon Sep 17 00:00:00 2001 From: afonsofrancof Date: Thu, 8 Aug 2024 22:12:34 +0100 Subject: [PATCH 5/8] Added FloatingText class. It shows text in a float --- src/list.rs | 6 ++-- src/preview_content.rs | 82 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 src/preview_content.rs diff --git a/src/list.rs b/src/list.rs index a60f89d1..c6897397 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,5 +1,5 @@ use crate::{ - float::Float, preview_content::PreviewContent, running_command::Command, state::AppState, + float::Float, preview_content::FloatingText, running_command::Command, state::AppState, }; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use ego_tree::{tree, NodeId}; @@ -33,7 +33,7 @@ pub struct CustomList { // This stores the filtered tree filtered_items: Vec, // This is the preview window for the commands - preview_float: Float, + preview_float: Float, } impl CustomList { @@ -416,7 +416,7 @@ impl CustomList { }; self.preview_float - .set_content(Some(PreviewContent::new(lines))); + .set_content(Some(FloatingText::new(lines))); } } } diff --git a/src/preview_content.rs b/src/preview_content.rs new file mode 100644 index 00000000..195ac24c --- /dev/null +++ b/src/preview_content.rs @@ -0,0 +1,82 @@ +use crate::float::FloatContent; +use crossterm::event::{KeyCode, KeyEvent}; +use ratatui::{ + layout::Rect, + style::{Style, Stylize}, + text::Line, + widgets::{Block, Borders, List}, + Frame, +}; + +pub struct FloatingText { + text: Vec, + scroll: usize, +} + +impl FloatingText { + pub fn new(text: Vec) -> Self { + Self { text, scroll: 0 } + } + + fn scroll_down(&mut self) { + if self.scroll + 1 < self.text.len() { + self.scroll += 1; + } + } + + fn scroll_up(&mut self) { + if self.scroll > 0 { + self.scroll -= 1; + } + } +} + +impl FloatContent for FloatingText { + fn draw(&mut self, frame: &mut Frame, area: Rect) { + // Define the Block with a border and background color + let block = Block::default() + .borders(Borders::ALL) + .style(Style::default()); + + // Draw the Block first + frame.render_widget(block.clone(), area); + + // Calculate the inner area to ensure text is not drawn over the border + let inner_area = block.inner(area); + + // Create the list of lines to be displayed + let lines: Vec = self + .text + .iter() + .skip(self.scroll) + .take(inner_area.height as usize) + .map(|line| Line::from(line.as_str())) + .collect(); + + // Create list widget + let list = List::new(lines) + .block(Block::default()) + .highlight_style(Style::default().reversed()); + + // Render the list inside the bordered area + frame.render_widget(list, inner_area); + } + + fn handle_key_event(&mut self, key: &KeyEvent) -> bool { + match key.code { + KeyCode::Down | KeyCode::Char('j') => { + self.scroll_down(); + true + } + KeyCode::Up | KeyCode::Char('k') => { + self.scroll_up(); + true + } + _ => false, + } + } + + fn is_finished(&self) -> bool { + true + } +} From da7f0780636643bf8325d9d97ce99113992987b5 Mon Sep 17 00:00:00 2001 From: afonsofrancof Date: Thu, 8 Aug 2024 23:37:19 +0100 Subject: [PATCH 6/8] Moved preview_content to floating_text and merged --- src/float.rs | 2 -- src/{preview_content.rs => floating_text.rs} | 0 src/list.rs | 4 +--- src/main.rs | 14 +++++++------- 4 files changed, 8 insertions(+), 12 deletions(-) rename src/{preview_content.rs => floating_text.rs} (100%) diff --git a/src/float.rs b/src/float.rs index 5048f1b8..cd0bd9f8 100644 --- a/src/float.rs +++ b/src/float.rs @@ -1,8 +1,6 @@ use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ layout::{Constraint, Direction, Layout, Rect}, - style::{Color, Style, Stylize}, - widgets::Block, Frame, }; diff --git a/src/preview_content.rs b/src/floating_text.rs similarity index 100% rename from src/preview_content.rs rename to src/floating_text.rs diff --git a/src/list.rs b/src/list.rs index c6897397..a750f393 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,6 +1,4 @@ -use crate::{ - float::Float, preview_content::FloatingText, running_command::Command, state::AppState, -}; +use crate::{float::Float, floating_text::FloatingText, running_command::Command, state::AppState}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use ego_tree::{tree, NodeId}; use ratatui::{ diff --git a/src/main.rs b/src/main.rs index ee104b1f..f7ae28ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ mod float; +mod floating_text; mod list; -mod preview_content; mod running_command; pub mod state; mod theme; @@ -140,11 +140,11 @@ fn run(terminal: &mut Terminal, state: &AppState) -> io::Result<( if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { continue; } - if let Some(ref mut command) = command_opt { - if command.handle_key_event(&key) { - command_opt = None; - } - } else { + + //Send the key to the float + //If we receive true, then the float processed the input + //If that's the case, don't propagate input to other widgets + if !command_float.handle_key_event(&key) { //Insert user input into the search bar if in_search_mode { match key.code { @@ -168,7 +168,7 @@ fn run(terminal: &mut Terminal, state: &AppState) -> io::Result<( _ => {} } } else if let Some(cmd) = custom_list.handle_key(key, state) { - command_opt = Some(RunningCommand::new(cmd, state)); + command_float.set_content(Some(RunningCommand::new(cmd, state))); } else { // Handle keys while not in search mode match key.code { From 7692c7c1c9e5d75e6e0a1324d212b6b209e80b6c Mon Sep 17 00:00:00 2001 From: afonsofrancof Date: Thu, 8 Aug 2024 23:59:33 +0100 Subject: [PATCH 7/8] Fix chris fat finger --- src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index 97e896a8..e85bd28d 100644 --- a/src/list.rs +++ b/src/list.rs @@ -307,7 +307,7 @@ impl CustomList { } KeyCode::Enter | KeyCode::Right | KeyCode::Char('l') => { - if self.preview_window_state.is_none() { + if self.preview_float.get_content().is_none() { self.handle_enter() } else { None From 8be90b9c7db347dd5f6c2a4ed2a20886aa8f20c9 Mon Sep 17 00:00:00 2001 From: Chris Titus Date: Thu, 8 Aug 2024 18:03:51 -0500 Subject: [PATCH 8/8] Update list.rs --- src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/list.rs b/src/list.rs index 97e896a8..e85bd28d 100644 --- a/src/list.rs +++ b/src/list.rs @@ -307,7 +307,7 @@ impl CustomList { } KeyCode::Enter | KeyCode::Right | KeyCode::Char('l') => { - if self.preview_window_state.is_none() { + if self.preview_float.get_content().is_none() { self.handle_enter() } else { None