From bfbdec4396905124d8475582ea411105da1dd476 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 6 Aug 2022 23:38:47 +0200 Subject: [PATCH] Move editor key handling to one place --- src/ui/chat/tree.rs | 39 +++++++++++--------------- src/ui/input.rs | 5 +++- src/ui/room.rs | 47 ++++++++++++++++++-------------- src/ui/rooms.rs | 26 ++++++++++-------- src/ui/util.rs | 59 ++++++++++++++++++++++++++++++++++++++++ src/ui/widgets/editor.rs | 3 -- 6 files changed, 120 insertions(+), 59 deletions(-) diff --git a/src/ui/chat/tree.rs b/src/ui/chat/tree.rs index 5ad7040..34d18a5 100644 --- a/src/ui/chat/tree.rs +++ b/src/ui/chat/tree.rs @@ -14,6 +14,7 @@ use toss::terminal::Terminal; use crate::store::{Msg, MsgStore}; use crate::ui::input::{key, KeyBindingsList, KeyEvent}; +use crate::ui::util; use crate::ui::widgets::editor::EditorState; use crate::ui::widgets::Widget; @@ -150,14 +151,10 @@ impl> InnerTreeViewState { } } - 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("enter", "send message"); - bindings.binding("←/→", "move cursor left/right"); - 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"); + util::list_editor_key_bindings(bindings, |_| true, true); } fn handle_editor_key_event( @@ -186,23 +183,19 @@ impl> InnerTreeViewState { } } - // Enter with *any* modifier pressed - if ctrl and shift don't - // work, maybe alt does - KeyEvent { - code: KeyCode::Enter, - .. - } => self.editor.insert_char(terminal.frame(), '\n'), - - key!(Char ch) => self.editor.insert_char(terminal.frame(), ch), - key!(Left) => self.editor.move_cursor_left(terminal.frame()), - key!(Right) => self.editor.move_cursor_right(terminal.frame()), - key!(Up) => self.editor.move_cursor_up(terminal.frame()), - 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, + _ => { + let handled = util::handle_editor_key_event( + &self.editor, + terminal, + crossterm_lock, + event, + |_| true, + true, + ); + if !handled { + return Reaction::NotHandled; + } + } } self.correction = Some(Correction::MakeCursorVisible); diff --git a/src/ui/input.rs b/src/ui/input.rs index b6cda4d..bcd366f 100644 --- a/src/ui/input.rs +++ b/src/ui/input.rs @@ -107,7 +107,10 @@ impl KeyBindingsList { pub fn binding(&mut self, binding: &str, description: &str) { 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)), ]); self.0.add_unsel(widget); diff --git a/src/ui/room.rs b/src/ui/room.rs index d18d03f..72bdd0d 100644 --- a/src/ui/room.rs +++ b/src/ui/room.rs @@ -26,7 +26,7 @@ use super::widgets::list::{List, ListState}; use super::widgets::padding::Padding; use super::widgets::text::Text; use super::widgets::BoxedWidget; -use super::UiEvent; +use super::{util, UiEvent}; enum State { Normal, @@ -302,6 +302,10 @@ impl EuphRoom { list.into() } + fn nick_char(c: char) -> bool { + c != '\n' + } + pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { bindings.heading("Room"); @@ -326,9 +330,7 @@ impl EuphRoom { State::ChooseNick(_) => { bindings.binding("esc", "abort"); bindings.binding("enter", "set nick"); - bindings.binding("←/→", "move cursor left/right"); - bindings.binding("backspace", "delete before cursor"); - bindings.binding("delete", "delete after cursor"); + util::list_editor_key_bindings(bindings, Self::nick_char, false); } } } @@ -376,24 +378,27 @@ impl EuphRoom { .await .handled() } - State::ChooseNick(ed) => { - match event { - key!(Esc) => self.state = State::Normal, - key!(Enter) => { - 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, + State::ChooseNick(ed) => match event { + key!(Esc) => { + self.state = State::Normal; + true } - 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, + ), + }, } } } diff --git a/src/ui/rooms.rs b/src/ui/rooms.rs index a6365e0..e73d625 100644 --- a/src/ui/rooms.rs +++ b/src/ui/rooms.rs @@ -25,7 +25,7 @@ use super::widgets::list::{List, ListState}; use super::widgets::padding::Padding; use super::widgets::text::Text; use super::widgets::BoxedWidget; -use super::UiEvent; +use super::{util, UiEvent}; enum State { ShowList, @@ -206,6 +206,10 @@ impl Rooms { list.into() } + fn room_char(c: char) -> bool { + c.is_ascii_alphanumeric() || c == '_' + } + pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { match &self.state { State::ShowList => { @@ -239,9 +243,7 @@ impl Rooms { bindings.heading("Rooms"); bindings.binding("esc", "abort"); bindings.binding("enter", "connect to room"); - bindings.binding("←/→", "move cursor left/right"); - bindings.binding("backspace", "delete before cursor"); - bindings.binding("delete", "delete after cursor"); + util::list_editor_key_bindings(bindings, Self::room_char, false); } } } @@ -311,14 +313,16 @@ impl Rooms { 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, }, } diff --git a/src/ui/util.rs b/src/ui/util.rs index 7507ed3..2449459 100644 --- a/src/ui/util.rs +++ b/src/ui/util.rs @@ -1,8 +1,12 @@ use std::sync::Arc; +use crossterm::event::KeyCode; use parking_lot::FairMutex; use toss::terminal::Terminal; +use super::input::{key, KeyBindingsList, KeyEvent}; +use super::widgets::editor::EditorState; + pub fn prompt( terminal: &mut Terminal, crossterm_lock: &Arc>, @@ -25,3 +29,58 @@ pub fn prompt( 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+", "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>, + 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 +} diff --git a/src/ui/widgets/editor.rs b/src/ui/widgets/editor.rs index 03eb8a4..53db6ec 100644 --- a/src/ui/widgets/editor.rs +++ b/src/ui/widgets/editor.rs @@ -330,9 +330,6 @@ impl EditorState { } } } - - // TODO Share key binding code - // TODO Support more of the emacs-y bindings, see bash as example } ////////////