Skip to content

IPluginHotkey Interface for Global Hotkey & Window Hotkey / Support Rename File & Folder by Hotkey or Context Menu #3770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 123 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
81d5065
Added function to convert rad to deg and vice versa
Koisu-unavailable Jun 19, 2025
4e03b76
cleanup
Koisu-unavailable Jun 19, 2025
0c34eb0
fix formatting
Koisu-unavailable Jun 19, 2025
762e1c7
Fix Typo
VictoriousRaptor Jun 20, 2025
60e8226
added new action keyword
Koisu-unavailable Jun 20, 2025
8d8388b
Merge branch 'dev' of https://github.com/Koisu-unavailable/Flow.Launc…
Koisu-unavailable Jun 20, 2025
9eb4e64
Revert "Fix Typo"
Koisu-unavailable Jun 20, 2025
3bc06ac
Reapply "Fix Typo"
Koisu-unavailable Jun 20, 2025
c116ea2
added new view
Koisu-unavailable Jun 20, 2025
91e3582
Revert "added new view"
Koisu-unavailable Jun 20, 2025
bfb2adf
Revert "Reapply "Fix Typo""
Koisu-unavailable Jun 20, 2025
670dd1c
added new view
Koisu-unavailable Jun 20, 2025
39366b7
Revert "Revert "added new view""
Koisu-unavailable Jun 20, 2025
459d85d
added new dialogue box for renaming files
Koisu-unavailable Jun 22, 2025
5ae6f97
renaming half-works
Koisu-unavailable Jun 22, 2025
8282304
basic features of renaming files works
Koisu-unavailable Jun 22, 2025
c943982
polishing changes
Koisu-unavailable Jun 22, 2025
502a50b
added keybind to open the renaming dialog
Koisu-unavailable Jun 23, 2025
f36ee61
feat: :sparkles: Added keybind to rename files
Koisu-unavailable Jun 24, 2025
e12c2be
polished ui
Koisu-unavailable Jun 24, 2025
efe6595
revert bad changes
Koisu-unavailable Jun 24, 2025
9f32814
removed unnesscasry stuff
Koisu-unavailable Jun 24, 2025
432f6da
fixed icon in hotkey settings
Koisu-unavailable Jun 24, 2025
657bde5
revert changes to calculator plugin
Koisu-unavailable Jun 24, 2025
8138158
refactoring
Koisu-unavailable Jun 24, 2025
518f883
Changed RenameDialog to modal and added exception logging
Koisu-unavailable Jun 24, 2025
781feeb
added default to RenameFileHotkey
Koisu-unavailable Jun 24, 2025
b0beaaf
formatting
Koisu-unavailable Jun 24, 2025
402d398
checks for reserved names
Koisu-unavailable Jun 24, 2025
b556dac
Refactoring
Koisu-unavailable Jun 24, 2025
a6f1981
removed fragile error handling
Koisu-unavailable Jun 24, 2025
7ae261e
fix typo
Koisu-unavailable Jun 24, 2025
a5ef0cb
fixed unreachable code
Koisu-unavailable Jun 24, 2025
a326a76
refactoring
Koisu-unavailable Jun 24, 2025
54f126a
removed dynamic type
Koisu-unavailable Jun 24, 2025
f991d91
Improved File Rename Feature and Icon Update
onesounds Jun 25, 2025
94f3746
Add plugin hotkey model
Jack251970 Jun 25, 2025
5f49c43
Display plugin hotkey setting
Jack251970 Jun 25, 2025
605837b
Add global key registeration
Jack251970 Jun 25, 2025
f841175
Fix log message issue
Jack251970 Jun 25, 2025
8abd531
Add window key registeration
Jack251970 Jun 25, 2025
4d77bad
Sort hotkey info list
Jack251970 Jun 25, 2025
8ed495d
Improve code quality
Jack251970 Jun 25, 2025
f7fa647
Use IPluginHotkey for explorer plugin
Jack251970 Jun 25, 2025
f2358a5
Fix hotkey control construction issue
Jack251970 Jun 25, 2025
30b9b1f
Add Visible api
Jack251970 Jun 25, 2025
f837b2a
Remove unused using
Jack251970 Jun 25, 2025
35f8ea3
Check hotkey mapper count
Jack251970 Jun 25, 2025
8da182d
Close window when escape pressed
Koisu-unavailable Jun 25, 2025
043bf3b
remove unused using
Koisu-unavailable Jun 25, 2025
0aff9e3
Merge branch 'rename-file' of https://github.com/Koisu-unavailable/Fl…
Jack251970 Jun 26, 2025
53e0bc3
Improve hotkey model
Jack251970 Jun 26, 2025
724b8a7
Support change window hotkey
Jack251970 Jun 26, 2025
c7de03b
Save & restore old command
Jack251970 Jun 26, 2025
b52c7e7
Check result null & Improve docuements
Jack251970 Jun 26, 2025
590ea61
Skip other commands if executed
Jack251970 Jun 26, 2025
ead9b1e
Return to skip other commands
Jack251970 Jun 26, 2025
030a7f3
Adjust margins
Jack251970 Jun 26, 2025
854f808
Use IPluginHotkey for plugin manager plugin
Jack251970 Jun 26, 2025
9f404aa
Use IPluginHotkey for program plugin
Jack251970 Jun 26, 2025
3fe1e50
Resolve context data for uwps
Jack251970 Jun 26, 2025
96b5eb3
Skip this plugin if all hotkeys are invisible
Jack251970 Jun 26, 2025
ddc890e
Skip invisible hotkeys
Jack251970 Jun 26, 2025
e9727c4
Add todo
Jack251970 Jun 26, 2025
95fadcb
Merge branch 'dev' into rename-file
Jack251970 Jun 27, 2025
cda33df
Make constructor protected
Koisu-unavailable Jun 27, 2025
96b3645
Merge branch 'rename-file' of https://github.com/Koisu-unavailable/Fl…
Koisu-unavailable Jun 27, 2025
7fddfd9
Merge branch 'dev' into rename-file
Jack251970 Jun 28, 2025
e087d33
Add hotkey id check
Jack251970 Jun 28, 2025
3b4698e
Use selected results
Jack251970 Jun 28, 2025
e24af14
Add hotkey ids for all results
Jack251970 Jun 28, 2025
73a232f
Fix clone issue
Jack251970 Jun 30, 2025
c9db3ec
Improve string resource
Jack251970 Jun 30, 2025
14310d2
Add code comments
Jack251970 Jul 1, 2025
b87f233
Code quality
Jack251970 Jul 1, 2025
afdb56d
Change RegisteredHotkeys to observable
Jack251970 Jul 2, 2025
1d2aa96
Add property changed for CustomPluginHotkey
Jack251970 Jul 2, 2025
989206b
Add is empty for HotkeyModel
Jack251970 Jul 2, 2025
5910d1d
Add property changed for RegisteredHotkeyData.Hotkey
Jack251970 Jul 2, 2025
2259d74
Add registered type, type, command, command parameter for RegisteredH…
Jack251970 Jul 2, 2025
3231b1d
Implement initialization model for plugin hotkeys
Jack251970 Jul 2, 2025
051af06
Add toggle cmmand for main view model
Jack251970 Jul 2, 2025
20f065a
Remove key bindings which will be registered in hotkey mapper
Jack251970 Jul 2, 2025
d332472
Add todo
Jack251970 Jul 2, 2025
43beef4
Use command & parameter for SetGlobalHotkeyWithChefKeys
Jack251970 Jul 2, 2025
39fa79b
Prepare to deprecation
Jack251970 Jul 2, 2025
6e94d16
Code quality
Jack251970 Jul 2, 2025
f36ca62
Fix typos
Jack251970 Jul 2, 2025
495e2c1
Revert "Add property changed for RegisteredHotkeyData.Hotkey"
Jack251970 Jul 2, 2025
40f1fc6
Allow setter for RegisteredHotkeyData.Hotkey
Jack251970 Jul 2, 2025
f63b8bd
Add ToString
Jack251970 Jul 2, 2025
e41eccc
Add initialization log information
Jack251970 Jul 2, 2025
3b1e014
Add change support for Flow Launcher hotkeys
Jack251970 Jul 2, 2025
2cf6bfb
Prepare for deprecation
Jack251970 Jul 2, 2025
730625c
Remove ChangeHotkey event & Remove deprecated functions
Jack251970 Jul 2, 2025
1c8a8d0
Code quality
Jack251970 Jul 2, 2025
e061f14
Check plugin modified state
Jack251970 Jul 2, 2025
1057f4d
Improve code quality
Jack251970 Jul 2, 2025
d46dedd
Remove Settings.CustomPluginHotkeys null check
Jack251970 Jul 2, 2025
fd3eef4
Add Equals & GetHashCode for CustomPluginHotkey
Jack251970 Jul 2, 2025
96e8eae
Revert "Add property changed for CustomPluginHotkey"
Jack251970 Jul 2, 2025
d0fc344
Explictly implement PropertyChanged
Jack251970 Jul 2, 2025
c4ce8fe
Add check for invalid query shortcuts
Jack251970 Jul 2, 2025
476ad9b
Add constructor for CustomPluginHotkey
Jack251970 Jul 2, 2025
42e2035
Improve string
Jack251970 Jul 2, 2025
ff99669
Refactor custom query hotkey setting window
Jack251970 Jul 2, 2025
5fa3bec
Add hotkey change events for custom query hotkeys
Jack251970 Jul 2, 2025
698fe79
Remove unused removing
Jack251970 Jul 2, 2025
7d0cf1f
Add plugin hotkey type
Jack251970 Jul 2, 2025
805b8ef
No need to check hotkey command when removing
Jack251970 Jul 2, 2025
503bc48
Add empty for hotkey model
Jack251970 Jul 2, 2025
db1c1b2
Use changed event for plugin hotkeys
Jack251970 Jul 2, 2025
c4fbb3d
Check count
Jack251970 Jul 2, 2025
dad681d
Use callback to check plugin hotkey change
Jack251970 Jul 2, 2025
8d26c1c
Add string resource
Jack251970 Jul 2, 2025
cd3eeba
Remove used functions
Jack251970 Jul 2, 2025
11c5b7c
Merge branch 'dev' into rename-file
Jack251970 Jul 2, 2025
e196aa4
Remove todos
Jack251970 Jul 2, 2025
e6fb766
Mark ActionContext as deprecated
Jack251970 Jul 2, 2025
f657494
Workaround for ActionContext compatibility
Jack251970 Jul 2, 2025
2625f6f
Make GetPluginsForInterface private
Jack251970 Jul 3, 2025
089c0fd
Initialize plugin enumerable after all plugins are initialized
Jack251970 Jul 3, 2025
2a52c28
Store plugin hotkey info
Jack251970 Jul 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 177 additions & 3 deletions Flow.Launcher.Core/Plugin/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
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;
Expand All @@ -26,11 +28,16 @@

private static IEnumerable<PluginPair> _contextMenuPlugins;
private static IEnumerable<PluginPair> _homePlugins;
private static IEnumerable<PluginPair> _resultUpdatePlugin;
private static IEnumerable<PluginPair> _translationPlugins;
private static IEnumerable<PluginPair> _hotkeyPlugins;

public static List<PluginPair> AllPlugins { get; private set; }
public static readonly HashSet<PluginPair> GlobalPlugins = new();
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new();

public static Action<PluginHotkeyChangedEvent> PluginHotkeyChanged { get; set; }

// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
Expand All @@ -39,6 +46,9 @@
private static List<PluginMetadata> _metadatas;
private static readonly List<string> _modifiedPlugins = new();

private static readonly Dictionary<PluginPair, List<BasePluginHotkey>> _pluginHotkeyInfo = new();
private static readonly Dictionary<HotkeyModel, List<(PluginMetadata, SearchWindowPluginHotkey)>> _windowPluginHotkeys = new();

/// <summary>
/// Directories that will hold Flow Launcher plugin directory
/// </summary>
Expand Down Expand Up @@ -111,7 +121,7 @@
await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch
{
IReloadable p => Task.Run(p.ReloadData),
IAsyncReloadable p => p.ReloadDataAsync(),

Check warning on line 124 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Reloadable` is not a recognized word. (unrecognized-spelling)
_ => Task.CompletedTask,
}).ToArray());
}
Expand Down Expand Up @@ -173,12 +183,18 @@
/// <param name="settings"></param>
public static void LoadPlugins(PluginsSettings settings)
{
_metadatas = PluginConfig.Parse(Directories);

Check warning on line 186 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`metadatas` is not a recognized word. (unrecognized-spelling)
Settings = settings;
Settings.UpdatePluginSettings(_metadatas);

Check warning on line 188 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`metadatas` is not a recognized word. (unrecognized-spelling)
AllPlugins = PluginsLoader.Plugins(_metadatas, Settings);

Check warning on line 189 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`metadatas` is not a recognized word. (unrecognized-spelling)
// Since dotnet plugins need to get assembly name first, we should update plugin directory after loading plugins
UpdatePluginDirectory(_metadatas);

Check warning on line 191 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`metadatas` is not a recognized word. (unrecognized-spelling)
// Initialize plugin enumerable after all plugins are initialized
_contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
_homePlugins = GetPluginsForInterface<IAsyncHomeQuery>();
_resultUpdatePlugin = GetPluginsForInterface<IResultUpdated>();
_translationPlugins = GetPluginsForInterface<IPluginI18n>();
_hotkeyPlugins = GetPluginsForInterface<IPluginHotkey>();
}

private static void UpdatePluginDirectory(List<PluginMetadata> metadatas)
Expand Down Expand Up @@ -248,8 +264,9 @@

await Task.WhenAll(InitTasks);

_contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
_homePlugins = GetPluginsForInterface<IAsyncHomeQuery>();
InitializePluginHotkeyInfo();
Settings.UpdatePluginHotkeyInfo(GetPluginHotkeyInfo());
InitializeWindowPluginHotkeys();

foreach (var plugin in AllPlugins)
{
Expand Down Expand Up @@ -409,7 +426,7 @@
return AllPlugins.FirstOrDefault(o => o.Metadata.ID == id);
}

public static IEnumerable<PluginPair> GetPluginsForInterface<T>() where T : IFeatures
private static IEnumerable<PluginPair> GetPluginsForInterface<T>() where T : IFeatures
{
// Handle scenario where this is called before all plugins are instantiated, e.g. language change on startup
return AllPlugins?.Where(p => p.Plugin is T) ?? Array.Empty<PluginPair>();
Expand Down Expand Up @@ -449,6 +466,140 @@
return _homePlugins.Any(p => p.Metadata.ID == id);
}

public static IList<PluginPair> GetResultUpdatePlugin()
{
return _resultUpdatePlugin.Where(p => !PluginModified(p.Metadata.ID)).ToList();
}

public static IList<PluginPair> GetTranslationPlugins()
{
// Here we still return the modified plugins to update the possible string resources
return _translationPlugins.ToList();
}

public static Dictionary<PluginPair, List<BasePluginHotkey>> GetPluginHotkeyInfo()
{
return _pluginHotkeyInfo;
}

public static Dictionary<HotkeyModel, List<(PluginMetadata Metadata, SearchWindowPluginHotkey SearchWindowPluginHotkey)>> GetWindowPluginHotkeys()
{
return _windowPluginHotkeys;
}

public static void UpdatePluginHotkeyInfoTranslations()
{
foreach (var plugin in _hotkeyPlugins)
{
var newHotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys();
if (_pluginHotkeyInfo.TryGetValue(plugin, out var oldHotkeys))
{
foreach (var newHotkey in newHotkeys)
{
if (oldHotkeys.FirstOrDefault(h => h.Id == newHotkey.Id) is BasePluginHotkey pluginHotkey)
{
pluginHotkey.Name = newHotkey.Name;
pluginHotkey.Description = newHotkey.Description;
}
else
{
oldHotkeys.Add(newHotkey);
}
}
}
else
{
_pluginHotkeyInfo.Add(plugin, newHotkeys);
}
}
}

private static void InitializePluginHotkeyInfo()
{
foreach (var plugin in _hotkeyPlugins)
{
var hotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys();
_pluginHotkeyInfo.Add(plugin, hotkeys);
}
}

private static void InitializeWindowPluginHotkeys()
{
foreach (var info in GetPluginHotkeyInfo())
{
var pluginPair = info.Key;
var hotkeyInfo = info.Value;
var metadata = pluginPair.Metadata;
foreach (var hotkey in hotkeyInfo)
{
if (hotkey.HotkeyType == HotkeyType.SearchWindow && hotkey is SearchWindowPluginHotkey searchWindowHotkey)
{
var hotkeySetting = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey;
var hotkeyModel = new HotkeyModel(hotkeySetting);
if (!_windowPluginHotkeys.TryGetValue(hotkeyModel, out var list))
{
list = new List<(PluginMetadata, SearchWindowPluginHotkey)>();
_windowPluginHotkeys[hotkeyModel] = list;
}
list.Add((pluginPair.Metadata, searchWindowHotkey));
}
}
}
}

public static void 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 oldHotkeyStr = settingHotkeyItem.Hotkey;
var oldHotkey = new HotkeyModel(oldHotkeyStr);
var newHotkeyStr = newHotkey.ToString();

// Update hotkey in plugin metadata & setting
oldHotkeyItem.Hotkey = newHotkeyStr;
settingHotkeyItem.Hotkey = newHotkeyStr;

PluginHotkeyChanged?.Invoke(new PluginHotkeyChangedEvent(oldHotkey, newHotkey, plugin, pluginHotkey));
}

public static void 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 oldHotkeyStr = settingHotkeyItem.Hotkey;
var converter = new KeyGestureConverter();
var oldHotkey = new HotkeyModel(oldHotkeyStr);
var newHotkeyStr = newHotkey.ToString();

// Update hotkey in plugin metadata & setting
oldHotkeyItem.Hotkey = newHotkeyStr;
settingHotkeyItem.Hotkey = newHotkeyStr;

// 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.Remove(oldHotkey);
}

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)
};
}

PluginHotkeyChanged?.Invoke(new PluginHotkeyChangedEvent(oldHotkey, newHotkey, plugin, pluginHotkey));
}

public static bool ActionKeywordRegistered(string actionKeyword)
{
// this method is only checking for action keywords (defined as not '*') registration
Expand Down Expand Up @@ -712,5 +863,28 @@
}

#endregion

#region Class

public class PluginHotkeyChangedEvent
{
public HotkeyModel NewHotkey { get; }

public HotkeyModel OldHotkey { get; }

public PluginMetadata Metadata { get; }

public BasePluginHotkey PluginHotkey { get; }

public PluginHotkeyChangedEvent(HotkeyModel oldHotkey, HotkeyModel newHotkey, PluginMetadata metadata, BasePluginHotkey pluginHotkey)
{
OldHotkey = oldHotkey;
NewHotkey = newHotkey;
Metadata = metadata;
PluginHotkey = pluginHotkey;
}
}

#endregion
}
}
8 changes: 6 additions & 2 deletions Flow.Launcher.Core/Resource/Internationalization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();

Check warning on line 24 in Flow.Launcher.Core/Resource/Internationalization.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Ioc` is not a recognized word. (unrecognized-spelling)

private const string Folder = "Languages";
private const string DefaultLanguageCode = "en";
Expand Down Expand Up @@ -74,7 +74,7 @@

private void AddPluginLanguageDirectories()
{
foreach (var plugin in PluginManager.GetPluginsForInterface<IPluginI18n>())
foreach (var plugin in PluginManager.GetTranslationPlugins())
{
var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location;
var dir = Path.GetDirectoryName(location);
Expand Down Expand Up @@ -278,7 +278,8 @@

private void UpdatePluginMetadataTranslations()
{
foreach (var p in PluginManager.GetPluginsForInterface<IPluginI18n>())
// Update plugin metadata name & description
foreach (var p in PluginManager.GetTranslationPlugins())
{
if (p.Plugin is not IPluginI18n pluginI18N) return;
try
Expand All @@ -292,6 +293,9 @@
API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e);
}
}

// Update plugin hotkey name & description
PluginManager.UpdatePluginHotkeyInfoTranslations();
}

private static string LanguageFile(string folder, string language)
Expand Down
18 changes: 16 additions & 2 deletions Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ public ModifierKeys ModifierKeys
}
}

public readonly bool IsEmpty => CharKey == Key.None && !Alt && !Shift && !Win && !Ctrl;

public static HotkeyModel Empty => new();

public HotkeyModel(string hotkeyString)
{
Parse(hotkeyString);
Expand Down Expand Up @@ -117,12 +121,22 @@ private void Parse(string hotkeyString)
}
}

public override string ToString()
public bool Equals(HotkeyModel other)
{
return CharKey == other.CharKey && ModifierKeys == other.ModifierKeys;
}

public KeyGesture ToKeyGesture()
{
return new KeyGesture(CharKey, ModifierKeys);
}

public override readonly string ToString()
{
return string.Join(" + ", EnumerateDisplayKeys());
}

public IEnumerable<string> EnumerateDisplayKeys()
public readonly IEnumerable<string> EnumerateDisplayKeys()
{
if (Ctrl && CharKey is not (Key.LeftCtrl or Key.RightCtrl))
{
Expand Down
4 changes: 2 additions & 2 deletions Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Flow.Launcher.Infrastructure.Hotkey;

Expand All @@ -13,5 +13,5 @@ public interface IHotkeySettings
/// A list of hotkeys that have already been registered. The dialog will display these hotkeys and provide a way to
/// unregister them.
/// </summary>
public List<RegisteredHotkeyData> RegisteredHotkeys { get; }
public ObservableCollection<RegisteredHotkeyData> RegisteredHotkeys { get; }
}
Loading
Loading