From 81d5065b38f1b56456e4d8fbc3595802276a1e75 Mon Sep 17 00:00:00 2001 From: Koisu Date: Thu, 19 Jun 2025 14:33:24 -0700 Subject: [PATCH 001/150] Added function to convert rad to deg and vice versa --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index b1e4cd60601..be07fd079a4 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -7,6 +7,8 @@ using Mages.Core; using Flow.Launcher.Plugin.Calculator.ViewModels; using Flow.Launcher.Plugin.Calculator.Views; +using System.IO; +using System.Net.Quic; namespace Flow.Launcher.Plugin.Calculator { @@ -15,12 +17,13 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider private static readonly Regex RegValidExpressChar = new Regex( @"^(" + @"ceil|floor|exp|pi|e|max|min|det|abs|log|ln|sqrt|" + - @"sin|cos|tan|arcsin|arccos|arctan|" + + @"sin|cos|tan|arcsin|arccos|arctan|rad2deg|deg2rad|" + @"eigval|eigvec|eig|sum|polar|plot|round|sort|real|zeta|" + @"bin2dec|hex2dec|oct2dec|" + @"factorial|sign|isprime|isinfty|" + @"==|~=|&&|\|\||(?:\<|\>)=?|" + @"[ei]|[0-9]|[\+\%\-\*\/\^\., ""]|[\(\)\|\!\[\]]" + + @")+$", RegexOptions.Compiled); private static readonly Regex RegBrackets = new Regex(@"[\(\)\[\]]", RegexOptions.Compiled); private static Engine MagesEngine; @@ -44,7 +47,12 @@ public void Init(PluginInitContext context) { { "e", Math.E }, // e is not contained in the default mages engine } - }); + } + ); + ; + Func rad2deg = (rad) => rad * (180.0 / Math.PI); + MagesEngine.SetFunction("rad2deg", rad2deg); + MagesEngine.SetFunction("deg2rad", (double x) => x * (Math.PI / 180.0)); } public List Query(Query query) @@ -68,7 +76,7 @@ public List Query(Query query) expression = query.Search; break; } - + // Replace all spaces in the expression var result = MagesEngine.Interpret(expression); if (result?.ToString() == "NaN") From 4e03b766a99980f2a4bafe21c686340cc6c8e0ab Mon Sep 17 00:00:00 2001 From: Koisu Date: Thu, 19 Jun 2025 14:35:18 -0700 Subject: [PATCH 002/150] cleanup --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index be07fd079a4..b2a1c82e39e 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -7,8 +7,7 @@ using Mages.Core; using Flow.Launcher.Plugin.Calculator.ViewModels; using Flow.Launcher.Plugin.Calculator.Views; -using System.IO; -using System.Net.Quic; + namespace Flow.Launcher.Plugin.Calculator { @@ -23,7 +22,6 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider @"factorial|sign|isprime|isinfty|" + @"==|~=|&&|\|\||(?:\<|\>)=?|" + @"[ei]|[0-9]|[\+\%\-\*\/\^\., ""]|[\(\)\|\!\[\]]" + - @")+$", RegexOptions.Compiled); private static readonly Regex RegBrackets = new Regex(@"[\(\)\[\]]", RegexOptions.Compiled); private static Engine MagesEngine; @@ -47,9 +45,7 @@ public void Init(PluginInitContext context) { { "e", Math.E }, // e is not contained in the default mages engine } - } - ); - ; + }); Func rad2deg = (rad) => rad * (180.0 / Math.PI); MagesEngine.SetFunction("rad2deg", rad2deg); MagesEngine.SetFunction("deg2rad", (double x) => x * (Math.PI / 180.0)); @@ -76,7 +72,7 @@ public List Query(Query query) expression = query.Search; break; } - // Replace all spaces in the expression + var result = MagesEngine.Interpret(expression); if (result?.ToString() == "NaN") From 0c34eb0096ce7b45fca6819fac123a38e6a4891b Mon Sep 17 00:00:00 2001 From: Koisu Date: Thu, 19 Jun 2025 14:56:40 -0700 Subject: [PATCH 003/150] fix formatting --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index b2a1c82e39e..bd6067a1cde 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -46,8 +46,7 @@ public void Init(PluginInitContext context) { "e", Math.E }, // e is not contained in the default mages engine } }); - Func rad2deg = (rad) => rad * (180.0 / Math.PI); - MagesEngine.SetFunction("rad2deg", rad2deg); + MagesEngine.SetFunction("rad2deg", (double rad) => rad * (180.0 / Math.PI)); MagesEngine.SetFunction("deg2rad", (double x) => x * (Math.PI / 180.0)); } From 762e1c75c306e9a12ea626db7e810f30fd7183d8 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 20 Jun 2025 09:01:29 +0800 Subject: [PATCH 004/150] Fix Typo --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index bd6067a1cde..9cd73527465 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -172,9 +172,9 @@ private static string GetDecimalSeparator() private bool IsBracketComplete(string query) { - var matchs = RegBrackets.Matches(query); + var matches = RegBrackets.Matches(query); var leftBracketCount = 0; - foreach (Match match in matchs) + foreach (Match match in matches) { if (match.Value == "(" || match.Value == "[") { From 60e82264c8ff735e3cc5b9245807dd606fc89e4b Mon Sep 17 00:00:00 2001 From: Koisu Date: Fri, 20 Jun 2025 11:16:28 -0700 Subject: [PATCH 005/150] added new action keyword --- .../Languages/en.xaml | 1 + .../Search/SearchManager.cs | 11 + .../Flow.Launcher.Plugin.Explorer/Settings.cs | 202 +++++++++++------- .../ViewModels/SettingsViewModel.cs | 4 +- 4 files changed, 140 insertions(+), 78 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 2e0f6a67db3..7000e74aec1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -55,6 +55,7 @@ File Content Search: Index Search: Quick Access: + Rename: Current Action Keyword Done Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 12df6c1458e..28609c86d50 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -47,6 +47,16 @@ public int GetHashCode(Result obj) internal async Task> SearchAsync(Query query, CancellationToken token) { var results = new HashSet(PathEqualityComparator.Instance); + if (ActionKeywordMatch(query, Settings.ActionKeyword.RenameActionKeyword)) + { + return new List() + { + new Result(){ + Title = "Done", + AutoCompleteText = "test" + } + }; + } // This allows the user to type the below action keywords and see/search the list of quick folder links if (ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword) @@ -151,6 +161,7 @@ private bool ActionKeywordMatch(Query query, Settings.ActionKeyword allowedActio keyword == Settings.IndexSearchActionKeyword, Settings.ActionKeyword.QuickAccessActionKeyword => Settings.QuickAccessKeywordEnabled && keyword == Settings.QuickAccessActionKeyword, + Settings.ActionKeyword.RenameActionKeyword => Settings.RenameActionKeywordEnabled && keyword == Settings.RenameActionKeyword, _ => throw new ArgumentOutOfRangeException(nameof(allowedActionKeyword), allowedActionKeyword, "actionKeyword out of range") }; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 77540f3a87b..ac0e5406241 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,13 +1,14 @@ -using Flow.Launcher.Plugin.Everything.Everything; -using Flow.Launcher.Plugin.Explorer.Search; -using Flow.Launcher.Plugin.Explorer.Search.Everything; -using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; -using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; -using System; +using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Text.Json.Serialization; +using Flow.Launcher.Plugin.Everything.Everything; +using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.Everything; using Flow.Launcher.Plugin.Explorer.Search.IProvider; +using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; +using Flow.Launcher.Plugin.Explorer.ViewModels; namespace Flow.Launcher.Plugin.Explorer { @@ -17,7 +18,8 @@ public class Settings public ObservableCollection QuickAccessLinks { get; set; } = new(); - public ObservableCollection IndexSearchExcludedSubdirectoryPaths { get; set; } = new ObservableCollection(); + public ObservableCollection IndexSearchExcludedSubdirectoryPaths { get; set; } = + new ObservableCollection(); public string EditorPath { get; set; } = ""; @@ -43,7 +45,8 @@ public class Settings public bool SearchActionKeywordEnabled { get; set; } = true; - public string FileContentSearchActionKeyword { get; set; } = Constants.DefaultContentSearchActionKeyword; + public string FileContentSearchActionKeyword { get; set; } = + Constants.DefaultContentSearchActionKeyword; public bool FileContentSearchKeywordEnabled { get; set; } = true; @@ -58,7 +61,8 @@ public class Settings public string QuickAccessActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; public bool QuickAccessKeywordEnabled { get; set; } - + public string RenameActionKeyword { get; set; } = "re:"; + public bool RenameActionKeywordEnabled { get; set; } = true; public bool WarnWindowsSearchServiceOff { get; set; } = true; @@ -67,9 +71,8 @@ public class Settings public bool ShowCreatedDateInPreviewPanel { get; set; } = true; public bool ShowModifiedDateInPreviewPanel { get; set; } = true; - - public bool ShowFileAgeInPreviewPanel { get; set; } = false; + public bool ShowFileAgeInPreviewPanel { get; set; } = false; public string PreviewPanelDateFormat { get; set; } = "yyyy-MM-dd"; @@ -80,44 +83,55 @@ public class Settings #region SearchEngine - private EverythingSearchManager EverythingManagerInstance => _everythingManagerInstance ??= new EverythingSearchManager(this); - private WindowsIndexSearchManager WindowsIndexSearchManager => _windowsIndexSearchManager ??= new WindowsIndexSearchManager(this); + private EverythingSearchManager EverythingManagerInstance => + _everythingManagerInstance ??= new EverythingSearchManager(this); + private WindowsIndexSearchManager WindowsIndexSearchManager => + _windowsIndexSearchManager ??= new WindowsIndexSearchManager(this); + public IndexSearchEngineOption IndexSearchEngine { get; set; } = + IndexSearchEngineOption.WindowsIndex; - public IndexSearchEngineOption IndexSearchEngine { get; set; } = IndexSearchEngineOption.WindowsIndex; [JsonIgnore] - public IIndexProvider IndexProvider => IndexSearchEngine switch - { - IndexSearchEngineOption.Everything => EverythingManagerInstance, - IndexSearchEngineOption.WindowsIndex => WindowsIndexSearchManager, - _ => throw new ArgumentOutOfRangeException(nameof(IndexSearchEngine)) - }; + public IIndexProvider IndexProvider => + IndexSearchEngine switch + { + IndexSearchEngineOption.Everything => EverythingManagerInstance, + IndexSearchEngineOption.WindowsIndex => WindowsIndexSearchManager, + _ => throw new ArgumentOutOfRangeException(nameof(IndexSearchEngine)) + }; - public PathEnumerationEngineOption PathEnumerationEngine { get; set; } = PathEnumerationEngineOption.WindowsIndex; + public PathEnumerationEngineOption PathEnumerationEngine { get; set; } = + PathEnumerationEngineOption.WindowsIndex; [JsonIgnore] - public IPathIndexProvider PathEnumerator => PathEnumerationEngine switch - { - PathEnumerationEngineOption.Everything => EverythingManagerInstance, - PathEnumerationEngineOption.WindowsIndex => WindowsIndexSearchManager, - _ => throw new ArgumentOutOfRangeException(nameof(PathEnumerationEngine)) - }; + public IPathIndexProvider PathEnumerator => + PathEnumerationEngine switch + { + PathEnumerationEngineOption.Everything => EverythingManagerInstance, + PathEnumerationEngineOption.WindowsIndex => WindowsIndexSearchManager, + _ => throw new ArgumentOutOfRangeException(nameof(PathEnumerationEngine)) + }; + + public ContentIndexSearchEngineOption ContentSearchEngine { get; set; } = + ContentIndexSearchEngineOption.WindowsIndex; - public ContentIndexSearchEngineOption ContentSearchEngine { get; set; } = ContentIndexSearchEngineOption.WindowsIndex; [JsonIgnore] - public IContentIndexProvider ContentIndexProvider => ContentSearchEngine switch - { - ContentIndexSearchEngineOption.Everything => EverythingManagerInstance, - ContentIndexSearchEngineOption.WindowsIndex => WindowsIndexSearchManager, - _ => throw new ArgumentOutOfRangeException(nameof(ContentSearchEngine)) - }; + public IContentIndexProvider ContentIndexProvider => + ContentSearchEngine switch + { + ContentIndexSearchEngineOption.Everything => EverythingManagerInstance, + ContentIndexSearchEngineOption.WindowsIndex => WindowsIndexSearchManager, + _ => throw new ArgumentOutOfRangeException(nameof(ContentSearchEngine)) + }; public enum PathEnumerationEngineOption { [Description("plugin_explorer_engine_windows_index")] WindowsIndex, + [Description("plugin_explorer_engine_everything")] Everything, + [Description("plugin_explorer_path_enumeration_engine_none")] DirectEnumeration } @@ -126,6 +140,7 @@ public enum IndexSearchEngineOption { [Description("plugin_explorer_engine_windows_index")] WindowsIndex, + [Description("plugin_explorer_engine_everything")] Everything, } @@ -134,6 +149,7 @@ public enum ContentIndexSearchEngineOption { [Description("plugin_explorer_engine_windows_index")] WindowsIndex, + [Description("plugin_explorer_engine_everything")] Everything, } @@ -152,9 +168,10 @@ public enum ContentIndexSearchEngineOption public bool EnableEverythingContentSearch { get; set; } = false; - public bool EverythingEnabled => IndexSearchEngine == IndexSearchEngineOption.Everything || - PathEnumerationEngine == PathEnumerationEngineOption.Everything || - ContentSearchEngine == ContentIndexSearchEngineOption.Everything; + public bool EverythingEnabled => + IndexSearchEngine == IndexSearchEngineOption.Everything + || PathEnumerationEngine == PathEnumerationEngineOption.Everything + || ContentSearchEngine == ContentIndexSearchEngineOption.Everything; public bool EverythingSearchFullPath { get; set; } = false; public bool EverythingEnableRunCount { get; set; } = true; @@ -167,47 +184,78 @@ internal enum ActionKeyword PathSearchActionKeyword, FileContentSearchActionKeyword, IndexSearchActionKeyword, - QuickAccessActionKeyword + QuickAccessActionKeyword, + RenameActionKeyword } - internal string GetActionKeyword(ActionKeyword actionKeyword) => actionKeyword switch - { - ActionKeyword.SearchActionKeyword => SearchActionKeyword, - ActionKeyword.PathSearchActionKeyword => PathSearchActionKeyword, - ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword, - ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword, - ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword, - _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyWord property not found") - }; - - internal void SetActionKeyword(ActionKeyword actionKeyword, string keyword) => _ = actionKeyword switch - { - ActionKeyword.SearchActionKeyword => SearchActionKeyword = keyword, - ActionKeyword.PathSearchActionKeyword => PathSearchActionKeyword = keyword, - ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword = keyword, - ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword = keyword, - ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword = keyword, - _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyWord property not found") - }; - - internal bool GetActionKeywordEnabled(ActionKeyword actionKeyword) => actionKeyword switch - { - ActionKeyword.SearchActionKeyword => SearchActionKeywordEnabled, - ActionKeyword.PathSearchActionKeyword => PathSearchKeywordEnabled, - ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled, - ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled, - ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled, - _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyword enabled status not defined") - }; - - internal void SetActionKeywordEnabled(ActionKeyword actionKeyword, bool enable) => _ = actionKeyword switch - { - ActionKeyword.SearchActionKeyword => SearchActionKeywordEnabled = enable, - ActionKeyword.PathSearchActionKeyword => PathSearchKeywordEnabled = enable, - ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled = enable, - ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled = enable, - ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled = enable, - _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyword enabled status not defined") - }; + internal string GetActionKeyword(ActionKeyword actionKeyword) => + actionKeyword switch + { + ActionKeyword.SearchActionKeyword => SearchActionKeyword, + ActionKeyword.PathSearchActionKeyword => PathSearchActionKeyword, + ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword, + ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword, + ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword, + ActionKeyword.RenameActionKeyword => RenameActionKeyword, + _ + => throw new ArgumentOutOfRangeException( + nameof(actionKeyword), + actionKeyword, + "ActionKeyWord property not found" + ) + }; + + internal void SetActionKeyword(ActionKeyword actionKeyword, string keyword) => + _ = actionKeyword switch + { + ActionKeyword.SearchActionKeyword => SearchActionKeyword = keyword, + ActionKeyword.PathSearchActionKeyword => PathSearchActionKeyword = keyword, + ActionKeyword.FileContentSearchActionKeyword + => FileContentSearchActionKeyword = keyword, + ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword = keyword, + ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword = keyword, + ActionKeyword.RenameActionKeyword => RenameActionKeyword = keyword, + _ + => throw new ArgumentOutOfRangeException( + nameof(actionKeyword), + actionKeyword, + "ActionKeyWord property not found" + ) + }; + + internal bool GetActionKeywordEnabled(ActionKeyword actionKeyword) => + actionKeyword switch + { + ActionKeyword.SearchActionKeyword => SearchActionKeywordEnabled, + ActionKeyword.PathSearchActionKeyword => PathSearchKeywordEnabled, + ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled, + ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled, + ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled, + ActionKeyword.RenameActionKeyword => RenameActionKeywordEnabled, + _ + => throw new ArgumentOutOfRangeException( + nameof(actionKeyword), + actionKeyword, + "ActionKeyword enabled status not defined" + ) + }; + + internal void SetActionKeywordEnabled(ActionKeyword actionKeyword, bool enable) => + _ = actionKeyword switch + { + ActionKeyword.SearchActionKeyword => SearchActionKeywordEnabled = enable, + ActionKeyword.PathSearchActionKeyword => PathSearchKeywordEnabled = enable, + ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled = enable, + ActionKeyword.FileContentSearchActionKeyword + => FileContentSearchKeywordEnabled = enable, + ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled = enable, + ActionKeyword.RenameActionKeyword => RenameActionKeywordEnabled = enable, + _ + => throw new ArgumentOutOfRangeException( + nameof(actionKeyword), + actionKeyword, + "ActionKeyword enabled status not defined" + ) + }; } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index 5aa6a13be49..48ea8215529 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -279,7 +279,9 @@ private void InitializeActionKeywordModels() new(Settings.ActionKeyword.IndexSearchActionKeyword, "plugin_explorer_actionkeywordview_indexsearch"), new(Settings.ActionKeyword.QuickAccessActionKeyword, - "plugin_explorer_actionkeywordview_quickaccess") + "plugin_explorer_actionkeywordview_quickaccess"), + new (Settings.ActionKeyword.RenameActionKeyword, + "plugin_explorer_actionkeywordview_rename") }; } From 9eb4e645738e51d14eca03af03a83dd6c83354a6 Mon Sep 17 00:00:00 2001 From: Koisu Date: Fri, 20 Jun 2025 12:10:53 -0700 Subject: [PATCH 006/150] Revert "Fix Typo" This reverts commit 762e1c75c306e9a12ea626db7e810f30fd7183d8. --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index 9cd73527465..bd6067a1cde 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -172,9 +172,9 @@ private static string GetDecimalSeparator() private bool IsBracketComplete(string query) { - var matches = RegBrackets.Matches(query); + var matchs = RegBrackets.Matches(query); var leftBracketCount = 0; - foreach (Match match in matches) + foreach (Match match in matchs) { if (match.Value == "(" || match.Value == "[") { From 3bc06ac6396ac134343a4997ff2f49fed2eb9f01 Mon Sep 17 00:00:00 2001 From: Koisu Date: Fri, 20 Jun 2025 12:10:59 -0700 Subject: [PATCH 007/150] Reapply "Fix Typo" This reverts commit 9eb4e645738e51d14eca03af03a83dd6c83354a6. --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index bd6067a1cde..9cd73527465 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -172,9 +172,9 @@ private static string GetDecimalSeparator() private bool IsBracketComplete(string query) { - var matchs = RegBrackets.Matches(query); + var matches = RegBrackets.Matches(query); var leftBracketCount = 0; - foreach (Match match in matchs) + foreach (Match match in matches) { if (match.Value == "(" || match.Value == "[") { From c116ea24a2612cfbf3cdacf7a740c7c5684e6621 Mon Sep 17 00:00:00 2001 From: Koisu Date: Fri, 20 Jun 2025 12:33:46 -0700 Subject: [PATCH 008/150] added new view --- .../Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml | 9 +++++++++ .../Views/RenameFile.xaml.cs | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml new file mode 100644 index 00000000000..645febe7e4a --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml @@ -0,0 +1,9 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index 8d3b4b3d023..1989280962b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -1,8 +1,119 @@ -using System; +using System.Linq; +using System.Windows; +using System.Windows.Input; +using CommunityToolkit.Mvvm.ComponentModel; -namespace Flow.Launcher.Plugin.Explorer.Views; -public partial class RenameFile +namespace Flow.Launcher.Plugin.Explorer.Views { - + [INotifyPropertyChanged] + public partial class RenameFile : Window + { + + + public string NewFileName + { + get => newFileName; + set + { + _ = SetProperty(ref newFileName, value); + } + } + + + private string newFileName = "gayTest"; + private string renamingText = "fes"; + public string RenamingText + { + get => renamingText; + set + { + _ = SetProperty(ref renamingText, value); + } + } + private readonly IPublicAPI _api; + + + public RenameFile(IPublicAPI api) + { + _api = api; + + + InitializeComponent(); + + RenameTb.Focus(); + Deactivated += (s, e) => + { + DialogResult = false; + Close(); + + }; + ShowInTaskbar = false; + RenameTb.Select(RenameTb.Text.Length, 0); + } + + private void OnDoneButtonClick(object sender, RoutedEventArgs e) + { + + + + + // if (ActionKeyword == Query.GlobalPluginWildcardSign) + // switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled) + // { + // case (Settings.ActionKeyword.FileContentSearchActionKeyword, true): + // _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_globalActionKeywordInvalid")); + // return; + // case (Settings.ActionKeyword.QuickAccessActionKeyword, true): + // _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid")); + // return; + // } + + // if (!KeywordEnabled || !_api.ActionKeywordAssigned(ActionKeyword)) + // { + // DialogResult = true; + // Close(); + // return; + // } + + // The keyword is not valid, so show message + _api.ShowMsgBox("new action keyword"); + } + + private void BtnCancel(object sender, RoutedEventArgs e) + { + Close(); + } + + private void TxtCurrentActionKeyword_OnKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + btnDone.Focus(); + OnDoneButtonClick(sender, e); + e.Handled = true; + } + if (e.Key == Key.Space) + { + e.Handled = true; + } + } + + + private void TextBox_Pasting(object sender, DataObjectPastingEventArgs e) + { + if (e.DataObject.GetDataPresent(DataFormats.Text)) + { + string text = e.DataObject.GetData(DataFormats.Text) as string; + if (!string.IsNullOrEmpty(text) && text.Any(char.IsWhiteSpace)) + { + e.CancelCommand(); + } + } + else + { + e.CancelCommand(); + } + } + } } From 5ae6f971a0eb2c9632537017c81b5ce49da072b5 Mon Sep 17 00:00:00 2001 From: Koisu Date: Sun, 22 Jun 2025 12:41:52 -0700 Subject: [PATCH 014/150] renaming half-works --- .../ContextMenu.cs | 32 +++++-- .../Helper/RenameThing.cs | 55 ++++++++++++ .../Languages/en.xaml | 2 + .../Views/RenameFile.xaml | 4 +- .../Views/RenameFile.xaml.cs | 88 +++++++++++-------- 5 files changed, 133 insertions(+), 48 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index fe4164e7c40..a8497999972 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -11,6 +11,7 @@ using Flow.Launcher.Plugin.Explorer.Helper; using Flow.Launcher.Plugin.Explorer.ViewModels; using Flow.Launcher.Plugin.Explorer.Views; +using System.Windows.Controls; namespace Flow.Launcher.Plugin.Explorer { @@ -192,18 +193,33 @@ public List LoadContextMenus(Result selectedResult) contextMenus.Add(new Result { Title = "Rename", - SubTitle = "Opens a dialogue to this", + SubTitle = "Opens a dialogue to rename this", Action = _ => { - - RenameFile window = new(Context.API); - Context.API.FocusQueryTextBox(); - if (!(window.ShowDialog() ?? false)) + Type T; + RenameFile window; + + + switch (record.Type) { - Context.API.FocusQueryTextBox(); - return false; + case ResultType.Folder: + window = new RenameFile(Context.API, new DirectoryInfo(record.FullPath)); + break; + case ResultType.File: + window = new RenameFile(Context.API, new FileInfo(record.FullPath)); + break; + default: + Context.API.ShowMsgError("Cannot rename this."); + return false; + } - Context.API.FocusQueryTextBox(); + + + + + + window.ShowDialog(); + return false; } }); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs new file mode 100644 index 00000000000..a2e298210fd --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -0,0 +1,55 @@ +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Flow.Launcher.Plugin.Explorer.Helper +{ + public static class RenameThing + { + public static void Rename(this FileSystemInfo info, string newName) + { + if (info is FileInfo) + { + FileInfo file = new FileInfo(info.FullName); + DirectoryInfo directory; + + directory = file.Directory ?? new DirectoryInfo(Path.GetPathRoot(file.FullName)); + if (Path.Join(info.FullName, directory.Name) == newName) + { + throw new NotANewNameException("New name was the same as the old name"); + } + File.Move(info.FullName, Path.Join(directory.FullName, newName)); + return; + } + else if (info is DirectoryInfo) + { + DirectoryInfo directory = new DirectoryInfo(info.FullName); + DirectoryInfo parent; + parent = directory.Parent ?? new DirectoryInfo(Path.GetPathRoot(directory.FullName)); + if (Path.Join(parent.FullName, directory.Name) == newName) + { + throw new NotANewNameException("New name was the same as the old name"); + } + Directory.Move(info.FullName, Path.Join(parent.FullName, newName)); + + } + else + { + throw new ArgumentException($"{nameof(info)} must be either, {nameof(FileInfo)} or {nameof(DirectoryInfo)}"); + } + } + } + [Serializable] + public class NotANewNameException : IOException + { + public NotANewNameException() { } + public NotANewNameException(string message) : base(message) { } + public NotANewNameException(string message, Exception inner) : base(message, inner) { } + protected NotANewNameException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 62f17bf5b29..89c9b2abde1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -195,4 +195,6 @@ New File name: Rename Rename + The given name: {0} was not new. + {0} may not be empty. diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml index c7c81d4861e..592ec284b7c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml @@ -84,8 +84,8 @@ Width="135" HorizontalAlignment="Left" VerticalAlignment="Center" - DataObject.Pasting="TextBox_Pasting" - PreviewKeyDown="TxtCurrentActionKeyword_OnKeyDown" + DataObject.Pasting="RenameTb_Pasting" + PreviewKeyDown="RenameTb_OnKeyDown" Text="{Binding NewFileName}"/> diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index 1989280962b..c38c0905d6a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -1,27 +1,31 @@ +using System; +using System.IO; using System.Linq; using System.Windows; using System.Windows.Input; using CommunityToolkit.Mvvm.ComponentModel; +using Flow.Launcher.Plugin.Explorer.Helper; namespace Flow.Launcher.Plugin.Explorer.Views { + [INotifyPropertyChanged] - public partial class RenameFile : Window + public partial class RenameFile : Window { public string NewFileName { - get => newFileName; + get => _newFileName; set { - _ = SetProperty(ref newFileName, value); + _ = SetProperty(ref _newFileName, value); } } - private string newFileName = "gayTest"; + private string _newFileName; private string renamingText = "fes"; public string RenamingText { @@ -32,9 +36,11 @@ public string RenamingText } } private readonly IPublicAPI _api; + private readonly string _oldFilePath; + private readonly FileSystemInfo _info; - public RenameFile(IPublicAPI api) + public RenameFile(IPublicAPI api, FileSystemInfo info) { _api = api; @@ -42,42 +48,48 @@ public RenameFile(IPublicAPI api) InitializeComponent(); RenameTb.Focus(); - Deactivated += (s, e) => - { - DialogResult = false; - Close(); - - }; + ShowInTaskbar = false; - RenameTb.Select(RenameTb.Text.Length, 0); + + RenameTb.SelectAll(); + _info = info; + _oldFilePath = _info.FullName; + _newFileName = _info.Name; + } private void OnDoneButtonClick(object sender, RoutedEventArgs e) { - - - - - // if (ActionKeyword == Query.GlobalPluginWildcardSign) - // switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled) - // { - // case (Settings.ActionKeyword.FileContentSearchActionKeyword, true): - // _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_globalActionKeywordInvalid")); - // return; - // case (Settings.ActionKeyword.QuickAccessActionKeyword, true): - // _api.ShowMsgBox(_api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid")); - // return; - // } - - // if (!KeywordEnabled || !_api.ActionKeywordAssigned(ActionKeyword)) - // { - // DialogResult = true; - // Close(); - // return; - // } - - // The keyword is not valid, so show message - _api.ShowMsgBox("new action keyword"); + // if it's just whitespace and nothing else + if (_newFileName.Trim() == "") + { + _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_field_may_not_be_empty"), "New file name")); + Show(); + return; + } + if () + try + { + _info.Rename(_newFileName); + } + catch (Exception exception) + { + switch (exception) + { + case FileNotFoundException: + + _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_file_not_found"), _oldFilePath)); + break; + case NotANewNameException notANewNameException: + _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_not_a_new_name"), _newFileName)); + _api.ShowMainWindow(); + break; + default: + _api.ShowMsgError(exception.ToString()); + break; + } + } + Close(); } private void BtnCancel(object sender, RoutedEventArgs e) @@ -85,7 +97,7 @@ private void BtnCancel(object sender, RoutedEventArgs e) Close(); } - private void TxtCurrentActionKeyword_OnKeyDown(object sender, KeyEventArgs e) + private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { @@ -100,7 +112,7 @@ private void TxtCurrentActionKeyword_OnKeyDown(object sender, KeyEventArgs e) } - private void TextBox_Pasting(object sender, DataObjectPastingEventArgs e) + private void RenameTb_Pasting(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(DataFormats.Text)) { From 82823041b2a4f139f5807e41f483dbf84b0beea2 Mon Sep 17 00:00:00 2001 From: Koisu Date: Sun, 22 Jun 2025 13:45:42 -0700 Subject: [PATCH 015/150] basic features of renaming files works --- .../Helper/RenameThing.cs | 44 +++++++++++-- .../Languages/en.xaml | 2 + .../Views/RenameFile.xaml | 1 - .../Views/RenameFile.xaml.cs | 61 +++++++++---------- 4 files changed, 70 insertions(+), 38 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs index a2e298210fd..1ba6c27093f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -13,11 +13,15 @@ public static void Rename(this FileSystemInfo info, string newName) { if (info is FileInfo) { + if (newName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + throw new InvalidNameException(); + } FileInfo file = new FileInfo(info.FullName); DirectoryInfo directory; - + directory = file.Directory ?? new DirectoryInfo(Path.GetPathRoot(file.FullName)); - if (Path.Join(info.FullName, directory.Name) == newName) + if (info.FullName == Path.Join(directory.FullName, newName)) { throw new NotANewNameException("New name was the same as the old name"); } @@ -26,10 +30,18 @@ public static void Rename(this FileSystemInfo info, string newName) } else if (info is DirectoryInfo) { + var invalidChars = Path.GetInvalidPathChars().ToList(); + invalidChars.Add('/'); + invalidChars.Add('\\'); + if (newName.IndexOfAny(invalidChars.ToArray()) >= 0) + { + throw new InvalidNameException(); + } DirectoryInfo directory = new DirectoryInfo(info.FullName); DirectoryInfo parent; parent = directory.Parent ?? new DirectoryInfo(Path.GetPathRoot(directory.FullName)); - if (Path.Join(parent.FullName, directory.Name) == newName) + + if (info.FullName == Path.Join(parent.FullName, newName)) { throw new NotANewNameException("New name was the same as the old name"); } @@ -42,8 +54,8 @@ public static void Rename(this FileSystemInfo info, string newName) } } } - [Serializable] - public class NotANewNameException : IOException + + internal class NotANewNameException : IOException { public NotANewNameException() { } public NotANewNameException(string message) : base(message) { } @@ -52,4 +64,26 @@ protected NotANewNameException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } + internal class FileAlreadyExistsException : IOException + { + public FileAlreadyExistsException() { } + public FileAlreadyExistsException(string message) : base(message) { } + public FileAlreadyExistsException(string message, Exception inner) : base(message, inner) { } + protected FileAlreadyExistsException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } + + internal class InvalidNameException : Exception + { + public InvalidNameException() { } + public InvalidNameException(string message) : base(message) { } + public InvalidNameException(string message, Exception inner) : base(message, inner) { } + protected InvalidNameException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } + + } + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 89c9b2abde1..425655f874e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -197,4 +197,6 @@ Rename The given name: {0} was not new. {0} may not be empty. + {0} is an invalid name. + The specified file: {0} was not found diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml index 592ec284b7c..a69d2e49deb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml @@ -84,7 +84,6 @@ Width="135" HorizontalAlignment="Left" VerticalAlignment="Center" - DataObject.Pasting="RenameTb_Pasting" PreviewKeyDown="RenameTb_OnKeyDown" Text="{Binding NewFileName}"/> diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index c38c0905d6a..deb26ec6c01 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -2,9 +2,11 @@ using System.IO; using System.Linq; using System.Windows; +using System.Windows.Controls; using System.Windows.Input; using CommunityToolkit.Mvvm.ComponentModel; using Flow.Launcher.Plugin.Explorer.Helper; +using Microsoft.VisualBasic.Logging; namespace Flow.Launcher.Plugin.Explorer.Views @@ -26,15 +28,7 @@ public string NewFileName private string _newFileName; - private string renamingText = "fes"; - public string RenamingText - { - get => renamingText; - set - { - _ = SetProperty(ref renamingText, value); - } - } + private readonly IPublicAPI _api; private readonly string _oldFilePath; @@ -48,29 +42,32 @@ public RenameFile(IPublicAPI api, FileSystemInfo info) InitializeComponent(); RenameTb.Focus(); - + ShowInTaskbar = false; RenameTb.SelectAll(); + _info = info; _oldFilePath = _info.FullName; - _newFileName = _info.Name; + NewFileName = _info.Name; + } private void OnDoneButtonClick(object sender, RoutedEventArgs e) { + // if it's just whitespace and nothing else - if (_newFileName.Trim() == "") + _api.LogInfo(nameof(RenameFile),$"THIS IS NEW FILE NAME: {NewFileName}"); + if (NewFileName.Trim() == "" || NewFileName == "") { _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_field_may_not_be_empty"), "New file name")); - Show(); return; } - if () + try { - _info.Rename(_newFileName); + _info.Rename(NewFileName); } catch (Exception exception) { @@ -80,16 +77,30 @@ private void OnDoneButtonClick(object sender, RoutedEventArgs e) _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_file_not_found"), _oldFilePath)); break; - case NotANewNameException notANewNameException: - _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_not_a_new_name"), _newFileName)); + case NotANewNameException: + _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_not_a_new_name"), NewFileName)); _api.ShowMainWindow(); break; + case InvalidNameException: + _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); + break; + case IOException iOException: + if (iOException.Message.Contains("incorrect")) + { + _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); + break; + } + else + { + goto default; + } default: _api.ShowMsgError(exception.ToString()); break; } } Close(); + } private void BtnCancel(object sender, RoutedEventArgs e) @@ -112,20 +123,6 @@ private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) } - private void RenameTb_Pasting(object sender, DataObjectPastingEventArgs e) - { - if (e.DataObject.GetDataPresent(DataFormats.Text)) - { - string text = e.DataObject.GetData(DataFormats.Text) as string; - if (!string.IsNullOrEmpty(text) && text.Any(char.IsWhiteSpace)) - { - e.CancelCommand(); - } - } - else - { - e.CancelCommand(); - } - } + } } From c943982684d643509ef129678e28bf9c98c93352 Mon Sep 17 00:00:00 2001 From: Koisu Date: Sun, 22 Jun 2025 13:58:55 -0700 Subject: [PATCH 016/150] polishing changes --- .../ContextMenu.cs | 22 ++++++++----------- .../Helper/RenameThing.cs | 2 +- .../Languages/en.xaml | 2 ++ .../Views/RenameFile.xaml.cs | 5 ----- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index a8497999972..79d133181fd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -192,14 +192,11 @@ public List LoadContextMenus(Result selectedResult) }); contextMenus.Add(new Result { - Title = "Rename", - SubTitle = "Opens a dialogue to rename this", + Title = Context.API.GetTranslation("plugin_explorer_rename_a_file"), + SubTitle = Context.API.GetTranslation("plugin_explorer_rename_subtitle"), Action = _ => { - Type T; RenameFile window; - - switch (record.Type) { case ResultType.Folder: @@ -209,19 +206,18 @@ public List LoadContextMenus(Result selectedResult) window = new RenameFile(Context.API, new FileInfo(record.FullPath)); break; default: - Context.API.ShowMsgError("Cannot rename this."); + Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_cannot_rename")); return false; - } + window.ShowDialog(); + return false; + }, + // placeholder until real image is found + IcoPath = Constants.ShowContextMenuImagePath, + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue70f") - - - window.ShowDialog(); - - return false; - } }); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs index 1ba6c27093f..a337b2dfbb7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -30,7 +30,7 @@ public static void Rename(this FileSystemInfo info, string newName) } else if (info is DirectoryInfo) { - var invalidChars = Path.GetInvalidPathChars().ToList(); + List invalidChars = Path.GetInvalidPathChars().ToList(); invalidChars.Add('/'); invalidChars.Add('\\'); if (newName.IndexOfAny(invalidChars.ToArray()) >= 0) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 425655f874e..688d177ab93 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -199,4 +199,6 @@ {0} may not be empty. {0} is an invalid name. The specified file: {0} was not found + Open a dialog to rename this. + This cannot be renamed. diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index deb26ec6c01..06801584497 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -115,11 +115,6 @@ private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) btnDone.Focus(); OnDoneButtonClick(sender, e); e.Handled = true; - } - if (e.Key == Key.Space) - { - e.Handled = true; - } } From 502a50b839d8383bde9cc8daf6cefd5ab47c7976 Mon Sep 17 00:00:00 2001 From: Koisu Date: Mon, 23 Jun 2025 15:23:57 -0700 Subject: [PATCH 017/150] added keybind to open the renaming dialog --- .../UserSettings/Settings.cs | 14 ++--- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 17 ++++++ Flow.Launcher/HotkeyControl.xaml.cs | 16 +++++- Flow.Launcher/HotkeyControlDialog.xaml.cs | 7 +++ Flow.Launcher/Languages/en.xaml | 3 ++ Flow.Launcher/MainWindow.xaml | 4 ++ Flow.Launcher/PublicAPIInstance.cs | 22 +++++++- .../Views/SettingsPaneHotkey.xaml | 9 ++++ Flow.Launcher/ViewModel/MainViewModel.cs | 53 +++++++++++++++++++ .../Helper/RenameThing.cs | 19 +++---- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 13 ++++- .../Search/SearchManager.cs | 11 ---- .../Views/RenameFile.xaml.cs | 30 ++++++----- 13 files changed, 173 insertions(+), 45 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 2dbdf0bf8a9..cd74178df5b 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -58,6 +58,7 @@ public void Save() public string OpenHistoryHotkey { get; set; } = $"Ctrl+H"; public string CycleHistoryUpHotkey { get; set; } = $"{KeyConstant.Alt} + Up"; public string CycleHistoryDownHotkey { get; set; } = $"{KeyConstant.Alt} + Down"; + public string RenameFileHotkey { get; set; } = $"F2"; private string _language = Constant.SystemLanguageCode; public string Language @@ -472,13 +473,14 @@ public List RegisteredHotkeys list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); - + if (!string.IsNullOrEmpty(RenameFileHotkey)) + list.Add(new RegisteredHotkeyData(RenameFileHotkey, "RenameFileHotkey", () => RenameFileHotkey = "")); // Custom Query Hotkeys - foreach (var customPluginHotkey in CustomPluginHotkeys) - { - if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey)) - list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = "")); - } + foreach (var customPluginHotkey in CustomPluginHotkeys) + { + if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey)) + list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = "")); + } return list; } diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index f47ee5e11c8..9905d156d6f 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -226,8 +226,25 @@ public interface IPublicAPI /// Query string /// The string that will be compared against the query /// Match results + MatchResult FuzzySearch(string query, string stringToCompare); + /// + /// Returns if the given name is valid file name even if it doesn't exist + /// + /// the name to check + public bool IsValidFileName(string name); + /// + /// Returns if the given name is valid directory name even if it doesn't exist + /// + /// the name to check + public bool IsValidDirectoryName(string name); + /// + /// Open A dialog to rename the given file or folder + /// + /// The directory or file info for the thing to rename. You must check if it actually exists or this will throw an exception + + /// /// Http download the spefic url and return as string /// diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index e8961058cdf..07388034355 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using System.IO; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -110,7 +111,9 @@ public enum HotkeyType SelectPrevItemHotkey, SelectPrevItemHotkey2, SelectNextItemHotkey, - SelectNextItemHotkey2 + SelectNextItemHotkey2, + RenameFileHotkey + } // We can initialize settings in static field because it has been constructed in App constuctor @@ -142,6 +145,8 @@ public string Hotkey HotkeyType.SelectPrevItemHotkey2 => _settings.SelectPrevItemHotkey2, HotkeyType.SelectNextItemHotkey => _settings.SelectNextItemHotkey, HotkeyType.SelectNextItemHotkey2 => _settings.SelectNextItemHotkey2, + HotkeyType.RenameFileHotkey => _settings.RenameFileHotkey, + _ => throw new System.NotImplementedException("Hotkey type not set") }; } @@ -201,6 +206,9 @@ public string Hotkey case HotkeyType.SelectNextItemHotkey2: _settings.SelectNextItemHotkey2 = value; break; + case HotkeyType.RenameFileHotkey: + _settings.RenameFileHotkey = value; + break; default: throw new System.NotImplementedException("Hotkey type not set"); } @@ -231,7 +239,7 @@ private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKey public string EmptyHotkey => App.API.GetTranslation("none"); - public ObservableCollection KeysToDisplay { get; set; } = new(); + public ObservableCollection KeysToDisplay { get; set; } = new ObservableCollection(); public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None); @@ -308,6 +316,7 @@ public void Delete() private void SetKeysToDisplay(HotkeyModel? hotkey) { + KeysToDisplay.Clear(); if (hotkey == null || hotkey == default(HotkeyModel)) @@ -318,8 +327,11 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) { + KeysToDisplay.Add(key); } + + } public void SetHotkey(string? keyStr, bool triggerValidate = true) diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index c7af8c5b8bb..9d6c8a4c37b 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -135,10 +135,12 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) } if (tbMsg == null) + return; if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData) { + var description = string.Format( App.API.GetTranslation(registeredHotkeyData.DescriptionResourceKey), registeredHotkeyData.DescriptionFormatVariables @@ -146,6 +148,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) Alert.Visibility = Visibility.Visible; if (registeredHotkeyData.RemoveHotkey is not null) { + tbMsg.Text = string.Format( App.API.GetTranslation("hotkeyUnavailableEditable"), description @@ -158,6 +161,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) } else { + tbMsg.Text = string.Format( App.API.GetTranslation("hotkeyUnavailableUneditable"), description @@ -175,6 +179,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) if (!CheckHotkeyAvailability(hotkey.Value, true)) { + tbMsg.Text = App.API.GetTranslation("hotkeyUnavailable"); Alert.Visibility = Visibility.Visible; SaveBtn.IsEnabled = false; @@ -182,10 +187,12 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) } else { + Alert.Visibility = Visibility.Collapsed; SaveBtn.IsEnabled = true; SaveBtn.Visibility = Visibility.Visible; } + } private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index bd4cbd28247..29ab4007696 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -555,4 +555,7 @@ File Size Created Last Modified + + + dcdc diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 9ff38a56442..8ea2351de20 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -211,6 +211,10 @@ Key="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}" Command="{Binding ForwardHistoryCommand}" Modifiers="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" /> + diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 6e82032ffe2..382cfee0aa4 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -33,6 +33,7 @@ using JetBrains.Annotations; using Squirrel; using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; +using Windows.Foundation.Metadata; namespace Flow.Launcher { @@ -222,7 +223,26 @@ public async void CopyToClipboard(string stringToCopy, bool directCopy = false, } } } - + public bool IsValidFileName(string name) + { + if (name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 || name.Trim() == "") + { + return false; + } + return true; + } + public bool IsValidDirectoryName(string name) + { + List invalidChars = Path.GetInvalidPathChars().ToList(); + invalidChars.Add('/'); + invalidChars.Add('\\'); + if (name.IndexOfAny(invalidChars.ToArray()) >= 0 || name.Trim() == "") + { + return false; + } + return true; + } + private static async Task RetryActionOnSTAThreadAsync(Action action, int retryCount = 6, int retryDelay = 150) { for (var i = 0; i < retryCount; i++) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml index 89eb2dccdaa..4fb15492fc8 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml @@ -204,6 +204,15 @@ + + + diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 64a39fa6279..6355b61a1c2 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Globalization; +using System.IO; using System.Linq; using System.Text; using System.Threading; @@ -22,6 +23,7 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using Microsoft.VisualStudio.Threading; +using Svg; namespace Flow.Launcher.ViewModel { @@ -60,6 +62,8 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable #endregion + + #region Constructor public MainViewModel() @@ -140,6 +144,9 @@ public MainViewModel() case nameof(Settings.OpenHistoryHotkey): OnPropertyChanged(nameof(OpenHistoryHotkey)); break; + case nameof(Settings.RenameFileHotkey): + OnPropertyChanged(nameof(Settings.RenameFileHotkey)); + break; } }; @@ -596,6 +603,7 @@ public void CopyAlternative() #endregion #region ViewModel Properties + public Settings Settings { get; } public string ClockText { get; private set; } @@ -913,9 +921,53 @@ private static string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotk public string OpenHistoryHotkey => VerifyOrSetDefaultHotkey(Settings.OpenHistoryHotkey, "Ctrl+H"); public string CycleHistoryUpHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryUpHotkey, "Alt+Up"); public string CycleHistoryDownHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryDownHotkey, "Alt+Down"); + public string RenameFileHotkey => VerifyOrSetDefaultHotkey(Settings.RenameFileHotkey, "F2"); + public bool StartWithEnglishMode => Settings.AlwaysStartEn; + #region renamingFiles + private int timesTriedToRenameFileWithExplorerDisabled = 0; + + [RelayCommand] + private void RenameFile() + { + const string explorerPluginID = "572be03c74c642baae319fc283e561a8"; + // check if explorer plugin is enabled + var explorerPluginMatches = App.API.GetAllPlugins().Where(plugin => plugin.Metadata.ID == "572be03c74c642baae319fc283e561a8"); + + if (!explorerPluginMatches.Any()) + { + timesTriedToRenameFileWithExplorerDisabled++; + return; + } + else if (!explorerPluginMatches.Any() && timesTriedToRenameFileWithExplorerDisabled > 3) + { + App.API.ShowMsg("Are you trying to rename a file?", "The explorer plugin needs to be enabled for the hotkey to rename files to work."); + timesTriedToRenameFileWithExplorerDisabled = 0; + } + else + { + dynamic explorerPlugin = explorerPluginMatches.First(); + string path = SelectedResults.SelectedItem.Result.SubTitle; + if (File.Exists(path) || Directory.Exists(path)) + { + explorerPlugin.Plugin.RenameDialog(new FileInfo(path), App.API ); // this feels kinda hacky + return; + } + else if (new DirectoryInfo(path).Parent == null) // check if isn't a root directory like C:\ + { + App.API.ShowMsgError("Cannot rename this."); + return; + } + + + + + } + } + #endregion + #endregion #region Preview @@ -1011,6 +1063,7 @@ private void TogglePreview() _ = ShowPreviewAsync(); } } + private async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true) { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs index a337b2dfbb7..e963b5b4b07 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -9,11 +9,11 @@ namespace Flow.Launcher.Plugin.Explorer.Helper { public static class RenameThing { - public static void Rename(this FileSystemInfo info, string newName) + public static void Rename(this FileSystemInfo info, string newName, IPublicAPI api) { if (info is FileInfo) { - if (newName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + if (!api.IsValidFileName(newName)) { throw new InvalidNameException(); } @@ -30,10 +30,10 @@ public static void Rename(this FileSystemInfo info, string newName) } else if (info is DirectoryInfo) { - List invalidChars = Path.GetInvalidPathChars().ToList(); - invalidChars.Add('/'); - invalidChars.Add('\\'); - if (newName.IndexOfAny(invalidChars.ToArray()) >= 0) + + + + if (!api.IsValidDirectoryName(newName)) { throw new InvalidNameException(); } @@ -47,12 +47,13 @@ public static void Rename(this FileSystemInfo info, string newName) } Directory.Move(info.FullName, Path.Join(parent.FullName, newName)); - } - else + }else { throw new ArgumentException($"{nameof(info)} must be either, {nameof(FileInfo)} or {nameof(DirectoryInfo)}"); } } + + } } internal class NotANewNameException : IOException @@ -85,5 +86,5 @@ protected InvalidNameException( } -} + diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 28382020435..1225711fbe2 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -10,6 +10,8 @@ using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Plugin.Explorer.Exceptions; +using System.Xml; +using System.CodeDom; namespace Flow.Launcher.Plugin.Explorer { @@ -42,11 +44,12 @@ public Task InitAsync(PluginInitContext context) contextMenu = new ContextMenu(Context, Settings, viewModel); searchManager = new SearchManager(Settings, Context); ResultManager.Init(Context, Settings); - + SortOptionTranslationHelper.API = context.API; EverythingApiDllImport.Load(Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "EverythingSDK", Environment.Is64BitProcess ? "x64" : "x86")); + return Task.CompletedTask; } @@ -55,8 +58,10 @@ public List LoadContextMenus(Result selectedResult) return contextMenu.LoadContextMenus(selectedResult); } + public async Task> QueryAsync(Query query, CancellationToken token) { + try { return await searchManager.SearchAsync(query, token); @@ -96,7 +101,10 @@ public string GetTranslatedPluginDescription() { return Context.API.GetTranslation("plugin_explorer_plugin_description"); } - + public void RenameDialog(FileSystemInfo info, IPublicAPI api) + { + new RenameFile(api, info).Show(); + } private void FillQuickAccessLinkNames() { // Legacy version does not have names for quick access links, so we fill them with the path name. @@ -108,5 +116,6 @@ private void FillQuickAccessLinkNames() } } } + } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 28609c86d50..12df6c1458e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -47,16 +47,6 @@ public int GetHashCode(Result obj) internal async Task> SearchAsync(Query query, CancellationToken token) { var results = new HashSet(PathEqualityComparator.Instance); - if (ActionKeywordMatch(query, Settings.ActionKeyword.RenameActionKeyword)) - { - return new List() - { - new Result(){ - Title = "Done", - AutoCompleteText = "test" - } - }; - } // This allows the user to type the below action keywords and see/search the list of quick folder links if (ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword) @@ -161,7 +151,6 @@ private bool ActionKeywordMatch(Query query, Settings.ActionKeyword allowedActio keyword == Settings.IndexSearchActionKeyword, Settings.ActionKeyword.QuickAccessActionKeyword => Settings.QuickAccessKeywordEnabled && keyword == Settings.QuickAccessActionKeyword, - Settings.ActionKeyword.RenameActionKeyword => Settings.RenameActionKeywordEnabled && keyword == Settings.RenameActionKeyword, _ => throw new ArgumentOutOfRangeException(nameof(allowedActionKeyword), allowedActionKeyword, "actionKeyword out of range") }; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index 06801584497..d27ef0e29dd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -11,11 +11,11 @@ namespace Flow.Launcher.Plugin.Explorer.Views { - + [INotifyPropertyChanged] - public partial class RenameFile : Window + public partial class RenameFile : Window { - + public string NewFileName { @@ -28,7 +28,7 @@ public string NewFileName private string _newFileName; - + private readonly IPublicAPI _api; private readonly string _oldFilePath; @@ -47,27 +47,28 @@ public RenameFile(IPublicAPI api, FileSystemInfo info) RenameTb.SelectAll(); + _info = info; _oldFilePath = _info.FullName; NewFileName = _info.Name; - - + + } private void OnDoneButtonClick(object sender, RoutedEventArgs e) { // if it's just whitespace and nothing else - _api.LogInfo(nameof(RenameFile),$"THIS IS NEW FILE NAME: {NewFileName}"); + _api.LogInfo(nameof(RenameFile), $"THIS IS NEW FILE NAME: {NewFileName}"); if (NewFileName.Trim() == "" || NewFileName == "") { _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_field_may_not_be_empty"), "New file name")); return; } - + try { - _info.Rename(NewFileName); + _info.Rename(NewFileName, _api); } catch (Exception exception) { @@ -100,14 +101,14 @@ private void OnDoneButtonClick(object sender, RoutedEventArgs e) } } Close(); - + } private void BtnCancel(object sender, RoutedEventArgs e) { Close(); } - + private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) @@ -115,9 +116,10 @@ private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) btnDone.Focus(); OnDoneButtonClick(sender, e); e.Handled = true; + } + + + } - - - } } From f36ee61de4e08ef7023d4ae7741da16f7b219833 Mon Sep 17 00:00:00 2001 From: Koisu Date: Mon, 23 Jun 2025 17:59:33 -0700 Subject: [PATCH 018/150] feat: :sparkles: Added keybind to rename files --- Flow.Launcher/ViewModel/MainViewModel.cs | 35 +++++++---- .../Helper/RenameThing.cs | 60 ++++++++++++++++++- .../Languages/en.xaml | 1 + .../Search/ResultManager.cs | 1 + .../Flow.Launcher.Plugin.Explorer/Settings.cs | 13 ++-- .../ViewModels/SettingsViewModel.cs | 2 - .../Views/ActionKeywordSetting.xaml.cs | 1 + .../Views/RenameFile.xaml.cs | 44 +------------- 8 files changed, 93 insertions(+), 64 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6355b61a1c2..9b89f486543 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -932,27 +932,40 @@ private static string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotk [RelayCommand] private void RenameFile() { + const string explorerPluginID = "572be03c74c642baae319fc283e561a8"; // check if explorer plugin is enabled - var explorerPluginMatches = App.API.GetAllPlugins().Where(plugin => plugin.Metadata.ID == "572be03c74c642baae319fc283e561a8"); + IEnumerable explorerPluginMatches = App.API.GetAllPlugins().Where( + plugin => plugin.Metadata.ID == explorerPluginID); - if (!explorerPluginMatches.Any()) + if (!explorerPluginMatches.Any() || explorerPluginMatches == null) { + timesTriedToRenameFileWithExplorerDisabled++; return; } - else if (!explorerPluginMatches.Any() && timesTriedToRenameFileWithExplorerDisabled > 3) + else if ((!explorerPluginMatches.Any() || explorerPluginMatches == null) && timesTriedToRenameFileWithExplorerDisabled > 3) { App.API.ShowMsg("Are you trying to rename a file?", "The explorer plugin needs to be enabled for the hotkey to rename files to work."); timesTriedToRenameFileWithExplorerDisabled = 0; + return; } else { - dynamic explorerPlugin = explorerPluginMatches.First(); - string path = SelectedResults.SelectedItem.Result.SubTitle; - if (File.Exists(path) || Directory.Exists(path)) + // at runtime the type of the will be + dynamic explorerPlugin = explorerPluginMatches.First(); // assuming there's only one match + string path = SelectedResults?.SelectedItem?.Result.SubTitle ?? ""; + string name = SelectedResults?.SelectedItem?.Result.Title ?? ""; + if (path.Trim() == "" || name.Trim() == "") return; + if (File.Exists(Path.Join(path, name))) + { + explorerPlugin.Plugin.RenameDialog(new FileInfo(Path.Join(path, name)), App.API); + return; + } + if (Directory.Exists(path)) { - explorerPlugin.Plugin.RenameDialog(new FileInfo(path), App.API ); // this feels kinda hacky + File.AppendAllText("YEE.idi", "YYEEE"); + explorerPlugin.Plugin.RenameDialog(new DirectoryInfo(path), App.API); // this feels kinda hacky return; } else if (new DirectoryInfo(path).Parent == null) // check if isn't a root directory like C:\ @@ -960,10 +973,10 @@ private void RenameFile() App.API.ShowMsgError("Cannot rename this."); return; } - - - - + + + + } } #endregion diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs index e963b5b4b07..4501d433793 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -4,12 +4,13 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using JetBrains.Annotations; namespace Flow.Launcher.Plugin.Explorer.Helper { public static class RenameThing { - public static void Rename(this FileSystemInfo info, string newName, IPublicAPI api) + private static void _rename(this FileSystemInfo info, string newName, IPublicAPI api) { if (info is FileInfo) { @@ -47,13 +48,66 @@ public static void Rename(this FileSystemInfo info, string newName, IPublicAPI a } Directory.Move(info.FullName, Path.Join(parent.FullName, newName)); - }else + } + else { throw new ArgumentException($"{nameof(info)} must be either, {nameof(FileInfo)} or {nameof(DirectoryInfo)}"); } - } } + /// + /// Renames a file system elemnt (directory or file) + /// + /// The requested new name + /// The or representing the old file + /// An instance of so this can create msgboxes + + public static void Rename(string NewFileName, FileSystemInfo oldInfo, IPublicAPI api) + { + // if it's just whitespace and nothing else + if (NewFileName.Trim() == "" || NewFileName == "") + { + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_field_may_not_be_empty"), "New file name")); + return; + } + + try + { + oldInfo._rename(NewFileName, api); + } + catch (Exception exception) + { + switch (exception) + { + case FileNotFoundException: + + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_file_not_found"), oldInfo.FullName)); + return; + case NotANewNameException: + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_not_a_new_name"), NewFileName)); + api.ShowMainWindow(); + return; + case InvalidNameException: + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); + return; + case IOException iOException: + if (iOException.Message.Contains("incorrect")) + { + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); + return; + } + else + { + goto default; + } + default: + api.ShowMsgError(exception.ToString()); + return; + } + } + api.ShowMsg(string.Format(api.GetTranslation("plugin_explorer_successful_rename"), NewFileName)); + } + } } internal class NotANewNameException : IOException diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 688d177ab93..291d28ebab4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -201,4 +201,5 @@ The specified file: {0} was not found Open a dialog to rename this. This cannot be renamed. + Successfully renamed it to: {0} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index e87d2df9791..8c9db37aa71 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -60,6 +60,7 @@ public static string GetAutoCompleteText(string title, Query query, string path, public static Result CreateResult(Query query, SearchResult result) { + return result.Type switch { ResultType.Folder or ResultType.Volume => diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index ac0e5406241..cf380ee003c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,6 +1,7 @@ using System; using System.Collections.ObjectModel; using System.ComponentModel; +using System.IO; using System.Text.Json.Serialization; using Flow.Launcher.Plugin.Everything.Everything; using Flow.Launcher.Plugin.Explorer.Search; @@ -9,6 +10,8 @@ using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.Explorer.ViewModels; +using Flow.Launcher.Plugin.Explorer.Views; + namespace Flow.Launcher.Plugin.Explorer { @@ -185,7 +188,7 @@ internal enum ActionKeyword FileContentSearchActionKeyword, IndexSearchActionKeyword, QuickAccessActionKeyword, - RenameActionKeyword + } internal string GetActionKeyword(ActionKeyword actionKeyword) => @@ -196,7 +199,7 @@ internal string GetActionKeyword(ActionKeyword actionKeyword) => ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword, ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword, ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword, - ActionKeyword.RenameActionKeyword => RenameActionKeyword, + _ => throw new ArgumentOutOfRangeException( nameof(actionKeyword), @@ -214,7 +217,7 @@ internal void SetActionKeyword(ActionKeyword actionKeyword, string keyword) => => FileContentSearchActionKeyword = keyword, ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword = keyword, ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword = keyword, - ActionKeyword.RenameActionKeyword => RenameActionKeyword = keyword, + _ => throw new ArgumentOutOfRangeException( nameof(actionKeyword), @@ -231,7 +234,7 @@ internal bool GetActionKeywordEnabled(ActionKeyword actionKeyword) => ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled, ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled, ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled, - ActionKeyword.RenameActionKeyword => RenameActionKeywordEnabled, + _ => throw new ArgumentOutOfRangeException( nameof(actionKeyword), @@ -249,7 +252,7 @@ internal void SetActionKeywordEnabled(ActionKeyword actionKeyword, bool enable) ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled = enable, ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled = enable, - ActionKeyword.RenameActionKeyword => RenameActionKeywordEnabled = enable, + _ => throw new ArgumentOutOfRangeException( nameof(actionKeyword), diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index 48ea8215529..49e6d41a821 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -280,8 +280,6 @@ private void InitializeActionKeywordModels() "plugin_explorer_actionkeywordview_indexsearch"), new(Settings.ActionKeyword.QuickAccessActionKeyword, "plugin_explorer_actionkeywordview_quickaccess"), - new (Settings.ActionKeyword.RenameActionKeyword, - "plugin_explorer_actionkeywordview_rename") }; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index 829a2feedd3..247ed5779a8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -98,6 +98,7 @@ private void TxtCurrentActionKeyword_OnKeyDown(object sender, KeyEventArgs e) } } + private void TextBox_Pasting(object sender, DataObjectPastingEventArgs e) { if (e.DataObject.GetDataPresent(DataFormats.Text)) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index d27ef0e29dd..4c33be2cfe6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -57,49 +57,7 @@ public RenameFile(IPublicAPI api, FileSystemInfo info) private void OnDoneButtonClick(object sender, RoutedEventArgs e) { - - // if it's just whitespace and nothing else - _api.LogInfo(nameof(RenameFile), $"THIS IS NEW FILE NAME: {NewFileName}"); - if (NewFileName.Trim() == "" || NewFileName == "") - { - _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_field_may_not_be_empty"), "New file name")); - return; - } - - try - { - _info.Rename(NewFileName, _api); - } - catch (Exception exception) - { - switch (exception) - { - case FileNotFoundException: - - _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_file_not_found"), _oldFilePath)); - break; - case NotANewNameException: - _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_not_a_new_name"), NewFileName)); - _api.ShowMainWindow(); - break; - case InvalidNameException: - _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); - break; - case IOException iOException: - if (iOException.Message.Contains("incorrect")) - { - _api.ShowMsgError(string.Format(_api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); - break; - } - else - { - goto default; - } - default: - _api.ShowMsgError(exception.ToString()); - break; - } - } + RenameThing.Rename(NewFileName, _info, _api); Close(); } From e12c2be419d3e89dd5f3b003d63eac1281016226 Mon Sep 17 00:00:00 2001 From: Koisu Date: Mon, 23 Jun 2025 18:57:41 -0700 Subject: [PATCH 019/150] polished ui --- .../Views/RenameFile.xaml | 12 ++++--- .../Views/RenameFile.xaml.cs | 35 +++++++++++++++---- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml index a69d2e49deb..64a8f453876 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml @@ -74,18 +74,20 @@ Orientation="Horizontal"> + Text="{Binding NewFileName}" + GotFocus="SelectAll_OnTextBoxGotFocus" + /> @@ -100,14 +102,14 @@ x:Name="btnCancel" Width="145" Height="30" - Margin="0 0 5 0" + Margin="5 0 5 0" Click="BtnCancel" Content="{DynamicResource cancel}"/> From 94f3746ddc7826af12a853601c74fa04cbd118a4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 20:24:50 +0800 Subject: [PATCH 036/150] Add plugin hotkey model --- Flow.Launcher.Core/Plugin/PluginManager.cs | 15 ++ .../UserSettings/PluginSettings.cs | 106 ++++++++++++++ .../Interfaces/IPluginHotkey.cs | 16 +++ Flow.Launcher.Plugin/PluginHotkey.cs | 129 ++++++++++++++++++ Flow.Launcher.Plugin/PluginMetadata.cs | 5 + 5 files changed, 271 insertions(+) create mode 100644 Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs create mode 100644 Flow.Launcher.Plugin/PluginHotkey.cs diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 9b525f331d2..96787bab598 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -26,6 +26,7 @@ public static class PluginManager 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 @@ public static async Task InitializePluginsAsync() _contextMenuPlugins = GetPluginsForInterface(); _homePlugins = GetPluginsForInterface(); + _hotkeyPlugins = GetPluginsForInterface(); + Settings.UpdatePluginHotkeyInfo(GetPluginHotkeyInfo()); foreach (var plugin in AllPlugins) { @@ -442,6 +445,18 @@ public static bool IsHomePlugin(string id) 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 920abc28426..f9fc1e50969 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -89,6 +89,110 @@ public void UpdatePluginSettings(List metadatas) } } + /// + /// 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 @@ public class Plugin 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 00000000000..a075c461925 --- /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 00000000000..eae080e4958 --- /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 09803cbd7cc..77edb5c7bec 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -152,6 +152,11 @@ internal set /// public string PluginCacheDirectoryPath { get; internal set; } + /// + /// List of registered plugin hotkeys. + /// + public List PluginHotkeys { get; set; } = new List(); + /// /// Convert to string. /// From 5f49c436e7be8c27176c0a3a6e45ce8e084414cc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 20:28:01 +0800 Subject: [PATCH 037/150] Display plugin hotkey setting --- .../Views/SettingsPaneHotkey.xaml | 25 +++++--- .../Views/SettingsPaneHotkey.xaml.cs | 61 ++++++++++++++++++- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml index 74e79e0804c..c145d16a121 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml @@ -204,19 +204,18 @@ - - - + + + - + + h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; + if (hotkey.Editable) + { + // TODO: Check if this can use + var hotkeyControl = new HotkeyControl + { + DefaultHotkey = hotkey.DefaultHotkey, + Hotkey = hotkeySetting, + Type = HotkeyControl.HotkeyType.CustomQueryHotkey, + ValidateKeyGesture = true + }; + card.Content = hotkeyControl; + // TODO: Update metadata & plugin setting hotkey + } + else + { + var hotkeyDisplay = new HotkeyDisplay + { + Keys = hotkeySetting + }; + card.Content = hotkeyDisplay; + } + hotkeyStackPanel.Children.Add(card); + } + excard.Content = hotkeyStackPanel; + PluginHotkeySettings.Children.Add(excard); + } + } } From 605837b4741b21fc4fca00036bd466369b0f88c4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 20:38:35 +0800 Subject: [PATCH 038/150] Add global key registeration --- Flow.Launcher/Helper/HotKeyMapper.cs | 43 ++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index e5fabb3a89f..261b9a0122e 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -1,11 +1,13 @@ -using Flow.Launcher.Infrastructure.Hotkey; +using System; +using ChefKeys; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; -using System; +using Flow.Launcher.Plugin; +using Flow.Launcher.ViewModel; using NHotkey; using NHotkey.Wpf; -using Flow.Launcher.ViewModel; -using ChefKeys; -using CommunityToolkit.Mvvm.DependencyInjection; namespace Flow.Launcher.Helper; @@ -23,6 +25,7 @@ internal static void Initialize() SetHotkey(_settings.Hotkey, OnToggleHotkey); LoadCustomPluginHotkey(); + LoadGlobalPluginHotkey(); } internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) @@ -142,6 +145,36 @@ internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) }); } + internal static void LoadGlobalPluginHotkey() + { + var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo(); + foreach (var info in pluginHotkeyInfos) + { + var pluginPair = info.Key; + var hotkeyInfo = info.Value; + var metadata = pluginPair.Metadata; + foreach (var hotkey in hotkeyInfo) + { + 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); + } + } + } + } + + internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, string hotkeySetting) + { + SetHotkey(hotkeySetting, (s, e) => + { + if (_mainViewModel.ShouldIgnoreHotkeys() || metadata.Disabled) + return; + + globalHotkey.Action?.Invoke(); + }); + } + internal static bool CheckAvailability(HotkeyModel currentHotkey) { try From f841175ccdfd9adb7ad2cd9fa11c110f69e55a2c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 21:37:02 +0800 Subject: [PATCH 039/150] Fix log message issue --- Flow.Launcher/Helper/HotKeyMapper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 261b9a0122e..de1933520ae 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -56,7 +56,7 @@ private static void SetWithChefKeys(string hotkeyStr) catch (Exception e) { App.API.LogError(ClassName, - string.Format("|HotkeyMapper.SetWithChefKeys|Error registering hotkey: {0} \nStackTrace:{1}", + string.Format("Error registering hotkey: {0} \nStackTrace:{1}", e.Message, e.StackTrace)); string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr); @@ -81,7 +81,7 @@ internal static void SetHotkey(HotkeyModel hotkey, EventHandler catch (Exception e) { App.API.LogError(ClassName, - string.Format("|HotkeyMapper.SetHotkey|Error registering hotkey {2}: {0} \nStackTrace:{1}", + string.Format("Error registering hotkey {2}: {0} \nStackTrace:{1}", e.Message, e.StackTrace, hotkeyStr)); @@ -107,7 +107,7 @@ internal static void RemoveHotkey(string hotkeyStr) catch (Exception e) { App.API.LogError(ClassName, - string.Format("|HotkeyMapper.RemoveHotkey|Error removing hotkey: {0} \nStackTrace:{1}", + string.Format("Error removing hotkey: {0} \nStackTrace:{1}", e.Message, e.StackTrace)); string errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr); From 8abd5318b36880b7aa036c19edb0cc6b4d9c7034 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 21:39:33 +0800 Subject: [PATCH 040/150] Add window key registeration --- Flow.Launcher.Core/Plugin/PluginManager.cs | 39 ++++++++- Flow.Launcher/Helper/HotKeyMapper.cs | 97 ++++++++++++++++++++++ Flow.Launcher/Languages/en.xaml | 2 + 3 files changed, 137 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 96787bab598..6620ba578fb 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -6,6 +6,7 @@ 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; @@ -39,6 +40,7 @@ public static class PluginManager private static PluginsSettings Settings; private static List _metadatas; private static readonly List _modifiedPlugins = new(); + private static readonly Dictionary> _windowPluginHotkeys = new(); /// /// Directories that will hold Flow Launcher plugin directory @@ -252,7 +254,9 @@ public static async Task InitializePluginsAsync() _contextMenuPlugins = GetPluginsForInterface(); _homePlugins = GetPluginsForInterface(); _hotkeyPlugins = GetPluginsForInterface(); - Settings.UpdatePluginHotkeyInfo(GetPluginHotkeyInfo()); + var pluginHotkeyInfo = GetPluginHotkeyInfo(); + Settings.UpdatePluginHotkeyInfo(pluginHotkeyInfo); + InitializeWindowPluginHotkeys(pluginHotkeyInfo); foreach (var plugin in AllPlugins) { @@ -457,6 +461,39 @@ public static Dictionary> GetPluginHotkeyInfo return hotkeyPluginInfos; } + public static Dictionary> GetWindowPluginHotkeys() + { + return _windowPluginHotkeys; + } + + private static void InitializeWindowPluginHotkeys(Dictionary> pluginHotkeyInfo) + { + foreach (var info in pluginHotkeyInfo) + { + 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 converter = new KeyGestureConverter(); + var keyGesture = (KeyGesture)converter.ConvertFromString(hotkeySetting); + if (keyGesture != null) + { + if (!_windowPluginHotkeys.TryGetValue(keyGesture, out var list)) + { + list = new List<(PluginMetadata, SearchWindowPluginHotkey)>(); + _windowPluginHotkeys[keyGesture] = list; + } + list.Add((pluginPair.Metadata, searchWindowHotkey)); + } + } + } + } + } + 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 de1933520ae..8a9c07d9a08 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -1,6 +1,11 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Input; using ChefKeys; using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; @@ -26,6 +31,7 @@ internal static void Initialize() SetHotkey(_settings.Hotkey, OnToggleHotkey); LoadCustomPluginHotkey(); LoadGlobalPluginHotkey(); + LoadWindowPluginHotkey(); } internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) @@ -175,6 +181,97 @@ internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, Plug }); } + internal static void LoadWindowPluginHotkey() + { + var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(); + foreach (var hotkey in windowPluginHotkeys) + { + var keyGesture = hotkey.Key; + SetWindowHotkey(keyGesture, hotkey.Value); + } + } + + private static void SetWindowHotkey(KeyGesture keyGesture, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) + { + try + { + if (Application.Current?.MainWindow is MainWindow window) + { + var command = BuildCommand(hotkeyModels); + + // Remove any existing key binding with the same gesture to avoid duplication + var existingBinding = window.InputBindings + .OfType() + .FirstOrDefault(kb => kb.Gesture == keyGesture); + + if (existingBinding != null) + { + throw new InvalidOperationException($"Key binding with gesture {keyGesture} already exists"); + } + + // Create and add the new key binding + var keyBinding = new KeyBinding(command, keyGesture); + window.InputBindings.Add(keyBinding); + } + } + catch (Exception e) + { + App.API.LogError(ClassName, + 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); + string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } + } + + private static ICommand BuildCommand(List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) + { + return new RelayCommand(() => + { + foreach (var hotkeyModel in hotkeyModels) + { + var metadata = hotkeyModel.Metadata; + if (metadata.Disabled) + continue; + + var pluginHotkey = hotkeyModel.PluginHotkey; + if (pluginHotkey.Action?.Invoke(_mainViewModel.Results.SelectedItem?.Result) ?? false) + App.API.HideMainWindow(); + } + }); + } + + internal static void RemoveWindowHotkey(KeyGesture keyGesture) + { + try + { + if (Application.Current?.MainWindow is MainWindow window) + { + // Find and remove the key binding with the specified gesture + var existingBinding = window.InputBindings + .OfType() + .FirstOrDefault(kb => kb.Gesture == keyGesture); + if (existingBinding != null) + { + window.InputBindings.Remove(existingBinding); + } + } + } + catch (Exception e) + { + App.API.LogError(ClassName, + string.Format("Error removing window hotkey: {0} \nStackTrace:{1}", + e.Message, + e.StackTrace)); + string errorMsg = string.Format(App.API.GetTranslation("unregisterWindowHotkeyFailed"), keyGesture.DisplayString); + string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } + } + internal static bool CheckAvailability(HotkeyModel currentHotkey) { try diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 3769adbe0b6..365215307a5 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -21,6 +21,8 @@ Failed to register hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. Failed to unregister hotkey "{0}". Please try again or see log for details + Failed to register window hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. + Failed to unregister window hotkey "{0}". Please try again or see log for details Flow Launcher Could not start {0} Invalid Flow Launcher plugin file format From 4d77bad83d697d29f7cef906ae62704d9fe56c17 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 21:48:20 +0800 Subject: [PATCH 041/150] Sort hotkey info list --- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index d888a642480..d04b482990a 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System.Linq; +using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using CommunityToolkit.Mvvm.DependencyInjection; @@ -50,6 +51,8 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) { Orientation = Orientation.Vertical }; + + var sortedHotkeyInfo = hotkeyInfo.OrderBy(h => h.Id).ToList(); foreach (var hotkey in hotkeyInfo) { var card = new Card() From 8ed495dc84d6ad305021beaeea5d1308c5e5b47a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 22:04:40 +0800 Subject: [PATCH 042/150] Improve code quality --- .../Helper/RenameThing.cs | 222 +++++++++--------- .../Views/RenameFile.xaml.cs | 27 +-- 2 files changed, 113 insertions(+), 136 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs index f6712453f4a..7743bc5275c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -1,140 +1,136 @@ -using System; +using System; using System.IO; +using System.Runtime.Serialization; -namespace Flow.Launcher.Plugin.Explorer.Helper +namespace Flow.Launcher.Plugin.Explorer.Helper; + +public static class RenameThing { - public static class RenameThing + private static void Rename(this FileSystemInfo info, string newName) { - private static void _rename(this FileSystemInfo info, string newName, IPublicAPI api) + if (info is FileInfo file) { - if (info is FileInfo) + if (!SharedCommands.FilesFolders.IsValidFileName(newName)) { - if (!SharedCommands.FilesFolders.IsValidFileName(newName)) - { - throw new InvalidNameException(); - } - FileInfo file = (FileInfo)info; - DirectoryInfo directory; - - directory = file.Directory ?? new DirectoryInfo(Path.GetPathRoot(file.FullName)); - string newPath = Path.Join(directory.FullName, newName); - if (info.FullName == newPath) - { - throw new NotANewNameException("New name was the same as the old name"); - } - if (File.Exists(newPath)) throw new ElementAlreadyExistsException(); - File.Move(info.FullName, newPath); - return; + throw new InvalidNameException(); } - else if (info is DirectoryInfo) + DirectoryInfo directory; + var rootPath = Path.GetPathRoot(file.FullName); + if (string.IsNullOrEmpty(rootPath)) return; + directory = file.Directory ?? new DirectoryInfo(rootPath); + string newPath = Path.Join(directory.FullName, newName); + if (info.FullName == newPath) { - if (!SharedCommands.FilesFolders.IsValidDirectoryName(newName)) - { - throw new InvalidNameException(); - } - DirectoryInfo directory = (DirectoryInfo)info; - DirectoryInfo parent; - parent = directory.Parent ?? new DirectoryInfo(Path.GetPathRoot(directory.FullName)); - string newPath = Path.Join(parent.FullName, newName); - if (info.FullName == newPath) - { - throw new NotANewNameException("New name was the same as the old name"); - } - if (Directory.Exists(newPath)) throw new ElementAlreadyExistsException(); - - Directory.Move(info.FullName, newPath); - + throw new NotANewNameException("New name was the same as the old name"); } - else - { - throw new ArgumentException($"{nameof(info)} must be either, {nameof(FileInfo)} or {nameof(DirectoryInfo)}"); - } - + if (File.Exists(newPath)) throw new ElementAlreadyExistsException(); + File.Move(info.FullName, newPath); + return; } - /// - /// Renames a file system element (directory or file) - /// - /// The requested new name - /// The or representing the old file - /// An instance of so this can create msgboxes - - public static void Rename(string NewFileName, FileSystemInfo oldInfo, IPublicAPI api) + else if (info is DirectoryInfo directory) { - // if it's just whitespace and nothing else - if (NewFileName.Trim() == "" || NewFileName == "") + if (!SharedCommands.FilesFolders.IsValidDirectoryName(newName)) { - api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_field_may_not_be_empty"), "New file name")); - return; + throw new InvalidNameException(); } - - try + DirectoryInfo parent; + var rootPath = Path.GetPathRoot(directory.FullName); + if (string.IsNullOrEmpty(rootPath)) return; + parent = directory.Parent ?? new DirectoryInfo(rootPath); + string newPath = Path.Join(parent.FullName, newName); + if (info.FullName == newPath) { - oldInfo._rename(NewFileName, api); + throw new NotANewNameException("New name was the same as the old name"); } - catch (Exception exception) + if (Directory.Exists(newPath)) throw new ElementAlreadyExistsException(); + + Directory.Move(info.FullName, newPath); + + } + else + { + throw new ArgumentException($"{nameof(info)} must be either, {nameof(FileInfo)} or {nameof(DirectoryInfo)}"); + } + } + + /// + /// Renames a file system element (directory or file) + /// + /// The requested new name + /// The or representing the old file + /// An instance of so this can create msgboxes + public static void Rename(string NewFileName, FileSystemInfo oldInfo, IPublicAPI api) + { + // if it's just whitespace and nothing else + if (NewFileName.Trim() == "" || NewFileName == "") + { + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_field_may_not_be_empty"), "New file name")); + return; + } + + try + { + oldInfo.Rename(NewFileName); + } + catch (Exception exception) + { + switch (exception) { - switch (exception) - { - case FileNotFoundException: - api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_file_not_found"), oldInfo.FullName)); - return; - case NotANewNameException: - api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_not_a_new_name"), NewFileName)); + case FileNotFoundException: + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_file_not_found"), oldInfo.FullName)); + return; + case NotANewNameException: + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_not_a_new_name"), NewFileName)); + return; + case InvalidNameException: + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); + return; + case ElementAlreadyExistsException: + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_element_already_exists"), NewFileName)); + break; + default: + string msg = exception.Message; + if (!string.IsNullOrEmpty(msg)) + { + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_exception"), exception.Message)); return; - case InvalidNameException: - api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_invalid_name"), NewFileName)); - return; - case ElementAlreadyExistsException: - api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_element_already_exists"), NewFileName)); - break; - default: - string msg = exception.Message; - if (!string.IsNullOrEmpty(msg)) - { - api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_exception"), exception.Message)); - return; - } - else - { - api.ShowMsgError(api.GetTranslation("plugin_explorer_no_reason_given_exception")); - } + } + else + { + api.ShowMsgError(api.GetTranslation("plugin_explorer_no_reason_given_exception")); + } - return; - } + return; } - api.ShowMsg(string.Format(api.GetTranslation("plugin_explorer_successful_rename"), NewFileName)); - } } + api.ShowMsg(string.Format(api.GetTranslation("plugin_explorer_successful_rename"), NewFileName)); } +} - internal class NotANewNameException : IOException - { - public NotANewNameException() { } - public NotANewNameException(string message) : base(message) { } - public NotANewNameException(string message, Exception inner) : base(message, inner) { } - protected NotANewNameException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - } - internal class ElementAlreadyExistsException : IOException { - public ElementAlreadyExistsException() { } - public ElementAlreadyExistsException(string message) : base(message) { } - public ElementAlreadyExistsException(string message, Exception inner) : base(message, inner) { } - protected ElementAlreadyExistsException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - } +internal class NotANewNameException : IOException +{ + public NotANewNameException() { } + public NotANewNameException(string message) : base(message) { } + public NotANewNameException(string message, Exception inner) : base(message, inner) { } + protected NotANewNameException( + SerializationInfo info, + StreamingContext context) : base(info, context) { } +} + internal class ElementAlreadyExistsException : IOException { + public ElementAlreadyExistsException() { } + public ElementAlreadyExistsException(string message) : base(message) { } + public ElementAlreadyExistsException(string message, Exception inner) : base(message, inner) { } + protected ElementAlreadyExistsException( + SerializationInfo info, + StreamingContext context) : base(info, context) { } +} - internal class InvalidNameException : Exception - { +internal class InvalidNameException : Exception +{ public InvalidNameException() { } public InvalidNameException(string message) : base(message) { } public InvalidNameException(string message, Exception inner) : base(message, inner) { } protected InvalidNameException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - } - - - - + SerializationInfo info, + StreamingContext context) : base(info, context) { } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index 3e12fd4cdaf..4591fa76e66 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -7,15 +7,11 @@ using CommunityToolkit.Mvvm.ComponentModel; using Flow.Launcher.Plugin.Explorer.Helper; - namespace Flow.Launcher.Plugin.Explorer.Views { - [INotifyPropertyChanged] public partial class RenameFile : Window { - - public string NewFileName { get => _newFileName; @@ -25,7 +21,6 @@ public string NewFileName } } - private string _newFileName; private readonly IPublicAPI _api; @@ -42,25 +37,16 @@ public RenameFile(IPublicAPI api, FileSystemInfo info) InitializeComponent(); - - ShowInTaskbar = false; - - - RenameTb.Focus(); - - - } + /// /// https://stackoverflow.com/a/59560352/24045055 /// - private async void SelectAll_OnTextBoxGotFocus(object sender, RoutedEventArgs e) { - - var textBox = sender as TextBox; + if (sender is not TextBox textBox) return; if (_info is DirectoryInfo) { await Application.Current.Dispatcher.InvokeAsync(textBox.SelectAll, DispatcherPriority.Background); @@ -70,15 +56,13 @@ private async void SelectAll_OnTextBoxGotFocus(object sender, RoutedEventArgs e) { string properName = Path.GetFileNameWithoutExtension(info.Name); Application.Current.Dispatcher.Invoke(textBox.Select, DispatcherPriority.Background, textBox.Text.IndexOf(properName), properName.Length ); - } - } + private void OnDoneButtonClick(object sender, RoutedEventArgs e) { RenameThing.Rename(NewFileName, _info, _api); Close(); - } private void BtnCancel(object sender, RoutedEventArgs e) @@ -94,9 +78,6 @@ private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) OnDoneButtonClick(sender, e); e.Handled = true; } - - - } } } From f7fa647da3e63b1bb85b3769c2db22cabcff7ca4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 22:11:42 +0800 Subject: [PATCH 043/150] Use IPluginHotkey for explorer plugin --- .../UserSettings/Settings.cs | 14 +- Flow.Launcher/HotkeyControl.xaml.cs | 14 +- Flow.Launcher/Languages/en.xaml | 5 - Flow.Launcher/MainWindow.xaml | 4 - Flow.Launcher/PublicAPIInstance.cs | 2 +- .../Views/SettingsPaneHotkey.xaml | 9 - Flow.Launcher/ViewModel/MainViewModel.cs | 76 -------- .../Languages/en.xaml | 1 + Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 172 +++++++++++++++--- .../Search/ResultManager.cs | 61 +------ 10 files changed, 161 insertions(+), 197 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index cd74178df5b..2dbdf0bf8a9 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -58,7 +58,6 @@ public void Save() public string OpenHistoryHotkey { get; set; } = $"Ctrl+H"; public string CycleHistoryUpHotkey { get; set; } = $"{KeyConstant.Alt} + Up"; public string CycleHistoryDownHotkey { get; set; } = $"{KeyConstant.Alt} + Down"; - public string RenameFileHotkey { get; set; } = $"F2"; private string _language = Constant.SystemLanguageCode; public string Language @@ -473,14 +472,13 @@ public List RegisteredHotkeys list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); - if (!string.IsNullOrEmpty(RenameFileHotkey)) - list.Add(new RegisteredHotkeyData(RenameFileHotkey, "RenameFileHotkey", () => RenameFileHotkey = "")); + // Custom Query Hotkeys - foreach (var customPluginHotkey in CustomPluginHotkeys) - { - if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey)) - list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = "")); - } + foreach (var customPluginHotkey in CustomPluginHotkeys) + { + if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey)) + list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = "")); + } return list; } diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 07388034355..33316938ccb 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -111,9 +111,7 @@ public enum HotkeyType SelectPrevItemHotkey, SelectPrevItemHotkey2, SelectNextItemHotkey, - SelectNextItemHotkey2, - RenameFileHotkey - + SelectNextItemHotkey2 } // We can initialize settings in static field because it has been constructed in App constuctor @@ -145,8 +143,6 @@ public string Hotkey HotkeyType.SelectPrevItemHotkey2 => _settings.SelectPrevItemHotkey2, HotkeyType.SelectNextItemHotkey => _settings.SelectNextItemHotkey, HotkeyType.SelectNextItemHotkey2 => _settings.SelectNextItemHotkey2, - HotkeyType.RenameFileHotkey => _settings.RenameFileHotkey, - _ => throw new System.NotImplementedException("Hotkey type not set") }; } @@ -206,9 +202,6 @@ public string Hotkey case HotkeyType.SelectNextItemHotkey2: _settings.SelectNextItemHotkey2 = value; break; - case HotkeyType.RenameFileHotkey: - _settings.RenameFileHotkey = value; - break; default: throw new System.NotImplementedException("Hotkey type not set"); } @@ -239,7 +232,7 @@ private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKey public string EmptyHotkey => App.API.GetTranslation("none"); - public ObservableCollection KeysToDisplay { get; set; } = new ObservableCollection(); + public ObservableCollection KeysToDisplay { get; set; } = new(); public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None); @@ -316,7 +309,6 @@ public void Delete() private void SetKeysToDisplay(HotkeyModel? hotkey) { - KeysToDisplay.Clear(); if (hotkey == null || hotkey == default(HotkeyModel)) @@ -330,8 +322,6 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) KeysToDisplay.Add(key); } - - } public void SetHotkey(string? keyStr, bool triggerValidate = true) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 365215307a5..55bbed7e087 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -557,9 +557,4 @@ File Size Created Last Modified - - - Rename a file/directory - Are you trying to rename a file? - The explorer plugin needs to be enabled for the hotkey to rename files to work. diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 8ea2351de20..9ff38a56442 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -211,10 +211,6 @@ Key="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='key'}" Command="{Binding ForwardHistoryCommand}" Modifiers="{Binding CycleHistoryDownHotkey, Converter={StaticResource StringToKeyBindingConverter}, ConverterParameter='modifiers'}" /> - diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 875a54f804e..6e82032ffe2 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -222,7 +222,7 @@ public async void CopyToClipboard(string stringToCopy, bool directCopy = false, } } } - + private static async Task RetryActionOnSTAThreadAsync(Action action, int retryCount = 6, int retryDelay = 150) { for (var i = 0; i < retryCount; i++) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml index c145d16a121..fd3d415cc80 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml @@ -204,15 +204,6 @@ - - - diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ff596352aa2..64a39fa6279 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.Globalization; -using System.IO; using System.Linq; using System.Text; using System.Threading; @@ -24,7 +23,6 @@ using Flow.Launcher.Storage; using Microsoft.VisualStudio.Threading; - namespace Flow.Launcher.ViewModel { public partial class MainViewModel : BaseModel, ISavable, IDisposable @@ -62,8 +60,6 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable #endregion - - #region Constructor public MainViewModel() @@ -144,9 +140,6 @@ public MainViewModel() case nameof(Settings.OpenHistoryHotkey): OnPropertyChanged(nameof(OpenHistoryHotkey)); break; - case nameof(Settings.RenameFileHotkey): - OnPropertyChanged(nameof(RenameFileHotkey)); - break; } }; @@ -603,7 +596,6 @@ public void CopyAlternative() #endregion #region ViewModel Properties - public Settings Settings { get; } public string ClockText { get; private set; } @@ -921,76 +913,9 @@ private static string VerifyOrSetDefaultHotkey(string hotkey, string defaultHotk public string OpenHistoryHotkey => VerifyOrSetDefaultHotkey(Settings.OpenHistoryHotkey, "Ctrl+H"); public string CycleHistoryUpHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryUpHotkey, "Alt+Up"); public string CycleHistoryDownHotkey => VerifyOrSetDefaultHotkey(Settings.CycleHistoryDownHotkey, "Alt+Down"); - public string RenameFileHotkey => VerifyOrSetDefaultHotkey(Settings.RenameFileHotkey, "F2"); - public bool StartWithEnglishMode => Settings.AlwaysStartEn; - #region renamingFiles - private int timesTriedToRenameFileWithExplorerDisabled = 0; - - [RelayCommand] - private void RenameFile() - { - // at runtime this is an instance the Flow.Launcher.Plugin.Explorer.Main - var explorerPlugin = GetExplorerPlugin(); - - if (!(explorerPlugin != null)) - { - timesTriedToRenameFileWithExplorerDisabled++; - if (timesTriedToRenameFileWithExplorerDisabled > 3) - { - App.API.ShowMsg(App.API.GetTranslation("AreTryingToRenameFile"), App.API.GetTranslation("ExplorerNeedsEnabledForRenameFile")); - timesTriedToRenameFileWithExplorerDisabled = 0; - } - return; - - } - else - { - string path = SelectedResults?.SelectedItem?.Result.SubTitle ?? ""; - string name = SelectedResults?.SelectedItem?.Result.Title ?? ""; - if (string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(name)) return; - ShowRenamingDialog(explorerPlugin, path, name); - } - } - /// - /// Get an instance of the explorer plugin if it's loaded - /// - /// Returns an instance of Flow.Launcher.Plugin.Explorer.Main, if it's not loaded this returns null. - private IAsyncPlugin GetExplorerPlugin() - { - const string explorerPluginID = "572be03c74c642baae319fc283e561a8"; - IEnumerable explorerPluginMatches = App.API.GetAllPlugins().Where( - plugin => plugin.Metadata.ID == explorerPluginID && plugin.Metadata.Disabled != true) ; - if (explorerPluginMatches.Any()) - { - // assuming it's the first plugin as no 2 plugins can be loaded with the same ID - return explorerPluginMatches.First().Plugin; - } - return null; - } - /// - /// Shows the dialog to rename a file system element. - /// - /// An instance of the Flow.Launcher.Plugin.Explorer.Main, which is invisible in the current namespace - /// The path of the element - /// The new name - private void ShowRenamingDialog(dynamic explorerPlugin, string path, string name) - { - if (File.Exists(Path.Join(path, name))) - { - explorerPlugin.RenameDialog(new FileInfo(Path.Join(path, name)), App.API); - return; - } - if (Directory.Exists(path)) - { - explorerPlugin.RenameDialog(new DirectoryInfo(path), App.API); - return; - } - } - #endregion - #endregion #region Preview @@ -1086,7 +1011,6 @@ private void TogglePreview() _ = ShowPreviewAsync(); } } - private async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true) { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 95b25079746..64ced20a9b7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -130,6 +130,7 @@ Show Windows Context Menu Open With Select a program to open with + Run As Administrator {0} free of {1} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index afd42c8d7e6..6f230edabf1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,24 +1,26 @@ -using Flow.Launcher.Plugin.Explorer.Helper; -using Flow.Launcher.Plugin.Explorer.Search; -using Flow.Launcher.Plugin.Explorer.Search.Everything; -using Flow.Launcher.Plugin.Explorer.ViewModels; -using Flow.Launcher.Plugin.Explorer.Views; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Plugin.Explorer.Exceptions; +using Flow.Launcher.Plugin.Explorer.Helper; +using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.Everything; +using Flow.Launcher.Plugin.Explorer.ViewModels; +using Flow.Launcher.Plugin.Explorer.Views; namespace Flow.Launcher.Plugin.Explorer { - public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n + public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IPluginHotkey { internal static PluginInitContext Context { get; set; } internal Settings Settings; + private static readonly string ClassName = nameof(Main); + private SettingsViewModel viewModel; private IContextMenu contextMenu; @@ -56,10 +58,8 @@ public List LoadContextMenus(Result selectedResult) return contextMenu.LoadContextMenus(selectedResult); } - public async Task> QueryAsync(Query query, CancellationToken token) { - try { return await searchManager.SearchAsync(query, token); @@ -99,21 +99,7 @@ public string GetTranslatedPluginDescription() { return Context.API.GetTranslation("plugin_explorer_plugin_description"); } - public void RenameDialog(FileSystemInfo info, IPublicAPI api) - { - if (info == null) throw new ArgumentNullException(nameof(info)); - if (api == null) throw new ArgumentNullException(nameof(api)); - try - { - new RenameFile(api, info).ShowDialog(); - } - catch (Exception ex) - { - api.ShowMsgError(api.GetTranslation("errorTitle"), api.GetTranslation("plugin_explorer_failed_to_open_rename_dialog")); - api.LogException(nameof(Main), $"Failed to open rename dialog: {ex.Message}", ex, nameof(RenameDialog)); - } - } private void FillQuickAccessLinkNames() { // Legacy version does not have names for quick access links, so we fill them with the path name. @@ -125,6 +111,144 @@ private void FillQuickAccessLinkNames() } } } - + + public List GetPuginHotkeys() + { + return new List + { + new SearchWindowPluginHotkey() + { + Id = 0, + Name = Context.API.GetTranslation("plugin_explorer_opencontainingfolder"), + Description = Context.API.GetTranslation("plugin_explorer_opencontainingfolder_subtitle"), + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue838"), + DefaultHotkey = "Ctrl+Enter", + Editable = false, + Action = (r) => + { + if (r.ContextData is SearchResult record) + { + if (record.Type is ResultType.File) + { + ResultManager.OpenFolder(record.FullPath, record.FullPath); + } + else + { + try + { + Context.API.OpenDirectory(Path.GetDirectoryName(record.FullPath), record.FullPath); + } + catch (Exception e) + { + var message = $"Fail to open file at {record.FullPath}"; + Context.API.LogException(ClassName, message, e); + Context.API.ShowMsgBox(e.Message, Context.API.GetTranslation("plugin_explorer_opendir_error")); + return false; + } + + return true; + } + } + + return false; + } + }, + new SearchWindowPluginHotkey() + { + Id = 1, + Name = Context.API.GetTranslation("plugin_explorer_show_contextmenu_title"), + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue700"), + DefaultHotkey = "Alt+Enter", + Editable = false, + Action = (r) => + { + if (r.ContextData is SearchResult record && record.Type is not ResultType.Volume) + { + try + { + ResultManager.ShowNativeContextMenu(record.FullPath, record.Type); + } + catch (Exception e) + { + var message = $"Fail to show context menu for {record.FullPath}"; + Context.API.LogException(ClassName, message, e); + } + } + + return false; + } + }, + new SearchWindowPluginHotkey() + { + Id = 2, + Name = Context.API.GetTranslation("plugin_explorer_run_as_administrator"), + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE7EF"), + DefaultHotkey = "Ctrl+Shift+Enter", + Editable = false, + Action = (r) => + { + if (r.ContextData is SearchResult record) + { + if (record.Type is ResultType.File) + { + var filePath = record.FullPath; + ResultManager.OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty, true); + } + else + { + try + { + ResultManager.OpenFolder(record.FullPath); + return true; + } + catch (Exception ex) + { + var message = $"Fail to open file at {record.FullPath}"; + Context.API.LogException(ClassName, message, ex); + Context.API.ShowMsgBox(ex.Message, Context.API.GetTranslation("plugin_explorer_opendir_error")); + return false; + } + } + return true; + } + + return false; + } + }, + new SearchWindowPluginHotkey() + { + Id = 3, + Name = Context.API.GetTranslation("plugin_explorer_rename_a_file"), + Description = Context.API.GetTranslation("plugin_explorer_rename_subtitle"), + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8ac"), + DefaultHotkey = "F2", + Editable = true, + Action = (r) => + { + if (r.ContextData is SearchResult record) + { + RenameFile window; + switch (record.Type) + { + case ResultType.Folder: + window = new RenameFile(Context.API, new DirectoryInfo(record.FullPath)); + break; + case ResultType.File: + window = new RenameFile(Context.API, new FileInfo(record.FullPath)); + break; + default: + Context.API.ShowMsgError(Context.API.GetTranslation("plugin_explorer_cannot_rename")); + return false; + } + window.ShowDialog(); + + return false; + } + + return false; + } + } + }; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index e87d2df9791..83195a47fc1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -108,40 +108,6 @@ internal static Result CreateFolderResult(string title, string subtitle, string PreviewPanel = new Lazy(() => new PreviewPanel(Settings, path, ResultType.Folder)), Action = c => { - if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt) - { - ShowNativeContextMenu(path, ResultType.Folder); - return false; - } - // open folder - if (c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift)) - { - try - { - OpenFolder(path); - return true; - } - catch (Exception ex) - { - Context.API.ShowMsgBox(ex.Message, Context.API.GetTranslation("plugin_explorer_opendir_error")); - return false; - } - } - // Open containing folder - if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control) - { - try - { - Context.API.OpenDirectory(Path.GetDirectoryName(path), path); - return true; - } - catch (Exception ex) - { - Context.API.ShowMsgBox(ex.Message, Context.API.GetTranslation("plugin_explorer_opendir_error")); - return false; - } - } - // If path search is disabled just open it in file manager if (Settings.DefaultOpenFolderInFileManager || (!Settings.PathSearchKeywordEnabled && !Settings.SearchActionKeywordEnabled)) { @@ -259,11 +225,6 @@ internal static Result CreateOpenCurrentFolderResult(string path, string actionK CopyText = folderPath, Action = c => { - if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt) - { - ShowNativeContextMenu(folderPath, ResultType.Folder); - return false; - } OpenFolder(folderPath); return true; }, @@ -296,25 +257,9 @@ internal static Result CreateFileResult(string filePath, Query query, int score PreviewPanel = new Lazy(() => new PreviewPanel(Settings, filePath, ResultType.File)), Action = c => { - if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt) - { - ShowNativeContextMenu(filePath, ResultType.File); - return false; - } try { - if (c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift)) - { - OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty, true); - } - else if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control) - { - OpenFolder(filePath, filePath); - } - else - { - OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty); - } + OpenFile(filePath, Settings.UseLocationAsWorkingDir ? Path.GetDirectoryName(filePath) : string.Empty); } catch (Exception ex) { @@ -337,13 +282,13 @@ private static bool IsMedia(string extension) return MediaExtensions.Contains(extension.ToLowerInvariant()); } - private static void OpenFile(string filePath, string workingDir = "", bool asAdmin = false) + public static void OpenFile(string filePath, string workingDir = "", bool asAdmin = false) { IncrementEverythingRunCounterIfNeeded(filePath); FilesFolders.OpenFile(filePath, workingDir, asAdmin, (string str) => Context.API.ShowMsgBox(str)); } - private static void OpenFolder(string folderPath, string fileNameOrFilePath = null) + public static void OpenFolder(string folderPath, string fileNameOrFilePath = null) { IncrementEverythingRunCounterIfNeeded(folderPath); Context.API.OpenDirectory(folderPath, fileNameOrFilePath); From f2358a56e8edd5a1e623974cd12b631a90045aac Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 22:20:28 +0800 Subject: [PATCH 044/150] Fix hotkey control construction issue --- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index d04b482990a..ddc5492ad2d 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -68,9 +68,9 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) // TODO: Check if this can use var hotkeyControl = new HotkeyControl { + Type = HotkeyControl.HotkeyType.CustomQueryHotkey, DefaultHotkey = hotkey.DefaultHotkey, Hotkey = hotkeySetting, - Type = HotkeyControl.HotkeyType.CustomQueryHotkey, ValidateKeyGesture = true }; card.Content = hotkeyControl; From 30b9b1f371f26768a5a2033adf3064d611505ec6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 22:41:16 +0800 Subject: [PATCH 045/150] Add Visible api --- Flow.Launcher.Plugin/PluginHotkey.cs | 5 +++++ Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/Flow.Launcher.Plugin/PluginHotkey.cs b/Flow.Launcher.Plugin/PluginHotkey.cs index eae080e4958..4ab6d61200e 100644 --- a/Flow.Launcher.Plugin/PluginHotkey.cs +++ b/Flow.Launcher.Plugin/PluginHotkey.cs @@ -53,6 +53,11 @@ public BasePluginHotkey(HotkeyType type) /// Indicates whether the hotkey is editable by the user in the settings page. /// public bool Editable { get; set; } = false; + + /// + /// Whether to show the hotkey in the settings page. + /// + public bool Visible { get; set; } = true; } /// diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 6f230edabf1..df2fed09fdd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -124,6 +124,7 @@ public List GetPuginHotkeys() Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue838"), DefaultHotkey = "Ctrl+Enter", Editable = false, + Visible = true, Action = (r) => { if (r.ContextData is SearchResult record) @@ -160,6 +161,7 @@ public List GetPuginHotkeys() Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue700"), DefaultHotkey = "Alt+Enter", Editable = false, + Visible = true, Action = (r) => { if (r.ContextData is SearchResult record && record.Type is not ResultType.Volume) @@ -185,6 +187,7 @@ public List GetPuginHotkeys() Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE7EF"), DefaultHotkey = "Ctrl+Shift+Enter", Editable = false, + Visible = true, Action = (r) => { if (r.ContextData is SearchResult record) @@ -223,6 +226,7 @@ public List GetPuginHotkeys() Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8ac"), DefaultHotkey = "F2", Editable = true, + Visible = true, Action = (r) => { if (r.ContextData is SearchResult record) From f837b2a5d25103a97b3c0f96f964aa9c29f06171 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 22:42:49 +0800 Subject: [PATCH 046/150] Remove unused using --- Flow.Launcher/HotkeyControl.xaml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 33316938ccb..93a07743633 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -1,5 +1,4 @@ using System.Collections.ObjectModel; -using System.IO; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; From 35f8ea3b38ddf715f0f4e1c1c14f052aeec34295 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 25 Jun 2025 23:15:45 +0800 Subject: [PATCH 047/150] Check hotkey mapper count --- Flow.Launcher/Helper/HotKeyMapper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 8a9c07d9a08..064ebf3bc5c 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -191,10 +191,11 @@ internal static void LoadWindowPluginHotkey() } } - private static void SetWindowHotkey(KeyGesture keyGesture, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) + internal static void SetWindowHotkey(KeyGesture keyGesture, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) { try { + if (hotkeyModels.Count == 0) return; if (Application.Current?.MainWindow is MainWindow window) { var command = BuildCommand(hotkeyModels); From 8da182dd25f18583b88c4cff430b209008e55626 Mon Sep 17 00:00:00 2001 From: Koisu Date: Wed, 25 Jun 2025 10:37:01 -0700 Subject: [PATCH 048/150] Close window when escape pressed --- .../Views/RenameFile.xaml | 2 +- .../Views/RenameFile.xaml.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml index 0f1c5578a21..ded2d92844b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Flow.Launcher.Plugin.Explorer.Views" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - Title="{DynamicResource plugin_explorer_manageactionkeywords_header}" + Title="" Height="180" MaxWidth="600" Background="{DynamicResource PopuBGColor}" diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index 4591fa76e66..0c50485f395 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -5,6 +5,7 @@ using System.Windows.Input; using System.Windows.Threading; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Plugin.Explorer.Helper; namespace Flow.Launcher.Plugin.Explorer.Views @@ -39,6 +40,14 @@ public RenameFile(IPublicAPI api, FileSystemInfo info) ShowInTaskbar = false; RenameTb.Focus(); + var window = Window.GetWindow(this); + window.KeyDown += (s, e) => + { + if (e.Key == Key.Escape) + { + Close(); + } + }; } /// @@ -55,10 +64,11 @@ private async void SelectAll_OnTextBoxGotFocus(object sender, RoutedEventArgs e) else if (_info is FileInfo info) { string properName = Path.GetFileNameWithoutExtension(info.Name); - Application.Current.Dispatcher.Invoke(textBox.Select, DispatcherPriority.Background, textBox.Text.IndexOf(properName), properName.Length ); + Application.Current.Dispatcher.Invoke(textBox.Select, DispatcherPriority.Background, textBox.Text.IndexOf(properName), properName.Length); } } + private void OnDoneButtonClick(object sender, RoutedEventArgs e) { RenameThing.Rename(NewFileName, _info, _api); @@ -79,5 +89,7 @@ private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) e.Handled = true; } } + + } } From 043bf3ba0bb1e999eccd1b704668ec4027d4ee3a Mon Sep 17 00:00:00 2001 From: Koisu Date: Wed, 25 Jun 2025 10:40:30 -0700 Subject: [PATCH 049/150] remove unused using --- Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs index 0c50485f395..323bb912fc9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs @@ -5,7 +5,6 @@ using System.Windows.Input; using System.Windows.Threading; using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Plugin.Explorer.Helper; namespace Flow.Launcher.Plugin.Explorer.Views @@ -89,7 +88,5 @@ private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) e.Handled = true; } } - - } } From 53e0bc33ab52c6710e72d2a9247211bd0798aff8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 09:24:59 +0800 Subject: [PATCH 050/150] Improve hotkey model --- Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index 25bc75a56c1..0885f65989c 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -117,12 +117,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 EnumerateDisplayKeys() + public readonly IEnumerable EnumerateDisplayKeys() { if (Ctrl && CharKey is not (Key.LeftCtrl or Key.RightCtrl)) { 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 051/150] 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 6620ba578fb..bb1f9b5b357 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -10,6 +10,7 @@ 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 @@ public static class PluginManager 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 @@ public static Dictionary> GetPluginHotkeyInfo return hotkeyPluginInfos; } - public static Dictionary> GetWindowPluginHotkeys() + public static Dictionary> GetWindowPluginHotkeys() { return _windowPluginHotkeys; } @@ -478,22 +479,66 @@ private static void InitializeWindowPluginHotkeys(Dictionary 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 064ebf3bc5c..fd0b17f0ccf 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -163,20 +163,27 @@ internal static void LoadGlobalPluginHotkey() { 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 void LoadWindowPluginHotkey() 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 void SetWindowHotkey(KeyGesture keyGesture, List<(PluginMetadata 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 void SetWindowHotkey(KeyGesture keyGesture, List<(PluginMetadata 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 @@ private static ICommand BuildCommand(List<(PluginMetadata Metadata, SearchWindow }); } - 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 void RemoveWindowHotkey(KeyGesture keyGesture) 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 ddc5492ad2d..3fa32284071 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -3,7 +3,11 @@ 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 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) 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 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) 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]); + } + } } From c7de03bbece54352dc50dc1c0dc0de2980e3639b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 10:31:43 +0800 Subject: [PATCH 052/150] Save & restore old command --- Flow.Launcher/Helper/HotKeyMapper.cs | 33 +++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index fd0b17f0ccf..9bbf7b92eb7 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -23,6 +23,8 @@ internal static class HotKeyMapper private static Settings _settings; private static MainViewModel _mainViewModel; + private static readonly Dictionary _windowHotkeyEvents = new(); + internal static void Initialize() { _mainViewModel = Ioc.Default.GetRequiredService(); @@ -204,9 +206,7 @@ internal static void SetWindowHotkey(HotkeyModel hotkey, List<(PluginMetadata Me if (hotkeyModels.Count == 0) return; if (Application.Current?.MainWindow is MainWindow window) { - var command = BuildCommand(hotkeyModels); - - // Remove any existing key binding with the same gesture to avoid duplication + // Cache the command for the hotkey if it already exists var keyGesture = hotkey.ToKeyGesture(); var existingBinding = window.InputBindings .OfType() @@ -214,13 +214,22 @@ internal static void SetWindowHotkey(HotkeyModel hotkey, List<(PluginMetadata Me kb.Gesture is KeyGesture keyGesture1 && keyGesture.Key == keyGesture1.Key && keyGesture.Modifiers == keyGesture1.Modifiers); - if (existingBinding != null) { - throw new InvalidOperationException($"Key binding with gesture {hotkey} already exists"); + // If the hotkey exists, remove the old command + if (_windowHotkeyEvents.ContainsKey(hotkey)) + { + window.InputBindings.Remove(existingBinding); + } + // If the hotkey does not exist, save the old command + else + { + _windowHotkeyEvents[hotkey] = existingBinding.Command; + } } // Create and add the new key binding + var command = BuildCommand(hotkey, hotkeyModels); var keyBinding = new KeyBinding(command, keyGesture); window.InputBindings.Add(keyBinding); } @@ -238,10 +247,15 @@ kb.Gesture is KeyGesture keyGesture1 && } } - private static ICommand BuildCommand(List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) + private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) { return new RelayCommand(() => { + if (_windowHotkeyEvents.TryGetValue(hotkey, out var existingCommand)) + { + existingCommand.Execute(null); + } + foreach (var hotkeyModel in hotkeyModels) { var metadata = hotkeyModel.Metadata; @@ -273,6 +287,13 @@ kb.Gesture is KeyGesture keyGesture1 && { window.InputBindings.Remove(existingBinding); } + + // Restore the command if it exists + if (_windowHotkeyEvents.TryGetValue(hotkey, out var command)) + { + var keyBinding = new KeyBinding(command, keyGesture); + window.InputBindings.Add(keyBinding); + } } } catch (Exception e) From b52c7e756cb499daf5b94fa2a2693a77473eed41 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 10:37:18 +0800 Subject: [PATCH 053/150] Check result null & Improve docuements --- Flow.Launcher.Plugin/PluginHotkey.cs | 2 +- Flow.Launcher/Helper/HotKeyMapper.cs | 27 ++++++++++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Plugin/PluginHotkey.cs b/Flow.Launcher.Plugin/PluginHotkey.cs index 4ab6d61200e..4067289e680 100644 --- a/Flow.Launcher.Plugin/PluginHotkey.cs +++ b/Flow.Launcher.Plugin/PluginHotkey.cs @@ -91,7 +91,7 @@ public SearchWindowPluginHotkey() : base(HotkeyType.SearchWindow) } /// - /// An action that will be executed when the hotkey is triggered. + /// An action that will be executed when the hotkey is triggered and a result is selected. /// public Func Action { get; set; } = null; } diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 9bbf7b92eb7..c2c69beff96 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -256,15 +256,28 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me existingCommand.Execute(null); } - foreach (var hotkeyModel in hotkeyModels) + var selectedResult = _mainViewModel.Results.SelectedItem?.Result; + // Check result nullability + if (selectedResult != null) { - var metadata = hotkeyModel.Metadata; - if (metadata.Disabled) - continue; + var pluginId = selectedResult.PluginID; + foreach (var hotkeyModel in hotkeyModels) + { + var metadata = hotkeyModel.Metadata; + var pluginHotkey = hotkeyModel.PluginHotkey; + + // Check plugin ID match + if (metadata.ID != pluginId) + continue; - var pluginHotkey = hotkeyModel.PluginHotkey; - if (pluginHotkey.Action?.Invoke(_mainViewModel.Results.SelectedItem?.Result) ?? false) - App.API.HideMainWindow(); + // Check plugin enabled state + if (metadata.Disabled) + continue; + + // Invoke action + if (pluginHotkey.Action?.Invoke(selectedResult) ?? false) + App.API.HideMainWindow(); + } } }); } From 590ea6184fa887a906cff6e80821787d95d23d98 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 10:45:01 +0800 Subject: [PATCH 054/150] Skip other commands if executed --- Flow.Launcher/Helper/HotKeyMapper.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index c2c69beff96..3dfe97d7cc9 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -251,11 +251,6 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me { return new RelayCommand(() => { - if (_windowHotkeyEvents.TryGetValue(hotkey, out var existingCommand)) - { - existingCommand.Execute(null); - } - var selectedResult = _mainViewModel.Results.SelectedItem?.Result; // Check result nullability if (selectedResult != null) @@ -274,11 +269,20 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me if (metadata.Disabled) continue; - // Invoke action - if (pluginHotkey.Action?.Invoke(selectedResult) ?? false) + // Check action nullability + if (pluginHotkey.Action == null) + continue; + + // Invoke action & return to skip other commands + if (pluginHotkey.Action.Invoke(selectedResult)) App.API.HideMainWindow(); } } + + if (_windowHotkeyEvents.TryGetValue(hotkey, out var existingCommand)) + { + existingCommand.Execute(null); + } }); } From ead9b1e2f6b383dae0dec66c2a15703763a848cb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 11:09:16 +0800 Subject: [PATCH 055/150] Return to skip other commands --- Flow.Launcher/Helper/HotKeyMapper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 3dfe97d7cc9..23f3d025c0b 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -276,6 +276,8 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me // Invoke action & return to skip other commands if (pluginHotkey.Action.Invoke(selectedResult)) App.API.HideMainWindow(); + + return; } } From 030a7f31c2eac05536305dd91cefca9c3b426d9b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 11:59:06 +0800 Subject: [PATCH 056/150] Adjust margins --- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml | 2 +- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml index fd3d415cc80..a08b70169cd 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml @@ -273,7 +273,7 @@ diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index 3fa32284071..a0cc0266255 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -48,7 +48,8 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) var metadata = pluginPair.Metadata; var excard = new ExCard() { - Title = metadata.Name + Title = metadata.Name, + Margin = new Thickness(0, 4, 0, 0), // TODO: Support displaying plugin icon here }; var hotkeyStackPanel = new StackPanel From 854f8082b1c2792b5daf4f660dc11cfeac9ca5c0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 11:59:52 +0800 Subject: [PATCH 057/150] Use IPluginHotkey for plugin manager plugin --- .../Main.cs | 37 +++++++++++++++++-- .../PluginsManager.cs | 12 ------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 742d85fc1d4..2b915bd5746 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -1,14 +1,14 @@ using System.Collections.Generic; using System.Linq; -using System.Windows.Controls; -using System.Threading.Tasks; using System.Threading; +using System.Threading.Tasks; +using System.Windows.Controls; using Flow.Launcher.Plugin.PluginsManager.ViewModels; using Flow.Launcher.Plugin.PluginsManager.Views; namespace Flow.Launcher.Plugin.PluginsManager { - public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n + public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IPluginHotkey { internal static PluginInitContext Context { get; set; } @@ -69,5 +69,36 @@ public string GetTranslatedPluginDescription() { return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description"); } + + public List GetPuginHotkeys() + { + return new List + { + new SearchWindowPluginHotkey + { + Id = 0, + Name = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_title"), + Description = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle"), + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uEB41"), + DefaultHotkey = "Ctrl+Enter", + Editable = false, + Visible = true, + Action = (r) => + { + if (r.ContextData is UserPlugin plugin) + { + if (!string.IsNullOrWhiteSpace(plugin.Website)) + { + Context.API.OpenUrl(plugin.Website); + + return true; + } + } + + return false; + } + } + }; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 25182f6d3d2..99282b7abc7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -527,12 +527,6 @@ internal List InstallFromWeb(string url) IcoPath = icoPath, Action = e => { - if (e.SpecialKeyState.CtrlPressed) - { - SearchWeb.OpenInBrowserTab(plugin.UrlDownload); - return ShouldHideWindow; - } - if (Settings.WarnFromUnknownSource) { if (!InstallSourceKnown(plugin.UrlDownload) @@ -633,12 +627,6 @@ internal async ValueTask> RequestInstallOrUpdateAsync(string search IcoPath = x.IcoPath, Action = e => { - if (e.SpecialKeyState.CtrlPressed) - { - SearchWeb.OpenInBrowserTab(x.Website); - return ShouldHideWindow; - } - Context.API.HideMainWindow(); _ = InstallOrUpdateAsync(x); // No need to wait return ShouldHideWindow; From 9f404aa92c29e383eb7d8fe0c99aa92e192d6d79 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 12:10:51 +0800 Subject: [PATCH 058/150] Use IPluginHotkey for program plugin --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 56 ++++++++++++++++++- .../Programs/UWPPackage.cs | 8 --- .../Programs/Win32.cs | 8 --- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index d2884599467..2596415bae5 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.Program { - public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, IAsyncReloadable, IDisposable + public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, IAsyncReloadable, IDisposable, IPluginHotkey { private static readonly string ClassName = nameof(Main); @@ -459,5 +459,59 @@ public void Dispose() { Win32.Dispose(); } + + public List GetPuginHotkeys() + { + return new List + { + new SearchWindowPluginHotkey() + { + Id = 0, + Name = Context.API.GetTranslation("flowlauncher_plugin_program_open_containing_folder"), + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue838"), + DefaultHotkey = "Ctrl+Enter", + Editable = false, + Visible = true, + Action = (r) => + { + if (r.ContextData is UWPPackage uwp) + { + Context.API.OpenDirectory(uwp.Location); + return true; + } + else if (r.ContextData is Win32 win32) + { + Context.API.OpenDirectory(win32.ParentDirectory, win32.FullPath); + return true; + } + + return false; + } + }, + // TODO: Do it after administrator mode PR + /*new SearchWindowPluginHotkey() + { + Id = 1, + Name = Context.API.GetTranslation("flowlauncher_plugin_program_run_as_administrator"), + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\uE7EF"), + DefaultHotkey = "Ctrl+Shift+Enter", + Editable = false, + Visible = true, + Action = (r) => + { + if (r.ContextData is UWPPackage uwp) + { + return true; + } + else if (r.ContextData is Win32 win32) + { + return true; + } + + return false; + } + },*/ + }; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs index cb33250e15e..116717b2f50 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -442,14 +442,6 @@ public Result Result(string query, IPublicAPI api) ContextData = this, Action = e => { - // Ctrl + Enter to open containing folder - bool openFolder = e.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control; - if (openFolder) - { - Main.Context.API.OpenDirectory(Location); - return true; - } - // Ctrl + Shift + Enter to run elevated bool elevated = e.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index a87b002d414..038257e47e6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -185,14 +185,6 @@ public Result Result(string query, IPublicAPI api) TitleToolTip = $"{title}\n{ExecutablePath}", Action = c => { - // Ctrl + Enter to open containing folder - bool openFolder = c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Control; - if (openFolder) - { - Main.Context.API.OpenDirectory(ParentDirectory, FullPath); - return true; - } - // Ctrl + Shift + Enter to run as admin bool runAsAdmin = c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift); From 3fe1e5023a2d0ba5a4b58c52d3467c8cbb1d89a9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 12:16:20 +0800 Subject: [PATCH 059/150] Resolve context data for uwps --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 2596415bae5..7b1a08548ac 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -474,7 +474,7 @@ public List GetPuginHotkeys() Visible = true, Action = (r) => { - if (r.ContextData is UWPPackage uwp) + if (r.ContextData is UWPApp uwp) { Context.API.OpenDirectory(uwp.Location); return true; @@ -499,7 +499,7 @@ public List GetPuginHotkeys() Visible = true, Action = (r) => { - if (r.ContextData is UWPPackage uwp) + if (r.ContextData is UWPApp uwp) { return true; } From 96b5eb3df923582124a467f2832b6a7521ac504c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 12:24:52 +0800 Subject: [PATCH 060/150] Skip this plugin if all hotkeys are invisible --- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index a0cc0266255..f232abe87a1 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -46,6 +46,11 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) var pluginPair = info.Key; var hotkeyInfo = info.Value; var metadata = pluginPair.Metadata; + + // Skip this plugin if all hotkeys are invisible + var allHotkeyInvisible = hotkeyInfo.All(h => !h.Visible); + if (allHotkeyInvisible) continue; + var excard = new ExCard() { Title = metadata.Name, From ddc890eb15b3287303ffd717149440f442557e5c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 12:25:21 +0800 Subject: [PATCH 061/150] Skip invisible hotkeys --- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index f232abe87a1..de499a717be 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -65,6 +65,9 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) var sortedHotkeyInfo = hotkeyInfo.OrderBy(h => h.Id).ToList(); foreach (var hotkey in hotkeyInfo) { + // Skip invisible hotkeys + if (!hotkey.Visible) continue; + var card = new Card() { Title = hotkey.Name, From e9727c45a0982e0e4e792fceb65f5e8f07653f5e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 26 Jun 2025 12:26:05 +0800 Subject: [PATCH 062/150] Add todo --- Flow.Launcher/Helper/HotKeyMapper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 23f3d025c0b..889f1bc9994 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -273,6 +273,7 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me if (pluginHotkey.Action == null) continue; + // TODO: Remove return to skip other commands & Organize main window hotkeys // Invoke action & return to skip other commands if (pluginHotkey.Action.Invoke(selectedResult)) App.API.HideMainWindow(); From cda33df0b0e545c42dc22cc19284fec75981faa1 Mon Sep 17 00:00:00 2001 From: Koisu Date: Fri, 27 Jun 2025 10:29:26 -0700 Subject: [PATCH 063/150] Make constructor protected --- Flow.Launcher.Plugin/PluginHotkey.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/PluginHotkey.cs b/Flow.Launcher.Plugin/PluginHotkey.cs index 4067289e680..18f650e88b9 100644 --- a/Flow.Launcher.Plugin/PluginHotkey.cs +++ b/Flow.Launcher.Plugin/PluginHotkey.cs @@ -14,7 +14,7 @@ public class BasePluginHotkey /// Initializes a new instance of the class with the specified hotkey type. /// /// - public BasePluginHotkey(HotkeyType type) + protected BasePluginHotkey(HotkeyType type) { HotkeyType = type; } From e087d33fbb40e7129130c628ad0cc1d8a2608a8d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Jun 2025 21:57:44 +0800 Subject: [PATCH 064/150] Add hotkey id check --- Flow.Launcher.Plugin/Result.cs | 6 ++++++ Flow.Launcher/Helper/HotKeyMapper.cs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index f0fcd48ffc0..f23ef535b50 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -257,6 +257,12 @@ public string PluginDirectory /// public bool ShowBadge { get; set; } = false; + /// + /// List of hotkey IDs that are supported for this result. + /// Those hotkeys should be registed by IPluginHotkey interface. + /// + public IList HotkeyIds { get; set; } = new List(); + /// /// Run this result, asynchronously /// diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 889f1bc9994..602cbd71c2d 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -269,6 +269,10 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me if (metadata.Disabled) continue; + // Check hotkey supported state + if (!selectedResult.HotkeyIds.Contains(pluginHotkey.Id)) + continue; + // Check action nullability if (pluginHotkey.Action == null) continue; From 3b4698e28279b142487cb0878f4dbf62db66a9d8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Jun 2025 21:59:28 +0800 Subject: [PATCH 065/150] Use selected results --- Flow.Launcher/Helper/HotKeyMapper.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 602cbd71c2d..cebb0f46ee5 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -251,7 +251,7 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me { return new RelayCommand(() => { - var selectedResult = _mainViewModel.Results.SelectedItem?.Result; + var selectedResult = _mainViewModel.GetSelectedResults().SelectedItem?.Result; // Check result nullability if (selectedResult != null) { diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 64a39fa6279..7af6ec1a02f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1711,6 +1711,11 @@ internal bool ResultsSelected(ResultsViewModel results) return selected; } + internal ResultsViewModel GetSelectedResults() + { + return SelectedResults; + } + #endregion #region Hotkey From e24af1478496c446ebde7f8c234d751fbbabc122 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 28 Jun 2025 22:11:55 +0800 Subject: [PATCH 066/150] Add hotkey ids for all results --- .../Search/ResultManager.cs | 20 +++++++++--- .../PluginsManager.cs | 31 ++++++++++++++----- .../Programs/UWPPackage.cs | 6 +++- .../Programs/Win32.cs | 6 +++- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 9063217c14a..68c1f0ccc38 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -1,9 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows.Controls; -using System.Windows.Input; using Flow.Launcher.Plugin.Explorer.Search.Everything; using Flow.Launcher.Plugin.Explorer.Views; using Flow.Launcher.Plugin.SharedCommands; @@ -133,7 +133,11 @@ internal static Result CreateFolderResult(string title, string subtitle, string Score = score, TitleToolTip = Main.Context.API.GetTranslation("plugin_explorer_plugin_ToolTipOpenDirectory"), SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFolderMoreInfoTooltip(path) : path, - ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, WindowsIndexed = windowsIndexed } + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path, WindowsIndexed = windowsIndexed }, + HotkeyIds = new List + { + 0, 1, 2, 3 + }, }; } @@ -238,7 +242,11 @@ internal static Result CreateOpenCurrentFolderResult(string path, string actionK OpenFolder(folderPath); return true; }, - ContextData = new SearchResult { Type = ResultType.Folder, FullPath = folderPath, WindowsIndexed = windowsIndexed } + ContextData = new SearchResult { Type = ResultType.Folder, FullPath = folderPath, WindowsIndexed = windowsIndexed }, + HotkeyIds = new List + { + 1 + }, }; } @@ -280,7 +288,11 @@ internal static Result CreateFileResult(string filePath, Query query, int score }, TitleToolTip = Main.Context.API.GetTranslation("plugin_explorer_plugin_ToolTipOpenContainingFolder"), SubTitleToolTip = Settings.DisplayMoreInformationInToolTip ? GetFileMoreInfoTooltip(filePath) : filePath, - ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, WindowsIndexed = windowsIndexed } + ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath, WindowsIndexed = windowsIndexed }, + HotkeyIds = new List + { + 0, 1, 2, 3 + }, }; return result; } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 99282b7abc7..a0990f5e386 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -388,12 +388,15 @@ await Context.API.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlu return true; }, - ContextData = - new UserPlugin - { - Website = x.PluginNewUserPlugin.Website, - UrlSourceCode = x.PluginNewUserPlugin.UrlSourceCode - } + ContextData = new UserPlugin + { + Website = x.PluginNewUserPlugin.Website, + UrlSourceCode = x.PluginNewUserPlugin.UrlSourceCode + }, + HotkeyIds = new List + { + 0 + }, }); // Update all result @@ -631,7 +634,11 @@ internal async ValueTask> RequestInstallOrUpdateAsync(string search _ = InstallOrUpdateAsync(x); // No need to wait return ShouldHideWindow; }, - ContextData = x + ContextData = x, + HotkeyIds = new List + { + 0 + }, }); return Search(results, search); @@ -724,7 +731,15 @@ internal List RequestUninstall(string search) } return false; - } + }, + ContextData = new UserPlugin + { + Website = x.Metadata.Website + }, + HotkeyIds = new List + { + 0 + }, }); return Search(results, search); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs index 116717b2f50..9ac1f6f6937 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs @@ -457,7 +457,11 @@ public Result Result(string query, IPublicAPI api) } return true; - } + }, + HotkeyIds = new List + { + 0 + }, }; diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 038257e47e6..dba638f1a8c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -199,7 +199,11 @@ public Result Result(string query, IPublicAPI api) _ = Task.Run(() => Main.StartProcess(Process.Start, info)); return true; - } + }, + HotkeyIds = new List + { + 0 + }, }; return result; From 73a232ff9f2587d53fce185ede8aee467419c4c9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 30 Jun 2025 12:03:20 +0800 Subject: [PATCH 067/150] Fix clone issue --- Flow.Launcher.Plugin/Result.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index f23ef535b50..2e73d7b3b88 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -314,6 +314,7 @@ public Result Clone() AddSelectedCount = AddSelectedCount, RecordKey = RecordKey, ShowBadge = ShowBadge, + HotkeyIds = HotkeyIds, }; } From c9db3ec3bb6cd7a5ca0bf0dab50bcaa57915c5c0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 30 Jun 2025 19:38:39 +0800 Subject: [PATCH 068/150] Improve string resource --- Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 64ced20a9b7..30a2ec5c02c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -199,7 +199,7 @@ {0} may not be empty. {0} is an invalid name. The specified item: {0} was not found - Open a dialog to rename this + Open a dialog to rename file or folder This cannot be renamed. Successfully renamed it to: {0} There is already a file with the name: {0} in this location From 14310d247c2588b37ac233ecfd3e50cc75c4595c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 1 Jul 2025 21:28:50 +0800 Subject: [PATCH 069/150] Add code comments --- Flow.Launcher/Helper/HotKeyMapper.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index cebb0f46ee5..682e1dbc757 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -16,6 +16,9 @@ namespace Flow.Launcher.Helper; +/// +/// Set Flow Launcher global hotkeys & window hotkeys +/// internal static class HotKeyMapper { private static readonly string ClassName = nameof(HotKeyMapper); @@ -130,6 +133,9 @@ private static void RemoveWithChefKeys(string hotkeyStr) ChefKeysManager.Stop(); } + /// + /// Custom Query Hotkeys (Global) + /// internal static void LoadCustomPluginHotkey() { if (_settings.CustomPluginHotkeys == null) @@ -153,6 +159,9 @@ internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) }); } + /// + /// Global Plugin Hotkeys (Global) + /// internal static void LoadGlobalPluginHotkey() { var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo(); @@ -190,6 +199,9 @@ internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, Plug }); } + /// + /// Plugin Window Hotkeys (Window) + /// internal static void LoadWindowPluginHotkey() { var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(); From b87f233ecbb4690d083c5e82066b8bbc7f824cf0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 1 Jul 2025 23:03:16 +0800 Subject: [PATCH 070/150] Code quality --- Flow.Launcher.Infrastructure/UserSettings/Settings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 2dbdf0bf8a9..a83e48ec8f3 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -359,9 +359,9 @@ public bool KeepMaxResults public int ActivateTimes { get; set; } - public ObservableCollection CustomPluginHotkeys { get; set; } = new ObservableCollection(); + public ObservableCollection CustomPluginHotkeys { get; set; } = new(); - public ObservableCollection CustomShortcuts { get; set; } = new ObservableCollection(); + public ObservableCollection CustomShortcuts { get; set; } = new(); [JsonIgnore] public ObservableCollection BuiltinShortcuts { get; set; } = new() @@ -432,7 +432,7 @@ public bool ShowAtTopmost public bool WMPInstalled { get; set; } = true; // This needs to be loaded last by staying at the bottom - public PluginsSettings PluginSettings { get; set; } = new PluginsSettings(); + public PluginsSettings PluginSettings { get; set; } = new(); [JsonIgnore] public List RegisteredHotkeys From afdb56df44eb8a95489dacf5ba745a685ceb1dc1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 12:56:56 +0800 Subject: [PATCH 071/150] Change RegisteredHotkeys to observable --- .../Hotkey/IHotkeySettings.cs | 4 +- .../UserSettings/Settings.cs | 87 +------------------ 2 files changed, 3 insertions(+), 88 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs b/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs index 448a70d191c..c20641d68e2 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.ObjectModel; namespace Flow.Launcher.Infrastructure.Hotkey; @@ -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. /// - public List RegisteredHotkeys { get; } + public ObservableCollection RegisteredHotkeys { get; } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index a83e48ec8f3..4e3bd7e378c 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -435,92 +435,7 @@ public bool ShowAtTopmost public PluginsSettings PluginSettings { get; set; } = new(); [JsonIgnore] - public List RegisteredHotkeys - { - get - { - var list = FixedHotkeys(); - - // Customizeable hotkeys - if (!string.IsNullOrEmpty(Hotkey)) - list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = "")); - if (!string.IsNullOrEmpty(PreviewHotkey)) - list.Add(new(PreviewHotkey, "previewHotkey", () => PreviewHotkey = "")); - if (!string.IsNullOrEmpty(AutoCompleteHotkey)) - list.Add(new(AutoCompleteHotkey, "autoCompleteHotkey", () => AutoCompleteHotkey = "")); - if (!string.IsNullOrEmpty(AutoCompleteHotkey2)) - list.Add(new(AutoCompleteHotkey2, "autoCompleteHotkey", () => AutoCompleteHotkey2 = "")); - if (!string.IsNullOrEmpty(SelectNextItemHotkey)) - list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = "")); - if (!string.IsNullOrEmpty(SelectNextItemHotkey2)) - list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = "")); - if (!string.IsNullOrEmpty(SelectPrevItemHotkey)) - list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = "")); - if (!string.IsNullOrEmpty(SelectPrevItemHotkey2)) - list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = "")); - if (!string.IsNullOrEmpty(SettingWindowHotkey)) - list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); - if (!string.IsNullOrEmpty(OpenHistoryHotkey)) - list.Add(new(OpenHistoryHotkey, "OpenHistoryHotkey", () => OpenHistoryHotkey = "")); - if (!string.IsNullOrEmpty(OpenContextMenuHotkey)) - list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); - if (!string.IsNullOrEmpty(SelectNextPageHotkey)) - list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); - if (!string.IsNullOrEmpty(SelectPrevPageHotkey)) - list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); - if (!string.IsNullOrEmpty(CycleHistoryUpHotkey)) - list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); - if (!string.IsNullOrEmpty(CycleHistoryDownHotkey)) - list.Add(new(CycleHistoryDownHotkey, "CycleHistoryDownHotkey", () => CycleHistoryDownHotkey = "")); - - // Custom Query Hotkeys - foreach (var customPluginHotkey in CustomPluginHotkeys) - { - if (!string.IsNullOrEmpty(customPluginHotkey.Hotkey)) - list.Add(new(customPluginHotkey.Hotkey, "customQueryHotkey", () => customPluginHotkey.Hotkey = "")); - } - - return list; - } - } - - private List FixedHotkeys() - { - return new List - { - new("Up", "HotkeyLeftRightDesc"), - new("Down", "HotkeyLeftRightDesc"), - new("Left", "HotkeyUpDownDesc"), - new("Right", "HotkeyUpDownDesc"), - new("Escape", "HotkeyESCDesc"), - new("F5", "ReloadPluginHotkey"), - new("Alt+Home", "HotkeySelectFirstResult"), - new("Alt+End", "HotkeySelectLastResult"), - new("Ctrl+R", "HotkeyRequery"), - new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"), - new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"), - new("Ctrl+OemPlus", "QuickHeightHotkey"), - new("Ctrl+OemMinus", "QuickHeightHotkey"), - new("Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc"), - new("Shift+Enter", "OpenContextMenuHotkey"), - new("Enter", "HotkeyRunDesc"), - new("Ctrl+Enter", "OpenContainFolderHotkey"), - new("Alt+Enter", "HotkeyOpenResult"), - new("Ctrl+F12", "ToggleGameModeHotkey"), - new("Ctrl+Shift+C", "CopyFilePathHotkey"), - - new($"{OpenResultModifiers}+D1", "HotkeyOpenResultN", 1), - new($"{OpenResultModifiers}+D2", "HotkeyOpenResultN", 2), - new($"{OpenResultModifiers}+D3", "HotkeyOpenResultN", 3), - new($"{OpenResultModifiers}+D4", "HotkeyOpenResultN", 4), - new($"{OpenResultModifiers}+D5", "HotkeyOpenResultN", 5), - new($"{OpenResultModifiers}+D6", "HotkeyOpenResultN", 6), - new($"{OpenResultModifiers}+D7", "HotkeyOpenResultN", 7), - new($"{OpenResultModifiers}+D8", "HotkeyOpenResultN", 8), - new($"{OpenResultModifiers}+D9", "HotkeyOpenResultN", 9), - new($"{OpenResultModifiers}+D0", "HotkeyOpenResultN", 10) - }; - } + public ObservableCollection RegisteredHotkeys { get; } = new(); } public enum LastQueryMode From 1d2aa96dcc94faee06547bf7379b0bf77bce97cc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 12:57:22 +0800 Subject: [PATCH 072/150] Add property changed for CustomPluginHotkey --- .../UserSettings/PluginHotkey.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs index 9dc395acaea..b67bba57ff9 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs @@ -4,7 +4,20 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public class CustomPluginHotkey : BaseModel { - public string Hotkey { get; set; } + private string _hotkey = string.Empty; + public string Hotkey + { + get => _hotkey; + set + { + if (_hotkey != value) + { + _hotkey = value; + OnPropertyChanged(); + } + } + } + public string ActionKeyword { get; set; } } } From 989206b59c6e87a9f0abf41b077ff1cb84ced16b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 12:57:45 +0800 Subject: [PATCH 073/150] Add is empty for HotkeyModel --- Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index 0885f65989c..bcfd795e993 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -49,6 +49,8 @@ public ModifierKeys ModifierKeys } } + public readonly bool IsEmpty => CharKey == Key.None && !Alt && !Shift && !Win && !Ctrl; + public HotkeyModel(string hotkeyString) { Parse(hotkeyString); From 5910d1de19301e6783a3a8216092f4d92edb7982 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 12:58:50 +0800 Subject: [PATCH 074/150] Add property changed for RegisteredHotkeyData.Hotkey --- .../Hotkey/RegisteredHotkeyData.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index b6a10fc3075..54df72c70d7 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -9,12 +9,24 @@ namespace Flow.Launcher.Infrastructure.Hotkey; /// and to display errors if user tries to register a hotkey /// that has already been registered, and optionally provides a way to unregister the hotkey. /// -public record RegisteredHotkeyData +public class RegisteredHotkeyData : BaseModel { /// /// representation of this hotkey. /// - public HotkeyModel Hotkey { get; } + private HotkeyModel _hotkey; + public HotkeyModel Hotkey + { + get => _hotkey; + set + { + if (!_hotkey.Equals(value)) + { + _hotkey = value; + OnPropertyChanged(); + } + } + } /// /// String key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey, From 2259d742f8d6d685f090538d95a7cd41909819eb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 12:59:19 +0800 Subject: [PATCH 075/150] Add registered type, type, command, command parameter for RegisteredHotkeyData --- .../Hotkey/RegisteredHotkeyData.cs | 181 +++++++++++++++++- 1 file changed, 178 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index 54df72c70d7..b34974e4807 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -1,4 +1,6 @@ using System; +using System.Windows.Input; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.Hotkey; @@ -11,6 +13,16 @@ namespace Flow.Launcher.Infrastructure.Hotkey; /// public class RegisteredHotkeyData : BaseModel { + /// + /// Type of this hotkey in the context of the application. + /// + public RegisteredHotkeyType RegisteredType { get; } + + /// + /// Type of this hotkey. + /// + public HotkeyType Type { get; } + /// /// representation of this hotkey. /// @@ -40,6 +52,16 @@ public HotkeyModel Hotkey /// public object?[] DescriptionFormatVariables { get; } = Array.Empty(); + /// + /// Command of this hotkey. If it's null, the hotkey is assumed to be registered by system. + /// + public ICommand? Command { get; } + + /// + /// Command parameter of this hotkey. + /// + public object? CommandParameter { get; } + /// /// An action that, when called, will unregister this hotkey. If it's null, it's assumed that /// this hotkey can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog. @@ -51,6 +73,12 @@ public HotkeyModel Hotkey /// descriptionResourceKey doesn't need any arguments for string.Format. If it does, /// use one of the other constructors. /// + /// + /// The type of this hotkey in the context of the application. + /// + /// + /// Whether this hotkey is global or search window specific. + /// /// /// The hotkey this class will represent. /// Example values: F1, Ctrl+Shift+Enter @@ -59,14 +87,68 @@ public HotkeyModel Hotkey /// The key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey, /// which represents the string "Reload Plugins Data" in en.xaml /// + /// + /// The command that will be executed when this hotkey is triggered. If it's null, the hotkey is assumed to be registered by system. + /// + /// + /// The command parameter that will be passed to the command when this hotkey is triggered. If it's null, no parameter will be passed. + /// /// /// An action that, when called, will unregister this hotkey. If it's null, it's assumed that this hotkey /// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog. /// - public RegisteredHotkeyData(string hotkey, string descriptionResourceKey, Action? removeHotkey = null) + public RegisteredHotkeyData( + RegisteredHotkeyType registeredType, HotkeyType type, string hotkey, string descriptionResourceKey, + ICommand? command, object? parameter = null, Action? removeHotkey = null) { + RegisteredType = registeredType; + Type = type; Hotkey = new HotkeyModel(hotkey); DescriptionResourceKey = descriptionResourceKey; + Command = command; + CommandParameter = parameter; + RemoveHotkey = removeHotkey; + } + + /// + /// Creates an instance of RegisteredHotkeyData. Assumes that the key specified in + /// descriptionResourceKey doesn't need any arguments for string.Format. If it does, + /// use one of the other constructors. + /// + /// + /// The type of this hotkey in the context of the application. + /// + /// + /// Whether this hotkey is global or search window specific. + /// + /// + /// The hotkey this class will represent. + /// Example values: F1, Ctrl+Shift+Enter + /// + /// + /// The key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey, + /// which represents the string "Reload Plugins Data" in en.xaml + /// + /// + /// The command that will be executed when this hotkey is triggered. If it's null, the hotkey is assumed to be registered by system. + /// + /// + /// The command parameter that will be passed to the command when this hotkey is triggered. If it's null, no parameter will be passed. + /// + /// + /// An action that, when called, will unregister this hotkey. If it's null, it's assumed that this hotkey + /// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog. + /// + public RegisteredHotkeyData( + RegisteredHotkeyType registeredType, HotkeyType type, HotkeyModel hotkey, string descriptionResourceKey, + ICommand? command, object? parameter = null, Action? removeHotkey = null) + { + RegisteredType = registeredType; + Type = type; + Hotkey = hotkey; + DescriptionResourceKey = descriptionResourceKey; + Command = command; + CommandParameter = parameter; RemoveHotkey = removeHotkey; } @@ -74,6 +156,12 @@ public RegisteredHotkeyData(string hotkey, string descriptionResourceKey, Action /// Creates an instance of RegisteredHotkeyData. Assumes that the key specified in /// descriptionResourceKey needs exactly one argument for string.Format. /// + /// + /// The type of this hotkey in the context of the application. + /// + /// + /// Whether this hotkey is global or search window specific. + /// /// /// The hotkey this class will represent. /// Example values: F1, Ctrl+Shift+Enter @@ -85,17 +173,28 @@ public RegisteredHotkeyData(string hotkey, string descriptionResourceKey, Action /// /// The value that will replace {0} in the localized string found via description. /// + /// + /// The command that will be executed when this hotkey is triggered. If it's null, the hotkey is assumed to be registered by system. + /// + /// + /// The command parameter that will be passed to the command when this hotkey is triggered. If it's null, no parameter will be passed. + /// /// /// An action that, when called, will unregister this hotkey. If it's null, it's assumed that this hotkey /// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog. /// public RegisteredHotkeyData( - string hotkey, string descriptionResourceKey, object? descriptionFormatVariable, Action? removeHotkey = null + RegisteredHotkeyType registeredType, HotkeyType type, string hotkey, string descriptionResourceKey, object? descriptionFormatVariable, + ICommand? command, object? parameter = null, Action? removeHotkey = null ) { + RegisteredType = registeredType; + Type = type; Hotkey = new HotkeyModel(hotkey); DescriptionResourceKey = descriptionResourceKey; DescriptionFormatVariables = new[] { descriptionFormatVariable }; + Command = command; + CommandParameter = parameter; RemoveHotkey = removeHotkey; } @@ -103,6 +202,12 @@ public RegisteredHotkeyData( /// Creates an instance of RegisteredHotkeyData. Assumes that the key specified in /// needs multiple arguments for string.Format. /// + /// + /// The type of this hotkey in the context of the application. + /// + /// + /// Whether this hotkey is global or search window specific. + /// /// /// The hotkey this class will represent. /// Example values: F1, Ctrl+Shift+Enter @@ -115,17 +220,87 @@ public RegisteredHotkeyData( /// Array of values that will replace {0}, {1}, {2}, etc. /// in the localized string found via description. /// + /// + /// The command that will be executed when this hotkey is triggered. If it's null, the hotkey is assumed to be registered by system. + /// + /// + /// The command parameter that will be passed to the command when this hotkey is triggered. If it's null, no parameter will be passed. + /// /// /// An action that, when called, will unregister this hotkey. If it's null, it's assumed that this hotkey /// can't be unregistered, and the "Overwrite" option will not appear in the hotkey dialog. /// public RegisteredHotkeyData( - string hotkey, string descriptionResourceKey, object?[] descriptionFormatVariables, Action? removeHotkey = null + RegisteredHotkeyType registeredType, HotkeyType type, string hotkey, string descriptionResourceKey, object?[] descriptionFormatVariables, + ICommand? command, object? parameter = null, Action? removeHotkey = null ) { + RegisteredType = registeredType; + Type = type; Hotkey = new HotkeyModel(hotkey); DescriptionResourceKey = descriptionResourceKey; DescriptionFormatVariables = descriptionFormatVariables; + Command = command; + CommandParameter = parameter; RemoveHotkey = removeHotkey; } } + +public enum RegisteredHotkeyType +{ + CtrlShiftEnter, + CtrlEnter, + AltEnter, + + Up, + Down, + Left, + Right, + + Esc, + Reload, + SelectFirstResult, + SelectLastResult, + ReQuery, + IncreaseWidth, + DecreaseWidth, + IncreaseMaxResult, + DecreaseMaxResult, + ShiftEnter, + Enter, + ToggleGameMode, + CopyFilePath, + OpenResultN1, + OpenResultN2, + OpenResultN3, + OpenResultN4, + OpenResultN5, + OpenResultN6, + OpenResultN7, + OpenResultN8, + OpenResultN9, + OpenResultN10, + + Toggle, + + Preview, + AutoComplete, + AutoComplete2, + SelectNextItem, + SelectNextItem2, + SelectPrevItem, + SelectPrevItem2, + SettingWindow, + OpenHistory, + OpenContextMenu, + SelectNextPage, + SelectPrevPage, + CycleHistoryUp, + CycleHistoryDown, + + CustomQuery, + + PluginGlobalHotkey, + + PluginWindowHotkey, +} From 3231b1d32fda0a7aa0f506b3797309c4c44bb1ad Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:02:42 +0800 Subject: [PATCH 076/150] Implement initialization model for plugin hotkeys --- Flow.Launcher/Helper/HotKeyMapper.cs | 499 +++++++++++++++++++++++---- 1 file changed, 434 insertions(+), 65 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 682e1dbc757..1085d141d54 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -26,37 +26,165 @@ internal static class HotKeyMapper private static Settings _settings; private static MainViewModel _mainViewModel; - private static readonly Dictionary _windowHotkeyEvents = new(); + // Registered hotkeys for ActionContext + private static List _actionContextRegisteredHotkeys; + + #region Initializatin internal static void Initialize() { _mainViewModel = Ioc.Default.GetRequiredService(); _settings = Ioc.Default.GetService(); - SetHotkey(_settings.Hotkey, OnToggleHotkey); - LoadCustomPluginHotkey(); - LoadGlobalPluginHotkey(); - LoadWindowPluginHotkey(); + InitializeRegisteredHotkeys(); + + foreach (var hotkey in _settings.RegisteredHotkeys) + { + SetHotkey(hotkey); + } + } + + private static void InitializeRegisteredHotkeys() + { + // Fixed hotkeys for ActionContext + _actionContextRegisteredHotkeys = new List + { + new(RegisteredHotkeyType.CtrlShiftEnter, HotkeyType.SearchWindow, "Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc", _mainViewModel.OpenResultCommand), + new(RegisteredHotkeyType.CtrlEnter, HotkeyType.SearchWindow, "Ctrl+Enter", "OpenContainFolderHotkey", _mainViewModel.OpenResultCommand), + new(RegisteredHotkeyType.AltEnter, HotkeyType.SearchWindow, "Alt+Enter", "HotkeyOpenResult", _mainViewModel.OpenResultCommand), + }; + + // Fixed hotkeys & Editable hotkeys + var list = new List + { + // System default window hotkeys + new(RegisteredHotkeyType.Up, HotkeyType.SearchWindow, "Up", "HotkeyLeftRightDesc", null), + new(RegisteredHotkeyType.Down, HotkeyType.SearchWindow, "Down", "HotkeyLeftRightDesc", null), + new(RegisteredHotkeyType.Left, HotkeyType.SearchWindow, "Left", "HotkeyUpDownDesc", null), + new(RegisteredHotkeyType.Right, HotkeyType.SearchWindow, "Right", "HotkeyUpDownDesc", null), + + // Flow Launcher window hotkeys + new(RegisteredHotkeyType.Esc, HotkeyType.SearchWindow, "Escape", "HotkeyESCDesc", _mainViewModel.EscCommand), + new(RegisteredHotkeyType.Reload, HotkeyType.SearchWindow, "F5", "ReloadPluginHotkey", _mainViewModel.ReloadPluginDataCommand), + new(RegisteredHotkeyType.SelectFirstResult, HotkeyType.SearchWindow, "Alt+Home", "HotkeySelectFirstResult", _mainViewModel.SelectFirstResultCommand), + new(RegisteredHotkeyType.SelectLastResult, HotkeyType.SearchWindow, "Alt+End", "HotkeySelectLastResult", _mainViewModel.SelectLastResultCommand), + new(RegisteredHotkeyType.ReQuery, HotkeyType.SearchWindow, "Ctrl+R", "HotkeyRequery", _mainViewModel.ReQueryCommand), + new(RegisteredHotkeyType.IncreaseWidth, HotkeyType.SearchWindow, "Ctrl+OemCloseBrackets", "QuickWidthHotkey", _mainViewModel.IncreaseWidthCommand), + new(RegisteredHotkeyType.DecreaseWidth, HotkeyType.SearchWindow, "Ctrl+OemOpenBrackets", "QuickWidthHotkey", _mainViewModel.DecreaseWidthCommand), + new(RegisteredHotkeyType.IncreaseMaxResult, HotkeyType.SearchWindow, "Ctrl+OemPlus", "QuickHeightHotkey", _mainViewModel.IncreaseMaxResultCommand), + new(RegisteredHotkeyType.DecreaseMaxResult, HotkeyType.SearchWindow, "Ctrl+OemMinus", "QuickHeightHotkey", _mainViewModel.DecreaseMaxResultCommand), + new(RegisteredHotkeyType.ShiftEnter, HotkeyType.SearchWindow, "Shift+Enter", "OpenContextMenuHotkey", _mainViewModel.LoadContextMenuCommand), + new(RegisteredHotkeyType.Enter, HotkeyType.SearchWindow, "Enter", "HotkeyRunDesc", _mainViewModel.OpenResultCommand), + new(RegisteredHotkeyType.ToggleGameMode, HotkeyType.SearchWindow, "Ctrl+F12", "ToggleGameModeHotkey", _mainViewModel.ToggleGameModeCommand), + new(RegisteredHotkeyType.CopyFilePath, HotkeyType.SearchWindow, "Ctrl+Shift+C", "CopyFilePathHotkey", _mainViewModel.CopyAlternativeCommand), + new(RegisteredHotkeyType.OpenResultN1, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D1", "HotkeyOpenResultN", 1, _mainViewModel.OpenResultCommand, 0), + new(RegisteredHotkeyType.OpenResultN2, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D2", "HotkeyOpenResultN", 2, _mainViewModel.OpenResultCommand, 1), + new(RegisteredHotkeyType.OpenResultN3, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D3", "HotkeyOpenResultN", 3, _mainViewModel.OpenResultCommand, 2), + new(RegisteredHotkeyType.OpenResultN4, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D4", "HotkeyOpenResultN", 4, _mainViewModel.OpenResultCommand, 3), + new(RegisteredHotkeyType.OpenResultN5, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D5", "HotkeyOpenResultN", 5, _mainViewModel.OpenResultCommand, 4), + new(RegisteredHotkeyType.OpenResultN6, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D6", "HotkeyOpenResultN", 6, _mainViewModel.OpenResultCommand, 5), + new(RegisteredHotkeyType.OpenResultN7, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D7", "HotkeyOpenResultN", 7, _mainViewModel.OpenResultCommand, 6), + new(RegisteredHotkeyType.OpenResultN8, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D8", "HotkeyOpenResultN", 8, _mainViewModel.OpenResultCommand, 7), + new(RegisteredHotkeyType.OpenResultN9, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D9", "HotkeyOpenResultN", 9, _mainViewModel.OpenResultCommand, 8), + new(RegisteredHotkeyType.OpenResultN10, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D0", "HotkeyOpenResultN", 10, _mainViewModel.OpenResultCommand, 9), + + // Flow Launcher global hotkeys + new(RegisteredHotkeyType.Toggle, HotkeyType.Global, _settings.Hotkey, "flowlauncherHotkey", _mainViewModel.CheckAndToggleFlowLauncherCommand, null, () => _settings.Hotkey = ""), + + // Flow Launcher window hotkeys + new(RegisteredHotkeyType.Preview, HotkeyType.SearchWindow, _settings.PreviewHotkey, "previewHotkey", _mainViewModel.TogglePreviewCommand, null, () => _settings.PreviewHotkey = ""), + new(RegisteredHotkeyType.AutoComplete, HotkeyType.SearchWindow, _settings.AutoCompleteHotkey, "autoCompleteHotkey", _mainViewModel.AutocompleteQueryCommand, null, () => _settings.AutoCompleteHotkey = ""), + new(RegisteredHotkeyType.AutoComplete2, HotkeyType.SearchWindow, _settings.AutoCompleteHotkey2, "autoCompleteHotkey", _mainViewModel.AutocompleteQueryCommand, null, () => _settings.AutoCompleteHotkey2 = ""), + new(RegisteredHotkeyType.SelectNextItem, HotkeyType.SearchWindow, _settings.SelectNextItemHotkey, "SelectNextItemHotkey", _mainViewModel.SelectNextItemCommand, null, () => _settings.SelectNextItemHotkey = ""), + new(RegisteredHotkeyType.SelectNextItem2, HotkeyType.SearchWindow, _settings.SelectNextItemHotkey2, "SelectNextItemHotkey", _mainViewModel.SelectNextItemCommand, null, () => _settings.SelectNextItemHotkey2 = ""), + new(RegisteredHotkeyType.SelectPrevItem, HotkeyType.SearchWindow, _settings.SelectPrevItemHotkey, "SelectPrevItemHotkey", _mainViewModel.SelectPrevItemCommand, null, () => _settings.SelectPrevItemHotkey = ""), + new(RegisteredHotkeyType.SelectPrevItem2, HotkeyType.SearchWindow, _settings.SelectPrevItemHotkey2, "SelectPrevItemHotkey", _mainViewModel.SelectPrevItemCommand, null, () => _settings.SelectPrevItemHotkey2 = ""), + new(RegisteredHotkeyType.SettingWindow, HotkeyType.SearchWindow, _settings.SettingWindowHotkey, "SettingWindowHotkey", _mainViewModel.OpenSettingCommand, null, () => _settings.SettingWindowHotkey = ""), + new(RegisteredHotkeyType.OpenHistory, HotkeyType.SearchWindow, _settings.OpenHistoryHotkey, "OpenHistoryHotkey", _mainViewModel.LoadHistoryCommand, null, () => _settings.OpenHistoryHotkey = ""), + new(RegisteredHotkeyType.OpenContextMenu, HotkeyType.SearchWindow, _settings.OpenContextMenuHotkey, "OpenContextMenuHotkey", _mainViewModel.LoadContextMenuCommand, null, () => _settings.OpenContextMenuHotkey = ""), + new(RegisteredHotkeyType.SelectNextPage, HotkeyType.SearchWindow, _settings.SelectNextPageHotkey, "SelectNextPageHotkey", _mainViewModel.SelectNextPageCommand, null, () => _settings.SelectNextPageHotkey = ""), + new(RegisteredHotkeyType.SelectPrevPage, HotkeyType.SearchWindow, _settings.SelectPrevPageHotkey, "SelectPrevPageHotkey", _mainViewModel.SelectPrevPageCommand, null, () => _settings.SelectPrevPageHotkey = ""), + new(RegisteredHotkeyType.CycleHistoryUp, HotkeyType.SearchWindow, _settings.CycleHistoryUpHotkey, "CycleHistoryUpHotkey", _mainViewModel.ReverseHistoryCommand, null, () => _settings.CycleHistoryUpHotkey = ""), + new(RegisteredHotkeyType.CycleHistoryDown, HotkeyType.SearchWindow, _settings.CycleHistoryDownHotkey, "CycleHistoryDownHotkey", _mainViewModel.ForwardHistoryCommand, null, () => _settings.CycleHistoryDownHotkey = "") + }; + + // Custom query global hotkeys + if (_settings.CustomPluginHotkeys != null) + { + foreach (var customPluginHotkey in _settings.CustomPluginHotkeys) + { + list.Add(new(RegisteredHotkeyType.CustomQuery, HotkeyType.Global, customPluginHotkey.Hotkey, "customQueryHotkey", CustomQueryHotkeyCommand, customPluginHotkey, () => customPluginHotkey.Hotkey = "")); + } + } + + // Plugin hotkeys + // Global plugin hotkeys + var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo(); + foreach (var info in pluginHotkeyInfos) + { + var pluginPair = info.Key; + var hotkeyInfo = info.Value; + var metadata = pluginPair.Metadata; + foreach (var hotkey in hotkeyInfo) + { + if (hotkey.HotkeyType == HotkeyType.Global && hotkey is GlobalPluginHotkey globalHotkey) + { + var hotkeyStr = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; + // TODO: Support removeAction + Action removeHotkeyAction = hotkey.Editable ? + /*() => metadata.PluginHotkeys.RemoveAll(h => h.Id == hotkey.Id) :*/ null: + null; + // TODO: Handle pluginGlobalHotkey & get translation from PluginManager + list.Add(new(RegisteredHotkeyType.PluginGlobalHotkey, HotkeyType.Global, hotkeyStr, "pluginGlobalHotkey", GlobalPluginHotkeyCommand, new GlobalPluginHotkeyPair(metadata, globalHotkey), () => { })); + } + } + } + + // Window plugin hotkeys + var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(); + foreach (var hotkey in windowPluginHotkeys) + { + var hotkeyModel = hotkey.Key; + var windowHotkeys = hotkey.Value; + // TODO: Support removeAction + Action removeHotkeysAction = windowHotkeys.All(h => h.SearchWindowPluginHotkey.Editable) ? + /*() => hotkeyModel.Metadata.PluginWindowHotkeys.RemoveAll(h => h.SearchWindowPluginHotkey.Editable) :*/ null : + null; + // TODO: Handle pluginWindowHotkey & get translation from PluginManager + list.Add(new(RegisteredHotkeyType.PluginWindowHotkey, HotkeyType.SearchWindow, hotkeyModel, "pluginWindowHotkey", WindowPluginHotkeyCommand, new WindowPluginHotkeyPair(windowHotkeys))); + } + + // Add registered hotkeys + foreach (var hotkey in list) + { + _settings.RegisteredHotkeys.Add(hotkey); + } } + #endregion + + // TODO: Deprecated internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) { if (!_mainViewModel.ShouldIgnoreHotkeys()) _mainViewModel.ToggleFlowLauncher(); } + // TODO: Deprecated internal static void OnToggleHotkeyWithChefKeys() { if (!_mainViewModel.ShouldIgnoreHotkeys()) _mainViewModel.ToggleFlowLauncher(); } + // TODO: Deprecated private static void SetHotkey(string hotkeyStr, EventHandler action) { var hotkey = new HotkeyModel(hotkeyStr); SetHotkey(hotkey, action); } + // TODO: Deprecated private static void SetWithChefKeys(string hotkeyStr) { try @@ -76,8 +204,14 @@ private static void SetWithChefKeys(string hotkeyStr) } } + // TODO: Deprecated internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) { + if (hotkey.IsEmpty) + { + return; + } + string hotkeyStr = hotkey.ToString(); try { @@ -102,6 +236,7 @@ internal static void SetHotkey(HotkeyModel hotkey, EventHandler } } + // TODO: Deprecated internal static void RemoveHotkey(string hotkeyStr) { try @@ -127,66 +262,299 @@ internal static void RemoveHotkey(string hotkeyStr) } } + // TODO: Deprecated private static void RemoveWithChefKeys(string hotkeyStr) { ChefKeysManager.UnregisterHotkey(hotkeyStr); ChefKeysManager.Stop(); } - /// - /// Custom Query Hotkeys (Global) - /// - internal static void LoadCustomPluginHotkey() + #region Hotkey Setting + + private static void SetHotkey(RegisteredHotkeyData hotkeyData) { - if (_settings.CustomPluginHotkeys == null) + if (hotkeyData is null || // Hotkey data is invalid + hotkeyData.Hotkey.IsEmpty || // Hotkey is none + hotkeyData.Command is null) // No need to set - it is a system command + { return; + } - foreach (CustomPluginHotkey hotkey in _settings.CustomPluginHotkeys) + if (hotkeyData.Type == HotkeyType.Global) { - SetCustomQueryHotkey(hotkey); + SetGlobalHotkey(hotkeyData); + } + else if (hotkeyData.Type == HotkeyType.SearchWindow) + { + SetWindowHotkey(hotkeyData); } } - internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) + private static void RemoveHotkey(RegisteredHotkeyData hotkeyData) { - SetHotkey(hotkey.Hotkey, (s, e) => + if (hotkeyData is null || // Hotkey data is invalid + hotkeyData.Hotkey.IsEmpty || // Hotkey is none + hotkeyData.Command is null) // No need to set - it is a system command { - if (_mainViewModel.ShouldIgnoreHotkeys()) + return; + } + + if (hotkeyData.Type == HotkeyType.Global) + { + RemoveGlobalHotkey(hotkeyData); + } + else if (hotkeyData.Type == HotkeyType.SearchWindow) + { + RemoveWindowHotkey(hotkeyData); + } + } + + private static void SetGlobalHotkey(RegisteredHotkeyData hotkeyData) + { + var hotkey = hotkeyData.Hotkey; + var hotkeyStr = hotkey.ToString(); + var hotkeyCommand = hotkeyData.Command; + var hotkeyCommandParameter = hotkeyData.CommandParameter; + try + { + if (hotkeyStr == "LWin" || hotkeyStr == "RWin") + { + SetGlobalHotkeyWithChefKeys(hotkeyData); return; + } - App.API.ShowMainWindow(); - App.API.ChangeQuery(hotkey.ActionKeyword, true); - }); + HotkeyManager.Current.AddOrReplace( + hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, + (s, e) => hotkeyCommand.Execute(hotkeyCommandParameter)); + } + catch (Exception e) + { + App.API.LogError(ClassName, $"Error registering hotkey {hotkeyStr}: {e.Message} \nStackTrace:{e.StackTrace}"); + var errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr); + var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } } - /// - /// Global Plugin Hotkeys (Global) - /// - internal static void LoadGlobalPluginHotkey() + private static void SetGlobalHotkeyWithChefKeys(RegisteredHotkeyData hotkeyData) { - var pluginHotkeyInfos = PluginManager.GetPluginHotkeyInfo(); - foreach (var info in pluginHotkeyInfos) + var hotkey = hotkeyData.Hotkey; + if (hotkey.IsEmpty) { - var pluginPair = info.Key; - var hotkeyInfo = info.Value; - var metadata = pluginPair.Metadata; - foreach (var hotkey in hotkeyInfo) + return; + } + + var hotkeyStr = hotkey.ToString(); + try + { + ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, OnToggleHotkeyWithChefKeys); + ChefKeysManager.Start(); + } + catch (Exception e) + { + App.API.LogError(ClassName, + string.Format("Error registering hotkey: {0} \nStackTrace:{1}", + e.Message, + e.StackTrace)); + string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr); + string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } + } + + private static void SetWindowHotkey(RegisteredHotkeyData hotkeyData) + { + var hotkey = hotkeyData.Hotkey; + var hotkeyCommand = hotkeyData.Command; + var hotkeyCommandParameter = hotkeyData.CommandParameter; + try + { + if (Application.Current?.MainWindow is MainWindow window) { - if (hotkey.HotkeyType == HotkeyType.Global && hotkey is GlobalPluginHotkey globalHotkey) + // Check if the hotkey already exists + var keyGesture = hotkey.ToKeyGesture(); + var existingBinding = window.InputBindings + .OfType() + .FirstOrDefault(kb => + kb.Gesture is KeyGesture keyGesture1 && + keyGesture.Key == keyGesture1.Key && + keyGesture.Modifiers == keyGesture1.Modifiers); + if (existingBinding != null) { - var hotkeyStr = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; - SetGlobalPluginHotkey(globalHotkey, metadata, hotkeyStr); + throw new InvalidOperationException($"Windows key {hotkey} already exists"); } + + // Add the new hotkey binding + var keyBinding = new KeyBinding() + { + Gesture = keyGesture, + Command = hotkeyCommand, + CommandParameter = hotkeyCommandParameter + }; + window.InputBindings.Add(keyBinding); } } + catch (Exception e) + { + App.API.LogError(ClassName, $"Error registering window hotkey {hotkey}: {e.Message} \nStackTrace:{e.StackTrace}"); + var errorMsg = string.Format(App.API.GetTranslation("registerWindowHotkeyFailed"), hotkey); + var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } + } + + private static void RemoveGlobalHotkey(RegisteredHotkeyData hotkeyData) + { + var hotkey = hotkeyData.Hotkey; + var hotkeyStr = hotkey.ToString(); + try + { + if (hotkeyStr == "LWin" || hotkeyStr == "RWin") + { + RemoveGlobalHotkeyWithChefKeys(hotkeyData); + return; + } + + if (!string.IsNullOrEmpty(hotkeyStr)) + HotkeyManager.Current.Remove(hotkeyStr); + } + catch (Exception e) + { + App.API.LogError(ClassName, $"Error removing hotkey: {e.Message} \nStackTrace:{e.StackTrace}"); + var errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr); + var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } + } + + private static void RemoveGlobalHotkeyWithChefKeys(RegisteredHotkeyData hotkeyData) + { + var hotkey = hotkeyData.Hotkey; + var hotkeyStr = hotkey.ToString(); + try + { + ChefKeysManager.UnregisterHotkey(hotkeyStr); + ChefKeysManager.Stop(); + } + catch (Exception e) + { + App.API.LogError(ClassName, $"Error removing hotkey: {e.Message} \nStackTrace:{e.StackTrace}"); + var errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr); + var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } + } + + private static void RemoveWindowHotkey(RegisteredHotkeyData hotkeyData) + { + var hotkey = hotkeyData.Hotkey; + try + { + if (Application.Current?.MainWindow is MainWindow window) + { + // Remove the key binding + var keyGesture = hotkey.ToKeyGesture(); + var existingBinding = window.InputBindings + .OfType() + .FirstOrDefault(kb => + kb.Gesture is KeyGesture keyGesture1 && + keyGesture.Key == keyGesture1.Key && + keyGesture.Modifiers == keyGesture1.Modifiers); + if (existingBinding != null) + { + window.InputBindings.Remove(existingBinding); + } + } + } + catch (Exception e) + { + App.API.LogError(ClassName, $"Error removing window hotkey: {e.Message} \nStackTrace:{e.StackTrace}"); + var errorMsg = string.Format(App.API.GetTranslation("unregisterWindowHotkeyFailed"), hotkey); + var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); + App.API.ShowMsgBox(errorMsg, errorMsgTitle); + } } + #endregion + + #region Commands + + private static RelayCommand _customQueryHotkeyCommand; + private static IRelayCommand CustomQueryHotkeyCommand => _customQueryHotkeyCommand ??= new RelayCommand(CustomQueryHotkey); + + private static RelayCommand _globalPluginHotkeyCommand; + private static IRelayCommand GlobalPluginHotkeyCommand => _globalPluginHotkeyCommand ??= new RelayCommand(GlobalPluginHotkey); + + private static RelayCommand _windowPluginHotkeyCommand; + private static IRelayCommand WindowPluginHotkeyCommand => _windowPluginHotkeyCommand ??= new RelayCommand(WindowPluginHotkey); + + private static void CustomQueryHotkey(CustomPluginHotkey customPluginHotkey) + { + if (_mainViewModel.ShouldIgnoreHotkeys()) + return; + + App.API.ShowMainWindow(); + App.API.ChangeQuery(customPluginHotkey.ActionKeyword, true); + } + + private static void GlobalPluginHotkey(GlobalPluginHotkeyPair pair) + { + if (_mainViewModel.ShouldIgnoreHotkeys() || pair.Metadata.Disabled) + return; + + pair.GlobalPluginHotkey.Action?.Invoke(); + } + + private static void WindowPluginHotkey(WindowPluginHotkeyPair pair) + { + // Get selected result + var selectedResult = _mainViewModel.GetSelectedResults().SelectedItem?.Result; + + // Check result nullability + if (selectedResult != null) + { + var pluginId = selectedResult.PluginID; + foreach (var hotkeyModel in pair.HotkeyModels) + { + var metadata = hotkeyModel.Metadata; + var pluginHotkey = hotkeyModel.PluginHotkey; + + if (metadata.ID != pluginId || // Check plugin ID match + metadata.Disabled || // Check plugin enabled state + !selectedResult.HotkeyIds.Contains(pluginHotkey.Id) || // Check hotkey supported state + pluginHotkey.Action == null) // Check action nullability + continue; + + // TODO: Remove return to skip other commands & Organize main window hotkeys + if (pluginHotkey.Action.Invoke(selectedResult)) + App.API.HideMainWindow(); + } + } + } + + #endregion + + // TODO: Deprecated + internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) + { + SetHotkey(hotkey.Hotkey, (s, e) => + { + if (_mainViewModel.ShouldIgnoreHotkeys()) + return; + + App.API.ShowMainWindow(); + App.API.ChangeQuery(hotkey.ActionKeyword, true); + }); + } + + // TODO: Deprecated internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, string hotkeyStr) { var hotkey = new HotkeyModel(hotkeyStr); SetGlobalPluginHotkey(globalHotkey, metadata, hotkey); } + // TODO: Deprecated internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, HotkeyModel hotkey) { var hotkeyStr = hotkey.ToString(); @@ -199,18 +567,7 @@ internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, Plug }); } - /// - /// Plugin Window Hotkeys (Window) - /// - internal static void LoadWindowPluginHotkey() - { - var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(); - foreach (var hotkey in windowPluginHotkeys) - { - SetWindowHotkey(hotkey.Key, hotkey.Value); - } - } - + // TODO: Deprecated internal static void SetWindowHotkey(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) { try @@ -228,16 +585,7 @@ kb.Gesture is KeyGesture keyGesture1 && keyGesture.Modifiers == keyGesture1.Modifiers); if (existingBinding != null) { - // If the hotkey exists, remove the old command - if (_windowHotkeyEvents.ContainsKey(hotkey)) - { - window.InputBindings.Remove(existingBinding); - } - // If the hotkey does not exist, save the old command - else - { - _windowHotkeyEvents[hotkey] = existingBinding.Command; - } + throw new InvalidOperationException($"Key binding {hotkey} already exists"); } // Create and add the new key binding @@ -259,6 +607,7 @@ kb.Gesture is KeyGesture keyGesture1 && } } + // TODO: Deprecated private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) { return new RelayCommand(() => @@ -297,14 +646,10 @@ private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Me return; } } - - if (_windowHotkeyEvents.TryGetValue(hotkey, out var existingCommand)) - { - existingCommand.Execute(null); - } }); } + // TODO: Deprecated internal static void RemoveWindowHotkey(HotkeyModel hotkey) { try @@ -323,13 +668,6 @@ kb.Gesture is KeyGesture keyGesture1 && { window.InputBindings.Remove(existingBinding); } - - // Restore the command if it exists - if (_windowHotkeyEvents.TryGetValue(hotkey, out var command)) - { - var keyBinding = new KeyBinding(command, keyGesture); - window.InputBindings.Add(keyBinding); - } } } catch (Exception e) @@ -344,6 +682,8 @@ kb.Gesture is KeyGesture keyGesture1 && } } + #region Check Hotkey + internal static bool CheckAvailability(HotkeyModel currentHotkey) { try @@ -362,4 +702,33 @@ internal static bool CheckAvailability(HotkeyModel currentHotkey) return false; } + + #endregion + + #region Private Classes + + private class GlobalPluginHotkeyPair + { + public PluginMetadata Metadata { get; } + + public GlobalPluginHotkey GlobalPluginHotkey { get; } + + public GlobalPluginHotkeyPair(PluginMetadata metadata, GlobalPluginHotkey globalPluginHotkey) + { + Metadata = metadata; + GlobalPluginHotkey = globalPluginHotkey; + } + } + + private class WindowPluginHotkeyPair + { + public List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> HotkeyModels { get; } + + public WindowPluginHotkeyPair(List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys) + { + HotkeyModels = hotkeys; + } + } + + #endregion } From 051af06c30c61dcbb7a2194266a17fc804cbefa3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:03:00 +0800 Subject: [PATCH 077/150] Add toggle cmmand for main view model --- Flow.Launcher/ViewModel/MainViewModel.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 7af6ec1a02f..00a2e3a9715 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -496,6 +496,13 @@ private static IReadOnlyList DeepCloneResults(IReadOnlyList resu #region BasicCommands + [RelayCommand] + private void CheckAndToggleFlowLauncher() + { + if (!ShouldIgnoreHotkeys()) + ToggleFlowLauncher(); + } + [RelayCommand] private void OpenSetting() { From 20f065aff9193b684020472139be408b56ff6118 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:03:16 +0800 Subject: [PATCH 078/150] Remove key bindings which will be registered in hotkey mapper --- Flow.Launcher/MainWindow.xaml | 163 ---------------------------------- 1 file changed, 163 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 9ff38a56442..70b6c1bf49e 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -49,169 +49,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From d3324726931d0ac27b7a79c1107019c16ce0dc6a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:03:26 +0800 Subject: [PATCH 079/150] Add todo --- Flow.Launcher/HotkeyControlDialog.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index c7af8c5b8bb..1d42b20db93 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -137,6 +137,7 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) if (tbMsg == null) return; + // TODO: If we need to check !v.Hotkey.IsEmpty && for v.Hotkey? if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData) { var description = string.Format( From 43beef42b889c4a88ce7a52ca5094df4c1c9c393 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:05:28 +0800 Subject: [PATCH 080/150] Use command & parameter for SetGlobalHotkeyWithChefKeys --- Flow.Launcher/Helper/HotKeyMapper.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 1085d141d54..22fff00d3f6 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -345,9 +345,11 @@ private static void SetGlobalHotkeyWithChefKeys(RegisteredHotkeyData hotkeyData) } var hotkeyStr = hotkey.ToString(); + var hotkeyCommand = hotkeyData.Command; + var hotkeyCommandParameter = hotkeyData.CommandParameter; try { - ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, OnToggleHotkeyWithChefKeys); + ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, () => hotkeyCommand.Execute(hotkeyCommandParameter)); ChefKeysManager.Start(); } catch (Exception e) From 39fa79b49dd3e615b0a8400a559e6e1118dc53e1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:07:31 +0800 Subject: [PATCH 081/150] Prepare to deprecation --- Flow.Launcher/Helper/HotKeyMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 22fff00d3f6..8d84f5117fb 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -171,7 +171,7 @@ internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) } // TODO: Deprecated - internal static void OnToggleHotkeyWithChefKeys() + private static void OnToggleHotkeyWithChefKeys() { if (!_mainViewModel.ShouldIgnoreHotkeys()) _mainViewModel.ToggleFlowLauncher(); From 6e94d1668bd6cf737322799093af6e197de7a02d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:09:13 +0800 Subject: [PATCH 082/150] Code quality --- Flow.Launcher/Helper/HotKeyMapper.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 8d84f5117fb..3fc2ec3812f 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -37,11 +37,6 @@ internal static void Initialize() _settings = Ioc.Default.GetService(); InitializeRegisteredHotkeys(); - - foreach (var hotkey in _settings.RegisteredHotkeys) - { - SetHotkey(hotkey); - } } private static void InitializeRegisteredHotkeys() @@ -154,10 +149,11 @@ private static void InitializeRegisteredHotkeys() list.Add(new(RegisteredHotkeyType.PluginWindowHotkey, HotkeyType.SearchWindow, hotkeyModel, "pluginWindowHotkey", WindowPluginHotkeyCommand, new WindowPluginHotkeyPair(windowHotkeys))); } - // Add registered hotkeys + // Add registered hotkeys & Set them foreach (var hotkey in list) { _settings.RegisteredHotkeys.Add(hotkey); + SetHotkey(hotkey); } } From f36ca62e7cc784e186c64ac64e186a0647e16026 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:09:50 +0800 Subject: [PATCH 083/150] Fix typos --- Flow.Launcher/Helper/HotKeyMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 3fc2ec3812f..1bb83b60f67 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -29,7 +29,7 @@ internal static class HotKeyMapper // Registered hotkeys for ActionContext private static List _actionContextRegisteredHotkeys; - #region Initializatin + #region Initialization internal static void Initialize() { From 495e2c1884d31b7628162d9571a99db2270a66e5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:27:16 +0800 Subject: [PATCH 084/150] Revert "Add property changed for RegisteredHotkeyData.Hotkey" This reverts commit 5910d1de19301e6783a3a8216092f4d92edb7982. --- .../Hotkey/RegisteredHotkeyData.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index b34974e4807..b0477d79d60 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -11,7 +11,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey; /// and to display errors if user tries to register a hotkey /// that has already been registered, and optionally provides a way to unregister the hotkey. /// -public class RegisteredHotkeyData : BaseModel +public record RegisteredHotkeyData { /// /// Type of this hotkey in the context of the application. @@ -26,19 +26,7 @@ public class RegisteredHotkeyData : BaseModel /// /// representation of this hotkey. /// - private HotkeyModel _hotkey; - public HotkeyModel Hotkey - { - get => _hotkey; - set - { - if (!_hotkey.Equals(value)) - { - _hotkey = value; - OnPropertyChanged(); - } - } - } + public HotkeyModel Hotkey { get; } /// /// String key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey, From 40f1fc642178e92558f804c7800faa40f045b597 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:29:06 +0800 Subject: [PATCH 085/150] Allow setter for RegisteredHotkeyData.Hotkey --- Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index b0477d79d60..a39702723d9 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -26,7 +26,7 @@ public record RegisteredHotkeyData /// /// representation of this hotkey. /// - public HotkeyModel Hotkey { get; } + public HotkeyModel Hotkey { get; set; } /// /// String key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey, From f63b8bd2fcd3e07db12a029ff21ad4eeb959ec62 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:46:15 +0800 Subject: [PATCH 086/150] Add ToString --- Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index a39702723d9..591d3c00088 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -232,6 +232,12 @@ public RegisteredHotkeyData( CommandParameter = parameter; RemoveHotkey = removeHotkey; } + + /// + public override string ToString() + { + return Hotkey.IsEmpty ? $"{RegisteredType} - {Hotkey}" : $"{RegisteredType} - None"; + } } public enum RegisteredHotkeyType From e41eccc88d6ac5d65b94cb1fc32b18843af5269f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:47:03 +0800 Subject: [PATCH 087/150] Add initialization log information --- Flow.Launcher/Helper/HotKeyMapper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 1bb83b60f67..8c148fe5edf 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -155,6 +155,8 @@ private static void InitializeRegisteredHotkeys() _settings.RegisteredHotkeys.Add(hotkey); SetHotkey(hotkey); } + + App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]"); } #endregion From 3b1e014e0396ed03440dfd0e4c6116d1f478530d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:47:33 +0800 Subject: [PATCH 088/150] Add change support for Flow Launcher hotkeys --- Flow.Launcher/Helper/HotKeyMapper.cs | 85 ++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 8c148fe5edf..4b42e4caae1 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Input; @@ -37,6 +38,8 @@ internal static void Initialize() _settings = Ioc.Default.GetService(); InitializeRegisteredHotkeys(); + + _settings.PropertyChanged += Settings_PropertyChanged; } private static void InitializeRegisteredHotkeys() @@ -159,6 +162,88 @@ private static void InitializeRegisteredHotkeys() App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]"); } + private static void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + // Flow Launcher global hotkeys + case nameof(_settings.Hotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.Toggle, _settings.Hotkey); + break; + + // Flow Launcher window hotkeys + case nameof(_settings.PreviewHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.Preview, _settings.PreviewHotkey); + break; + case nameof(_settings.AutoCompleteHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.AutoComplete, _settings.AutoCompleteHotkey); + break; + case nameof(_settings.AutoCompleteHotkey2): + ChangeRegisteredHotkey(RegisteredHotkeyType.AutoComplete2, _settings.AutoCompleteHotkey2); + break; + case nameof(_settings.SelectNextItemHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.SelectNextItem, _settings.SelectNextItemHotkey); + break; + case nameof(_settings.SelectNextItemHotkey2): + ChangeRegisteredHotkey(RegisteredHotkeyType.SelectNextItem2, _settings.SelectNextItemHotkey2); + break; + case nameof(_settings.SelectPrevItemHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.SelectPrevItem, _settings.SelectPrevItemHotkey); + break; + case nameof(_settings.SelectPrevItemHotkey2): + ChangeRegisteredHotkey(RegisteredHotkeyType.SelectPrevItem2, _settings.SelectPrevItemHotkey2); + break; + case nameof(_settings.SettingWindowHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.SettingWindow, _settings.SettingWindowHotkey); + break; + case nameof(_settings.OpenHistoryHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenHistory, _settings.OpenHistoryHotkey); + break; + case nameof(_settings.OpenContextMenuHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenContextMenu, _settings.OpenContextMenuHotkey); + break; + case nameof(_settings.SelectNextPageHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.SelectNextPage, _settings.SelectNextPageHotkey); + break; + case nameof(_settings.SelectPrevPageHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.SelectPrevPage, _settings.SelectPrevPageHotkey); + break; + case nameof(_settings.CycleHistoryUpHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.CycleHistoryUp, _settings.CycleHistoryUpHotkey); + break; + case nameof(_settings.CycleHistoryDownHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.CycleHistoryDown, _settings.CycleHistoryDownHotkey); + break; + } + } + + private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, string newHotkeyStr) + { + var newHotkey = new HotkeyModel(newHotkeyStr); + ChangeRegisteredHotkey(registeredType, newHotkey); + } + + private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, HotkeyModel newHotkey) + { + // Find the old registered hotkey data item + var registeredHotkeyData = _settings.RegisteredHotkeys.FirstOrDefault(h => h.RegisteredType == registeredType); + + // If it is not found, return + if (registeredHotkeyData == null) + { + return; + } + + // Remove the old hotkey + RemoveHotkey(registeredHotkeyData); + + // Update the hotkey string + registeredHotkeyData.Hotkey = newHotkey; + + // Set the new hotkey + SetHotkey(registeredHotkeyData); + } + #endregion // TODO: Deprecated From 2cf6bfb7250f8ff063221bf0869aeaec42d3606a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 13:50:08 +0800 Subject: [PATCH 089/150] Prepare for deprecation --- Flow.Launcher/Helper/HotKeyMapper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 4b42e4caae1..6f5d63b7bbf 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -674,7 +674,7 @@ kb.Gesture is KeyGesture keyGesture1 && } // Create and add the new key binding - var command = BuildCommand(hotkey, hotkeyModels); + var command = BuildCommand(hotkeyModels); var keyBinding = new KeyBinding(command, keyGesture); window.InputBindings.Add(keyBinding); } @@ -693,7 +693,7 @@ kb.Gesture is KeyGesture keyGesture1 && } // TODO: Deprecated - private static ICommand BuildCommand(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) + private static ICommand BuildCommand(List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) { return new RelayCommand(() => { From 730625c6db3aa300d0240e71da3129528a974676 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 14:02:50 +0800 Subject: [PATCH 090/150] Remove ChangeHotkey event & Remove deprecated functions --- Flow.Launcher/Helper/HotKeyMapper.cs | 55 +------------------ Flow.Launcher/HotkeyControl.xaml.cs | 8 --- .../Resources/Pages/WelcomePage2.xaml | 8 +-- .../Resources/Pages/WelcomePage2.xaml.cs | 8 --- .../ViewModels/SettingsPaneHotkeyViewModel.cs | 7 --- .../Views/SettingsPaneHotkey.xaml | 1 - 6 files changed, 5 insertions(+), 82 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 6f5d63b7bbf..816ad23ebd4 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -246,20 +246,6 @@ private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, #endregion - // TODO: Deprecated - internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) - { - if (!_mainViewModel.ShouldIgnoreHotkeys()) - _mainViewModel.ToggleFlowLauncher(); - } - - // TODO: Deprecated - private static void OnToggleHotkeyWithChefKeys() - { - if (!_mainViewModel.ShouldIgnoreHotkeys()) - _mainViewModel.ToggleFlowLauncher(); - } - // TODO: Deprecated private static void SetHotkey(string hotkeyStr, EventHandler action) { @@ -268,27 +254,7 @@ private static void SetHotkey(string hotkeyStr, EventHandler ac } // TODO: Deprecated - private static void SetWithChefKeys(string hotkeyStr) - { - try - { - ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, OnToggleHotkeyWithChefKeys); - ChefKeysManager.Start(); - } - catch (Exception e) - { - App.API.LogError(ClassName, - string.Format("Error registering hotkey: {0} \nStackTrace:{1}", - e.Message, - e.StackTrace)); - string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); - App.API.ShowMsgBox(errorMsg, errorMsgTitle); - } - } - - // TODO: Deprecated - internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) + private static void SetHotkey(HotkeyModel hotkey, EventHandler action) { if (hotkey.IsEmpty) { @@ -298,12 +264,6 @@ internal static void SetHotkey(HotkeyModel hotkey, EventHandler string hotkeyStr = hotkey.ToString(); try { - if (hotkeyStr == "LWin" || hotkeyStr == "RWin") - { - SetWithChefKeys(hotkeyStr); - return; - } - HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); } catch (Exception e) @@ -324,12 +284,6 @@ internal static void RemoveHotkey(string hotkeyStr) { try { - if (hotkeyStr == "LWin" || hotkeyStr == "RWin") - { - RemoveWithChefKeys(hotkeyStr); - return; - } - if (!string.IsNullOrEmpty(hotkeyStr)) HotkeyManager.Current.Remove(hotkeyStr); } @@ -345,13 +299,6 @@ internal static void RemoveHotkey(string hotkeyStr) } } - // TODO: Deprecated - private static void RemoveWithChefKeys(string hotkeyStr) - { - ChefKeysManager.UnregisterHotkey(hotkeyStr); - ChefKeysManager.Stop(); - } - #region Hotkey Setting private static void SetHotkey(RegisteredHotkeyData hotkeyData) diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 93a07743633..679e7397697 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -242,11 +242,6 @@ public void GetNewHotkey(object sender, RoutedEventArgs e) private async Task OpenHotkeyDialogAsync() { - if (!string.IsNullOrEmpty(Hotkey)) - { - HotKeyMapper.RemoveHotkey(Hotkey); - } - var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, WindowTitle) { Owner = Window.GetWindow(this) @@ -300,8 +295,6 @@ private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) public void Delete() { - if (!string.IsNullOrEmpty(Hotkey)) - HotKeyMapper.RemoveHotkey(Hotkey); Hotkey = ""; SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None)); } @@ -318,7 +311,6 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) { - KeysToDisplay.Add(key); } } diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml index cf0dff9ab37..8ea4974f19e 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml @@ -38,7 +38,7 @@ - + @@ -90,11 +90,12 @@ - + + Text="{DynamicResource Welcome_Page2_Title}" + TextWrapping="WrapWithOverflow" /> WallpaperPathRetrieval.GetWallpaperBrush(); diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 7a7c19dd358..8a58bb4dd6a 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -3,7 +3,6 @@ using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; -using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; @@ -28,12 +27,6 @@ public SettingsPaneHotkeyViewModel(Settings settings) Settings = settings; } - [RelayCommand] - private void SetTogglingHotkey(HotkeyModel hotkey) - { - HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey); - } - [RelayCommand] private void CustomHotkeyDelete() { diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml index 8c5ffd861bf..a211d67101b 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml @@ -32,7 +32,6 @@ Icon="" Sub="{DynamicResource flowlauncherHotkeyToolTip}"> Date: Wed, 2 Jul 2025 14:04:02 +0800 Subject: [PATCH 091/150] Code quality --- Flow.Launcher/Helper/HotKeyMapper.cs | 58 +++++++++++++++------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 816ad23ebd4..bebfa16d535 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -217,33 +217,6 @@ private static void Settings_PropertyChanged(object sender, PropertyChangedEvent } } - private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, string newHotkeyStr) - { - var newHotkey = new HotkeyModel(newHotkeyStr); - ChangeRegisteredHotkey(registeredType, newHotkey); - } - - private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, HotkeyModel newHotkey) - { - // Find the old registered hotkey data item - var registeredHotkeyData = _settings.RegisteredHotkeys.FirstOrDefault(h => h.RegisteredType == registeredType); - - // If it is not found, return - if (registeredHotkeyData == null) - { - return; - } - - // Remove the old hotkey - RemoveHotkey(registeredHotkeyData); - - // Update the hotkey string - registeredHotkeyData.Hotkey = newHotkey; - - // Set the new hotkey - SetHotkey(registeredHotkeyData); - } - #endregion // TODO: Deprecated @@ -509,6 +482,37 @@ kb.Gesture is KeyGesture keyGesture1 && #endregion + #region Hotkey Changing + + private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, string newHotkeyStr) + { + var newHotkey = new HotkeyModel(newHotkeyStr); + ChangeRegisteredHotkey(registeredType, newHotkey); + } + + private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, HotkeyModel newHotkey) + { + // Find the old registered hotkey data item + var registeredHotkeyData = _settings.RegisteredHotkeys.FirstOrDefault(h => h.RegisteredType == registeredType); + + // If it is not found, return + if (registeredHotkeyData == null) + { + return; + } + + // Remove the old hotkey + RemoveHotkey(registeredHotkeyData); + + // Update the hotkey string + registeredHotkeyData.Hotkey = newHotkey; + + // Set the new hotkey + SetHotkey(registeredHotkeyData); + } + + #endregion + #region Commands private static RelayCommand _customQueryHotkeyCommand; From e061f14ec722d3bedb979b8a6b18fb2d3ef5a635 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 14:07:23 +0800 Subject: [PATCH 092/150] Check plugin modified state --- Flow.Launcher/Helper/HotKeyMapper.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index bebfa16d535..c7bcb0a1752 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -535,7 +535,11 @@ private static void CustomQueryHotkey(CustomPluginHotkey customPluginHotkey) private static void GlobalPluginHotkey(GlobalPluginHotkeyPair pair) { - if (_mainViewModel.ShouldIgnoreHotkeys() || pair.Metadata.Disabled) + if (pair.Metadata.Disabled || // Check plugin enabled state + App.API.PluginModified(pair.Metadata.ID)) // Check plugin modified state + return; + + if (_mainViewModel.ShouldIgnoreHotkeys()) return; pair.GlobalPluginHotkey.Action?.Invoke(); @@ -557,6 +561,7 @@ private static void WindowPluginHotkey(WindowPluginHotkeyPair pair) if (metadata.ID != pluginId || // Check plugin ID match metadata.Disabled || // Check plugin enabled state + App.API.PluginModified(metadata.ID) || // Check plugin modified state !selectedResult.HotkeyIds.Contains(pluginHotkey.Id) || // Check hotkey supported state pluginHotkey.Action == null) // Check action nullability continue; From 1057f4d2f652b25e0ab2410af7e77d2d1dfac7b0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 14:09:29 +0800 Subject: [PATCH 093/150] Improve code quality --- Flow.Launcher/Helper/HotKeyMapper.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index c7bcb0a1752..af936d59f85 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -535,14 +535,17 @@ private static void CustomQueryHotkey(CustomPluginHotkey customPluginHotkey) private static void GlobalPluginHotkey(GlobalPluginHotkeyPair pair) { - if (pair.Metadata.Disabled || // Check plugin enabled state - App.API.PluginModified(pair.Metadata.ID)) // Check plugin modified state + var metadata = pair.Metadata; + var pluginHotkey = pair.GlobalPluginHotkey; + + if (metadata.Disabled || // Check plugin enabled state + App.API.PluginModified(metadata.ID)) // Check plugin modified state return; if (_mainViewModel.ShouldIgnoreHotkeys()) return; - pair.GlobalPluginHotkey.Action?.Invoke(); + pluginHotkey.Action?.Invoke(); } private static void WindowPluginHotkey(WindowPluginHotkeyPair pair) From d46deddb9485723a5c5bf43c93451cece872c82b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 14:11:18 +0800 Subject: [PATCH 094/150] Remove Settings.CustomPluginHotkeys null check --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 2 -- Flow.Launcher/Helper/HotKeyMapper.cs | 9 +++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index 77febde9d5b..cc7b0fee996 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -30,8 +30,6 @@ private void btnAdd_OnClick(object sender, RoutedEventArgs e) { if (!update) { - _settings.CustomPluginHotkeys ??= new ObservableCollection(); - var pluginHotkey = new CustomPluginHotkey { Hotkey = HotkeyControl.CurrentHotkey.ToString(), ActionKeyword = tbAction.Text diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index af936d59f85..058941f6c78 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -105,14 +105,11 @@ private static void InitializeRegisteredHotkeys() new(RegisteredHotkeyType.CycleHistoryUp, HotkeyType.SearchWindow, _settings.CycleHistoryUpHotkey, "CycleHistoryUpHotkey", _mainViewModel.ReverseHistoryCommand, null, () => _settings.CycleHistoryUpHotkey = ""), new(RegisteredHotkeyType.CycleHistoryDown, HotkeyType.SearchWindow, _settings.CycleHistoryDownHotkey, "CycleHistoryDownHotkey", _mainViewModel.ForwardHistoryCommand, null, () => _settings.CycleHistoryDownHotkey = "") }; - + // Custom query global hotkeys - if (_settings.CustomPluginHotkeys != null) + foreach (var customPluginHotkey in _settings.CustomPluginHotkeys) { - foreach (var customPluginHotkey in _settings.CustomPluginHotkeys) - { - list.Add(new(RegisteredHotkeyType.CustomQuery, HotkeyType.Global, customPluginHotkey.Hotkey, "customQueryHotkey", CustomQueryHotkeyCommand, customPluginHotkey, () => customPluginHotkey.Hotkey = "")); - } + list.Add(new(RegisteredHotkeyType.CustomQuery, HotkeyType.Global, customPluginHotkey.Hotkey, "customQueryHotkey", CustomQueryHotkeyCommand, customPluginHotkey, () => customPluginHotkey.Hotkey = "")); } // Plugin hotkeys From fd3eef46f43d1ab1822c76d0f2070336fcb40e45 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 14:33:46 +0800 Subject: [PATCH 095/150] Add Equals & GetHashCode for CustomPluginHotkey --- .../UserSettings/PluginHotkey.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs index b67bba57ff9..614703fa9a5 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs @@ -1,4 +1,5 @@ -using Flow.Launcher.Plugin; +using System; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.UserSettings { @@ -19,5 +20,20 @@ public string Hotkey } public string ActionKeyword { get; set; } + + public override bool Equals(object other) + { + if (other is CustomPluginHotkey otherHotkey) + { + return Hotkey == otherHotkey.Hotkey && ActionKeyword == otherHotkey.ActionKeyword; + } + + return false; + } + + public override int GetHashCode() + { + return HashCode.Combine(Hotkey, ActionKeyword); + } } } From 96e8eae83e00b1fbf19966e9da79863f5b6c4bda Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 14:34:00 +0800 Subject: [PATCH 096/150] Revert "Add property changed for CustomPluginHotkey" This reverts commit 1d2aa96dcc94faee06547bf7379b0bf77bce97cc. --- .../UserSettings/PluginHotkey.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs index 614703fa9a5..60d0a99b345 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs @@ -5,20 +5,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings { public class CustomPluginHotkey : BaseModel { - private string _hotkey = string.Empty; - public string Hotkey - { - get => _hotkey; - set - { - if (_hotkey != value) - { - _hotkey = value; - OnPropertyChanged(); - } - } - } - + public string Hotkey { get; set; } public string ActionKeyword { get; set; } public override bool Equals(object other) From d0fc344b546b41952422ea0661757b7b87535fcf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 14:40:25 +0800 Subject: [PATCH 097/150] Explictly implement PropertyChanged --- .../UserSettings/Settings.cs | 225 ++++++++++++++++-- 1 file changed, 210 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 4e3bd7e378c..88c4cfb8194 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -39,25 +39,220 @@ public void Save() _storage.Save(); } - public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; public string OpenResultModifiers { get; set; } = KeyConstant.Alt; public string ColorScheme { get; set; } = "System"; public bool ShowOpenResultHotkey { get; set; } = true; public double WindowSize { get; set; } = 580; - public string PreviewHotkey { get; set; } = $"F1"; - public string AutoCompleteHotkey { get; set; } = $"{KeyConstant.Ctrl} + Tab"; - public string AutoCompleteHotkey2 { get; set; } = $""; - public string SelectNextItemHotkey { get; set; } = $"Tab"; - public string SelectNextItemHotkey2 { get; set; } = $""; - public string SelectPrevItemHotkey { get; set; } = $"Shift + Tab"; - public string SelectPrevItemHotkey2 { get; set; } = $""; - public string SelectNextPageHotkey { get; set; } = $"PageUp"; - public string SelectPrevPageHotkey { get; set; } = $"PageDown"; - public string OpenContextMenuHotkey { get; set; } = $"Ctrl+O"; - public string SettingWindowHotkey { get; set; } = $"Ctrl+I"; - public string OpenHistoryHotkey { get; set; } = $"Ctrl+H"; - public string CycleHistoryUpHotkey { get; set; } = $"{KeyConstant.Alt} + Up"; - public string CycleHistoryDownHotkey { get; set; } = $"{KeyConstant.Alt} + Down"; + + private string _hotkey = $"{KeyConstant.Alt} + {KeyConstant.Space}"; + public string Hotkey + { + get => _hotkey; + set + { + if (_hotkey != value) + { + _hotkey = value; + OnPropertyChanged(); + } + } + } + + private string _previewHotkey = "F1"; + public string PreviewHotkey + { + get => _previewHotkey; + set + { + if (_previewHotkey != value) + { + _previewHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _autoCompleteHotkey = $"{KeyConstant.Ctrl} + Tab"; + public string AutoCompleteHotkey + { + get => _autoCompleteHotkey; + set + { + if (_autoCompleteHotkey != value) + { + _autoCompleteHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _autoCompleteHotkey2 = ""; + public string AutoCompleteHotkey2 + { + get => _autoCompleteHotkey2; + set + { + if (_autoCompleteHotkey2 != value) + { + _autoCompleteHotkey2 = value; + OnPropertyChanged(); + } + } + } + + private string _selectNextItemHotkey = "Tab"; + public string SelectNextItemHotkey + { + get => _selectNextItemHotkey; + set + { + if (_selectNextItemHotkey != value) + { + _selectNextItemHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _selectNextItemHotkey2 = ""; + public string SelectNextItemHotkey2 + { + get => _selectNextItemHotkey2; + set + { + if (_selectNextItemHotkey2 != value) + { + _selectNextItemHotkey2 = value; + OnPropertyChanged(); + } + } + } + + private string _selectPrevItemHotkey = "Shift + Tab"; + public string SelectPrevItemHotkey + { + get => _selectPrevItemHotkey; + set + { + if (_selectPrevItemHotkey != value) + { + _selectPrevItemHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _selectPrevItemHotkey2 = ""; + public string SelectPrevItemHotkey2 + { + get => _selectPrevItemHotkey2; + set + { + if (_selectPrevItemHotkey2 != value) + { + _selectPrevItemHotkey2 = value; + OnPropertyChanged(); + } + } + } + + private string _selectNextPageHotkey = "PageUp"; + public string SelectNextPageHotkey + { + get => _selectNextPageHotkey; + set + { + if (_selectNextPageHotkey != value) + { + _selectNextPageHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _selectPrevPageHotkey = "PageDown"; + public string SelectPrevPageHotkey + { + get => _selectPrevPageHotkey; + set + { + if (_selectPrevPageHotkey != value) + { + _selectPrevPageHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _openContextMenuHotkey = "Ctrl+O"; + public string OpenContextMenuHotkey + { + get => _openContextMenuHotkey; + set + { + if (_openContextMenuHotkey != value) + { + _openContextMenuHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _settingWindowHotkey = "Ctrl+I"; + public string SettingWindowHotkey + { + get => _settingWindowHotkey; + set + { + if (_settingWindowHotkey != value) + { + _settingWindowHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _openHistoryHotkey = "Ctrl+H"; + public string OpenHistoryHotkey + { + get => _openHistoryHotkey; + set + { + if (_openHistoryHotkey != value) + { + _openHistoryHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _cycleHistoryUpHotkey = $"{KeyConstant.Alt} + Up"; + public string CycleHistoryUpHotkey + { + get => _cycleHistoryUpHotkey; + set + { + if (_cycleHistoryUpHotkey != value) + { + _cycleHistoryUpHotkey = value; + OnPropertyChanged(); + } + } + } + + private string _cycleHistoryDownHotkey = $"{KeyConstant.Alt} + Down"; + public string CycleHistoryDownHotkey + { + get => _cycleHistoryDownHotkey; + set + { + if (_cycleHistoryDownHotkey != value) + { + _cycleHistoryDownHotkey = value; + OnPropertyChanged(); + } + } + } private string _language = Constant.SystemLanguageCode; public string Language From c4ce8fe580297ee09f616f40cc16409222fc8913 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 15:17:18 +0800 Subject: [PATCH 098/150] Add check for invalid query shortcuts --- Flow.Launcher/CustomShortcutSetting.xaml.cs | 2 ++ Flow.Launcher/Languages/en.xaml | 1 + .../ViewModels/SettingsPaneHotkeyViewModel.cs | 12 ++++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs index e180f657024..f4644a267e9 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml.cs +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -43,12 +43,14 @@ private void BtnAdd_OnClick(object sender, RoutedEventArgs e) App.API.ShowMsgBox(App.API.GetTranslation("emptyShortcut")); return; } + // Check if key is modified or adding a new one if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key)) { App.API.ShowMsgBox(App.API.GetTranslation("duplicateShortcut")); return; } + DialogResult = !update || originalKey != Key || originalValue != Value; Close(); } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 55bbed7e087..7c94030eff0 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -446,6 +446,7 @@ Shortcut already exists, please enter a new Shortcut or edit the existing one. Shortcut and/or its expansion is empty. + Shortcut is invalid Save diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index 8a58bb4dd6a..bec170ec85c 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -107,10 +107,18 @@ private void CustomShortcutEdit() return; } - var window = new CustomShortcutSetting(item.Key, item.Value, this); + var settingItem = Settings.CustomShortcuts.FirstOrDefault(o => + o.Key == item.Key && o.Value == item.Value); + if (settingItem == null) + { + App.API.ShowMsgBox(App.API.GetTranslation("invalidShortcut")); + return; + } + + var window = new CustomShortcutSetting(settingItem.Key, settingItem.Value, this); if (window.ShowDialog() is not true) return; - var index = Settings.CustomShortcuts.IndexOf(item); + var index = Settings.CustomShortcuts.IndexOf(settingItem); Settings.CustomShortcuts[index] = new CustomShortcutModel(window.Key, window.Value); } From 476ad9be9b85b28191584bbed36c0ba71094347f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 15:17:35 +0800 Subject: [PATCH 099/150] Add constructor for CustomPluginHotkey --- Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs index 60d0a99b345..0c5c3802879 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginHotkey.cs @@ -8,6 +8,12 @@ public class CustomPluginHotkey : BaseModel public string Hotkey { get; set; } public string ActionKeyword { get; set; } + public CustomPluginHotkey(string hotkey, string actionKeyword) + { + Hotkey = hotkey; + ActionKeyword = actionKeyword; + } + public override bool Equals(object other) { if (other is CustomPluginHotkey otherHotkey) From 42e20352e1d6f25796ad923e35661a898b15486b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 15:18:48 +0800 Subject: [PATCH 100/150] Improve string --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 7c94030eff0..cc6fa7036f4 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -431,7 +431,7 @@ Press a custom hotkey to open Flow Launcher and input the specified query automatically. Preview Hotkey is unavailable, please select a new hotkey - Invalid plugin hotkey + Hotkey is invalid Update Binding Hotkey Current hotkey is unavailable. From ff996697727ec914b0b91f3d6628218ca67ce04b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 15:46:19 +0800 Subject: [PATCH 101/150] Refactor custom query hotkey setting window --- Flow.Launcher/CustomQueryHotkeySetting.xaml | 18 ++++- .../CustomQueryHotkeySetting.xaml.cs | 68 +++++++------------ Flow.Launcher/Languages/en.xaml | 1 + .../ViewModels/SettingsPaneHotkeyViewModel.cs | 23 +++++-- 4 files changed, 61 insertions(+), 49 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index 0171e6d79c3..9575f812181 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -119,7 +119,8 @@ Grid.Column="1" Margin="10" HorizontalAlignment="Stretch" - VerticalAlignment="Center" /> + VerticalAlignment="Center" + Text="{Binding ActionKeyword}" /> diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index cc7b0fee996..685fdf00ab0 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -1,71 +1,52 @@ -using System.Collections.ObjectModel; -using System.Linq; -using System.Windows; -using System.Windows.Input; +using System.Windows; using System.Windows.Controls; -using Flow.Launcher.Helper; +using System.Windows.Input; using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher { public partial class CustomQueryHotkeySetting : Window { - private readonly Settings _settings; + public string Hotkey { get; set; } = string.Empty; + public string ActionKeyword { get; set; } = string.Empty; - private bool update; - private CustomPluginHotkey updateCustomHotkey; + private readonly bool update; + private readonly CustomPluginHotkey originalCustomHotkey; - public CustomQueryHotkeySetting(Settings settings) + public CustomQueryHotkeySetting() { - _settings = settings; InitializeComponent(); + lblAdd.Visibility = Visibility.Visible; } - private void BtnCancel_OnClick(object sender, RoutedEventArgs e) + public CustomQueryHotkeySetting(CustomPluginHotkey hotkey) { - Close(); + originalCustomHotkey = hotkey; + update = true; + ActionKeyword = originalCustomHotkey.ActionKeyword; + InitializeComponent(); + lblUpdate.Visibility = Visibility.Visible; + HotkeyControl.SetHotkey(originalCustomHotkey.Hotkey, false); } - private void btnAdd_OnClick(object sender, RoutedEventArgs e) + private void BtnCancel_OnClick(object sender, RoutedEventArgs e) { - if (!update) - { - var pluginHotkey = new CustomPluginHotkey - { - Hotkey = HotkeyControl.CurrentHotkey.ToString(), ActionKeyword = tbAction.Text - }; - _settings.CustomPluginHotkeys.Add(pluginHotkey); - - HotKeyMapper.SetCustomQueryHotkey(pluginHotkey); - } - else - { - var oldHotkey = updateCustomHotkey.Hotkey; - updateCustomHotkey.ActionKeyword = tbAction.Text; - updateCustomHotkey.Hotkey = HotkeyControl.CurrentHotkey.ToString(); - //remove origin hotkey - HotKeyMapper.RemoveHotkey(oldHotkey); - HotKeyMapper.SetCustomQueryHotkey(updateCustomHotkey); - } - + DialogResult = false; Close(); } - public void UpdateItem(CustomPluginHotkey item) + private void btnAdd_OnClick(object sender, RoutedEventArgs e) { - updateCustomHotkey = _settings.CustomPluginHotkeys.FirstOrDefault(o => - o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); - if (updateCustomHotkey == null) + Hotkey = HotkeyControl.CurrentHotkey.ToString(); + + if (string.IsNullOrEmpty(Hotkey) && string.IsNullOrEmpty(ActionKeyword)) { - App.API.ShowMsgBox(App.API.GetTranslation("invalidPluginHotkey")); - Close(); + App.API.ShowMsgBox(App.API.GetTranslation("emptyPluginHotkey")); return; } - tbAction.Text = updateCustomHotkey.ActionKeyword; - HotkeyControl.SetHotkey(updateCustomHotkey.Hotkey, false); - update = true; - lblAdd.Text = App.API.GetTranslation("update"); + DialogResult = !update || originalCustomHotkey.Hotkey != Hotkey || originalCustomHotkey.ActionKeyword != ActionKeyword; + Close(); } private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) @@ -77,6 +58,7 @@ private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e) { + DialogResult = false; Close(); } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index cc6fa7036f4..a48cdae89b1 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -438,6 +438,7 @@ This hotkey is reserved for "{0}" and can't be used. Please choose another hotkey. This hotkey is already in use by "{0}". If you press "Overwrite", it will be removed from "{0}". Press the keys you want to use for this function. + Hotkey and action keyword are empty Custom Query Shortcut diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index bec170ec85c..e15961d8cc4 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -62,15 +62,30 @@ private void CustomHotkeyEdit() return; } - var window = new CustomQueryHotkeySetting(Settings); - window.UpdateItem(item); - window.ShowDialog(); + var settingItem = Settings.CustomPluginHotkeys.FirstOrDefault(o => + o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey); + if (settingItem == null) + { + App.API.ShowMsgBox(App.API.GetTranslation("invalidPluginHotkey")); + return; + } + + var window = new CustomQueryHotkeySetting(settingItem); + if (window.ShowDialog() is not true) return; + + var index = Settings.CustomPluginHotkeys.IndexOf(settingItem); + Settings.CustomPluginHotkeys[index] = new CustomPluginHotkey(window.Hotkey, window.ActionKeyword); } [RelayCommand] private void CustomHotkeyAdd() { - new CustomQueryHotkeySetting(Settings).ShowDialog(); + var window = new CustomQueryHotkeySetting(); + if (window.ShowDialog() is true) + { + var customHotkey = new CustomPluginHotkey(window.Hotkey, window.ActionKeyword); + Settings.CustomPluginHotkeys.Add(customHotkey); + } } [RelayCommand] From 5fa3becb5c761b7e1aae9ea92ece3483fcfe232f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 15:52:42 +0800 Subject: [PATCH 102/150] Add hotkey change events for custom query hotkeys --- Flow.Launcher/Helper/HotKeyMapper.cs | 96 ++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 058941f6c78..f4a4b121f9a 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Windows; @@ -40,6 +41,7 @@ internal static void Initialize() InitializeRegisteredHotkeys(); _settings.PropertyChanged += Settings_PropertyChanged; + _settings.CustomPluginHotkeys.CollectionChanged += CustomPluginHotkeys_CollectionChanged; } private static void InitializeRegisteredHotkeys() @@ -109,7 +111,7 @@ private static void InitializeRegisteredHotkeys() // Custom query global hotkeys foreach (var customPluginHotkey in _settings.CustomPluginHotkeys) { - list.Add(new(RegisteredHotkeyType.CustomQuery, HotkeyType.Global, customPluginHotkey.Hotkey, "customQueryHotkey", CustomQueryHotkeyCommand, customPluginHotkey, () => customPluginHotkey.Hotkey = "")); + list.Add(GetRegisteredHotkeyData(customPluginHotkey)); } // Plugin hotkeys @@ -159,6 +161,8 @@ private static void InitializeRegisteredHotkeys() App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]"); } + #region Hotkey Change Events + private static void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) @@ -214,6 +218,83 @@ private static void Settings_PropertyChanged(object sender, PropertyChangedEvent } } + private static void CustomPluginHotkeys_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var item in e.NewItems) + { + if (item is CustomPluginHotkey customPluginHotkey) + { + var hotkeyData = GetRegisteredHotkeyData(customPluginHotkey); + _settings.RegisteredHotkeys.Add(hotkeyData); + SetHotkey(hotkeyData); + } + } + break; + case NotifyCollectionChangedAction.Remove: + foreach (var item in e.OldItems) + { + if (item is CustomPluginHotkey customPluginHotkey) + { + var hotkeyData = SearchRegisteredHotkeyData(customPluginHotkey); + _settings.RegisteredHotkeys.Remove(hotkeyData); + RemoveHotkey(hotkeyData); + } + } + break; + case NotifyCollectionChangedAction.Replace: + foreach (var item in e.OldItems) + { + if (item is CustomPluginHotkey customPluginHotkey) + { + var hotkeyData = SearchRegisteredHotkeyData(customPluginHotkey); + _settings.RegisteredHotkeys.Remove(hotkeyData); + RemoveHotkey(hotkeyData); + } + } + foreach (var item in e.NewItems) + { + if (item is CustomPluginHotkey customPluginHotkey) + { + var hotkeyData = GetRegisteredHotkeyData(customPluginHotkey); + _settings.RegisteredHotkeys.Add(hotkeyData); + SetHotkey(hotkeyData); + } + } + break; + } + } + + #endregion + + #endregion + + #region Custom Query Hotkey + + private static RegisteredHotkeyData GetRegisteredHotkeyData(CustomPluginHotkey customPluginHotkey) + { + return new(RegisteredHotkeyType.CustomQuery, HotkeyType.Global, customPluginHotkey.Hotkey, "customQueryHotkey", CustomQueryHotkeyCommand, customPluginHotkey, () => ClearHotkeyForCustomQueryHotkey(customPluginHotkey)); + } + + private static RegisteredHotkeyData SearchRegisteredHotkeyData(CustomPluginHotkey customPluginHotkey) + { + return _settings.RegisteredHotkeys.FirstOrDefault(h => h.RegisteredType == RegisteredHotkeyType.CustomQuery && + customPluginHotkey.Equals(h.CommandParameter)); + } + + private static void ClearHotkeyForCustomQueryHotkey(CustomPluginHotkey customPluginHotkey) + { + // Clear hotkey for custom query hotkey + customPluginHotkey.Hotkey = string.Empty; + + // Remove hotkey events + var hotkeyData = SearchRegisteredHotkeyData(customPluginHotkey); + _settings.RegisteredHotkeys.Remove(hotkeyData); + RemoveHotkey(hotkeyData); + } + #endregion // TODO: Deprecated @@ -575,19 +656,6 @@ private static void WindowPluginHotkey(WindowPluginHotkeyPair pair) #endregion - // TODO: Deprecated - internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) - { - SetHotkey(hotkey.Hotkey, (s, e) => - { - if (_mainViewModel.ShouldIgnoreHotkeys()) - return; - - App.API.ShowMainWindow(); - App.API.ChangeQuery(hotkey.ActionKeyword, true); - }); - } - // TODO: Deprecated internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, string hotkeyStr) { From 698fe792650af2605cf1cb54ad36146ebcf9963f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 16:03:46 +0800 Subject: [PATCH 103/150] Remove unused removing --- .../SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs index e15961d8cc4..2cd3b66c440 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs @@ -1,7 +1,6 @@ using System.Linq; using System.Windows; using CommunityToolkit.Mvvm.Input; -using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; @@ -48,7 +47,6 @@ private void CustomHotkeyDelete() if (result is MessageBoxResult.Yes) { Settings.CustomPluginHotkeys.Remove(item); - HotKeyMapper.RemoveHotkey(item.Hotkey); } } From 7d0cf1fb7a2f7e2ec9154931e2a901d9f8fa7235 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 16:07:54 +0800 Subject: [PATCH 104/150] Add plugin hotkey type --- Flow.Launcher/HotkeyControl.xaml.cs | 19 ++++++++++++++++++- .../Views/SettingsPaneHotkey.xaml.cs | 3 ++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 679e7397697..9e3b28f0a1c 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -110,7 +110,10 @@ public enum HotkeyType SelectPrevItemHotkey, SelectPrevItemHotkey2, SelectNextItemHotkey, - SelectNextItemHotkey2 + SelectNextItemHotkey2, + // Plugin hotkeys + GlobalPluginHotkey, + WindowPluginHotkey, } // We can initialize settings in static field because it has been constructed in App constuctor @@ -142,6 +145,9 @@ public string Hotkey HotkeyType.SelectPrevItemHotkey2 => _settings.SelectPrevItemHotkey2, HotkeyType.SelectNextItemHotkey => _settings.SelectNextItemHotkey, HotkeyType.SelectNextItemHotkey2 => _settings.SelectNextItemHotkey2, + // Plugin hotkeys + HotkeyType.GlobalPluginHotkey => hotkey, + HotkeyType.WindowPluginHotkey => hotkey, _ => throw new System.NotImplementedException("Hotkey type not set") }; } @@ -201,6 +207,17 @@ public string Hotkey case HotkeyType.SelectNextItemHotkey2: _settings.SelectNextItemHotkey2 = value; break; + // Plugin hotkeys + case HotkeyType.GlobalPluginHotkey: + // We should not save it to settings here because it is a custom plugin hotkey + // and it will be saved in the plugin settings + hotkey = value; + break; + case HotkeyType.WindowPluginHotkey: + // We should not save it to settings here because it is a custom plugin hotkey + // and it will be saved in the plugin settings + hotkey = value; + break; default: throw new System.NotImplementedException("Hotkey type not set"); } diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index de499a717be..e32bc2dbe36 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -80,7 +80,8 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) { var hotkeyControl = new HotkeyControl { - Type = HotkeyControl.HotkeyType.CustomQueryHotkey, + Type = hotkey.HotkeyType == HotkeyType.Global ? + HotkeyControl.HotkeyType.GlobalPluginHotkey : HotkeyControl.HotkeyType.WindowPluginHotkey, DefaultHotkey = hotkey.DefaultHotkey, ValidateKeyGesture = true }; From 805b8efe0422ff9cec0710c62066f7579e4917f5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 17:05:13 +0800 Subject: [PATCH 105/150] No need to check hotkey command when removing --- Flow.Launcher/Helper/HotKeyMapper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index f4a4b121f9a..cd7a501e37c 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -374,8 +374,7 @@ private static void SetHotkey(RegisteredHotkeyData hotkeyData) private static void RemoveHotkey(RegisteredHotkeyData hotkeyData) { if (hotkeyData is null || // Hotkey data is invalid - hotkeyData.Hotkey.IsEmpty || // Hotkey is none - hotkeyData.Command is null) // No need to set - it is a system command + hotkeyData.Hotkey.IsEmpty) // Hotkey is none { return; } From 503bc485cb63233c76e8ed4ae1bf21dc0ec23746 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 17:44:55 +0800 Subject: [PATCH 106/150] Add empty for hotkey model --- Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index bcfd795e993..6d2abff4c15 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -51,6 +51,8 @@ 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); From db1c1b2d97b4ed286eaa7fb14078ebc620916d2a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 17:48:56 +0800 Subject: [PATCH 107/150] Use changed event for plugin hotkeys --- Flow.Launcher.Core/Plugin/PluginManager.cs | 45 +++++++++++++++++----- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index bb1f9b5b357..df5d3f99dcd 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -34,6 +34,8 @@ public static class PluginManager public static readonly HashSet GlobalPlugins = new(); public static readonly Dictionary NonGlobalPlugins = new(); + public static Action 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(); @@ -41,6 +43,7 @@ public static class PluginManager private static PluginsSettings Settings; private static List _metadatas; private static readonly List _modifiedPlugins = new(); + private static readonly Dictionary> _windowPluginHotkeys = new(); /// @@ -491,27 +494,28 @@ private static void InitializeWindowPluginHotkeys(Dictionary h.Id == pluginHotkey.Id); var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id); - var oldHotkey = settingHotkeyItem.Hotkey; + 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; - return oldHotkey; + PluginHotkeyChanged?.Invoke(new PluginHotkeyChangedEvent(oldHotkey, newHotkey, plugin, pluginHotkey)); } - public static (HotkeyModel Old, HotkeyModel New) ChangePluginHotkey(PluginMetadata plugin, SearchWindowPluginHotkey pluginHotkey, HotkeyModel newHotkey) + 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 oldHotkey = settingHotkeyItem.Hotkey; + var oldHotkeyStr = settingHotkeyItem.Hotkey; var converter = new KeyGestureConverter(); - var oldHotkeyModel = new HotkeyModel(oldHotkey); + var oldHotkey = new HotkeyModel(oldHotkeyStr); var newHotkeyStr = newHotkey.ToString(); // Update hotkey in plugin metadata & setting @@ -519,8 +523,8 @@ public static (HotkeyModel Old, HotkeyModel New) ChangePluginHotkey(PluginMetada 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(); + var oldHotkeyModels = _windowPluginHotkeys[oldHotkey]; + _windowPluginHotkeys[oldHotkey] = oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id).ToList(); if (_windowPluginHotkeys.TryGetValue(newHotkey, out var newHotkeyModels)) { @@ -536,7 +540,7 @@ public static (HotkeyModel Old, HotkeyModel New) ChangePluginHotkey(PluginMetada }; } - return (oldHotkeyModel, newHotkey); + PluginHotkeyChanged?.Invoke(new PluginHotkeyChangedEvent(oldHotkey, newHotkey, plugin, pluginHotkey)); } public static bool ActionKeywordRegistered(string actionKeyword) @@ -802,5 +806,28 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool remo } #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 } } From c4fbb3dbec2b2b0e3d1258ddb410b27291667d11 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 18:01:41 +0800 Subject: [PATCH 108/150] Check count --- Flow.Launcher.Core/Plugin/PluginManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index df5d3f99dcd..ea4ae72e104 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -525,6 +525,10 @@ public static void ChangePluginHotkey(PluginMetadata plugin, SearchWindowPluginH // 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)) { From dad681d0a358bc515ad6e512296659c467f478c4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 18:02:20 +0800 Subject: [PATCH 109/150] Use callback to check plugin hotkey change --- Flow.Launcher/Helper/HotKeyMapper.cs | 276 ++++++++------------------- 1 file changed, 82 insertions(+), 194 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index cd7a501e37c..614116c2658 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -13,7 +13,6 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; -using NHotkey; using NHotkey.Wpf; namespace Flow.Launcher.Helper; @@ -42,6 +41,7 @@ internal static void Initialize() _settings.PropertyChanged += Settings_PropertyChanged; _settings.CustomPluginHotkeys.CollectionChanged += CustomPluginHotkeys_CollectionChanged; + PluginManager.PluginHotkeyChanged += PluginManager_PluginHotkeyChanged; } private static void InitializeRegisteredHotkeys() @@ -127,12 +127,7 @@ private static void InitializeRegisteredHotkeys() if (hotkey.HotkeyType == HotkeyType.Global && hotkey is GlobalPluginHotkey globalHotkey) { var hotkeyStr = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; - // TODO: Support removeAction - Action removeHotkeyAction = hotkey.Editable ? - /*() => metadata.PluginHotkeys.RemoveAll(h => h.Id == hotkey.Id) :*/ null: - null; - // TODO: Handle pluginGlobalHotkey & get translation from PluginManager - list.Add(new(RegisteredHotkeyType.PluginGlobalHotkey, HotkeyType.Global, hotkeyStr, "pluginGlobalHotkey", GlobalPluginHotkeyCommand, new GlobalPluginHotkeyPair(metadata, globalHotkey), () => { })); + list.Add(GetRegisteredHotkeyData(new(hotkeyStr), metadata, globalHotkey)); } } } @@ -143,12 +138,7 @@ private static void InitializeRegisteredHotkeys() { var hotkeyModel = hotkey.Key; var windowHotkeys = hotkey.Value; - // TODO: Support removeAction - Action removeHotkeysAction = windowHotkeys.All(h => h.SearchWindowPluginHotkey.Editable) ? - /*() => hotkeyModel.Metadata.PluginWindowHotkeys.RemoveAll(h => h.SearchWindowPluginHotkey.Editable) :*/ null : - null; - // TODO: Handle pluginWindowHotkey & get translation from PluginManager - list.Add(new(RegisteredHotkeyType.PluginWindowHotkey, HotkeyType.SearchWindow, hotkeyModel, "pluginWindowHotkey", WindowPluginHotkeyCommand, new WindowPluginHotkeyPair(windowHotkeys))); + list.Add(GetRegisteredHotkeyData(hotkeyModel, windowHotkeys)); } // Add registered hotkeys & Set them @@ -161,6 +151,8 @@ private static void InitializeRegisteredHotkeys() App.API.LogDebug(ClassName, $"Initialize {_settings.RegisteredHotkeys.Count} hotkeys:\n[\n\t{string.Join(",\n\t", _settings.RegisteredHotkeys)}\n]"); } + #endregion + #region Hotkey Change Events private static void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) @@ -267,7 +259,46 @@ private static void CustomPluginHotkeys_CollectionChanged(object sender, NotifyC } } - #endregion + private static void PluginManager_PluginHotkeyChanged(PluginManager.PluginHotkeyChangedEvent e) + { + var oldHotkey = e.OldHotkey; + var newHotkey = e.NewHotkey; + var metadata = e.Metadata; + var pluginHotkey = e.PluginHotkey; + + if (pluginHotkey is GlobalPluginHotkey globalPluginHotkey) + { + var hotkeyData = SearchRegisteredHotkeyData(metadata, globalPluginHotkey); + RemoveHotkey(hotkeyData); + hotkeyData.Hotkey = newHotkey; + SetHotkey(hotkeyData); + } + else if (pluginHotkey is SearchWindowPluginHotkey) + { + // Search hotkey & Remove registered hotkey data & Unregister hotkeys + var oldHotkeyData = SearchRegisteredHotkeyData(RegisteredHotkeyType.PluginWindowHotkey, oldHotkey); + _settings.RegisteredHotkeys.Remove(oldHotkeyData); + RemoveHotkey(oldHotkeyData); + var newHotkeyData = SearchRegisteredHotkeyData(RegisteredHotkeyType.PluginWindowHotkey, newHotkey); + _settings.RegisteredHotkeys.Remove(newHotkeyData); + RemoveHotkey(newHotkeyData); + + // Get hotkey data & Add new registered hotkeys & Register hotkeys + var windowPluginHotkeys = PluginManager.GetWindowPluginHotkeys(); + if (windowPluginHotkeys.TryGetValue(oldHotkey, out var oldHotkeyModels)) + { + oldHotkeyData = GetRegisteredHotkeyData(oldHotkey, oldHotkeyModels); + _settings.RegisteredHotkeys.Add(oldHotkeyData); + SetHotkey(oldHotkeyData); + } + if (windowPluginHotkeys.TryGetValue(newHotkey, out var newHotkeyModels)) + { + newHotkeyData = GetRegisteredHotkeyData(newHotkey, newHotkeyModels); + _settings.RegisteredHotkeys.Add(newHotkeyData); + SetHotkey(newHotkeyData); + } + } + } #endregion @@ -280,7 +311,8 @@ private static RegisteredHotkeyData GetRegisteredHotkeyData(CustomPluginHotkey c private static RegisteredHotkeyData SearchRegisteredHotkeyData(CustomPluginHotkey customPluginHotkey) { - return _settings.RegisteredHotkeys.FirstOrDefault(h => h.RegisteredType == RegisteredHotkeyType.CustomQuery && + return _settings.RegisteredHotkeys.FirstOrDefault(h => + h.RegisteredType == RegisteredHotkeyType.CustomQuery && customPluginHotkey.Equals(h.CommandParameter)); } @@ -297,59 +329,39 @@ private static void ClearHotkeyForCustomQueryHotkey(CustomPluginHotkey customPlu #endregion - // TODO: Deprecated - private static void SetHotkey(string hotkeyStr, EventHandler action) + #region Plugin Hotkey + + private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, PluginMetadata metadata, GlobalPluginHotkey pluginHotkey) { - var hotkey = new HotkeyModel(hotkeyStr); - SetHotkey(hotkey, action); + Action removeHotkeyAction = pluginHotkey.Editable ? + () => PluginManager.ChangePluginHotkey(metadata, pluginHotkey, HotkeyModel.Empty) : null; + return new(RegisteredHotkeyType.PluginGlobalHotkey, HotkeyType.Global, hotkey, "pluginHotkey", GlobalPluginHotkeyCommand, new GlobalPluginHotkeyPair(metadata, pluginHotkey), removeHotkeyAction); } - // TODO: Deprecated - private static void SetHotkey(HotkeyModel hotkey, EventHandler action) + private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> windowHotkeys) { - if (hotkey.IsEmpty) - { - return; - } - - string hotkeyStr = hotkey.ToString(); - try - { - HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); - } - catch (Exception e) - { - App.API.LogError(ClassName, - string.Format("Error registering hotkey {2}: {0} \nStackTrace:{1}", - e.Message, - e.StackTrace, - hotkeyStr)); - string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); - App.API.ShowMsgBox(errorMsg, errorMsgTitle); - } + Action removeHotkeysAction = windowHotkeys.All(h => h.PluginHotkey.Editable) ? + () => + { + foreach (var (metadata, pluginHotkey) in windowHotkeys) + { + PluginManager.ChangePluginHotkey(metadata, pluginHotkey, HotkeyModel.Empty); + } + } : null; + return new(RegisteredHotkeyType.PluginWindowHotkey, HotkeyType.SearchWindow, hotkey, "pluginHotkey", WindowPluginHotkeyCommand, new WindowPluginHotkeyPair(windowHotkeys), removeHotkeysAction); } - // TODO: Deprecated - internal static void RemoveHotkey(string hotkeyStr) + private static RegisteredHotkeyData SearchRegisteredHotkeyData(PluginMetadata metadata, GlobalPluginHotkey globalPluginHotkey) { - try - { - if (!string.IsNullOrEmpty(hotkeyStr)) - HotkeyManager.Current.Remove(hotkeyStr); - } - catch (Exception e) - { - App.API.LogError(ClassName, - string.Format("Error removing hotkey: {0} \nStackTrace:{1}", - e.Message, - e.StackTrace)); - string errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); - App.API.ShowMsgBox(errorMsg, errorMsgTitle); - } + return _settings.RegisteredHotkeys.FirstOrDefault(h => + h.RegisteredType == RegisteredHotkeyType.PluginGlobalHotkey && + h.CommandParameter is GlobalPluginHotkeyPair pair && + pair.Metadata.ID == metadata.ID && + pair.GlobalPluginHotkey.Id == globalPluginHotkey.Id); } + #endregion + #region Hotkey Setting private static void SetHotkey(RegisteredHotkeyData hotkeyData) @@ -590,6 +602,17 @@ private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, #endregion + #region Hotkey Searching + + private static RegisteredHotkeyData SearchRegisteredHotkeyData(RegisteredHotkeyType registeredHotkeyType, HotkeyModel hotkeyModel) + { + return _settings.RegisteredHotkeys.FirstOrDefault(h => + h.RegisteredType == registeredHotkeyType && + h.Hotkey.Equals(hotkeyModel)); + } + + #endregion + #region Commands private static RelayCommand _customQueryHotkeyCommand; @@ -655,141 +678,6 @@ private static void WindowPluginHotkey(WindowPluginHotkeyPair pair) #endregion - // TODO: Deprecated - internal static void SetGlobalPluginHotkey(GlobalPluginHotkey globalHotkey, PluginMetadata metadata, string hotkeyStr) - { - var hotkey = new HotkeyModel(hotkeyStr); - SetGlobalPluginHotkey(globalHotkey, metadata, hotkey); - } - - // TODO: Deprecated - 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(); - }); - } - - // TODO: Deprecated - internal static void SetWindowHotkey(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) - { - try - { - if (hotkeyModels.Count == 0) return; - if (Application.Current?.MainWindow is MainWindow window) - { - // Cache the command for the hotkey if it already exists - var keyGesture = hotkey.ToKeyGesture(); - var existingBinding = window.InputBindings - .OfType() - .FirstOrDefault(kb => - kb.Gesture is KeyGesture keyGesture1 && - keyGesture.Key == keyGesture1.Key && - keyGesture.Modifiers == keyGesture1.Modifiers); - if (existingBinding != null) - { - throw new InvalidOperationException($"Key binding {hotkey} already exists"); - } - - // Create and add the new key binding - var command = BuildCommand(hotkeyModels); - var keyBinding = new KeyBinding(command, keyGesture); - window.InputBindings.Add(keyBinding); - } - } - catch (Exception e) - { - App.API.LogError(ClassName, - string.Format("Error registering window hotkey {2}: {0} \nStackTrace:{1}", - e.Message, - e.StackTrace, - hotkey)); - string errorMsg = string.Format(App.API.GetTranslation("registerWindowHotkeyFailed"), hotkey); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); - App.API.ShowMsgBox(errorMsg, errorMsgTitle); - } - } - - // TODO: Deprecated - private static ICommand BuildCommand(List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeyModels) - { - return new RelayCommand(() => - { - var selectedResult = _mainViewModel.GetSelectedResults().SelectedItem?.Result; - // Check result nullability - if (selectedResult != null) - { - var pluginId = selectedResult.PluginID; - foreach (var hotkeyModel in hotkeyModels) - { - var metadata = hotkeyModel.Metadata; - var pluginHotkey = hotkeyModel.PluginHotkey; - - // Check plugin ID match - if (metadata.ID != pluginId) - continue; - - // Check plugin enabled state - if (metadata.Disabled) - continue; - - // Check hotkey supported state - if (!selectedResult.HotkeyIds.Contains(pluginHotkey.Id)) - continue; - - // Check action nullability - if (pluginHotkey.Action == null) - continue; - - // TODO: Remove return to skip other commands & Organize main window hotkeys - // Invoke action & return to skip other commands - if (pluginHotkey.Action.Invoke(selectedResult)) - App.API.HideMainWindow(); - - return; - } - } - }); - } - - // TODO: Deprecated - 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 is KeyGesture keyGesture1 && - keyGesture.Key == keyGesture1.Key && - keyGesture.Modifiers == keyGesture1.Modifiers); - if (existingBinding != null) - { - window.InputBindings.Remove(existingBinding); - } - } - } - catch (Exception e) - { - App.API.LogError(ClassName, - string.Format("Error removing window hotkey: {0} \nStackTrace:{1}", - e.Message, - e.StackTrace)); - string errorMsg = string.Format(App.API.GetTranslation("unregisterWindowHotkeyFailed"), hotkey); - string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); - App.API.ShowMsgBox(errorMsg, errorMsgTitle); - } - } - #region Check Hotkey internal static bool CheckAvailability(HotkeyModel currentHotkey) From 8d26c1c4e7ee7e37c41fb92e23f83a1db31caef7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 18:02:29 +0800 Subject: [PATCH 110/150] Add string resource --- Flow.Launcher/Languages/en.xaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index a48cdae89b1..96a31005278 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -315,6 +315,7 @@ Show Result Badges For supported plugins, badges are displayed to help distinguish them more easily. Show Result Badges for Global Query Only + Plugin hotkey HTTP Proxy From cd3eebaac62d326a81d060f7db1e6f2f1100df80 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 18:02:50 +0800 Subject: [PATCH 111/150] Remove used functions --- .../Views/SettingsPaneHotkey.xaml.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index e32bc2dbe36..a21d4ae9711 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -5,7 +5,6 @@ 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; @@ -81,12 +80,13 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) var hotkeyControl = new HotkeyControl { Type = hotkey.HotkeyType == HotkeyType.Global ? - HotkeyControl.HotkeyType.GlobalPluginHotkey : HotkeyControl.HotkeyType.WindowPluginHotkey, + HotkeyControl.HotkeyType.GlobalPluginHotkey : + HotkeyControl.HotkeyType.WindowPluginHotkey, DefaultHotkey = hotkey.DefaultHotkey, ValidateKeyGesture = true }; hotkeyControl.SetHotkey(hotkeySetting, true); - hotkeyControl.ChangeHotkey = new RelayCommand((m) => ChangePluginHotkey(metadata, hotkey, m)); + hotkeyControl.ChangeHotkey = new RelayCommand((h) => ChangePluginHotkey(metadata, hotkey, h)); card.Content = hotkeyControl; } else @@ -108,18 +108,11 @@ private static void ChangePluginHotkey(PluginMetadata metadata, BasePluginHotkey { if (pluginHotkey is GlobalPluginHotkey globalPluginHotkey) { - var oldHotkey = PluginManager.ChangePluginHotkey(metadata, globalPluginHotkey, newHotkey); - HotKeyMapper.RemoveHotkey(oldHotkey); - HotKeyMapper.SetGlobalPluginHotkey(globalPluginHotkey, metadata, newHotkey); + PluginManager.ChangePluginHotkey(metadata, globalPluginHotkey, 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]); + PluginManager.ChangePluginHotkey(metadata, windowPluginHotkey, newHotkey); } } } From e196aa420a1e4c62545363a64391585d109edf37 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 19:39:38 +0800 Subject: [PATCH 112/150] Remove todos --- Flow.Launcher/Helper/HotKeyMapper.cs | 2 +- Flow.Launcher/HotkeyControlDialog.xaml.cs | 1 - Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 614116c2658..0132d335ccc 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -669,7 +669,7 @@ private static void WindowPluginHotkey(WindowPluginHotkeyPair pair) pluginHotkey.Action == null) // Check action nullability continue; - // TODO: Remove return to skip other commands & Organize main window hotkeys + // TODO: Remove return to skip other commands if (pluginHotkey.Action.Invoke(selectedResult)) App.API.HideMainWindow(); } diff --git a/Flow.Launcher/HotkeyControlDialog.xaml.cs b/Flow.Launcher/HotkeyControlDialog.xaml.cs index 1d42b20db93..c7af8c5b8bb 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml.cs +++ b/Flow.Launcher/HotkeyControlDialog.xaml.cs @@ -137,7 +137,6 @@ private void SetKeysToDisplay(HotkeyModel? hotkey) if (tbMsg == null) return; - // TODO: If we need to check !v.Hotkey.IsEmpty && for v.Hotkey? if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData) { var description = string.Format( diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index a21d4ae9711..b99ea6e6d9b 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -54,7 +54,6 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) { Title = metadata.Name, Margin = new Thickness(0, 4, 0, 0), - // TODO: Support displaying plugin icon here }; var hotkeyStackPanel = new StackPanel { From e6fb766fec527e88e2f0e11629dc3a60b7771048 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 20:02:03 +0800 Subject: [PATCH 113/150] Mark ActionContext as deprecated --- Flow.Launcher.Plugin/ActionContext.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/ActionContext.cs b/Flow.Launcher.Plugin/ActionContext.cs index 9e05bbd0617..052b2063975 100644 --- a/Flow.Launcher.Plugin/ActionContext.cs +++ b/Flow.Launcher.Plugin/ActionContext.cs @@ -1,4 +1,5 @@ -using System.Windows.Input; +using System; +using System.Windows.Input; namespace Flow.Launcher.Plugin { @@ -6,6 +7,7 @@ namespace Flow.Launcher.Plugin /// Context provided as a parameter when invoking a /// or /// + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] public class ActionContext { /// From f6574947a006ba18f1837cb44ce7bb54814c51b0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 2 Jul 2025 20:35:26 +0800 Subject: [PATCH 114/150] Workaround for ActionContext compatibility --- Flow.Launcher/Helper/HotKeyMapper.cs | 107 +++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 0132d335ccc..8a791959092 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -27,9 +27,6 @@ internal static class HotKeyMapper private static Settings _settings; private static MainViewModel _mainViewModel; - // Registered hotkeys for ActionContext - private static List _actionContextRegisteredHotkeys; - #region Initialization internal static void Initialize() @@ -37,6 +34,7 @@ internal static void Initialize() _mainViewModel = Ioc.Default.GetRequiredService(); _settings = Ioc.Default.GetService(); + InitializeActionContextHotkeys(); InitializeRegisteredHotkeys(); _settings.PropertyChanged += Settings_PropertyChanged; @@ -46,14 +44,6 @@ internal static void Initialize() private static void InitializeRegisteredHotkeys() { - // Fixed hotkeys for ActionContext - _actionContextRegisteredHotkeys = new List - { - new(RegisteredHotkeyType.CtrlShiftEnter, HotkeyType.SearchWindow, "Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc", _mainViewModel.OpenResultCommand), - new(RegisteredHotkeyType.CtrlEnter, HotkeyType.SearchWindow, "Ctrl+Enter", "OpenContainFolderHotkey", _mainViewModel.OpenResultCommand), - new(RegisteredHotkeyType.AltEnter, HotkeyType.SearchWindow, "Alt+Enter", "HotkeyOpenResult", _mainViewModel.OpenResultCommand), - }; - // Fixed hotkeys & Editable hotkeys var list = new List { @@ -348,7 +338,7 @@ private static RegisteredHotkeyData GetRegisteredHotkeyData(HotkeyModel hotkey, PluginManager.ChangePluginHotkey(metadata, pluginHotkey, HotkeyModel.Empty); } } : null; - return new(RegisteredHotkeyType.PluginWindowHotkey, HotkeyType.SearchWindow, hotkey, "pluginHotkey", WindowPluginHotkeyCommand, new WindowPluginHotkeyPair(windowHotkeys), removeHotkeysAction); + return new(RegisteredHotkeyType.PluginWindowHotkey, HotkeyType.SearchWindow, hotkey, "pluginHotkey", WindowPluginHotkeyCommand, new WindowPluginHotkeyPair(hotkey, windowHotkeys), removeHotkeysAction); } private static RegisteredHotkeyData SearchRegisteredHotkeyData(PluginMetadata metadata, GlobalPluginHotkey globalPluginHotkey) @@ -475,7 +465,11 @@ kb.Gesture is KeyGesture keyGesture1 && keyGesture.Modifiers == keyGesture1.Modifiers); if (existingBinding != null) { - throw new InvalidOperationException($"Windows key {hotkey} already exists"); + // If the hotkey is not a hotkey for ActionContext events, throw an exception to avoid duplicates + if (!IsActionContextEvent(window, existingBinding, hotkey)) + { + throw new InvalidOperationException($"Windows key {hotkey} already exists"); + } } // Add the new hotkey binding @@ -558,6 +552,9 @@ kb.Gesture is KeyGesture keyGesture1 && { window.InputBindings.Remove(existingBinding); } + + // Restore the key binding for ActionContext events + RestoreActionContextEvent(hotkey, keyGesture); } } catch (Exception e) @@ -672,7 +669,13 @@ private static void WindowPluginHotkey(WindowPluginHotkeyPair pair) // TODO: Remove return to skip other commands if (pluginHotkey.Action.Invoke(selectedResult)) App.API.HideMainWindow(); + + // Return after invoking the first matching hotkey action so that we will not invoke action context event + return; } + + // When no plugin hotkey action is invoked, invoke the action context event + InvokeActionContextEvent(pair.Hotkey); } } @@ -701,6 +704,78 @@ internal static bool CheckAvailability(HotkeyModel currentHotkey) #endregion + #region Action Context Hotkey (Obsolete) + + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] + private static List _actionContextRegisteredHotkeys; + + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] + private static readonly Dictionary _actionContextHotkeyEvents = new(); + + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] + private static void InitializeActionContextHotkeys() + { + // Fixed hotkeys for ActionContext + _actionContextRegisteredHotkeys = new List + { + new(RegisteredHotkeyType.CtrlShiftEnter, HotkeyType.SearchWindow, "Ctrl+Shift+Enter", "HotkeyCtrlShiftEnterDesc", _mainViewModel.OpenResultCommand), + new(RegisteredHotkeyType.CtrlEnter, HotkeyType.SearchWindow, "Ctrl+Enter", "OpenContainFolderHotkey", _mainViewModel.OpenResultCommand), + new(RegisteredHotkeyType.AltEnter, HotkeyType.SearchWindow, "Alt+Enter", "HotkeyOpenResult", _mainViewModel.OpenResultCommand), + }; + + // Register ActionContext hotkeys and they will be cached and restored in _actionContextHotkeyEvents + foreach (var hotkey in _actionContextRegisteredHotkeys) + { + _actionContextHotkeyEvents[hotkey.Hotkey] = (hotkey.Command, hotkey.CommandParameter); + SetWindowHotkey(hotkey); + } + } + + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] + private static bool IsActionContextEvent(MainWindow window, KeyBinding existingBinding, HotkeyModel hotkey) + { + // Check if this hotkey is a hotkey for ActionContext events + if (!_actionContextHotkeyEvents.ContainsKey(hotkey) && + _actionContextHotkeyEvents[hotkey].Command == existingBinding.Command || + _actionContextHotkeyEvents[hotkey].Parameter == existingBinding.CommandParameter) + { + // If the hotkey is not for ActionContext events, return false + return true; + } + + return false; + } + + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] + private static void RestoreActionContextEvent(HotkeyModel hotkey, KeyGesture keyGesture) + { + // Restore the ActionContext event by adding the key binding back + if (_actionContextHotkeyEvents.TryGetValue(hotkey, out var actionContextItem)) + { + if (Application.Current?.MainWindow is MainWindow window) + { + var keyBinding = new KeyBinding + { + Gesture = keyGesture, + Command = actionContextItem.Command, + CommandParameter = actionContextItem.Parameter + }; + window.InputBindings.Add(keyBinding); + } + } + } + + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] + private static void InvokeActionContextEvent(HotkeyModel hotkey) + { + if (_actionContextHotkeyEvents.TryGetValue(hotkey, out var actionContextItem)) + { + actionContextItem.Command.Execute(actionContextItem.Parameter); + } + } + + #endregion + #region Private Classes private class GlobalPluginHotkeyPair @@ -718,10 +793,14 @@ public GlobalPluginHotkeyPair(PluginMetadata metadata, GlobalPluginHotkey global private class WindowPluginHotkeyPair { + [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] + public HotkeyModel Hotkey { get; } + public List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> HotkeyModels { get; } - public WindowPluginHotkeyPair(List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys) + public WindowPluginHotkeyPair(HotkeyModel hotkey, List<(PluginMetadata Metadata, SearchWindowPluginHotkey PluginHotkey)> hotkeys) { + Hotkey = hotkey; HotkeyModels = hotkeys; } } From 2625f6fa41c622b40e61737b24af81cfa83c2cfd Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 3 Jul 2025 10:26:12 +0800 Subject: [PATCH 115/150] Make GetPluginsForInterface private --- Flow.Launcher.Core/Plugin/PluginManager.cs | 17 ++++++++++++++++- .../Resource/Internationalization.cs | 4 ++-- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index c79100fc52d..45659aedb4d 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -28,6 +28,8 @@ public static class PluginManager private static IEnumerable _contextMenuPlugins; private static IEnumerable _homePlugins; + private static IEnumerable _resultUpdatePlugin; + private static IEnumerable _translationPlugins; private static IEnumerable _hotkeyPlugins; public static List AllPlugins { get; private set; } @@ -257,6 +259,8 @@ public static async Task InitializePluginsAsync() _contextMenuPlugins = GetPluginsForInterface(); _homePlugins = GetPluginsForInterface(); + _resultUpdatePlugin = GetPluginsForInterface(); + _translationPlugins = GetPluginsForInterface(); _hotkeyPlugins = GetPluginsForInterface(); var pluginHotkeyInfo = GetPluginHotkeyInfo(); Settings.UpdatePluginHotkeyInfo(pluginHotkeyInfo); @@ -420,7 +424,7 @@ public static PluginPair GetPluginForId(string id) return AllPlugins.FirstOrDefault(o => o.Metadata.ID == id); } - public static IEnumerable GetPluginsForInterface() where T : IFeatures + private static IEnumerable GetPluginsForInterface() 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(); @@ -460,6 +464,17 @@ public static bool IsHomePlugin(string id) return _homePlugins.Any(p => p.Metadata.ID == id); } + public static IList GetResultUpdatePlugin() + { + return _resultUpdatePlugin.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + } + + public static IList GetTranslationPlugins() + { + // Here we still return the modified plugins to update the possible string resources + return _translationPlugins.ToList(); + } + public static Dictionary> GetPluginHotkeyInfo() { var hotkeyPluginInfos = new Dictionary>(); diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 24edc5ed8fe..8879e5d638a 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -74,7 +74,7 @@ public static void InitSystemLanguageCode() private void AddPluginLanguageDirectories() { - foreach (var plugin in PluginManager.GetPluginsForInterface()) + foreach (var plugin in PluginManager.GetTranslationPlugins()) { var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location; var dir = Path.GetDirectoryName(location); @@ -278,7 +278,7 @@ public static string GetTranslation(string key) private void UpdatePluginMetadataTranslations() { - foreach (var p in PluginManager.GetPluginsForInterface()) + foreach (var p in PluginManager.GetTranslationPlugins()) { if (p.Plugin is not IPluginI18n pluginI18N) return; try diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 00a2e3a9715..1f5f408b3b2 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -260,7 +260,7 @@ void continueAction(Task t) public void RegisterResultsUpdatedEvent() { - foreach (var pair in PluginManager.GetPluginsForInterface()) + foreach (var pair in PluginManager.GetResultUpdatePlugin()) { var plugin = (IResultUpdated)pair.Plugin; plugin.ResultsUpdated += (s, e) => From 089c0fd89275591f5c115ba252bb56af65bdea4f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 3 Jul 2025 10:59:49 +0800 Subject: [PATCH 116/150] Initialize plugin enumerable after all plugins are initialized --- Flow.Launcher.Core/Plugin/PluginManager.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 45659aedb4d..f23b529cc4b 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -188,6 +188,12 @@ public static void LoadPlugins(PluginsSettings settings) AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); // Since dotnet plugins need to get assembly name first, we should update plugin directory after loading plugins UpdatePluginDirectory(_metadatas); + // Initialize plugin enumerable after all plugins are initialized + _contextMenuPlugins = GetPluginsForInterface(); + _homePlugins = GetPluginsForInterface(); + _resultUpdatePlugin = GetPluginsForInterface(); + _translationPlugins = GetPluginsForInterface(); + _hotkeyPlugins = GetPluginsForInterface(); } private static void UpdatePluginDirectory(List metadatas) @@ -257,11 +263,6 @@ public static async Task InitializePluginsAsync() await Task.WhenAll(InitTasks); - _contextMenuPlugins = GetPluginsForInterface(); - _homePlugins = GetPluginsForInterface(); - _resultUpdatePlugin = GetPluginsForInterface(); - _translationPlugins = GetPluginsForInterface(); - _hotkeyPlugins = GetPluginsForInterface(); var pluginHotkeyInfo = GetPluginHotkeyInfo(); Settings.UpdatePluginHotkeyInfo(pluginHotkeyInfo); InitializeWindowPluginHotkeys(pluginHotkeyInfo); From 2a52c28bddeb117eaeee545b2982b738eb157200 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 3 Jul 2025 11:18:32 +0800 Subject: [PATCH 117/150] Store plugin hotkey info --- Flow.Launcher.Core/Plugin/PluginManager.cs | 54 ++++++++++++++----- .../Resource/Internationalization.cs | 4 ++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index f23b529cc4b..fcb261f248f 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -46,6 +46,7 @@ public static class PluginManager private static List _metadatas; private static readonly List _modifiedPlugins = new(); + private static readonly Dictionary> _pluginHotkeyInfo = new(); private static readonly Dictionary> _windowPluginHotkeys = new(); /// @@ -263,9 +264,9 @@ public static async Task InitializePluginsAsync() await Task.WhenAll(InitTasks); - var pluginHotkeyInfo = GetPluginHotkeyInfo(); - Settings.UpdatePluginHotkeyInfo(pluginHotkeyInfo); - InitializeWindowPluginHotkeys(pluginHotkeyInfo); + InitializePluginHotkeyInfo(); + Settings.UpdatePluginHotkeyInfo(GetPluginHotkeyInfo()); + InitializeWindowPluginHotkeys(); foreach (var plugin in AllPlugins) { @@ -478,24 +479,53 @@ public static IList GetTranslationPlugins() public static Dictionary> GetPluginHotkeyInfo() { - var hotkeyPluginInfos = new Dictionary>(); + return _pluginHotkeyInfo; + } + + public static Dictionary> GetWindowPluginHotkeys() + { + return _windowPluginHotkeys; + } + + public static void UpdatePluginHotkeyInfoTranslations() + { foreach (var plugin in _hotkeyPlugins) { - var hotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys(); - hotkeyPluginInfos.Add(plugin, hotkeys); + 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); + } } - - return hotkeyPluginInfos; } - public static Dictionary> GetWindowPluginHotkeys() + private static void InitializePluginHotkeyInfo() { - return _windowPluginHotkeys; + foreach (var plugin in _hotkeyPlugins) + { + var hotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys(); + _pluginHotkeyInfo.Add(plugin, hotkeys); + } } - private static void InitializeWindowPluginHotkeys(Dictionary> pluginHotkeyInfo) + private static void InitializeWindowPluginHotkeys() { - foreach (var info in pluginHotkeyInfo) + foreach (var info in GetPluginHotkeyInfo()) { var pluginPair = info.Key; var hotkeyInfo = info.Value; diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 8879e5d638a..0b173b69bf6 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -278,6 +278,7 @@ public static string GetTranslation(string key) private void UpdatePluginMetadataTranslations() { + // Update plugin metadata name & description foreach (var p in PluginManager.GetTranslationPlugins()) { if (p.Plugin is not IPluginI18n pluginI18N) return; @@ -292,6 +293,9 @@ private void UpdatePluginMetadataTranslations() API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e); } } + + // Update plugin hotkey name & description + PluginManager.UpdatePluginHotkeyInfoTranslations(); } private static string LanguageFile(string folder, string language) From 48745867730e99ea5235a94c8a3d5d495ca60fc6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:15:33 +0800 Subject: [PATCH 118/150] Fix build issue --- Flow.Launcher.Core/Plugin/PluginManager.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index cc7fa2bc12b..34e6887fdb4 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -476,17 +476,6 @@ public static bool IsHomePlugin(string id) return _homePlugins.Where(p => !PluginModified(p.Metadata.ID)).Any(p => p.Metadata.ID == id); } - public static IList GetResultUpdatePlugin() - { - return _resultUpdatePlugin.Where(p => !PluginModified(p.Metadata.ID)).ToList(); - } - - public static IList GetTranslationPlugins() - { - // Here we still return the modified plugins to update the possible string resources - return _translationPlugins.ToList(); - } - public static Dictionary> GetPluginHotkeyInfo() { return _pluginHotkeyInfo; From 194871189eef605a826873391d77690bbdac9656 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:18:16 +0800 Subject: [PATCH 119/150] Add hotkey get function --- Flow.Launcher.Core/Plugin/PluginManager.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 34e6887fdb4..7c352bd7871 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -442,6 +442,11 @@ public static IList GetTranslationPlugins() return _translationPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); } + public static IList GetHotkeyPlugins() + { + return _hotkeyPlugins.Where(p => !PluginModified(p.Metadata.ID)).ToList(); + } + public static List GetContextMenusForPlugin(Result result) { var results = new List(); @@ -488,7 +493,7 @@ public static Dictionary> GetPluginHotkeyInfo public static void UpdatePluginHotkeyInfoTranslations() { - foreach (var plugin in _hotkeyPlugins) + foreach (var plugin in GetHotkeyPlugins()) { var newHotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys(); if (_pluginHotkeyInfo.TryGetValue(plugin, out var oldHotkeys)) @@ -515,7 +520,7 @@ public static void UpdatePluginHotkeyInfoTranslations() private static void InitializePluginHotkeyInfo() { - foreach (var plugin in _hotkeyPlugins) + foreach (var plugin in GetHotkeyPlugins()) { var hotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys(); _pluginHotkeyInfo.Add(plugin, hotkeys); From 9b920618077e41abbae9c9ca6b3cb7dbf0284701 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:28:26 +0800 Subject: [PATCH 120/150] Check modified & use IDictionary --- Flow.Launcher.Core/Plugin/PluginManager.cs | 10 ++++++---- .../UserSettings/PluginSettings.cs | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 7c352bd7871..231a477f089 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -481,14 +481,16 @@ public static bool IsHomePlugin(string id) return _homePlugins.Where(p => !PluginModified(p.Metadata.ID)).Any(p => p.Metadata.ID == id); } - public static Dictionary> GetPluginHotkeyInfo() + public static IDictionary> GetPluginHotkeyInfo() { - return _pluginHotkeyInfo; + return _pluginHotkeyInfo.Where(p => !PluginModified(p.Key.Metadata.ID)) + .ToDictionary(p => p.Key, p => p.Value); } - public static Dictionary> GetWindowPluginHotkeys() + public static IDictionary> GetWindowPluginHotkeys() { - return _windowPluginHotkeys; + // 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); } public static void UpdatePluginHotkeyInfoTranslations() diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index f9fc1e50969..0e881005a9e 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -93,7 +93,7 @@ public void UpdatePluginSettings(List metadatas) /// Update plugin hotkey information in metadata and plugin setting. /// /// - public void UpdatePluginHotkeyInfo(Dictionary> hotkeyPluginInfo) + public void UpdatePluginHotkeyInfo(IDictionary> hotkeyPluginInfo) { foreach (var info in hotkeyPluginInfo) { From 89b454bb6c3fb03851f9813dbdc24a7436fe5f9f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:29:19 +0800 Subject: [PATCH 121/150] Fix typos --- Flow.Launcher.Core/Plugin/PluginManager.cs | 4 ++-- Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 231a477f089..6f8a55dc281 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -497,7 +497,7 @@ public static void UpdatePluginHotkeyInfoTranslations() { foreach (var plugin in GetHotkeyPlugins()) { - var newHotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys(); + var newHotkeys = ((IPluginHotkey)plugin.Plugin).GetPluginHotkeys(); if (_pluginHotkeyInfo.TryGetValue(plugin, out var oldHotkeys)) { foreach (var newHotkey in newHotkeys) @@ -524,7 +524,7 @@ private static void InitializePluginHotkeyInfo() { foreach (var plugin in GetHotkeyPlugins()) { - var hotkeys = ((IPluginHotkey)plugin.Plugin).GetPuginHotkeys(); + var hotkeys = ((IPluginHotkey)plugin.Plugin).GetPluginHotkeys(); _pluginHotkeyInfo.Add(plugin, hotkeys); } } diff --git a/Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs b/Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs index a075c461925..34ee2f1bc2a 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs @@ -11,6 +11,6 @@ public interface IPluginHotkey : IFeatures /// Get the list of plugin hotkeys which will be registered in the settings page. /// /// - List GetPuginHotkeys(); + List GetPluginHotkeys(); } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 9b1882cb3a4..136de3c5efd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -112,7 +112,7 @@ private void FillQuickAccessLinkNames() } } - public List GetPuginHotkeys() + public List GetPluginHotkeys() { return new List { diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 2b915bd5746..4a53c4e85f7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -70,7 +70,7 @@ public string GetTranslatedPluginDescription() return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description"); } - public List GetPuginHotkeys() + public List GetPluginHotkeys() { return new List { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 2dee3d8b5e4..bf4d833e060 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -460,7 +460,7 @@ public void Dispose() Win32.Dispose(); } - public List GetPuginHotkeys() + public List GetPluginHotkeys() { return new List { From f52ef92d45f3a93bafae42ba2b1c24e53c1261f2 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:32:43 +0800 Subject: [PATCH 122/150] Fix string typos --- Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 30a2ec5c02c..324a14c6155 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -204,6 +204,6 @@ Successfully renamed it to: {0} There is already a file with the name: {0} in this location Failed to open rename dialog. - An error occured: {0}. - An error occured and no reason was given. + An error occurred: {0}. + An error occurred and no reason was given. From 51917809b3fcf9bc805b3507a68fdd3e8c3eba7d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:50:52 +0800 Subject: [PATCH 123/150] Fix blank line change --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index a1d990aa07c..1827354d0ba 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -226,11 +226,8 @@ public interface IPublicAPI /// Query string /// The string that will be compared against the query /// Match results - MatchResult FuzzySearch(string query, string stringToCompare); - - /// /// Http download the spefic url and return as string /// From 4055e305388e2f1e58b370962e245bad2862f51a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:52:36 +0800 Subject: [PATCH 124/150] Use set hotkey function instead of setter --- .../Hotkey/RegisteredHotkeyData.cs | 11 ++++++++++- Flow.Launcher/Helper/HotKeyMapper.cs | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index 591d3c00088..befd49318b2 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -26,7 +26,7 @@ public record RegisteredHotkeyData /// /// representation of this hotkey. /// - public HotkeyModel Hotkey { get; set; } + public HotkeyModel Hotkey { get; private set; } /// /// String key in the localization dictionary that represents this hotkey. For example, ReloadPluginHotkey, @@ -233,6 +233,15 @@ public RegisteredHotkeyData( RemoveHotkey = removeHotkey; } + /// + /// Sets the hotkey for this registered hotkey data. + /// + /// + public void SetHotkey(HotkeyModel hotkey) + { + Hotkey = hotkey; + } + /// public override string ToString() { diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 8a791959092..93be0f42a0e 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -260,7 +260,7 @@ private static void PluginManager_PluginHotkeyChanged(PluginManager.PluginHotkey { var hotkeyData = SearchRegisteredHotkeyData(metadata, globalPluginHotkey); RemoveHotkey(hotkeyData); - hotkeyData.Hotkey = newHotkey; + hotkeyData.SetHotkey(newHotkey); SetHotkey(hotkeyData); } else if (pluginHotkey is SearchWindowPluginHotkey) @@ -591,7 +591,7 @@ private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, RemoveHotkey(registeredHotkeyData); // Update the hotkey string - registeredHotkeyData.Hotkey = newHotkey; + registeredHotkeyData.SetHotkey(newHotkey); // Set the new hotkey SetHotkey(registeredHotkeyData); From a693773013d48cf016264fe345cb3bd2d32f22f6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:55:27 +0800 Subject: [PATCH 125/150] Improve code quality --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 79f30736875..75fcb244bca 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -365,6 +364,7 @@ public static void ValidateDataDirectory(string bundledDataDirectory, string dat } } } + /// /// Return true is the given name is a valid file name /// @@ -377,13 +377,14 @@ public static bool IsValidFileName(string name) } return true; } + /// /// Returns true is the given name is a valid name for a directory, not a path /// public static bool IsValidDirectoryName(string name) { if (IsReservedName(name)) return false; - char[] invalidChars = Path.GetInvalidPathChars().Append('/').ToArray().Append('\\').ToArray(); + var invalidChars = Path.GetInvalidPathChars().Append('/').ToArray().Append('\\').ToArray(); if (name.IndexOfAny(invalidChars) >= 0) { return false; From d79272a9088aa55d49c72f0f25f784676d32ea8f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 6 Jul 2025 20:55:46 +0800 Subject: [PATCH 126/150] Cache reversed names --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 75fcb244bca..3f74c256574 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -391,11 +391,13 @@ public static bool IsValidDirectoryName(string name) } return true; } + + private static readonly string[] ReservedNames = new[] { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" }; + private static bool IsReservedName(string name) { - string[] reservedNames = new[] { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" }; - string nameWithoutExtension = Path.GetFileNameWithoutExtension(name).ToUpperInvariant(); - if (reservedNames.Contains(nameWithoutExtension)) + var nameWithoutExtension = Path.GetFileNameWithoutExtension(name).ToUpperInvariant(); + if (ReservedNames.Contains(nameWithoutExtension)) { return true; } From c326901812f213bfbdf6917c1a713ed52a770830 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 21:09:56 +0800 Subject: [PATCH 127/150] Fix spelling --- Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs index 6d2abff4c15..fc635e51917 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs @@ -169,9 +169,9 @@ public readonly IEnumerable EnumerateDisplayKeys() /// /// Validate hotkey /// - /// Try to validate hotkey as a KeyGesture. + /// Try to validate hotkey as a KeyGesture. /// - public bool Validate(bool validateKeyGestrue = false) + public bool Validate(bool validateKeyGesture = false) { switch (CharKey) { @@ -186,7 +186,7 @@ public bool Validate(bool validateKeyGestrue = false) case Key.None: return false; default: - if (validateKeyGestrue) + if (validateKeyGesture) { try { From e8707addd028c4f4c64b2e4f5743dcdef33f6baf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 21:14:10 +0800 Subject: [PATCH 128/150] Unify error strings --- Flow.Launcher/Helper/HotKeyMapper.cs | 4 ++-- Flow.Launcher/Languages/en.xaml | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 93be0f42a0e..ff596fb791a 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -485,7 +485,7 @@ kb.Gesture is KeyGesture keyGesture1 && catch (Exception e) { App.API.LogError(ClassName, $"Error registering window hotkey {hotkey}: {e.Message} \nStackTrace:{e.StackTrace}"); - var errorMsg = string.Format(App.API.GetTranslation("registerWindowHotkeyFailed"), hotkey); + var errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkey); var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); App.API.ShowMsgBox(errorMsg, errorMsgTitle); } @@ -560,7 +560,7 @@ kb.Gesture is KeyGesture keyGesture1 && catch (Exception e) { App.API.LogError(ClassName, $"Error removing window hotkey: {e.Message} \nStackTrace:{e.StackTrace}"); - var errorMsg = string.Format(App.API.GetTranslation("unregisterWindowHotkeyFailed"), hotkey); + var errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkey); var errorMsgTitle = App.API.GetTranslation("MessageBoxTitle"); App.API.ShowMsgBox(errorMsg, errorMsgTitle); } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 07747f2b5a1..b807ef07a8e 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -21,8 +21,6 @@ Failed to register hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. Failed to unregister hotkey "{0}". Please try again or see log for details - Failed to register window hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. - Failed to unregister window hotkey "{0}". Please try again or see log for details Flow Launcher Could not start {0} Invalid Flow Launcher plugin file format From 40ba1aea4090da19f9a1fe2eb6c8435063fbaa2e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 22:09:20 +0800 Subject: [PATCH 129/150] Do not validate key gesture for plugin hotkeys --- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index b99ea6e6d9b..a93a7771692 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -82,9 +82,9 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) HotkeyControl.HotkeyType.GlobalPluginHotkey : HotkeyControl.HotkeyType.WindowPluginHotkey, DefaultHotkey = hotkey.DefaultHotkey, - ValidateKeyGesture = true + ValidateKeyGesture = false, + Hotkey = hotkeySetting }; - hotkeyControl.SetHotkey(hotkeySetting, true); hotkeyControl.ChangeHotkey = new RelayCommand((h) => ChangePluginHotkey(metadata, hotkey, h)); card.Content = hotkeyControl; } From c2c8a82472a27b83cebf122e335cf186395528c6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 22:15:51 +0800 Subject: [PATCH 130/150] Restore plugin hotkey setting if it is not editable anymore --- Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index 0e881005a9e..f4d55060f90 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -132,6 +132,10 @@ public void UpdatePluginHotkeyInfo(IDictionary Date: Thu, 10 Jul 2025 22:43:31 +0800 Subject: [PATCH 131/150] Fix typos Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- Flow.Launcher.Plugin/Result.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 2e73d7b3b88..07cff6fd06d 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -259,7 +259,7 @@ public string PluginDirectory /// /// List of hotkey IDs that are supported for this result. - /// Those hotkeys should be registed by IPluginHotkey interface. + /// Those hotkeys should be registered by IPluginHotkey interface. /// public IList HotkeyIds { get; set; } = new List(); From 476d84699593229b1f88fe2d692660443ddfe049 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 22:45:39 +0800 Subject: [PATCH 132/150] Improve code quality --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 3f74c256574..f11575637c4 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -384,7 +384,7 @@ public static bool IsValidFileName(string name) public static bool IsValidDirectoryName(string name) { if (IsReservedName(name)) return false; - var invalidChars = Path.GetInvalidPathChars().Append('/').ToArray().Append('\\').ToArray(); + var invalidChars = Path.GetInvalidPathChars().Concat(new[] { '/', '\\' }).ToArray(); if (name.IndexOfAny(invalidChars) >= 0) { return false; From 3dcf22ec4510126d361800879c4c03e9f690b263 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 22:46:18 +0800 Subject: [PATCH 133/150] Use sorted info --- Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index a93a7771692..f3b4564daf1 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -61,7 +61,7 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) }; var sortedHotkeyInfo = hotkeyInfo.OrderBy(h => h.Id).ToList(); - foreach (var hotkey in hotkeyInfo) + foreach (var hotkey in sortedHotkeyInfo) { // Skip invisible hotkeys if (!hotkey.Visible) continue; From 00778560f8cfd2ed2cd8b9ad37f89c98ab304a62 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 22:48:00 +0800 Subject: [PATCH 134/150] Return for exceptions --- Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs index 7743bc5275c..8b2ff1c5ceb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -87,7 +87,7 @@ public static void Rename(string NewFileName, FileSystemInfo oldInfo, IPublicAPI return; case ElementAlreadyExistsException: api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_element_already_exists"), NewFileName)); - break; + return; default: string msg = exception.Message; if (!string.IsNullOrEmpty(msg)) From ad81a3c091b7aadadbce6f561e496f74961ac9fc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 22:48:40 +0800 Subject: [PATCH 135/150] Fix hotkey string issue --- Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index befd49318b2..a1b577ea70c 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -245,7 +245,7 @@ public void SetHotkey(HotkeyModel hotkey) /// public override string ToString() { - return Hotkey.IsEmpty ? $"{RegisteredType} - {Hotkey}" : $"{RegisteredType} - None"; + return Hotkey.IsEmpty ? $"{RegisteredType} - None" : $"{RegisteredType} - {Hotkey}"; } } From 96bf44501e1b3f753d319a200fd26bf6fdd00e4e Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 10 Jul 2025 22:50:58 +0800 Subject: [PATCH 136/150] Fix action context hotkey event logic --- Flow.Launcher/Helper/HotKeyMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index ff596fb791a..540af9b526e 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -736,7 +736,7 @@ private static bool IsActionContextEvent(MainWindow window, KeyBinding existingB { // Check if this hotkey is a hotkey for ActionContext events if (!_actionContextHotkeyEvents.ContainsKey(hotkey) && - _actionContextHotkeyEvents[hotkey].Command == existingBinding.Command || + _actionContextHotkeyEvents[hotkey].Command == existingBinding.Command && _actionContextHotkeyEvents[hotkey].Parameter == existingBinding.CommandParameter) { // If the hotkey is not for ActionContext events, return false From 633e6d3dfb5cb0e3ea4897324b37900969816634 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 14:44:05 +0800 Subject: [PATCH 137/150] Redesign welcome page 3 --- .../Resources/Pages/WelcomePage3.xaml | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml index 0c1dcfea047..b4aa1de4472 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml @@ -91,19 +91,75 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b7db22a8494f2284e460c953d3d3b8639772ff97 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 15:10:52 +0800 Subject: [PATCH 138/150] Redesign hotkey page --- .../Views/SettingsPaneHotkey.xaml | 95 +++++++++++-------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml index a211d67101b..b5162c8098b 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml @@ -79,52 +79,52 @@ Sub="{DynamicResource hotkeyPresetsToolTip}"> - + - + - + - + - + - + - + - + - + + + + - - - - + + + + + + + + + - - + + From 4ad7ca9d43abf60deda50401d7d3e23ba7890a87 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 15:11:21 +0800 Subject: [PATCH 139/150] Only use one string --- Flow.Launcher/Resources/Pages/WelcomePage3.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml index b4aa1de4472..5d37e4f788f 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml @@ -83,7 +83,7 @@ From 0d4598b301c45e67d691bcbd674d308197e1ec5d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 15:12:56 +0800 Subject: [PATCH 140/150] Add property changed for OpenResultModifiers --- .../UserSettings/Settings.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index a8e58f0fc81..6308e2cd0d4 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -39,7 +39,20 @@ public void Save() _storage.Save(); } - public string OpenResultModifiers { get; set; } = KeyConstant.Alt; + private string _openResultModifiers = KeyConstant.Alt; + public string OpenResultModifiers + { + get => _openResultModifiers; + set + { + if (_openResultModifiers != value) + { + _openResultModifiers = value; + OnPropertyChanged(); + } + } + } + public string ColorScheme { get; set; } = "System"; public bool ShowOpenResultHotkey { get; set; } = true; public double WindowSize { get; set; } = 580; From 6c21f746a76be02b6ed5ed2a33693784f992ed24 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 15:15:48 +0800 Subject: [PATCH 141/150] Add Result Modifier Hotkeys changed event --- Flow.Launcher/Helper/HotKeyMapper.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 540af9b526e..96df8d8666b 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -67,6 +67,8 @@ private static void InitializeRegisteredHotkeys() new(RegisteredHotkeyType.Enter, HotkeyType.SearchWindow, "Enter", "HotkeyRunDesc", _mainViewModel.OpenResultCommand), new(RegisteredHotkeyType.ToggleGameMode, HotkeyType.SearchWindow, "Ctrl+F12", "ToggleGameModeHotkey", _mainViewModel.ToggleGameModeCommand), new(RegisteredHotkeyType.CopyFilePath, HotkeyType.SearchWindow, "Ctrl+Shift+C", "CopyFilePathHotkey", _mainViewModel.CopyAlternativeCommand), + + // Result Modifier Hotkeys new(RegisteredHotkeyType.OpenResultN1, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D1", "HotkeyOpenResultN", 1, _mainViewModel.OpenResultCommand, 0), new(RegisteredHotkeyType.OpenResultN2, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D2", "HotkeyOpenResultN", 2, _mainViewModel.OpenResultCommand, 1), new(RegisteredHotkeyType.OpenResultN3, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D3", "HotkeyOpenResultN", 3, _mainViewModel.OpenResultCommand, 2), @@ -197,6 +199,21 @@ private static void Settings_PropertyChanged(object sender, PropertyChangedEvent case nameof(_settings.CycleHistoryDownHotkey): ChangeRegisteredHotkey(RegisteredHotkeyType.CycleHistoryDown, _settings.CycleHistoryDownHotkey); break; + + // Result Modifier Hotkeys + case nameof(_settings.OpenResultModifiers): + // Change all result modifier hotkeys + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN1, $"{_settings.OpenResultModifiers}+D1"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN2, $"{_settings.OpenResultModifiers}+D2"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN3, $"{_settings.OpenResultModifiers}+D3"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN4, $"{_settings.OpenResultModifiers}+D4"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN5, $"{_settings.OpenResultModifiers}+D5"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN6, $"{_settings.OpenResultModifiers}+D6"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN7, $"{_settings.OpenResultModifiers}+D7"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN8, $"{_settings.OpenResultModifiers}+D8"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN9, $"{_settings.OpenResultModifiers}+D9"); + ChangeRegisteredHotkey(RegisteredHotkeyType.OpenResultN10, $"{_settings.OpenResultModifiers}+D0"); + break; } } From 28bd07d28b24d3bb602f8efeafe56c66026fa8e7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 15:18:18 +0800 Subject: [PATCH 142/150] Improve code quality --- Flow.Launcher/Helper/HotKeyMapper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 96df8d8666b..6082f4b282f 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -483,9 +483,9 @@ kb.Gesture is KeyGesture keyGesture1 && if (existingBinding != null) { // If the hotkey is not a hotkey for ActionContext events, throw an exception to avoid duplicates - if (!IsActionContextEvent(window, existingBinding, hotkey)) + if (!IsActionContextEvent(existingBinding, hotkey)) { - throw new InvalidOperationException($"Windows key {hotkey} already exists"); + throw new InvalidOperationException($"Key {hotkey} already exists in window"); } } @@ -749,7 +749,7 @@ private static void InitializeActionContextHotkeys() } [Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] - private static bool IsActionContextEvent(MainWindow window, KeyBinding existingBinding, HotkeyModel hotkey) + private static bool IsActionContextEvent(KeyBinding existingBinding, HotkeyModel hotkey) { // Check if this hotkey is a hotkey for ActionContext events if (!_actionContextHotkeyEvents.ContainsKey(hotkey) && From 64cf1ed1303c14ad9e9dd410ac3536fa1a7ee36c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 15:22:27 +0800 Subject: [PATCH 143/150] Fix IsActionContextEvent logic --- Flow.Launcher/Helper/HotKeyMapper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 6082f4b282f..aa48142d910 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -752,9 +752,9 @@ private static void InitializeActionContextHotkeys() private static bool IsActionContextEvent(KeyBinding existingBinding, HotkeyModel hotkey) { // Check if this hotkey is a hotkey for ActionContext events - if (!_actionContextHotkeyEvents.ContainsKey(hotkey) && - _actionContextHotkeyEvents[hotkey].Command == existingBinding.Command && - _actionContextHotkeyEvents[hotkey].Parameter == existingBinding.CommandParameter) + if (_actionContextHotkeyEvents.TryGetValue(hotkey, out var value) && + value.Command == existingBinding.Command && + value.Parameter == existingBinding.CommandParameter) { // If the hotkey is not for ActionContext events, return false return true; From ada2af8d0aa34002dcbcc9c41c7cf1e9c6915523 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 12 Jul 2025 15:32:31 +0800 Subject: [PATCH 144/150] Fix open result command parameter issue --- Flow.Launcher/Helper/HotKeyMapper.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index aa48142d910..e692f51316b 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -69,16 +69,16 @@ private static void InitializeRegisteredHotkeys() new(RegisteredHotkeyType.CopyFilePath, HotkeyType.SearchWindow, "Ctrl+Shift+C", "CopyFilePathHotkey", _mainViewModel.CopyAlternativeCommand), // Result Modifier Hotkeys - new(RegisteredHotkeyType.OpenResultN1, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D1", "HotkeyOpenResultN", 1, _mainViewModel.OpenResultCommand, 0), - new(RegisteredHotkeyType.OpenResultN2, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D2", "HotkeyOpenResultN", 2, _mainViewModel.OpenResultCommand, 1), - new(RegisteredHotkeyType.OpenResultN3, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D3", "HotkeyOpenResultN", 3, _mainViewModel.OpenResultCommand, 2), - new(RegisteredHotkeyType.OpenResultN4, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D4", "HotkeyOpenResultN", 4, _mainViewModel.OpenResultCommand, 3), - new(RegisteredHotkeyType.OpenResultN5, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D5", "HotkeyOpenResultN", 5, _mainViewModel.OpenResultCommand, 4), - new(RegisteredHotkeyType.OpenResultN6, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D6", "HotkeyOpenResultN", 6, _mainViewModel.OpenResultCommand, 5), - new(RegisteredHotkeyType.OpenResultN7, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D7", "HotkeyOpenResultN", 7, _mainViewModel.OpenResultCommand, 6), - new(RegisteredHotkeyType.OpenResultN8, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D8", "HotkeyOpenResultN", 8, _mainViewModel.OpenResultCommand, 7), - new(RegisteredHotkeyType.OpenResultN9, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D9", "HotkeyOpenResultN", 9, _mainViewModel.OpenResultCommand, 8), - new(RegisteredHotkeyType.OpenResultN10, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D0", "HotkeyOpenResultN", 10, _mainViewModel.OpenResultCommand, 9), + new(RegisteredHotkeyType.OpenResultN1, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D1", "HotkeyOpenResultN", 1, _mainViewModel.OpenResultCommand, "0"), + new(RegisteredHotkeyType.OpenResultN2, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D2", "HotkeyOpenResultN", 2, _mainViewModel.OpenResultCommand, "1"), + new(RegisteredHotkeyType.OpenResultN3, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D3", "HotkeyOpenResultN", 3, _mainViewModel.OpenResultCommand, "2"), + new(RegisteredHotkeyType.OpenResultN4, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D4", "HotkeyOpenResultN", 4, _mainViewModel.OpenResultCommand, "3"), + new(RegisteredHotkeyType.OpenResultN5, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D5", "HotkeyOpenResultN", 5, _mainViewModel.OpenResultCommand, "4"), + new(RegisteredHotkeyType.OpenResultN6, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D6", "HotkeyOpenResultN", 6, _mainViewModel.OpenResultCommand, "5"), + new(RegisteredHotkeyType.OpenResultN7, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D7", "HotkeyOpenResultN", 7, _mainViewModel.OpenResultCommand, "6"), + new(RegisteredHotkeyType.OpenResultN8, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D8", "HotkeyOpenResultN", 8, _mainViewModel.OpenResultCommand, "7"), + new(RegisteredHotkeyType.OpenResultN9, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D9", "HotkeyOpenResultN", 9, _mainViewModel.OpenResultCommand, "8"), + new(RegisteredHotkeyType.OpenResultN10, HotkeyType.SearchWindow, $"{_settings.OpenResultModifiers}+D0", "HotkeyOpenResultN", 10, _mainViewModel.OpenResultCommand, "9"), // Flow Launcher global hotkeys new(RegisteredHotkeyType.Toggle, HotkeyType.Global, _settings.Hotkey, "flowlauncherHotkey", _mainViewModel.CheckAndToggleFlowLauncherCommand, null, () => _settings.Hotkey = ""), From 11e8a58b3b3c3a8af013287495c08d0c65a38df0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 20 Jul 2025 20:33:58 +0800 Subject: [PATCH 145/150] Add dialog jump hotkey --- Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs | 7 +++++-- .../Hotkey/RegisteredHotkeyData.cs | 1 + Flow.Launcher/Helper/HotKeyMapper.cs | 5 +++++ .../ViewModels/SettingsPaneGeneralViewModel.cs | 10 +--------- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs index 65652878fc8..0c211eb908e 100644 --- a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs +++ b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs @@ -9,10 +9,10 @@ using Flow.Launcher.Infrastructure.DialogJump.Models; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using NHotkey; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.Accessibility; +using CommunityToolkit.Mvvm.Input; namespace Flow.Launcher.Infrastructure.DialogJump { @@ -455,7 +455,10 @@ private static void InvokeHideDialogJumpWindow() #region Hotkey - public static void OnToggleHotkey(object sender, HotkeyEventArgs args) + private static RelayCommand _dialogJumpCommand; + public static IRelayCommand DialogJumpCommand => _dialogJumpCommand ??= new RelayCommand(OnToggleHotkey); + + private static void OnToggleHotkey() { _ = Task.Run(async () => { diff --git a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs index a1b577ea70c..8459798ba80 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs @@ -285,6 +285,7 @@ public enum RegisteredHotkeyType OpenResultN10, Toggle, + DialogJump, Preview, AutoComplete, diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index e692f51316b..08e4194a46e 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -9,6 +9,7 @@ using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Infrastructure.DialogJump; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; @@ -82,6 +83,7 @@ private static void InitializeRegisteredHotkeys() // Flow Launcher global hotkeys new(RegisteredHotkeyType.Toggle, HotkeyType.Global, _settings.Hotkey, "flowlauncherHotkey", _mainViewModel.CheckAndToggleFlowLauncherCommand, null, () => _settings.Hotkey = ""), + new(RegisteredHotkeyType.DialogJump, HotkeyType.Global, _settings.DialogJumpHotkey, "dialogJumpHotkey", DialogJump.DialogJumpCommand, null, () => _settings.DialogJumpHotkey = ""), // Flow Launcher window hotkeys new(RegisteredHotkeyType.Preview, HotkeyType.SearchWindow, _settings.PreviewHotkey, "previewHotkey", _mainViewModel.TogglePreviewCommand, null, () => _settings.PreviewHotkey = ""), @@ -155,6 +157,9 @@ private static void Settings_PropertyChanged(object sender, PropertyChangedEvent case nameof(_settings.Hotkey): ChangeRegisteredHotkey(RegisteredHotkeyType.Toggle, _settings.Hotkey); break; + case nameof(_settings.DialogJumpHotkey): + ChangeRegisteredHotkey(RegisteredHotkeyType.DialogJump, _settings.DialogJumpHotkey); + break; // Flow Launcher window hotkeys case nameof(_settings.PreviewHotkey): diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 21444ccee30..de3147bf57f 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; @@ -156,14 +156,6 @@ public bool EnableDialogJump { Settings.EnableDialogJump = value; DialogJump.SetupDialogJump(value); - if (Settings.EnableDialogJump) - { - HotKeyMapper.SetHotkey(new(Settings.DialogJumpHotkey), DialogJump.OnToggleHotkey); - } - else - { - HotKeyMapper.RemoveHotkey(Settings.DialogJumpHotkey); - } } } } From 2ce415791a26a9ecababfc62f829dd5c5d60e3b7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 20 Jul 2025 20:35:26 +0800 Subject: [PATCH 146/150] Fix build issue --- Flow.Launcher/HotkeyControl.xaml.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 8b4418133c2..befd8ac68ed 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -1,4 +1,4 @@ -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -221,7 +221,8 @@ public string Hotkey case HotkeyType.WindowPluginHotkey: // We should not save it to settings here because it is a custom plugin hotkey // and it will be saved in the plugin settings - hotkey = value; + hotkey = value; + break; default: throw new System.NotImplementedException("Hotkey type not set"); } From 2f71d0d41798ef4bd176f5efe2978556a1ac7d68 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 20 Jul 2025 20:41:17 +0800 Subject: [PATCH 147/150] Fix key duplication --- Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs index 8b2ff1c5ceb..82b9be8180a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs @@ -77,7 +77,7 @@ public static void Rename(string NewFileName, FileSystemInfo oldInfo, IPublicAPI switch (exception) { case FileNotFoundException: - api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_file_not_found"), oldInfo.FullName)); + api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_item_not_found"), oldInfo.FullName)); return; case NotANewNameException: api.ShowMsgError(string.Format(api.GetTranslation("plugin_explorer_not_a_new_name"), NewFileName)); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 757c33d2ba4..013236658d8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -205,7 +205,7 @@ The given name: {0} was not new. {0} may not be empty. {0} is an invalid name. - The specified item: {0} was not found + The specified item: {0} was not found Open a dialog to rename file or folder This cannot be renamed. Successfully renamed it to: {0} From adb1adb2094a7facdf235255af477c6b4f7eed39 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 20 Jul 2025 20:42:37 +0800 Subject: [PATCH 148/150] Do not execute when dialog jump is disabled --- Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs index 0c211eb908e..f39a9dd74b8 100644 --- a/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs +++ b/Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs @@ -453,13 +453,15 @@ private static void InvokeHideDialogJumpWindow() #endregion - #region Hotkey + #region Hotkey Command private static RelayCommand _dialogJumpCommand; public static IRelayCommand DialogJumpCommand => _dialogJumpCommand ??= new RelayCommand(OnToggleHotkey); private static void OnToggleHotkey() { + if (!_settings.EnableDialogJump) return; + _ = Task.Run(async () => { try From b88e2e9e8ead07b5c3f1fd0e957f05e6ee47077f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 20 Jul 2025 20:58:49 +0800 Subject: [PATCH 149/150] Do not register dialog jump hotkey when dialog jump is disabled --- .../UserSettings/Settings.cs | 14 +++++++++- Flow.Launcher/Helper/HotKeyMapper.cs | 26 ++++++++++++------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index c6cf2097a30..0c322bc0757 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -531,7 +531,19 @@ public CustomBrowserViewModel CustomBrowser } }; - public bool EnableDialogJump { get; set; } = true; + private bool _enableDialogJump = true; + public bool EnableDialogJump + { + get => _enableDialogJump; + set + { + if (_enableDialogJump != value) + { + _enableDialogJump = value; + OnPropertyChanged(); + } + } + } public bool AutoDialogJump { get; set; } = false; diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 08e4194a46e..29fca1cbe63 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -139,6 +139,11 @@ private static void InitializeRegisteredHotkeys() 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); } @@ -158,7 +163,10 @@ private static void Settings_PropertyChanged(object sender, PropertyChangedEvent ChangeRegisteredHotkey(RegisteredHotkeyType.Toggle, _settings.Hotkey); break; case nameof(_settings.DialogJumpHotkey): - ChangeRegisteredHotkey(RegisteredHotkeyType.DialogJump, _settings.DialogJumpHotkey); + ChangeRegisteredHotkey(RegisteredHotkeyType.DialogJump, _settings.DialogJumpHotkey, _settings.EnableDialogJump); + break; + case nameof(_settings.EnableDialogJump): + ChangeRegisteredHotkey(RegisteredHotkeyType.DialogJump, _settings.DialogJumpHotkey, _settings.EnableDialogJump); break; // Flow Launcher window hotkeys @@ -592,22 +600,19 @@ kb.Gesture is KeyGesture keyGesture1 && #region Hotkey Changing - private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, string newHotkeyStr) + private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, string newHotkeyStr, bool setHotkey = true) { var newHotkey = new HotkeyModel(newHotkeyStr); - ChangeRegisteredHotkey(registeredType, newHotkey); + ChangeRegisteredHotkey(registeredType, newHotkey, setHotkey); } - private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, HotkeyModel newHotkey) + private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, HotkeyModel newHotkey, bool setHotkey = true) { // Find the old registered hotkey data item var registeredHotkeyData = _settings.RegisteredHotkeys.FirstOrDefault(h => h.RegisteredType == registeredType); // If it is not found, return - if (registeredHotkeyData == null) - { - return; - } + if (registeredHotkeyData == null) return; // Remove the old hotkey RemoveHotkey(registeredHotkeyData); @@ -616,7 +621,10 @@ private static void ChangeRegisteredHotkey(RegisteredHotkeyType registeredType, registeredHotkeyData.SetHotkey(newHotkey); // Set the new hotkey - SetHotkey(registeredHotkeyData); + if (setHotkey) + { + SetHotkey(registeredHotkeyData); + } } #endregion From 98816ac1814c08be860c94cd73db5d000a381bd4 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 27 Jul 2025 20:10:10 +0800 Subject: [PATCH 150/150] Fix build issue --- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 869ba849781..ada0dfef198 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading;