Skip to content

Commit 811239b

Browse files
committed
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.
1 parent dccdd95 commit 811239b

File tree

2 files changed

+59
-28
lines changed

2 files changed

+59
-28
lines changed

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public static class PluginManager
3434
private static readonly ConcurrentDictionary<string, PluginPair> _nonGlobalPlugins = [];
3535

3636
public static event Action<PluginHotkeyChangedEvent> PluginHotkeyChanged;
37+
public static event Action<PluginPair> PluginHotkeyInitialized;
3738

3839
private static PluginsSettings Settings;
3940
private static readonly ConcurrentBag<string> ModifiedPlugins = [];
@@ -46,7 +47,7 @@ public static class PluginManager
4647

4748
private static readonly Lock _pluginHotkeyInfoUpdateLock = new();
4849
private static readonly ConcurrentDictionary<PluginPair, List<BasePluginHotkey>> _pluginHotkeyInfo = [];
49-
private static readonly ConcurrentDictionary<HotkeyModel, List<(PluginMetadata, SearchWindowPluginHotkey)>> _windowPluginHotkeys = [];
50+
private static readonly ConcurrentDictionary<HotkeyModel, ConcurrentBag<(PluginMetadata, SearchWindowPluginHotkey)>> _windowPluginHotkeys = [];
5051

5152
/// <summary>
5253
/// Directories that will hold Flow Launcher plugin directory
@@ -383,6 +384,7 @@ private static void CheckPluginHotkeys(PluginPair pair)
383384
}
384385
InitializeWindowPluginHotkey(pair);
385386
_hotkeyPlugins.Add(pair);
387+
PluginHotkeyInitialized?.Invoke(pair);
386388
}
387389
}
388390

@@ -872,10 +874,30 @@ public static Dictionary<PluginPair, List<BasePluginHotkey>> GetPluginHotkeyInfo
872874
}
873875
}
874876

875-
public static Dictionary<HotkeyModel, List<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>> GetWindowPluginHotkeys()
877+
public static Dictionary<HotkeyModel, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>> GetWindowPluginHotkeys(string id = null)
876878
{
877879
// Here we do not need to check PluginModified since we will check it in hotkey events
878-
return _windowPluginHotkeys.ToDictionary(p => p.Key, p => p.Value);
880+
if (id == null)
881+
{
882+
// Return all window plugin hotkeys
883+
return _windowPluginHotkeys.ToDictionary(p => p.Key, p => p.Value);
884+
}
885+
else
886+
{
887+
// Return window plugin hotkeys for specified plugin id
888+
// If one HotkeyModel is already included by other plugins, it will be removed so that HotkeyMapper will not register this Window hotkey duplicately
889+
var windowPluginHotkeys = new Dictionary<HotkeyModel, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>>();
890+
foreach (var key in _windowPluginHotkeys)
891+
{
892+
// Check if all items in the list are from the specified plugin
893+
if (key.Value.All(x => x.Item1.ID == id))
894+
{
895+
// We must use the reference of this ConcurrentBag so that it can be updated in the next
896+
windowPluginHotkeys[key.Key] = key.Value;
897+
}
898+
}
899+
return windowPluginHotkeys;
900+
}
879901
}
880902

881903
public static void UpdatePluginHotkeyInfoTranslations(PluginPair pair)
@@ -932,8 +954,8 @@ public static void ChangePluginHotkey(PluginMetadata plugin, SearchWindowPluginH
932954

933955
// Update window plugin hotkey dictionary
934956
var oldHotkeyModels = _windowPluginHotkeys[oldHotkey];
935-
_windowPluginHotkeys[oldHotkey] = oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id).ToList();
936-
if (_windowPluginHotkeys[oldHotkey].Count == 0)
957+
_windowPluginHotkeys[oldHotkey] = [.. oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id)];
958+
if (_windowPluginHotkeys[oldHotkey].IsEmpty)
937959
{
938960
_windowPluginHotkeys.TryRemove(oldHotkey, out var _);
939961
}
@@ -942,7 +964,7 @@ public static void ChangePluginHotkey(PluginMetadata plugin, SearchWindowPluginH
942964
{
943965
var newList = newHotkeyModels.ToList();
944966
newList.Add((plugin, pluginHotkey));
945-
_windowPluginHotkeys[newHotkey] = newList;
967+
_windowPluginHotkeys[newHotkey] = [.. newList];
946968
}
947969
else
948970
{

Flow.Launcher/Helper/HotKeyMapper.cs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Collections.Specialized;
45
using System.ComponentModel;
@@ -41,6 +42,7 @@ internal static void Initialize()
4142
_settings.PropertyChanged += Settings_PropertyChanged;
4243
_settings.CustomPluginHotkeys.CollectionChanged += CustomPluginHotkeys_CollectionChanged;
4344
PluginManager.PluginHotkeyChanged += PluginManager_PluginHotkeyChanged;
45+
PluginManager.PluginHotkeyInitialized += PluginManager_PluginHotkeyInitialized;
4446
}
4547

4648
private static void InitializeRegisteredHotkeys()
@@ -109,9 +111,27 @@ private static void InitializeRegisteredHotkeys()
109111
list.Add(GetRegisteredHotkeyData(customPluginHotkey));
110112
}
111113

112-
// Plugin hotkeys
114+
// Add registered hotkeys & Set them
115+
foreach (var hotkey in list)
116+
{
117+
_settings.RegisteredHotkeys.Add(hotkey);
118+
if (hotkey.RegisteredType == RegisteredHotkeyType.DialogJump && !_settings.EnableDialogJump)
119+
{
120+
// If dialog jump is disabled, do not register the hotkey
121+
continue;
122+
}
123+
SetHotkey(hotkey);
124+
}
125+
126+
App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]");
127+
}
128+
129+
private static void PluginManager_PluginHotkeyInitialized(PluginPair pair)
130+
{
131+
var list = new List<RegisteredHotkeyData>();
132+
113133
// Global plugin hotkeys
114-
var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo();
134+
var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo(pair.Metadata.ID);
115135
foreach (var info in pluginHotkeyInfos)
116136
{
117137
var pluginPair = info.Key;
@@ -128,7 +148,7 @@ private static void InitializeRegisteredHotkeys()
128148
}
129149

130150
// Window plugin hotkeys
131-
var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys();
151+
var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(pair.Metadata.ID);
132152
foreach (var hotkey in windowPluginHotkeys)
133153
{
134154
var hotkeyModel = hotkey.Key;
@@ -140,15 +160,10 @@ private static void InitializeRegisteredHotkeys()
140160
foreach (var hotkey in list)
141161
{
142162
_settings.RegisteredHotkeys.Add(hotkey);
143-
if (hotkey.RegisteredType == RegisteredHotkeyType.DialogJump && !_settings.EnableDialogJump)
144-
{
145-
// If dialog jump is disabled, do not register the hotkey
146-
continue;
147-
}
148163
SetHotkey(hotkey);
149164
}
150165

151-
App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]");
166+
App.API.LogDebug(ClassName, $"Initialize {list.Count} hotkeys for {pair.Metadata.Name}:\n[\n\t{string.Join(",\n\t", list)}\n]");
152167
}
153168

154169
#endregion
@@ -359,7 +374,7 @@ private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey,
359374
return new(RegisteredHotkeyType.PluginGlobalHotkey, HotkeyType.Global, hotkey, "pluginHotkey", GlobalPluginHotkeyCommand, new GlobalPluginHotkeyPair(metadata, pluginHotkey), removeHotkeyAction);
360375
}
361376

362-
private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> windowHotkeys)
377+
private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> windowHotkeys)
363378
{
364379
Action removeHotkeysAction = windowHotkeys.All(h => h.PluginHotkey.Editable) ?
365380
() =>
@@ -747,12 +762,12 @@ internal static bool CheckAvailability(HotkeyModel currentHotkey)
747762
private static void InitializeActionContextHotkeys()
748763
{
749764
// Fixed hotkeys for ActionContext
750-
_actionContextRegisteredHotkeys = new List<RegisteredHotkeyData>
751-
{
765+
_actionContextRegisteredHotkeys =
766+
[
752767
new(RegisteredHotkeyType.CtrlShiftEnter, HotkeyType.SearchWindow, "Ctrl+Shift+Enter", nameof(Localize.HotkeyCtrlShiftEnterDesc), _mainViewModel.OpenResultCommand),
753768
new(RegisteredHotkeyType.CtrlEnter, HotkeyType.SearchWindow, "Ctrl+Enter", nameof(Localize.OpenContainFolderHotkey), _mainViewModel.OpenResultCommand),
754769
new(RegisteredHotkeyType.AltEnter, HotkeyType.SearchWindow, "Alt+Enter", nameof(Localize.HotkeyOpenResult), _mainViewModel.OpenResultCommand),
755-
};
770+
];
756771

757772
// Register ActionContext hotkeys and they will be cached and restored in _actionContextHotkeyEvents
758773
foreach (var hotkey in _actionContextRegisteredHotkeys)
@@ -822,18 +837,12 @@ public GlobalPluginHotkeyPair(PluginMetadata metadata, GlobalPluginHotkey global
822837
}
823838
}
824839

825-
private class WindowPluginHotkeyPair
840+
private class WindowPluginHotkeyPair(HotkeyModel hotkey, ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys)
826841
{
827842
[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")]
828-
public HotkeyModel Hotkey { get; }
843+
public HotkeyModel Hotkey { get; } = hotkey;
829844

830-
public List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> HotkeyModels { get; }
831-
832-
public WindowPluginHotkeyPair(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys)
833-
{
834-
Hotkey = hotkey;
835-
HotkeyModels = hotkeys;
836-
}
845+
public ConcurrentBag<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> HotkeyModels { get; } = hotkeys;
837846
}
838847

839848
#endregion

0 commit comments

Comments
 (0)