Move editor key handling to one place

This commit is contained in:
Joscha 2022-08-06 23:38:47 +02:00
parent f48a4a6416
commit bfbdec4396
6 changed files with 120 additions and 59 deletions

View file

@ -14,6 +14,7 @@ use toss::terminal::Terminal;
use crate::store::{Msg, MsgStore}; use crate::store::{Msg, MsgStore};
use crate::ui::input::{key, KeyBindingsList, KeyEvent}; use crate::ui::input::{key, KeyBindingsList, KeyEvent};
use crate::ui::util;
use crate::ui::widgets::editor::EditorState; use crate::ui::widgets::editor::EditorState;
use crate::ui::widgets::Widget; use crate::ui::widgets::Widget;
@ -150,14 +151,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
} }
} }
pub fn list_editor_key_bindings(&self, bindings: &mut KeyBindingsList) { fn list_editor_key_bindings(&self, bindings: &mut KeyBindingsList) {
bindings.binding("esc", "close editor"); bindings.binding("esc", "close editor");
bindings.binding("enter", "send message"); bindings.binding("enter", "send message");
bindings.binding("←/→", "move cursor left/right"); util::list_editor_key_bindings(bindings, |_| true, true);
bindings.binding("backspace", "delete before cursor");
bindings.binding("delete", "delete after cursor");
bindings.binding("ctrl+e", "edit in $EDITOR");
bindings.binding("ctrl+l", "clear editor contents");
} }
fn handle_editor_key_event( fn handle_editor_key_event(
@ -186,23 +183,19 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
} }
} }
// Enter with *any* modifier pressed - if ctrl and shift don't _ => {
// work, maybe alt does let handled = util::handle_editor_key_event(
KeyEvent { &self.editor,
code: KeyCode::Enter, terminal,
.. crossterm_lock,
} => self.editor.insert_char(terminal.frame(), '\n'), event,
|_| true,
key!(Char ch) => self.editor.insert_char(terminal.frame(), ch), true,
key!(Left) => self.editor.move_cursor_left(terminal.frame()), );
key!(Right) => self.editor.move_cursor_right(terminal.frame()), if !handled {
key!(Up) => self.editor.move_cursor_up(terminal.frame()), return Reaction::NotHandled;
key!(Down) => self.editor.move_cursor_down(terminal.frame()), }
key!(Backspace) => self.editor.backspace(terminal.frame()), }
key!(Delete) => self.editor.delete(),
key!(Ctrl + 'e') => self.editor.edit_externally(terminal, crossterm_lock),
key!(Ctrl + 'l') => self.editor.clear(),
_ => return Reaction::NotHandled,
} }
self.correction = Some(Correction::MakeCursorVisible); self.correction = Some(Correction::MakeCursorVisible);

View file

@ -107,7 +107,10 @@ impl KeyBindingsList {
pub fn binding(&mut self, binding: &str, description: &str) { pub fn binding(&mut self, binding: &str, description: &str) {
let widget = HJoin::new(vec![ let widget = HJoin::new(vec![
Segment::new(Resize::new(Text::new((binding, Self::binding_style()))).min_width(16)), Segment::new(
Resize::new(Padding::new(Text::new((binding, Self::binding_style()))).right(1))
.min_width(16),
),
Segment::new(Text::new(description)), Segment::new(Text::new(description)),
]); ]);
self.0.add_unsel(widget); self.0.add_unsel(widget);

View file

@ -26,7 +26,7 @@ use super::widgets::list::{List, ListState};
use super::widgets::padding::Padding; use super::widgets::padding::Padding;
use super::widgets::text::Text; use super::widgets::text::Text;
use super::widgets::BoxedWidget; use super::widgets::BoxedWidget;
use super::UiEvent; use super::{util, UiEvent};
enum State { enum State {
Normal, Normal,
@ -302,6 +302,10 @@ impl EuphRoom {
list.into() list.into()
} }
fn nick_char(c: char) -> bool {
c != '\n'
}
pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) {
bindings.heading("Room"); bindings.heading("Room");
@ -326,9 +330,7 @@ impl EuphRoom {
State::ChooseNick(_) => { State::ChooseNick(_) => {
bindings.binding("esc", "abort"); bindings.binding("esc", "abort");
bindings.binding("enter", "set nick"); bindings.binding("enter", "set nick");
bindings.binding("←/→", "move cursor left/right"); util::list_editor_key_bindings(bindings, Self::nick_char, false);
bindings.binding("backspace", "delete before cursor");
bindings.binding("delete", "delete after cursor");
} }
} }
} }
@ -376,24 +378,27 @@ impl EuphRoom {
.await .await
.handled() .handled()
} }
State::ChooseNick(ed) => { State::ChooseNick(ed) => match event {
match event { key!(Esc) => {
key!(Esc) => self.state = State::Normal, self.state = State::Normal;
key!(Enter) => { true
if let Some(room) = &self.room {
let _ = room.nick(ed.text());
}
self.state = State::Normal;
}
key!(Char ch) => ed.insert_char(terminal.frame(), ch),
key!(Backspace) => ed.backspace(terminal.frame()),
key!(Left) => ed.move_cursor_left(terminal.frame()),
key!(Right) => ed.move_cursor_right(terminal.frame()),
key!(Delete) => ed.delete(),
_ => return false,
} }
true key!(Enter) => {
} if let Some(room) = &self.room {
let _ = room.nick(ed.text());
}
self.state = State::Normal;
true
}
_ => util::handle_editor_key_event(
ed,
terminal,
crossterm_lock,
event,
Self::nick_char,
false,
),
},
} }
} }
} }

View file

@ -25,7 +25,7 @@ use super::widgets::list::{List, ListState};
use super::widgets::padding::Padding; use super::widgets::padding::Padding;
use super::widgets::text::Text; use super::widgets::text::Text;
use super::widgets::BoxedWidget; use super::widgets::BoxedWidget;
use super::UiEvent; use super::{util, UiEvent};
enum State { enum State {
ShowList, ShowList,
@ -206,6 +206,10 @@ impl Rooms {
list.into() list.into()
} }
fn room_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_'
}
pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) {
match &self.state { match &self.state {
State::ShowList => { State::ShowList => {
@ -239,9 +243,7 @@ impl Rooms {
bindings.heading("Rooms"); bindings.heading("Rooms");
bindings.binding("esc", "abort"); bindings.binding("esc", "abort");
bindings.binding("enter", "connect to room"); bindings.binding("enter", "connect to room");
bindings.binding("←/→", "move cursor left/right"); util::list_editor_key_bindings(bindings, Self::room_char, false);
bindings.binding("backspace", "delete before cursor");
bindings.binding("delete", "delete after cursor");
} }
} }
} }
@ -311,14 +313,16 @@ impl Rooms {
self.state = State::ShowRoom(name); self.state = State::ShowRoom(name);
} }
} }
key!(Char ch) if ch.is_ascii_alphanumeric() || ch == '_' => { _ => {
ed.insert_char(terminal.frame(), ch) return util::handle_editor_key_event(
ed,
terminal,
crossterm_lock,
event,
Self::room_char,
false,
)
} }
key!(Left) => ed.move_cursor_left(terminal.frame()),
key!(Right) => ed.move_cursor_right(terminal.frame()),
key!(Backspace) => ed.backspace(terminal.frame()),
key!(Delete) => ed.delete(),
_ => return false,
}, },
} }

View file

@ -1,8 +1,12 @@
use std::sync::Arc; use std::sync::Arc;
use crossterm::event::KeyCode;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use toss::terminal::Terminal; use toss::terminal::Terminal;
use super::input::{key, KeyBindingsList, KeyEvent};
use super::widgets::editor::EditorState;
pub fn prompt( pub fn prompt(
terminal: &mut Terminal, terminal: &mut Terminal,
crossterm_lock: &Arc<FairMutex<()>>, crossterm_lock: &Arc<FairMutex<()>>,
@ -25,3 +29,58 @@ pub fn prompt(
Some(content) Some(content)
} }
} }
// TODO Support more of the emacs-y bindings, see bash as example
pub fn list_editor_key_bindings(
bindings: &mut KeyBindingsList,
char_filter: impl Fn(char) -> bool,
can_edit_externally: bool,
) {
if char_filter('\n') {
bindings.binding("enter+<any modifier>", "insert newline");
}
bindings.binding("backspace", "delete before cursor");
bindings.binding("delete", "delete after cursor");
bindings.binding("ctrl+l", "clear editor contents");
if can_edit_externally {
bindings.binding("ctrl+e", "edit in $EDITOR");
}
bindings.binding("arrow keys", "move cursor");
}
pub fn handle_editor_key_event(
editor: &EditorState,
terminal: &mut Terminal,
crossterm_lock: &Arc<FairMutex<()>>,
event: KeyEvent,
char_filter: impl Fn(char) -> bool,
can_edit_externally: bool,
) -> bool {
match event {
// Enter with *any* modifier pressed - if ctrl and shift don't
// work, maybe alt does
key!(Enter) => return false,
KeyEvent {
code: KeyCode::Enter,
..
} if char_filter('\n') => editor.insert_char(terminal.frame(), '\n'),
// Editing
key!(Char ch) if char_filter(ch) => editor.insert_char(terminal.frame(), ch),
key!(Backspace) => editor.backspace(terminal.frame()),
key!(Delete) => editor.delete(),
key!(Ctrl + 'l') => editor.clear(),
key!(Ctrl + 'e') if can_edit_externally => editor.edit_externally(terminal, crossterm_lock),
// Cursor movement
key!(Left) => editor.move_cursor_left(terminal.frame()),
key!(Right) => editor.move_cursor_right(terminal.frame()),
key!(Up) => editor.move_cursor_up(terminal.frame()),
key!(Down) => editor.move_cursor_down(terminal.frame()),
_ => return false,
}
true
}

View file

@ -330,9 +330,6 @@ impl EditorState {
} }
} }
} }
// TODO Share key binding code
// TODO Support more of the emacs-y bindings, see bash as example
} }
//////////// ////////////