diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 9b525f331..96787bab5 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -26,6 +26,7 @@ namespace Flow.Launcher.Core.Plugin private static IEnumerable _contextMenuPlugins; private static IEnumerable _homePlugins; + private static IEnumerable _hotkeyPlugins; public static List AllPlugins { get; private set; } public static readonly HashSet GlobalPlugins = new(); @@ -250,6 +251,8 @@ namespace Flow.Launcher.Core.Plugin _contextMenuPlugins = GetPluginsForInterface(); _homePlugins = GetPluginsForInterface(); + _hotkeyPlugins = GetPluginsForInterface(); + Settings.UpdatePluginHotkeyInfo(GetPluginHotkeyInfo()); foreach (var plugin in AllPlugins) { @@ -442,6 +445,18 @@ namespace Flow.Launcher.Core.Plugin return _homePlugins.Any(p => p.Metadata.ID == id); } + public static Dictionary> GetPluginHotkeyInfo() + { + var hotkeyPluginInfos = new Dictionary>(); + foreach (var plugin in _hotkeyPlugins) + { + var hotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys(); + hotkeyPluginInfos.Add(plugin, hotkeys); + } + + return hotkeyPluginInfos; + } + public static bool ActionKeywordRegistered(string actionKeyword) { // this method is only checking for action keywords (defined as not '*') registration diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index 920abc284..f9fc1e509 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -89,6 +89,110 @@ namespace Flow.Launcher.Infrastructure.UserSettings } } + /// + /// Update plugin hotkey information in metadata and plugin setting. + /// + /// + public void UpdatePluginHotkeyInfo(Dictionary> hotkeyPluginInfo) + { + foreach (var info in hotkeyPluginInfo) + { + var pluginPair = info.Key; + var hotkeyInfo = info.Value; + var metadata = pluginPair.Metadata; + if (Plugins.TryGetValue(pluginPair.Metadata.ID, out var plugin)) + { + if (plugin.pluginHotkeys == null || plugin.pluginHotkeys.Count == 0) + { + // If plugin hotkeys does not exist, create a new one and initialize with default values + plugin.pluginHotkeys = new List(); + foreach (var hotkey in hotkeyInfo) + { + plugin.pluginHotkeys.Add(new PluginHotkey + { + Id = hotkey.Id, + DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values + Hotkey = hotkey.DefaultHotkey // use default value + }); + metadata.PluginHotkeys.Add(new PluginHotkey + { + Id = hotkey.Id, + DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values + Hotkey = hotkey.DefaultHotkey // use default value + }); + } + } + else + { + // If plugin hotkeys exist, update the existing hotkeys with the new values + foreach (var hotkey in hotkeyInfo) + { + var existingHotkey = plugin.pluginHotkeys.Find(h => h.Id == hotkey.Id); + if (existingHotkey != null) + { + // Update existing hotkey + existingHotkey.DefaultHotkey = hotkey.DefaultHotkey; // hotkey info provides default values + metadata.PluginHotkeys.Add(new PluginHotkey + { + Id = hotkey.Id, + DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values + Hotkey = existingHotkey.Hotkey // use settings value + }); + } + else + { + // Add new hotkey if it does not exist + plugin.pluginHotkeys.Add(new PluginHotkey + { + Id = hotkey.Id, + DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values + Hotkey = hotkey.DefaultHotkey // use default value + }); + metadata.PluginHotkeys.Add(new PluginHotkey + { + Id = hotkey.Id, + DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values + Hotkey = hotkey.DefaultHotkey // use default value + }); + } + } + } + } + else + { + // If settings does not exist, create a new one + Plugins[metadata.ID] = new Plugin + { + ID = metadata.ID, + Name = metadata.Name, + Version = metadata.Version, + DefaultActionKeywords = metadata.ActionKeywords, // metadata provides default values + ActionKeywords = metadata.ActionKeywords, // use default value + Disabled = metadata.Disabled, + HomeDisabled = metadata.HomeDisabled, + Priority = metadata.Priority, + DefaultSearchDelayTime = metadata.SearchDelayTime, // metadata provides default values + SearchDelayTime = metadata.SearchDelayTime, // use default value + }; + foreach (var hotkey in hotkeyInfo) + { + Plugins[metadata.ID].pluginHotkeys.Add(new PluginHotkey + { + Id = hotkey.Id, + DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values + Hotkey = hotkey.DefaultHotkey // use default value + }); + metadata.PluginHotkeys.Add(new PluginHotkey + { + Id = hotkey.Id, + DefaultHotkey = hotkey.DefaultHotkey, // hotkey info provides default values + Hotkey = hotkey.DefaultHotkey // use default value + }); + } + } + } + } + public Plugin GetPluginSettings(string id) { if (Plugins.TryGetValue(id, out var plugin)) @@ -126,6 +230,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings public int? SearchDelayTime { get; set; } + public List pluginHotkeys { get; set; } = new List(); + /// /// Used only to save the state of the plugin in settings /// diff --git a/Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs b/Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs new file mode 100644 index 000000000..a075c4619 --- /dev/null +++ b/Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Flow.Launcher.Plugin +{ + /// + /// Represent plugins that support global hotkey or search window hotkey. + /// + public interface IPluginHotkey : IFeatures + { + /// + /// Get the list of plugin hotkeys which will be registered in the settings page. + /// + /// + List GetPuginHotkeys(); + } +} diff --git a/Flow.Launcher.Plugin/PluginHotkey.cs b/Flow.Launcher.Plugin/PluginHotkey.cs new file mode 100644 index 000000000..eae080e49 --- /dev/null +++ b/Flow.Launcher.Plugin/PluginHotkey.cs @@ -0,0 +1,129 @@ +using System; + +namespace Flow.Launcher.Plugin; + +/// +/// Represents a base plugin hotkey model. +/// +/// +/// Do not use this class directly. Use or instead. +/// +public class BasePluginHotkey +{ + /// + /// Initializes a new instance of the class with the specified hotkey type. + /// + /// + public BasePluginHotkey(HotkeyType type) + { + HotkeyType = type; + } + + /// + /// The unique identifier for the hotkey, which is used to identify and rank the hotkey in the settings page. + /// + public int Id { get; set; } = 0; + + /// + /// The name of the hotkey, which will be displayed in the settings page. + /// + public string Name { get; set; } = string.Empty; + + /// + /// The description of the hotkey, which will be displayed in the settings page. + /// + public string Description { get; set; } = string.Empty; + + /// + /// The glyph information for the hotkey, which will be displayed in the settings page. + /// + public GlyphInfo Glyph { get; set; } + + /// + /// The default hotkey that will be used if the user does not set a custom hotkey. + /// + public string DefaultHotkey { get; set; } = string.Empty; + + /// + /// The type of the hotkey, which can be either global or search window specific. + /// + public HotkeyType HotkeyType { get; } = HotkeyType.Global; + + /// + /// Indicates whether the hotkey is editable by the user in the settings page. + /// + public bool Editable { get; set; } = false; +} + +/// +/// Represent a global plugin hotkey model. +/// +public class GlobalPluginHotkey : BasePluginHotkey +{ + /// + /// Initializes a new instance of the class. + /// + public GlobalPluginHotkey() : base(HotkeyType.Global) + { + } + + /// + /// An action that will be executed when the hotkey is triggered. + /// + public Action Action { get; set; } = null; +} + +/// +/// Represents a plugin hotkey that is specific to the search window. +/// +public class SearchWindowPluginHotkey : BasePluginHotkey +{ + /// + /// Initializes a new instance of the class. + /// + public SearchWindowPluginHotkey() : base(HotkeyType.SearchWindow) + { + } + + /// + /// An action that will be executed when the hotkey is triggered. + /// + public Func Action { get; set; } = null; +} + +/// +/// Represents the type of hotkey for a plugin. +/// +public enum HotkeyType +{ + /// + /// A hotkey that will be trigged globally, regardless of the active window. + /// + Global, + + /// + /// A hotkey that will be triggered only when the search window is active. + /// + SearchWindow +} + +/// +/// Represents a plugin hotkey model which is used to store the hotkey information for a plugin. +/// +public class PluginHotkey +{ + /// + /// The unique identifier for the hotkey. + /// + public int Id { get; set; } = 0; + + /// + /// The default hotkey that will be used if the user does not set a custom hotkey. + /// + public string DefaultHotkey { get; set; } = string.Empty; + + /// + /// The current hotkey that the user has set for the plugin. + /// + public string Hotkey { get; set; } = string.Empty; +} diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 09803cbd7..77edb5c7b 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -152,6 +152,11 @@ namespace Flow.Launcher.Plugin /// public string PluginCacheDirectoryPath { get; internal set; } + /// + /// List of registered plugin hotkeys. + /// + public List PluginHotkeys { get; set; } = new List(); + /// /// Convert to string. ///