diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index 25bc75a56..a6a38c4f1 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Input; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.Hotkey { @@ -13,6 +14,9 @@ namespace Flow.Launcher.Infrastructure.Hotkey 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 @@ namespace Flow.Launcher.Infrastructure.Hotkey } } - 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 @@ namespace Flow.Launcher.Infrastructure.Hotkey } 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 @@ namespace Flow.Launcher.Infrastructure.Hotkey } } - 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 317485176..0deb4dc23 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 0bcc9368d..1d00c391d 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -15,7 +15,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { 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 @@ namespace Flow.Launcher.Infrastructure.UserSettings // 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 @@ namespace Flow.Launcher.Infrastructure.UserSettings 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("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($"{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 068afda15..7815ce9ab 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 8b30b8be1..17aed0667 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); + ChefKeysManager.RegisterHotkey(hotkey, previousHotkey, action); } - internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) + internal static void UnregisterHotkey(string hotkey) { - 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); - } - } - - internal static void RemoveHotkey(string hotkeyStr) - { - 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 class HotKeyMapper 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 class HotKeyMapper }); } - internal static bool CheckAvailability(HotkeyModel currentHotkey) + internal static bool CanRegisterHotkey(string hotkey) { - try - { - HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { }); - - return true; - } - catch - { - } - finally - { - HotkeyManager.Current.Remove("HotkeyAvailabilityTest"); - } - - return false; + return ChefKeysManager.CanRegisterHotkey(hotkey); } + + internal static bool CheckHotkeyAvailability(string hotkey) => ChefKeysManager.IsAvailable(hotkey); + + internal static bool CheckHotkeyValid(string hotkey) => ChefKeysManager.IsValidHotkey(hotkey); + } diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index a42bde7c9..9f1c8761e 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 @@ namespace Flow.Launcher 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 @@ namespace Flow.Launcher 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 @@ namespace Flow.Launcher 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 @@ namespace Flow.Launcher 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 @@ namespace Flow.Launcher } } - 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 @@ namespace Flow.Launcher 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 @@ namespace Flow.Launcher 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 a7b99f670..9d7d0cb87 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -6,8 +6,9 @@ using System.Windows.Input; 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 partial class HotkeyControlDialog : ContentDialog 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 @@ public partial class HotkeyControlDialog : ContentDialog 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 @@ public partial class HotkeyControlDialog : ContentDialog //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 @@ public partial class HotkeyControlDialog : ContentDialog _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) { - KeysToDisplay.Add(key); + foreach (var key in hotkey.Value.DisplayKeysRaw()!) + { + KeysToDisplay.Add(key); + } + } + else + { + 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 @@ public partial class HotkeyControlDialog : ContentDialog OverwriteBtn.Visibility = Visibility.Visible; _overwriteOtherHotkey = registeredHotkeyData.RemoveHotkey; } + else { tbMsg.Text = string.Format( @@ -153,7 +220,11 @@ public partial class HotkeyControlDialog : ContentDialog 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 @@ public partial class HotkeyControlDialog : ContentDialog } } - 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 4c465d61f..53c58ba8b 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 6c6fcbb62..dfb4a7b94 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 7dfb85a83..bb5124c16 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs @@ -27,7 +27,7 @@ namespace Flow.Launcher.Resources.Pages [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 6d8af9a3f..ef9e484ba 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -33,7 +33,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel [RelayCommand] private void SetTogglingHotkey(HotkeyModel hotkey) { - HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey); + HotKeyMapper.RegisterHotkey(hotkey.HotkeyRaw, hotkey.PreviousHotkey, HotKeyMapper.ToggleHotkey); } [RelayCommand] @@ -57,7 +57,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel 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 22e3960ef..61c671be9 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" />