From 98920637f92c7ec002f060c415f1cc9860ba42b0 Mon Sep 17 00:00:00 2001 From: Arijit Basu Date: Mon, 15 Mar 2021 14:49:30 +0530 Subject: [PATCH] Number input --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/app.rs | 110 ++++++++++++++++++++++++++++++++++++-------------- src/config.rs | 2 + src/input.rs | 90 ++++++----------------------------------- src/ui.rs | 18 ++++++++- 6 files changed, 112 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8155998..700c3ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1133,7 +1133,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xplr" -version = "0.1.12" +version = "0.1.13" dependencies = [ "criterion", "crossterm", diff --git a/Cargo.toml b/Cargo.toml index 81dceb9..3c94536 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xplr" -version = "0.1.12" # Update app.rs +version = "0.1.13" # Update app.rs authors = ["Arijit Basu "] edition = "2018" description = "An experimental, minimal, configurable TUI file explorer, stealing ideas from nnn and fzf." diff --git a/src/app.rs b/src/app.rs index 9ad1184..381449d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -12,7 +12,7 @@ use std::io::BufReader; use std::path::Path; use std::path::PathBuf; -pub const VERSION: &str = "v0.1.12"; // Update Cargo.toml +pub const VERSION: &str = "v0.1.13"; // Update Cargo.toml pub const UNSUPPORTED_STR: &str = "???"; pub const TOTAL_ROWS: usize = 50; @@ -300,6 +300,7 @@ pub struct App { pub parsed_help_menu: Vec<(String, String)>, pub show_hidden: bool, pub task: Task, + pub number_input: usize, } impl App { @@ -311,6 +312,7 @@ impl App { mode: Mode, show_hidden: bool, focus: Option, + number_input: usize, ) -> Result { let directory_buffer = DirectoryBuffer::load(config, focus.or(Some(0)), &pwd, show_hidden, selected_paths)?; @@ -336,6 +338,7 @@ impl App { parsed_help_menu, show_hidden, task: Task::NoOp, + number_input: number_input, }) } @@ -348,6 +351,7 @@ impl App { self.mode, self.show_hidden, self.directory_buffer.focus, + 0, ) } @@ -367,6 +371,20 @@ impl App { mode, self.show_hidden, self.directory_buffer.focus, + 0, + ) + } + + pub fn number_input(self, n: u8) -> Result { + Self::new( + &self.config, + &self.directory_buffer.pwd, + &self.saved_buffers, + &self.selected_paths, + self.mode, + self.show_hidden, + self.directory_buffer.focus, + self.number_input * 10 + n as usize, ) } @@ -379,6 +397,7 @@ impl App { self.mode, !self.show_hidden, self.directory_buffer.focus, + 0, ) } @@ -397,6 +416,7 @@ impl App { self.mode, self.show_hidden, focus, + 0, ) } @@ -415,6 +435,7 @@ impl App { self.mode, self.show_hidden, focus, + 0, ) } @@ -429,10 +450,11 @@ impl App { pub fn focus_next(self) -> Result { let len = self.directory_buffer.total; + let step = self.number_input.max(1); let focus = self .directory_buffer .focus - .map(|f| (len - 1).min(f + 1)) + .map(|f| (len.max(1) - 1).min(f + step)) .or(Some(0)); Self::new( @@ -443,18 +465,20 @@ impl App { self.mode, self.show_hidden, focus, + 0, ) } pub fn focus_previous(self) -> Result { let len = self.directory_buffer.total; + let step = self.number_input.max(1); let focus = if len == 0 { None } else { self.directory_buffer .focus - .map(|f| Some(1.max(f) - 1)) - .unwrap_or(Some(len - 1)) + .map(|f| Some(step.max(f) - step)) + .unwrap_or(Some(step.max(len) - step)) }; Self::new( @@ -465,6 +489,7 @@ impl App { self.mode, self.show_hidden, focus, + 0, ) } @@ -493,6 +518,7 @@ impl App { self.mode.clone(), self.show_hidden, focus, + 0, ) }) .unwrap_or_else(|| Ok(self.to_owned())) @@ -507,6 +533,7 @@ impl App { self.mode.clone(), self.show_hidden, Some(idx.clone()), + 0, ) } @@ -519,6 +546,7 @@ impl App { self.mode.clone(), self.show_hidden, Some(DirectoryBuffer::relative_focus(idx.clone())), + 0, ) } @@ -533,39 +561,51 @@ impl App { self.directory_buffer .focus .map(|f| ((f as isize) + idx).min(0) as usize), // TODO: make it safer + 0, ) } - pub fn enter(self) -> Result { - let pwd = self - .directory_buffer - .focused() - .map(|(p, _)| p) - .map(|p| { - if p.is_dir() { - p - } else { - self.directory_buffer.pwd.clone() - } - }) - .unwrap_or_else(|| self.directory_buffer.pwd.clone()); + pub fn enter(mut self) -> Result { + let mut step = self.number_input.max(1); + while step > 0 { + let pwd = self + .directory_buffer + .focused() + .map(|(p, _)| p) + .map(|p| { + if p.is_dir() { + p + } else { + self.directory_buffer.pwd.clone() + } + }) + .unwrap_or_else(|| self.directory_buffer.pwd.clone()); - let focus = self.saved_buffers.get(&pwd).unwrap_or(&None); + let focus = self.saved_buffers.get(&pwd).unwrap_or(&None); - Self::new( - &self.config, - &pwd, - &self.saved_buffers, - &self.selected_paths, - self.mode, - self.show_hidden, - focus.clone(), - ) + self = Self::new( + &self.config, + &pwd, + &self.saved_buffers, + &self.selected_paths, + self.mode, + self.show_hidden, + focus.clone(), + 0, + )?; + step -= 1; + } + Ok(self) } - pub fn back(self) -> Result { - let pwd = self.directory_buffer.pwd.clone(); - self.focus_path(&pwd) + pub fn back(mut self) -> Result { + let mut step = self.number_input.max(1); + while step > 0 { + let pwd = self.directory_buffer.pwd.clone(); + self = self.focus_path(&pwd)?; + step -= 1; + } + Ok(self) } pub fn select(self) -> Result { @@ -587,6 +627,7 @@ impl App { Mode::Select, self.show_hidden, self.directory_buffer.focus, + 0, ) } @@ -619,6 +660,7 @@ impl App { mode, self.show_hidden, self.directory_buffer.focus, + 0, ) } @@ -638,6 +680,7 @@ impl App { mode, self.show_hidden, self.directory_buffer.focus, + self.number_input, ) } @@ -693,11 +736,15 @@ impl App { } pub fn actions_from_key(&self, key: &Key) -> Option> { - self.parsed_key_bindings.get(key).map(|(_, a)| a.to_owned()) + match key { + Key::Number(n) => Some(vec![Action::NumberInput(*n)]), + key => self.parsed_key_bindings.get(key).map(|(_, a)| a.to_owned()), + } } pub fn handle(self, action: &Action) -> Result { match action { + Action::NumberInput(n) => self.number_input(*n), Action::ToggleShowHidden => self.toggle_hidden(), Action::Back => self.back(), Action::Enter => self.enter(), @@ -763,6 +810,7 @@ pub fn create() -> Result { Mode::Explore, config.general.show_hidden, None, + 0, )?; if let Some(file) = file_to_focus { diff --git a/src/config.rs b/src/config.rs index 863d257..91a2acf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -43,6 +43,7 @@ impl Mode { match (self, action) { // Special (_, Action::Terminate) => true, + (_, Action::NumberInput(_)) => true, // Explore mode (Self::Explore, Action::Back) => true, @@ -126,6 +127,7 @@ pub struct CommandConfig { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Action { + NumberInput(u8), ToggleShowHidden, Back, Enter, diff --git a/src/input.rs b/src/input.rs index e70c48b..d222ee8 100644 --- a/src/input.rs +++ b/src/input.rs @@ -4,6 +4,8 @@ use termion::event::Key as TermionKey; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum Key { + Number(u8), + Backspace, Left, Right, @@ -21,50 +23,6 @@ pub enum Key { Tab, Esc, - Zero, - One, - Two, - Three, - Four, - Five, - Six, - Seven, - Eight, - Nine, - - CtrlZero, - CtrlOne, - CtrlTwo, - CtrlThree, - CtrlFour, - CtrlFive, - CtrlSix, - CtrlSeven, - CtrlEight, - CtrlNine, - - AltZero, - AltOne, - AltTwo, - AltThree, - AltFour, - AltFive, - AltSix, - AltSeven, - AltEight, - AltNine, - - ShiftZero, - ShiftOne, - ShiftTwo, - ShiftThree, - ShiftFour, - ShiftFive, - ShiftSix, - ShiftSeven, - ShiftEight, - ShiftNine, - A, B, C, @@ -256,6 +214,17 @@ impl std::fmt::Display for Key { impl Key { pub fn from_termion_event(key: TermionKey) -> Self { match key { + TermionKey::Char('0') => Key::Number(0), + TermionKey::Char('1') => Key::Number(1), + TermionKey::Char('2') => Key::Number(2), + TermionKey::Char('3') => Key::Number(3), + TermionKey::Char('4') => Key::Number(4), + TermionKey::Char('5') => Key::Number(5), + TermionKey::Char('6') => Key::Number(6), + TermionKey::Char('7') => Key::Number(7), + TermionKey::Char('8') => Key::Number(8), + TermionKey::Char('9') => Key::Number(9), + TermionKey::Backspace => Key::Backspace, TermionKey::Left => Key::Left, TermionKey::Right => Key::Right, @@ -273,39 +242,6 @@ impl Key { TermionKey::Char('\t') => Key::Tab, TermionKey::Esc => Key::Esc, - TermionKey::Char('0') => Key::Zero, - TermionKey::Char('1') => Key::One, - TermionKey::Char('2') => Key::Two, - TermionKey::Char('3') => Key::Three, - TermionKey::Char('4') => Key::Four, - TermionKey::Char('5') => Key::Five, - TermionKey::Char('6') => Key::Six, - TermionKey::Char('7') => Key::Seven, - TermionKey::Char('8') => Key::Eight, - TermionKey::Char('9') => Key::Nine, - - TermionKey::Ctrl('0') => Key::CtrlZero, - TermionKey::Ctrl('1') => Key::CtrlOne, - TermionKey::Ctrl('2') => Key::CtrlTwo, - TermionKey::Ctrl('3') => Key::CtrlThree, - TermionKey::Ctrl('4') => Key::CtrlFour, - TermionKey::Ctrl('5') => Key::CtrlFive, - TermionKey::Ctrl('6') => Key::CtrlSix, - TermionKey::Ctrl('7') => Key::CtrlSeven, - TermionKey::Ctrl('8') => Key::CtrlEight, - TermionKey::Ctrl('9') => Key::CtrlNine, - - TermionKey::Alt('0') => Key::AltZero, - TermionKey::Alt('1') => Key::AltOne, - TermionKey::Alt('2') => Key::AltTwo, - TermionKey::Alt('3') => Key::AltThree, - TermionKey::Alt('4') => Key::AltFour, - TermionKey::Alt('5') => Key::AltFive, - TermionKey::Alt('6') => Key::AltSix, - TermionKey::Alt('7') => Key::AltSeven, - TermionKey::Alt('8') => Key::AltEight, - TermionKey::Alt('9') => Key::AltNine, - TermionKey::Char('a') => Key::A, TermionKey::Char('b') => Key::B, TermionKey::Char('c') => Key::C, diff --git a/src/ui.rs b/src/ui.rs index bce55c6..3df6556 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -2,7 +2,9 @@ use crate::app; use handlebars::Handlebars; use tui::backend::Backend; use tui::layout::{Constraint as TUIConstraint, Direction, Layout}; -use tui::widgets::{Block, Borders, Cell, List, ListItem, ListState, Row, Table, TableState}; +use tui::widgets::{ + Block, Borders, Cell, List, ListItem, ListState, Paragraph, Row, Table, TableState, +}; use tui::Frame; pub fn draw( @@ -105,7 +107,14 @@ pub fn draw( let left_chunks = Layout::default() .direction(Direction::Vertical) - .constraints([TUIConstraint::Percentage(40), TUIConstraint::Percentage(60)].as_ref()) + .constraints( + [ + TUIConstraint::Percentage(40), + TUIConstraint::Percentage(50), + TUIConstraint::Min(1), + ] + .as_ref(), + ) .split(chunks[1]); let selected: Vec = app @@ -141,7 +150,12 @@ pub fn draw( ) .widths(&[TUIConstraint::Percentage(40), TUIConstraint::Percentage(60)]); + // Input box + let input_box = Paragraph::new(format!("> {}", &app.number_input)) + .block(Block::default().borders(Borders::ALL).title(" input ")); + f.render_stateful_widget(table, chunks[0], table_state); f.render_stateful_widget(selected_list, left_chunks[0], list_state); f.render_widget(help_menu, left_chunks[1]); + f.render_widget(input_box, left_chunks[2]); }