mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-07-09 08:16:41 +12:00
Rework hotkey registration for sharing hotkeys between objects.
Use colors for working, failing or unset hotkeys in HotkeySelector.
This commit is contained in:
parent
8ece540881
commit
ce2d9772f6
|
@ -13,7 +13,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
internal class KeyboardInput : IDisposable
|
||||
{
|
||||
private GlobalHotkeys.HotkeyRegistrationHandle? hotkeyHandle;
|
||||
private GlobalHotkeys.IHotkeyRegistration? hotkeyRegistration;
|
||||
|
||||
private Menu? focussedMenu;
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace SystemTrayMenu.Handler
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
GlobalHotkeys.Unregister(hotkeyHandle);
|
||||
GlobalHotkeys.Unregister(hotkeyRegistration);
|
||||
}
|
||||
|
||||
internal bool RegisterHotKey(string hotKeyString)
|
||||
|
@ -36,7 +36,7 @@ namespace SystemTrayMenu.Handler
|
|||
{
|
||||
try
|
||||
{
|
||||
hotkeyHandle = GlobalHotkeys.Register(hotKeyString);
|
||||
hotkeyRegistration = GlobalHotkeys.Register(hotKeyString);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ namespace SystemTrayMenu.Handler
|
|||
return false;
|
||||
}
|
||||
|
||||
hotkeyHandle.KeyPressed += (_) => HotKeyPressed?.Invoke();
|
||||
hotkeyRegistration.KeyPressed += (_) => HotKeyPressed?.Invoke();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -30,6 +30,15 @@ namespace SystemTrayMenu.Helpers
|
|||
HWnd.AddHook(Hook);
|
||||
}
|
||||
|
||||
internal interface IHotkeyRegistration
|
||||
{
|
||||
event Action<IHotkeyRegistration>? KeyPressed;
|
||||
|
||||
ModifierKeys GetModifierKeys();
|
||||
|
||||
Key GetKey();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a global hotkey.
|
||||
/// Function is thread safe.
|
||||
|
@ -38,8 +47,8 @@ namespace SystemTrayMenu.Helpers
|
|||
/// </summary>
|
||||
/// <param name="modifiers">Hotkey modifiers.</param>
|
||||
/// <param name="key">Hotkey major key.</param>
|
||||
/// <returns>Handle of this registration.</returns>
|
||||
internal static HotkeyRegistrationHandle Register(ModifierKeys modifiers, Key key)
|
||||
/// <returns>Registration interface.</returns>
|
||||
internal static IHotkeyRegistration Register(ModifierKeys modifiers, Key key)
|
||||
{
|
||||
int virtualKeyCode = KeyInterop.VirtualKeyFromKey(key);
|
||||
int id = 0;
|
||||
|
@ -61,14 +70,14 @@ namespace SystemTrayMenu.Helpers
|
|||
}
|
||||
}
|
||||
|
||||
HotkeyRegistration regHandle = new()
|
||||
HotkeyRegistration registration = new()
|
||||
{
|
||||
Id = id,
|
||||
Modifiers = modifiers,
|
||||
Key = key,
|
||||
};
|
||||
Registrations.Add(regHandle);
|
||||
return regHandle;
|
||||
Registrations.Add(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -78,8 +87,8 @@ namespace SystemTrayMenu.Helpers
|
|||
/// The caller needs to call UnregisterHotkey to free up ressources.
|
||||
/// </summary>
|
||||
/// <param name="hotKeyString">Hotkey string representation.</param>
|
||||
/// <returns>Handle of this registration.</returns>
|
||||
internal static HotkeyRegistrationHandle Register(string hotKeyString)
|
||||
/// <returns>Registration interface.</returns>
|
||||
internal static IHotkeyRegistration Register(string hotKeyString)
|
||||
{
|
||||
var (modifiers, key) = ParseKeysAndModifiersFromString(hotKeyString);
|
||||
return Register(modifiers, key);
|
||||
|
@ -89,11 +98,11 @@ namespace SystemTrayMenu.Helpers
|
|||
/// Unregisters a global hotkey in a thread safe manner.
|
||||
/// Function is thread safe.
|
||||
/// </summary>
|
||||
/// <param name="regHandle">Handle of the registration.</param>
|
||||
/// <param name="registration">Registration interface.</param>
|
||||
/// <returns>true: Success or false: Failure.</returns>
|
||||
internal static bool Unregister(HotkeyRegistrationHandle? regHandle)
|
||||
internal static bool Unregister(IHotkeyRegistration? registration)
|
||||
{
|
||||
if (regHandle == null || regHandle is not HotkeyRegistration reg || Registrations.Contains(reg))
|
||||
if (registration == null || registration is not HotkeyRegistration reg || Registrations.Contains(reg))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -111,6 +120,26 @@ namespace SystemTrayMenu.Helpers
|
|||
return true;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
var (modifiers, key) = ParseKeysAndModifiersFromString(hotKeyString);
|
||||
|
||||
lock (CriticalSectionLock)
|
||||
{
|
||||
foreach (var registration in Registrations)
|
||||
{
|
||||
if (modifiers == registration.Modifiers && key == registration.Key)
|
||||
{
|
||||
return registration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static ModifierKeys ModifierKeysFromString(string modifiersString)
|
||||
{
|
||||
ModifierKeys modifiers = ModifierKeys.None;
|
||||
|
@ -296,11 +325,11 @@ namespace SystemTrayMenu.Helpers
|
|||
HotkeyRegistration? reg = null;
|
||||
lock (CriticalSectionLock)
|
||||
{
|
||||
foreach (var regHandle in Registrations)
|
||||
foreach (var registration in Registrations)
|
||||
{
|
||||
if (modifiers == regHandle.Modifiers && key == regHandle.Key)
|
||||
if (modifiers == registration.Modifiers && key == registration.Key)
|
||||
{
|
||||
reg = regHandle;
|
||||
reg = registration;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -313,22 +342,21 @@ namespace SystemTrayMenu.Helpers
|
|||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal abstract class HotkeyRegistrationHandle
|
||||
private class HotkeyRegistration : IHotkeyRegistration
|
||||
{
|
||||
internal event Action<HotkeyRegistrationHandle>? KeyPressed;
|
||||
public event Action<IHotkeyRegistration>? KeyPressed;
|
||||
|
||||
protected void RiseKeyPressed() => KeyPressed?.Invoke(this);
|
||||
}
|
||||
|
||||
private class HotkeyRegistration : HotkeyRegistrationHandle
|
||||
{
|
||||
internal int Id { get; init; }
|
||||
|
||||
internal ModifierKeys Modifiers { get; set; }
|
||||
|
||||
internal Key Key { get; set; }
|
||||
|
||||
internal void OnKeyPressed() => RiseKeyPressed();
|
||||
public ModifierKeys GetModifierKeys() => Modifiers;
|
||||
|
||||
public Key GetKey() => Key;
|
||||
|
||||
internal void OnKeyPressed() => KeyPressed?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,13 @@ namespace SystemTrayMenu.UserInterface
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using SystemTrayMenu.Helpers;
|
||||
using SystemTrayMenu.Utilities;
|
||||
using static SystemTrayMenu.Helpers.GlobalHotkeys;
|
||||
|
||||
public sealed class HotkeySelector : TextBox
|
||||
{
|
||||
|
@ -21,9 +24,8 @@ namespace SystemTrayMenu.UserInterface
|
|||
private readonly IList<int> needNonShiftModifier = new List<int>();
|
||||
private readonly IList<int> needNonAltGrModifier = new List<int>();
|
||||
|
||||
private GlobalHotkeys.HotkeyRegistrationHandle? hotkeyHandle;
|
||||
|
||||
// These variables store the current hotkey and modifier(s)
|
||||
private IHotkeyRegistration? hotkeyHandle;
|
||||
private Key hotkey = Key.None;
|
||||
private ModifierKeys modifiers = ModifierKeys.None;
|
||||
private Action? handler;
|
||||
|
@ -40,8 +42,6 @@ namespace SystemTrayMenu.UserInterface
|
|||
IsEnabled = false,
|
||||
};
|
||||
|
||||
Text = string.Empty;
|
||||
|
||||
// Handle events that occurs when keys are pressed
|
||||
KeyUp += HotkeyControl_KeyUp;
|
||||
KeyDown += HotkeyControl_KeyDown;
|
||||
|
@ -73,37 +73,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
};
|
||||
|
||||
PopulateModifierLists();
|
||||
}
|
||||
|
||||
~HotkeySelector()
|
||||
{
|
||||
GlobalHotkeys.Unregister(hotkeyHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets used to get/set the hotkey (e.g. Key.A).
|
||||
/// </summary>
|
||||
public Key Hotkey
|
||||
{
|
||||
get => hotkey;
|
||||
set
|
||||
{
|
||||
hotkey = value;
|
||||
Redraw(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets used to get/set the modifier keys (e.g. Alt | Control).
|
||||
/// </summary>
|
||||
public ModifierKeys HotkeyModifiers
|
||||
{
|
||||
get => modifiers;
|
||||
set
|
||||
{
|
||||
modifiers = value;
|
||||
Redraw(true);
|
||||
}
|
||||
SetHotkeyRegistration((IHotkeyRegistration?)null);
|
||||
}
|
||||
|
||||
internal bool Reassigning { get; private set; }
|
||||
|
@ -137,13 +107,43 @@ namespace SystemTrayMenu.UserInterface
|
|||
public override string ToString() => HotkeyToString(modifiers, hotkey);
|
||||
|
||||
/// <summary>
|
||||
/// Used to get/set the hotkey (e.g. Key.A).
|
||||
/// Set the registration interface the control is working on.
|
||||
/// </summary>
|
||||
/// <param name="hotkey">hotkey.</param>
|
||||
public void SetHotkey(string hotkey)
|
||||
/// <param name="registration">Registration interface.</param>
|
||||
internal void SetHotkeyRegistration(IHotkeyRegistration? registration)
|
||||
{
|
||||
this.hotkey = GlobalHotkeys.KeyFromString(hotkey);
|
||||
modifiers = GlobalHotkeys.ModifierKeysFromString(hotkey);
|
||||
hotkeyHandle = registration;
|
||||
if (hotkeyHandle != null)
|
||||
{
|
||||
hotkey = hotkeyHandle.GetKey();
|
||||
modifiers = hotkeyHandle.GetModifierKeys();
|
||||
Background = Brushes.LightGreen;
|
||||
}
|
||||
else
|
||||
{
|
||||
hotkey = Key.None;
|
||||
modifiers = ModifierKeys.None;
|
||||
Background = SystemColors.ControlBrush;
|
||||
}
|
||||
|
||||
Text = HotkeyToLocalizedString(modifiers, hotkey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the registration interface the control is working on.
|
||||
/// The registration interface is looked up by given hotkey combination string.
|
||||
/// </summary>
|
||||
/// <param name="hotkeyString">Hotkey combination string.</param>
|
||||
internal void SetHotkeyRegistration(string hotkeyString) => SetHotkeyRegistration(FindRegistration(hotkeyString));
|
||||
|
||||
/// <summary>
|
||||
/// Change the hotkey to given combination.
|
||||
/// </summary>
|
||||
/// <param name="hotkeyString">Hotkey combination string.</param>
|
||||
internal void ChangeHotkey(string hotkeyString)
|
||||
{
|
||||
hotkey = KeyFromString(hotkeyString);
|
||||
modifiers = ModifierKeysFromString(hotkeyString);
|
||||
Redraw(true);
|
||||
}
|
||||
|
||||
|
@ -154,25 +154,29 @@ namespace SystemTrayMenu.UserInterface
|
|||
/// <param name="key">The virtual key code.</param>
|
||||
/// <param name="handler">A HotKeyHandler, this will be called to handle the hotkey press.</param>
|
||||
/// <returns>the hotkey number, -1 if failed.</returns>
|
||||
public int RegisterHotKey(ModifierKeys modifiers, Key key, Action handler)
|
||||
internal int RegisterHotKey(ModifierKeys modifiers, Key key, Action handler)
|
||||
{
|
||||
if (key == Key.None)
|
||||
{
|
||||
Background = SystemColors.ControlBrush;
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
hotkeyHandle = GlobalHotkeys.Register(modifiers, key);
|
||||
hotkeyHandle = Register(modifiers, key);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Background = Brushes.IndianRed;
|
||||
Log.Info($"Couldn't register hotkey modifier {modifiers} key {key} ex: " + ex.ToString());
|
||||
return -1;
|
||||
}
|
||||
|
||||
this.handler = handler;
|
||||
hotkeyHandle.KeyPressed += (_) => handler.Invoke();
|
||||
|
||||
Background = Brushes.LightGreen;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -186,7 +190,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
private void UnregisterHotKey()
|
||||
{
|
||||
GlobalHotkeys.Unregister(hotkeyHandle);
|
||||
Unregister(hotkeyHandle);
|
||||
hotkeyHandle = null;
|
||||
}
|
||||
|
||||
|
@ -283,7 +287,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
hotkey = Key.None;
|
||||
}
|
||||
|
||||
Text = GlobalHotkeys.HotkeyToLocalizedString(modifiers, hotkey);
|
||||
Text = HotkeyToLocalizedString(modifiers, hotkey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
</GroupBox>
|
||||
<GroupBox Header="{u:Translate 'Hotkey'}">
|
||||
<DockPanel LastChildFill="False">
|
||||
<local:HotkeySelector x:Name="textBoxHotkey" Text="None" AcceptsTab="False" MinWidth="200" VerticalAlignment="Center"/>
|
||||
<local:HotkeySelector x:Name="textBoxHotkey" AcceptsTab="False" MinWidth="200" VerticalAlignment="Center"/>
|
||||
<Button x:Name="buttonHotkeyDefault" DockPanel.Dock="Right" Content="{u:Translate 'Default'}" Margin="3" MinWidth="76" Click="ButtonHotkeyDefault_Click" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
</GroupBox>
|
||||
|
|
|
@ -86,7 +86,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
checkBoxCheckForUpdates.IsChecked = Settings.Default.CheckForUpdates;
|
||||
|
||||
textBoxHotkey.SetHotkey(Settings.Default.HotKey);
|
||||
textBoxHotkey.SetHotkeyRegistration(Settings.Default.HotKey);
|
||||
|
||||
InitializeLanguage();
|
||||
void InitializeLanguage()
|
||||
|
@ -737,7 +737,7 @@ namespace SystemTrayMenu.UserInterface
|
|||
|
||||
private void ButtonHotkeyDefault_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
textBoxHotkey.SetHotkey((string)Settings.Default.Properties["HotKey"].DefaultValue); // see Settings.Default.HotKey
|
||||
textBoxHotkey.ChangeHotkey((string)Settings.Default.Properties["HotKey"].DefaultValue); // see Settings.Default.HotKey
|
||||
}
|
||||
|
||||
private void ButtonGeneralDefault_Click(object sender, RoutedEventArgs e)
|
||||
|
|
Loading…
Reference in a new issue