mirror of
https://github.com/sayanarijit/xplr
synced 2024-11-04 18:00:14 +00:00
Number input
This commit is contained in:
parent
53b18ae8f4
commit
98920637f9
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1133,7 +1133,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "xplr"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"crossterm",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "xplr"
|
||||
version = "0.1.12" # Update app.rs
|
||||
version = "0.1.13" # Update app.rs
|
||||
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "An experimental, minimal, configurable TUI file explorer, stealing ideas from nnn and fzf."
|
||||
|
110
src/app.rs
110
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<usize>,
|
||||
number_input: usize,
|
||||
) -> Result<Self, Error> {
|
||||
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, Error> {
|
||||
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<Self, Error> {
|
||||
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<Self, Error> {
|
||||
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<Self, Error> {
|
||||
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<Self, Error> {
|
||||
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<Self, Error> {
|
||||
let pwd = self.directory_buffer.pwd.clone();
|
||||
self.focus_path(&pwd)
|
||||
pub fn back(mut self) -> Result<Self, Error> {
|
||||
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<Self, Error> {
|
||||
@ -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<Vec<Action>> {
|
||||
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<Self, Error> {
|
||||
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<App, Error> {
|
||||
Mode::Explore,
|
||||
config.general.show_hidden,
|
||||
None,
|
||||
0,
|
||||
)?;
|
||||
|
||||
if let Some(file) = file_to_focus {
|
||||
|
@ -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,
|
||||
|
90
src/input.rs
90
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,
|
||||
|
18
src/ui.rs
18
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<B: Backend>(
|
||||
@ -105,7 +107,14 @@ pub fn draw<B: Backend>(
|
||||
|
||||
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<ListItem> = app
|
||||
@ -141,7 +150,12 @@ pub fn draw<B: Backend>(
|
||||
)
|
||||
.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]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user