From e73dc78c6bbed81dd2f03e79183f473dcca888b1 Mon Sep 17 00:00:00 2001 From: Barafu Albino Cheetah Date: Fri, 19 Jul 2024 21:11:41 +0300 Subject: [PATCH 1/4] Implement to_string() for keyboard::Keys and key --- src/component/keyboard.rs | 130 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/src/component/keyboard.rs b/src/component/keyboard.rs index 17e65a0..4a38977 100644 --- a/src/component/keyboard.rs +++ b/src/component/keyboard.rs @@ -63,6 +63,23 @@ impl FromStr for Keys { } } +impl ToString for Keys { + fn to_string(&self) -> String { + let mut s: Vec = Vec::new(); + + for k in &self.0 { + match k { + Key::Control => s.push("CTRL".to_string()), + Key::Shift => s.push("SHIFT".to_string()), + Key::Alt => s.push("ALT".to_string()), + Key::Meta => s.push("META".to_string()), + _ => s.push(key_to_string(k).unwrap_or_else(|_| "not set".to_string()).to_uppercase()), + } + } + s.join("+") + } +} + // We can't use [`enigo::Key::Unicode`], it will cause panic. // Don't know why, maybe that can only be used in main thread. fn key_of(key: char) -> Result { @@ -180,3 +197,116 @@ fn key_of(key: char) -> Result { Ok(k) } + +pub fn key_to_string(key: &Key) -> Result { + match key { + #[cfg(all(unix, not(target_os = "macos")))] + Key::Unicode(c) => Ok(c.to_string()), + + #[cfg(target_os = "windows")] + Key::Other(v) => match v { + 0x30 => Ok('0'.to_string()), + 0x31 => Ok('1'.to_string()), + 0x32 => Ok('2'.to_string()), + 0x33 => Ok('3'.to_string()), + 0x34 => Ok('4'.to_string()), + 0x35 => Ok('5'.to_string()), + 0x36 => Ok('6'.to_string()), + 0x37 => Ok('7'.to_string()), + 0x38 => Ok('8'.to_string()), + 0x39 => Ok('9'.to_string()), + 0x41 => Ok('A'.to_string()), + 0x42 => Ok('B'.to_string()), + 0x43 => Ok('C'.to_string()), + 0x44 => Ok('D'.to_string()), + 0x45 => Ok('E'.to_string()), + 0x46 => Ok('F'.to_string()), + 0x47 => Ok('G'.to_string()), + 0x48 => Ok('H'.to_string()), + 0x49 => Ok('I'.to_string()), + 0x4A => Ok('J'.to_string()), + 0x4B => Ok('K'.to_string()), + 0x4C => Ok('L'.to_string()), + 0x4D => Ok('M'.to_string()), + 0x4E => Ok('N'.to_string()), + 0x4F => Ok('O'.to_string()), + 0x50 => Ok('P'.to_string()), + 0x51 => Ok('Q'.to_string()), + 0x52 => Ok('R'.to_string()), + 0x53 => Ok('S'.to_string()), + 0x54 => Ok('T'.to_string()), + 0x55 => Ok('U'.to_string()), + 0x56 => Ok('V'.to_string()), + 0x57 => Ok('W'.to_string()), + 0x58 => Ok('X'.to_string()), + 0x59 => Ok('Y'.to_string()), + 0x5A => Ok('Z'.to_string()), + 0xBB => Ok('='.to_string()), + 0xBD => Ok('-'.to_string()), + 0xBA => Ok(';'.to_string()), + 0xBC => Ok(','.to_string()), + 0xBE => Ok('.'.to_string()), + 0xBF => Ok('/'.to_string()), + 0xC0 => Ok('`'.to_string()), + 0xDB => Ok('['.to_string()), + 0xDD => Ok(']'.to_string()), + 0xDC => Ok('\\'.to_string()), + 0xDE => Ok('\''.to_string()), + _ => Err(Error::UnsupportedKey(format!("{:x}", v))), + }, + + #[cfg(target_os = "macos")] + Key::Other(v) => match v { + 0 => Ok('A'.to_string()), + 1 => Ok('S'.to_string()), + 2 => Ok('D'.to_string()), + 3 => Ok('F'.to_string()), + 4 => Ok('H'.to_string()), + 5 => Ok('G'.to_string()), + 6 => Ok('Z'.to_string()), + 7 => Ok('X'.to_string()), + 8 => Ok('C'.to_string()), + 9 => Ok('V'.to_string()), + 11 => Ok('B'.to_string()), + 12 => Ok('Q'.to_string()), + 13 => Ok('W'.to_string()), + 14 => Ok('E'.to_string()), + 15 => Ok('R'.to_string()), + 16 => Ok('Y'.to_string()), + 17 => Ok('T'.to_string()), + 18 => Ok('1'.to_string()), + 19 => Ok('2'.to_string()), + 20 => Ok('3'.to_string()), + 21 => Ok('4'.to_string()), + 22 => Ok('6'.to_string()), + 23 => Ok('5'.to_string()), + 24 => Ok('='.to_string()), + 25 => Ok('9'.to_string()), + 26 => Ok('7'.to_string()), + 27 => Ok('-'.to_string()), + 28 => Ok('8'.to_string()), + 29 => Ok('0'.to_string()), + 30 => Ok(']'.to_string()), + 31 => Ok('O'.to_string()), + 32 => Ok('U'.to_string()), + 33 => Ok('['.to_string()), + 34 => Ok('I'.to_string()), + 35 => Ok('P'.to_string()), + 37 => Ok('L'.to_string()), + 38 => Ok('J'.to_string()), + 39 => Ok('\''.to_string()), + 40 => Ok('K'.to_string()), + 41 => Ok(';'.to_string()), + 42 => Ok('\\'.to_string()), + 43 => Ok(','.to_string()), + 44 => Ok('/'.to_string()), + 45 => Ok('N'.to_string()), + 46 => Ok('M'.to_string()), + 47 => Ok('.').to_string(), + 50 => Ok('`'.to_string()), + _ => Err(Error::UnsupportedKey(format!("{:x}", v))), + }, + + _ => Err(Error::UnsupportedKey(format!("{:?}", key))), + } +} From 1cd490cb208a797c5850af0db23a82f92a2ca54e Mon Sep 17 00:00:00 2001 From: Barafu Albino Cheetah Date: Fri, 19 Jul 2024 21:13:05 +0300 Subject: [PATCH 2/4] Allow unsetting global hotkeys Allows global hotkeys to be set to "not set" by setting to any incompatible value, for example Esc. --- src/component/function.rs | 2 +- src/service/hotkey.rs | 106 ++++++++++++++++++++++++-------------- src/ui/panel/setting.rs | 2 +- 3 files changed, 69 insertions(+), 41 deletions(-) diff --git a/src/component/function.rs b/src/component/function.rs index e643834..79525b1 100644 --- a/src/component/function.rs +++ b/src/component/function.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use super::setting::Chat; use crate::widget::ComboBoxItem; -#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, Hash, Eq)] #[serde(rename_all = "kebab-case")] pub enum Function { Rewrite, diff --git a/src/service/hotkey.rs b/src/service/hotkey.rs index c1163e5..90d3bc9 100644 --- a/src/service/hotkey.rs +++ b/src/service/hotkey.rs @@ -1,5 +1,6 @@ // std use std::{ + collections::HashMap, fmt::{Debug, Formatter, Result as FmtResult}, sync::{mpsc::Sender, Arc}, thread, @@ -22,7 +23,7 @@ use crate::{ pub struct Hotkey { // The manager need to be kept alive during the whole program life. - _manager: GlobalHotKeyManager, + ghk_manager: GlobalHotKeyManager, manager: Arc>, abort: ArtBool, } @@ -107,17 +108,22 @@ impl Hotkey { } }); - Ok(Self { _manager, manager, abort }) + Ok(Self { ghk_manager: _manager, manager, abort }) } - pub fn renew(&self, hotkeys: &Hotkeys) { + pub fn renew(&self, hotkeys: &mut Hotkeys) { tracing::info!("renewing hotkey manager"); let mut manager = self.manager.write(); - self._manager.unregister_all(&manager.hotkeys).expect("unregister must succeed"); + for (hotkey, _keys) in manager.new_hotkeys.values() { + self.ghk_manager.unregister(*hotkey).expect("unregister must succeed"); + } + + *manager = Manager::new(&self.ghk_manager, hotkeys).expect("renew must succeed"); - *manager = Manager::new(&self._manager, hotkeys).expect("renew must succeed"); + // Write hotkey texts back into the settings. + manager.read_hotkeys_into_settings(hotkeys); } pub fn abort(&self) { @@ -131,46 +137,68 @@ impl Debug for Hotkey { } struct Manager { - hotkeys: [HotKey; 4], - hotkeys_keys: [Keys; 4], + /// Only the actually registered hotkeys + new_hotkeys: HashMap, } impl Manager { - fn new(_manager: &GlobalHotKeyManager, hotkeys: &Hotkeys) -> Result { - let hotkeys_raw = [ - &hotkeys.rewrite, - &hotkeys.rewrite_directly, - &hotkeys.translate, - &hotkeys.translate_directly, + // Creates new manager. Assumes that hotkeys are valid. + fn new(ghk_manager: &GlobalHotKeyManager, settings_hotkeys: &Hotkeys) -> Result { + let hotkey_str_and_function = [ + (&settings_hotkeys.rewrite, Function::Rewrite), + (&settings_hotkeys.rewrite_directly, Function::RewriteDirectly), + (&settings_hotkeys.translate, Function::Translate), + (&settings_hotkeys.translate_directly, Function::TranslateDirectly), ]; - let hotkeys: [HotKey; 4] = hotkeys_raw - .iter() - .map(|h| h.parse()) - .collect::, _>>() - .map_err(GlobalHotKeyError::Parse)? - .try_into() - .expect("array must fit"); - - _manager.register_all(&hotkeys).map_err(GlobalHotKeyError::Main)?; - - let hotkeys_keys = hotkeys_raw - .iter() - .map(|h| h.parse()) - .collect::, _>>()? - .try_into() - .expect("array must fit"); - - Ok(Self { hotkeys, hotkeys_keys }) + + let mut new_hotkeys: HashMap = HashMap::new(); + + for (hotkey_str, funk) in hotkey_str_and_function.into_iter() { + //Parse error possible when str value is "Not set" + let hotkey: HotKey = match hotkey_str.parse() { + Ok(v) => v, + Err(_) => continue, + }; + // Same goes for Keys + let hotkey_keys: Keys = match hotkey_str.parse() { + Ok(v) => v, + Err(_) => continue, + }; + // If manager.register fails, ignore the key, and it becomes "Not set". + let e = ghk_manager.register(hotkey); + if e.is_err() { + continue; + } + + // If key has been registered, add it to new_hotkeys + new_hotkeys.insert(funk, (hotkey, hotkey_keys)); + } + + Ok(Self { new_hotkeys }) } fn match_func(&self, id: u32) -> (Function, Keys) { - match id { - i if i == self.hotkeys[0].id => (Function::Rewrite, self.hotkeys_keys[0].clone()), - i if i == self.hotkeys[1].id => - (Function::RewriteDirectly, self.hotkeys_keys[1].clone()), - i if i == self.hotkeys[2].id => (Function::Translate, self.hotkeys_keys[2].clone()), - i if i == self.hotkeys[3].id => - (Function::TranslateDirectly, self.hotkeys_keys[3].clone()), - _ => unreachable!(), + for (k, v) in self.new_hotkeys.iter() { + if id == v.0.id { + return (*k, v.1.clone()); + } + } + unreachable!(); + } + + // Copies text representation of actually registered hotkeys back to settings. + // Replaces text for unset hotkeys with "Not set" + fn read_hotkeys_into_settings(&self, settings_hotkeys: &mut Hotkeys) { + let hotkey_str_and_function = [ + (&mut settings_hotkeys.rewrite, Function::Rewrite), + (&mut settings_hotkeys.rewrite_directly, Function::RewriteDirectly), + (&mut settings_hotkeys.translate, Function::Translate), + (&mut settings_hotkeys.translate_directly, Function::TranslateDirectly), + ]; + for (k, v) in hotkey_str_and_function.into_iter() { + *k = match self.new_hotkeys.get(&v) { + Some(v) => v.1.to_string().to_uppercase(), + None => "Not set".into(), + }; } } } diff --git a/src/ui/panel/setting.rs b/src/ui/panel/setting.rs index a012dfe..d7b7cc4 100644 --- a/src/ui/panel/setting.rs +++ b/src/ui/panel/setting.rs @@ -179,7 +179,7 @@ impl Setting { changed }) { - ctx.services.hotkey.renew(&ctx.components.setting.hotkeys); + ctx.services.hotkey.renew(&mut ctx.components.setting.hotkeys); } }); }); From 22d0c3e08286b4e54768d89cb5b2ded3b58c09ca Mon Sep 17 00:00:00 2001 From: Barafu Albino Cheetah Date: Sat, 20 Jul 2024 14:46:10 +0300 Subject: [PATCH 3/4] Improve keyboard.rs Replace key_to_string()->String with key_to_str()->&str. --- src/component/keyboard.rs | 206 +++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/src/component/keyboard.rs b/src/component/keyboard.rs index 4a38977..34cefe3 100644 --- a/src/component/keyboard.rs +++ b/src/component/keyboard.rs @@ -73,7 +73,7 @@ impl ToString for Keys { Key::Shift => s.push("SHIFT".to_string()), Key::Alt => s.push("ALT".to_string()), Key::Meta => s.push("META".to_string()), - _ => s.push(key_to_string(k).unwrap_or_else(|_| "not set".to_string()).to_uppercase()), + _ => s.push(key_to_str(k).unwrap_or_else(|_| "not set").to_uppercase()), } } s.join("+") @@ -198,114 +198,114 @@ fn key_of(key: char) -> Result { Ok(k) } -pub fn key_to_string(key: &Key) -> Result { +pub fn key_to_str(key: &Key) -> Result<&'static str> { match key { #[cfg(all(unix, not(target_os = "macos")))] - Key::Unicode(c) => Ok(c.to_string()), + Key::Unicode(c) => Ok(c), #[cfg(target_os = "windows")] - Key::Other(v) => match v { - 0x30 => Ok('0'.to_string()), - 0x31 => Ok('1'.to_string()), - 0x32 => Ok('2'.to_string()), - 0x33 => Ok('3'.to_string()), - 0x34 => Ok('4'.to_string()), - 0x35 => Ok('5'.to_string()), - 0x36 => Ok('6'.to_string()), - 0x37 => Ok('7'.to_string()), - 0x38 => Ok('8'.to_string()), - 0x39 => Ok('9'.to_string()), - 0x41 => Ok('A'.to_string()), - 0x42 => Ok('B'.to_string()), - 0x43 => Ok('C'.to_string()), - 0x44 => Ok('D'.to_string()), - 0x45 => Ok('E'.to_string()), - 0x46 => Ok('F'.to_string()), - 0x47 => Ok('G'.to_string()), - 0x48 => Ok('H'.to_string()), - 0x49 => Ok('I'.to_string()), - 0x4A => Ok('J'.to_string()), - 0x4B => Ok('K'.to_string()), - 0x4C => Ok('L'.to_string()), - 0x4D => Ok('M'.to_string()), - 0x4E => Ok('N'.to_string()), - 0x4F => Ok('O'.to_string()), - 0x50 => Ok('P'.to_string()), - 0x51 => Ok('Q'.to_string()), - 0x52 => Ok('R'.to_string()), - 0x53 => Ok('S'.to_string()), - 0x54 => Ok('T'.to_string()), - 0x55 => Ok('U'.to_string()), - 0x56 => Ok('V'.to_string()), - 0x57 => Ok('W'.to_string()), - 0x58 => Ok('X'.to_string()), - 0x59 => Ok('Y'.to_string()), - 0x5A => Ok('Z'.to_string()), - 0xBB => Ok('='.to_string()), - 0xBD => Ok('-'.to_string()), - 0xBA => Ok(';'.to_string()), - 0xBC => Ok(','.to_string()), - 0xBE => Ok('.'.to_string()), - 0xBF => Ok('/'.to_string()), - 0xC0 => Ok('`'.to_string()), - 0xDB => Ok('['.to_string()), - 0xDD => Ok(']'.to_string()), - 0xDC => Ok('\\'.to_string()), - 0xDE => Ok('\''.to_string()), - _ => Err(Error::UnsupportedKey(format!("{:x}", v))), - }, + Key::Other(v) => Ok(match v { + 0x30 => "0", + 0x31 => "1", + 0x32 => "2", + 0x33 => "3", + 0x34 => "4", + 0x35 => "5", + 0x36 => "6", + 0x37 => "7", + 0x38 => "8", + 0x39 => "9", + 0x41 => "A", + 0x42 => "B", + 0x43 => "C", + 0x44 => "D", + 0x45 => "E", + 0x46 => "F", + 0x47 => "G", + 0x48 => "H", + 0x49 => "I", + 0x4A => "J", + 0x4B => "K", + 0x4C => "L", + 0x4D => "M", + 0x4E => "N", + 0x4F => "O", + 0x50 => "P", + 0x51 => "Q", + 0x52 => "R", + 0x53 => "S", + 0x54 => "T", + 0x55 => "U", + 0x56 => "V", + 0x57 => "W", + 0x58 => "X", + 0x59 => "Y", + 0x5A => "Z", + 0xBB => "=", + 0xBD => "-", + 0xBA => ";", + 0xBC => ",", + 0xBE => ".", + 0xBF => "/", + 0xC0 => "`", + 0xDB => "[", + 0xDD => "]", + 0xDC => "\\", + 0xDE => "'", + _ => return Err(Error::UnsupportedKey(format!("{:x}", v))), + }), #[cfg(target_os = "macos")] - Key::Other(v) => match v { - 0 => Ok('A'.to_string()), - 1 => Ok('S'.to_string()), - 2 => Ok('D'.to_string()), - 3 => Ok('F'.to_string()), - 4 => Ok('H'.to_string()), - 5 => Ok('G'.to_string()), - 6 => Ok('Z'.to_string()), - 7 => Ok('X'.to_string()), - 8 => Ok('C'.to_string()), - 9 => Ok('V'.to_string()), - 11 => Ok('B'.to_string()), - 12 => Ok('Q'.to_string()), - 13 => Ok('W'.to_string()), - 14 => Ok('E'.to_string()), - 15 => Ok('R'.to_string()), - 16 => Ok('Y'.to_string()), - 17 => Ok('T'.to_string()), - 18 => Ok('1'.to_string()), - 19 => Ok('2'.to_string()), - 20 => Ok('3'.to_string()), - 21 => Ok('4'.to_string()), - 22 => Ok('6'.to_string()), - 23 => Ok('5'.to_string()), - 24 => Ok('='.to_string()), - 25 => Ok('9'.to_string()), - 26 => Ok('7'.to_string()), - 27 => Ok('-'.to_string()), - 28 => Ok('8'.to_string()), - 29 => Ok('0'.to_string()), - 30 => Ok(']'.to_string()), - 31 => Ok('O'.to_string()), - 32 => Ok('U'.to_string()), - 33 => Ok('['.to_string()), - 34 => Ok('I'.to_string()), - 35 => Ok('P'.to_string()), - 37 => Ok('L'.to_string()), - 38 => Ok('J'.to_string()), - 39 => Ok('\''.to_string()), - 40 => Ok('K'.to_string()), - 41 => Ok(';'.to_string()), - 42 => Ok('\\'.to_string()), - 43 => Ok(','.to_string()), - 44 => Ok('/'.to_string()), - 45 => Ok('N'.to_string()), - 46 => Ok('M'.to_string()), - 47 => Ok('.').to_string(), - 50 => Ok('`'.to_string()), - _ => Err(Error::UnsupportedKey(format!("{:x}", v))), - }, + Key::Other(v) => Ok(match v { + 0 => "A", + 1 => "S", + 2 => "D", + 3 => "F", + 4 => "H", + 5 => "G", + 6 => "Z", + 7 => "X", + 8 => "C", + 9 => "V", + 11 => "B", + 12 => "Q", + 13 => "W", + 14 => "E", + 15 => "R", + 16 => "Y", + 17 => "T", + 18 => "1", + 19 => "2", + 20 => "3", + 21 => "4", + 22 => "6", + 23 => "5", + 24 => "=", + 25 => "9", + 26 => "7", + 27 => "-", + 28 => "8", + 29 => "0", + 30 => "]", + 31 => "O", + 32 => "U", + 33 => "[", + 34 => "I", + 35 => "P", + 37 => "L", + 38 => "J", + 39 => "'", + 40 => "K", + 41 => ";", + 42 => "\\", + 43 => ",", + 44 => "/", + 45 => "N", + 46 => "M", + 47 => ".", + 50 => "`", + _ => return Err(Error::UnsupportedKey(format!("{:x}", v))), + }), _ => Err(Error::UnsupportedKey(format!("{:?}", key))), } From 50398bdcbe968e435f8fb1296e314d499a381dd2 Mon Sep 17 00:00:00 2001 From: Barafu Albino Cheetah Date: Sat, 20 Jul 2024 16:47:48 +0300 Subject: [PATCH 4/4] Refactor hotkey::Manager --- src/service/hotkey.rs | 99 ++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/src/service/hotkey.rs b/src/service/hotkey.rs index 90d3bc9..deeaf69 100644 --- a/src/service/hotkey.rs +++ b/src/service/hotkey.rs @@ -1,6 +1,5 @@ // std use std::{ - collections::HashMap, fmt::{Debug, Formatter, Result as FmtResult}, sync::{mpsc::Sender, Arc}, thread, @@ -38,7 +37,7 @@ impl Hotkey { ) -> Result { let ctx = ctx.to_owned(); let _manager = GlobalHotKeyManager::new().map_err(GlobalHotKeyError::Main)?; - let manager = Arc::new(RwLock::new(Manager::new(&_manager, hotkeys)?)); + let manager = Arc::new(RwLock::new(Manager::new(&_manager, hotkeys))); let manager_ = manager.clone(); let notification_sound = state.general.notification_sound.clone(); let activated_function = state.chat.activated_function.clone(); @@ -116,11 +115,9 @@ impl Hotkey { let mut manager = self.manager.write(); - for (hotkey, _keys) in manager.new_hotkeys.values() { - self.ghk_manager.unregister(*hotkey).expect("unregister must succeed"); - } + manager.unregister_hotkeys(&self.ghk_manager); - *manager = Manager::new(&self.ghk_manager, hotkeys).expect("renew must succeed"); + *manager = Manager::new(&self.ghk_manager, hotkeys); // Write hotkey texts back into the settings. manager.read_hotkeys_into_settings(hotkeys); @@ -136,50 +133,73 @@ impl Debug for Hotkey { } } +#[derive(Debug)] +struct HotKeyPair { + hotkey: HotKey, + keys: Keys, +} struct Manager { - /// Only the actually registered hotkeys - new_hotkeys: HashMap, + /// Hotkeys. The order follows the order in settings + hotkeys_list: [Option; 4], } impl Manager { - // Creates new manager. Assumes that hotkeys are valid. - fn new(ghk_manager: &GlobalHotKeyManager, settings_hotkeys: &Hotkeys) -> Result { - let hotkey_str_and_function = [ - (&settings_hotkeys.rewrite, Function::Rewrite), - (&settings_hotkeys.rewrite_directly, Function::RewriteDirectly), - (&settings_hotkeys.translate, Function::Translate), - (&settings_hotkeys.translate_directly, Function::TranslateDirectly), + /// Creates new manager. Registers hotkeys with global manager. + /// If any hotkey did not gersister, stores None instead of it. + fn new(ghk_manager: &GlobalHotKeyManager, settings_hotkeys: &Hotkeys) -> Self { + let hotkey_str_list = [ + &settings_hotkeys.rewrite, + &settings_hotkeys.rewrite_directly, + &settings_hotkeys.translate, + &settings_hotkeys.translate_directly, ]; - let mut new_hotkeys: HashMap = HashMap::new(); + let mut hotkeys_list: Vec> = Vec::with_capacity(4); - for (hotkey_str, funk) in hotkey_str_and_function.into_iter() { + for hotkey_str in hotkey_str_list.into_iter() { //Parse error possible when str value is "Not set" let hotkey: HotKey = match hotkey_str.parse() { Ok(v) => v, - Err(_) => continue, + Err(_) => { + hotkeys_list.push(None); + continue; + }, }; // Same goes for Keys - let hotkey_keys: Keys = match hotkey_str.parse() { + let keys: Keys = match hotkey_str.parse() { Ok(v) => v, - Err(_) => continue, + Err(_) => { + hotkeys_list.push(None); + continue; + }, }; // If manager.register fails, ignore the key, and it becomes "Not set". let e = ghk_manager.register(hotkey); if e.is_err() { + hotkeys_list.push(None); continue; } // If key has been registered, add it to new_hotkeys - new_hotkeys.insert(funk, (hotkey, hotkey_keys)); + hotkeys_list.push(Some(HotKeyPair { hotkey, keys })); } - Ok(Self { new_hotkeys }) + Self { hotkeys_list: hotkeys_list.try_into().expect("hotkeys_list must have 4 elements") } } fn match_func(&self, id: u32) -> (Function, Keys) { - for (k, v) in self.new_hotkeys.iter() { - if id == v.0.id { - return (*k, v.1.clone()); + // Must follow the same order of functions as in settings + const FUNCTION_LIST: [Function; 4] = [ + Function::Rewrite, + Function::RewriteDirectly, + Function::Translate, + Function::TranslateDirectly, + ]; + + for (i, pair) in self.hotkeys_list.iter().enumerate() { + if let Some(pair) = pair { + if pair.hotkey.id == id { + return (FUNCTION_LIST[i], pair.keys.clone()); + } } } unreachable!(); @@ -188,17 +208,28 @@ impl Manager { // Copies text representation of actually registered hotkeys back to settings. // Replaces text for unset hotkeys with "Not set" fn read_hotkeys_into_settings(&self, settings_hotkeys: &mut Hotkeys) { - let hotkey_str_and_function = [ - (&mut settings_hotkeys.rewrite, Function::Rewrite), - (&mut settings_hotkeys.rewrite_directly, Function::RewriteDirectly), - (&mut settings_hotkeys.translate, Function::Translate), - (&mut settings_hotkeys.translate_directly, Function::TranslateDirectly), + let hotkey_str_list = [ + &mut settings_hotkeys.rewrite, + &mut settings_hotkeys.rewrite_directly, + &mut settings_hotkeys.translate, + &mut settings_hotkeys.translate_directly, ]; - for (k, v) in hotkey_str_and_function.into_iter() { - *k = match self.new_hotkeys.get(&v) { - Some(v) => v.1.to_string().to_uppercase(), - None => "Not set".into(), + + for (i, k) in hotkey_str_list.into_iter().enumerate() { + *k = match &self.hotkeys_list[i] { + Some(p) => p.keys.to_string(), + None => "Not set".to_string(), }; } } + + /// Unregisters all hotkeys with given global hotkey manager. + fn unregister_hotkeys(&mut self, ghk_manager: &GlobalHotKeyManager) { + for list_entry in self.hotkeys_list.iter_mut() { + if let Some(pair) = list_entry { + ghk_manager.unregister(pair.hotkey).expect("unregister must succeed"); + } + *list_entry = None; + } + } }