Match key events using macros
This commit is contained in:
parent
df0403a782
commit
20ea96f83e
7 changed files with 152 additions and 155 deletions
22
src/ui.rs
22
src/ui.rs
|
|
@ -1,4 +1,5 @@
|
||||||
mod chat;
|
mod chat;
|
||||||
|
mod input;
|
||||||
mod room;
|
mod room;
|
||||||
mod rooms;
|
mod rooms;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
@ -7,7 +8,7 @@ mod widgets;
|
||||||
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, KeyEvent, KeyModifiers, MouseEvent};
|
use crossterm::event::{Event, KeyCode, MouseEvent};
|
||||||
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};
|
||||||
|
|
@ -19,6 +20,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, KeyEvent};
|
||||||
use self::rooms::Rooms;
|
use self::rooms::Rooms;
|
||||||
use self::widgets::BoxedWidget;
|
use self::widgets::BoxedWidget;
|
||||||
|
|
||||||
|
|
@ -152,7 +154,7 @@ 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::Key(event)) => {
|
||||||
self.handle_key_event(event, terminal, &crossterm_lock)
|
self.handle_key_event(event.into(), terminal, &crossterm_lock)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
UiEvent::Term(Event::Mouse(event)) => self.handle_mouse_event(event).await?,
|
UiEvent::Term(Event::Mouse(event)) => self.handle_mouse_event(event).await?,
|
||||||
|
|
@ -193,17 +195,13 @@ impl Ui {
|
||||||
terminal: &mut Terminal,
|
terminal: &mut Terminal,
|
||||||
crossterm_lock: &Arc<FairMutex<()>>,
|
crossterm_lock: &Arc<FairMutex<()>>,
|
||||||
) -> EventHandleResult {
|
) -> EventHandleResult {
|
||||||
// Always exit when ctrl+c is pressed. Previously, shift+q would also
|
match event {
|
||||||
// unconditionally quit cove, but that interfered with typing text in
|
// Exit unconditionally on ctrl+c. Previously, shift+q would also
|
||||||
|
// unconditionally exit, but that interfered with typing text in
|
||||||
// inline editors.
|
// inline editors.
|
||||||
let ctrl_c = event.modifiers == KeyModifiers::CONTROL && event.code == KeyCode::Char('c');
|
key!(Ctrl + 'c') => return EventHandleResult::Stop,
|
||||||
if ctrl_c {
|
key!(F 1) => self.mode = Mode::Main,
|
||||||
return EventHandleResult::Stop;
|
key!(F 2) => self.mode = Mode::Log,
|
||||||
}
|
|
||||||
|
|
||||||
match event.code {
|
|
||||||
KeyCode::F(1) => self.mode = Mode::Main,
|
|
||||||
KeyCode::F(2) => self.mode = Mode::Log,
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ mod tree;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crossterm::event::KeyEvent;
|
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use toss::frame::{Frame, Size};
|
use toss::frame::{Frame, Size};
|
||||||
|
|
@ -15,6 +14,7 @@ use crate::store::{Msg, MsgStore};
|
||||||
|
|
||||||
use self::tree::{TreeView, TreeViewState};
|
use self::tree::{TreeView, TreeViewState};
|
||||||
|
|
||||||
|
use super::input::KeyEvent;
|
||||||
use super::widgets::Widget;
|
use super::widgets::Widget;
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,14 @@ mod widgets;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::KeyCode;
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use toss::frame::{Frame, Pos, Size};
|
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, KeyEvent};
|
||||||
use crate::ui::widgets::editor::EditorState;
|
use crate::ui::widgets::editor::EditorState;
|
||||||
use crate::ui::widgets::Widget;
|
use crate::ui::widgets::Widget;
|
||||||
|
|
||||||
|
|
@ -66,101 +67,61 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
coming_from: Option<M::Id>,
|
coming_from: Option<M::Id>,
|
||||||
parent: Option<M::Id>,
|
parent: Option<M::Id>,
|
||||||
) -> Reaction<M> {
|
) -> Reaction<M> {
|
||||||
let harmless_char = (event.modifiers - KeyModifiers::SHIFT).is_empty();
|
|
||||||
|
|
||||||
// TODO Tab-completion
|
// TODO Tab-completion
|
||||||
match event.code {
|
match event {
|
||||||
KeyCode::Esc => {
|
key!(Esc) => {
|
||||||
self.cursor = coming_from.map(Cursor::Msg).unwrap_or(Cursor::Bottom);
|
self.cursor = coming_from.map(Cursor::Msg).unwrap_or(Cursor::Bottom);
|
||||||
Reaction::Handled
|
return Reaction::Handled;
|
||||||
}
|
}
|
||||||
KeyCode::Enter if event.modifiers.is_empty() => {
|
|
||||||
|
key!(Enter) => {
|
||||||
let content = self.editor.text();
|
let content = self.editor.text();
|
||||||
if content.trim().is_empty() {
|
if !content.trim().is_empty() {
|
||||||
Reaction::Handled
|
|
||||||
} else {
|
|
||||||
self.cursor = Cursor::Pseudo {
|
self.cursor = Cursor::Pseudo {
|
||||||
coming_from,
|
coming_from,
|
||||||
parent: parent.clone(),
|
parent: parent.clone(),
|
||||||
};
|
};
|
||||||
Reaction::Composed { parent, content }
|
return Reaction::Composed { parent, content };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Enter => {
|
|
||||||
// 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
|
||||||
self.editor.insert_char('\n');
|
KeyEvent {
|
||||||
|
code: KeyCode::Enter,
|
||||||
|
..
|
||||||
|
} => self.editor.insert_char('\n'),
|
||||||
|
|
||||||
|
key!(Char ch) => self.editor.insert_char(ch),
|
||||||
|
key!(Backspace) => self.editor.backspace(),
|
||||||
|
key!(Left) => self.editor.move_cursor_left(),
|
||||||
|
key!(Right) => self.editor.move_cursor_right(),
|
||||||
|
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);
|
||||||
Reaction::Handled
|
Reaction::Handled
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => {
|
|
||||||
self.editor.backspace();
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
|
||||||
Reaction::Handled
|
|
||||||
}
|
|
||||||
KeyCode::Left => {
|
|
||||||
self.editor.move_cursor_left();
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
|
||||||
Reaction::Handled
|
|
||||||
}
|
|
||||||
KeyCode::Right => {
|
|
||||||
self.editor.move_cursor_right();
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
|
||||||
Reaction::Handled
|
|
||||||
}
|
|
||||||
KeyCode::Delete => {
|
|
||||||
self.editor.delete();
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
|
||||||
Reaction::Handled
|
|
||||||
}
|
|
||||||
KeyCode::Char(ch) if harmless_char => {
|
|
||||||
self.editor.insert_char(ch);
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
|
||||||
Reaction::Handled
|
|
||||||
}
|
|
||||||
KeyCode::Char('e') if event.modifiers == KeyModifiers::CONTROL => {
|
|
||||||
self.editor.edit_externally(terminal, crossterm_lock);
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
|
||||||
Reaction::Handled
|
|
||||||
}
|
|
||||||
KeyCode::Char('l') if event.modifiers == KeyModifiers::CONTROL => {
|
|
||||||
self.editor.clear();
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
|
||||||
Reaction::Handled
|
|
||||||
}
|
|
||||||
_ => Reaction::NotHandled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_movement_key_event(&mut self, frame: &mut Frame, event: KeyEvent) -> bool {
|
async fn handle_movement_key_event(&mut self, frame: &mut Frame, event: KeyEvent) -> bool {
|
||||||
let chat_height = frame.size().height - 3;
|
let chat_height = frame.size().height - 3;
|
||||||
let shift_only = event.modifiers.difference(KeyModifiers::SHIFT).is_empty();
|
|
||||||
|
|
||||||
match event.code {
|
match event {
|
||||||
KeyCode::Char('k') | KeyCode::Up if shift_only => self.move_cursor_up().await,
|
key!('k') | key!(Up) => self.move_cursor_up().await,
|
||||||
KeyCode::Char('j') | KeyCode::Down if shift_only => self.move_cursor_down().await,
|
key!('j') | key!(Down) => self.move_cursor_down().await,
|
||||||
KeyCode::Char('h') | KeyCode::Left if shift_only => self.move_cursor_older().await,
|
key!('h') | key!(Left) => self.move_cursor_older().await,
|
||||||
KeyCode::Char('l') | KeyCode::Right if shift_only => self.move_cursor_newer().await,
|
key!('l') | key!(Right) => self.move_cursor_newer().await,
|
||||||
KeyCode::Char('g') | KeyCode::Home if shift_only => self.move_cursor_to_top().await,
|
key!('g') | key!(Home) => self.move_cursor_to_top().await,
|
||||||
KeyCode::Char('G') | KeyCode::End if shift_only => self.move_cursor_to_bottom().await,
|
key!('G') | key!(End) => self.move_cursor_to_bottom().await,
|
||||||
KeyCode::Char('y') if event.modifiers == KeyModifiers::CONTROL => self.scroll_up(1),
|
key!(Ctrl + 'y') => self.scroll_up(1),
|
||||||
KeyCode::Char('e') if event.modifiers == KeyModifiers::CONTROL => self.scroll_down(1),
|
key!(Ctrl + 'e') => self.scroll_down(1),
|
||||||
KeyCode::Char('u') if event.modifiers == KeyModifiers::CONTROL => {
|
key!(Ctrl + 'u') => self.scroll_up((chat_height / 2).into()),
|
||||||
let delta = chat_height / 2;
|
key!(Ctrl + 'd') => self.scroll_down((chat_height / 2).into()),
|
||||||
self.scroll_up(delta.into());
|
key!(Ctrl + 'b') => self.scroll_up(chat_height.saturating_sub(1).into()),
|
||||||
}
|
key!(Ctrl + 'f') => self.scroll_down(chat_height.saturating_sub(1).into()),
|
||||||
KeyCode::Char('d') if event.modifiers == KeyModifiers::CONTROL => {
|
|
||||||
let delta = chat_height / 2;
|
|
||||||
self.scroll_down(delta.into());
|
|
||||||
}
|
|
||||||
KeyCode::Char('b') if event.modifiers == KeyModifiers::CONTROL => {
|
|
||||||
let delta = chat_height.saturating_sub(1);
|
|
||||||
self.scroll_up(delta.into());
|
|
||||||
}
|
|
||||||
KeyCode::Char('f') if event.modifiers == KeyModifiers::CONTROL => {
|
|
||||||
let delta = chat_height.saturating_sub(1);
|
|
||||||
self.scroll_down(delta.into());
|
|
||||||
}
|
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,35 +133,21 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
event: KeyEvent,
|
event: KeyEvent,
|
||||||
id: Option<M::Id>,
|
id: Option<M::Id>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let shift_only = event.modifiers.difference(KeyModifiers::SHIFT).is_empty();
|
match event {
|
||||||
if !shift_only {
|
key!('r') => {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match event.code {
|
|
||||||
KeyCode::Char('r') => {
|
|
||||||
if let Some(parent) = self.parent_for_normal_reply().await {
|
if let Some(parent) = self.parent_for_normal_reply().await {
|
||||||
self.cursor = Cursor::Editor {
|
self.cursor = Cursor::editor(id, parent);
|
||||||
coming_from: id,
|
|
||||||
parent,
|
|
||||||
};
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('R') => {
|
key!('R') => {
|
||||||
if let Some(parent) = self.parent_for_alternate_reply().await {
|
if let Some(parent) = self.parent_for_alternate_reply().await {
|
||||||
self.cursor = Cursor::Editor {
|
self.cursor = Cursor::editor(id, parent);
|
||||||
coming_from: id,
|
|
||||||
parent,
|
|
||||||
};
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('t' | 'T') => {
|
key!('t') | key!('T') => {
|
||||||
self.cursor = Cursor::Editor {
|
self.cursor = Cursor::editor(id, None);
|
||||||
coming_from: id,
|
|
||||||
parent: None,
|
|
||||||
};
|
|
||||||
self.correction = Some(Correction::MakeCursorVisible);
|
self.correction = Some(Correction::MakeCursorVisible);
|
||||||
}
|
}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,15 @@ pub enum Cursor<I> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I> Cursor<I> {
|
||||||
|
pub fn editor(coming_from: Option<I>, parent: Option<I>) -> Self {
|
||||||
|
Self::Editor {
|
||||||
|
coming_from,
|
||||||
|
parent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<I: Eq> Cursor<I> {
|
impl<I: Eq> Cursor<I> {
|
||||||
pub fn refers_to(&self, id: &I) -> bool {
|
pub fn refers_to(&self, id: &I) -> bool {
|
||||||
if let Self::Msg(own_id) = self {
|
if let Self::Msg(own_id) = self {
|
||||||
|
|
|
||||||
48
src/ui/input.rs
Normal file
48
src/ui/input.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
|
|
||||||
|
/// A key event data type that is a bit easier to pattern match on than
|
||||||
|
/// [`crossterm::event::KeyEvent`].
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct KeyEvent {
|
||||||
|
pub code: KeyCode,
|
||||||
|
pub shift: bool,
|
||||||
|
pub ctrl: bool,
|
||||||
|
pub alt: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crossterm::event::KeyEvent> for KeyEvent {
|
||||||
|
fn from(event: crossterm::event::KeyEvent) -> Self {
|
||||||
|
Self {
|
||||||
|
code: event.code,
|
||||||
|
shift: event.modifiers.contains(KeyModifiers::SHIFT),
|
||||||
|
ctrl: event.modifiers.contains(KeyModifiers::CONTROL),
|
||||||
|
alt: event.modifiers.contains(KeyModifiers::ALT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
macro_rules! key {
|
||||||
|
// 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!(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!(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!(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, } };
|
||||||
|
}
|
||||||
|
pub(crate) use key;
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::KeyCode;
|
||||||
use crossterm::style::{Color, ContentStyle, Stylize};
|
use crossterm::style::{Color, ContentStyle, Stylize};
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use tokio::sync::oneshot::error::TryRecvError;
|
use tokio::sync::oneshot::error::TryRecvError;
|
||||||
|
|
@ -14,6 +14,7 @@ use crate::euph::{self, Joined, Status};
|
||||||
use crate::vault::EuphVault;
|
use crate::vault::EuphVault;
|
||||||
|
|
||||||
use super::chat::{ChatState, Reaction};
|
use super::chat::{ChatState, Reaction};
|
||||||
|
use super::input::{key, 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;
|
||||||
|
|
@ -328,11 +329,7 @@ impl EuphRoom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !event.modifiers.is_empty() {
|
if let key!('n') | key!('N') = event {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let KeyCode::Char('n' | 'N') = event.code {
|
|
||||||
self.state = State::ChooseNick(EditorState::with_initial_text(
|
self.state = State::ChooseNick(EditorState::with_initial_text(
|
||||||
joined.session.name.clone(),
|
joined.session.name.clone(),
|
||||||
));
|
));
|
||||||
|
|
@ -349,19 +346,19 @@ impl EuphRoom {
|
||||||
.handled()
|
.handled()
|
||||||
}
|
}
|
||||||
State::ChooseNick(ed) => {
|
State::ChooseNick(ed) => {
|
||||||
match event.code {
|
match event {
|
||||||
KeyCode::Esc => self.state = State::Normal,
|
key!(Esc) => self.state = State::Normal,
|
||||||
KeyCode::Enter => {
|
key!(Enter) => {
|
||||||
if let Some(room) = &self.room {
|
if let Some(room) = &self.room {
|
||||||
let _ = room.nick(ed.text());
|
let _ = room.nick(ed.text());
|
||||||
}
|
}
|
||||||
self.state = State::Normal;
|
self.state = State::Normal;
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => ed.backspace(),
|
key!(Char ch) => ed.insert_char(ch),
|
||||||
KeyCode::Left => ed.move_cursor_left(),
|
key!(Backspace) => ed.backspace(),
|
||||||
KeyCode::Right => ed.move_cursor_right(),
|
key!(Left) => ed.move_cursor_left(),
|
||||||
KeyCode::Delete => ed.delete(),
|
key!(Right) => ed.move_cursor_right(),
|
||||||
KeyCode::Char(ch) => ed.insert_char(ch),
|
key!(Delete) => ed.delete(),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::KeyCode;
|
||||||
use crossterm::style::{ContentStyle, Stylize};
|
use crossterm::style::{ContentStyle, Stylize};
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
@ -13,6 +13,7 @@ use crate::euph::api::SessionType;
|
||||||
use crate::euph::{Joined, Status};
|
use crate::euph::{Joined, Status};
|
||||||
use crate::vault::Vault;
|
use crate::vault::Vault;
|
||||||
|
|
||||||
|
use super::input::{key, KeyEvent};
|
||||||
use super::room::EuphRoom;
|
use super::room::EuphRoom;
|
||||||
use super::widgets::background::Background;
|
use super::widgets::background::Background;
|
||||||
use super::widgets::border::Border;
|
use super::widgets::border::Border;
|
||||||
|
|
@ -205,34 +206,31 @@ impl Rooms {
|
||||||
event: KeyEvent,
|
event: KeyEvent,
|
||||||
) {
|
) {
|
||||||
match &self.state {
|
match &self.state {
|
||||||
State::ShowList => match event.code {
|
State::ShowList => match event {
|
||||||
KeyCode::Enter => {
|
key!(Enter) => {
|
||||||
if let Some(name) = self.list.cursor() {
|
if let Some(name) = self.list.cursor() {
|
||||||
self.state = State::ShowRoom(name);
|
self.state = State::ShowRoom(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('k') | KeyCode::Up => self.list.move_cursor_up(),
|
key!('k') | key!(Up) => self.list.move_cursor_up(),
|
||||||
KeyCode::Char('j') | KeyCode::Down => self.list.move_cursor_down(),
|
key!('j') | key!(Down) => self.list.move_cursor_down(),
|
||||||
KeyCode::Char('g') | KeyCode::Home => self.list.move_cursor_to_top(),
|
key!('g') | key!(Home) => self.list.move_cursor_to_top(),
|
||||||
KeyCode::Char('G') | KeyCode::End => self.list.move_cursor_to_bottom(),
|
key!('G') | key!(End) => self.list.move_cursor_to_bottom(),
|
||||||
KeyCode::Char('y') if event.modifiers == KeyModifiers::CONTROL => {
|
key!(Ctrl + 'y') => self.list.scroll_up(1),
|
||||||
self.list.scroll_up(1)
|
key!(Ctrl + 'e') => self.list.scroll_down(1),
|
||||||
}
|
|
||||||
KeyCode::Char('e') if event.modifiers == KeyModifiers::CONTROL => {
|
key!('c') => {
|
||||||
self.list.scroll_down(1)
|
|
||||||
}
|
|
||||||
KeyCode::Char('c') => {
|
|
||||||
if let Some(name) = self.list.cursor() {
|
if let Some(name) = self.list.cursor() {
|
||||||
self.get_or_insert_room(name).connect();
|
self.get_or_insert_room(name).connect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('C') => self.state = State::Connect(EditorState::new()),
|
key!('C') => self.state = State::Connect(EditorState::new()),
|
||||||
KeyCode::Char('d') => {
|
key!('d') => {
|
||||||
if let Some(name) = self.list.cursor() {
|
if let Some(name) = self.list.cursor() {
|
||||||
self.get_or_insert_room(name).disconnect();
|
self.get_or_insert_room(name).disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('D') => {
|
key!('D') => {
|
||||||
// TODO Check whether user wanted this via popup
|
// TODO Check whether user wanted this via popup
|
||||||
if let Some(name) = self.list.cursor() {
|
if let Some(name) = self.list.cursor() {
|
||||||
self.euph_rooms.remove(&name);
|
self.euph_rooms.remove(&name);
|
||||||
|
|
@ -250,24 +248,24 @@ impl Rooms {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.code == KeyCode::Esc {
|
if let key!(Esc) = event {
|
||||||
self.state = State::ShowList;
|
self.state = State::ShowList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::Connect(ed) => match event.code {
|
State::Connect(ed) => match event {
|
||||||
KeyCode::Esc => self.state = State::ShowList,
|
key!(Esc) => self.state = State::ShowList,
|
||||||
KeyCode::Enter => {
|
key!(Enter) => {
|
||||||
let name = ed.text();
|
let name = ed.text();
|
||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
self.get_or_insert_room(name.clone()).connect();
|
self.get_or_insert_room(name.clone()).connect();
|
||||||
self.state = State::ShowRoom(name);
|
self.state = State::ShowRoom(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => ed.backspace(),
|
key!(Char ch) if ch.is_ascii_alphanumeric() || ch == '_' => ed.insert_char(ch),
|
||||||
KeyCode::Left => ed.move_cursor_left(),
|
key!(Backspace) => ed.backspace(),
|
||||||
KeyCode::Right => ed.move_cursor_right(),
|
key!(Left) => ed.move_cursor_left(),
|
||||||
KeyCode::Delete => ed.delete(),
|
key!(Right) => ed.move_cursor_right(),
|
||||||
KeyCode::Char(ch) if ch.is_ascii_alphanumeric() || ch == '_' => ed.insert_char(ch),
|
key!(Delete) => ed.delete(),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue