Support easier key remaps

Also,

- Add key binding `~` to go to homedir.
- Add customizable cursor and prompts.
- Improve the help menus.
This commit is contained in:
Arijit Basu 2021-04-11 21:05:46 +05:30 committed by Arijit Basu
parent d34dc77ea5
commit 055c1083d6
4 changed files with 117 additions and 65 deletions

View File

@ -889,10 +889,12 @@ impl App {
fn handle_key(mut self, key: Key) -> Result<Self> { fn handle_key(mut self, key: Key) -> Result<Self> {
let kb = self.mode.key_bindings.clone(); let kb = self.mode.key_bindings.clone();
let key_str = key.to_string();
let default = kb.default.clone(); let default = kb.default.clone();
let msgs = kb let msgs = kb
.on_key .on_key
.get(&key.to_string()) .get(&key_str)
.or_else(|| kb.remaps.get(&key_str).and_then(|k| kb.on_key.get(k)))
.map(|a| Some(a.messages.clone())) .map(|a| Some(a.messages.clone()))
.unwrap_or_else(|| { .unwrap_or_else(|| {
if key.is_alphabet() { if key.is_alphabet() {
@ -1419,14 +1421,23 @@ impl App {
.map(|l| match l { .map(|l| match l {
HelpMenuLine::Paragraph(p) => format!("\t{}\n", p), HelpMenuLine::Paragraph(p) => format!("\t{}\n", p),
HelpMenuLine::KeyMap(k, h) => { HelpMenuLine::KeyMap(k, h) => {
format!(" {:15} | {}\n", k, h) let remaps = self
.mode()
.key_bindings
.remaps
.iter()
.filter(|(_, t)| t == &k)
.map(|(f, _)| f.clone())
.collect::<Vec<String>>()
.join(", ");
format!(" {:15} | {:25} | {}\n", k, remaps, h)
} }
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(""); .join("");
format!( format!(
"### {}\n\n key | action\n --------------- | ------\n{}\n", "### {}\n\n key | remaps | action\n --------------- | ------------------------- |------\n{}\n",
name, help name, help
) )
}) })

View File

@ -160,6 +160,14 @@ pub struct UiElement {
pub style: Style, pub style: Style,
} }
impl UiElement {
fn extend(mut self, other: Self) -> Self {
self.format = other.format.or(self.format);
self.style = other.style.extend(self.style);
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct TableRowConfig { pub struct TableRowConfig {
@ -250,6 +258,12 @@ pub struct GeneralConfig {
#[serde(default)] #[serde(default)]
pub show_hidden: Option<bool>, pub show_hidden: Option<bool>,
#[serde(default)]
pub cursor: UiElement,
#[serde(default)]
pub prompt: UiElement,
#[serde(default)] #[serde(default)]
pub table: TableConfig, pub table: TableConfig,
@ -266,6 +280,8 @@ pub struct GeneralConfig {
impl GeneralConfig { impl GeneralConfig {
pub fn extend(mut self, other: Self) -> Self { pub fn extend(mut self, other: Self) -> Self {
self.show_hidden = other.show_hidden.or(self.show_hidden); self.show_hidden = other.show_hidden.or(self.show_hidden);
self.cursor = other.cursor.extend(self.cursor);
self.prompt = other.prompt.extend(self.prompt);
self.table = other.table.extend(self.table); self.table = other.table.extend(self.table);
self.default_ui = other.default_ui.extend(self.default_ui); self.default_ui = other.default_ui.extend(self.default_ui);
self.focus_ui = other.focus_ui.extend(self.focus_ui); self.focus_ui = other.focus_ui.extend(self.focus_ui);
@ -277,6 +293,9 @@ impl GeneralConfig {
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct KeyBindings { pub struct KeyBindings {
#[serde(default)]
pub remaps: BTreeMap<String, String>,
#[serde(default)] #[serde(default)]
pub on_key: BTreeMap<String, Action>, pub on_key: BTreeMap<String, Action>,
@ -295,6 +314,8 @@ pub struct KeyBindings {
impl KeyBindings { impl KeyBindings {
pub fn extend(mut self, mut other: Self) -> Self { pub fn extend(mut self, mut other: Self) -> Self {
other.remaps.extend(self.remaps);
self.remaps = other.remaps;
other.on_key.extend(self.on_key); other.on_key.extend(self.on_key);
self.on_key = other.on_key; self.on_key = other.on_key;
self.on_alphabet = other.on_alphabet.or(self.on_alphabet); self.on_alphabet = other.on_alphabet.or(self.on_alphabet);

View File

@ -1,6 +1,10 @@
version: v0.4.0 version: v0.4.0
general: general:
show_hidden: false show_hidden: false
prompt:
format: "> "
cursor:
format:
table: table:
header: header:
cols: cols:
@ -180,6 +184,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
c: c:
help: copy here help: copy here
@ -226,6 +231,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
enter: enter:
help: create file help: create file
@ -266,6 +272,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
enter: enter:
help: create directory help: create directory
@ -306,6 +313,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
ctrl-c: ctrl-c:
help: cancel & quit help: cancel & quit
@ -337,6 +345,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
enter: enter:
help: rename help: rename
@ -377,6 +386,15 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps:
/: ctrl-f
h: left
j: down
k: up
l: right
v: space
q: ctrl-c
on_key: on_key:
'#': '#':
help: null help: null
@ -390,12 +408,6 @@ modes:
input: . input: .
case_sensitive: false case_sensitive: false
- Explore - Explore
/:
help: null
messages:
- SwitchMode: search
- SetInputBuffer: ''
- Explore
':': ':':
help: action help: action
messages: messages:
@ -405,16 +417,22 @@ modes:
messages: messages:
- BashExec: | - BashExec: |
${PAGER:-less} "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}" ${PAGER:-less} "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}"
'~':
help: go home
messages:
- BashExecSilently: |
echo "ChangeDirectory: ${HOME:?}" >> "${XPLR_PIPE_MSG_IN:?}"
echo "Explore" >> "${XPLR_PIPE_MSG_IN:?}"
G: G:
help: go to bottom help: go to bottom
messages: messages:
- FocusLast - FocusLast
ctrl-c: ctrl-c:
help: cancel & quit [q] help: cancel & quit
messages: messages:
- Terminate - Terminate
ctrl-f: ctrl-f:
help: search [/] help: search
messages: messages:
- SwitchMode: search - SwitchMode: search
- SetInputBuffer: '' - SetInputBuffer: ''
@ -424,7 +442,7 @@ modes:
messages: messages:
- SwitchMode: delete - SwitchMode: delete
down: down:
help: down [j] help: down
messages: messages:
- FocusNext - FocusNext
enter: enter:
@ -435,55 +453,29 @@ modes:
help: go to help: go to
messages: messages:
- SwitchMode: go to - SwitchMode: go to
h:
help: null
messages:
- Back
j:
help: null
messages:
- FocusNext
k:
help: null
messages:
- FocusPrevious
l:
help: null
messages:
- Enter
left: left:
help: back [h] help: back
messages: messages:
- Back - Back
q:
help: null
messages:
- Terminate
r: r:
help: rename help: rename
messages: messages:
- SwitchMode: rename - SwitchMode: rename
- BashExecSilently: | - BashExecSilently: |
echo "SetInputBuffer: $(basename ${XPLR_FOCUS_PATH})" >> "${XPLR_PIPE_MSG_IN:?}" echo "SetInputBuffer: $(basename ${XPLR_FOCUS_PATH})" >> "${XPLR_PIPE_MSG_IN:?}"
right: right:
help: enter [l] help: enter
messages: messages:
- Enter - Enter
space: space:
help: toggle selection [v] help: toggle selection
messages: messages:
- ToggleSelection - ToggleSelection
- FocusNext - FocusNext
up: up:
help: up [k] help: up
messages: messages:
- FocusPrevious - FocusPrevious
v:
help: null
messages:
- ToggleSelection
- FocusNext
on_alphabet: null on_alphabet: null
on_number: on_number:
help: input help: input
@ -501,6 +493,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
ctrl-c: ctrl-c:
help: cancel & quit help: cancel & quit
@ -532,6 +525,9 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps:
j: down
k: up
on_key: on_key:
backspace: backspace:
help: clear help: clear
@ -542,7 +538,7 @@ modes:
messages: messages:
- Terminate - Terminate
down: down:
help: to down [j] help: to down
messages: messages:
- FocusNextByRelativeIndexFromInput - FocusNextByRelativeIndexFromInput
- SwitchMode: default - SwitchMode: default
@ -551,18 +547,8 @@ modes:
messages: messages:
- FocusByIndexFromInput - FocusByIndexFromInput
- SwitchMode: default - SwitchMode: default
j:
help: null
messages:
- FocusNextByRelativeIndexFromInput
- SwitchMode: default
k:
help: null
messages:
- FocusPreviousByRelativeIndexFromInput
- SwitchMode: default
up: up:
help: to up [k] help: to up
messages: messages:
- FocusPreviousByRelativeIndexFromInput - FocusPreviousByRelativeIndexFromInput
- SwitchMode: default - SwitchMode: default
@ -581,6 +567,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
d: d:
help: delete help: delete
@ -637,6 +624,9 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps:
q: ctrl-c
on_number: on_number:
help: go to index help: go to index
messages: messages:
@ -679,11 +669,7 @@ modes:
- SwitchMode: default - SwitchMode: default
ctrl-c: ctrl-c:
help: cancel & quit [q] help: cancel & quit
messages:
- Terminate
q:
messages: messages:
- Terminate - Terminate
@ -695,6 +681,7 @@ modes:
help: null help: null
extra_help: null extra_help: null
key_bindings: key_bindings:
remaps: {}
on_key: on_key:
backspace: backspace:
help: clear help: clear

View File

@ -9,6 +9,7 @@ use tui::backend::Backend;
use tui::layout::Rect; use tui::layout::Rect;
use tui::layout::{Constraint as TuiConstraint, Direction, Layout}; use tui::layout::{Constraint as TuiConstraint, Direction, Layout};
use tui::style::{Color, Style}; use tui::style::{Color, Style};
use tui::text::{Span, Spans};
use tui::widgets::{ use tui::widgets::{
Block, Borders, Cell, List, ListItem, ListState, Paragraph, Row, Table, TableState, Block, Borders, Cell, List, ListItem, ListState, Paragraph, Row, Table, TableState,
}; };
@ -282,7 +283,18 @@ fn draw_help_menu<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &
.into_iter() .into_iter()
.map(|l| match l { .map(|l| match l {
HelpMenuLine::Paragraph(p) => Row::new([Cell::from(p)].to_vec()), HelpMenuLine::Paragraph(p) => Row::new([Cell::from(p)].to_vec()),
HelpMenuLine::KeyMap(k, h) => Row::new([Cell::from(k), Cell::from(h)].to_vec()), HelpMenuLine::KeyMap(k, h) => {
let remaps = app
.mode()
.key_bindings
.remaps
.iter()
.filter(|(_, t)| t == &&k)
.map(|(f, _)| f.clone())
.collect::<Vec<String>>()
.join("|");
Row::new([Cell::from(k), Cell::from(remaps), Cell::from(h)].to_vec())
}
}) })
.collect::<Vec<Row>>(); .collect::<Vec<Row>>();
@ -292,15 +304,36 @@ fn draw_help_menu<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &
.borders(Borders::ALL) .borders(Borders::ALL)
.title(format!(" Help [{}] ", &app.mode().name)), .title(format!(" Help [{}] ", &app.mode().name)),
) )
.widths(&[TuiConstraint::Percentage(30), TuiConstraint::Percentage(70)]); .widths(&[
TuiConstraint::Percentage(20),
TuiConstraint::Percentage(20),
TuiConstraint::Percentage(60),
]);
f.render_widget(help_menu, rect); f.render_widget(help_menu, rect);
} }
fn draw_input_buffer<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) { fn draw_input_buffer<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) {
let input_buf = Paragraph::new(format!( let input_buf = Paragraph::new(Spans::from(vec![
"> {}", Span::styled(
app.input_buffer().unwrap_or_else(|| "".into()) app.config()
)) .general
.prompt
.format
.clone()
.unwrap_or_default(),
app.config().general.prompt.style.into(),
),
Span::raw(app.input_buffer().unwrap_or_else(|| "".into())),
Span::styled(
app.config()
.general
.cursor
.format
.clone()
.unwrap_or_default(),
app.config().general.cursor.style.into(),
),
]))
.block(Block::default().borders(Borders::ALL).title(" Input ")); .block(Block::default().borders(Borders::ALL).title(" Input "));
f.render_widget(input_buf, rect); f.render_widget(input_buf, rect);
} }