diff --git a/Helpers/GlobalHotkeys.cs b/Helpers/GlobalHotkeys.cs index 7ce39d8..6208355 100644 --- a/Helpers/GlobalHotkeys.cs +++ b/Helpers/GlobalHotkeys.cs @@ -39,6 +39,12 @@ namespace SystemTrayMenu.Helpers Key GetKey(); } + /// + /// Gets or sets a value indicating whether hotkeys are enabled + /// (e.g. during user configuration dialog). + /// + internal static bool IsEnabled { get; set; } = true; + /// /// Registers a global hotkey. /// Function is thread safe. @@ -57,9 +63,9 @@ namespace SystemTrayMenu.Helpers { foreach (var reg in Registrations) { - if (id < reg.Id) + if (id <= reg.Id) { - id = reg.Id; + id = reg.Id + 1; // TODO: Rework to re-use gaps } } @@ -102,7 +108,7 @@ namespace SystemTrayMenu.Helpers /// true: Success or false: Failure. internal static bool Unregister(IHotkeyRegistration? registration) { - if (registration == null || registration is not HotkeyRegistration reg || Registrations.Contains(reg)) + if (registration == null || registration is not HotkeyRegistration reg || !Registrations.Contains(reg)) { return true; } @@ -120,6 +126,61 @@ namespace SystemTrayMenu.Helpers return true; } + internal static bool Reassign(IHotkeyRegistration? registration, ModifierKeys modifiers, Key key) + { + if (registration == null || registration is not HotkeyRegistration reg || !Registrations.Contains(reg)) + { + return false; + } + + if (modifiers == reg.Modifiers && key == reg.Key) + { + return true; // Yes, nothing changed, but we return true as requested key is properly registered even when unchanged. + } + + int virtualKeyCode = KeyInterop.VirtualKeyFromKey(key); + int id = 0; + + lock (CriticalSectionLock) + { + foreach (var regs in Registrations) + { + if (id <= regs.Id) + { + id = reg.Id + 1; // TODO: Rework to re-use gaps + } + } + + if (!NativeMethods.User32RegisterHotKey(HWnd.Handle, id, (uint)modifiers, (uint)virtualKeyCode)) + { + string errorHint = NativeMethods.GetLastErrorHint(); + throw new InvalidOperationException(Translator.GetText("Could not register the hot key.") + " (" + errorHint + ")"); + } + + // In case unregister failes, unfortunately registration remains + // but will not trigger anything as we change our hotkey registration. + // However, this means the hotkey keeps being registered with this application + // and the key combination will not be availalbe for re-registration till app restart. + // TODO: Decide how to handle this? Restart App? Try keep old registartion and not update it? + if (!NativeMethods.User32UnregisterHotKey(HWnd.Handle, reg.Id)) + { + Log.Info("Hotkey registration cannot unregister key " + reg.Modifiers.ToString() + " with modifiers " + reg.Modifiers.ToString()); + } + + reg.Id = id; + reg.Modifiers = modifiers; + reg.Key = key; + } + + return true; + } + + internal static bool Reassign(IHotkeyRegistration? registration, string hotKeyString) + { + var (modifiers, key) = ParseKeysAndModifiersFromString(hotKeyString); + return Reassign(registration, modifiers, key); + } + // TODO: Instead of searching for the registration, it should be passed to the caller instead. // Only this ensures caller and registrator are talking about the SAME registration. internal static IHotkeyRegistration? FindRegistration(string hotKeyString) @@ -316,7 +377,7 @@ namespace SystemTrayMenu.Helpers const int WmHotkey = 0x0312; // check if we got a hot key pressed. - if (msg == WmHotkey) + if (msg == WmHotkey && IsEnabled) { ModifierKeys modifiers = (ModifierKeys)((int)lParam & 0xFFFF); int virtualKeyCode = ((int)lParam >> 16) & 0xFFFF; @@ -346,7 +407,7 @@ namespace SystemTrayMenu.Helpers { public event Action? KeyPressed; - internal int Id { get; init; } + internal int Id { get; set; } internal ModifierKeys Modifiers { get; set; } diff --git a/UserInterface/HotkeySelector.cs b/UserInterface/HotkeySelector.cs index ea6a212..c95a7e7 100644 --- a/UserInterface/HotkeySelector.cs +++ b/UserInterface/HotkeySelector.cs @@ -25,7 +25,7 @@ namespace SystemTrayMenu.UserInterface private readonly IList needNonAltGrModifier = new List(); // These variables store the current hotkey and modifier(s) - private IHotkeyRegistration? hotkeyHandle; + private IHotkeyRegistration? hotkeyRegistration; private Key hotkey = Key.None; private ModifierKeys modifiers = ModifierKeys.None; private Action? handler; @@ -48,11 +48,7 @@ namespace SystemTrayMenu.UserInterface PreviewKeyDown += HandlePreviewKeyDown; PreviewTextInput += HandlePreviewTextInput; - GotFocus += (_, _) => - { - UnregisterHotKey(); - Reassigning = true; - }; + GotFocus += (_, _) => GlobalHotkeys.IsEnabled = false; LostFocus += (_, _) => { #if TODO // HOTKEY @@ -66,18 +62,14 @@ namespace SystemTrayMenu.UserInterface /// /// Whether the hotkeys could be registered to the users content. This also applies if conflicts arise and the user decides to ignore these (i.e. not to register the conflicting hotkey). RegisterHotkeys(false); -#else - ReregisterHotKey(); #endif - Reassigning = false; + GlobalHotkeys.IsEnabled = true; }; PopulateModifierLists(); SetHotkeyRegistration((IHotkeyRegistration?)null); } - internal bool Reassigning { get; private set; } - public static string HotkeyToString(ModifierKeys modifierKeyCode, Key key) { StringBuilder hotkeyString = new(); @@ -112,11 +104,11 @@ namespace SystemTrayMenu.UserInterface /// Registration interface. internal void SetHotkeyRegistration(IHotkeyRegistration? registration) { - hotkeyHandle = registration; - if (hotkeyHandle != null) + hotkeyRegistration = registration; + if (hotkeyRegistration != null) { - hotkey = hotkeyHandle.GetKey(); - modifiers = hotkeyHandle.GetModifierKeys(); + hotkey = hotkeyRegistration.GetKey(); + modifiers = hotkeyRegistration.GetModifierKeys(); Background = Brushes.LightGreen; } else @@ -140,12 +132,7 @@ namespace SystemTrayMenu.UserInterface /// Change the hotkey to given combination. /// /// Hotkey combination string. - internal void ChangeHotkey(string hotkeyString) - { - hotkey = KeyFromString(hotkeyString); - modifiers = ModifierKeysFromString(hotkeyString); - Redraw(true); - } + internal void ChangeHotkey(string hotkeyString) => Reassign(hotkeyRegistration, hotkeyString); /// /// Register a hotkey. @@ -164,7 +151,7 @@ namespace SystemTrayMenu.UserInterface try { - hotkeyHandle = Register(modifiers, key); + hotkeyRegistration = Register(modifiers, key); } catch (InvalidOperationException ex) { @@ -174,26 +161,12 @@ namespace SystemTrayMenu.UserInterface } this.handler = handler; - hotkeyHandle.KeyPressed += (_) => handler.Invoke(); + hotkeyRegistration.KeyPressed += (_) => handler.Invoke(); Background = Brushes.LightGreen; return 1; } - private void ReregisterHotKey() - { - if (handler != null) - { - RegisterHotKey(modifiers, hotkey, handler); - } - } - - private void UnregisterHotKey() - { - Unregister(hotkeyHandle); - hotkeyHandle = null; - } - /// /// Clears the current hotkey and resets the TextBox. /// @@ -354,6 +327,7 @@ namespace SystemTrayMenu.UserInterface { modifiers = Keyboard.Modifiers; hotkey = e.Key; + Reassign(hotkeyRegistration, modifiers, hotkey); Redraw(false); } } diff --git a/UserInterface/SettingsWindow.xaml.cs b/UserInterface/SettingsWindow.xaml.cs index b7b3659..457bafa 100644 --- a/UserInterface/SettingsWindow.xaml.cs +++ b/UserInterface/SettingsWindow.xaml.cs @@ -14,6 +14,7 @@ namespace SystemTrayMenu.UserInterface using System.Windows.Input; using System.Windows.Media.Imaging; using Microsoft.Win32; + using SystemTrayMenu.Helpers; using SystemTrayMenu.Properties; using SystemTrayMenu.UserInterface.FolderBrowseDialog; using SystemTrayMenu.Utilities; @@ -445,7 +446,7 @@ namespace SystemTrayMenu.UserInterface private void HandlePreviewKeyDown(object sender, KeyEventArgs e) { - if (e.Key == Key.Escape && !textBoxHotkey.Reassigning) + if (e.Key == Key.Escape && GlobalHotkeys.IsEnabled) { Close(); }