From 5ad9f0f3e734fe9b4606bae65c24f27362986af8 Mon Sep 17 00:00:00 2001 From: Joscha Date: Wed, 10 Aug 2022 23:19:18 +0200 Subject: [PATCH] Include pastes in input events --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/ui.rs | 30 ++++++++++--------------- src/ui/chat.rs | 8 +++---- src/ui/chat/tree.rs | 47 +++++++++++++++++++--------------------- src/ui/euph/room.rs | 12 +++++----- src/ui/input.rs | 53 ++++++++++++++++++++++++++++++--------------- src/ui/rooms.rs | 10 ++++----- src/ui/util.rs | 12 +++++----- 9 files changed, 93 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7adfa03..2a9bedc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1248,7 +1248,7 @@ dependencies = [ [[package]] name = "toss" version = "0.1.0" -source = "git+https://github.com/Garmelon/toss.git?rev=fbe9e065fcc76f445c8e4feee04dcbf230586a4c#fbe9e065fcc76f445c8e4feee04dcbf230586a4c" +source = "git+https://github.com/Garmelon/toss.git?rev=7e429132458514e8dc99ab6be789b9c8225ed00e#7e429132458514e8dc99ab6be789b9c8225ed00e" dependencies = [ "crossterm", "unicode-linebreak", diff --git a/Cargo.toml b/Cargo.toml index 22920b5..ea25d8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ features = ["rustls-tls-native-roots"] [dependencies.toss] git = "https://github.com/Garmelon/toss.git" -rev = "fbe9e065fcc76f445c8e4feee04dcbf230586a4c" +rev = "7e429132458514e8dc99ab6be789b9c8225ed00e" # [patch."https://github.com/Garmelon/toss.git"] # toss = { path = "../toss/" } diff --git a/src/ui.rs b/src/ui.rs index 2398737..4b31d89 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -9,7 +9,7 @@ use std::convert::Infallible; use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; -use crossterm::event::{Event, KeyCode, MouseEvent}; +use crossterm::event::{Event, KeyCode}; use parking_lot::FairMutex; use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; @@ -21,7 +21,7 @@ use crate::vault::Vault; pub use self::chat::ChatMsg; use self::chat::ChatState; -use self::input::{key, KeyBindingsList, KeyEvent}; +use self::input::{key, InputEvent, KeyBindingsList, KeyEvent}; use self::rooms::Rooms; use self::widgets::layer::Layer; use self::widgets::list::ListState; @@ -158,12 +158,13 @@ impl Ui { let result = match event { UiEvent::Redraw => EventHandleResult::Continue, - UiEvent::Term(Event::Key(event)) => { - self.handle_key_event(event.into(), terminal, &crossterm_lock) - .await + UiEvent::Term(event) => { + if let Some(event) = InputEvent::from_event(event) { + self.handle_event(terminal, &crossterm_lock, &event).await + } else { + EventHandleResult::Continue + } } - UiEvent::Term(Event::Mouse(event)) => self.handle_mouse_event(event).await?, - UiEvent::Term(_) => EventHandleResult::Continue, }; match result { EventHandleResult::Continue => {} @@ -219,11 +220,11 @@ impl Ui { } } - async fn handle_key_event( + async fn handle_event( &mut self, - event: KeyEvent, terminal: &mut Terminal, crossterm_lock: &Arc>, + event: &InputEvent, ) -> EventHandleResult { if let key!(Ctrl + 'c') = event { // Exit unconditionally on ctrl+c. Previously, shift+q would also @@ -261,12 +262,12 @@ impl Ui { let handled = match self.mode { Mode::Main => { self.rooms - .handle_key_event(terminal, crossterm_lock, event) + .handle_event(terminal, crossterm_lock, event) .await } Mode::Log => self .log_chat - .handle_key_event(terminal, crossterm_lock, event, false) + .handle_event(terminal, crossterm_lock, event, false) .await .handled(), }; @@ -282,11 +283,4 @@ impl Ui { EventHandleResult::Continue } - - async fn handle_mouse_event( - &mut self, - _event: MouseEvent, - ) -> anyhow::Result { - Ok(EventHandleResult::Continue) - } } diff --git a/src/ui/chat.rs b/src/ui/chat.rs index 7040999..05a20d5 100644 --- a/src/ui/chat.rs +++ b/src/ui/chat.rs @@ -14,7 +14,7 @@ use crate::store::{Msg, MsgStore}; use self::tree::{TreeView, TreeViewState}; -use super::input::{KeyBindingsList, KeyEvent}; +use super::input::{InputEvent, KeyBindingsList}; use super::widgets::Widget; /////////// @@ -90,17 +90,17 @@ impl> ChatState { } } - pub async fn handle_key_event( + pub async fn handle_event( &mut self, terminal: &mut Terminal, crossterm_lock: &Arc>, - event: KeyEvent, + event: &InputEvent, can_compose: bool, ) -> Reaction { match self.mode { Mode::Tree => { self.tree - .handle_key_event(terminal, crossterm_lock, event, can_compose) + .handle_event(terminal, crossterm_lock, event, can_compose) .await } } diff --git a/src/ui/chat/tree.rs b/src/ui/chat/tree.rs index 1882c52..0aa5ec8 100644 --- a/src/ui/chat/tree.rs +++ b/src/ui/chat/tree.rs @@ -14,7 +14,7 @@ use toss::frame::{Frame, Pos, Size}; use toss::terminal::Terminal; use crate::store::{Msg, MsgStore}; -use crate::ui::input::{key, KeyBindingsList, KeyEvent}; +use crate::ui::input::{key, InputEvent, KeyBindingsList, KeyEvent}; use crate::ui::util; use crate::ui::widgets::editor::EditorState; use crate::ui::widgets::Widget; @@ -79,7 +79,7 @@ impl> InnerTreeViewState { bindings.binding("z", "center cursor on screen"); } - async fn handle_movement_key_event(&mut self, frame: &mut Frame, event: KeyEvent) -> bool { + async fn handle_movement_event(&mut self, frame: &mut Frame, event: &InputEvent) -> bool { let chat_height = frame.size().height - 3; match event { @@ -115,7 +115,7 @@ impl> InnerTreeViewState { bindings.binding("ctrl+s", "mark all older messages as seen"); } - async fn handle_action_key_event(&mut self, event: KeyEvent, id: Option<&M::Id>) -> bool { + async fn handle_action_event(&mut self, event: &InputEvent, id: Option<&M::Id>) -> bool { match event { key!(' ') => { if let Some(id) = id { @@ -162,9 +162,9 @@ impl> InnerTreeViewState { bindings.binding("t", "start a new thread"); } - async fn handle_edit_initiating_key_event( + async fn handle_edit_initiating_event( &mut self, - event: KeyEvent, + event: &InputEvent, id: Option, ) -> bool { match event { @@ -198,20 +198,20 @@ impl> InnerTreeViewState { } } - async fn handle_normal_key_event( + async fn handle_normal_event( &mut self, frame: &mut Frame, - event: KeyEvent, + event: &InputEvent, can_compose: bool, id: Option, ) -> bool { #[allow(clippy::if_same_then_else)] - if self.handle_movement_key_event(frame, event).await { + if self.handle_movement_event(frame, event).await { true - } else if self.handle_action_key_event(event, id.as_ref()).await { + } else if self.handle_action_event(event, id.as_ref()).await { true } else if can_compose { - self.handle_edit_initiating_key_event(event, id).await + self.handle_edit_initiating_event(event, id).await } else { false } @@ -223,11 +223,11 @@ impl> InnerTreeViewState { util::list_editor_key_bindings(bindings, |_| true, true); } - fn handle_editor_key_event( + fn handle_editor_event( &mut self, terminal: &mut Terminal, crossterm_lock: &Arc>, - event: KeyEvent, + event: &InputEvent, coming_from: Option, parent: Option, ) -> Reaction { @@ -250,7 +250,7 @@ impl> InnerTreeViewState { } _ => { - let handled = util::handle_editor_key_event( + let handled = util::handle_editor_event( &self.editor, terminal, crossterm_lock, @@ -281,17 +281,17 @@ impl> InnerTreeViewState { } } - async fn handle_key_event( + async fn handle_event( &mut self, terminal: &mut Terminal, crossterm_lock: &Arc>, - event: KeyEvent, + event: &InputEvent, can_compose: bool, ) -> Reaction { match &self.cursor { Cursor::Bottom => { if self - .handle_normal_key_event(terminal.frame(), event, can_compose, None) + .handle_normal_event(terminal.frame(), event, can_compose, None) .await { Reaction::Handled @@ -302,7 +302,7 @@ impl> InnerTreeViewState { Cursor::Msg(id) => { let id = id.clone(); if self - .handle_normal_key_event(terminal.frame(), event, can_compose, Some(id)) + .handle_normal_event(terminal.frame(), event, can_compose, Some(id)) .await { Reaction::Handled @@ -313,7 +313,7 @@ impl> InnerTreeViewState { Cursor::Editor { coming_from, parent, - } => self.handle_editor_key_event( + } => self.handle_editor_event( terminal, crossterm_lock, event, @@ -321,10 +321,7 @@ impl> InnerTreeViewState { parent.clone(), ), Cursor::Pseudo { .. } => { - if self - .handle_movement_key_event(terminal.frame(), event) - .await - { + if self.handle_movement_event(terminal.frame(), event).await { Reaction::Handled } else { Reaction::NotHandled @@ -367,17 +364,17 @@ impl> TreeViewState { self.0.lock().await.list_key_bindings(bindings, can_compose); } - pub async fn handle_key_event( + pub async fn handle_event( &mut self, terminal: &mut Terminal, crossterm_lock: &Arc>, - event: KeyEvent, + event: &InputEvent, can_compose: bool, ) -> Reaction { self.0 .lock() .await - .handle_key_event(terminal, crossterm_lock, event, can_compose) + .handle_event(terminal, crossterm_lock, event, can_compose) .await } diff --git a/src/ui/euph/room.rs b/src/ui/euph/room.rs index c68cade..a9f7b2d 100644 --- a/src/ui/euph/room.rs +++ b/src/ui/euph/room.rs @@ -13,7 +13,7 @@ use crate::euph::api::{SessionType, SessionView, Snowflake}; use crate::euph::{self, Joined, Status}; use crate::store::MsgStore; use crate::ui::chat::{ChatState, Reaction}; -use crate::ui::input::{key, KeyBindingsList, KeyEvent}; +use crate::ui::input::{key, InputEvent, KeyBindingsList, KeyEvent}; use crate::ui::widgets::background::Background; use crate::ui::widgets::border::Border; use crate::ui::widgets::editor::EditorState; @@ -352,11 +352,11 @@ impl EuphRoom { } } - pub async fn handle_key_event( + pub async fn handle_event( &mut self, terminal: &mut Terminal, crossterm_lock: &Arc>, - event: KeyEvent, + event: &InputEvent, ) -> bool { match &self.state { State::Normal => { @@ -365,7 +365,7 @@ impl EuphRoom { if let Ok(Some(Status::Joined(joined))) = room.status().await { match self .chat - .handle_key_event(terminal, crossterm_lock, event, true) + .handle_event(terminal, crossterm_lock, event, true) .await { Reaction::NotHandled => {} @@ -391,7 +391,7 @@ impl EuphRoom { } self.chat - .handle_key_event(terminal, crossterm_lock, event, false) + .handle_event(terminal, crossterm_lock, event, false) .await .handled() } @@ -407,7 +407,7 @@ impl EuphRoom { self.state = State::Normal; true } - _ => util::handle_editor_key_event( + _ => util::handle_editor_event( ed, terminal, crossterm_lock, diff --git a/src/ui/input.rs b/src/ui/input.rs index fbcf8ad..aa2e619 100644 --- a/src/ui/input.rs +++ b/src/ui/input.rs @@ -1,6 +1,6 @@ use std::convert::Infallible; -use crossterm::event::{KeyCode, KeyModifiers}; +use crossterm::event::{Event, KeyCode, KeyModifiers}; use crossterm::style::{ContentStyle, Stylize}; use toss::styled::Styled; @@ -16,6 +16,22 @@ use super::widgets::resize::Resize; use super::widgets::text::Text; use super::widgets::BoxedWidget; +#[derive(Debug, Clone)] +pub enum InputEvent { + Key(KeyEvent), + Paste(String), +} + +impl InputEvent { + pub fn from_event(event: Event) -> Option { + match event { + crossterm::event::Event::Key(key) => Some(Self::Key(key.into())), + crossterm::event::Event::Paste(text) => Some(Self::Paste(text)), + _ => None, + } + } +} + /// A key event data type that is a bit easier to pattern match on than /// [`crossterm::event::KeyEvent`]. #[derive(Debug, Clone, Copy)] @@ -39,27 +55,30 @@ impl From for KeyEvent { #[rustfmt::skip] macro_rules! key { + // key!(Paste text) + ( Paste $text:ident ) => { InputEvent::Paste($text) }; + // key!('a') - ( $key:literal ) => { KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: false, } }; - ( Ctrl + $key:literal ) => { KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: true, alt: false, } }; - ( Alt + $key:literal ) => { KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: true, } }; + ( $key:literal ) => { InputEvent::Key(KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: false, }) }; + ( Ctrl + $key:literal ) => { InputEvent::Key(KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: true, alt: false, }) }; + ( Alt + $key:literal ) => { InputEvent::Key(KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: true, }) }; - // key!(Char(xyz)) - ( Char $key:pat ) => { KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: false, } }; - ( Ctrl + Char $key:pat ) => { KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: true, alt: false, } }; - ( Alt + Char $key:pat ) => { KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: true, } }; + // key!(Char c) + ( Char $key:pat ) => { InputEvent::Key(KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: false, }) }; + ( Ctrl + Char $key:pat ) => { InputEvent::Key(KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: true, alt: false, }) }; + ( Alt + Char $key:pat ) => { InputEvent::Key(KeyEvent { code: KeyCode::Char($key), shift: _, ctrl: false, alt: true, }) }; - // key!(F(n)) - ( F $key:pat ) => { KeyEvent { code: KeyCode::F($key), shift: false, ctrl: false, alt: false, } }; - ( Shift + F $key:pat ) => { KeyEvent { code: KeyCode::F($key), shift: true, ctrl: false, alt: false, } }; - ( Ctrl + F $key:pat ) => { KeyEvent { code: KeyCode::F($key), shift: false, ctrl: true, alt: false, } }; - ( Alt + F $key:pat ) => { KeyEvent { code: KeyCode::F($key), shift: false, ctrl: false, alt: true, } }; + // key!(F n) + ( F $key:pat ) => { InputEvent::Key(KeyEvent { code: KeyCode::F($key), shift: false, ctrl: false, alt: false, }) }; + ( Shift + F $key:pat ) => { InputEvent::Key(KeyEvent { code: KeyCode::F($key), shift: true, ctrl: false, alt: false, }) }; + ( Ctrl + F $key:pat ) => { InputEvent::Key(KeyEvent { code: KeyCode::F($key), shift: false, ctrl: true, alt: false, }) }; + ( Alt + F $key:pat ) => { InputEvent::Key(KeyEvent { code: KeyCode::F($key), shift: false, ctrl: false, alt: true, }) }; // key!(other) - ( $key:ident ) => { KeyEvent { code: KeyCode::$key, shift: false, ctrl: false, alt: false, } }; - ( Shift + $key:ident ) => { KeyEvent { code: KeyCode::$key, shift: true, ctrl: false, alt: false, } }; - ( Ctrl + $key:ident ) => { KeyEvent { code: KeyCode::$key, shift: false, ctrl: true, alt: false, } }; - ( Alt + $key:ident ) => { KeyEvent { code: KeyCode::$key, shift: false, ctrl: false, alt: true, } }; + ( $key:ident ) => { InputEvent::Key(KeyEvent { code: KeyCode::$key, shift: false, ctrl: false, alt: false, }) }; + ( Shift + $key:ident ) => { InputEvent::Key(KeyEvent { code: KeyCode::$key, shift: true, ctrl: false, alt: false, }) }; + ( Ctrl + $key:ident ) => { InputEvent::Key(KeyEvent { code: KeyCode::$key, shift: false, ctrl: true, alt: false, }) }; + ( Alt + $key:ident ) => { InputEvent::Key(KeyEvent { code: KeyCode::$key, shift: false, ctrl: false, alt: true, }) }; } pub(crate) use key; diff --git a/src/ui/rooms.rs b/src/ui/rooms.rs index 672cb6f..b4a5298 100644 --- a/src/ui/rooms.rs +++ b/src/ui/rooms.rs @@ -14,7 +14,7 @@ use crate::euph::{Joined, Status}; use crate::vault::Vault; use super::euph::room::EuphRoom; -use super::input::{key, KeyBindingsList, KeyEvent}; +use super::input::{key, InputEvent, KeyBindingsList, KeyEvent}; use super::widgets::background::Background; use super::widgets::border::Border; use super::widgets::editor::EditorState; @@ -270,11 +270,11 @@ impl Rooms { } } - pub async fn handle_key_event( + pub async fn handle_event( &mut self, terminal: &mut Terminal, crossterm_lock: &Arc>, - event: KeyEvent, + event: &InputEvent, ) -> bool { self.stabilize_rooms().await; @@ -318,7 +318,7 @@ impl Rooms { }, State::ShowRoom(name) => { if let Some(room) = self.euph_rooms.get_mut(name) { - if room.handle_key_event(terminal, crossterm_lock, event).await { + if room.handle_event(terminal, crossterm_lock, event).await { return true; } @@ -340,7 +340,7 @@ impl Rooms { } } _ => { - return util::handle_editor_key_event( + return util::handle_editor_event( ed, terminal, crossterm_lock, diff --git a/src/ui/util.rs b/src/ui/util.rs index c985862..2d56380 100644 --- a/src/ui/util.rs +++ b/src/ui/util.rs @@ -4,7 +4,7 @@ use crossterm::event::KeyCode; use parking_lot::FairMutex; use toss::terminal::Terminal; -use super::input::{key, KeyBindingsList, KeyEvent}; +use super::input::{key, InputEvent, KeyBindingsList, KeyEvent}; use super::widgets::editor::EditorState; pub fn prompt( @@ -59,11 +59,11 @@ pub fn list_editor_key_bindings( bindings.binding("↑/↓", "move cursor up/down"); } -pub fn handle_editor_key_event( +pub fn handle_editor_event( editor: &EditorState, terminal: &mut Terminal, crossterm_lock: &Arc>, - event: KeyEvent, + event: &InputEvent, char_filter: impl Fn(char) -> bool, can_edit_externally: bool, ) -> bool { @@ -71,13 +71,13 @@ pub fn handle_editor_key_event( // Enter with *any* modifier pressed - if ctrl and shift don't // work, maybe alt does key!(Enter) => return false, - KeyEvent { + InputEvent::Key(KeyEvent { code: KeyCode::Enter, .. - } if char_filter('\n') => editor.insert_char(terminal.frame(), '\n'), + }) 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!(Char ch) if char_filter(*ch) => editor.insert_char(terminal.frame(), *ch), key!(Ctrl + 'h') | key!(Backspace) => editor.backspace(terminal.frame()), key!(Ctrl + 'd') | key!(Delete) => editor.delete(), key!(Ctrl + 'l') => editor.clear(),