Move editor key handling to one place
This commit is contained in:
parent
f48a4a6416
commit
bfbdec4396
6 changed files with 120 additions and 59 deletions
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -330,9 +330,6 @@ impl EditorState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Share key binding code
|
|
||||||
// TODO Support more of the emacs-y bindings, see bash as example
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////
|
////////////
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue