From 724b8a72cbc9a9644f8f79a58956ea3b426c71a7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 09:28:25 +0800 Subject: [PATCH] Support change window hotkey --- Flow.Launcher.Core/Plugin/PluginManager.cs | 67 ++++++++++++++++--- Flow.Launcher/Helper/HotKeyMapper.cs | 44 +++++++----- .../Views/SettingsPaneHotkey.xaml.cs | 28 +++++++- 3 files changed, 110 insertions(+), 29 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 6620ba578..bb1f9b5b3 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -10,6 +10,7 @@ using System.Windows.Input; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.ExternalPlugins; using Flow.Launcher.Infrastructure; +using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; @@ -40,7 +41,7 @@ namespace Flow.Launcher.Core.Plugin private static PluginsSettings Settings; private static List _metadatas; private static readonly List _modifiedPlugins = new(); - private static readonly Dictionary> _windowPluginHotkeys = new(); + private static readonly Dictionary> _windowPluginHotkeys = new(); /// /// Directories that will hold Flow Launcher plugin directory @@ -461,7 +462,7 @@ namespace Flow.Launcher.Core.Plugin return hotkeyPluginInfos; } - public static Dictionary> GetWindowPluginHotkeys() + public static Dictionary> GetWindowPluginHotkeys() { return _windowPluginHotkeys; } @@ -478,22 +479,66 @@ namespace Flow.Launcher.Core.Plugin if (hotkey.HotkeyType == HotkeyType.SearchWindow && hotkey is SearchWindowPluginHotkey searchWindowHotkey) { var hotkeySetting = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; - var converter = new KeyGestureConverter(); - var keyGesture = (KeyGesture)converter.ConvertFromString(hotkeySetting); - if (keyGesture != null) + var hotkeyModel = new HotkeyModel(hotkeySetting); + if (!_windowPluginHotkeys.TryGetValue(hotkeyModel, out var list)) { - if (!_windowPluginHotkeys.TryGetValue(keyGesture, out var list)) - { - list = new List<(PluginMetadata, SearchWindowPluginHotkey)>(); - _windowPluginHotkeys[keyGesture] = list; - } - list.Add((pluginPair.Metadata, searchWindowHotkey)); + list = new List<(PluginMetadata, SearchWindowPluginHotkey)>(); + _windowPluginHotkeys[hotkeyModel] = list; } + list.Add((pluginPair.Metadata, searchWindowHotkey)); } } } } + public static string ChangePluginHotkey(PluginMetadata plugin, GlobalPluginHotkey pluginHotkey, HotkeyModel newHotkey) + { + var oldHotkeyItem = plugin.PluginHotkeys.First(h => h.Id == pluginHotkey.Id); + var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id); + var oldHotkey = settingHotkeyItem.Hotkey; + var newHotkeyStr = newHotkey.ToString(); + + // Update hotkey in plugin metadata & setting + oldHotkeyItem.Hotkey = newHotkeyStr; + settingHotkeyItem.Hotkey = newHotkeyStr; + + return oldHotkey; + } + + public static (HotkeyModel Old, HotkeyModel New) ChangePluginHotkey(PluginMetadata plugin, SearchWindowPluginHotkey pluginHotkey, HotkeyModel newHotkey) + { + var oldHotkeyItem = plugin.PluginHotkeys.First(h => h.Id == pluginHotkey.Id); + var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id); + var oldHotkey = settingHotkeyItem.Hotkey; + var converter = new KeyGestureConverter(); + var oldHotkeyModel = new HotkeyModel(oldHotkey); + var newHotkeyStr = newHotkey.ToString(); + + // Update hotkey in plugin metadata & setting + oldHotkeyItem.Hotkey = newHotkeyStr; + settingHotkeyItem.Hotkey = newHotkeyStr; + + // Update window plugin hotkey dictionary + var oldHotkeyModels = _windowPluginHotkeys[oldHotkeyModel]; + _windowPluginHotkeys[oldHotkeyModel] = oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id).ToList(); + + if (_windowPluginHotkeys.TryGetValue(newHotkey, out var newHotkeyModels)) + { + var newList = newHotkeyModels.ToList(); + newList.Add((plugin, pluginHotkey)); + _windowPluginHotkeys[newHotkey] = newList; + } + else + { + _windowPluginHotkeys[newHotkey] = new List<(PluginMetadata, SearchWindowPluginHotkey)>() + { + (plugin, pluginHotkey) + }; + } + + return (oldHotkeyModel, newHotkey); + } + public static bool ActionKeywordRegistered(string actionKeyword) { // this method is only checking for action keywords (defined as not '*') registration diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 064ebf3bc..fd0b17f0c 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -163,20 +163,27 @@ internal static class HotKeyMapper { if (hotkey.HotkeyType == HotkeyType.Global && hotkey is GlobalPluginHotkey globalHotkey) { - var hotkeySetting = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; - SetGlobalPluginHotkey(globalHotkey, metadata, hotkeySetting); + var hotkeyStr = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; + SetGlobalPluginHotkey(globalHotkey, metadata, hotkeyStr); } } } } - internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, string hotkeySetting) + internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, string hotkeyStr) { - SetHotkey(hotkeySetting, (s, e) => + var hotkey = new HotkeyModel(hotkeyStr); + SetGlobalPluginHotkey(globalHotkey, metadata, hotkey); + } + + internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, HotkeyModel hotkey) + { + var hotkeyStr = hotkey.ToString(); + SetHotkey(hotkeyStr, (s, e) => { if (_mainViewModel.ShouldIgnoreHotkeys() || metadata.Disabled) return; - + globalHotkey.Action?.Invoke(); }); } @@ -186,12 +193,11 @@ internal static class HotKeyMapper var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(); foreach (var hotkey in windowPluginHotkeys) { - var keyGesture = hotkey.Key; - SetWindowHotkey(keyGesture, hotkey.Value); + SetWindowHotkey(hotkey.Key, hotkey.Value); } } - internal static void SetWindowHotkey(KeyGesture keyGesture, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) + internal static void SetWindowHotkey(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) { try { @@ -201,13 +207,17 @@ internal static class HotKeyMapper var command = BuildCommand(hotkeyModels); // Remove any existing key binding with the same gesture to avoid duplication + var keyGesture = hotkey.ToKeyGesture(); var existingBinding = window.InputBindings .OfType() - .FirstOrDefault(kb => kb.Gesture == keyGesture); + .FirstOrDefault(kb => + kb.Gesture is KeyGesture keyGesture1 && + keyGesture.Key == keyGesture1.Key && + keyGesture.Modifiers == keyGesture1.Modifiers); if (existingBinding != null) { - throw new InvalidOperationException($"Key binding with gesture {keyGesture} already exists"); + throw new InvalidOperationException($"Key binding with gesture {hotkey} already exists"); } // Create and add the new key binding @@ -221,8 +231,8 @@ internal static class HotKeyMapper string.Format("Error registering window hotkey {2}: {0} \nStackTrace:{1}", e.Message, e.StackTrace, - keyGesture.DisplayString)); - string errorMsg = string.Format(App.API.GetTranslation("registerWindowHotkeyFailed"), keyGesture.DisplayString); + hotkey)); + string errorMsg = string.Format(App.API.GetTranslation("registerWindowHotkeyFailed"), hotkey); string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); App.API.ShowMsgBox(errorMsg, errorMsgTitle); } @@ -245,16 +255,20 @@ internal static class HotKeyMapper }); } - internal static void RemoveWindowHotkey(KeyGesture keyGesture) + internal static void RemoveWindowHotkey(HotkeyModel hotkey) { try { if (Application.Current?.MainWindow is MainWindow window) { // Find and remove the key binding with the specified gesture + var keyGesture = hotkey.ToKeyGesture(); var existingBinding = window.InputBindings .OfType() - .FirstOrDefault(kb => kb.Gesture == keyGesture); + .FirstOrDefault(kb => + kb.Gesture is KeyGesture keyGesture1 && + keyGesture.Key == keyGesture1.Key && + keyGesture.Modifiers == keyGesture1.Modifiers); if (existingBinding != null) { window.InputBindings.Remove(existingBinding); @@ -267,7 +281,7 @@ internal static class HotKeyMapper string.Format("Error removing window hotkey: {0} \nStackTrace:{1}", e.Message, e.StackTrace)); - string errorMsg = string.Format(App.API.GetTranslation("unregisterWindowHotkeyFailed"), keyGesture.DisplayString); + string errorMsg = string.Format(App.API.GetTranslation("unregisterWindowHotkeyFailed"), hotkey); string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); App.API.ShowMsgBox(errorMsg, errorMsgTitle); } diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index ddc5492ad..3fa322840 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -3,7 +3,11 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Helper; +using Flow.Launcher.Infrastructure.Hotkey; +using Flow.Launcher.Plugin; using Flow.Launcher.Resources.Controls; using Flow.Launcher.SettingPages.ViewModels; using Flow.Launcher.ViewModel; @@ -65,16 +69,15 @@ public partial class SettingsPaneHotkey var hotkeySetting = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; if (hotkey.Editable) { - // TODO: Check if this can use var hotkeyControl = new HotkeyControl { Type = HotkeyControl.HotkeyType.CustomQueryHotkey, DefaultHotkey = hotkey.DefaultHotkey, - Hotkey = hotkeySetting, ValidateKeyGesture = true }; + hotkeyControl.SetHotkey(hotkeySetting, true); + hotkeyControl.ChangeHotkey = new RelayCommand((m) => ChangePluginHotkey(metadata, hotkey, m)); card.Content = hotkeyControl; - // TODO: Update metadata & plugin setting hotkey } else { @@ -90,4 +93,23 @@ public partial class SettingsPaneHotkey PluginHotkeySettings.Children.Add(excard); } } + + private static void ChangePluginHotkey(PluginMetadata metadata, BasePluginHotkey pluginHotkey, HotkeyModel newHotkey) + { + if (pluginHotkey is GlobalPluginHotkey globalPluginHotkey) + { + var oldHotkey = PluginManager.ChangePluginHotkey(metadata, globalPluginHotkey, newHotkey); + HotKeyMapper.RemoveHotkey(oldHotkey); + HotKeyMapper.SetGlobalPluginHotkey(globalPluginHotkey, metadata, newHotkey); + } + else if (pluginHotkey is SearchWindowPluginHotkey windowPluginHotkey) + { + var (oldHotkeyModel, newHotkeyModel) = PluginManager.ChangePluginHotkey(metadata, windowPluginHotkey, newHotkey); + var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(); + HotKeyMapper.RemoveWindowHotkey(oldHotkeyModel); + HotKeyMapper.RemoveWindowHotkey(newHotkeyModel); + HotKeyMapper.SetWindowHotkey(oldHotkeyModel, windowPluginHotkeys[oldHotkeyModel]); + HotKeyMapper.SetWindowHotkey(newHotkeyModel, windowPluginHotkeys[newHotkeyModel]); + } + } }