|
|
@@ -21,11 +21,12 @@ pub const MESSAGES_SHOWN: usize = 200;
|
|
|
|
|
|
/// Key code of the function key that toggles the list mode.
|
|
|
const LIST_MODE_FUNCTION_KEY: u8 = 2;
|
|
|
-/// Character that, combined with Ctrl, quits the application.
|
|
|
-const QUIT_CHAR: char = 'q';
|
|
|
-/// Character that, combined with Ctrl, toggles the autoreply of the open chat; with Ctrl+Shift
|
|
|
-/// it toggles the global autoreply.
|
|
|
-const AUTOREPLY_CHAR: char = 'a';
|
|
|
+/// Key code of the function key (F3) that toggles the autoreply of the open chat.
|
|
|
+const AUTOREPLY_FUNCTION_KEY: u8 = 3;
|
|
|
+/// Key code of the function key (F4) that toggles the global autoreply.
|
|
|
+const GLOBAL_AUTOREPLY_FUNCTION_KEY: u8 = 4;
|
|
|
+/// Key code of the function key (F10) that quits the application.
|
|
|
+const QUIT_FUNCTION_KEY: u8 = 10;
|
|
|
/// Sender jid stored for messages written by the user (own account).
|
|
|
const OWN_SENDER_JID: &str = "";
|
|
|
|
|
|
@@ -565,25 +566,21 @@ impl App {
|
|
|
/// Purpose: handle a key press on any screen.
|
|
|
/// Inputs: `key`, the pressed key.
|
|
|
/// Output: `Ok(())`, or a history error.
|
|
|
- /// Side effects: may quit, change focus/mode/selection, edit the input, send or open chats.
|
|
|
+ /// Side effects: F10 quits on any screen; on the main screen F2 toggles the list mode, F3 the
|
|
|
+ /// autoreply of the open chat and F4 the global autoreply (any focus); Ctrl command chords are
|
|
|
+ /// ignored so they never type into the input or the filter; otherwise may change
|
|
|
+ /// focus/selection, edit the input, send or open chats.
|
|
|
fn handle_key(&mut self, key: KeyEvent) -> Result<()> {
|
|
|
- let ctrl = is_command(key.modifiers);
|
|
|
- if ctrl && key.code == KeyCode::Char(QUIT_CHAR) {
|
|
|
+ if key.code == KeyCode::F(QUIT_FUNCTION_KEY) {
|
|
|
self.should_quit = true;
|
|
|
return Ok(());
|
|
|
}
|
|
|
if self.screen != Screen::Main {
|
|
|
return Ok(());
|
|
|
}
|
|
|
- if ctrl {
|
|
|
+ if is_command(key.modifiers) {
|
|
|
self.cancel_pending_delete();
|
|
|
- if is_global_toggle(&key) {
|
|
|
- return self.toggle_global_autoreply();
|
|
|
- }
|
|
|
- return match key.code {
|
|
|
- KeyCode::Char(AUTOREPLY_CHAR) => self.toggle_autoreply(),
|
|
|
- _ => Ok(()),
|
|
|
- };
|
|
|
+ return Ok(());
|
|
|
}
|
|
|
match (key.code, self.focus) {
|
|
|
(KeyCode::Tab, _) => {
|
|
|
@@ -591,6 +588,14 @@ impl App {
|
|
|
self.focus = toggled_focus(self.focus);
|
|
|
}
|
|
|
(KeyCode::F(LIST_MODE_FUNCTION_KEY), _) => self.toggle_list_mode(),
|
|
|
+ (KeyCode::F(AUTOREPLY_FUNCTION_KEY), _) => {
|
|
|
+ self.cancel_pending_delete();
|
|
|
+ return self.toggle_autoreply();
|
|
|
+ }
|
|
|
+ (KeyCode::F(GLOBAL_AUTOREPLY_FUNCTION_KEY), _) => {
|
|
|
+ self.cancel_pending_delete();
|
|
|
+ return self.toggle_global_autoreply();
|
|
|
+ }
|
|
|
(code, Focus::List) => return self.handle_list_key(code),
|
|
|
(code, Focus::Input) => self.handle_input_key(code),
|
|
|
}
|
|
|
@@ -755,7 +760,7 @@ impl App {
|
|
|
self.selected = 0;
|
|
|
}
|
|
|
|
|
|
- /// Purpose: toggle the AI autoreply of the open chat.
|
|
|
+ /// Purpose: toggle the AI autoreply of the open chat (F3).
|
|
|
/// Inputs: none.
|
|
|
/// Output: `Ok(())`, or a history error.
|
|
|
/// Side effects: writes the history, updates `status`, reloads chats; refuses to enable
|
|
|
@@ -831,7 +836,7 @@ impl App {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// Purpose: toggle the global AI autoreply, which answers every chat regardless of the
|
|
|
+ /// Purpose: toggle the global AI autoreply (F4), which answers every chat regardless of the
|
|
|
/// per-chat flags.
|
|
|
/// Inputs: none.
|
|
|
/// Output: `Ok(())`, or a history error.
|
|
|
@@ -1159,20 +1164,6 @@ fn is_command(modifiers: KeyModifiers) -> bool {
|
|
|
modifiers.contains(KeyModifiers::CONTROL) && !modifiers.contains(KeyModifiers::ALT)
|
|
|
}
|
|
|
|
|
|
-/// Purpose: tell whether a key event is the Shift variant of the autoreply chord.
|
|
|
-/// Inputs: `key`, the key event (callers check the Ctrl command chord separately).
|
|
|
-/// Output: `true` when the key is `AUTOREPLY_CHAR` (either case) with Shift held; `false` otherwise.
|
|
|
-/// Side effects: none. Terminals may report Ctrl+Shift+A as `'a'` or `'A'` with Shift, so the
|
|
|
-/// character is compared ignoring ASCII case.
|
|
|
-fn is_global_toggle(key: &KeyEvent) -> bool {
|
|
|
- match key.code {
|
|
|
- KeyCode::Char(c) => {
|
|
|
- c.eq_ignore_ascii_case(&AUTOREPLY_CHAR) && key.modifiers.contains(KeyModifiers::SHIFT)
|
|
|
- }
|
|
|
- _ => false,
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
use super::*;
|
|
|
@@ -1292,21 +1283,13 @@ mod tests {
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
- fn is_global_toggle_accepts_ctrl_shift_a_in_both_cases() {
|
|
|
- let chord = KeyModifiers::CONTROL | KeyModifiers::SHIFT;
|
|
|
- assert!(is_global_toggle(&KeyEvent::new(KeyCode::Char('a'), chord)));
|
|
|
- assert!(is_global_toggle(&KeyEvent::new(KeyCode::Char('A'), chord)));
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn is_global_toggle_rejects_ctrl_only_and_other_chars() {
|
|
|
- let chord = KeyModifiers::CONTROL | KeyModifiers::SHIFT;
|
|
|
- assert!(!is_global_toggle(&KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)));
|
|
|
- assert!(!is_global_toggle(&KeyEvent::new(KeyCode::Char('c'), chord)));
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn ctrl_q_is_the_quit_key() {
|
|
|
- assert_eq!(QUIT_CHAR, 'q');
|
|
|
+ fn command_function_keys_are_fixed_and_distinct() {
|
|
|
+ let keys = [
|
|
|
+ LIST_MODE_FUNCTION_KEY,
|
|
|
+ AUTOREPLY_FUNCTION_KEY,
|
|
|
+ GLOBAL_AUTOREPLY_FUNCTION_KEY,
|
|
|
+ QUIT_FUNCTION_KEY,
|
|
|
+ ];
|
|
|
+ assert_eq!(keys, [2, 3, 4, 10]);
|
|
|
}
|
|
|
}
|