mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Refactor hotkey handling and improve thread safety
- Added `PluginHotkeyInitialized` event in `PluginManager` to notify when plugin hotkeys are initialized. - Replaced `List` with `ConcurrentBag` for `_windowPluginHotkeys` to ensure thread-safe operations. - Enhanced `GetWindowPluginHotkeys` to support filtering by plugin ID. - Updated `HotKeyMapper` to handle `PluginHotkeyInitialized` and register hotkeys dynamically. - Refactored `WindowPluginHotkeyPair` and `GetRegisteredHotkeyData` to use `ConcurrentBag`. - Improved debug logging for hotkey initialization. - Refactored initialization of ActionContext hotkeys using modern C# collection syntax. - General code cleanup for readability and maintainability.
This commit is contained in:
parent
dccdd95045
commit
811239b158
2 changed files with 59 additions and 28 deletions
|
|
@ -34,6 +34,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
private static readonly ConcurrentDictionary<string, PluginPair> _nonGlobalPlugins = [];
|
||||
|
||||
public static event Action<PluginHotkeyChangedEvent> PluginHotkeyChanged;
|
||||
public static event Action<PluginPair> PluginHotkeyInitialized;
|
||||
|
||||
private static PluginsSettings Settings;
|
||||
private static readonly ConcurrentBag<string> ModifiedPlugins = [];
|
||||
|
|
@ -46,7 +47,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
private static readonly Lock _pluginHotkeyInfoUpdateLock = new();
|
||||
private static readonly ConcurrentDictionary<PluginPair, List<BasePluginHotkey>> _pluginHotkeyInfo = [];
|
||||
private static readonly ConcurrentDictionary<HotkeyModel, List<(PluginMetadata, SearchWindowPluginHotkey)>> _windowPluginHotkeys = [];
|
||||
private static readonly ConcurrentDictionary<HotkeyModel, ConcurrentBag<(PluginMetadata, SearchWindowPluginHotkey)>> _windowPluginHotkeys = [];
|
||||
|
||||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
|
|
@ -383,6 +384,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
InitializeWindowPluginHotkey(pair);
|
||||
_hotkeyPlugins.Add(pair);
|
||||
PluginHotkeyInitialized?.Invoke(pair);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -872,10 +874,30 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
public static Dictionary<HotkeyModel, List<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>> GetWindowPluginHotkeys()
|
||||
public static Dictionary<HotkeyModel, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>> GetWindowPluginHotkeys(string id = null)
|
||||
{
|
||||
// Here we do not need to check PluginModified since we will check it in hotkey events
|
||||
return _windowPluginHotkeys.ToDictionary(p => p.Key, p => p.Value);
|
||||
if (id == null)
|
||||
{
|
||||
// Return all window plugin hotkeys
|
||||
return _windowPluginHotkeys.ToDictionary(p => p.Key, p => p.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return window plugin hotkeys for specified plugin id
|
||||
// If one HotkeyModel is already included by other plugins, it will be removed so that HotkeyMapper will not register this Window hotkey duplicately
|
||||
var windowPluginHotkeys = new Dictionary<HotkeyModel, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>>();
|
||||
foreach (var key in _windowPluginHotkeys)
|
||||
{
|
||||
// Check if all items in the list are from the specified plugin
|
||||
if (key.Value.All(x => x.Item1.ID == id))
|
||||
{
|
||||
// We must use the reference of this ConcurrentBag so that it can be updated in the next
|
||||
windowPluginHotkeys[key.Key] = key.Value;
|
||||
}
|
||||
}
|
||||
return windowPluginHotkeys;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdatePluginHotkeyInfoTranslations(PluginPair pair)
|
||||
|
|
@ -932,8 +954,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
// Update window plugin hotkey dictionary
|
||||
var oldHotkeyModels = _windowPluginHotkeys[oldHotkey];
|
||||
_windowPluginHotkeys[oldHotkey] = oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id).ToList();
|
||||
if (_windowPluginHotkeys[oldHotkey].Count == 0)
|
||||
_windowPluginHotkeys[oldHotkey] = [.. oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id)];
|
||||
if (_windowPluginHotkeys[oldHotkey].IsEmpty)
|
||||
{
|
||||
_windowPluginHotkeys.TryRemove(oldHotkey, out var _);
|
||||
}
|
||||
|
|
@ -942,7 +964,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
var newList = newHotkeyModels.ToList();
|
||||
newList.Add((plugin, pluginHotkey));
|
||||
_windowPluginHotkeys[newHotkey] = newList;
|
||||
_windowPluginHotkeys[newHotkey] = [.. newList];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
|
@ -41,6 +42,7 @@ internal static class HotKeyMapper
|
|||
_settings.PropertyChanged += Settings_PropertyChanged;
|
||||
_settings.CustomPluginHotkeys.CollectionChanged += CustomPluginHotkeys_CollectionChanged;
|
||||
PluginManager.PluginHotkeyChanged += PluginManager_PluginHotkeyChanged;
|
||||
PluginManager.PluginHotkeyInitialized += PluginManager_PluginHotkeyInitialized;
|
||||
}
|
||||
|
||||
private static void InitializeRegisteredHotkeys()
|
||||
|
|
@ -109,9 +111,27 @@ internal static class HotKeyMapper
|
|||
list.Add(GetRegisteredHotkeyData(customPluginHotkey));
|
||||
}
|
||||
|
||||
// Plugin hotkeys
|
||||
// Add registered hotkeys & Set them
|
||||
foreach (var hotkey in list)
|
||||
{
|
||||
_settings.RegisteredHotkeys.Add(hotkey);
|
||||
if (hotkey.RegisteredType == RegisteredHotkeyType.DialogJump && !_settings.EnableDialogJump)
|
||||
{
|
||||
// If dialog jump is disabled, do not register the hotkey
|
||||
continue;
|
||||
}
|
||||
SetHotkey(hotkey);
|
||||
}
|
||||
|
||||
App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]");
|
||||
}
|
||||
|
||||
private static void PluginManager_PluginHotkeyInitialized(PluginPair pair)
|
||||
{
|
||||
var list = new List<RegisteredHotkeyData>();
|
||||
|
||||
// Global plugin hotkeys
|
||||
var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo();
|
||||
var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo(pair.Metadata.ID);
|
||||
foreach (var info in pluginHotkeyInfos)
|
||||
{
|
||||
var pluginPair = info.Key;
|
||||
|
|
@ -128,7 +148,7 @@ internal static class HotKeyMapper
|
|||
}
|
||||
|
||||
// Window plugin hotkeys
|
||||
var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys();
|
||||
var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(pair.Metadata.ID);
|
||||
foreach (var hotkey in windowPluginHotkeys)
|
||||
{
|
||||
var hotkeyModel = hotkey.Key;
|
||||
|
|
@ -140,15 +160,10 @@ internal static class HotKeyMapper
|
|||
foreach (var hotkey in list)
|
||||
{
|
||||
_settings.RegisteredHotkeys.Add(hotkey);
|
||||
if (hotkey.RegisteredType == RegisteredHotkeyType.DialogJump && !_settings.EnableDialogJump)
|
||||
{
|
||||
// If dialog jump is disabled, do not register the hotkey
|
||||
continue;
|
||||
}
|
||||
SetHotkey(hotkey);
|
||||
}
|
||||
|
||||
App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]");
|
||||
App.API.LogDebug(ClassName, $"Initialize {list.Count} hotkeys for {pair.Metadata.Name}:\n[\n\t{string.Join(",\n\t", list)}\n]");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -359,7 +374,7 @@ internal static class HotKeyMapper
|
|||
return new(RegisteredHotkeyType.PluginGlobalHotkey, HotkeyType.Global, hotkey, "pluginHotkey", GlobalPluginHotkeyCommand, new GlobalPluginHotkeyPair(metadata, pluginHotkey), removeHotkeyAction);
|
||||
}
|
||||
|
||||
private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> windowHotkeys)
|
||||
private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> windowHotkeys)
|
||||
{
|
||||
Action removeHotkeysAction = windowHotkeys.All(h => h.PluginHotkey.Editable) ?
|
||||
() =>
|
||||
|
|
@ -747,12 +762,12 @@ internal static class HotKeyMapper
|
|||
private static void InitializeActionContextHotkeys()
|
||||
{
|
||||
// Fixed hotkeys for ActionContext
|
||||
_actionContextRegisteredHotkeys = new List<RegisteredHotkeyData>
|
||||
{
|
||||
_actionContextRegisteredHotkeys =
|
||||
[
|
||||
new(RegisteredHotkeyType.CtrlShiftEnter, HotkeyType.SearchWindow, "Ctrl+Shift+Enter", nameof(Localize.HotkeyCtrlShiftEnterDesc), _mainViewModel.OpenResultCommand),
|
||||
new(RegisteredHotkeyType.CtrlEnter, HotkeyType.SearchWindow, "Ctrl+Enter", nameof(Localize.OpenContainFolderHotkey), _mainViewModel.OpenResultCommand),
|
||||
new(RegisteredHotkeyType.AltEnter, HotkeyType.SearchWindow, "Alt+Enter", nameof(Localize.HotkeyOpenResult), _mainViewModel.OpenResultCommand),
|
||||
};
|
||||
];
|
||||
|
||||
// Register ActionContext hotkeys and they will be cached and restored in _actionContextHotkeyEvents
|
||||
foreach (var hotkey in _actionContextRegisteredHotkeys)
|
||||
|
|
@ -822,18 +837,12 @@ internal static class HotKeyMapper
|
|||
}
|
||||
}
|
||||
|
||||
private class WindowPluginHotkeyPair
|
||||
private class WindowPluginHotkeyPair(HotkeyModel hotkey, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys)
|
||||
{
|
||||
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
|
||||
public HotkeyModel Hotkey { get; }
|
||||
public HotkeyModel Hotkey { get; } = hotkey;
|
||||
|
||||
public List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> HotkeyModels { get; }
|
||||
|
||||
public WindowPluginHotkeyPair(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys)
|
||||
{
|
||||
Hotkey = hotkey;
|
||||
HotkeyModels = hotkeys;
|
||||
}
|
||||
public ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> HotkeyModels { get; } = hotkeys;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
Loading…
Reference in a new issue