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