From 27ccd3b24c7cd07f2e33a9e229f7871e627039b9 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 1 Jan 2025 13:50:42 +1100 Subject: [PATCH 1/6] add new hotkey support for win and four combo keys --- .../Hotkey/HotkeyModel.cs | 114 ++++++++++++++--- Flow.Launcher.Infrastructure/KeyConstant.cs | 5 +- .../UserSettings/Settings.cs | 96 +++++++-------- Flow.Launcher/CustomQueryHotkeySetting.xaml | 3 +- .../CustomQueryHotkeySetting.xaml.cs | 6 +- Flow.Launcher/Flow.Launcher.csproj | 2 +- Flow.Launcher/Helper/HotKeyMapper.cs | 66 +++------- Flow.Launcher/HotkeyControl.xaml.cs | 85 +++++++++---- Flow.Launcher/HotkeyControlDialog.xaml.cs | 116 ++++++++++++++---- Flow.Launcher/Languages/en.xaml | 2 - .../Resources/Pages/WelcomePage2.xaml | 5 +- .../Resources/Pages/WelcomePage2.xaml.cs | 2 +- .../ViewModels/SettingsPaneHotkeyViewModel.cs | 4 +- .../Views/SettingsPaneHotkey.xaml | 5 +- 14 files changed, 334 insertions(+), 177 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index 25bc75a56c1..a6a38c4f15a 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Linq; using System.Windows.Input; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.Hotkey { @@ -13,6 +14,9 @@ public record struct HotkeyModel public bool Win { get; set; } public bool Ctrl { get; set; } + public string HotkeyRaw { get; set; } = string.Empty; + public string PreviousHotkey { get; set; } = string.Empty; + public Key CharKey { get; set; } = Key.None; private static readonly Dictionary specialSymbolDictionary = new Dictionary @@ -49,18 +53,56 @@ public ModifierKeys ModifierKeys } } - public HotkeyModel(string hotkeyString) + // Used for WPF control only + public void SetHotkeyFromString(string hotkeyString) { + Clear(); Parse(hotkeyString); + HotkeyRaw = ToChefKeysString(); } - public HotkeyModel(bool alt, bool shift, bool win, bool ctrl, Key key) + internal void SetHotkeyFromWPFControl(SpecialKeyState specialKeyState, Key key) { - Alt = alt; - Shift = shift; - Win = win; - Ctrl = ctrl; + Alt = specialKeyState.AltPressed; + Shift = specialKeyState.ShiftPressed; + Win = specialKeyState.WinPressed; + Ctrl = specialKeyState.CtrlPressed; CharKey = key; + HotkeyRaw = ToChefKeysString(); + PreviousHotkey = string.Empty; + } + + public HotkeyModel(string hotkey) + { + SetHotkeyFromString(hotkey); + } + + //public HotkeyModel(bool alt, bool shift, bool win, bool ctrl, Key key) + //{ + // Alt = alt; + // Shift = shift; + // Win = win; + // Ctrl = ctrl; + // CharKey = key; + //} + + // Use for ChefKeys only + internal void AddString(string key) + { + HotkeyRaw = string.IsNullOrEmpty(HotkeyRaw) ? key : HotkeyRaw + "+" + key; + } + + internal bool MaxKeysReached() => DisplayKeysRaw().Count() == 4; + + internal void Clear() + { + Alt = false; + Shift = false; + Win = false; + Ctrl = false; + HotkeyRaw = string.Empty; + PreviousHotkey = string.Empty; + CharKey = Key.None; } private void Parse(string hotkeyString) @@ -71,28 +113,36 @@ private void Parse(string hotkeyString) } List keys = hotkeyString.Replace(" ", "").Split('+').ToList(); - if (keys.Contains("Alt")) + if (keys.Contains("Alt") || keys.Contains("LeftAlt") || keys.Contains("RightAlt")) { Alt = true; keys.Remove("Alt"); + keys.Remove("LeftAlt"); + keys.Remove("RightAlt"); } - if (keys.Contains("Shift")) + if (keys.Contains("Shift") || keys.Contains("LeftShift") || keys.Contains("RightShift")) { Shift = true; keys.Remove("Shift"); + keys.Remove("LeftShift"); + keys.Remove("RightShift"); } - if (keys.Contains("Win")) + if (keys.Contains("Win") || keys.Contains("LWin") || keys.Contains("RWin")) { Win = true; keys.Remove("Win"); + keys.Remove("LWin"); + keys.Remove("RWin"); } - if (keys.Contains("Ctrl")) + if (keys.Contains("Ctrl") || keys.Contains("LeftCtrl")|| keys.Contains("RightCtrl")) { Ctrl = true; keys.Remove("Ctrl"); + keys.Remove("LeftCtrl"); + keys.Remove("RightCtrl"); } if (keys.Count == 1) @@ -117,47 +167,71 @@ private void Parse(string hotkeyString) } } - public override string ToString() + public string ToChefKeysString() { - return string.Join(" + ", EnumerateDisplayKeys()); + var key = string.Join("+", EnumerateDisplayKeys(true)); + + return key; } - public IEnumerable EnumerateDisplayKeys() + public IEnumerable DisplayKeysRaw() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+') : Array.Empty(); + + public IEnumerable EnumerateDisplayKeys(bool forChefKeys = false) { if (Ctrl && CharKey is not (Key.LeftCtrl or Key.RightCtrl)) { - yield return "Ctrl"; + yield return GetKeyString("Ctrl", forChefKeys); } if (Alt && CharKey is not (Key.LeftAlt or Key.RightAlt)) { - yield return "Alt"; + yield return GetKeyString("Alt", forChefKeys); } if (Shift && CharKey is not (Key.LeftShift or Key.RightShift)) { - yield return "Shift"; + yield return GetKeyString("Shift", forChefKeys); } if (Win && CharKey is not (Key.LWin or Key.RWin)) { - yield return "Win"; + yield return GetKeyString("Win", forChefKeys); } if (CharKey != Key.None) { yield return specialSymbolDictionary.TryGetValue(CharKey, out var value) ? value - : CharKey.ToString(); + : GetKeyString(CharKey.ToString(), forChefKeys); + } + } + + private string GetKeyString(string key, bool convertToChefKeysString) + { + if (!convertToChefKeysString) + return key; + + switch (key) + { + case "Alt": + return "LeftAlt"; + case "Ctrl": + return "LeftCtrl"; + case "Shift": + return "LeftShift"; + case "Win": + return "LWin"; + default: + return key; } } /// - /// Validate hotkey + /// Validate hotkey for WPF control only /// /// Try to validate hotkey as a KeyGesture. /// - public bool Validate(bool validateKeyGestrue = false) + public bool ValidateForWpf(bool validateKeyGestrue = false) { switch (CharKey) { diff --git a/Flow.Launcher.Infrastructure/KeyConstant.cs b/Flow.Launcher.Infrastructure/KeyConstant.cs index 31748517643..0deb4dc2306 100644 --- a/Flow.Launcher.Infrastructure/KeyConstant.cs +++ b/Flow.Launcher.Infrastructure/KeyConstant.cs @@ -2,8 +2,9 @@ { public static class KeyConstant { - public const string Ctrl = nameof(Ctrl); public const string Alt = nameof(Alt); + public const string LeftAlt = nameof(LeftAlt); + public const string Ctrl = nameof(Ctrl); public const string Space = nameof(Space); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 0bcc9368d22..1d00c391dfd 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -15,7 +15,7 @@ public class Settings : BaseModel, IHotkeySettings { private string language = "en"; private string _theme = Constant.DefaultTheme; - public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; + public string Hotkey { get; set; } = $"{KeyConstant.LeftAlt} + {KeyConstant.Space}"; public string OpenResultModifiers { get; set; } = KeyConstant.Alt; public string ColorScheme { get; set; } = "System"; public bool ShowOpenResultHotkey { get; set; } = true; @@ -288,32 +288,32 @@ public List RegisteredHotkeys // Customizeable hotkeys if(!string.IsNullOrEmpty(Hotkey)) list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = "")); - if(!string.IsNullOrEmpty(PreviewHotkey)) + if (!string.IsNullOrEmpty(PreviewHotkey)) list.Add(new(PreviewHotkey, "previewHotkey", () => PreviewHotkey = "")); - if(!string.IsNullOrEmpty(AutoCompleteHotkey)) + if (!string.IsNullOrEmpty(AutoCompleteHotkey)) list.Add(new(AutoCompleteHotkey, "autoCompleteHotkey", () => AutoCompleteHotkey = "")); - if(!string.IsNullOrEmpty(AutoCompleteHotkey2)) + if (!string.IsNullOrEmpty(AutoCompleteHotkey2)) list.Add(new(AutoCompleteHotkey2, "autoCompleteHotkey", () => AutoCompleteHotkey2 = "")); - if(!string.IsNullOrEmpty(SelectNextItemHotkey)) + if (!string.IsNullOrEmpty(SelectNextItemHotkey)) list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = "")); - if(!string.IsNullOrEmpty(SelectNextItemHotkey2)) + if (!string.IsNullOrEmpty(SelectNextItemHotkey2)) list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = "")); - if(!string.IsNullOrEmpty(SelectPrevItemHotkey)) + if (!string.IsNullOrEmpty(SelectPrevItemHotkey)) list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = "")); - if(!string.IsNullOrEmpty(SelectPrevItemHotkey2)) + if (!string.IsNullOrEmpty(SelectPrevItemHotkey2)) list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = "")); - if(!string.IsNullOrEmpty(SettingWindowHotkey)) - list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); - if(!string.IsNullOrEmpty(OpenContextMenuHotkey)) - list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); - if(!string.IsNullOrEmpty(SelectNextPageHotkey)) - list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); - if(!string.IsNullOrEmpty(SelectPrevPageHotkey)) - list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); - if (!string.IsNullOrEmpty(CycleHistoryUpHotkey)) - list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); - if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) - list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); + //if (!string.IsNullOrEmpty(SettingWindowHotkey)) + // list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); + //if (!string.IsNullOrEmpty(OpenContextMenuHotkey)) + // list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); + //if (!string.IsNullOrEmpty(SelectNextPageHotkey)) + // list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); + //if (!string.IsNullOrEmpty(SelectPrevPageHotkey)) + // list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); + //if (!string.IsNullOrEmpty(CycleHistoryUpHotkey)) + // list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); + //if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) + // list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); // Custom Query Hotkeys foreach (var customPluginHotkey in CustomPluginHotkeys) @@ -334,34 +334,34 @@ private List FixedHotkeys() new("Down", "HotkeyLeftRightDesc"), new("Left", "HotkeyUpDownDesc"), new("Right", "HotkeyUpDownDesc"), - new("Escape", "HotkeyESCDesc"), - new("F5", "ReloadPluginHotkey"), - new("Alt+Home", "HotkeySelectFirstResult"), - new("Alt+End", "HotkeySelectLastResult"), - new("Ctrl+R", "HotkeyRequery"), - new("Ctrl+H", "ToggleHistoryHotkey"), - new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"), - new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"), - new("Ctrl+OemPlus", "QuickHeightHotkey"), - new("Ctrl+OemMinus", "QuickHeightHotkey"), - new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"), - new("Shift+Enter", "OpenContextMenuHotkey"), - new("Enter", "HotkeyRunDesc"), - new("Ctrl+Enter", "OpenContainFolderHotkey"), - new("Alt+Enter", "HotkeyOpenResult"), - new("Ctrl+F12", "ToggleGameModeHotkey"), - new("Ctrl+Shift+C", "CopyFilePathHotkey"), - - new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1), - new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2), - new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3), - new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4), - new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5), - new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6), - new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7), - new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8), - new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9), - new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10) + //new("Escape", "HotkeyESCDesc"), + //new("F5", "ReloadPluginHotkey"), + //new("Alt+Home", "HotkeySelectFirstResult"), + //new("Alt+End", "HotkeySelectLastResult"), + //new("Ctrl+R", "HotkeyRequery"), + //new("Ctrl+H", "ToggleHistoryHotkey"), + //new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"), + //new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"), + //new("Ctrl+OemPlus", "QuickHeightHotkey"), + //new("Ctrl+OemMinus", "QuickHeightHotkey"), + //new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"), + //new("Shift+Enter", "OpenContextMenuHotkey"), + //new("Enter", "HotkeyRunDesc"), + //new("Ctrl+Enter", "OpenContainFolderHotkey"), + //new("Alt+Enter", "HotkeyOpenResult"), + //new("Ctrl+F12", "ToggleGameModeHotkey"), + //new("Ctrl+Shift+C", "CopyFilePathHotkey"), + + //new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1), + //new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2), + //new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3), + //new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4), + //new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5), + //new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6), + //new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7), + //new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8), + //new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9), + //new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10) }; } } diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index 068afda15b4..7815ce9abb9 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -107,7 +107,8 @@ VerticalAlignment="Center" HorizontalContentAlignment="Left" HotkeySettings="{Binding Settings}" - DefaultHotkey="" /> + DefaultHotkey="" + IsWPFHotkeyControl="False" /> + all @@ -93,7 +94,6 @@ - diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 8b30b8be1f5..17aed066770 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -1,11 +1,11 @@ using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using System; -using NHotkey; -using NHotkey.Wpf; using Flow.Launcher.Core.Resource; using Flow.Launcher.ViewModel; using Flow.Launcher.Core; +using ChefKeys; +using System.Windows.Input; namespace Flow.Launcher.Helper; @@ -13,56 +13,37 @@ internal static class HotKeyMapper { private static Settings _settings; private static MainViewModel _mainViewModel; - + internal static void Initialize(MainViewModel mainVM) { _mainViewModel = mainVM; _settings = _mainViewModel.Settings; - SetHotkey(_settings.Hotkey, OnToggleHotkey); + ChefKeysManager.RegisterHotkey(_settings.Hotkey, ToggleHotkey); + ChefKeysManager.Start(); + LoadCustomPluginHotkey(); } - internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) + internal static void ToggleHotkey() { if (!_mainViewModel.ShouldIgnoreHotkeys()) _mainViewModel.ToggleFlowLauncher(); } - private static void SetHotkey(string hotkeyStr, EventHandler action) + internal static void RegisterHotkey(string hotkey, string previousHotkey, Action action) { - var hotkey = new HotkeyModel(hotkeyStr); - SetHotkey(hotkey, action); - } - - internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) - { - string hotkeyStr = hotkey.ToString(); - try - { - HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); - } - catch (Exception) - { - string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); - string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle"); - MessageBoxEx.Show(errorMsg, errorMsgTitle); - } + ChefKeysManager.RegisterHotkey(hotkey, previousHotkey, action); } - internal static void RemoveHotkey(string hotkeyStr) + internal static void UnregisterHotkey(string hotkey) { - if (!string.IsNullOrEmpty(hotkeyStr)) - { - HotkeyManager.Current.Remove(hotkeyStr); - } + if (!string.IsNullOrEmpty(hotkey)) + ChefKeysManager.UnregisterHotkey(hotkey); } internal static void LoadCustomPluginHotkey() { - if (_settings.CustomPluginHotkeys == null) - return; - foreach (CustomPluginHotkey hotkey in _settings.CustomPluginHotkeys) { SetCustomQueryHotkey(hotkey); @@ -71,7 +52,7 @@ internal static void LoadCustomPluginHotkey() internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) { - SetHotkey(hotkey.Hotkey, (s, e) => + ChefKeysManager.RegisterHotkey(hotkey.Hotkey, () => { if (_mainViewModel.ShouldIgnoreHotkeys()) return; @@ -81,22 +62,13 @@ internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) }); } - internal static bool CheckAvailability(HotkeyModel currentHotkey) + internal static bool CanRegisterHotkey(string hotkey) { - try - { - HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { }); + return ChefKeysManager.CanRegisterHotkey(hotkey); + } - return true; - } - catch - { - } - finally - { - HotkeyManager.Current.Remove("HotkeyAvailabilityTest"); - } + internal static bool CheckHotkeyAvailability(string hotkey) => ChefKeysManager.IsAvailable(hotkey); + + internal static bool CheckHotkeyValid(string hotkey) => ChefKeysManager.IsValidHotkey(hotkey); - return false; - } } diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index a42bde7c9cd..9f1c8761eb1 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.ObjectModel; +using System.Globalization; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -12,6 +13,8 @@ namespace Flow.Launcher { public partial class HotkeyControl { + private HotkeyControlDialog hotkeyControlDialog; + public IHotkeySettings HotkeySettings { get { return (IHotkeySettings)GetValue(HotkeySettingsProperty); } set { SetValue(HotkeySettingsProperty, value); } @@ -71,8 +74,12 @@ private static void OnHotkeyChanged(DependencyObject d, DependencyPropertyChange return; } - hotkeyControl.SetKeysToDisplay(new HotkeyModel(hotkeyControl.Hotkey)); - hotkeyControl.CurrentHotkey = new HotkeyModel(hotkeyControl.Hotkey); + //hotkeyControl.SetKeysToDisplay(new HotkeyModel(hotkeyControl.Hotkey)); + //hotkeyControl.CurrentHotkey = new HotkeyModel(hotkeyControl.Hotkey); + + var hotkeyModel = new HotkeyModel(hotkeyControl.Hotkey); + hotkeyControl.SetKeysToDisplay(hotkeyModel); + hotkeyControl.CurrentHotkey = hotkeyModel; } @@ -103,6 +110,19 @@ public string Hotkey set { SetValue(HotkeyProperty, value); } } + public static readonly DependencyProperty IsWPFHotkeyControlProperty = DependencyProperty.Register( + nameof(IsWPFHotkeyControl), + typeof(bool), + typeof(HotkeyControl), + new PropertyMetadata(true) + ); + + public bool IsWPFHotkeyControl + { + get { return (bool)GetValue(IsWPFHotkeyControlProperty); } + set { SetValue(IsWPFHotkeyControlProperty, value); } + } + public HotkeyControl() { InitializeComponent(); @@ -111,14 +131,17 @@ public HotkeyControl() SetKeysToDisplay(CurrentHotkey); } - private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => - hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey); + private static bool CheckHotkeyValid(string hotkey) + => HotKeyMapper.CheckHotkeyValid(hotkey); + + private static bool CheckWPFHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) + => hotkey.ValidateForWpf(validateKeyGesture); public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none"); public ObservableCollection KeysToDisplay { get; set; } = new(); - public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None); + public HotkeyModel CurrentHotkey { get; private set; } = new(); public void GetNewHotkey(object sender, RoutedEventArgs e) @@ -128,20 +151,15 @@ public void GetNewHotkey(object sender, RoutedEventArgs e) private async Task OpenHotkeyDialog() { - if (!string.IsNullOrEmpty(Hotkey)) - { - HotKeyMapper.RemoveHotkey(Hotkey); - } - - var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, HotkeySettings, WindowTitle); - await dialog.ShowAsync(); - switch (dialog.ResultType) + hotkeyControlDialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, HotkeySettings, IsWPFHotkeyControl, WindowTitle); + await hotkeyControlDialog.ShowAsync(); + switch (hotkeyControlDialog.ResultType) { case HotkeyControlDialog.EResultType.Cancel: - SetHotkey(Hotkey); + //SetHotkey(Hotkey); return; case HotkeyControlDialog.EResultType.Save: - SetHotkey(dialog.ResultValue); + SetHotkey(hotkeyControlDialog.ResultValue); break; case HotkeyControlDialog.EResultType.Delete: Delete(); @@ -149,25 +167,33 @@ private async Task OpenHotkeyDialog() } } - private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) { + // WPF hotkey control uses CharKey + if (string.IsNullOrEmpty(keyModel.HotkeyRaw) || string.IsNullOrEmpty(keyModel.CharKey.ToString())) + return; + if (triggerValidate) { - bool hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture); + var hotkeyAvailable = IsWPFHotkeyControl + ? CheckWPFHotkeyAvailability(keyModel, ValidateKeyGesture) + : CheckHotkeyValid(keyModel.HotkeyRaw); if (!hotkeyAvailable) - { return; - } - Hotkey = keyModel.ToString(); + Hotkey = keyModel.HotkeyRaw; SetKeysToDisplay(CurrentHotkey); + + // If exists then will be unregistered, if doesn't no errors will be thrown. + if (IsWPFHotkeyControl) + HotKeyMapper.UnregisterHotkey(keyModel.HotkeyRaw); + ChangeHotkey?.Execute(keyModel); } else { - Hotkey = keyModel.ToString(); + Hotkey = keyModel.HotkeyRaw; ChangeHotkey?.Execute(keyModel); } } @@ -175,16 +201,16 @@ private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) public void Delete() { if (!string.IsNullOrEmpty(Hotkey)) - HotKeyMapper.RemoveHotkey(Hotkey); + HotKeyMapper.UnregisterHotkey(Hotkey); Hotkey = ""; - SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None)); + SetKeysToDisplay(new HotkeyModel(Hotkey)); } private void SetKeysToDisplay(HotkeyModel? hotkey) { KeysToDisplay.Clear(); - if (hotkey == null || hotkey == default(HotkeyModel)) + if (hotkey == null || string.IsNullOrEmpty(hotkey.Value.HotkeyRaw)) { KeysToDisplay.Add(EmptyHotkey); return; @@ -198,7 +224,16 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) public void SetHotkey(string? keyStr, bool triggerValidate = true) { - SetHotkey(new HotkeyModel(keyStr), triggerValidate); + if (string.IsNullOrEmpty(keyStr)) + return; + + // index 0 - new hotkey to be added, index 1 - old hotkey to be removed + var hotkeyNewOld = keyStr.Split(":"); + var hotkey = new HotkeyModel(hotkeyNewOld[0]) + { + PreviousHotkey = hotkeyNewOld.Length == 2 ? hotkeyNewOld[1] : hotkeyNewOld[0] + }; + SetHotkey(hotkey, triggerValidate); } } } diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index a7b99f6704b..9d7d0cb8730 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -6,8 +6,9 @@ using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure.Hotkey; -using Flow.Launcher.Plugin; using ModernWpf.Controls; +using ChefKeys; +using System.Collections.Generic; namespace Flow.Launcher; @@ -19,7 +20,15 @@ public partial class HotkeyControlDialog : ContentDialog private Action? _overwriteOtherHotkey; private string DefaultHotkey { get; } public string WindowTitle { get; } - public HotkeyModel CurrentHotkey { get; private set; } + + public HotkeyModel CurrentHotkey; + + public HotkeyModel HotkeyToUpdate; + + private bool isWPFHotkeyControl = true; + + private bool clearKeysOnFirstType; + public ObservableCollection KeysToDisplay { get; } = new(); public enum EResultType @@ -33,40 +42,68 @@ public enum EResultType public string ResultValue { get; private set; } = string.Empty; public static string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none"); - public HotkeyControlDialog(string hotkey, string defaultHotkey, IHotkeySettings hotkeySettings, string windowTitle = "") + public HotkeyControlDialog( + string hotkey, + string defaultHotkey, + IHotkeySettings hotkeySettings, + bool isWPFHotkeyControl, + string windowTitle = "") { + this.isWPFHotkeyControl = isWPFHotkeyControl; + WindowTitle = windowTitle switch { "" or null => InternationalizationManager.Instance.GetTranslation("hotkeyRegTitle"), _ => windowTitle }; DefaultHotkey = defaultHotkey; + CurrentHotkey = new HotkeyModel(hotkey); + // This is a requirement to be set with current hotkey for the WPF hotkey control when saving without any new changes + HotkeyToUpdate = new HotkeyModel(hotkey); + _hotkeySettings = hotkeySettings; + SetKeysToDisplay(CurrentHotkey); + clearKeysOnFirstType = true; InitializeComponent(); + + ChefKeysManager.StartMenuEnableBlocking = true; } private void Reset(object sender, RoutedEventArgs routedEventArgs) { - SetKeysToDisplay(new HotkeyModel(DefaultHotkey)); + HotkeyToUpdate = new HotkeyModel(DefaultHotkey); + SetKeysToDisplay(HotkeyToUpdate); + clearKeysOnFirstType = true; } private void Delete(object sender, RoutedEventArgs routedEventArgs) { + HotkeyToUpdate.Clear(); KeysToDisplay.Clear(); KeysToDisplay.Add(EmptyHotkey); + tbMsg.Text = string.Empty; + SaveBtn.IsEnabled = true; + SaveBtn.Visibility = Visibility.Visible; + OverwriteBtn.IsEnabled = false; + OverwriteBtn.Visibility = Visibility.Collapsed; + Alert.Visibility = Visibility.Collapsed; } private void Cancel(object sender, RoutedEventArgs routedEventArgs) { + ChefKeysManager.StartMenuEnableBlocking = false; + ResultType = EResultType.Cancel; Hide(); } private void Save(object sender, RoutedEventArgs routedEventArgs) { + ChefKeysManager.StartMenuEnableBlocking = false; + if (KeysToDisplay.Count == 1 && KeysToDisplay[0] == EmptyHotkey) { ResultType = EResultType.Delete; @@ -74,7 +111,9 @@ private void Save(object sender, RoutedEventArgs routedEventArgs) return; } ResultType = EResultType.Save; - ResultValue = string.Join("+", KeysToDisplay); + var newHotkey = string.Join("+", KeysToDisplay); + var oldHotkey = !string.IsNullOrEmpty(CurrentHotkey.HotkeyRaw) ? CurrentHotkey.HotkeyRaw : newHotkey; + ResultValue = string.Format("{0}:{1}", newHotkey, oldHotkey); Hide(); } @@ -85,17 +124,29 @@ private void OnPreviewKeyDown(object sender, KeyEventArgs e) //when alt is pressed, the real key should be e.SystemKey Key key = e.Key == Key.System ? e.SystemKey : e.Key; - SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers(); + if (clearKeysOnFirstType) + { + KeysToDisplay.Clear(); + HotkeyToUpdate.Clear(); + clearKeysOnFirstType = false; + } - var hotkeyModel = new HotkeyModel( - specialKeyState.AltPressed, - specialKeyState.ShiftPressed, - specialKeyState.WinPressed, - specialKeyState.CtrlPressed, - key); + if (ChefKeysManager.StartMenuBlocked && key.ToString() == ChefKeysManager.StartMenuSimulatedKey) + return; - CurrentHotkey = hotkeyModel; - SetKeysToDisplay(CurrentHotkey); + if (!isWPFHotkeyControl) + { + if (HotkeyToUpdate.MaxKeysReached()) + return; + + HotkeyToUpdate.AddString(key.ToString()); + } + else + { + HotkeyToUpdate.SetHotkeyFromWPFControl(GlobalHotkey.CheckModifiers(), key); + } + + SetKeysToDisplay(HotkeyToUpdate); } private void SetKeysToDisplay(HotkeyModel? hotkey) @@ -103,21 +154,36 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) _overwriteOtherHotkey = null; KeysToDisplay.Clear(); - if (hotkey == null || hotkey == default(HotkeyModel)) + if (hotkey is null || string.IsNullOrEmpty(hotkey.Value.HotkeyRaw)) { KeysToDisplay.Add(EmptyHotkey); return; } - foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) + IEnumerable enumerateMethod; + + if (!isWPFHotkeyControl) + { + foreach (var key in hotkey.Value.DisplayKeysRaw()!) + { + KeysToDisplay.Add(key); + } + } + else { - KeysToDisplay.Add(key); + foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) + { + KeysToDisplay.Add(key); + } } if (tbMsg == null) return; - if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData) + if (_hotkeySettings.RegisteredHotkeys + .FirstOrDefault(v => v.Hotkey == hotkey + || v.Hotkey.ToChefKeysString() == hotkey.Value.HotkeyRaw) + is { } registeredHotkeyData) { var description = string.Format( InternationalizationManager.Instance.GetTranslation(registeredHotkeyData.DescriptionResourceKey), @@ -136,6 +202,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) OverwriteBtn.Visibility = Visibility.Visible; _overwriteOtherHotkey = registeredHotkeyData.RemoveHotkey; } + else { tbMsg.Text = string.Format( @@ -153,7 +220,11 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) OverwriteBtn.IsEnabled = false; OverwriteBtn.Visibility = Visibility.Collapsed; - if (!CheckHotkeyAvailability(hotkey.Value, true)) + var isHotkeyAvailable = !isWPFHotkeyControl + ? CheckHotkeyAvailability(hotkey.Value.HotkeyRaw) + : CheckWPFHotkeyAvailability(hotkey.Value, true); + + if (!isHotkeyAvailable) { tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable"); Alert.Visibility = Visibility.Visible; @@ -168,8 +239,11 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) } } - private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => - hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey); + private static bool CheckHotkeyAvailability(string hotkey) + => HotKeyMapper.CanRegisterHotkey(hotkey); + + private static bool CheckWPFHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) + => hotkey.ValidateForWpf(validateKeyGesture) && HotKeyMapper.CheckHotkeyAvailability(hotkey.HotkeyRaw); private void Overwrite(object sender, RoutedEventArgs e) { diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 4c465d61f52..53c58ba8bfb 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -14,8 +14,6 @@ Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help - Failed to register hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. - Flow Launcher Could not start {0} Invalid Flow Launcher plugin file format Set as topmost in this query diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml index 6c6fcbb625f..dfb4a7b9486 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml @@ -113,11 +113,12 @@ + WindowTitle="{DynamicResource flowlauncherHotkey}" + IsWPFHotkeyControl="False" /> diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs index 7dfb85a8303..bb5124c16e6 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs @@ -27,7 +27,7 @@ protected override void OnNavigatedTo(NavigationEventArgs e) [RelayCommand] private static void SetTogglingHotkey(HotkeyModel hotkey) { - HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey); + HotKeyMapper.RegisterHotkey(hotkey.HotkeyRaw, hotkey.PreviousHotkey, HotKeyMapper.ToggleHotkey); } } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 6d8af9a3f62..ef9e484baba 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -33,7 +33,7 @@ public SettingsPaneHotkeyViewModel(Settings settings) [RelayCommand] private void SetTogglingHotkey(HotkeyModel hotkey) { - HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey); + HotKeyMapper.RegisterHotkey(hotkey.HotkeyRaw, hotkey.PreviousHotkey, HotKeyMapper.ToggleHotkey); } [RelayCommand] @@ -57,7 +57,7 @@ private void CustomHotkeyDelete() if (result is MessageBoxResult.Yes) { Settings.CustomPluginHotkeys.Remove(item); - HotKeyMapper.RemoveHotkey(item.Hotkey); + HotKeyMapper.UnregisterHotkey(item.Hotkey); } } diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml index 22e3960ef19..61c671be9a1 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml @@ -33,11 +33,12 @@ Sub="{DynamicResource flowlauncherHotkeyToolTip}"> + WindowTitle="{DynamicResource flowlauncherHotkey}" + IsWPFHotkeyControl="False" /> Date: Wed, 1 Jan 2025 15:14:58 +1100 Subject: [PATCH 2/6] bump ChefKeys version --- Flow.Launcher/Flow.Launcher.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index f894a6d43cf..004fafae479 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -83,7 +83,7 @@ - + all From 25bf44d7117da179c706d2a6bcac6c8b141c91d0 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 2 Jan 2025 17:21:34 +1100 Subject: [PATCH 3/6] do not allow add on long key press --- .../Hotkey/HotkeyModel.cs | 2 + .../UserSettings/Settings.cs | 80 +++++++++---------- Flow.Launcher/HotkeyControlDialog.xaml.cs | 14 +++- 3 files changed, 53 insertions(+), 43 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index a6a38c4f15a..82b53d95157 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -92,6 +92,8 @@ internal void AddString(string key) HotkeyRaw = string.IsNullOrEmpty(HotkeyRaw) ? key : HotkeyRaw + "+" + key; } + internal string GetLastKeySet() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+').Last() : string.Empty; + internal bool MaxKeysReached() => DisplayKeysRaw().Count() == 4; internal void Clear() diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 06a11113200..da1c164aeb0 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -302,18 +302,18 @@ public List RegisteredHotkeys list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = "")); if (!string.IsNullOrEmpty(SelectPrevItemHotkey2)) list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = "")); - //if (!string.IsNullOrEmpty(SettingWindowHotkey)) - // list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); - //if (!string.IsNullOrEmpty(OpenContextMenuHotkey)) - // list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); - //if (!string.IsNullOrEmpty(SelectNextPageHotkey)) - // list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); - //if (!string.IsNullOrEmpty(SelectPrevPageHotkey)) - // list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); - //if (!string.IsNullOrEmpty(CycleHistoryUpHotkey)) - // list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); - //if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) - // list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); + if (!string.IsNullOrEmpty(SettingWindowHotkey)) + list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); + if (!string.IsNullOrEmpty(OpenContextMenuHotkey)) + list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); + if (!string.IsNullOrEmpty(SelectNextPageHotkey)) + list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); + if (!string.IsNullOrEmpty(SelectPrevPageHotkey)) + list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); + if (!string.IsNullOrEmpty(CycleHistoryUpHotkey)) + list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); + if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) + list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); // Custom Query Hotkeys foreach (var customPluginHotkey in CustomPluginHotkeys) @@ -334,34 +334,34 @@ private List FixedHotkeys() new("Down", "HotkeyLeftRightDesc"), new("Left", "HotkeyUpDownDesc"), new("Right", "HotkeyUpDownDesc"), - //new("Escape", "HotkeyESCDesc"), - //new("F5", "ReloadPluginHotkey"), - //new("Alt+Home", "HotkeySelectFirstResult"), - //new("Alt+End", "HotkeySelectLastResult"), - //new("Ctrl+R", "HotkeyRequery"), - //new("Ctrl+H", "ToggleHistoryHotkey"), - //new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"), - //new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"), - //new("Ctrl+OemPlus", "QuickHeightHotkey"), - //new("Ctrl+OemMinus", "QuickHeightHotkey"), - //new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"), - //new("Shift+Enter", "OpenContextMenuHotkey"), - //new("Enter", "HotkeyRunDesc"), - //new("Ctrl+Enter", "OpenContainFolderHotkey"), - //new("Alt+Enter", "HotkeyOpenResult"), - //new("Ctrl+F12", "ToggleGameModeHotkey"), - //new("Ctrl+Shift+C", "CopyFilePathHotkey"), - - //new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1), - //new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2), - //new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3), - //new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4), - //new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5), - //new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6), - //new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7), - //new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8), - //new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9), - //new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10) + new("Escape", "HotkeyESCDesc"), + new("F5", "ReloadPluginHotkey"), + new("Alt+Home", "HotkeySelectFirstResult"), + new("Alt+End", "HotkeySelectLastResult"), + new("Ctrl+R", "HotkeyRequery"), + new("Ctrl+H", "ToggleHistoryHotkey"), + new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"), + new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"), + new("Ctrl+OemPlus", "QuickHeightHotkey"), + new("Ctrl+OemMinus", "QuickHeightHotkey"), + new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"), + new("Shift+Enter", "OpenContextMenuHotkey"), + new("Enter", "HotkeyRunDesc"), + new("Ctrl+Enter", "OpenContainFolderHotkey"), + new("Alt+Enter", "HotkeyOpenResult"), + new("Ctrl+F12", "ToggleGameModeHotkey"), + new("Ctrl+Shift+C", "CopyFilePathHotkey"), + + new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1), + new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2), + new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3), + new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4), + new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5), + new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6), + new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7), + new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8), + new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9), + new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10) }; } } diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index 9d7d0cb8730..d5146fcf5bb 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -134,19 +134,27 @@ private void OnPreviewKeyDown(object sender, KeyEventArgs e) if (ChefKeysManager.StartMenuBlocked && key.ToString() == ChefKeysManager.StartMenuSimulatedKey) return; + AddKey(key); + + SetKeysToDisplay(HotkeyToUpdate); + } + + private void AddKey(Key key) + { + if (HotkeyToUpdate.GetLastKeySet() == key.ToString()) + return; + if (!isWPFHotkeyControl) { if (HotkeyToUpdate.MaxKeysReached()) return; - + HotkeyToUpdate.AddString(key.ToString()); } else { HotkeyToUpdate.SetHotkeyFromWPFControl(GlobalHotkey.CheckModifiers(), key); } - - SetKeysToDisplay(HotkeyToUpdate); } private void SetKeysToDisplay(HotkeyModel? hotkey) From be796fd899a59a264ff5d9cbb437f07685cb2779 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 2 Jan 2025 20:08:41 +1100 Subject: [PATCH 4/6] use string for WPFControl & HotkeyModel with left right key accuracy --- .../Hotkey/GlobalHotkey.cs | 38 +++- .../Hotkey/HotkeyModel.cs | 194 +++++++++++------- Flow.Launcher.Plugin/ActionContext.cs | 8 + Flow.Launcher/HotkeyControl.xaml.cs | 4 +- Flow.Launcher/HotkeyControlDialog.xaml.cs | 31 +-- 5 files changed, 172 insertions(+), 103 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs index b2a14075581..1d1eee6ea63 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs @@ -37,25 +37,53 @@ private static UnhookWindowsHookExSafeHandle SetHook(HOOKPROC proc, WINDOWS_HOOK public static SpecialKeyState CheckModifiers() { SpecialKeyState state = new SpecialKeyState(); - if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_SHIFT) & 0x8000) != 0) + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LSHIFT) & 0x8000) != 0) { //SHIFT is pressed + state.LeftShiftPressed = true; state.ShiftPressed = true; } - if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_CONTROL) & 0x8000) != 0) + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_RSHIFT) & 0x8000) != 0) + { + //SHIFT is pressed + state.RightShiftPressed = true; + state.ShiftPressed = true; + } + + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LCONTROL) & 0x8000) != 0) + { + //CONTROL is pressed + state.LeftCtrlPressed = true; + state.CtrlPressed = true; + } + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_RCONTROL) & 0x8000) != 0) { //CONTROL is pressed + state.RightCtrlPressed = true; state.CtrlPressed = true; } - if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_MENU) & 0x8000) != 0) + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LMENU) & 0x8000) != 0) { //ALT is pressed + state.LeftAltPressed = true; state.AltPressed = true; } - if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LWIN) & 0x8000) != 0 || - (PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_RWIN) & 0x8000) != 0) + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_RMENU) & 0x8000) != 0) + { + //ALT is pressed + state.RightAltPressed = true; + state.AltPressed = true; + } + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LWIN) & 0x8000) != 0) + { + //WIN is pressed + state.LWinPressed = true; + state.WinPressed = true; + } + if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_RWIN) & 0x8000) != 0) { //WIN is pressed + state.RWinPressed = true; state.WinPressed = true; } diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index 82b53d95157..b2f6ee719a7 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -9,10 +9,14 @@ namespace Flow.Launcher.Infrastructure.Hotkey { public record struct HotkeyModel { - public bool Alt { get; set; } - public bool Shift { get; set; } - public bool Win { get; set; } - public bool Ctrl { get; set; } + public bool LeftAlt { get; set; } + public bool LeftShift { get; set; } + public bool LWin { get; set; } + public bool LeftCtrl { get; set; } + public bool RightAlt { get; set; } + public bool RightShift { get; set; } + public bool RWin { get; set; } + public bool RightCtrl { get; set; } public string HotkeyRaw { get; set; } = string.Empty; public string PreviousHotkey { get; set; } = string.Empty; @@ -29,22 +33,22 @@ public ModifierKeys ModifierKeys get { ModifierKeys modifierKeys = ModifierKeys.None; - if (Alt) + if (LeftAlt || RightAlt) { modifierKeys |= ModifierKeys.Alt; } - if (Shift) + if (LeftShift || RightShift) { modifierKeys |= ModifierKeys.Shift; } - if (Win) + if (LWin || RWin) { modifierKeys |= ModifierKeys.Windows; } - if (Ctrl) + if (LeftCtrl || RightCtrl) { modifierKeys |= ModifierKeys.Control; } @@ -58,17 +62,21 @@ public void SetHotkeyFromString(string hotkeyString) { Clear(); Parse(hotkeyString); - HotkeyRaw = ToChefKeysString(); + HotkeyRaw = FromWPFKeysToString(); } internal void SetHotkeyFromWPFControl(SpecialKeyState specialKeyState, Key key) { - Alt = specialKeyState.AltPressed; - Shift = specialKeyState.ShiftPressed; - Win = specialKeyState.WinPressed; - Ctrl = specialKeyState.CtrlPressed; + LeftAlt = specialKeyState.LeftAltPressed; + LeftShift = specialKeyState.LeftShiftPressed; + LWin = specialKeyState.LWinPressed; + LeftCtrl = specialKeyState.LeftCtrlPressed; + RightAlt = specialKeyState.RightAltPressed; + RightShift = specialKeyState.RightShiftPressed; + RWin = specialKeyState.RWinPressed; + RightCtrl = specialKeyState.RightCtrlPressed; CharKey = key; - HotkeyRaw = ToChefKeysString(); + HotkeyRaw = FromWPFKeysToString(); PreviousHotkey = string.Empty; } @@ -77,31 +85,25 @@ public HotkeyModel(string hotkey) SetHotkeyFromString(hotkey); } - //public HotkeyModel(bool alt, bool shift, bool win, bool ctrl, Key key) - //{ - // Alt = alt; - // Shift = shift; - // Win = win; - // Ctrl = ctrl; - // CharKey = key; - //} - // Use for ChefKeys only internal void AddString(string key) { HotkeyRaw = string.IsNullOrEmpty(HotkeyRaw) ? key : HotkeyRaw + "+" + key; + Parse(HotkeyRaw); } - internal string GetLastKeySet() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+').Last() : string.Empty; - - internal bool MaxKeysReached() => DisplayKeysRaw().Count() == 4; + internal string GetLastKeySet() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+').Last() : string.Empty; internal void Clear() { - Alt = false; - Shift = false; - Win = false; - Ctrl = false; + LeftAlt = false; + LeftShift = false; + LWin = false; + LeftCtrl = false; + RightAlt = false; + RightShift = false; + RWin = false; + RightCtrl = false; HotkeyRaw = string.Empty; PreviousHotkey = string.Empty; CharKey = Key.None; @@ -114,36 +116,52 @@ private void Parse(string hotkeyString) return; } - List keys = hotkeyString.Replace(" ", "").Split('+').ToList(); - if (keys.Contains("Alt") || keys.Contains("LeftAlt") || keys.Contains("RightAlt")) + List keys = hotkeyString.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList(); + if (keys.Contains("Alt") || keys.Contains("LeftAlt")) { - Alt = true; + LeftAlt = true; keys.Remove("Alt"); keys.Remove("LeftAlt"); + } + if (keys.Contains("RightAlt")) + { + RightAlt = true; keys.Remove("RightAlt"); } - if (keys.Contains("Shift") || keys.Contains("LeftShift") || keys.Contains("RightShift")) + if (keys.Contains("Shift") || keys.Contains("LeftShift")) { - Shift = true; + LeftShift = true; keys.Remove("Shift"); keys.Remove("LeftShift"); + } + if (keys.Contains("RightShift")) + { + RightShift = true; keys.Remove("RightShift"); } - if (keys.Contains("Win") || keys.Contains("LWin") || keys.Contains("RWin")) + if (keys.Contains("Win") || keys.Contains("LWin")) { - Win = true; + LWin = true; keys.Remove("Win"); keys.Remove("LWin"); + } + if (keys.Contains("RWin")) + { + RWin = true; keys.Remove("RWin"); } - if (keys.Contains("Ctrl") || keys.Contains("LeftCtrl")|| keys.Contains("RightCtrl")) + if (keys.Contains("Ctrl") || keys.Contains("LeftCtrl")) { - Ctrl = true; + LeftCtrl = true; keys.Remove("Ctrl"); keys.Remove("LeftCtrl"); + } + if (keys.Contains("RightCtrl")) + { + RightCtrl = true; keys.Remove("RightCtrl"); } @@ -169,62 +187,90 @@ private void Parse(string hotkeyString) } } - public string ToChefKeysString() + public readonly string ToWPFHotkeyString() { - var key = string.Join("+", EnumerateDisplayKeys(true)); - - return key; + var hotkey = string.Empty; + + foreach (var key in HotkeyRaw.Split('+')) + { + if (!string.IsNullOrEmpty(hotkey)) + hotkey += " + "; + + switch (key) + { + case "LeftCtrl" or "RightCtrl": + hotkey += "Ctrl"; + break; + case "LeftAlt" or "RightAlt": + hotkey += "Alt"; + break; + case "LeftShift" or "RightShift": + hotkey += "Shift"; + break; + case "LWin" or "RWin": + hotkey += "Win"; + break; + + default: + hotkey += key; + break; + } + } + + return hotkey; } - public IEnumerable DisplayKeysRaw() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+') : Array.Empty(); + public IEnumerable EnumerateDisplayKeys() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+') : Array.Empty(); + + //For WPF hotkey control + public string FromWPFKeysToString() => string.Join("+", EnumerateWPFKeys()); - public IEnumerable EnumerateDisplayKeys(bool forChefKeys = false) + public IEnumerable EnumerateWPFKeys() { - if (Ctrl && CharKey is not (Key.LeftCtrl or Key.RightCtrl)) + if (LeftCtrl && CharKey is not Key.LeftCtrl) { - yield return GetKeyString("Ctrl", forChefKeys); + yield return "LeftCtrl"; } - if (Alt && CharKey is not (Key.LeftAlt or Key.RightAlt)) + if (LeftAlt && CharKey is not Key.LeftAlt) { - yield return GetKeyString("Alt", forChefKeys); + yield return "LeftAlt"; } - if (Shift && CharKey is not (Key.LeftShift or Key.RightShift)) + if (LeftShift && CharKey is not Key.LeftShift) { - yield return GetKeyString("Shift", forChefKeys); + yield return "LeftShift"; } - if (Win && CharKey is not (Key.LWin or Key.RWin)) + if (LWin && CharKey is not Key.LWin) { - yield return GetKeyString("Win", forChefKeys); + yield return "LWin"; + } + if (RightCtrl && CharKey is not Key.RightCtrl) + { + yield return "RightCtrl"; } - if (CharKey != Key.None) + if (RightAlt && CharKey is not Key.RightAlt) { - yield return specialSymbolDictionary.TryGetValue(CharKey, out var value) - ? value - : GetKeyString(CharKey.ToString(), forChefKeys); + yield return "RightAlt"; } - } - private string GetKeyString(string key, bool convertToChefKeysString) - { - if (!convertToChefKeysString) - return key; + if (RightShift && CharKey is not Key.RightShift) + { + yield return "RightShift"; + } - switch (key) + if (RWin && CharKey is not Key.RWin) { - case "Alt": - return "LeftAlt"; - case "Ctrl": - return "LeftCtrl"; - case "Shift": - return "LeftShift"; - case "Win": - return "LWin"; - default: - return key; + yield return "RWin"; + } + + if (CharKey != Key.None) + { + yield return specialSymbolDictionary.TryGetValue(CharKey, out var value) + ? value + : CharKey.ToString(); } } @@ -235,6 +281,8 @@ private string GetKeyString(string key, bool convertToChefKeysString) /// public bool ValidateForWpf(bool validateKeyGestrue = false) { + Parse(HotkeyRaw); + switch (CharKey) { case Key.LeftAlt: diff --git a/Flow.Launcher.Plugin/ActionContext.cs b/Flow.Launcher.Plugin/ActionContext.cs index e31c8e31d8b..a38907a9d75 100644 --- a/Flow.Launcher.Plugin/ActionContext.cs +++ b/Flow.Launcher.Plugin/ActionContext.cs @@ -38,6 +38,14 @@ public class SpecialKeyState /// True if the Windows key is pressed. /// public bool WinPressed { get; set; } + public bool LeftShiftPressed { get; set; } + public bool RightShiftPressed { get; set; } + public bool LeftCtrlPressed { get; set; } + public bool RightCtrlPressed { get; set; } + public bool LeftAltPressed { get; set; } + public bool RightAltPressed { get; set; } + public bool LWinPressed { get; set; } + public bool RWinPressed { get; set; } /// /// Get this object represented as a flag combination. diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 9f1c8761eb1..cff59795532 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -182,7 +182,7 @@ private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) if (!hotkeyAvailable) return; - Hotkey = keyModel.HotkeyRaw; + Hotkey = keyModel.ToWPFHotkeyString(); SetKeysToDisplay(CurrentHotkey); // If exists then will be unregistered, if doesn't no errors will be thrown. @@ -193,7 +193,7 @@ private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) } else { - Hotkey = keyModel.HotkeyRaw; + Hotkey = keyModel.ToWPFHotkeyString(); ChangeHotkey?.Execute(keyModel); } } diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index d5146fcf5bb..e4a2d8cad76 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -144,17 +144,10 @@ private void AddKey(Key key) if (HotkeyToUpdate.GetLastKeySet() == key.ToString()) return; - if (!isWPFHotkeyControl) - { - if (HotkeyToUpdate.MaxKeysReached()) - return; + if (MaxKeysLimitReached()) + return; - HotkeyToUpdate.AddString(key.ToString()); - } - else - { - HotkeyToUpdate.SetHotkeyFromWPFControl(GlobalHotkey.CheckModifiers(), key); - } + HotkeyToUpdate.AddString(key.ToString()); } private void SetKeysToDisplay(HotkeyModel? hotkey) @@ -170,19 +163,9 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) IEnumerable enumerateMethod; - if (!isWPFHotkeyControl) - { - foreach (var key in hotkey.Value.DisplayKeysRaw()!) - { - KeysToDisplay.Add(key); - } - } - else + foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) { - foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) - { - KeysToDisplay.Add(key); - } + KeysToDisplay.Add(key); } if (tbMsg == null) @@ -190,7 +173,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) if (_hotkeySettings.RegisteredHotkeys .FirstOrDefault(v => v.Hotkey == hotkey - || v.Hotkey.ToChefKeysString() == hotkey.Value.HotkeyRaw) + || v.Hotkey.FromWPFKeysToString() == hotkey.Value.HotkeyRaw) is { } registeredHotkeyData) { var description = string.Format( @@ -253,6 +236,8 @@ private static bool CheckHotkeyAvailability(string hotkey) private static bool CheckWPFHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => hotkey.ValidateForWpf(validateKeyGesture) && HotKeyMapper.CheckHotkeyAvailability(hotkey.HotkeyRaw); + private bool MaxKeysLimitReached() => isWPFHotkeyControl ? KeysToDisplay.Count == 2 : KeysToDisplay.Count == 4; + private void Overwrite(object sender, RoutedEventArgs e) { _overwriteOtherHotkey?.Invoke(); From 33d5a741e7dc95291b5ffcdc205587a3316f50d8 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 2 Jan 2025 20:30:06 +1100 Subject: [PATCH 5/6] use WPF Control hotkey display --- Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs | 5 +++-- Flow.Launcher/HotkeyControlDialog.xaml.cs | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index b2f6ee719a7..a6037a81d4c 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -187,6 +187,7 @@ private void Parse(string hotkeyString) } } + // WPF Control hotkey form i.e. simplified text e.g. LeftAlt+X -> Alt + X, includes space around '+' public readonly string ToWPFHotkeyString() { var hotkey = string.Empty; @@ -220,9 +221,9 @@ public readonly string ToWPFHotkeyString() return hotkey; } - public IEnumerable EnumerateDisplayKeys() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+') : Array.Empty(); + // Display in the form of WPF Control i.e. simplified text e.g. LeftAlt -> Alt + public IEnumerable EnumerateDisplayKeys() => !string.IsNullOrEmpty(HotkeyRaw) ? ToWPFHotkeyString().Split(" + ") : Array.Empty(); - //For WPF hotkey control public string FromWPFKeysToString() => string.Join("+", EnumerateWPFKeys()); public IEnumerable EnumerateWPFKeys() diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index e4a2d8cad76..13734a83107 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -161,8 +161,6 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) return; } - IEnumerable enumerateMethod; - foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) { KeysToDisplay.Add(key); From 03b8cae870525f3402e7cf4580f8d856fdf4448f Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 2 Jan 2025 21:39:23 +1100 Subject: [PATCH 6/6] uniformly use HotkeyRaw --- .../Hotkey/HotkeyModel.cs | 337 +++++++----------- Flow.Launcher/HotkeyControl.xaml.cs | 3 +- Flow.Launcher/HotkeyControlDialog.xaml.cs | 2 +- 3 files changed, 136 insertions(+), 206 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index a6037a81d4c..912c8cca555 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -9,182 +9,29 @@ namespace Flow.Launcher.Infrastructure.Hotkey { public record struct HotkeyModel { - public bool LeftAlt { get; set; } - public bool LeftShift { get; set; } - public bool LWin { get; set; } - public bool LeftCtrl { get; set; } - public bool RightAlt { get; set; } - public bool RightShift { get; set; } - public bool RWin { get; set; } - public bool RightCtrl { get; set; } - public string HotkeyRaw { get; set; } = string.Empty; public string PreviousHotkey { get; set; } = string.Empty; - public Key CharKey { get; set; } = Key.None; - - private static readonly Dictionary specialSymbolDictionary = new Dictionary - { - { Key.Space, "Space" }, { Key.Oem3, "~" } - }; - - public ModifierKeys ModifierKeys - { - get - { - ModifierKeys modifierKeys = ModifierKeys.None; - if (LeftAlt || RightAlt) - { - modifierKeys |= ModifierKeys.Alt; - } - - if (LeftShift || RightShift) - { - modifierKeys |= ModifierKeys.Shift; - } - - if (LWin || RWin) - { - modifierKeys |= ModifierKeys.Windows; - } - - if (LeftCtrl || RightCtrl) - { - modifierKeys |= ModifierKeys.Control; - } - - return modifierKeys; - } - } - - // Used for WPF control only - public void SetHotkeyFromString(string hotkeyString) - { - Clear(); - Parse(hotkeyString); - HotkeyRaw = FromWPFKeysToString(); - } - - internal void SetHotkeyFromWPFControl(SpecialKeyState specialKeyState, Key key) - { - LeftAlt = specialKeyState.LeftAltPressed; - LeftShift = specialKeyState.LeftShiftPressed; - LWin = specialKeyState.LWinPressed; - LeftCtrl = specialKeyState.LeftCtrlPressed; - RightAlt = specialKeyState.RightAltPressed; - RightShift = specialKeyState.RightShiftPressed; - RWin = specialKeyState.RWinPressed; - RightCtrl = specialKeyState.RightCtrlPressed; - CharKey = key; - HotkeyRaw = FromWPFKeysToString(); - PreviousHotkey = string.Empty; - } - + // HotkeyRaw always be without spaces round '+'. WPF Control hotkey string saved to settings will contain spaces. public HotkeyModel(string hotkey) { - SetHotkeyFromString(hotkey); + HotkeyRaw = ToHotkeyRawString(hotkey); } - // Use for ChefKeys only internal void AddString(string key) { HotkeyRaw = string.IsNullOrEmpty(HotkeyRaw) ? key : HotkeyRaw + "+" + key; - Parse(HotkeyRaw); } + // Display in the form of WPF Control i.e. simplified text e.g. LeftAlt -> Alt + public IEnumerable EnumerateDisplayKeys() => !string.IsNullOrEmpty(HotkeyRaw) ? ToWPFHotkeyString().Split(" + ") : Array.Empty(); + internal string GetLastKeySet() => !string.IsNullOrEmpty(HotkeyRaw) ? HotkeyRaw.Split('+').Last() : string.Empty; internal void Clear() { - LeftAlt = false; - LeftShift = false; - LWin = false; - LeftCtrl = false; - RightAlt = false; - RightShift = false; - RWin = false; - RightCtrl = false; HotkeyRaw = string.Empty; PreviousHotkey = string.Empty; - CharKey = Key.None; - } - - private void Parse(string hotkeyString) - { - if (string.IsNullOrEmpty(hotkeyString)) - { - return; - } - - List keys = hotkeyString.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList(); - if (keys.Contains("Alt") || keys.Contains("LeftAlt")) - { - LeftAlt = true; - keys.Remove("Alt"); - keys.Remove("LeftAlt"); - } - if (keys.Contains("RightAlt")) - { - RightAlt = true; - keys.Remove("RightAlt"); - } - - if (keys.Contains("Shift") || keys.Contains("LeftShift")) - { - LeftShift = true; - keys.Remove("Shift"); - keys.Remove("LeftShift"); - } - if (keys.Contains("RightShift")) - { - RightShift = true; - keys.Remove("RightShift"); - } - - if (keys.Contains("Win") || keys.Contains("LWin")) - { - LWin = true; - keys.Remove("Win"); - keys.Remove("LWin"); - } - if (keys.Contains("RWin")) - { - RWin = true; - keys.Remove("RWin"); - } - - if (keys.Contains("Ctrl") || keys.Contains("LeftCtrl")) - { - LeftCtrl = true; - keys.Remove("Ctrl"); - keys.Remove("LeftCtrl"); - } - if (keys.Contains("RightCtrl")) - { - RightCtrl = true; - keys.Remove("RightCtrl"); - } - - if (keys.Count == 1) - { - string charKey = keys[0]; - KeyValuePair? specialSymbolPair = - specialSymbolDictionary.FirstOrDefault(pair => pair.Value == charKey); - if (specialSymbolPair.Value.Value != null) - { - CharKey = specialSymbolPair.Value.Key; - } - else - { - try - { - CharKey = (Key)Enum.Parse(typeof(Key), charKey); - } - catch (ArgumentException) - { - } - } - } } // WPF Control hotkey form i.e. simplified text e.g. LeftAlt+X -> Alt + X, includes space around '+' @@ -221,59 +68,46 @@ public readonly string ToWPFHotkeyString() return hotkey; } - // Display in the form of WPF Control i.e. simplified text e.g. LeftAlt -> Alt - public IEnumerable EnumerateDisplayKeys() => !string.IsNullOrEmpty(HotkeyRaw) ? ToWPFHotkeyString().Split(" + ") : Array.Empty(); - - public string FromWPFKeysToString() => string.Join("+", EnumerateWPFKeys()); - - public IEnumerable EnumerateWPFKeys() + // Converts any WPF Control hotkey e.g. Alt + X -> LeftAlt+X + public readonly string ToHotkeyRawString(string wpfHotkey) { - if (LeftCtrl && CharKey is not Key.LeftCtrl) - { - yield return "LeftCtrl"; - } - - if (LeftAlt && CharKey is not Key.LeftAlt) - { - yield return "LeftAlt"; - } + var hotkey = string.Empty; - if (LeftShift && CharKey is not Key.LeftShift) + foreach (var key in wpfHotkey.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) { - yield return "LeftShift"; - } + if (!string.IsNullOrEmpty(hotkey)) + hotkey += "+"; - if (LWin && CharKey is not Key.LWin) - { - yield return "LWin"; - } - if (RightCtrl && CharKey is not Key.RightCtrl) - { - yield return "RightCtrl"; - } + switch (key) + { + case "Ctrl": + hotkey += "LeftCtrl"; + break; + case "Alt": + hotkey += "LeftAlt"; + break; + case "Shift": + hotkey += "LeftShift"; + break; + case "Win": + hotkey += "LWin"; + break; - if (RightAlt && CharKey is not Key.RightAlt) - { - yield return "RightAlt"; + default: + hotkey += key; + break; + } } - if (RightShift && CharKey is not Key.RightShift) - { - yield return "RightShift"; - } + return hotkey; + } - if (RWin && CharKey is not Key.RWin) - { - yield return "RWin"; - } + public bool Alt { get; set; } + public bool Shift { get; set; } + public bool Win { get; set; } + public bool Ctrl { get; set; } - if (CharKey != Key.None) - { - yield return specialSymbolDictionary.TryGetValue(CharKey, out var value) - ? value - : CharKey.ToString(); - } - } + public Key CharKey { get; set; } = Key.None; /// /// Validate hotkey for WPF control only @@ -321,6 +155,103 @@ public bool ValidateForWpf(bool validateKeyGestrue = false) } } + private void Parse(string hotkeyString) + { + if (string.IsNullOrEmpty(hotkeyString)) + { + return; + } + + List keys = hotkeyString.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToList(); + + if (keys.Contains("Alt") || keys.Contains("LeftAlt") || keys.Contains("RightAlt")) + { + Alt = true; + keys.Remove("Alt"); + keys.Remove("LeftAlt"); + keys.Remove("RightAlt"); + } + + if (keys.Contains("Shift") || keys.Contains("LeftShift") || keys.Contains("RightShift")) + { + Shift = true; + keys.Remove("Shift"); + keys.Remove("LeftShift"); + keys.Remove("RightShift"); + } + + if (keys.Contains("Win") || keys.Contains("LWin") || keys.Contains("RWin")) + { + Win = true; + keys.Remove("Win"); + keys.Remove("LWin"); + keys.Remove("RWin"); + } + + if (keys.Contains("Ctrl") || keys.Contains("LeftCtrl") || keys.Contains("RightCtrl")) + { + Ctrl = true; + keys.Remove("Ctrl"); + keys.Remove("LeftCtrl"); + keys.Remove("RightCtrl"); + } + + if (keys.Count == 1) + { + string charKey = keys[0]; + KeyValuePair? specialSymbolPair = + specialSymbolDictionary.FirstOrDefault(pair => pair.Value == charKey); + if (specialSymbolPair.Value.Value != null) + { + CharKey = specialSymbolPair.Value.Key; + } + else + { + try + { + CharKey = (Key)Enum.Parse(typeof(Key), charKey); + } + catch (ArgumentException) + { + } + } + } + } + + private static readonly Dictionary specialSymbolDictionary = new Dictionary + { + { Key.Space, "Space" }, { Key.Oem3, "~" } + }; + + public ModifierKeys ModifierKeys + { + get + { + ModifierKeys modifierKeys = ModifierKeys.None; + if (Alt) + { + modifierKeys |= ModifierKeys.Alt; + } + + if (Shift) + { + modifierKeys |= ModifierKeys.Shift; + } + + if (Win) + { + modifierKeys |= ModifierKeys.Windows; + } + + if (Ctrl) + { + modifierKeys |= ModifierKeys.Control; + } + + return modifierKeys; + } + } + private static bool IsPrintableCharacter(Key key) { // https://stackoverflow.com/questions/11881199/identify-if-a-event-key-is-text-not-only-alphanumeric @@ -349,7 +280,7 @@ private static bool IsPrintableCharacter(Key key) public override int GetHashCode() { - return HashCode.Combine(ModifierKeys, CharKey); + return HotkeyRaw.GetHashCode(); } } } diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index cff59795532..1fdcc6afb0c 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -169,8 +169,7 @@ private async Task OpenHotkeyDialog() private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) { - // WPF hotkey control uses CharKey - if (string.IsNullOrEmpty(keyModel.HotkeyRaw) || string.IsNullOrEmpty(keyModel.CharKey.ToString())) + if (string.IsNullOrEmpty(keyModel.HotkeyRaw)) return; if (triggerValidate) diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index 13734a83107..d149902fad2 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -171,7 +171,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) if (_hotkeySettings.RegisteredHotkeys .FirstOrDefault(v => v.Hotkey == hotkey - || v.Hotkey.FromWPFKeysToString() == hotkey.Value.HotkeyRaw) + || v.Hotkey.HotkeyRaw == hotkey.Value.HotkeyRaw) is { } registeredHotkeyData) { var description = string.Format(