diff --git a/crates/libtiny_tui/src/exit_dialogue.rs b/crates/libtiny_tui/src/exit_dialogue.rs index bfc0ad55..bb17a463 100644 --- a/crates/libtiny_tui/src/exit_dialogue.rs +++ b/crates/libtiny_tui/src/exit_dialogue.rs @@ -48,7 +48,7 @@ impl ExitDialogue { } } - pub(crate) fn keypressed(&self, key_action: KeyAction) -> WidgetRet { + pub(crate) fn keypressed(&self, key_action: &KeyAction) -> WidgetRet { match key_action { KeyAction::Input('y') | KeyAction::InputSend => WidgetRet::Abort, _ => WidgetRet::Remove, diff --git a/crates/libtiny_tui/src/input_area/mod.rs b/crates/libtiny_tui/src/input_area/mod.rs index 475487ed..2fb40122 100644 --- a/crates/libtiny_tui/src/input_area/mod.rs +++ b/crates/libtiny_tui/src/input_area/mod.rs @@ -211,7 +211,7 @@ impl InputArea { } } - pub(crate) fn keypressed(&mut self, key_action: KeyAction) -> WidgetRet { + pub(crate) fn keypressed(&mut self, key_action: &KeyAction) -> WidgetRet { match key_action { KeyAction::InputSend => { if self.current_buffer_len() > 0 { @@ -330,10 +330,13 @@ impl InputArea { } KeyAction::Input(ch) => { self.modify(); - self.buffer.insert(self.cursor as usize, ch); + self.buffer.insert(self.cursor as usize, *ch); self.inc_cursor(); WidgetRet::KeyHandled } + KeyAction::RunIrcCommand(cmd) => { + WidgetRet::Command(cmd.to_owned()) + } _ => WidgetRet::KeyIgnored, } } @@ -777,18 +780,18 @@ mod tests { #[test] fn text_field_bug() { let mut text_field = InputArea::new(10, 50); - text_field.keypressed(KeyAction::Input('a')); - text_field.keypressed(KeyAction::Input(' ')); - text_field.keypressed(KeyAction::Input('b')); - text_field.keypressed(KeyAction::Input(' ')); - text_field.keypressed(KeyAction::Input('c')); - text_field.keypressed(KeyAction::InputSend); - text_field.keypressed(KeyAction::InputPrevEntry); + text_field.keypressed(&KeyAction::Input('a')); + text_field.keypressed(&KeyAction::Input(' ')); + text_field.keypressed(&KeyAction::Input('b')); + text_field.keypressed(&KeyAction::Input(' ')); + text_field.keypressed(&KeyAction::Input('c')); + text_field.keypressed(&KeyAction::InputSend); + text_field.keypressed(&KeyAction::InputPrevEntry); // this panics: - text_field.keypressed(KeyAction::InputMoveWordLeft); + text_field.keypressed(&KeyAction::InputMoveWordLeft); // a b ^c assert_eq!(text_field.cursor, 4); - text_field.keypressed(KeyAction::InputMoveWordRight); + text_field.keypressed(&KeyAction::InputMoveWordRight); assert_eq!(text_field.cursor, 5); } diff --git a/crates/libtiny_tui/src/key_map.rs b/crates/libtiny_tui/src/key_map.rs index febb0973..a19419b0 100644 --- a/crates/libtiny_tui/src/key_map.rs +++ b/crates/libtiny_tui/src/key_map.rs @@ -8,7 +8,7 @@ use term_input::{Arrow, FKey, Key}; #[derive(Debug, PartialEq)] pub(crate) struct KeyMap(HashMap); -#[derive(Debug, Copy, Clone, Deserialize, PartialEq)] +#[derive(Debug, Clone, Deserialize, PartialEq)] #[serde(rename_all = "snake_case")] pub(crate) enum KeyAction { Cancel, @@ -31,6 +31,7 @@ pub(crate) enum KeyAction { MessagesScrollBottom, Input(char), + RunIrcCommand(String), InputAutoComplete, InputNextEntry, InputPrevEntry, @@ -67,6 +68,7 @@ impl Display for KeyAction { KeyAction::MessagesScrollTop => "messages_scroll_top", KeyAction::MessagesScrollBottom => "messages_scroll_bottom", KeyAction::Input(c) => return writeln!(f, "input_{}", c), + KeyAction::RunIrcCommand(string) => return writeln!(f, "run_irc_command_{}", string), KeyAction::InputAutoComplete => "input_auto_complete", KeyAction::InputNextEntry => "input_next_entry", KeyAction::InputPrevEntry => "input_prev_entry", @@ -150,7 +152,7 @@ impl KeyMap { } pub(crate) fn load(&mut self, key_map: &KeyMap) { - self.0.extend(key_map.0.iter()) + self.0.extend(key_map.0.clone().into_iter()) } } @@ -357,6 +359,8 @@ fn deser_keymap() { tab_goto: 1 a: input: b + b: + run_irc_command: clear "#; let mut expect = KeyMap(HashMap::new()); expect @@ -364,6 +368,10 @@ fn deser_keymap() { .insert(Key::Ctrl('a'), KeyAction::InputMoveCursStart); expect.0.insert(Key::Ctrl('e'), KeyAction::TabGoto('1')); expect.0.insert(Key::Char('a'), KeyAction::Input('b')); + expect.0.insert( + Key::Char('b'), + KeyAction::RunIrcCommand("clear".to_string()), + ); let key_map: KeyMap = serde_yaml::from_str(s).unwrap(); assert_eq!(expect, key_map); diff --git a/crates/libtiny_tui/src/lib.rs b/crates/libtiny_tui/src/lib.rs index efc2e985..5aeeafbe 100644 --- a/crates/libtiny_tui/src/lib.rs +++ b/crates/libtiny_tui/src/lib.rs @@ -178,6 +178,20 @@ async fn input_handler( return; } TUIRet::KeyHandled | TUIRet::KeyIgnored(_) | TUIRet::EventIgnored(_) => {} + TUIRet::KeyCommand{cmd, from} => { + let result = tui.borrow_mut().try_handle_cmd(&cmd, &from); + match result { + CmdResult::Ok => {} + CmdResult::Continue => { + snd_ev.try_send(Event::Cmd { cmd, source: from }).unwrap() + } + CmdResult::Quit(msg) => { + snd_ev.try_send(Event::Abort { msg }).unwrap(); + let _ = snd_abort.try_send(()); + return; + } + } + } TUIRet::Input { msg, from } => { if msg[0] == '/' { // Handle TUI commands, send others to downstream diff --git a/crates/libtiny_tui/src/messaging.rs b/crates/libtiny_tui/src/messaging.rs index f81cf790..5634087a 100644 --- a/crates/libtiny_tui/src/messaging.rs +++ b/crates/libtiny_tui/src/messaging.rs @@ -130,7 +130,7 @@ impl MessagingUI { self.msg_area.draw(tb, colors, pos_x, pos_y); } - pub(crate) fn keypressed(&mut self, key_action: KeyAction) -> WidgetRet { + pub(crate) fn keypressed(&mut self, key_action: &KeyAction) -> WidgetRet { match key_action { KeyAction::Exit => { self.toggle_exit_dialogue(); diff --git a/crates/libtiny_tui/src/tui.rs b/crates/libtiny_tui/src/tui.rs index 081ce182..94195285 100644 --- a/crates/libtiny_tui/src/tui.rs +++ b/crates/libtiny_tui/src/tui.rs @@ -33,6 +33,11 @@ pub(crate) enum TUIRet { KeyIgnored(Key), EventIgnored(Event), + KeyCommand { + cmd: String, + from: MsgSource, + }, + /// INVARIANT: The vec will have at least one char. // Can't make MsgSource a ref because of this weird error: // https://users.rust-lang.org/t/borrow-checker-bug/5165 @@ -670,9 +675,13 @@ impl TUI { }); if let Some(key_action) = key_action { - match self.tabs[self.active_idx].widget.keypressed(key_action) { + match self.tabs[self.active_idx].widget.keypressed(&key_action) { WidgetRet::KeyHandled => TUIRet::KeyHandled, WidgetRet::KeyIgnored => self.handle_keypress(key, key_action, rcv_editor_ret), + WidgetRet::Command(cmd) => TUIRet::KeyCommand { + cmd, + from: self.tabs[self.active_idx].src.clone(), + }, WidgetRet::Input(input) => TUIRet::Input { msg: input, from: self.tabs[self.active_idx].src.clone(), diff --git a/crates/libtiny_tui/src/widget.rs b/crates/libtiny_tui/src/widget.rs index 83debb07..ee3f74e1 100644 --- a/crates/libtiny_tui/src/widget.rs +++ b/crates/libtiny_tui/src/widget.rs @@ -7,6 +7,9 @@ pub(crate) enum WidgetRet { /// An input is submitted. Input(Vec), + + /// A command is ran + Command(String), /// Remove the widget. E.g. close the tab, hide the dialogue etc. Remove,