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