From 89bca3b6a96976abf7acc98d2fea4176f6bf24bc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:00:57 +0800 Subject: [PATCH 001/158] Add plugin cache path --- Flow.Launcher.Infrastructure/Constant.cs | 1 + Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index c86ed4324..d694e0d35 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -47,6 +47,7 @@ namespace Flow.Launcher.Infrastructure public const string Themes = "Themes"; public const string Settings = "Settings"; public const string Logs = "Logs"; + public const string Cache = "Cache"; public const string Website = "https://flowlauncher.com"; public const string SponsorPage = "https://github.com/sponsors/Flow-Launcher"; diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index e294f52b8..fe3be43aa 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -26,7 +26,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings } public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); - public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), "Settings", Constant.Plugins); + public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), Constant.Settings, Constant.Plugins); + public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); public const string PythonEnvironmentName = "Python"; public const string NodeEnvironmentName = "Node.js"; From 591941898048af3e07a394bc17d3d780758a1dc9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:01:53 +0800 Subject: [PATCH 002/158] Add assembly name & plugin settings path & plugin cache path in meta data for csharp plugins --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 6 ++++++ Flow.Launcher.Plugin/PluginMetadata.cs | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 7973c66ba..e35f2a097 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -73,6 +74,11 @@ namespace Flow.Launcher.Core.Plugin typeof(IAsyncPlugin)); plugin = Activator.CreateInstance(type) as IAsyncPlugin; + + var assemblyName = assembly.GetName().Name; + metadata.AssemblyName = assemblyName; + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, assemblyName); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, assemblyName); } #if DEBUG catch (Exception e) diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index b4e06913e..15ba2deaa 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -16,12 +16,14 @@ namespace Flow.Launcher.Plugin public string Website { get; set; } public bool Disabled { get; set; } public string ExecuteFilePath { get; private set;} - public string ExecuteFileName { get; set; } + [JsonIgnore] + public string AssemblyName { get; internal set; } + public string PluginDirectory { - get { return _pluginDirectory; } + get => _pluginDirectory; internal set { _pluginDirectory = value; @@ -49,9 +51,17 @@ namespace Flow.Launcher.Plugin /// [JsonIgnore] public long InitTime { get; set; } + [JsonIgnore] public long AvgQueryTime { get; set; } + [JsonIgnore] public int QueryCount { get; set; } + + [JsonIgnore] + public string PluginSettingsDirectoryPath { get; internal set; } + + [JsonIgnore] + public string PluginCacheDirectoryPath { get; internal set; } } } From f1b5e68cf2efc1da7b561dc495c1b89954196317 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:02:32 +0800 Subject: [PATCH 003/158] Remove reflection codes for deleting csharp plugin settings --- Flow.Launcher.Core/Plugin/PluginManager.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 29f1604c4..7f73bf1cf 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -548,14 +548,10 @@ namespace Flow.Launcher.Core.Plugin { if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader { - var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath); - var assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var assemblyName = assembly.GetName().Name; - // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin // so we need to remove it from the api instance var method = API.GetType().GetMethod("RemovePluginSettings"); - var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName }); + var pluginJsonStorage = method?.Invoke(API, new object[] { plugin.AssemblyName }); // if there exists a json storage for current plugin, we need to delete the directory path if (pluginJsonStorage != null) From 1aaba46b8f284ce512ab8aa6c98ee66f97511927 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:06:21 +0800 Subject: [PATCH 004/158] Use constants & data location for code quality --- Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs | 2 +- Flow.Launcher.Infrastructure/Storage/JsonStorage.cs | 2 +- Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs | 2 +- Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 4 +++- .../SettingPages/ViewModels/SettingsPaneAboutViewModel.cs | 4 ++-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 2a439b8cc..48c5fc141 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Infrastructure.Storage /// public class BinaryStorage { - const string DirectoryName = "Cache"; + const string DirectoryName = Constant.Cache; const string FileSuffix = ".cache"; diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 642250627..7008026c8 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -16,7 +16,7 @@ namespace Flow.Launcher.Infrastructure.Storage protected T? Data; // need a new directory name - public const string DirectoryName = "Settings"; + public const string DirectoryName = Constant.Settings; public const string FileSuffix = ".json"; protected string FilePath { get; init; } = null!; diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index bc3900da8..42453a7a1 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -13,7 +13,7 @@ namespace Flow.Launcher.Infrastructure.Storage // C# related, add python related below var dataType = typeof(T); AssemblyName = dataType.Assembly.GetName().Name; - DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, AssemblyName); + DirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, AssemblyName); Helper.ValidateDirectory(DirectoryPath); FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index fe3be43aa..97cd2da54 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -25,8 +25,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings return false; } + public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); - public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), Constant.Settings, Constant.Plugins); + + public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectorty, Constant.Plugins); public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); public const string PythonEnvironmentName = "Python"; diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 6e81db5e0..2892a0654 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -77,13 +77,13 @@ public partial class SettingsPaneAboutViewModel : BaseModel [RelayCommand] private void OpenSettingsFolder() { - PluginManager.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Settings)); + PluginManager.API.OpenDirectory(DataLocation.SettingsDirectorty); } [RelayCommand] private void OpenParentOfSettingsFolder(object parameter) { - string settingsFolderPath = Path.Combine(DataLocation.DataDirectory(), Constant.Settings); + string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectorty); string parentFolderPath = Path.GetDirectoryName(settingsFolderPath); PluginManager.API.OpenDirectory(parentFolderPath); } From 9cd30112fe6d9056a211e8f9dabe17c056e429a9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:28:03 +0800 Subject: [PATCH 005/158] Add documents for plugin metadata --- Flow.Launcher.Plugin/PluginMetadata.cs | 98 ++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 15ba2deaa..6dc8acc63 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -4,23 +4,74 @@ using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin { + /// + /// Plugin metadata + /// public class PluginMetadata : BaseModel { private string _pluginDirectory; + + /// + /// Plugin ID. + /// public string ID { get; set; } + + /// + /// Plugin name. + /// public string Name { get; set; } + + /// + /// Plugin author. + /// public string Author { get; set; } + + /// + /// Plugin version. + /// public string Version { get; set; } + + /// + /// Plugin language. + /// See + /// public string Language { get; set; } + + /// + /// Plugin description. + /// public string Description { get; set; } + + /// + /// Plugin website. + /// public string Website { get; set; } + + /// + /// Whether plugin is disabled. + /// public bool Disabled { get; set; } - public string ExecuteFilePath { get; private set;} + + /// + /// Plugin execute file path. + /// + public string ExecuteFilePath { get; private set; } + + /// + /// Plugin execute file name. + /// public string ExecuteFileName { get; set; } + /// + /// Plugin assembly name. + /// Only available for .Net plugins. + /// [JsonIgnore] public string AssemblyName { get; internal set; } + /// + /// Plugin source directory. + /// public string PluginDirectory { get => _pluginDirectory; @@ -32,36 +83,69 @@ namespace Flow.Launcher.Plugin } } + /// + /// The first action keyword of plugin. + /// public string ActionKeyword { get; set; } + /// + /// All action keywords of plugin. + /// public List ActionKeywords { get; set; } + /// + /// Plugin icon path. + /// public string IcoPath { get; set;} - - public override string ToString() - { - return Name; - } + /// + /// Plugin priority. + /// [JsonIgnore] public int Priority { get; set; } /// - /// Init time include both plugin load time and init time + /// Init time include both plugin load time and init time. /// [JsonIgnore] public long InitTime { get; set; } + /// + /// Average query time. + /// [JsonIgnore] public long AvgQueryTime { get; set; } + /// + /// Query count. + /// [JsonIgnore] public int QueryCount { get; set; } + /// + /// The path to the plugin settings directory. + /// It is used to store plugin settings files and data files. + /// When plugin is deleted, FL will ask users whether to keep its settings. + /// If users do not want to keep, this directory will be deleted. + /// [JsonIgnore] public string PluginSettingsDirectoryPath { get; internal set; } + /// + /// The path to the plugin cache directory. + /// It is used to store cache files. + /// When plugin is deleted, this directory will be deleted as well. + /// [JsonIgnore] public string PluginCacheDirectoryPath { get; internal set; } + + /// + /// Convert to string. + /// + /// + public override string ToString() + { + return Name; + } } } From 8f7ad27aef953b7b8d9343b0f1444c596c9aef0c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:28:14 +0800 Subject: [PATCH 006/158] Remove useless usings --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 11 ----------- Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs | 16 ---------------- 2 files changed, 27 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 97c3c8981..f6b200aa5 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -1,10 +1,8 @@ using Flow.Launcher.Core.Resource; -using Flow.Launcher.Infrastructure; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Text; using System.Text.Json; using System.Threading; @@ -14,15 +12,6 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Microsoft.IO; using System.Windows; -using System.Windows.Controls; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; -using CheckBox = System.Windows.Controls.CheckBox; -using Control = System.Windows.Controls.Control; -using Orientation = System.Windows.Controls.Orientation; -using TextBox = System.Windows.Controls.TextBox; -using UserControl = System.Windows.Controls.UserControl; -using System.Windows.Documents; namespace Flow.Launcher.Core.Plugin { diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs index ed8f94bcf..947221e77 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs @@ -1,32 +1,16 @@ using Flow.Launcher.Core.Resource; -using Flow.Launcher.Infrastructure; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Microsoft.IO; -using System.Windows; -using System.Windows.Controls; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; -using CheckBox = System.Windows.Controls.CheckBox; using Control = System.Windows.Controls.Control; -using Orientation = System.Windows.Controls.Orientation; -using TextBox = System.Windows.Controls.TextBox; -using UserControl = System.Windows.Controls.UserControl; -using System.Windows.Documents; -using static System.Windows.Forms.LinkLabel; -using Droplex; -using System.Windows.Forms; -using Microsoft.VisualStudio.Threading; namespace Flow.Launcher.Core.Plugin { From d07b304f9ec809ab7636151f571dd515e14b540f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 11:35:41 +0800 Subject: [PATCH 007/158] Improve code quality --- Flow.Launcher.Core/Plugin/PluginConfig.cs | 7 +++---- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 4 ++-- .../UserSettings/PluginSettings.cs | 4 +--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index dd6517a7f..163f97046 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.IO; @@ -9,7 +9,6 @@ using System.Text.Json; namespace Flow.Launcher.Core.Plugin { - internal abstract class PluginConfig { /// @@ -112,7 +111,7 @@ namespace Flow.Launcher.Core.Plugin metadata = JsonSerializer.Deserialize(File.ReadAllText(configPath)); metadata.PluginDirectory = pluginDirectory; // for plugins which doesn't has ActionKeywords key - metadata.ActionKeywords = metadata.ActionKeywords ?? new List { metadata.ActionKeyword }; + metadata.ActionKeywords ??= new List { metadata.ActionKeyword }; // for plugin still use old ActionKeyword metadata.ActionKeyword = metadata.ActionKeywords?[0]; } @@ -137,4 +136,4 @@ namespace Flow.Launcher.Core.Plugin return metadata; } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index e35f2a097..70c43435d 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -81,7 +81,7 @@ namespace Flow.Launcher.Core.Plugin metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, assemblyName); } #if DEBUG - catch (Exception e) + catch (Exception) { throw; } @@ -117,7 +117,7 @@ namespace Flow.Launcher.Core.Plugin if (erroredPlugins.Count > 0) { - var errorPluginString = String.Join(Environment.NewLine, erroredPlugins); + var errorPluginString = string.Join(Environment.NewLine, erroredPlugins); var errorMessage = "The following " + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index 98f4dccda..1d06e18f7 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -32,10 +32,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings { foreach (var metadata in metadatas) { - if (Plugins.ContainsKey(metadata.ID)) + if (Plugins.TryGetValue(metadata.ID, out var settings)) { - var settings = Plugins[metadata.ID]; - if (string.IsNullOrEmpty(settings.Version)) settings.Version = metadata.Version; From b50db58673d19756dda1de4d8d453f8c5e535504 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 12:37:08 +0800 Subject: [PATCH 008/158] Add assembly name & plugin settings path & plugin cache path in meta data for non-csharp plugins --- .../Environments/AbstractPluginEnvironment.cs | 6 ++++ Flow.Launcher.Core/Plugin/PluginsLoader.cs | 28 ++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 6d41e2383..93058759a 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -4,6 +4,7 @@ using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Windows; using System.Windows.Forms; @@ -113,7 +114,12 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments foreach (var metadata in PluginMetadataList) { if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) + { pluginPairs.Add(CreatePluginPair(filePath, metadata)); + metadata.AssemblyName = string.Empty; + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + } } return pluginPairs; diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 70c43435d..91177e745 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -139,9 +139,19 @@ namespace Flow.Launcher.Core.Plugin { return source .Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => { - Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata + var plugin = new PluginPair + { + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), + Metadata = metadata + }; + + plugin.Metadata.AssemblyName = string.Empty; + plugin.Metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, plugin.Metadata.Name); + plugin.Metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, plugin.Metadata.Name); + + return plugin; }); } @@ -149,9 +159,19 @@ namespace Flow.Launcher.Core.Plugin { return source .Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => { - Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), Metadata = metadata + var plugin = new PluginPair + { + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), + Metadata = metadata + }; + + plugin.Metadata.AssemblyName = string.Empty; + plugin.Metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, plugin.Metadata.Name); + plugin.Metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, plugin.Metadata.Name); + + return plugin; }); } } From 601211173c761255770b53502d025549c5d985df Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:17:02 +0800 Subject: [PATCH 009/158] Add log directory & version log directory & themes directory in data location --- Flow.Launcher.Infrastructure/Logger/Log.cs | 2 +- .../UserSettings/DataLocation.cs | 4 ++++ .../ViewModels/SettingsPaneAboutViewModel.cs | 2 +- .../ViewModels/SettingsPaneThemeViewModel.cs | 2 +- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 14 ++++++-------- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Logger/Log.cs b/Flow.Launcher.Infrastructure/Logger/Log.cs index d4bd473ac..fff52a8ab 100644 --- a/Flow.Launcher.Infrastructure/Logger/Log.cs +++ b/Flow.Launcher.Infrastructure/Logger/Log.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Infrastructure.Logger { public static class Log { - public const string DirectoryName = "Logs"; + public const string DirectoryName = Constant.Logs; public static string CurrentLogDirectory { get; } diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 97cd2da54..96997b806 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -25,8 +25,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings return false; } + public static readonly string VersionLogDirectory = Path.Combine(LogDirectory, Constant.Version); + + public static readonly string LogDirectory = Path.Combine(DataDirectory(), Constant.Logs); public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); + public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectorty, Constant.Plugins); public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 2892a0654..ee684e7ca 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -115,7 +115,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel private static DirectoryInfo GetLogDir(string version = "") { - return new DirectoryInfo(Path.Combine(DataLocation.DataDirectory(), Constant.Logs, version)); + return new DirectoryInfo(Path.Combine(DataLocation.LogDirectory, version)); } private static List GetLogFiles(string version = "") diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 8d8ccb780..fc9122b53 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -420,7 +420,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel [RelayCommand] private void OpenThemesFolder() { - App.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Themes)); + App.API.OpenDirectory(DataLocation.ThemesDirectory); } public void UpdateColorScheme() diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 2331ee68c..13bba04b4 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -147,8 +147,6 @@ namespace Flow.Launcher.Plugin.Sys private List Commands() { var results = new List(); - var logPath = Path.Combine(DataLocation.DataDirectory(), "Logs", Constant.Version); - var userDataPath = DataLocation.DataDirectory(); var recycleBinFolder = "shell:RecycleBinFolder"; results.AddRange(new[] { @@ -406,11 +404,11 @@ namespace Flow.Launcher.Plugin.Sys Title = "Open Log Location", SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_open_log_location"), IcoPath = "Images\\app.png", - CopyText = logPath, - AutoCompleteText = logPath, + CopyText = DataLocation.VersionLogDirectory, + AutoCompleteText = DataLocation.VersionLogDirectory, Action = c => { - context.API.OpenDirectory(logPath); + context.API.OpenDirectory(DataLocation.VersionLogDirectory); return true; } }, @@ -432,11 +430,11 @@ namespace Flow.Launcher.Plugin.Sys Title = "Flow Launcher UserData Folder", SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_open_userdata_location"), IcoPath = "Images\\app.png", - CopyText = userDataPath, - AutoCompleteText = userDataPath, + CopyText = DataLocation.DataDirectory(), + AutoCompleteText = DataLocation.DataDirectory(), Action = c => { - context.API.OpenDirectory(userDataPath); + context.API.OpenDirectory(DataLocation.DataDirectory()); return true; } }, From 3efe550b7f2d5008a20e749258e452062edb7ad6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:28:58 +0800 Subject: [PATCH 010/158] Use context plugin settings path --- .../Environments/AbstractPluginEnvironment.cs | 3 ++- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 3 +-- Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs | 12 +----------- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 1 + .../Storage/PluginJsonStorage.cs | 8 -------- 5 files changed, 5 insertions(+), 22 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 93058759a..5f507021c 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -115,10 +115,11 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments { if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) { - pluginPairs.Add(CreatePluginPair(filePath, metadata)); metadata.AssemblyName = string.Empty; metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + + pluginPairs.Add(CreatePluginPair(filePath, metadata)); } } diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index f6b200aa5..88d595301 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -8,7 +8,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Microsoft.IO; using System.Windows; @@ -31,7 +30,7 @@ namespace Flow.Launcher.Core.Plugin private int RequestId { get; set; } private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml"); - private string SettingPath => Path.Combine(DataLocation.PluginSettingsDirectory, Context.CurrentPluginMetadata.Name, "Settings.json"); + private string SettingPath => Path.Combine(Context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "Settings.json"); public override List LoadContextMenus(Result selectedResult) { diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs index 947221e77..c0852958e 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -28,8 +27,7 @@ namespace Flow.Launcher.Core.Plugin private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml"); - private string SettingDirectory => Path.Combine(DataLocation.PluginSettingsDirectory, - Context.CurrentPluginMetadata.Name); + private string SettingDirectory => Context.CurrentPluginMetadata.PluginSettingsDirectoryPath; private string SettingPath => Path.Combine(SettingDirectory, "Settings.json"); @@ -145,13 +143,5 @@ namespace Flow.Launcher.Core.Plugin { return Settings.CreateSettingPanel(); } - - public void DeletePluginSettingsDirectory() - { - if (Directory.Exists(SettingDirectory)) - { - Directory.Delete(SettingDirectory, true); - } - } } } diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 91177e745..6e9bcd05f 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -75,6 +75,7 @@ namespace Flow.Launcher.Core.Plugin plugin = Activator.CreateInstance(type) as IAsyncPlugin; + // Same as PluginJsonStorage.cs constructor var assemblyName = assembly.GetName().Name; metadata.AssemblyName = assemblyName; metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, assemblyName); diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index 42453a7a1..b377c81aa 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -23,13 +23,5 @@ namespace Flow.Launcher.Infrastructure.Storage { Data = data; } - - public void DeleteDirectory() - { - if (Directory.Exists(DirectoryPath)) - { - Directory.Delete(DirectoryPath, true); - } - } } } From 126153bf20b258babca833fb35e0f22a7d10f15f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:35:17 +0800 Subject: [PATCH 011/158] Improve plugin settings directory clean & Support plugin cache directory clean --- Flow.Launcher.Core/Plugin/PluginManager.cs | 61 +++++++++------------- Flow.Launcher/Languages/en.xaml | 2 + Flow.Launcher/PublicAPIInstance.cs | 5 +- 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 7f73bf1cf..9462df740 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -546,50 +546,41 @@ namespace Flow.Launcher.Core.Plugin if (removePluginSettings) { - if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader + // For dotnet plugins, we need to remove their PluginJsonStorage instance + if (AllowedLanguage.IsDotNet(plugin.Language)) { - // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin - // so we need to remove it from the api instance var method = API.GetType().GetMethod("RemovePluginSettings"); - var pluginJsonStorage = method?.Invoke(API, new object[] { plugin.AssemblyName }); - - // if there exists a json storage for current plugin, we need to delete the directory path - if (pluginJsonStorage != null) - { - var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory"); - try - { - deleteMethod?.Invoke(pluginJsonStorage, null); - } - catch (Exception e) - { - Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); - API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), - string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); - } - } + method?.Invoke(API, new object[] { plugin.AssemblyName }); } - else // the plugin with json prc interface + + try { - var pluginPair = AllPlugins.FirstOrDefault(p => p.Metadata.ID == plugin.ID); - if (pluginPair != null && pluginPair.Plugin is JsonRPCPlugin jsonRpcPlugin) - { - try - { - jsonRpcPlugin.DeletePluginSettingsDirectory(); - } - catch (Exception e) - { - Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); - API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), - string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); - } - } + var pluginSettingsDirectory = plugin.PluginSettingsDirectoryPath; + if (Directory.Exists(pluginSettingsDirectory)) + Directory.Delete(pluginSettingsDirectory, true); + } + catch (Exception e) + { + Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin settings folder for {plugin.Name}", e); + API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), + string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); } } if (removePluginFromSettings) { + try + { + var pluginCacheDirectory = plugin.PluginCacheDirectoryPath; + if (Directory.Exists(pluginCacheDirectory)) + Directory.Delete(pluginCacheDirectory, true); + } + catch (Exception e) + { + Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin cache folder for {plugin.Name}", e); + API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"), + string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name)); + } Settings.Plugins.Remove(plugin.ID); AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index c66772c83..cbef73b84 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -131,6 +131,8 @@ Uninstall Fail to remove plugin settings Plugins: {0} - Fail to remove plugin settings files, please remove them manually + Fail to remove plugin cache + Plugins: {0} - Fail to remove plugin cache files, please remove them manually Plugin Store diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index e5bc74958..cfbe0e951 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -189,7 +189,7 @@ namespace Flow.Launcher private readonly ConcurrentDictionary _pluginJsonStorages = new(); - public object RemovePluginSettings(string assemblyName) + public void RemovePluginSettings(string assemblyName) { foreach (var keyValuePair in _pluginJsonStorages) { @@ -199,11 +199,8 @@ namespace Flow.Launcher if (name == assemblyName) { _pluginJsonStorages.Remove(key, out var pluginJsonStorage); - return pluginJsonStorage; } } - - return null; } /// From 3106b025e399ce37ecdd2a63cf5b6d9c90c95b93 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:46:58 +0800 Subject: [PATCH 012/158] Support plugin directory update & validate --- .../Environments/AbstractPluginEnvironment.cs | 3 --- Flow.Launcher.Core/Plugin/PluginManager.cs | 27 ++++++++++++++++--- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 10 +------ Flow.Launcher.Infrastructure/Logger/Log.cs | 2 +- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 5f507021c..7ed5f903f 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -116,9 +116,6 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) { metadata.AssemblyName = string.Empty; - metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); - metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); - pluginPairs.Add(CreatePluginPair(filePath, metadata)); } } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 9462df740..c88937c75 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -32,7 +32,7 @@ namespace Flow.Launcher.Core.Plugin private static PluginsSettings Settings; private static List _metadatas; - private static List _modifiedPlugins = new List(); + private static List _modifiedPlugins = new(); /// /// Directories that will hold Flow Launcher plugin directory @@ -152,6 +152,27 @@ namespace Flow.Launcher.Core.Plugin Settings = settings; Settings.UpdatePluginSettings(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); + UpdateAndValidatePluginDirectory(_metadatas); + } + + private static void UpdateAndValidatePluginDirectory(List metadatas) + { + foreach (var metadata in metadatas) + { + if (AllowedLanguage.IsDotNet(metadata.Language)) + { + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName); + } + else + { + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + } + + Helper.ValidateDirectory(metadata.PluginSettingsDirectoryPath); + Helper.ValidateDirectory(metadata.PluginCacheDirectoryPath); + } } /// @@ -226,11 +247,9 @@ namespace Flow.Launcher.Core.Plugin if (query is null) return Array.Empty(); - if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword)) + if (!NonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugin)) return GlobalPlugins; - - var plugin = NonGlobalPlugins[query.ActionKeyword]; return new List { plugin diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 6e9bcd05f..03f2ed4c6 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -75,11 +75,7 @@ namespace Flow.Launcher.Core.Plugin plugin = Activator.CreateInstance(type) as IAsyncPlugin; - // Same as PluginJsonStorage.cs constructor - var assemblyName = assembly.GetName().Name; - metadata.AssemblyName = assemblyName; - metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, assemblyName); - metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, assemblyName); + metadata.AssemblyName = assembly.GetName().Name; } #if DEBUG catch (Exception) @@ -149,8 +145,6 @@ namespace Flow.Launcher.Core.Plugin }; plugin.Metadata.AssemblyName = string.Empty; - plugin.Metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, plugin.Metadata.Name); - plugin.Metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, plugin.Metadata.Name); return plugin; }); @@ -169,8 +163,6 @@ namespace Flow.Launcher.Core.Plugin }; plugin.Metadata.AssemblyName = string.Empty; - plugin.Metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, plugin.Metadata.Name); - plugin.Metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, plugin.Metadata.Name); return plugin; }); diff --git a/Flow.Launcher.Infrastructure/Logger/Log.cs b/Flow.Launcher.Infrastructure/Logger/Log.cs index fff52a8ab..5b5a9279d 100644 --- a/Flow.Launcher.Infrastructure/Logger/Log.cs +++ b/Flow.Launcher.Infrastructure/Logger/Log.cs @@ -18,7 +18,7 @@ namespace Flow.Launcher.Infrastructure.Logger static Log() { - CurrentLogDirectory = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Version); + CurrentLogDirectory = DataLocation.VersionLogDirectory; if (!Directory.Exists(CurrentLogDirectory)) { Directory.CreateDirectory(CurrentLogDirectory); From 012ef494e10dd7f1ee1ce84a9e2cf9d52784b3db Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 14:11:45 +0800 Subject: [PATCH 013/158] Fix log directory fetch issue --- Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 96997b806..ed2179760 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -25,9 +25,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings return false; } - public static readonly string VersionLogDirectory = Path.Combine(LogDirectory, Constant.Version); + public static string VersionLogDirectory => Path.Combine(LogDirectory, Constant.Version); + public static string LogDirectory => Path.Combine(DataDirectory(), Constant.Logs); - public static readonly string LogDirectory = Path.Combine(DataDirectory(), Constant.Logs); public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); From 58de62565ab9c86e00ef6433ab937d623a45db60 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 14:14:19 +0800 Subject: [PATCH 014/158] Do not validate plugin settings & cache path --- Flow.Launcher.Core/Plugin/PluginManager.cs | 7 ++----- Flow.Launcher.Plugin/PluginMetadata.cs | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index c88937c75..bbd189efb 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -152,10 +152,10 @@ namespace Flow.Launcher.Core.Plugin Settings = settings; Settings.UpdatePluginSettings(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); - UpdateAndValidatePluginDirectory(_metadatas); + UpdatePluginDirectory(_metadatas); } - private static void UpdateAndValidatePluginDirectory(List metadatas) + private static void UpdatePluginDirectory(List metadatas) { foreach (var metadata in metadatas) { @@ -169,9 +169,6 @@ namespace Flow.Launcher.Core.Plugin metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); } - - Helper.ValidateDirectory(metadata.PluginSettingsDirectoryPath); - Helper.ValidateDirectory(metadata.PluginCacheDirectoryPath); } } diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 6dc8acc63..dae8f58fd 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -123,7 +123,7 @@ namespace Flow.Launcher.Plugin public int QueryCount { get; set; } /// - /// The path to the plugin settings directory. + /// The path to the plugin settings directory which is not validated. /// It is used to store plugin settings files and data files. /// When plugin is deleted, FL will ask users whether to keep its settings. /// If users do not want to keep, this directory will be deleted. @@ -132,7 +132,7 @@ namespace Flow.Launcher.Plugin public string PluginSettingsDirectoryPath { get; internal set; } /// - /// The path to the plugin cache directory. + /// The path to the plugin cache directory which is not validated. /// It is used to store cache files. /// When plugin is deleted, this directory will be deleted as well. /// From 47adfd12868c155fb0de621f9b16f72b6b336c93 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:15:02 +0800 Subject: [PATCH 015/158] Improve code quality --- .../Storage/BinaryStorage.cs | 23 +++++++------------ .../UserSettings/DataLocation.cs | 1 + 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 48c5fc141..5b73faae6 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -1,9 +1,4 @@ -using System; -using System.IO; -using System.Reflection; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters; -using System.Runtime.Serialization.Formatters.Binary; +using System.IO; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; @@ -16,18 +11,16 @@ namespace Flow.Launcher.Infrastructure.Storage /// Normally, it has better performance, but not readable /// /// - /// It utilize MemoryPack, which means the object must be MemoryPackSerializable - /// https://github.com/Cysharp/MemoryPack + /// It utilize MemoryPack, which means the object must be MemoryPackSerializable /// public class BinaryStorage { - const string DirectoryName = Constant.Cache; + public const string FileSuffix = ".cache"; - const string FileSuffix = ".cache"; - - public BinaryStorage(string filename) + // Let the derived class to set the file path + public BinaryStorage(string filename, string directoryPath = null) { - var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName); + directoryPath ??= DataLocation.CacheDirectory; Helper.ValidateDirectory(directoryPath); FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}"); @@ -58,14 +51,14 @@ namespace Flow.Launcher.Infrastructure.Storage } } - private async ValueTask DeserializeAsync(Stream stream, T defaultData) + private static async ValueTask DeserializeAsync(Stream stream, T defaultData) { try { var t = await MemoryPackSerializer.DeserializeAsync(stream); return t; } - catch (System.Exception e) + catch (System.Exception) { // Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e); return defaultData; diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index ed2179760..53812ef15 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -28,6 +28,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public static string VersionLogDirectory => Path.Combine(LogDirectory, Constant.Version); public static string LogDirectory => Path.Combine(DataDirectory(), Constant.Logs); + public static readonly string CacheDirectory = Path.Combine(DataDirectory(), Constant.Cache); public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); From a0c2a42e17d79f464d0a64f0b70e75b42cb8a214 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:15:26 +0800 Subject: [PATCH 016/158] Let Program plugin use plugin cache path --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 39 +++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index b3763aaa6..6e1cdffb5 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; +using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.Program.Programs; using Flow.Launcher.Plugin.Program.Views; using Flow.Launcher.Plugin.Program.Views.Models; @@ -188,9 +191,41 @@ namespace Flow.Launcher.Plugin.Program await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () => { - _win32Storage = new BinaryStorage("Win32"); + Helper.ValidateDirectory(Context.CurrentPluginMetadata.PluginCacheDirectoryPath); + + static bool MoveFile(string sourcePath, string destinationPath) + { + if (!File.Exists(sourcePath)) + { + return false; + } + + if (File.Exists(destinationPath)) + { + File.Delete(sourcePath); + return false; + } + + var destinationDirectory = Path.GetDirectoryName(destinationPath); + if (!Directory.Exists(destinationDirectory) && (!string.IsNullOrEmpty(destinationDirectory))) + { + Directory.CreateDirectory(destinationDirectory); + } + File.Move(sourcePath, destinationPath); + return true; + } + + // Move old cache files to the new cache directory + var oldWin32CacheFile = Path.Combine(DataLocation.CacheDirectory, $"Win32.cache"); + var newWin32CacheFile = Path.Combine(Context.CurrentPluginMetadata.PluginCacheDirectoryPath, $"Win32.cache"); + MoveFile(oldWin32CacheFile, newWin32CacheFile); + var oldUWPCacheFile = Path.Combine(DataLocation.CacheDirectory, $"UWP.cache"); + var newUWPCacheFile = Path.Combine(Context.CurrentPluginMetadata.PluginCacheDirectoryPath, $"UWP.cache"); + MoveFile(oldUWPCacheFile, newUWPCacheFile); + + _win32Storage = new BinaryStorage("Win32", Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _win32s = await _win32Storage.TryLoadAsync(Array.Empty()); - _uwpStorage = new BinaryStorage("UWP"); + _uwpStorage = new BinaryStorage("UWP", Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _uwps = await _uwpStorage.TryLoadAsync(Array.Empty()); }); Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); From a29ed64f3c526b09a060ec18a0f53963afed3b7b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:27:04 +0800 Subject: [PATCH 017/158] Use metadata for plugin settings directory --- Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index ce53c7da5..7ad9715bb 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Infrastructure; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.WebSearch @@ -183,9 +182,8 @@ namespace Flow.Launcher.Plugin.WebSearch DefaultImagesDirectory = Path.Combine(pluginDirectory, Images); Helper.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory); - // Custom images directory is in the WebSearch's data location folder - var name = Path.GetFileNameWithoutExtension(_context.CurrentPluginMetadata.ExecuteFileName); - CustomImagesDirectory = Path.Combine(DataLocation.PluginSettingsDirectory, name, "CustomIcons"); + // Custom images directory is in the WebSearch's data location folder + CustomImagesDirectory = Path.Combine(_context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "CustomIcons"); }; } From 65ae342bcaabf79ab3d010e2b260e40de57f5f8d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:37:13 +0800 Subject: [PATCH 018/158] Add documents for Flow.Launcher.Plugin --- Flow.Launcher.Plugin/ActionContext.cs | 3 ++ .../Interfaces/IResultUpdated.cs | 27 +++++++++++- .../Interfaces/ISettingProvider.cs | 7 ++++ Flow.Launcher.Plugin/PluginInitContext.cs | 8 ++++ Flow.Launcher.Plugin/PluginPair.cs | 28 +++++++++++-- Flow.Launcher.Plugin/Query.cs | 7 ++-- Flow.Launcher.Plugin/Result.cs | 2 - .../SharedCommands/SearchWeb.cs | 5 ++- .../SharedCommands/ShellCommand.cs | 23 ++++++++++ .../SharedModels/MatchResult.cs | 42 +++++++++++++++++++ 10 files changed, 141 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Plugin/ActionContext.cs b/Flow.Launcher.Plugin/ActionContext.cs index e31c8e31d..9e05bbd06 100644 --- a/Flow.Launcher.Plugin/ActionContext.cs +++ b/Flow.Launcher.Plugin/ActionContext.cs @@ -51,6 +51,9 @@ namespace Flow.Launcher.Plugin (WinPressed ? ModifierKeys.Windows : ModifierKeys.None); } + /// + /// Default object with all keys not pressed. + /// public static readonly SpecialKeyState Default = new () { CtrlPressed = false, ShiftPressed = false, diff --git a/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs b/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs index fd21460ac..aa4e4a56d 100644 --- a/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs +++ b/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs @@ -4,17 +4,42 @@ using System.Threading; namespace Flow.Launcher.Plugin { + /// + /// Interface for plugins that want to manually update their results + /// public interface IResultUpdated : IFeatures { + /// + /// Event that is triggered when the results are updated + /// event ResultUpdatedEventHandler ResultsUpdated; } + /// + /// Delegate for the ResultsUpdated event + /// + /// + /// public delegate void ResultUpdatedEventHandler(IResultUpdated sender, ResultUpdatedEventArgs e); + /// + /// Event arguments for the ResultsUpdated event + /// public class ResultUpdatedEventArgs : EventArgs { + /// + /// List of results that should be displayed + /// public List Results; + + /// + /// Query that triggered the update + /// public Query Query; + + /// + /// Token that can be used to cancel the update + /// public CancellationToken Token { get; init; } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs b/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs index d5ffba20b..f034243c3 100644 --- a/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs @@ -2,8 +2,15 @@ namespace Flow.Launcher.Plugin { + /// + /// This interface is used to create settings panel for .Net plugins + /// public interface ISettingProvider { + /// + /// Create settings panel control for .Net plugins + /// + /// Control CreateSettingPanel(); } } diff --git a/Flow.Launcher.Plugin/PluginInitContext.cs b/Flow.Launcher.Plugin/PluginInitContext.cs index f040752bd..a42e3930c 100644 --- a/Flow.Launcher.Plugin/PluginInitContext.cs +++ b/Flow.Launcher.Plugin/PluginInitContext.cs @@ -5,10 +5,18 @@ /// public class PluginInitContext { + /// + /// Default constructor. + /// public PluginInitContext() { } + /// + /// Constructor. + /// + /// + /// public PluginInitContext(PluginMetadata currentPluginMetadata, IPublicAPI api) { CurrentPluginMetadata = currentPluginMetadata; diff --git a/Flow.Launcher.Plugin/PluginPair.cs b/Flow.Launcher.Plugin/PluginPair.cs index 7bf634691..037af7427 100644 --- a/Flow.Launcher.Plugin/PluginPair.cs +++ b/Flow.Launcher.Plugin/PluginPair.cs @@ -1,21 +1,37 @@ namespace Flow.Launcher.Plugin { + /// + /// Plugin instance and plugin metadata + /// public class PluginPair { + /// + /// Plugin instance + /// public IAsyncPlugin Plugin { get; internal set; } + + /// + /// Plugin metadata + /// public PluginMetadata Metadata { get; internal set; } - - + /// + /// Convert to string + /// + /// public override string ToString() { return Metadata.Name; } + /// + /// Compare by plugin metadata ID + /// + /// + /// public override bool Equals(object obj) { - PluginPair r = obj as PluginPair; - if (r != null) + if (obj is PluginPair r) { return string.Equals(r.Metadata.ID, Metadata.ID); } @@ -25,6 +41,10 @@ } } + /// + /// Get hash coode + /// + /// public override int GetHashCode() { var hashcode = Metadata.ID?.GetHashCode() ?? 0; diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index e182491c2..a9694e263 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -2,10 +2,11 @@ namespace Flow.Launcher.Plugin { + /// + /// Represents a query that is sent to a plugin. + /// public class Query { - public Query() { } - /// /// Raw query, this includes action keyword if it has /// We didn't recommend use this property directly. You should always use Search property. @@ -55,13 +56,13 @@ namespace Flow.Launcher.Plugin /// public string ActionKeyword { get; init; } - [JsonIgnore] /// /// Splits by spaces and returns the first item. /// /// /// returns an empty string when does not have enough items. /// + [JsonIgnore] public string FirstSearch => SplitSearch(0); [JsonIgnore] diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 9b16cc1cb..910485438 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -13,7 +12,6 @@ namespace Flow.Launcher.Plugin /// public class Result { - private string _pluginDirectory; private string _icoPath; diff --git a/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs b/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs index a7744ffac..752c85933 100644 --- a/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs +++ b/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs @@ -6,6 +6,9 @@ using System.Linq; namespace Flow.Launcher.Plugin.SharedCommands { + /// + /// Contains methods to open a search in a new browser window or tab. + /// public static class SearchWeb { private static string GetDefaultBrowserPath() @@ -106,4 +109,4 @@ namespace Flow.Launcher.Plugin.SharedCommands } } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs index a0440e30d..288222d4f 100644 --- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs +++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs @@ -8,12 +8,26 @@ using Windows.Win32.Foundation; namespace Flow.Launcher.Plugin.SharedCommands { + /// + /// Contains methods for running shell commands + /// public static class ShellCommand { + /// + /// Delegate for EnumThreadWindows + /// + /// + /// + /// public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam); private static bool containsSecurityWindow; + /// + /// Runs a windows command using the provided ProcessStartInfo + /// + /// + /// public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo) { processStartInfo.Verb = "RunAsUser"; @@ -65,6 +79,15 @@ namespace Flow.Launcher.Plugin.SharedCommands return buffer[..length].ToString(); } + /// + /// Runs a windows command using the provided ProcessStartInfo + /// + /// + /// + /// + /// + /// + /// public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "", bool createNoWindow = false) { diff --git a/Flow.Launcher.Plugin/SharedModels/MatchResult.cs b/Flow.Launcher.Plugin/SharedModels/MatchResult.cs index 5144eb61d..36677d4bb 100644 --- a/Flow.Launcher.Plugin/SharedModels/MatchResult.cs +++ b/Flow.Launcher.Plugin/SharedModels/MatchResult.cs @@ -2,14 +2,29 @@ namespace Flow.Launcher.Plugin.SharedModels { + /// + /// Represents the result of a match operation. + /// public class MatchResult { + /// + /// Initializes a new instance of the class. + /// + /// + /// public MatchResult(bool success, SearchPrecisionScore searchPrecision) { Success = success; SearchPrecision = searchPrecision; } + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// public MatchResult(bool success, SearchPrecisionScore searchPrecision, List matchData, int rawScore) { Success = success; @@ -18,6 +33,9 @@ namespace Flow.Launcher.Plugin.SharedModels RawScore = rawScore; } + /// + /// Whether the match operation was successful. + /// public bool Success { get; set; } /// @@ -30,6 +48,9 @@ namespace Flow.Launcher.Plugin.SharedModels /// private int _rawScore; + /// + /// The raw calculated search score without any search precision filtering applied. + /// public int RawScore { get { return _rawScore; } @@ -45,8 +66,15 @@ namespace Flow.Launcher.Plugin.SharedModels /// public List MatchData { get; set; } + /// + /// The search precision score used to filter the search results. + /// public SearchPrecisionScore SearchPrecision { get; set; } + /// + /// Determines if the search precision score is met. + /// + /// public bool IsSearchPrecisionScoreMet() { return IsSearchPrecisionScoreMet(_rawScore); @@ -63,10 +91,24 @@ namespace Flow.Launcher.Plugin.SharedModels } } + /// + /// Represents the search precision score used to filter search results. + /// public enum SearchPrecisionScore { + /// + /// The highest search precision score. + /// Regular = 50, + + /// + /// The medium search precision score. + /// Low = 20, + + /// + /// The lowest search precision score. + /// None = 0 } } From 66228151aeb437339aca65769d3facaeb8819776 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 16:08:06 +0800 Subject: [PATCH 019/158] Fix typos --- Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 4 ++-- .../SettingPages/ViewModels/SettingsPaneAboutViewModel.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 53812ef15..5b948e450 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -29,11 +29,11 @@ namespace Flow.Launcher.Infrastructure.UserSettings public static string LogDirectory => Path.Combine(DataDirectory(), Constant.Logs); public static readonly string CacheDirectory = Path.Combine(DataDirectory(), Constant.Cache); - public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); + public static readonly string SettingsDirectory = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); - public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectorty, Constant.Plugins); + public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectory, Constant.Plugins); public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); public const string PythonEnvironmentName = "Python"; diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index ee684e7ca..adbafe31e 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -77,13 +77,13 @@ public partial class SettingsPaneAboutViewModel : BaseModel [RelayCommand] private void OpenSettingsFolder() { - PluginManager.API.OpenDirectory(DataLocation.SettingsDirectorty); + PluginManager.API.OpenDirectory(DataLocation.SettingsDirectory); } [RelayCommand] private void OpenParentOfSettingsFolder(object parameter) { - string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectorty); + string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectory); string parentFolderPath = Path.GetDirectoryName(settingsFolderPath); PluginManager.API.OpenDirectory(parentFolderPath); } From fe86e23dead49655301f1608d1e7789b26b66e61 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 16:11:49 +0800 Subject: [PATCH 020/158] Add exception handles --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 34 ++++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 6e1cdffb5..3be23214c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -193,26 +193,46 @@ namespace Flow.Launcher.Plugin.Program { Helper.ValidateDirectory(Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - static bool MoveFile(string sourcePath, string destinationPath) + static void MoveFile(string sourcePath, string destinationPath) { if (!File.Exists(sourcePath)) { - return false; + return; } if (File.Exists(destinationPath)) { - File.Delete(sourcePath); - return false; + try + { + File.Delete(sourcePath); + } + catch (Exception) + { + // Ignore, we will handle next time we start the plugin + } + return; } var destinationDirectory = Path.GetDirectoryName(destinationPath); if (!Directory.Exists(destinationDirectory) && (!string.IsNullOrEmpty(destinationDirectory))) { - Directory.CreateDirectory(destinationDirectory); + try + { + Directory.CreateDirectory(destinationDirectory); + } + catch (Exception) + { + // Ignore, we will handle next time we start the plugin + } + } + try + { + File.Move(sourcePath, destinationPath); + } + catch (Exception) + { + // Ignore, we will handle next time we start the plugin } - File.Move(sourcePath, destinationPath); - return true; } // Move old cache files to the new cache directory From f8d0981898c3fec208da6a8a1bc6e76183035e62 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 25 Feb 2025 10:22:39 +0800 Subject: [PATCH 021/158] Update json rpc plugin directory before loading plugins --- Flow.Launcher.Core/Plugin/PluginManager.cs | 24 +++++++++++++++------- Flow.Launcher.Plugin/PluginMetadata.cs | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index bbd189efb..c8fe7e818 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -151,11 +151,26 @@ namespace Flow.Launcher.Core.Plugin _metadatas = PluginConfig.Parse(Directories); Settings = settings; Settings.UpdatePluginSettings(_metadatas); + // Update Json RPC plugin directory before loading plugins so that we can pass the correct plugin directory + UpdateJsonRPCPluginDirectory(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); - UpdatePluginDirectory(_metadatas); + // Update dotnet plugin directory after loading plugins because we need to get assembly name first + UpdateNotNetPluginDirectory(_metadatas); } - private static void UpdatePluginDirectory(List metadatas) + private static void UpdateJsonRPCPluginDirectory(List metadatas) + { + foreach (var metadata in metadatas) + { + if (!AllowedLanguage.IsDotNet(metadata.Language)) + { + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + } + } + } + + private static void UpdateNotNetPluginDirectory(List metadatas) { foreach (var metadata in metadatas) { @@ -164,11 +179,6 @@ namespace Flow.Launcher.Core.Plugin metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName); metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName); } - else - { - metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); - metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); - } } } diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index dae8f58fd..259716ec1 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -9,8 +9,6 @@ namespace Flow.Launcher.Plugin /// public class PluginMetadata : BaseModel { - private string _pluginDirectory; - /// /// Plugin ID. /// @@ -69,6 +67,8 @@ namespace Flow.Launcher.Plugin [JsonIgnore] public string AssemblyName { get; internal set; } + private string _pluginDirectory; + /// /// Plugin source directory. /// From ce3a3e912aac6bfb74e684aa4320cd193272f2bd Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Mar 2025 19:43:16 +0800 Subject: [PATCH 022/158] Fix plugin settings delete issue --- Flow.Launcher.Core/Plugin/PluginManager.cs | 37 ++++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index ce3f1ac6d..4d8bf76b7 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -72,15 +72,20 @@ namespace Flow.Launcher.Core.Plugin { foreach (var pluginPair in AllPlugins) { - switch (pluginPair.Plugin) - { - case IDisposable disposable: - disposable.Dispose(); - break; - case IAsyncDisposable asyncDisposable: - await asyncDisposable.DisposeAsync(); - break; - } + await DisposePluginAsync(pluginPair); + } + } + + private static async Task DisposePluginAsync(PluginPair pluginPair) + { + switch (pluginPair.Plugin) + { + case IDisposable disposable: + disposable.Dispose(); + break; + case IAsyncDisposable asyncDisposable: + await asyncDisposable.DisposeAsync(); + break; } } @@ -565,13 +570,25 @@ namespace Flow.Launcher.Core.Plugin } } - internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) + internal static async void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { throw new ArgumentException($"Plugin {plugin.Name} has been modified"); } + if (removePluginFromSettings) + { + // If we want to remove plugin from AllPlugins, + // we need to dispose them so that they can release file handles + // which can help FL to delete the plugin settings & cache folders successfully + var pluginPairs = AllPlugins.FindAll(p => p.Metadata.ID == plugin.ID); + foreach (var pluginPair in pluginPairs) + { + await DisposePluginAsync(pluginPair); + } + } + if (removePluginSettings) { // For dotnet plugins, we need to remove their PluginJsonStorage instance From 486cc6ac4985bbdd7360332b718a77e4f015278a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Mar 2025 20:15:49 +0800 Subject: [PATCH 023/158] Fix async task issue --- Flow.Launcher.Core/Plugin/PluginManager.cs | 10 +++++----- .../PluginsManager.cs | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 4d8bf76b7..456f0a699 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -469,10 +469,10 @@ namespace Flow.Launcher.Core.Plugin /// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url, /// unless it's a local path installation /// - public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) + public static async Task UpdatePluginAsync(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) { InstallPlugin(newVersion, zipFilePath, checkModified:false); - UninstallPlugin(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); + await UninstallPluginAsync(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); _modifiedPlugins.Add(existingVersion.ID); } @@ -487,9 +487,9 @@ namespace Flow.Launcher.Core.Plugin /// /// Uninstall a plugin. /// - public static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) + public static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) { - UninstallPlugin(plugin, removePluginFromSettings, removePluginSettings, true); + await UninstallPluginAsync(plugin, removePluginFromSettings, removePluginSettings, true); } #endregion @@ -570,7 +570,7 @@ namespace Flow.Launcher.Core.Plugin } } - internal static async void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) + internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index f4c8a66da..07bbfdaa0 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -341,7 +341,7 @@ namespace Flow.Launcher.Plugin.PluginsManager } else { - PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin, + await PluginManager.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlugin, downloadToFilePath); if (Settings.AutoRestartAfterChanging) @@ -433,7 +433,7 @@ namespace Flow.Launcher.Plugin.PluginsManager if (cts.IsCancellationRequested) return; else - PluginManager.UpdatePlugin(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, + await PluginManager.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath); } catch (Exception ex) @@ -681,7 +681,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Title = $"{x.Metadata.Name} by {x.Metadata.Author}", SubTitle = x.Metadata.Description, IcoPath = x.Metadata.IcoPath, - Action = e => + AsyncAction = async e => { string message; if (Settings.AutoRestartAfterChanging) @@ -704,7 +704,7 @@ namespace Flow.Launcher.Plugin.PluginsManager MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Context.API.HideMainWindow(); - Uninstall(x.Metadata); + await UninstallAsync(x.Metadata); if (Settings.AutoRestartAfterChanging) { Context.API.RestartApp(); @@ -729,7 +729,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return Search(results, search); } - private void Uninstall(PluginMetadata plugin) + private async Task UninstallAsync(PluginMetadata plugin) { try { @@ -737,7 +737,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_subtitle"), Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_title"), button: MessageBoxButton.YesNo) == MessageBoxResult.No; - PluginManager.UninstallPlugin(plugin, removePluginFromSettings: true, removePluginSettings: removePluginSettings); + await PluginManager.UninstallPluginAsync(plugin, removePluginFromSettings: true, removePluginSettings: removePluginSettings); } catch (ArgumentException e) { From af3b3916764f355568ac6117b123287ef255d54f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Mar 2025 20:20:30 +0800 Subject: [PATCH 024/158] Fix dispose --- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 456f0a699..76d83cbcf 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -577,7 +577,7 @@ namespace Flow.Launcher.Core.Plugin throw new ArgumentException($"Plugin {plugin.Name} has been modified"); } - if (removePluginFromSettings) + if (removePluginSettings || removePluginFromSettings) { // If we want to remove plugin from AllPlugins, // we need to dispose them so that they can release file handles From b1a46817f01439beab05e1fc59d7ff19fe80d2bd Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 15 Mar 2025 21:58:55 +0800 Subject: [PATCH 025/158] Support search delay --- .../UserSettings/Settings.cs | 3 + Flow.Launcher/Flow.Launcher.csproj | 1 + Flow.Launcher/MainWindow.xaml.cs | 174 +++++++++++++----- Flow.Launcher/ViewModel/MainViewModel.cs | 3 +- 4 files changed, 130 insertions(+), 51 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 93f6db111..66da9f59a 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -275,6 +275,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool LeaveCmdOpen { get; set; } public bool HideWhenDeactivated { get; set; } = true; + public bool SearchQueryResultsWithDelay { get; set; } = false; + public int SearchInputDelay { get; set; } = 150; + [JsonConverter(typeof(JsonStringEnumConverter))] public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor; diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 1e305d3d9..d4508ad67 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -104,6 +104,7 @@ + diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 3616e4c59..2f1b2ce66 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -5,7 +5,6 @@ using System.Windows; using System.Windows.Input; using System.Windows.Media.Animation; using System.Windows.Controls; -using System.Windows.Forms; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; @@ -27,6 +26,7 @@ using DataObject = System.Windows.DataObject; using System.Windows.Media; using System.Windows.Interop; using Windows.Win32; +using System.Reactive.Linq; namespace Flow.Launcher { @@ -68,6 +68,7 @@ namespace Flow.Launcher var handle = new WindowInteropHelper(this).Handle; var win = HwndSource.FromHwnd(handle); win.AddHook(WndProc); + SetupSearchTextBoxReactiveness(_settings.SearchQueryResultsWithDelay); }; } @@ -204,63 +205,63 @@ namespace Flow.Launcher switch (e.PropertyName) { case nameof(MainViewModel.MainWindowVisibilityStatus): - { - Dispatcher.Invoke(() => { - if (_viewModel.MainWindowVisibilityStatus) + Dispatcher.Invoke(() => { - if (_settings.UseSound) + if (_viewModel.MainWindowVisibilityStatus) { - SoundPlay(); - } + if (_settings.UseSound) + { + SoundPlay(); + } - UpdatePosition(); - PreviewReset(); - Activate(); - QueryTextBox.Focus(); - _settings.ActivateTimes++; - if (!_viewModel.LastQuerySelected) + UpdatePosition(); + PreviewReset(); + Activate(); + QueryTextBox.Focus(); + _settings.ActivateTimes++; + if (!_viewModel.LastQuerySelected) + { + QueryTextBox.SelectAll(); + _viewModel.LastQuerySelected = true; + } + + if (_viewModel.ProgressBarVisibility == Visibility.Visible && + isProgressBarStoryboardPaused) + { + _progressBarStoryboard.Begin(ProgressBar, true); + isProgressBarStoryboardPaused = false; + } + + if (_settings.UseAnimation) + WindowAnimator(); + } + else if (!isProgressBarStoryboardPaused) { - QueryTextBox.SelectAll(); - _viewModel.LastQuerySelected = true; + _progressBarStoryboard.Stop(ProgressBar); + isProgressBarStoryboardPaused = true; } - - if (_viewModel.ProgressBarVisibility == Visibility.Visible && - isProgressBarStoryboardPaused) + }); + break; + } + case nameof(MainViewModel.ProgressBarVisibility): + { + Dispatcher.Invoke(() => + { + if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) + { + _progressBarStoryboard.Stop(ProgressBar); + isProgressBarStoryboardPaused = true; + } + else if (_viewModel.MainWindowVisibilityStatus && + isProgressBarStoryboardPaused) { _progressBarStoryboard.Begin(ProgressBar, true); isProgressBarStoryboardPaused = false; } - - if (_settings.UseAnimation) - WindowAnimator(); - } - else if (!isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Stop(ProgressBar); - isProgressBarStoryboardPaused = true; - } - }); - break; - } - case nameof(MainViewModel.ProgressBarVisibility): - { - Dispatcher.Invoke(() => - { - if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Stop(ProgressBar); - isProgressBarStoryboardPaused = true; - } - else if (_viewModel.MainWindowVisibilityStatus && - isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Begin(ProgressBar, true); - isProgressBarStoryboardPaused = false; - } - }); - break; - } + }); + break; + } case nameof(MainViewModel.QueryTextCursorMovedToEnd): if (_viewModel.QueryTextCursorMovedToEnd) { @@ -415,10 +416,10 @@ namespace Flow.Launcher { switch (e.Button) { - case MouseButtons.Left: + case System.Windows.Forms.MouseButtons.Left: _viewModel.ToggleFlowLauncher(); break; - case MouseButtons.Right: + case System.Windows.Forms.MouseButtons.Right: contextMenu.IsOpen = true; // Get context menu handle and bring it to the foreground @@ -857,5 +858,78 @@ namespace Flow.Launcher be.UpdateSource(); } } + + #region Search Delay + + // Edited from: https://github.com/microsoft/PowerToys + + private IDisposable _reactiveSubscription; + + private void SetupSearchTextBoxReactiveness(bool showResultsWithDelay) + { + if (_reactiveSubscription != null) + { + _reactiveSubscription.Dispose(); + _reactiveSubscription = null; + } + + QueryTextBox.TextChanged -= QueryTextBox_TextChanged; + + if (showResultsWithDelay) + { + _reactiveSubscription = Observable.FromEventPattern( + conversion => (sender, eventArg) => conversion(sender, eventArg), + add => QueryTextBox.TextChanged += add, + remove => QueryTextBox.TextChanged -= remove) + .Do(@event => ClearAutoCompleteText((TextBox)@event.Sender)) + .Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay)) + .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery((TextBox)@event.Sender))) + .Subscribe(); + } + else + { + QueryTextBox.TextChanged += QueryTextBox_TextChanged; + } + } + + private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e) + { + var textBox = (TextBox)sender; + ClearAutoCompleteText(textBox); + PerformSearchQuery(textBox); + } + + private void ClearAutoCompleteText(TextBox textBox) + { + var text = textBox.Text; + var autoCompleteText = QueryTextSuggestionBox.Text; + + if (ShouldAutoCompleteTextBeEmpty(text, autoCompleteText)) + { + QueryTextSuggestionBox.Text = string.Empty; + } + } + + private static bool ShouldAutoCompleteTextBeEmpty(string queryText, string autoCompleteText) + { + if (string.IsNullOrEmpty(autoCompleteText)) + { + return false; + } + else + { + // Using Ordinal this is internal + return string.IsNullOrEmpty(queryText) || !autoCompleteText.StartsWith(queryText, StringComparison.Ordinal); + } + } + + private void PerformSearchQuery(TextBox textBox) + { + var text = textBox.Text; + _viewModel.QueryText = text; + _viewModel.Query(); + } + + #endregion } } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6b0144a03..373ecdece 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -569,7 +569,6 @@ namespace Flow.Launcher.ViewModel { _queryText = value; OnPropertyChanged(); - Query(); } } @@ -631,6 +630,7 @@ namespace Flow.Launcher.ViewModel { // re-query is done in QueryText's setter method QueryText = queryText; + Query(); // set to false so the subsequent set true triggers // PropertyChanged and MoveQueryTextToEnd is called QueryTextCursorMovedToEnd = false; @@ -695,6 +695,7 @@ namespace Flow.Launcher.ViewModel else { QueryText = string.Empty; + Query(); } } From 3abcebd02b6a0321c963fdd53e6067d30a10c153 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 15 Mar 2025 22:35:51 +0800 Subject: [PATCH 026/158] Add settings ui --- .../UserSettings/Settings.cs | 2 +- Flow.Launcher/Languages/en.xaml | 4 ++++ Flow.Launcher/MainWindow.xaml.cs | 2 +- .../SettingsPaneGeneralViewModel.cs | 16 ++++++++++++++ .../Views/SettingsPaneGeneral.xaml | 22 +++++++++++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 66da9f59a..97ee49ef2 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -276,7 +276,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool HideWhenDeactivated { get; set; } = true; public bool SearchQueryResultsWithDelay { get; set; } = false; - public int SearchInputDelay { get; set; } = 150; + public int SearchInputDelay { get; set; } = 120; [JsonConverter(typeof(JsonStringEnumConverter))] public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor; diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index a3f87cd30..454dffd24 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -94,6 +94,10 @@ Flow Launcher search window is hidden in the tray after starting up. Hide tray icon When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. + Search Delay + Delay for a while to search when typing. This reduces interface jumpiness and result load. + Search Delay Time + Delay time which search results appear when typing is stopped. Default is 120ms. Query Search Precision Changes minimum match score required for results. None diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 2f1b2ce66..9e7b380ed 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -865,7 +865,7 @@ namespace Flow.Launcher private IDisposable _reactiveSubscription; - private void SetupSearchTextBoxReactiveness(bool showResultsWithDelay) + public void SetupSearchTextBoxReactiveness(bool showResultsWithDelay) { if (_reactiveSubscription != null) { diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index de4f158ad..72bdb609b 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -139,6 +139,22 @@ public partial class SettingsPaneGeneralViewModel : BaseModel } } + public bool SearchQueryResultsWithDelay + { + get => Settings.SearchQueryResultsWithDelay; + set + { + Settings.SearchQueryResultsWithDelay = value; + + ((MainWindow)System.Windows.Application.Current.MainWindow).SetupSearchTextBoxReactiveness(value); + } + } + + public IEnumerable SearchInputDelayRange => new List() + { + 30, 60, 90, 120, 150, 180, 210, 240, 270, 300 + }; + public List LastQueryModes { get; } = DropdownDataGeneric.GetValues("LastQuery"); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index a80e618e8..986e822e2 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -172,6 +172,28 @@ OnContent="{DynamicResource enable}" /> + + + + + + + + + + Date: Sat, 15 Mar 2025 22:44:42 +0800 Subject: [PATCH 027/158] Improve strings --- 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 454dffd24..2426d90af 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -97,7 +97,7 @@ Search Delay Delay for a while to search when typing. This reduces interface jumpiness and result load. Search Delay Time - Delay time which search results appear when typing is stopped. Default is 120ms. + Delay time after which search results appear when typing is stopped. Default is 120ms. Query Search Precision Changes minimum match score required for results. None From dc3f663947287a9cee926b23991bccc3dc9cf6f1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 16 Mar 2025 10:25:22 +0800 Subject: [PATCH 028/158] Use property changed to change --- Flow.Launcher.Infrastructure/UserSettings/Settings.cs | 11 ++++++++++- Flow.Launcher/MainWindow.xaml.cs | 5 ++++- .../ViewModels/SettingsPaneGeneralViewModel.cs | 11 ----------- .../SettingPages/Views/SettingsPaneGeneral.xaml | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 97ee49ef2..df49630c5 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -275,7 +275,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool LeaveCmdOpen { get; set; } public bool HideWhenDeactivated { get; set; } = true; - public bool SearchQueryResultsWithDelay { get; set; } = false; + bool _searchQueryResultsWithDelay { get; set; } + public bool SearchQueryResultsWithDelay + { + get => _searchQueryResultsWithDelay; + set + { + _searchQueryResultsWithDelay = value; + OnPropertyChanged(); + } + } public int SearchInputDelay { get; set; } = 120; [JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 9e7b380ed..a7dc8c1d1 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -297,6 +297,9 @@ namespace Flow.Launcher case nameof(Settings.WindowTop): Top = _settings.WindowTop; break; + case nameof(Settings.SearchQueryResultsWithDelay): + SetupSearchTextBoxReactiveness(_settings.SearchQueryResultsWithDelay); + break; } }; } @@ -865,7 +868,7 @@ namespace Flow.Launcher private IDisposable _reactiveSubscription; - public void SetupSearchTextBoxReactiveness(bool showResultsWithDelay) + private void SetupSearchTextBoxReactiveness(bool showResultsWithDelay) { if (_reactiveSubscription != null) { diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 72bdb609b..ced565bc2 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -139,17 +139,6 @@ public partial class SettingsPaneGeneralViewModel : BaseModel } } - public bool SearchQueryResultsWithDelay - { - get => Settings.SearchQueryResultsWithDelay; - set - { - Settings.SearchQueryResultsWithDelay = value; - - ((MainWindow)System.Windows.Application.Current.MainWindow).SetupSearchTextBoxReactiveness(value); - } - } - public IEnumerable SearchInputDelayRange => new List() { 30, 60, 90, 120, 150, 180, 210, 240, 270, 300 diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index 986e822e2..ffb58d094 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -178,7 +178,7 @@ Icon="" Sub="{DynamicResource searchDelayToolTip}"> From e98b1441a4f52b59f116210db706f52205d085ec Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 16 Mar 2025 10:33:06 +0800 Subject: [PATCH 029/158] Improve code quality --- Flow.Launcher/MainWindow.xaml.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index a7dc8c1d1..06e19e051 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -68,7 +68,6 @@ namespace Flow.Launcher var handle = new WindowInteropHelper(this).Handle; var win = HwndSource.FromHwnd(handle); win.AddHook(WndProc); - SetupSearchTextBoxReactiveness(_settings.SearchQueryResultsWithDelay); }; } @@ -196,6 +195,8 @@ namespace Flow.Launcher InitializePosition(); InitializePosition(); PreviewReset(); + // Setup search text box reactiveness + SetupSearchTextBoxReactiveness(_settings.SearchQueryResultsWithDelay); // since the default main window visibility is visible // so we need set focus during startup QueryTextBox.Focus(); From b0b1a2661ad2efb23080d6ed438f0aca5bae2030 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 16 Mar 2025 20:26:06 +0800 Subject: [PATCH 030/158] Fix build issue & Cleanup codes --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 4a75ce3fb..e629f887e 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Windows; @@ -435,7 +434,7 @@ namespace Flow.Launcher.Plugin.Sys AutoCompleteText = DataLocation.VersionLogDirectory, Action = c => { - _context.API.OpenDirectory(logPath); + _context.API.OpenDirectory(DataLocation.VersionLogDirectory); return true; } }, @@ -459,7 +458,7 @@ namespace Flow.Launcher.Plugin.Sys AutoCompleteText = DataLocation.DataDirectory(), Action = c => { - _context.API.OpenDirectory(userDataPath); + _context.API.OpenDirectory(DataLocation.DataDirectory()); return true; } }, From c114f2d8b56b8b8c34015eab8dbcabedcd915273 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 07:43:34 +0800 Subject: [PATCH 031/158] Fix build issue --- Flow.Launcher/MainWindow.xaml.cs | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index b9aeaf380..ce0f6ba6e 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -219,28 +219,12 @@ namespace Flow.Launcher _viewModel.LastQuerySelected = true; } - if (_viewModel.ProgressBarVisibility == Visibility.Visible && - isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Begin(ProgressBar, true); - isProgressBarStoryboardPaused = false; - } - if (_settings.UseAnimation) WindowAnimator(); } - else if (!isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Stop(ProgressBar); - isProgressBarStoryboardPaused = true; - } - - if (_settings.UseAnimation) - WindowAnimator(); - } - }); - break; - } + }); + break; + } case nameof(MainViewModel.QueryTextCursorMovedToEnd): if (_viewModel.QueryTextCursorMovedToEnd) { From 8cdb87271ad31e398a5c7bddce406b433c90a1cc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 08:01:21 +0800 Subject: [PATCH 032/158] Remove auto complete text clear because FL uses binding --- Flow.Launcher/MainWindow.xaml.cs | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index ce0f6ba6e..e901f4c17 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -880,8 +880,7 @@ namespace Flow.Launcher conversion => (sender, eventArg) => conversion(sender, eventArg), add => QueryTextBox.TextChanged += add, remove => QueryTextBox.TextChanged -= remove) - .Do(@event => ClearAutoCompleteText((TextBox)@event.Sender)) - .Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay)) + .Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay * 10)) .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery((TextBox)@event.Sender))) .Subscribe(); } @@ -894,34 +893,9 @@ namespace Flow.Launcher private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e) { var textBox = (TextBox)sender; - ClearAutoCompleteText(textBox); PerformSearchQuery(textBox); } - private void ClearAutoCompleteText(TextBox textBox) - { - var text = textBox.Text; - var autoCompleteText = QueryTextSuggestionBox.Text; - - if (ShouldAutoCompleteTextBeEmpty(text, autoCompleteText)) - { - QueryTextSuggestionBox.Text = string.Empty; - } - } - - private static bool ShouldAutoCompleteTextBeEmpty(string queryText, string autoCompleteText) - { - if (string.IsNullOrEmpty(autoCompleteText)) - { - return false; - } - else - { - // Using Ordinal this is internal - return string.IsNullOrEmpty(queryText) || !autoCompleteText.StartsWith(queryText, StringComparison.Ordinal); - } - } - private void PerformSearchQuery(TextBox textBox) { var text = textBox.Text; From e98964a01eba96e58f87328b94b2853515fafe80 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 08:01:36 +0800 Subject: [PATCH 033/158] Change to one way binding mode --- Flow.Launcher/MainWindow.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 0720501ca..df681470a 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -243,7 +243,7 @@ PreviewDragOver="OnPreviewDragOver" PreviewKeyUp="QueryTextBox_KeyUp" Style="{DynamicResource QueryBoxStyle}" - Text="{Binding QueryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + Text="{Binding QueryText, Mode=OneWay}" Visibility="Visible" WindowChrome.IsHitTestVisibleInChrome="True"> @@ -377,7 +377,7 @@ Style="{DynamicResource SeparatorStyle}" /> - + From fc4b5c9e6c75661638eef35aa9f922428ad34f68 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 09:06:30 +0800 Subject: [PATCH 034/158] Fix --- Flow.Launcher/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index e901f4c17..d41165c81 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -880,7 +880,7 @@ namespace Flow.Launcher conversion => (sender, eventArg) => conversion(sender, eventArg), add => QueryTextBox.TextChanged += add, remove => QueryTextBox.TextChanged -= remove) - .Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay * 10)) + .Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay)) .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery((TextBox)@event.Sender))) .Subscribe(); } From 5dd9e8d963e8e941b401980010f30c0e9c8af9c6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 09:11:15 +0800 Subject: [PATCH 035/158] Add plugin search delay settings panel --- .../UserSettings/PluginSettings.cs | 7 ++- .../UserSettings/Settings.cs | 12 +++++ Flow.Launcher.Plugin/PluginMetadata.cs | 2 + Flow.Launcher/Languages/en.xaml | 1 + .../Controls/InstalledPluginDisplay.xaml | 2 + .../Controls/InstalledPluginSearchDelay.xaml | 46 +++++++++++++++++++ .../InstalledPluginSearchDelay.xaml.cs | 11 +++++ .../SettingsPaneGeneralViewModel.cs | 5 +- Flow.Launcher/ViewModel/PluginViewModel.cs | 21 ++++++++- 9 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml create mode 100644 Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml.cs diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index 98f4dccda..0ebbdb318 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -51,6 +51,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings } metadata.Disabled = settings.Disabled; metadata.Priority = settings.Priority; + metadata.SearchDelayTime = settings.SearchDelayTime; } else { @@ -59,9 +60,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings ID = metadata.ID, Name = metadata.Name, Version = metadata.Version, - ActionKeywords = metadata.ActionKeywords, + ActionKeywords = metadata.ActionKeywords, Disabled = metadata.Disabled, - Priority = metadata.Priority + Priority = metadata.Priority, + SearchDelayTime = metadata.SearchDelayTime, }; } } @@ -74,6 +76,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public string Version { get; set; } public List ActionKeywords { get; set; } // a reference of the action keywords from plugin manager public int Priority { get; set; } + public int SearchDelayTime { get; set; } /// /// Used only to save the state of the plugin in settings diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index df49630c5..82e4468d4 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -287,6 +287,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings } public int SearchInputDelay { get; set; } = 120; + [JsonIgnore] + public List SearchInputDelayRange { get; } = new() + { + 30, 60, 90, 120, 150, 180, 210, 240, 270, 300 + }; + + [JsonIgnore] + public List PluginSearchInputDelayRange { get; } = new() + { + 0, 30, 60, 90, 120, 150 + }; + [JsonConverter(typeof(JsonStringEnumConverter))] public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor; diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index b4e06913e..3cb025c77 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -34,6 +34,8 @@ namespace Flow.Launcher.Plugin public List ActionKeywords { get; set; } + public int SearchDelayTime { get; set; } + public string IcoPath { get; set;} public override string ToString() diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 2426d90af..83f0df08d 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -124,6 +124,7 @@ Current action keyword New action keyword Change Action Keywords + Change Seach Delay Time Current Priority New Priority Priority diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml index ed3c29690..e27a15784 100644 --- a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml +++ b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml @@ -95,6 +95,8 @@ + + + + + +  + + + + + + diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml.cs b/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml.cs new file mode 100644 index 000000000..ad9284074 --- /dev/null +++ b/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml.cs @@ -0,0 +1,11 @@ +using System.Windows.Controls; + +namespace Flow.Launcher.Resources.Controls; + +public partial class InstalledPluginSearchDelay : UserControl +{ + public InstalledPluginSearchDelay() + { + InitializeComponent(); + } +} diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index ced565bc2..6e97543db 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -139,10 +139,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel } } - public IEnumerable SearchInputDelayRange => new List() - { - 30, 60, 90, 120, 150, 180, 210, 240, 270, 300 - }; + public IEnumerable SearchInputDelayRange => Settings.SearchInputDelayRange; public List LastQueryModes { get; } = DropdownDataGeneric.GetValues("LastQuery"); diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 209a81395..230a76e7a 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -8,6 +8,9 @@ using System.Windows.Controls; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Resource; using Flow.Launcher.Resources.Controls; +using System.Collections.Generic; +using CommunityToolkit.Mvvm.DependencyInjection; +using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher.ViewModel { @@ -81,6 +84,19 @@ namespace Flow.Launcher.ViewModel } } + public IEnumerable PluginSearchInputDelayRange { get; } = + Ioc.Default.GetRequiredService().PluginSearchInputDelayRange; + + public int PluginSearchDelayTime + { + get => PluginPair.Metadata.SearchDelayTime; + set + { + PluginPair.Metadata.SearchDelayTime = value; + PluginSettingsObject.SearchDelayTime = value; + } + } + private Control _settingControl; private bool _isExpanded; @@ -88,7 +104,10 @@ namespace Flow.Launcher.ViewModel public Control BottomPart1 => IsExpanded ? _bottomPart1 ??= new InstalledPluginDisplayKeyword() : null; private Control _bottomPart2; - public Control BottomPart2 => IsExpanded ? _bottomPart2 ??= new InstalledPluginDisplayBottomData() : null; + public Control BottomPart2 => IsExpanded ? _bottomPart2 ??= new InstalledPluginSearchDelay() : null; + + private Control _bottomPart3; + public Control BottomPart3 => IsExpanded ? _bottomPart3 ??= new InstalledPluginDisplayBottomData() : null; public bool HasSettingControl => PluginPair.Plugin is ISettingProvider && (PluginPair.Plugin is not JsonRPCPluginBase jsonRPCPluginBase || jsonRPCPluginBase.NeedCreateSettingPanel()); public Control SettingControl From 2c949d6c92774a4e5030b6fdaaeb2132173b82dd Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 09:42:29 +0800 Subject: [PATCH 036/158] Fix setting control --- Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml index e27a15784..66a3ad62e 100644 --- a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml +++ b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml @@ -117,7 +117,7 @@ Content="{Binding SettingControl}" /> - + From 933582b620a6b84c9c6357d3542a36adabe5aaee Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 11:12:58 +0800 Subject: [PATCH 037/158] Change name to search delay & Support plugin search delay --- .../UserSettings/PluginSettings.cs | 6 +- .../UserSettings/Settings.cs | 9 ++- Flow.Launcher.Plugin/PluginMetadata.cs | 2 +- Flow.Launcher/MainWindow.xaml.cs | 29 ++++++-- .../Controls/InstalledPluginSearchDelay.xaml | 4 +- .../SettingsPaneGeneralViewModel.cs | 2 +- .../Views/SettingsPaneGeneral.xaml | 4 +- Flow.Launcher/ViewModel/MainViewModel.cs | 70 +++++++++++++++---- Flow.Launcher/ViewModel/PluginViewModel.cs | 12 ++-- 9 files changed, 98 insertions(+), 40 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index 0ebbdb318..7e9e22063 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -51,7 +51,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings } metadata.Disabled = settings.Disabled; metadata.Priority = settings.Priority; - metadata.SearchDelayTime = settings.SearchDelayTime; + metadata.SearchDelay = settings.SearchDelay; } else { @@ -63,7 +63,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings ActionKeywords = metadata.ActionKeywords, Disabled = metadata.Disabled, Priority = metadata.Priority, - SearchDelayTime = metadata.SearchDelayTime, + SearchDelay = metadata.SearchDelay, }; } } @@ -76,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public string Version { get; set; } public List ActionKeywords { get; set; } // a reference of the action keywords from plugin manager public int Priority { get; set; } - public int SearchDelayTime { get; set; } + public int SearchDelay { get; set; } /// /// Used only to save the state of the plugin in settings diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 82e4468d4..3de2bdb61 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -285,16 +285,19 @@ namespace Flow.Launcher.Infrastructure.UserSettings OnPropertyChanged(); } } - public int SearchInputDelay { get; set; } = 120; + public int SearchDelay { get; set; } = 120; + + // TODO: Remove debug codes. + public const int SearchDelayInterval = 30 * 60; [JsonIgnore] - public List SearchInputDelayRange { get; } = new() + public List SearchDelayRange { get; } = new() { 30, 60, 90, 120, 150, 180, 210, 240, 270, 300 }; [JsonIgnore] - public List PluginSearchInputDelayRange { get; } = new() + public List PluginSearchDelayRange { get; } = new() { 0, 30, 60, 90, 120, 150 }; diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 3cb025c77..5300c2550 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -34,7 +34,7 @@ namespace Flow.Launcher.Plugin public List ActionKeywords { get; set; } - public int SearchDelayTime { get; set; } + public int SearchDelay { get; set; } public string IcoPath { get; set;} diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index d41165c81..a615bfa2f 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -880,8 +880,18 @@ namespace Flow.Launcher conversion => (sender, eventArg) => conversion(sender, eventArg), add => QueryTextBox.TextChanged += add, remove => QueryTextBox.TextChanged -= remove) - .Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay)) - .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery((TextBox)@event.Sender))) + .Throttle(TimeSpan.FromMilliseconds(_settings.SearchDelay * 10)) + .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery(0, (TextBox)@event.Sender))) + .Throttle(TimeSpan.FromMilliseconds(Settings.SearchDelayInterval)) + .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery(30, (TextBox)@event.Sender))) + .Throttle(TimeSpan.FromMilliseconds(Settings.SearchDelayInterval)) + .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery(60, (TextBox)@event.Sender))) + .Throttle(TimeSpan.FromMilliseconds(Settings.SearchDelayInterval)) + .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery(90, (TextBox)@event.Sender))) + .Throttle(TimeSpan.FromMilliseconds(Settings.SearchDelayInterval)) + .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery(120, (TextBox)@event.Sender))) + .Throttle(TimeSpan.FromMilliseconds(Settings.SearchDelayInterval)) + .Do(@event => Dispatcher.Invoke(() => PerformSearchQuery(150, (TextBox)@event.Sender))) .Subscribe(); } else @@ -893,14 +903,19 @@ namespace Flow.Launcher private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e) { var textBox = (TextBox)sender; - PerformSearchQuery(textBox); + PerformSearchQuery(null, textBox); } - private void PerformSearchQuery(TextBox textBox) + // If delayInputTime is null, we will query plugins with all plugin search delay times + private void PerformSearchQuery(int? searchDelay, TextBox textBox) { - var text = textBox.Text; - _viewModel.QueryText = text; - _viewModel.Query(); + // Only update query text when search delay is null or 0 + if (searchDelay.GetValueOrDefault(0) == 0) + { + var text = textBox.Text; + _viewModel.QueryText = text; + } + _viewModel.Query(searchDelay); } #endregion diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml b/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml index 8d69c08d9..80fd7525b 100644 --- a/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml +++ b/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml @@ -38,8 +38,8 @@ Cursor="Hand" DockPanel.Dock="Right" FontWeight="Bold" - ItemsSource="{Binding PluginSearchInputDelayRange}" - SelectedItem="{Binding PluginSearchDelayTime}" + ItemsSource="{Binding PluginSearchDelayRange}" + SelectedItem="{Binding PluginSearchDelay}" ToolTip="{DynamicResource pluginSearchDelayTooltip}" /> diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 6e97543db..d8fe8bb50 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -139,7 +139,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel } } - public IEnumerable SearchInputDelayRange => Settings.SearchInputDelayRange; + public IEnumerable SearchDelayRange => Settings.SearchDelayRange; public List LastQueryModes { get; } = DropdownDataGeneric.GetValues("LastQuery"); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml index ffb58d094..bde74c653 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml @@ -189,8 +189,8 @@ Sub="{DynamicResource searchDelayTimeToolTip}"> + ItemsSource="{Binding SearchDelayRange}" + SelectedItem="{Binding Settings.SearchDelay}" /> diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 373ecdece..c5658516a 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -274,14 +274,14 @@ namespace Flow.Launcher.ViewModel { if (SelectedIsFromQueryResults()) { - QueryResults(isReQuery: true); + QueryResults(null, isReQuery: true); } } public void ReQuery(bool reselect) { BackToQueryResults(); - QueryResults(isReQuery: true, reSelect: reselect); + QueryResults(null, isReQuery: true, reSelect: reselect); } [RelayCommand] @@ -630,14 +630,14 @@ namespace Flow.Launcher.ViewModel { // re-query is done in QueryText's setter method QueryText = queryText; - Query(); + Query(null); // set to false so the subsequent set true triggers // PropertyChanged and MoveQueryTextToEnd is called QueryTextCursorMovedToEnd = false; } else if (isReQuery) { - Query(isReQuery: true); + Query(null, isReQuery: true); } QueryTextCursorMovedToEnd = true; @@ -690,12 +690,12 @@ namespace Flow.Launcher.ViewModel // http://stackoverflow.com/posts/25895769/revisions if (string.IsNullOrEmpty(QueryText)) { - Query(); + Query(null); } else { QueryText = string.Empty; - Query(); + Query(null); } } @@ -979,19 +979,27 @@ namespace Flow.Launcher.ViewModel #region Query - public void Query(bool isReQuery = false) + public void Query(int? searchDelay, bool isReQuery = false) { if (SelectedIsFromQueryResults()) { - QueryResults(isReQuery); + QueryResults(searchDelay, isReQuery); } else if (ContextMenuSelected()) { - QueryContextMenu(); + // Only query history when search delay is null or 0 + if (searchDelay.GetValueOrDefault(0) == 0) + { + QueryContextMenu(); + } } else if (HistorySelected()) { - QueryHistory(); + // Only query history when search delay is null or 0 + if (searchDelay.GetValueOrDefault(0) == 0) + { + QueryHistory(); + } } } @@ -1081,7 +1089,7 @@ namespace Flow.Launcher.ViewModel private readonly IReadOnlyList _emptyResult = new List(); - private async void QueryResults(bool isReQuery = false, bool reSelect = true) + private async void QueryResults(int? searchDelay, bool isReQuery = false, bool reSelect = true) { _updateSource?.Cancel(); @@ -1157,12 +1165,44 @@ namespace Flow.Launcher.ViewModel // plugins is ICollection, meaning LINQ will get the Count and preallocate Array - var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch + Task[] tasks; + if (searchDelay.HasValue) { - false => QueryTask(plugin, reSelect), - true => Task.CompletedTask - }).ToArray(); + var searchDelayValue = searchDelay.Value; + tasks = plugins.Select(plugin => (plugin.Metadata.Disabled || plugin.Metadata.SearchDelay != searchDelayValue) switch + { + false => QueryTask(plugin, reSelect), + true => Task.CompletedTask + }).ToArray(); + // TODO: Remove debug codes. + System.Diagnostics.Debug.WriteLine($"Querying {searchDelayValue}ms"); + foreach (var plugin in plugins) + { + if (!(plugin.Metadata.Disabled || plugin.Metadata.SearchDelay != searchDelayValue)) + { + System.Diagnostics.Debug.WriteLine($"Querying {plugin.Metadata.Name}"); + } + } + } + else + { + tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch + { + false => QueryTask(plugin, reSelect), + true => Task.CompletedTask + }).ToArray(); + + // TODO: Remove debug codes. + System.Diagnostics.Debug.WriteLine($"Querying null ms"); + foreach (var plugin in plugins) + { + if (!plugin.Metadata.Disabled) + { + System.Diagnostics.Debug.WriteLine($"Querying {plugin.Metadata.Name}"); + } + } + } try { diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 230a76e7a..e44443ac0 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -84,16 +84,16 @@ namespace Flow.Launcher.ViewModel } } - public IEnumerable PluginSearchInputDelayRange { get; } = - Ioc.Default.GetRequiredService().PluginSearchInputDelayRange; + public IEnumerable PluginSearchDelayRange { get; } = + Ioc.Default.GetRequiredService().PluginSearchDelayRange; - public int PluginSearchDelayTime + public int PluginSearchDelay { - get => PluginPair.Metadata.SearchDelayTime; + get => PluginPair.Metadata.SearchDelay; set { - PluginPair.Metadata.SearchDelayTime = value; - PluginSettingsObject.SearchDelayTime = value; + PluginPair.Metadata.SearchDelay = value; + PluginSettingsObject.SearchDelay = value; } } From 2738636a4cc43c95c8b73739961d813b92317ea7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 18 Mar 2025 12:35:14 +0800 Subject: [PATCH 038/158] Improve debug codes --- Flow.Launcher/ViewModel/MainViewModel.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index c5658516a..5a627b269 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1091,6 +1091,8 @@ namespace Flow.Launcher.ViewModel private async void QueryResults(int? searchDelay, bool isReQuery = false, bool reSelect = true) { + System.Diagnostics.Debug.WriteLine("!!!QueryResults"); + _updateSource?.Cancel(); var query = ConstructQuery(QueryText, Settings.CustomShortcuts, Settings.BuiltinShortcuts); @@ -1176,14 +1178,15 @@ namespace Flow.Launcher.ViewModel }).ToArray(); // TODO: Remove debug codes. - System.Diagnostics.Debug.WriteLine($"Querying {searchDelayValue}ms"); + System.Diagnostics.Debug.Write($"!!!{query.RawQuery} Querying {searchDelayValue}ms"); foreach (var plugin in plugins) { if (!(plugin.Metadata.Disabled || plugin.Metadata.SearchDelay != searchDelayValue)) { - System.Diagnostics.Debug.WriteLine($"Querying {plugin.Metadata.Name}"); + System.Diagnostics.Debug.Write($"{plugin.Metadata.Name}"); } } + System.Diagnostics.Debug.Write("\n"); } else { @@ -1194,14 +1197,15 @@ namespace Flow.Launcher.ViewModel }).ToArray(); // TODO: Remove debug codes. - System.Diagnostics.Debug.WriteLine($"Querying null ms"); + System.Diagnostics.Debug.Write($"!!!{query.RawQuery} Querying null ms"); foreach (var plugin in plugins) { if (!plugin.Metadata.Disabled) { - System.Diagnostics.Debug.WriteLine($"Querying {plugin.Metadata.Name}"); + System.Diagnostics.Debug.Write($"{plugin.Metadata.Name}"); } } + System.Diagnostics.Debug.Write("\n"); } try From dc92f6a2719d0bf2557c999425694f62e062a72b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 21 Mar 2025 07:57:13 +0800 Subject: [PATCH 039/158] Fix build issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 9f2ccc679..91be72483 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1147,7 +1147,7 @@ namespace Flow.Launcher.ViewModel var searchDelayValue = searchDelay.Value; tasks = plugins.Select(plugin => (plugin.Metadata.Disabled || plugin.Metadata.SearchDelay != searchDelayValue) switch { - false => QueryTask(plugin, reSelect), + false => QueryTaskAsync(plugin, reSelect), true => Task.CompletedTask }).ToArray(); @@ -1166,7 +1166,7 @@ namespace Flow.Launcher.ViewModel { tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch { - false => QueryTask(plugin, reSelect), + false => QueryTaskAsync(plugin, reSelect), true => Task.CompletedTask }).ToArray(); From 102636f3574b9acbbfc4de3034e2b4a9587785ee Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 21 Mar 2025 07:57:27 +0800 Subject: [PATCH 040/158] Improve code quality --- Flow.Launcher/MainWindow.xaml.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 82e7046cf..43274e496 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.Linq; using System.Media; +using System.Reactive.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -10,7 +11,6 @@ using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Animation; -using System.Windows.Controls; using System.Windows.Shapes; using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; @@ -22,14 +22,8 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.ViewModel; using ModernWpf.Controls; -using Key = System.Windows.Input.Key; -using System.Media; using DataObject = System.Windows.DataObject; -using System.Windows.Media; -using System.Windows.Interop; -using Windows.Win32; -using System.Reactive.Linq; -using System.Windows.Shapes; +using Key = System.Windows.Input.Key; using MouseButtons = System.Windows.Forms.MouseButtons; using NotifyIcon = System.Windows.Forms.NotifyIcon; using Screen = System.Windows.Forms.Screen; @@ -539,10 +533,10 @@ namespace Flow.Launcher { switch (e.Button) { - case System.Windows.Forms.MouseButtons.Left: + case MouseButtons.Left: _viewModel.ToggleFlowLauncher(); break; - case System.Windows.Forms.MouseButtons.Right: + case MouseButtons.Right: contextMenu.IsOpen = true; // Get context menu handle and bring it to the foreground From 8c387d0d94b7db090013ceef679c6a0e518e3383 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 21 Mar 2025 07:58:00 +0800 Subject: [PATCH 041/158] Improve code quality & Add oneway bind mode --- .../Controls/InstalledPluginDisplay.xaml | 41 ++++++++++--------- Flow.Launcher/ViewModel/PluginViewModel.cs | 36 +++++++++------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml index 66a3ad62e..b19c668e0 100644 --- a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml +++ b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml @@ -1,14 +1,16 @@ - + + Source="{Binding Image, Mode=OneWay, IsAsync=True}" /> - + - + @@ -98,9 +101,9 @@ + BorderThickness="0 1 0 0"> + - #49443c + #36363d - + @@ -149,7 +149,7 @@ x:Key="ClockPanel" BasedOn="{StaticResource ClockPanel}" TargetType="{x:Type StackPanel}"> - + - - + TargetType="{x:Type Window}" /> + + - #545454 + #2e436e + TargetType="{x:Type ScrollBar}" /> + - + + + 8 + 10 0 10 0 + 0 0 0 10 + - - \ No newline at end of file + From 3f0c142d3d7c9f672ede65d346496fd7f2342a20 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 11:56:44 +0800 Subject: [PATCH 056/158] Fix hide window startup issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2bfc9587c..192f8b481 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -638,8 +638,13 @@ namespace Flow.Launcher.ViewModel /// private async Task ChangeQueryTextAsync(string queryText, bool isReQuery = false) { - await Application.Current.Dispatcher.InvokeAsync(async () => + // Must check access so that we will not block the UI thread which cause window visibility issue + if (!Application.Current.Dispatcher.CheckAccess()) { + await Application.Current.Dispatcher.InvokeAsync(() => ChangeQueryText(queryText, isReQuery)); + return; + } + BackToQueryResults(); if (QueryText != queryText) @@ -656,7 +661,6 @@ namespace Flow.Launcher.ViewModel } QueryTextCursorMovedToEnd = true; - }); } public bool LastQuerySelected { get; set; } From 7d39c5259604cb7e83088f46b6959e2644db62cc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 12:00:23 +0800 Subject: [PATCH 057/158] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 62 ++++++++++++---- Flow.Launcher/ViewModel/MainViewModel.cs | 91 ++++++------------------ 2 files changed, 70 insertions(+), 83 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 25d9c5d2d..89d4f0847 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -161,24 +161,67 @@ namespace Flow.Launcher { if (_viewModel.MainWindowVisibilityStatus) { + // Set clock and search icon opacity + var opacity = _settings.UseAnimation ? 0.0 : 1.0; + ClockPanel.Opacity = opacity; + SearchIcon.Opacity = opacity; + + // Set clock and search icon visibility + ClockPanel.Visibility = string.IsNullOrEmpty(_viewModel.QueryText) ? Visibility.Visible : Visibility.Collapsed; + if (_viewModel.PluginIconSource != null) + { + SearchIcon.Opacity = 0.0; + } + else + { + _viewModel.SearchIconVisibility = Visibility.Visible; + } + + // Play sound effect before activing the window if (_settings.UseSound) { SoundPlay(); } + // Update position & Activate UpdatePosition(); - _viewModel.ResetPreview(); Activate(); - QueryTextBox.Focus(); - _settings.ActivateTimes++; + + // Reset preview + _viewModel.ResetPreview(); + + // Select last query if need if (!_viewModel.LastQuerySelected) { QueryTextBox.SelectAll(); _viewModel.LastQuerySelected = true; } + // Focus query box + QueryTextBox.Focus(); + + // Play window animation if (_settings.UseAnimation) + { WindowAnimation(); + } + + _settings.ActivateTimes++; + } + else + { + // Set clock and search icon opacity + var opacity = _settings.UseAnimation ? 0.0 : 1.0; + ClockPanel.Opacity = opacity; + SearchIcon.Opacity = opacity; + + // Set clock and search icon visibility + ClockPanel.Visibility = Visibility.Hidden; + _viewModel.SearchIconVisibility = Visibility.Hidden; + + // Force UI update + ClockPanel.UpdateLayout(); + SearchIcon.UpdateLayout(); } }); break; @@ -191,7 +234,6 @@ namespace Flow.Launcher Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); _viewModel.QueryTextCursorMovedToEnd = false; } - break; case nameof(MainViewModel.GameModeStatus): _notifyIcon.Icon = _viewModel.GameModeStatus @@ -280,8 +322,8 @@ namespace Flow.Launcher _settings.WindowLeft = Left; _settings.WindowTop = Top; - ClockPanel.Opacity = 0; - SearchIcon.Opacity = 0; + ClockPanel.Opacity = 0.0; + SearchIcon.Opacity = 0.0; // This condition stops extra hide call when animator is on, // which causes the toggling to occasional hide instead of show. @@ -291,7 +333,9 @@ namespace Flow.Launcher // This also stops the mainwindow from flickering occasionally after Settings window is opened // and always after Settings window is closed. if (_settings.UseAnimation) + { await Task.Delay(100); + } if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewVisible) { @@ -765,12 +809,6 @@ namespace Flow.Launcher { _isArrowKeyPressed = true; - UpdatePosition(); - - var opacity = _settings.UseAnimation ? 0.0 : 1.0; - ClockPanel.Opacity = opacity; - SearchIcon.Opacity = opacity; - var clocksb = new Storyboard(); var iconsb = new Storyboard(); var easing = new CircleEase { EasingMode = EasingMode.EaseInOut }; diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 192f8b481..9e63244f4 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; @@ -645,22 +644,22 @@ namespace Flow.Launcher.ViewModel return; } - BackToQueryResults(); + BackToQueryResults(); - if (QueryText != queryText) - { - // re-query is done in QueryText's setter method - QueryText = queryText; - // set to false so the subsequent set true triggers - // PropertyChanged and MoveQueryTextToEnd is called - QueryTextCursorMovedToEnd = false; - } - else if (isReQuery) - { - await QueryAsync(isReQuery: true); - } + if (QueryText != queryText) + { + // re-query is done in QueryText's setter method + QueryText = queryText; + // set to false so the subsequent set true triggers + // PropertyChanged and MoveQueryTextToEnd is called + QueryTextCursorMovedToEnd = false; + } + else if (isReQuery) + { + await QueryAsync(isReQuery: true); + } - QueryTextCursorMovedToEnd = true; + QueryTextCursorMovedToEnd = true; } public bool LastQuerySelected { get; set; } @@ -1448,43 +1447,10 @@ namespace Flow.Launcher.ViewModel #pragma warning disable VSTHRD100 // Avoid async void methods - public async void Show() + public void Show() { - await Application.Current.Dispatcher.InvokeAsync(() => - { - if (Application.Current.MainWindow is MainWindow mainWindow) - { - // 📌 Remove DWM Cloak (Make the window visible normally) - Win32Helper.DWMSetCloakForWindow(mainWindow, false); - - // Clock and SearchIcon hide when show situation - var opacity = Settings.UseAnimation ? 0.0 : 1.0; - mainWindow.ClockPanel.Opacity = opacity; - mainWindow.SearchIcon.Opacity = opacity; - - // QueryText sometimes is null when it is just initialized - if (QueryText != null && QueryText.Length != 0) - { - mainWindow.ClockPanel.Visibility = Visibility.Collapsed; - } - else - { - mainWindow.ClockPanel.Visibility = Visibility.Visible; - } - - if (PluginIconSource != null) - { - mainWindow.SearchIcon.Opacity = 0; - } - else - { - SearchIconVisibility = Visibility.Visible; - } - - // 📌 Restore UI elements - //mainWindow.SearchIcon.Visibility = Visibility.Visible; - } - }, DispatcherPriority.Render); + // 📌 Remove DWM Cloak (Make the window visible normally) + Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, false); // Update WPF properties MainWindowVisibility = Visibility.Visible; @@ -1500,6 +1466,9 @@ namespace Flow.Launcher.ViewModel public async void Hide() { + // 📌 Apply DWM Cloak (Completely hide the window) + Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, true); + lastHistoryIndex = 1; if (ExternalPreviewVisible) @@ -1534,26 +1503,6 @@ namespace Flow.Launcher.ViewModel break; } - await Application.Current.Dispatcher.InvokeAsync(() => - { - if (Application.Current.MainWindow is MainWindow mainWindow) - { - // 📌 Set Opacity of icon and clock to 0 and apply Visibility.Hidden - var opacity = Settings.UseAnimation ? 0.0 : 1.0; - mainWindow.ClockPanel.Opacity = opacity; - mainWindow.SearchIcon.Opacity = opacity; - mainWindow.ClockPanel.Visibility = Visibility.Hidden; - SearchIconVisibility = Visibility.Hidden; - - // Force UI update - mainWindow.ClockPanel.UpdateLayout(); - mainWindow.SearchIcon.UpdateLayout(); - - // 📌 Apply DWM Cloak (Completely hide the window) - Win32Helper.DWMSetCloakForWindow(mainWindow, true); - } - }); - if (StartWithEnglishMode) { Win32Helper.RestorePreviousKeyboardLayout(); From de5803482c3509f11d5c911c1e888ee40fdbd848 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 12:33:07 +0800 Subject: [PATCH 058/158] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 89d4f0847..a44b3a305 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -101,11 +101,16 @@ namespace Flow.Launcher // Check first launch if (_settings.FirstLaunch) { + // Set First Launch to false _settings.FirstLaunch = false; + + // Set Backdrop Type to Acrylic for Windows 11 when First Launch. Default is None + if (Win32Helper.IsBackdropSupported()) _settings.BackdropType = BackdropTypes.Acrylic; + + // Save settings App.API.SaveAppAllSettings(); - /* Set Backdrop Type to Acrylic for Windows 11 when First Launch. Default is None. */ - if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000)) - _settings.BackdropType = BackdropTypes.Acrylic; + + // Show Welcome Window var WelcomeWindow = new WelcomeWindow(); WelcomeWindow.Show(); } @@ -206,6 +211,7 @@ namespace Flow.Launcher WindowAnimation(); } + // Update activate times _settings.ActivateTimes++; } else From 589fefce3c307db30c0cf404ee0352f8a31f8f87 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 13:50:36 +0800 Subject: [PATCH 059/158] Adjust blank lines --- Flow.Launcher/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index a44b3a305..9eb1271ec 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -948,6 +948,7 @@ namespace Flow.Launcher ClockPanel.BeginAnimation(OpacityProperty, fadeOut); } + // ✅ 4. When showing ClockPanel (apply fade-in animation) else if (shouldShowClock && ClockPanel.Visibility != Visibility.Visible && !_isClockPanelAnimating) { @@ -971,7 +972,6 @@ namespace Flow.Launcher } } - private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0) { if (style == null) From 40d2d58406a62d2d66f1ec49c329cf2040b24306 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:31:46 +0800 Subject: [PATCH 060/158] Fix InvalidOperationException --- Flow.Launcher/MainWindow.xaml.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 9eb1271ec..7b3814c87 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -296,7 +296,8 @@ namespace Flow.Launcher Notification.Uninstall(); // After plugins are all disposed, we can close the main window _canClose = true; - Close(); + // Use this instead of Close() to avoid InvalidOperationException when calling Close() in OnClosing event + Application.Current.Shutdown(); } } From b519313c25f8b74f8577c89f1a15d907bde961ba Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:32:03 +0800 Subject: [PATCH 061/158] Adjust log debug location --- Flow.Launcher/App.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 833c63ddf..1270d16bf 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -138,13 +138,13 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { - Log.SetLogLevel(_settings.LogLevel); - Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); + Log.SetLogLevel(_settings.LogLevel); + RegisterAppDomainExceptions(); RegisterDispatcherUnhandledException(); From 8269304f4e48dc28bef83ef7a6d9568279bef214 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:45:52 +0800 Subject: [PATCH 062/158] Check disposed for result view update ending --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 9e63244f4..6bfde42c5 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -212,7 +212,8 @@ namespace Flow.Launcher.ViewModel queue.Clear(); } - Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); + if (!_disposed) + Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); } void continueAction(Task t) From 91e2366c499cebae354f0aa3d0b62dbc3e4ad900 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 14:49:28 +0800 Subject: [PATCH 063/158] Revert "Adjust log debug location" This reverts commit b519313c25f8b74f8577c89f1a15d907bde961ba. --- Flow.Launcher/App.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 1270d16bf..833c63ddf 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -138,13 +138,13 @@ namespace Flow.Launcher { await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => { + Log.SetLogLevel(_settings.LogLevel); + Ioc.Default.GetRequiredService().PreStartCleanUpAfterPortabilityUpdate(); Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); - Log.SetLogLevel(_settings.LogLevel); - RegisterAppDomainExceptions(); RegisterDispatcherUnhandledException(); From 8a08e03cf95173b8c6c84166190832e5199b4118 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 15:52:31 +0800 Subject: [PATCH 064/158] Bind to vm --- Flow.Launcher/MainWindow.xaml | 5 ++++- Flow.Launcher/ViewModel/MainViewModel.cs | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 087b2e998..34b376cc9 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -290,7 +290,9 @@ + Opacity="{Binding ClockPanelOpacity}" + Style="{DynamicResource ClockPanel}" + Visibility="{Binding ClockPanelVisibility}"> diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6bfde42c5..1631ff7c3 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -760,7 +760,10 @@ namespace Flow.Launcher.ViewModel public event VisibilityChangedEventHandler VisibilityChanged; + public Visibility ClockPanelVisibility { get; set; } public Visibility SearchIconVisibility { get; set; } + public double ClockPanelOpacity { get; set; } = 1; + public double SearchIconOpacity { get; set; } = 1; public double MainWindowWidth { From 693ba52fbc3e83775840e2f5591d88372b4dff4b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 16:13:02 +0800 Subject: [PATCH 065/158] Fix first-time window flicker & clock panel flicker issue --- Flow.Launcher/MainWindow.xaml.cs | 53 +++++------------------ Flow.Launcher/ViewModel/MainViewModel.cs | 55 +++++++++++++++++++++--- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 7b3814c87..86b65386e 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -166,22 +166,6 @@ namespace Flow.Launcher { if (_viewModel.MainWindowVisibilityStatus) { - // Set clock and search icon opacity - var opacity = _settings.UseAnimation ? 0.0 : 1.0; - ClockPanel.Opacity = opacity; - SearchIcon.Opacity = opacity; - - // Set clock and search icon visibility - ClockPanel.Visibility = string.IsNullOrEmpty(_viewModel.QueryText) ? Visibility.Visible : Visibility.Collapsed; - if (_viewModel.PluginIconSource != null) - { - SearchIcon.Opacity = 0.0; - } - else - { - _viewModel.SearchIconVisibility = Visibility.Visible; - } - // Play sound effect before activing the window if (_settings.UseSound) { @@ -214,21 +198,6 @@ namespace Flow.Launcher // Update activate times _settings.ActivateTimes++; } - else - { - // Set clock and search icon opacity - var opacity = _settings.UseAnimation ? 0.0 : 1.0; - ClockPanel.Opacity = opacity; - SearchIcon.Opacity = opacity; - - // Set clock and search icon visibility - ClockPanel.Visibility = Visibility.Hidden; - _viewModel.SearchIconVisibility = Visibility.Hidden; - - // Force UI update - ClockPanel.UpdateLayout(); - SearchIcon.UpdateLayout(); - } }); break; } @@ -329,8 +298,8 @@ namespace Flow.Launcher _settings.WindowLeft = Left; _settings.WindowTop = Top; - ClockPanel.Opacity = 0.0; - SearchIcon.Opacity = 0.0; + _viewModel.ClockPanelOpacity = 0.0; + _viewModel.SearchIconOpacity = 0.0; // This condition stops extra hide call when animator is on, // which causes the toggling to occasional hide instead of show. @@ -908,28 +877,28 @@ namespace Flow.Launcher var animationDuration = TimeSpan.FromMilliseconds(animationLength * 2 / 3); // ✅ Conditions for showing ClockPanel (No query input & ContextMenu, History are closed) - bool shouldShowClock = QueryTextBox.Text.Length == 0 && + var shouldShowClock = QueryTextBox.Text.Length == 0 && ContextMenu.Visibility != Visibility.Visible && History.Visibility != Visibility.Visible; // ✅ 1. When ContextMenu opens, immediately set Visibility.Hidden (force hide without animation) if (ContextMenu.Visibility == Visibility.Visible) { - ClockPanel.Visibility = Visibility.Hidden; - ClockPanel.Opacity = 0.0; // Set to 0 in case Opacity animation affects it + _viewModel.ClockPanelVisibility = Visibility.Hidden; + _viewModel.ClockPanelOpacity = 0.0; // Set to 0 in case Opacity animation affects it return; } // ✅ 2. When ContextMenu is closed, keep it Hidden if there's text in the query (remember previous state) if (ContextMenu.Visibility != Visibility.Visible && QueryTextBox.Text.Length > 0) { - ClockPanel.Visibility = Visibility.Hidden; - ClockPanel.Opacity = 0.0; + _viewModel.ClockPanelVisibility = Visibility.Hidden; + _viewModel.ClockPanelOpacity = 0.0; return; } // ✅ 3. When hiding ClockPanel (apply fade-out animation) - if ((!shouldShowClock) && ClockPanel.Visibility == Visibility.Visible && !_isClockPanelAnimating) + if ((!shouldShowClock) && _viewModel.ClockPanelVisibility == Visibility.Visible && !_isClockPanelAnimating) { _isClockPanelAnimating = true; @@ -943,7 +912,7 @@ namespace Flow.Launcher fadeOut.Completed += (s, e) => { - ClockPanel.Visibility = Visibility.Hidden; // ✅ Completely hide after animation + _viewModel.ClockPanelVisibility = Visibility.Hidden; // ✅ Completely hide after animation _isClockPanelAnimating = false; }; @@ -951,13 +920,13 @@ namespace Flow.Launcher } // ✅ 4. When showing ClockPanel (apply fade-in animation) - else if (shouldShowClock && ClockPanel.Visibility != Visibility.Visible && !_isClockPanelAnimating) + else if (shouldShowClock && _viewModel.ClockPanelVisibility != Visibility.Visible && !_isClockPanelAnimating) { _isClockPanelAnimating = true; Application.Current.Dispatcher.Invoke(() => { - ClockPanel.Visibility = Visibility.Visible; // ✅ Set Visibility to Visible first + _viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first var fadeIn = new DoubleAnimation { diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 1631ff7c3..18ee2cb5b 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; @@ -1453,8 +1454,30 @@ namespace Flow.Launcher.ViewModel public void Show() { - // 📌 Remove DWM Cloak (Make the window visible normally) - Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, false); + Application.Current.Dispatcher.Invoke(() => + { + if (Application.Current.MainWindow is MainWindow mainWindow) + { + // 📌 Remove DWM Cloak (Make the window visible normally) + Win32Helper.DWMSetCloakForWindow(mainWindow, false); + + // Set clock and search icon opacity + var opacity = Settings.UseAnimation ? 0.0 : 1.0; + ClockPanelOpacity = opacity; + SearchIconOpacity = opacity; + + // Set clock and search icon visibility + ClockPanelVisibility = string.IsNullOrEmpty(QueryText) ? Visibility.Visible : Visibility.Collapsed; + if (PluginIconSource != null) + { + SearchIconOpacity = 0.0; + } + else + { + SearchIconVisibility = Visibility.Visible; + } + } + }, DispatcherPriority.Render); // Update WPF properties MainWindowVisibility = Visibility.Visible; @@ -1470,9 +1493,6 @@ namespace Flow.Launcher.ViewModel public async void Hide() { - // 📌 Apply DWM Cloak (Completely hide the window) - Win32Helper.DWMSetCloakForWindow(Application.Current.MainWindow, true); - lastHistoryIndex = 1; if (ExternalPreviewVisible) @@ -1507,11 +1527,36 @@ namespace Flow.Launcher.ViewModel break; } + Application.Current.Dispatcher.Invoke(() => + { + if (Application.Current.MainWindow is MainWindow mainWindow) + { + // Set clock and search icon opacity + var opacity = Settings.UseAnimation ? 0.0 : 1.0; + ClockPanelOpacity = opacity; + SearchIconOpacity = opacity; + + // Set clock and search icon visibility + ClockPanelVisibility = Visibility.Hidden; + SearchIconVisibility = Visibility.Hidden; + + // Force UI update + mainWindow.ClockPanel.UpdateLayout(); + mainWindow.SearchIcon.UpdateLayout(); + + // 📌 Apply DWM Cloak (Completely hide the window) + Win32Helper.DWMSetCloakForWindow(mainWindow, true); + } + }, DispatcherPriority.Render); + if (StartWithEnglishMode) { Win32Helper.RestorePreviousKeyboardLayout(); } + // Delay for a while to make sure clock will not flicker + await Task.Delay(50); + // Update WPF properties //MainWindowOpacity = 0; MainWindowVisibilityStatus = false; From 82909cd9fa81e3f8855bfe8c0f5c1ddda390c756 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 21:39:25 +0800 Subject: [PATCH 066/158] Fix application null exception & Remove unused MainWindowOpacity --- Flow.Launcher/MainWindow.xaml | 1 - Flow.Launcher/ViewModel/MainViewModel.cs | 15 +++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 34b376cc9..533819d17 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -24,7 +24,6 @@ Left="{Binding Settings.WindowLeft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Loaded="OnLoaded" LocationChanged="OnLocationChanged" - Opacity="{Binding MainWindowOpacity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" PreviewKeyDown="OnKeyDown" PreviewKeyUp="OnKeyUp" PreviewMouseMove="OnPreviewMouseMove" diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 18ee2cb5b..c69fc4484 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -753,8 +753,7 @@ namespace Flow.Launcher.ViewModel public Visibility ProgressBarVisibility { get; set; } public Visibility MainWindowVisibility { get; set; } - public double MainWindowOpacity { get; set; } = 1; - + // This is to be used for determining the visibility status of the mainwindow instead of MainWindowVisibility // because it is more accurate and reliable representation than using Visibility as a condition check public bool MainWindowVisibilityStatus { get; set; } = true; @@ -1454,9 +1453,11 @@ namespace Flow.Launcher.ViewModel public void Show() { + // Invoke on UI thread Application.Current.Dispatcher.Invoke(() => { - if (Application.Current.MainWindow is MainWindow mainWindow) + // When application is exitting, the Application.Current will be null + if (Application.Current?.MainWindow is MainWindow mainWindow) { // 📌 Remove DWM Cloak (Make the window visible normally) Win32Helper.DWMSetCloakForWindow(mainWindow, false); @@ -1481,10 +1482,10 @@ namespace Flow.Launcher.ViewModel // Update WPF properties MainWindowVisibility = Visibility.Visible; - MainWindowOpacity = 1; MainWindowVisibilityStatus = true; VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = true }); + // Switch keyboard layout if (StartWithEnglishMode) { Win32Helper.SwitchToEnglishKeyboardLayout(true); @@ -1527,9 +1528,11 @@ namespace Flow.Launcher.ViewModel break; } + // Invoke on UI thread Application.Current.Dispatcher.Invoke(() => { - if (Application.Current.MainWindow is MainWindow mainWindow) + // When application is exitting, the Application.Current will be null + if (Application.Current?.MainWindow is MainWindow mainWindow) { // Set clock and search icon opacity var opacity = Settings.UseAnimation ? 0.0 : 1.0; @@ -1549,6 +1552,7 @@ namespace Flow.Launcher.ViewModel } }, DispatcherPriority.Render); + // Switch keyboard layout if (StartWithEnglishMode) { Win32Helper.RestorePreviousKeyboardLayout(); @@ -1558,7 +1562,6 @@ namespace Flow.Launcher.ViewModel await Task.Delay(50); // Update WPF properties - //MainWindowOpacity = 0; MainWindowVisibilityStatus = false; MainWindowVisibility = Visibility.Collapsed; VisibilityChanged?.Invoke(this, new VisibilityChangedEventArgs { IsVisible = false }); From 494e947ccc4134bcf6709ae570ee5c61a7d9fb83 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 21:42:40 +0800 Subject: [PATCH 067/158] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 47 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 86b65386e..d8c05971a 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -241,7 +241,7 @@ namespace Flow.Launcher }; // QueryTextBox.Text change detection (modified to only work when character count is 1 or higher) - QueryTextBox.TextChanged += (sender, e) => UpdateClockPanelVisibility(); + QueryTextBox.TextChanged += (s, e) => UpdateClockPanelVisibility(); // Detecting ContextMenu.Visibility changes DependencyPropertyDescriptor @@ -351,7 +351,6 @@ namespace Flow.Launcher _viewModel.LoadContextMenuCommand.Execute(null); e.Handled = true; } - break; case Key.Left: if (!_viewModel.QueryResultsSelected() && QueryTextBox.CaretIndex == 0) @@ -359,7 +358,6 @@ namespace Flow.Launcher _viewModel.EscCommand.Execute(null); e.Handled = true; } - break; case Key.Back: if (specialKeyState.CtrlPressed) @@ -378,7 +376,6 @@ namespace Flow.Launcher } } } - break; default: break; @@ -864,8 +861,11 @@ namespace Flow.Launcher private void UpdateClockPanelVisibility() { if (QueryTextBox == null || ContextMenu == null || History == null || ClockPanel == null) + { return; + } + // ✅ Initialize animation length & duration var animationLength = _settings.AnimationSpeed switch { AnimationSpeeds.Slow => 560, @@ -873,7 +873,6 @@ namespace Flow.Launcher AnimationSpeeds.Fast => 160, _ => _settings.CustomAnimationLength }; - var animationDuration = TimeSpan.FromMilliseconds(animationLength * 2 / 3); // ✅ Conditions for showing ClockPanel (No query input & ContextMenu, History are closed) @@ -890,15 +889,21 @@ namespace Flow.Launcher } // ✅ 2. When ContextMenu is closed, keep it Hidden if there's text in the query (remember previous state) - if (ContextMenu.Visibility != Visibility.Visible && QueryTextBox.Text.Length > 0) + else if (QueryTextBox.Text.Length > 0) { _viewModel.ClockPanelVisibility = Visibility.Hidden; _viewModel.ClockPanelOpacity = 0.0; return; } + // ✅ Prevent multiple animations + if (_isClockPanelAnimating) + { + return; + } + // ✅ 3. When hiding ClockPanel (apply fade-out animation) - if ((!shouldShowClock) && _viewModel.ClockPanelVisibility == Visibility.Visible && !_isClockPanelAnimating) + if ((!shouldShowClock) && _viewModel.ClockPanelVisibility == Visibility.Visible) { _isClockPanelAnimating = true; @@ -920,32 +925,32 @@ namespace Flow.Launcher } // ✅ 4. When showing ClockPanel (apply fade-in animation) - else if (shouldShowClock && _viewModel.ClockPanelVisibility != Visibility.Visible && !_isClockPanelAnimating) + else if (shouldShowClock && _viewModel.ClockPanelVisibility != Visibility.Visible) { _isClockPanelAnimating = true; - Application.Current.Dispatcher.Invoke(() => + _viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first + + var fadeIn = new DoubleAnimation { - _viewModel.ClockPanelVisibility = Visibility.Visible; // ✅ Set Visibility to Visible first + From = 0.0, + To = 1.0, + Duration = animationDuration, + FillBehavior = FillBehavior.HoldEnd + }; - var fadeIn = new DoubleAnimation - { - From = 0.0, - To = 1.0, - Duration = animationDuration, - FillBehavior = FillBehavior.HoldEnd - }; + fadeIn.Completed += (s, e) => _isClockPanelAnimating = false; - fadeIn.Completed += (s, e) => _isClockPanelAnimating = false; - ClockPanel.BeginAnimation(OpacityProperty, fadeIn); - }, DispatcherPriority.Render); + ClockPanel.BeginAnimation(OpacityProperty, fadeIn); } } private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0) { if (style == null) + { return defaultOpacity; + } foreach (Setter setter in style.Setters.Cast()) { @@ -961,7 +966,9 @@ namespace Flow.Launcher private static Thickness GetThicknessFromStyle(Style style, Thickness defaultThickness) { if (style == null) + { return defaultThickness; + } foreach (Setter setter in style.Setters.Cast()) { From c64e16d086366d712bb33f23485250c852934775 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 28 Mar 2025 21:46:23 +0800 Subject: [PATCH 068/158] Code quality --- Flow.Launcher/MainWindow.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index d8c05971a..0fd802f1e 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -111,8 +111,8 @@ namespace Flow.Launcher App.API.SaveAppAllSettings(); // Show Welcome Window - var WelcomeWindow = new WelcomeWindow(); - WelcomeWindow.Show(); + var welcomeWindow = new WelcomeWindow(); + welcomeWindow.Show(); } // Hide window if need From 57bc886079fc213978445e32929cbc0fba3b0203 Mon Sep 17 00:00:00 2001 From: DB p Date: Sat, 29 Mar 2025 00:45:46 +0900 Subject: [PATCH 069/158] - Add hotkey size baseon in blur themes - Add hotkey colors and selected style --- Flow.Launcher/Resources/Dark.xaml | 5 +++- Flow.Launcher/Resources/Light.xaml | 3 +++ Flow.Launcher/ResultListBox.xaml | 1 + Flow.Launcher/Themes/Base.xaml | 28 +++++++++++++++++----- Flow.Launcher/Themes/BlurBlack Darker.xaml | 12 ++++++---- Flow.Launcher/Themes/BlurBlack.xaml | 12 ++++++---- Flow.Launcher/Themes/BlurWhite.xaml | 12 ++++++---- Flow.Launcher/Themes/Discord Dark.xaml | 12 ++++++---- Flow.Launcher/Themes/League.xaml | 20 +++++++++------- Flow.Launcher/Themes/Nord Darker.xaml | 14 +++++++---- Flow.Launcher/Themes/Pink.xaml | 12 ++++++---- Flow.Launcher/Themes/Win11Light.xaml | 21 ++++++++++++---- 12 files changed, 108 insertions(+), 44 deletions(-) diff --git a/Flow.Launcher/Resources/Dark.xaml b/Flow.Launcher/Resources/Dark.xaml index f1ebba080..ec089b378 100644 --- a/Flow.Launcher/Resources/Dark.xaml +++ b/Flow.Launcher/Resources/Dark.xaml @@ -58,6 +58,9 @@ + + + @@ -115,7 +118,7 @@ - + diff --git a/Flow.Launcher/Resources/Light.xaml b/Flow.Launcher/Resources/Light.xaml index 12b35971d..aa6da9fb2 100644 --- a/Flow.Launcher/Resources/Light.xaml +++ b/Flow.Launcher/Resources/Light.xaml @@ -51,6 +51,9 @@ + + + diff --git a/Flow.Launcher/ResultListBox.xaml b/Flow.Launcher/ResultListBox.xaml index 67ba9e391..4c3bd1d12 100644 --- a/Flow.Launcher/ResultListBox.xaml +++ b/Flow.Launcher/ResultListBox.xaml @@ -215,6 +215,7 @@ + diff --git a/Flow.Launcher/Themes/Base.xaml b/Flow.Launcher/Themes/Base.xaml index b292657e6..35d1f9a41 100644 --- a/Flow.Launcher/Themes/Base.xaml +++ b/Flow.Launcher/Themes/Base.xaml @@ -351,23 +351,41 @@ - + + + @@ -494,7 +512,6 @@ x:Key="ItemHotkeyStyle" BasedOn="{StaticResource BaseItemHotkeyStyle}" TargetType="{x:Type TextBlock}"> - @@ -502,7 +519,6 @@ x:Key="ItemHotkeySelectedStyle" BasedOn="{StaticResource BaseItemHotkeySelectedStyle}" TargetType="{x:Type TextBlock}"> - diff --git a/Flow.Launcher/Themes/BlurBlack Darker.xaml b/Flow.Launcher/Themes/BlurBlack Darker.xaml index 2bef19373..143d036af 100644 --- a/Flow.Launcher/Themes/BlurBlack Darker.xaml +++ b/Flow.Launcher/Themes/BlurBlack Darker.xaml @@ -145,13 +145,17 @@ - - diff --git a/Flow.Launcher/Themes/BlurBlack.xaml b/Flow.Launcher/Themes/BlurBlack.xaml index c45827074..47e2b0720 100644 --- a/Flow.Launcher/Themes/BlurBlack.xaml +++ b/Flow.Launcher/Themes/BlurBlack.xaml @@ -142,13 +142,17 @@ - - diff --git a/Flow.Launcher/Themes/BlurWhite.xaml b/Flow.Launcher/Themes/BlurWhite.xaml index 8bf1f06e2..fdc3a9b45 100644 --- a/Flow.Launcher/Themes/BlurWhite.xaml +++ b/Flow.Launcher/Themes/BlurWhite.xaml @@ -148,13 +148,17 @@ - - diff --git a/Flow.Launcher/Themes/Discord Dark.xaml b/Flow.Launcher/Themes/Discord Dark.xaml index 46f2a91ec..59f7adb49 100644 --- a/Flow.Launcher/Themes/Discord Dark.xaml +++ b/Flow.Launcher/Themes/Discord Dark.xaml @@ -99,13 +99,17 @@ - diff --git a/Flow.Launcher/Themes/League.xaml b/Flow.Launcher/Themes/League.xaml index f1c8ba192..b59d577f7 100644 --- a/Flow.Launcher/Themes/League.xaml +++ b/Flow.Launcher/Themes/League.xaml @@ -11,7 +11,7 @@ TargetType="{x:Type TextBox}"> - + @@ -57,7 +57,7 @@ TargetType="{x:Type Rectangle}"> - + - - F1 M12000,12000z M0,0z M10354,10962C10326,10951 10279,10927 10249,10907 10216,10886 9476,10153 8370,9046 7366,8042 6541,7220 6536,7220 6532,7220 6498,7242 6461,7268 6213,7447 5883,7619 5592,7721 5194,7860 4802,7919 4360,7906 3612,7886 2953,7647 2340,7174 2131,7013 1832,6699 1664,6465 1394,6088 1188,5618 1097,5170 1044,4909 1030,4764 1030,4470 1030,4130 1056,3914 1135,3609 1263,3110 1511,2633 1850,2235 1936,2134 2162,1911 2260,1829 2781,1395 3422,1120 4090,1045 4271,1025 4667,1025 4848,1045 5505,1120 6100,1368 6630,1789 6774,1903 7081,2215 7186,2355 7362,2588 7467,2759 7579,2990 7802,3455 7911,3937 7911,4460 7911,4854 7861,5165 7737,5542 7684,5702 7675,5724 7602,5885 7517,6071 7390,6292 7270,6460 7242,6499 7220,6533 7220,6538 7220,6542 8046,7371 9055,8380 10441,9766 10898,10229 10924,10274 10945,10308 10966,10364 10976,10408 10990,10472 10991,10493 10980,10554 10952,10717 10840,10865 10690,10937 10621,10971 10607,10974 10510,10977 10425,10980 10395,10977 10354,10962z M4685,7050C5214,7001 5694,6809 6100,6484 6209,6396 6396,6209 6484,6100 7151,5267 7246,4110 6721,3190 6369,2571 5798,2137 5100,1956 4706,1855 4222,1855 3830,1957 3448,2056 3140,2210 2838,2453 2337,2855 2010,3427 1908,4080 1877,4274 1877,4656 1908,4850 1948,5105 2028,5370 2133,5590 2459,6272 3077,6782 3810,6973 3967,7014 4085,7034 4290,7053 4371,7061 4583,7059 4685,7050z @@ -141,7 +145,7 @@ x:Key="PreviewBorderStyle" BasedOn="{StaticResource BasePreviewBorderStyle}" TargetType="{x:Type Border}"> - + - - - - + + + - From b4c790e1ec9ee56bebd699a78ff9c12b14a3c338 Mon Sep 17 00:00:00 2001 From: DB p Date: Sat, 29 Mar 2025 01:17:32 +0900 Subject: [PATCH 071/158] Add margin for classics --- Flow.Launcher/Themes/BlurBlack Darker.xaml | 1 + Flow.Launcher/Themes/BlurBlack.xaml | 1 + Flow.Launcher/Themes/BlurWhite.xaml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/Flow.Launcher/Themes/BlurBlack Darker.xaml b/Flow.Launcher/Themes/BlurBlack Darker.xaml index 143d036af..b68641984 100644 --- a/Flow.Launcher/Themes/BlurBlack Darker.xaml +++ b/Flow.Launcher/Themes/BlurBlack Darker.xaml @@ -15,6 +15,7 @@ Dark #C7000000 #C7000000 + 0 0 0 8 From f485e8605a14361bdd6417eba0b5bf2d6a92b0cc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 13:10:06 +0800 Subject: [PATCH 075/158] Support placeholder text --- .../UserSettings/Settings.cs | 11 +++++ Flow.Launcher/Languages/en.xaml | 4 +- Flow.Launcher/MainWindow.xaml | 8 ++++ Flow.Launcher/MainWindow.xaml.cs | 43 +++++++++++++++++++ .../ViewModels/SettingsPaneThemeViewModel.cs | 6 +++ .../SettingPages/Views/SettingsPaneTheme.xaml | 15 +++++-- 6 files changed, 83 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 63debfb47..38c4fbe11 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -113,6 +113,17 @@ namespace Flow.Launcher.Infrastructure.UserSettings public double? SettingWindowLeft { get; set; } = null; public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal; + bool _showPlaceholder { get; set; } = true; + public bool ShowPlaceholder + { + get => _showPlaceholder; + set + { + _showPlaceholder = value; + OnPropertyChanged(); + } + } + public int CustomExplorerIndex { get; set; } = 0; [JsonIgnore] diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index f0454b496..f4e2d88af 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -39,7 +39,7 @@ Game Mode Suspend the use of Hotkeys. Position Reset - Reset search window position + Type here to search Settings @@ -202,6 +202,8 @@ Date This theme supports two(light/dark) modes. This theme supports Blur Transparent Background. + Placeholder Text + Display placeholder text when query is empty Hotkey diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 533819d17..b9e355e2f 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -218,6 +218,14 @@ + Settings.SoundVolume = value; } + public bool ShowPlaceholder + { + get => Settings.ShowPlaceholder; + set => Settings.ShowPlaceholder = value; + } + public bool UseClock { get => Settings.UseClock; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml index 614237146..5b92ee9b9 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml @@ -477,7 +477,6 @@ - - - + + + + + From a4dac91093960175e48aec20f9186f545ec7c228 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 13:25:00 +0800 Subject: [PATCH 076/158] Support resize window --- .../UserSettings/Settings.cs | 13 ++++++++++++- Flow.Launcher/Languages/en.xaml | 12 +++++++----- Flow.Launcher/MainWindow.xaml.cs | 15 +++++++++++++++ .../ViewModels/SettingsPaneThemeViewModel.cs | 6 ++++++ .../SettingPages/Views/SettingsPaneTheme.xaml | 19 +++++++++++++++++-- 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 38c4fbe11..cae5d977f 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -113,7 +113,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings public double? SettingWindowLeft { get; set; } = null; public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal; - bool _showPlaceholder { get; set; } = true; + bool _resizeWindow { get; set; } = true; + public bool ResizeWindow + { + get => _resizeWindow; + set + { + _resizeWindow = value; + OnPropertyChanged(); + } + } + + bool _showPlaceholder { get; set; } = false; public bool ShowPlaceholder { get => _showPlaceholder; diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index f4e2d88af..656431e78 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -104,11 +104,6 @@ Always Preview Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled - Backdrop Type - None - Acrylic - Mica - Mica Alt Search Plugin @@ -200,10 +195,17 @@ Custom Clock Date + Backdrop Type + None + Acrylic + Mica + Mica Alt This theme supports two(light/dark) modes. This theme supports Blur Transparent Background. Placeholder Text Display placeholder text when query is empty + Allow window size change + Allow dragging the search window edges to change its size Hotkey diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 85eff3a57..097ce7df4 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -115,6 +115,9 @@ namespace Flow.Launcher welcomeWindow.Show(); } + // Initialize resize mode + SetupResizeMode(); + // Initialize place holder SetupPlaceholderText(); @@ -243,6 +246,9 @@ namespace Flow.Launcher case nameof(Settings.ShowPlaceholder): SetupPlaceholderText(); break; + case nameof(Settings.ResizeWindow): + SetupResizeMode(); + break; } }; @@ -1071,6 +1077,15 @@ namespace Flow.Launcher #endregion + #region Resize Mode + + private void SetupResizeMode() + { + ResizeMode = _settings.ResizeWindow ? ResizeMode.CanResize : ResizeMode.NoResize; + } + + #endregion + #region IDisposable protected virtual void Dispose(bool disposing) diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 6e83d487a..9c32e9bb9 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -259,6 +259,12 @@ public partial class SettingsPaneThemeViewModel : BaseModel set => Settings.SoundVolume = value; } + public bool ResizeWindow + { + get => Settings.ResizeWindow; + set => Settings.ResizeWindow = value; + } + public bool ShowPlaceholder { get => Settings.ShowPlaceholder; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml index 5b92ee9b9..3abf46f09 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml @@ -30,6 +30,7 @@ VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.ScrollUnit="Pixel"> + + @@ -260,7 +262,8 @@ - + + - + + + + + + Date: Sat, 29 Mar 2025 14:15:24 +0800 Subject: [PATCH 077/158] Support change resize boarder thinkness --- Flow.Launcher.Core/Resource/Theme.cs | 41 +++++++++++++++++++--------- Flow.Launcher/MainWindow.xaml.cs | 14 +++++++--- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 489002617..44802aa91 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -24,7 +24,7 @@ namespace Flow.Launcher.Core.Resource { #region Properties & Fields - public bool BlurEnabled { get; set; } + public bool BlurEnabled { get; private set; } private const string ThemeMetadataNamePrefix = "Name:"; private const string ThemeMetadataIsDarkPrefix = "IsDark:"; @@ -42,6 +42,8 @@ namespace Flow.Launcher.Core.Resource private static string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder); private static string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder); + private Thickness _themeResizeBorderThickness; + #endregion #region Constructor @@ -463,7 +465,7 @@ namespace Flow.Launcher.Core.Resource var effectSetter = new Setter { - Property = Border.EffectProperty, + Property = UIElement.EffectProperty, Value = new DropShadowEffect { Opacity = 0.3, @@ -473,12 +475,12 @@ namespace Flow.Launcher.Core.Resource } }; - if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) is not Setter marginSetter) + if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is not Setter marginSetter) { var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin); marginSetter = new Setter() { - Property = Border.MarginProperty, + Property = FrameworkElement.MarginProperty, Value = margin, }; windowBorderStyle.Setters.Add(marginSetter); @@ -508,12 +510,12 @@ namespace Flow.Launcher.Core.Resource var dict = GetCurrentResourceDictionary(); var windowBorderStyle = dict["WindowBorderStyle"] as Style; - if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) is Setter effectSetter) + if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == UIElement.EffectProperty) is Setter effectSetter) { windowBorderStyle.Setters.Remove(effectSetter); } - if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) is Setter marginSetter) + if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is Setter marginSetter) { var currentMargin = (Thickness)marginSetter.Value; var newMargin = new Thickness( @@ -529,28 +531,41 @@ namespace Flow.Launcher.Core.Resource UpdateResourceDictionary(dict); } + public void SetResizeBoarderThickness(WindowChrome windowChrome, bool resizeWindow) + { + if (resizeWindow) + { + windowChrome.ResizeBorderThickness = _themeResizeBorderThickness; + } + else + { + windowChrome.ResizeBorderThickness = new Thickness(0); + } + } + // because adding drop shadow effect will change the margin of the window, // we need to update the window chrome thickness to correct set the resize border - private static void SetResizeBoarderThickness(Thickness? effectMargin) + private void SetResizeBoarderThickness(Thickness? effectMargin) { var window = Application.Current.MainWindow; if (WindowChrome.GetWindowChrome(window) is WindowChrome windowChrome) { - Thickness thickness; + // Save the theme resize border thickness so that we can restore it if we change ResizeWindow setting if (effectMargin == null) { - thickness = SystemParameters.WindowResizeBorderThickness; + _themeResizeBorderThickness = SystemParameters.WindowResizeBorderThickness; } else { - thickness = new Thickness( + _themeResizeBorderThickness = new Thickness( effectMargin.Value.Left + SystemParameters.WindowResizeBorderThickness.Left, effectMargin.Value.Top + SystemParameters.WindowResizeBorderThickness.Top, effectMargin.Value.Right + SystemParameters.WindowResizeBorderThickness.Right, effectMargin.Value.Bottom + SystemParameters.WindowResizeBorderThickness.Bottom); } - windowChrome.ResizeBorderThickness = thickness; + // Apply the resize border thickness to the window chrome + SetResizeBoarderThickness(windowChrome, _settings.ResizeWindow); } } @@ -582,7 +597,7 @@ namespace Flow.Launcher.Core.Resource { AutoDropShadow(useDropShadowEffect); } - }, DispatcherPriority.Normal); + }, DispatcherPriority.Render); } /// @@ -596,7 +611,7 @@ namespace Flow.Launcher.Core.Resource var (backdropType, _) = GetActualValue(); SetBlurForWindow(GetCurrentTheme(), backdropType); - }, DispatcherPriority.Normal); + }, DispatcherPriority.Render); } /// diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 097ce7df4..ce34aef1f 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -11,6 +11,7 @@ using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; +using System.Windows.Shell; using System.Windows.Threading; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Core.Plugin; @@ -115,9 +116,6 @@ namespace Flow.Launcher welcomeWindow.Show(); } - // Initialize resize mode - SetupResizeMode(); - // Initialize place holder SetupPlaceholderText(); @@ -152,13 +150,17 @@ namespace Flow.Launcher UpdatePosition(); // Refresh frame - await Ioc.Default.GetRequiredService().RefreshFrameAsync(); + await _theme.RefreshFrameAsync(); + + // Initialize resize mode after refreshing frame + SetupResizeMode(); // Reset preview _viewModel.ResetPreview(); // Since the default main window visibility is visible, so we need set focus during startup QueryTextBox.Focus(); + // Set the initial state of the QueryTextBoxCursorMovedToEnd property // Without this part, when shown for the first time, switching the context menu does not move the cursor to the end. _viewModel.QueryTextCursorMovedToEnd = false; @@ -1082,6 +1084,10 @@ namespace Flow.Launcher private void SetupResizeMode() { ResizeMode = _settings.ResizeWindow ? ResizeMode.CanResize : ResizeMode.NoResize; + if (WindowChrome.GetWindowChrome(this) is WindowChrome windowChrome) + { + _theme.SetResizeBoarderThickness(windowChrome, _settings.ResizeWindow); + } } #endregion From c74dd200886b809c709f63e13a83e52098b93b1f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 14:15:35 +0800 Subject: [PATCH 078/158] Remove ThemeManager.Instance --- Flow.Launcher.Core/Resource/ThemeManager.cs | 12 ------------ Flow.Launcher/App.xaml.cs | 1 - 2 files changed, 13 deletions(-) delete mode 100644 Flow.Launcher.Core/Resource/ThemeManager.cs diff --git a/Flow.Launcher.Core/Resource/ThemeManager.cs b/Flow.Launcher.Core/Resource/ThemeManager.cs deleted file mode 100644 index 3cbe8319a..000000000 --- a/Flow.Launcher.Core/Resource/ThemeManager.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using CommunityToolkit.Mvvm.DependencyInjection; - -namespace Flow.Launcher.Core.Resource -{ - [Obsolete("ThemeManager.Instance is obsolete. Use Ioc.Default.GetRequiredService() instead.")] - public class ThemeManager - { - public static Theme Instance - => Ioc.Default.GetRequiredService(); - } -} diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 833c63ddf..7b1d113fb 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -177,7 +177,6 @@ namespace Flow.Launcher HotKeyMapper.Initialize(); // main windows needs initialized before theme change because of blur settings - // TODO: Clean ThemeManager.Instance in future Ioc.Default.GetRequiredService().ChangeTheme(); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); From 84f2686710e937621ecdc7a864ad944fa4f89fe8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 15:08:49 +0800 Subject: [PATCH 079/158] Support placeholder size change --- .../UserSettings/Settings.cs | 10 ++++++ Flow.Launcher/Languages/en.xaml | 6 ++-- Flow.Launcher/MainWindow.xaml | 2 +- Flow.Launcher/MainWindow.xaml.cs | 4 +++ .../ViewModels/SettingsPaneThemeViewModel.cs | 6 ++++ .../SettingPages/Views/SettingsPaneTheme.xaml | 32 +++++++++++++------ Flow.Launcher/ViewModel/MainViewModel.cs | 18 +++++++++++ 7 files changed, 65 insertions(+), 13 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index cae5d977f..7c747ae36 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -134,6 +134,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings OnPropertyChanged(); } } + string _placeholderText { get; set; } = string.Empty; + public string PlaceholderText + { + get => _placeholderText; + set + { + _placeholderText = value; + OnPropertyChanged(); + } + } public int CustomExplorerIndex { get; set; } = 0; diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 656431e78..a9c5ab49f 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -202,8 +202,10 @@ Mica Alt This theme supports two(light/dark) modes. This theme supports Blur Transparent Background. - Placeholder Text - Display placeholder text when query is empty + Show placeholder + Display placeholder when query is empty + Placeholder text + Change placeholder text. Input empty will use: Type here to search Allow window size change Allow dragging the search window edges to change its size diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index b9e355e2f..82ac63b7d 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -224,7 +224,7 @@ FontSize="{Binding QueryBoxFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="False" Style="{DynamicResource QuerySuggestionBoxStyle}" - Text="{DynamicResource queryTextBoxSuggestion}" + Text="{Binding PlaceholderText, Mode=OneWay}" Visibility="Collapsed" /> Settings.ShowPlaceholder = value; } + public string PlaceholderText + { + get => Settings.PlaceholderText; + set => Settings.PlaceholderText = value; + } + public bool UseClock { get => Settings.UseClock; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml index 3abf46f09..462ea799e 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml @@ -70,6 +70,7 @@ + - - - + + + + + + + + + diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index c69fc4484..ec5b52c9d 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -765,6 +765,24 @@ namespace Flow.Launcher.ViewModel public double ClockPanelOpacity { get; set; } = 1; public double SearchIconOpacity { get; set; } = 1; + private string _placeholderText; + public string PlaceholderText + { + get => _placeholderText; + set + { + if (string.IsNullOrEmpty(value)) + { + _placeholderText = App.API.GetTranslation("queryTextBoxSuggestion"); + } + else + { + _placeholderText = value; + } + OnPropertyChanged(); + } + } + public double MainWindowWidth { get => Settings.WindowSize; From 91c5e84ca0f89d5ab0714ddcae86541c51b89778 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 15:27:25 +0800 Subject: [PATCH 080/158] Integrate resize window to fixed window height --- Flow.Launcher.Core/Resource/Theme.cs | 10 ++-- .../UserSettings/Settings.cs | 58 +++++++++++-------- Flow.Launcher/Languages/en.xaml | 6 +- Flow.Launcher/MainWindow.xaml.cs | 6 +- .../ViewModels/SettingsPaneThemeViewModel.cs | 6 -- .../SettingPages/Views/SettingsPaneTheme.xaml | 22 +++---- 6 files changed, 52 insertions(+), 56 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 44802aa91..a1b2c3e0e 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -531,15 +531,15 @@ namespace Flow.Launcher.Core.Resource UpdateResourceDictionary(dict); } - public void SetResizeBoarderThickness(WindowChrome windowChrome, bool resizeWindow) + public void SetResizeBoarderThickness(WindowChrome windowChrome, bool fixedWindowSize) { - if (resizeWindow) + if (fixedWindowSize) { - windowChrome.ResizeBorderThickness = _themeResizeBorderThickness; + windowChrome.ResizeBorderThickness = new Thickness(0); } else { - windowChrome.ResizeBorderThickness = new Thickness(0); + windowChrome.ResizeBorderThickness = _themeResizeBorderThickness; } } @@ -565,7 +565,7 @@ namespace Flow.Launcher.Core.Resource } // Apply the resize border thickness to the window chrome - SetResizeBoarderThickness(windowChrome, _settings.ResizeWindow); + SetResizeBoarderThickness(windowChrome, _settings.KeepMaxResults); } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 7c747ae36..bd146f49a 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -68,11 +68,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings get => _theme; set { - if (value == _theme) - return; - _theme = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(MaxResultsToShow)); + if (value != _theme) + { + _theme = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(MaxResultsToShow)); + } } } public bool UseDropShadowEffect { get; set; } = true; @@ -113,25 +114,17 @@ namespace Flow.Launcher.Infrastructure.UserSettings public double? SettingWindowLeft { get; set; } = null; public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal; - bool _resizeWindow { get; set; } = true; - public bool ResizeWindow - { - get => _resizeWindow; - set - { - _resizeWindow = value; - OnPropertyChanged(); - } - } - bool _showPlaceholder { get; set; } = false; public bool ShowPlaceholder { get => _showPlaceholder; set { - _showPlaceholder = value; - OnPropertyChanged(); + if (_showPlaceholder != value) + { + _showPlaceholder = value; + OnPropertyChanged(); + } } } string _placeholderText { get; set; } = string.Empty; @@ -140,8 +133,11 @@ namespace Flow.Launcher.Infrastructure.UserSettings get => _placeholderText; set { - _placeholderText = value; - OnPropertyChanged(); + if (_placeholderText != value) + { + _placeholderText = value; + OnPropertyChanged(); + } } } @@ -273,10 +269,26 @@ namespace Flow.Launcher.Infrastructure.UserSettings /// public double CustomWindowTop { get; set; } = 0; - public bool KeepMaxResults { get; set; } = false; - public int MaxResultsToShow { get; set; } = 5; - public int ActivateTimes { get; set; } + /// + /// Fixed window size + /// + private bool _keepMaxResults { get; set; } = false; + public bool KeepMaxResults + { + get => _keepMaxResults; + set + { + if (_keepMaxResults != value) + { + _keepMaxResults = value; + OnPropertyChanged(); + } + } + } + public int MaxResultsToShow { get; set; } = 5; + + public int ActivateTimes { get; set; } public ObservableCollection CustomPluginHotkeys { get; set; } = new ObservableCollection(); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index a9c5ab49f..6d890dfba 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -72,8 +72,6 @@ Empty last Query Preserve Last Action Keyword Select Last Action Keyword - Fixed Window Height - The window height is not adjustable by dragging. Maximum results shown You can also quickly adjust this by using CTRL+Plus and CTRL+Minus. Ignore hotkeys in fullscreen mode @@ -206,8 +204,8 @@ Display placeholder when query is empty Placeholder text Change placeholder text. Input empty will use: Type here to search - Allow window size change - Allow dragging the search window edges to change its size + Fixed Window Size + The window size is not adjustable by dragging. Hotkey diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 6ffc010a6..3f0dcc3e1 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -252,7 +252,7 @@ namespace Flow.Launcher case nameof(Settings.PlaceholderText): _viewModel.PlaceholderText = _settings.PlaceholderText; break; - case nameof(Settings.ResizeWindow): + case nameof(Settings.KeepMaxResults): SetupResizeMode(); break; } @@ -1087,10 +1087,10 @@ namespace Flow.Launcher private void SetupResizeMode() { - ResizeMode = _settings.ResizeWindow ? ResizeMode.CanResize : ResizeMode.NoResize; + ResizeMode = _settings.KeepMaxResults ? ResizeMode.NoResize : ResizeMode.CanResize; if (WindowChrome.GetWindowChrome(this) is WindowChrome windowChrome) { - _theme.SetResizeBoarderThickness(windowChrome, _settings.ResizeWindow); + _theme.SetResizeBoarderThickness(windowChrome, _settings.KeepMaxResults); } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 64fe351fa..099be496b 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -259,12 +259,6 @@ public partial class SettingsPaneThemeViewModel : BaseModel set => Settings.SoundVolume = value; } - public bool ResizeWindow - { - get => Settings.ResizeWindow; - set => Settings.ResizeWindow = value; - } - public bool ShowPlaceholder { get => Settings.ShowPlaceholder; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml index 462ea799e..f9eef8924 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml @@ -499,28 +499,20 @@ Text="{DynamicResource browserMoreThemes}" Uri="{Binding LinkThemeGallery}" /> - - - - - - + - + Date: Sat, 29 Mar 2025 15:38:44 +0800 Subject: [PATCH 081/158] Code quality & Fix --- Flow.Launcher/MainWindow.xaml | 2 +- Flow.Launcher/MainWindow.xaml.cs | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 82ac63b7d..2e6133df0 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -10,7 +10,7 @@ xmlns:vm="clr-namespace:Flow.Launcher.ViewModel" Name="FlowMainWindow" Title="Flow Launcher" - Width="{Binding MainWindowWidth, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + Width="{Binding MainWindowWidth, Mode=OneWay}" MinWidth="400" MinHeight="30" d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 3f0dcc3e1..5df1b318f 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -455,23 +455,25 @@ namespace Flow.Launcher { _initialWidth = (int)Width; _initialHeight = (int)Height; + handled = true; } else if (msg == Win32Helper.WM_EXITSIZEMOVE) { if (_initialHeight != (int)Height) { - var shadowMargin = 0; - var (_, useDropShadowEffect) = _theme.GetActualValue(); - if (useDropShadowEffect) - { - shadowMargin = 32; - } - if (!_settings.KeepMaxResults) { - var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize; + // Get shadow margin + var shadowMargin = 0; + var (_, useDropShadowEffect) = _theme.GetActualValue(); + if (useDropShadowEffect) + { + shadowMargin = 32; + } + // Calculate max results to show + var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize; if (itemCount < 2) { _settings.MaxResultsToShow = 2; @@ -483,11 +485,16 @@ namespace Flow.Launcher } SizeToContent = SizeToContent.Height; - _viewModel.MainWindowWidth = Width; } if (_initialWidth != (int)Width) { + if (!_settings.KeepMaxResults) + { + // Update width + _viewModel.MainWindowWidth = Width; + } + SizeToContent = SizeToContent.Height; } From 6f32a6f098851cf3af741b394b6a91c17db0c67a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 15:48:52 +0800 Subject: [PATCH 082/158] Fix placeholder text issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ec5b52c9d..a8425a456 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -768,17 +768,10 @@ namespace Flow.Launcher.ViewModel private string _placeholderText; public string PlaceholderText { - get => _placeholderText; + get => string.IsNullOrEmpty(_placeholderText) ? App.API.GetTranslation("queryTextBoxPlaceholder") : _placeholderText; set { - if (string.IsNullOrEmpty(value)) - { - _placeholderText = App.API.GetTranslation("queryTextBoxSuggestion"); - } - else - { - _placeholderText = value; - } + _placeholderText = value; OnPropertyChanged(); } } From 78b661733d46353381af8fe25dd578a9dd7f8613 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 15:49:22 +0800 Subject: [PATCH 083/158] Improve placeholder text tooltip --- Flow.Launcher/Languages/en.xaml | 4 ++-- .../SettingPages/ViewModels/SettingsPaneThemeViewModel.cs | 5 +++++ Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 6d890dfba..c170c44e4 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -39,7 +39,7 @@ Game Mode Suspend the use of Hotkeys. Position Reset - Type here to search + Type here to search Settings @@ -203,7 +203,7 @@ Show placeholder Display placeholder when query is empty Placeholder text - Change placeholder text. Input empty will use: Type here to search + Change placeholder text. Input empty will use: {0} Fixed Window Size The window size is not adjustable by dragging. diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 099be496b..e35c978ed 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -265,6 +265,11 @@ public partial class SettingsPaneThemeViewModel : BaseModel set => Settings.ShowPlaceholder = value; } + public string PlaceholderTextTip + { + get => string.Format(App.API.GetTranslation("PlaceholderTextTip"), App.API.GetTranslation("queryTextBoxPlaceholder")); + } + public string PlaceholderText { get => Settings.PlaceholderText; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml index f9eef8924..5e1ba24ea 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml @@ -549,9 +549,9 @@ + Sub="{Binding PlaceholderTextTip}"> From 714860e416831cbdebdd7f636e3a9ecb13cae0a5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 16:03:41 +0800 Subject: [PATCH 084/158] Fix hotkey size change issue --- Flow.Launcher/MainWindow.xaml | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 2e6133df0..82ac63b7d 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -10,7 +10,7 @@ xmlns:vm="clr-namespace:Flow.Launcher.ViewModel" Name="FlowMainWindow" Title="Flow Launcher" - Width="{Binding MainWindowWidth, Mode=OneWay}" + Width="{Binding MainWindowWidth, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="400" MinHeight="30" d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index a8425a456..56de70b47 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -584,7 +584,7 @@ namespace Flow.Launcher.ViewModel [RelayCommand] private void IncreaseWidth() { - Settings.WindowSize += 100; + MainWindowWidth += 100; Settings.WindowLeft -= 50; OnPropertyChanged(nameof(MainWindowWidth)); } @@ -592,14 +592,14 @@ namespace Flow.Launcher.ViewModel [RelayCommand] private void DecreaseWidth() { - if (MainWindowWidth - 100 < 400 || Settings.WindowSize == 400) + if (MainWindowWidth - 100 < 400 || MainWindowWidth == 400) { - Settings.WindowSize = 400; + MainWindowWidth = 400; } else { + MainWindowWidth -= 100; Settings.WindowLeft += 50; - Settings.WindowSize -= 100; } OnPropertyChanged(nameof(MainWindowWidth)); From f160670d4f33068074f15a6301289ee78539fd11 Mon Sep 17 00:00:00 2001 From: DB p Date: Sat, 29 Mar 2025 17:24:47 +0900 Subject: [PATCH 085/158] Remove QuerySuggestionBoxStyle background --- Flow.Launcher/Themes/Discord Dark.xaml | 1 - Flow.Launcher/Themes/League.xaml | 1 - Flow.Launcher/Themes/Pink.xaml | 1 - 3 files changed, 3 deletions(-) diff --git a/Flow.Launcher/Themes/Discord Dark.xaml b/Flow.Launcher/Themes/Discord Dark.xaml index 9e39ee5bd..fb88da313 100644 --- a/Flow.Launcher/Themes/Discord Dark.xaml +++ b/Flow.Launcher/Themes/Discord Dark.xaml @@ -28,7 +28,6 @@ BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/League.xaml b/Flow.Launcher/Themes/League.xaml index f1c8ba192..ffecf3fcb 100644 --- a/Flow.Launcher/Themes/League.xaml +++ b/Flow.Launcher/Themes/League.xaml @@ -24,7 +24,6 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Pink.xaml b/Flow.Launcher/Themes/Pink.xaml index d7de4e246..5bbfa26d6 100644 --- a/Flow.Launcher/Themes/Pink.xaml +++ b/Flow.Launcher/Themes/Pink.xaml @@ -22,7 +22,6 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - From 356f229fd44e65f0975ca3f700df3e96b4a87a5c Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Sat, 29 Mar 2025 16:38:54 +0800 Subject: [PATCH 086/158] Update Flow.Launcher.Core/Resource/Theme.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Flow.Launcher.Core/Resource/Theme.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index a1b2c3e0e..a4cca2a1d 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -531,7 +531,7 @@ namespace Flow.Launcher.Core.Resource UpdateResourceDictionary(dict); } - public void SetResizeBoarderThickness(WindowChrome windowChrome, bool fixedWindowSize) + public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize) { if (fixedWindowSize) { From cd01260ee91686e0ae2ca051c834bbe2a7c6e5f6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 16:41:11 +0800 Subject: [PATCH 087/158] Fix typos --- Flow.Launcher.Core/Resource/Theme.cs | 2 +- Flow.Launcher/MainWindow.xaml.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index a4cca2a1d..e5980b62f 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -565,7 +565,7 @@ namespace Flow.Launcher.Core.Resource } // Apply the resize border thickness to the window chrome - SetResizeBoarderThickness(windowChrome, _settings.KeepMaxResults); + SetResizeBorderThickness(windowChrome, _settings.KeepMaxResults); } } diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 5df1b318f..8f22d64b8 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -1097,7 +1097,7 @@ namespace Flow.Launcher ResizeMode = _settings.KeepMaxResults ? ResizeMode.NoResize : ResizeMode.CanResize; if (WindowChrome.GetWindowChrome(this) is WindowChrome windowChrome) { - _theme.SetResizeBoarderThickness(windowChrome, _settings.KeepMaxResults); + _theme.SetResizeBorderThickness(windowChrome, _settings.KeepMaxResults); } } From 9ac9ec849741194d48a964c7b3032bc174e2b923 Mon Sep 17 00:00:00 2001 From: DB p Date: Sat, 29 Mar 2025 18:10:03 +0900 Subject: [PATCH 088/158] - Fix Preview in WelcomePage - Adjust BG Color - Fix aspect ratio wallpaper bitmap --- .../Helper/WallpaperPathRetrieval.cs | 105 +++++++++++------- .../Resources/Pages/WelcomePage1.xaml | 4 +- .../Resources/Pages/WelcomePage2.xaml | 2 +- .../Resources/Pages/WelcomePage5.xaml | 6 +- 4 files changed, 72 insertions(+), 45 deletions(-) diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs index 151ce97dd..47a77f398 100644 --- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs +++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs @@ -13,7 +13,6 @@ namespace Flow.Launcher.Helper; public static class WallpaperPathRetrieval { private static readonly int MAX_CACHE_SIZE = 3; - private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new(); public static Brush GetWallpaperBrush() @@ -27,46 +26,73 @@ public static class WallpaperPathRetrieval try { var wallpaperPath = Win32Helper.GetWallpaperPath(); - if (wallpaperPath is not null && File.Exists(wallpaperPath)) + if (string.IsNullOrEmpty(wallpaperPath) || !File.Exists(wallpaperPath)) { - // Since the wallpaper file name can be the same (TranscodedWallpaper), - // we need to add the last modified date to differentiate them - var dateModified = File.GetLastWriteTime(wallpaperPath); - wallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper); - if (cachedWallpaper != null) - { - return cachedWallpaper; - } - - // We should not dispose the memory stream since the bitmap is still in use - var memStream = new MemoryStream(File.ReadAllBytes(wallpaperPath)); - var bitmap = new BitmapImage(); - bitmap.BeginInit(); - bitmap.StreamSource = memStream; - bitmap.DecodePixelWidth = 800; - bitmap.DecodePixelHeight = 600; - bitmap.EndInit(); - bitmap.Freeze(); // Make the bitmap thread-safe - var wallpaperBrush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill }; - wallpaperBrush.Freeze(); // Make the brush thread-safe - - // Manage cache size - if (wallpaperCache.Count >= MAX_CACHE_SIZE) - { - // Remove the oldest wallpaper from the cache - var oldestCache = wallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault(); - if (oldestCache != default) - { - wallpaperCache.Remove(oldestCache); - } - } - - wallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush); - return wallpaperBrush; + App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Wallpaper path is invalid: {wallpaperPath}"); + var wallpaperColor = GetWallpaperColor(); + return new SolidColorBrush(wallpaperColor); } - var wallpaperColor = GetWallpaperColor(); - return new SolidColorBrush(wallpaperColor); + // Since the wallpaper file name can be the same (TranscodedWallpaper), + // we need to add the last modified date to differentiate them + var dateModified = File.GetLastWriteTime(wallpaperPath); + wallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper); + if (cachedWallpaper != null) + { + App.API.LogInfo(nameof(WallpaperPathRetrieval), "Using cached wallpaper"); + return cachedWallpaper; + } + + // We should not dispose the memory stream since the bitmap is still in use + var memStream = new MemoryStream(File.ReadAllBytes(wallpaperPath)); + var bitmap = new BitmapImage(); + bitmap.BeginInit(); + bitmap.StreamSource = memStream; + bitmap.EndInit(); + + if (bitmap.PixelWidth == 0 || bitmap.PixelHeight == 0) + { + App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Failed to load bitmap: Width={bitmap.PixelWidth}, Height={bitmap.PixelHeight}"); + return new SolidColorBrush(Colors.Transparent); + } + + var originalWidth = bitmap.PixelWidth; + var originalHeight = bitmap.PixelHeight; + + // Calculate the scaling factor to fit the image within 800x600 while preserving aspect ratio + double widthRatio = 800.0 / originalWidth; + double heightRatio = 600.0 / originalHeight; + double scaleFactor = Math.Min(widthRatio, heightRatio); + + int decodedPixelWidth = (int)(originalWidth * scaleFactor); + int decodedPixelHeight = (int)(originalHeight * scaleFactor); + + // Set DecodePixelWidth and DecodePixelHeight to resize the image while preserving aspect ratio + bitmap = new BitmapImage(); + bitmap.BeginInit(); + memStream.Seek(0, SeekOrigin.Begin); // Reset stream position + bitmap.StreamSource = memStream; + bitmap.DecodePixelWidth = decodedPixelWidth; + bitmap.DecodePixelHeight = decodedPixelHeight; + bitmap.EndInit(); + bitmap.Freeze(); // Make the bitmap thread-safe + var wallpaperBrush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill }; + wallpaperBrush.Freeze(); // Make the brush thread-safe + + // Manage cache size + if (wallpaperCache.Count >= MAX_CACHE_SIZE) + { + // Remove the oldest wallpaper from the cache + var oldestCache = wallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault(); + if (oldestCache != default) + { + wallpaperCache.Remove(oldestCache); + } + } + + wallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush); + return wallpaperBrush; + } catch (Exception ex) { @@ -86,8 +112,9 @@ public static class WallpaperPathRetrieval var parts = strResult.Trim().Split(new[] { ' ' }, 3).Select(byte.Parse).ToList(); return Color.FromRgb(parts[0], parts[1], parts[2]); } - catch + catch (Exception ex) { + App.API.LogException(nameof(WallpaperPathRetrieval), "Error parsing wallpaper color", ex); } } diff --git a/Flow.Launcher/Resources/Pages/WelcomePage1.xaml b/Flow.Launcher/Resources/Pages/WelcomePage1.xaml index 1728195bd..32fdb62fc 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage1.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage1.xaml @@ -110,8 +110,8 @@ - - + + diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml index 04c76d027..f62cec3bf 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml @@ -60,7 +60,7 @@ HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal"> - + diff --git a/Flow.Launcher/Resources/Pages/WelcomePage5.xaml b/Flow.Launcher/Resources/Pages/WelcomePage5.xaml index c898ac9a0..3df4b506e 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage5.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage5.xaml @@ -58,10 +58,10 @@ - + - - + + From c685075436f904bc3142d8f7f211a123fcf762cb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 29 Mar 2025 21:01:30 +0800 Subject: [PATCH 089/158] Code quality & Lock & Bitmap memory & Registry access improvement --- .../Helper/WallpaperPathRetrieval.cs | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs index 47a77f398..77bebe2d3 100644 --- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs +++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs @@ -12,8 +12,9 @@ namespace Flow.Launcher.Helper; public static class WallpaperPathRetrieval { - private static readonly int MAX_CACHE_SIZE = 3; - private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new(); + private const int MaxCacheSize = 3; + private static readonly Dictionary<(string, DateTime), ImageBrush> WallpaperCache = new(); + private static readonly object CacheLock = new(); public static Brush GetWallpaperBrush() { @@ -36,42 +37,38 @@ public static class WallpaperPathRetrieval // Since the wallpaper file name can be the same (TranscodedWallpaper), // we need to add the last modified date to differentiate them var dateModified = File.GetLastWriteTime(wallpaperPath); - wallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper); - if (cachedWallpaper != null) + lock (CacheLock) { - App.API.LogInfo(nameof(WallpaperPathRetrieval), "Using cached wallpaper"); - return cachedWallpaper; + WallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper); + if (cachedWallpaper != null) + { + return cachedWallpaper; + } } + + using var fileStream = File.OpenRead(wallpaperPath); + var decoder = BitmapDecoder.Create(fileStream, BitmapCreateOptions.DelayCreation, BitmapCacheOption.None); + var frame = decoder.Frames[0]; + var originalWidth = frame.PixelWidth; + var originalHeight = frame.PixelHeight; - // We should not dispose the memory stream since the bitmap is still in use - var memStream = new MemoryStream(File.ReadAllBytes(wallpaperPath)); - var bitmap = new BitmapImage(); - bitmap.BeginInit(); - bitmap.StreamSource = memStream; - bitmap.EndInit(); - - if (bitmap.PixelWidth == 0 || bitmap.PixelHeight == 0) + if (originalWidth == 0 || originalHeight == 0) { - App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Failed to load bitmap: Width={bitmap.PixelWidth}, Height={bitmap.PixelHeight}"); + App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}"); return new SolidColorBrush(Colors.Transparent); } - var originalWidth = bitmap.PixelWidth; - var originalHeight = bitmap.PixelHeight; - // Calculate the scaling factor to fit the image within 800x600 while preserving aspect ratio - double widthRatio = 800.0 / originalWidth; - double heightRatio = 600.0 / originalHeight; - double scaleFactor = Math.Min(widthRatio, heightRatio); - - int decodedPixelWidth = (int)(originalWidth * scaleFactor); - int decodedPixelHeight = (int)(originalHeight * scaleFactor); + var widthRatio = 800.0 / originalWidth; + var heightRatio = 600.0 / originalHeight; + var scaleFactor = Math.Min(widthRatio, heightRatio); + var decodedPixelWidth = (int)(originalWidth * scaleFactor); + var decodedPixelHeight = (int)(originalHeight * scaleFactor); // Set DecodePixelWidth and DecodePixelHeight to resize the image while preserving aspect ratio - bitmap = new BitmapImage(); + var bitmap = new BitmapImage(); bitmap.BeginInit(); - memStream.Seek(0, SeekOrigin.Begin); // Reset stream position - bitmap.StreamSource = memStream; + bitmap.UriSource = new Uri(wallpaperPath); bitmap.DecodePixelWidth = decodedPixelWidth; bitmap.DecodePixelHeight = decodedPixelHeight; bitmap.EndInit(); @@ -80,19 +77,21 @@ public static class WallpaperPathRetrieval wallpaperBrush.Freeze(); // Make the brush thread-safe // Manage cache size - if (wallpaperCache.Count >= MAX_CACHE_SIZE) + lock (CacheLock) { - // Remove the oldest wallpaper from the cache - var oldestCache = wallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault(); - if (oldestCache != default) + if (WallpaperCache.Count >= MaxCacheSize) { - wallpaperCache.Remove(oldestCache); + // Remove the oldest wallpaper from the cache + var oldestCache = WallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault(); + if (oldestCache != default) + { + WallpaperCache.Remove(oldestCache); + } } + + WallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush); + return wallpaperBrush; } - - wallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush); - return wallpaperBrush; - } catch (Exception ex) { @@ -103,7 +102,7 @@ public static class WallpaperPathRetrieval private static Color GetWallpaperColor() { - RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true); + RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", false); var result = key?.GetValue("Background", null); if (result is string strResult) { @@ -114,7 +113,7 @@ public static class WallpaperPathRetrieval } catch (Exception ex) { - App.API.LogException(nameof(WallpaperPathRetrieval), "Error parsing wallpaper color", ex); + App.API.LogException(nameof(WallpaperPathRetrieval), "Error parsing wallpaper color", ex); } } From 9e11c9c01587ece56f04442a1d9b6393a729c1f8 Mon Sep 17 00:00:00 2001 From: DB p Date: Sat, 29 Mar 2025 22:46:08 +0900 Subject: [PATCH 090/158] Adjust query padding --- Flow.Launcher/Themes/Circle System.xaml | 4 ++-- Flow.Launcher/Themes/Cyan Dark.xaml | 4 ++-- Flow.Launcher/Themes/Darker Glass.xaml | 4 ++-- Flow.Launcher/Themes/Dracula.xaml | 4 ++-- Flow.Launcher/Themes/Midnight.xaml | 4 ++-- Flow.Launcher/Themes/SlimLight.xaml | 4 ++-- Flow.Launcher/Themes/Sublime.xaml | 4 ++-- Flow.Launcher/Themes/Ubuntu.xaml | 4 ++-- Flow.Launcher/Themes/Win10System.xaml | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Flow.Launcher/Themes/Circle System.xaml b/Flow.Launcher/Themes/Circle System.xaml index 600b9e9dc..24c7bd65b 100644 --- a/Flow.Launcher/Themes/Circle System.xaml +++ b/Flow.Launcher/Themes/Circle System.xaml @@ -26,7 +26,7 @@ x:Key="QueryBoxStyle" BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - + @@ -36,7 +36,7 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - + diff --git a/Flow.Launcher/Themes/Cyan Dark.xaml b/Flow.Launcher/Themes/Cyan Dark.xaml index 5a9bc2595..59ebad0f6 100644 --- a/Flow.Launcher/Themes/Cyan Dark.xaml +++ b/Flow.Launcher/Themes/Cyan Dark.xaml @@ -33,7 +33,7 @@ x:Key="QueryBoxStyle" BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - + @@ -44,7 +44,7 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - + diff --git a/Flow.Launcher/Themes/Darker Glass.xaml b/Flow.Launcher/Themes/Darker Glass.xaml index 55f1dacc0..9ffaaf566 100644 --- a/Flow.Launcher/Themes/Darker Glass.xaml +++ b/Flow.Launcher/Themes/Darker Glass.xaml @@ -21,7 +21,7 @@ x:Key="QueryBoxStyle" BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - + @@ -31,7 +31,7 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - + diff --git a/Flow.Launcher/Themes/Dracula.xaml b/Flow.Launcher/Themes/Dracula.xaml index eb8cc9557..6e3510f79 100644 --- a/Flow.Launcher/Themes/Dracula.xaml +++ b/Flow.Launcher/Themes/Dracula.xaml @@ -21,7 +21,7 @@ - + @@ -107,8 +107,8 @@ - - + + @@ -120,11 +120,11 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flow.Launcher/SearchDelaySpeedWindow.xaml.cs b/Flow.Launcher/SearchDelaySpeedWindow.xaml.cs new file mode 100644 index 000000000..5889a1280 --- /dev/null +++ b/Flow.Launcher/SearchDelaySpeedWindow.xaml.cs @@ -0,0 +1,55 @@ +using System.Linq; +using System.Windows; +using Flow.Launcher.Plugin; +using Flow.Launcher.SettingPages.ViewModels; +using Flow.Launcher.ViewModel; +using static Flow.Launcher.SettingPages.ViewModels.SettingsPaneGeneralViewModel; + +namespace Flow.Launcher; + +public partial class SearchDelaySpeedWindow : Window +{ + private readonly PluginViewModel _pluginViewModel; + + public SearchDelaySpeedWindow(PluginViewModel pluginViewModel) + { + InitializeComponent(); + _pluginViewModel = pluginViewModel; + } + + private void SearchDelaySpeed_OnLoaded(object sender, RoutedEventArgs e) + { + tbOldSearchDelaySpeed.Text = _pluginViewModel.SearchDelaySpeedText; + var searchDelaySpeeds = DropdownDataGeneric.GetValues("SearchDelaySpeed"); + SearchDelaySpeedData selected = null; + // Because default value is SearchDelaySpeeds.Slow, we need to get selected value before adding default value + if (_pluginViewModel.PluginSearchDelay != null) + { + selected = searchDelaySpeeds.FirstOrDefault(x => x.Value == _pluginViewModel.PluginSearchDelay); + } + // Add default value to the beginning of the list + // This value should be null + searchDelaySpeeds.Insert(0, new SearchDelaySpeedData { Display = App.API.GetTranslation(PluginViewModel.DefaultLocalizationKey), LocalizationKey = PluginViewModel.DefaultLocalizationKey }); + selected ??= searchDelaySpeeds.FirstOrDefault(); + tbDelay.ItemsSource = searchDelaySpeeds; + tbDelay.SelectedItem = selected; + tbDelay.Focus(); + } + + private void BtnCancel_OnClick(object sender, RoutedEventArgs e) + { + Close(); + } + + private void btnDone_OnClick(object sender, RoutedEventArgs _) + { + // Update search delay speed + var selected = tbDelay.SelectedItem as SearchDelaySpeedData; + SearchDelaySpeeds? changedValue = selected?.LocalizationKey != PluginViewModel.DefaultLocalizationKey ? selected.Value : null; + _pluginViewModel.PluginSearchDelay = changedValue; + + // Update search delay speed text and close window + _pluginViewModel.OnSearchDelaySpeedChanged(); + Close(); + } +} diff --git a/Flow.Launcher/SettingPages/ViewModels/DropdownDataGeneric.cs b/Flow.Launcher/SettingPages/ViewModels/DropdownDataGeneric.cs index 15a814436..c8c119e94 100644 --- a/Flow.Launcher/SettingPages/ViewModels/DropdownDataGeneric.cs +++ b/Flow.Launcher/SettingPages/ViewModels/DropdownDataGeneric.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Flow.Launcher.Core.Resource; using Flow.Launcher.Plugin; namespace Flow.Launcher.SettingPages.ViewModels; @@ -9,7 +8,7 @@ public class DropdownDataGeneric : BaseModel where TValue : Enum { public string Display { get; set; } public TValue Value { get; private init; } - private string LocalizationKey { get; init; } + public string LocalizationKey { get; set; } public static List GetValues(string keyPrefix) where TR : DropdownDataGeneric, new() { @@ -19,7 +18,7 @@ public class DropdownDataGeneric : BaseModel where TValue : Enum foreach (var value in enumValues) { var key = keyPrefix + value; - var display = InternationalizationManager.Instance.GetTranslation(key); + var display = App.API.GetTranslation(key); data.Add(new TR { Display = display, Value = value, LocalizationKey = key }); } @@ -30,7 +29,7 @@ public class DropdownDataGeneric : BaseModel where TValue : Enum { foreach (var item in options) { - item.Display = InternationalizationManager.Instance.GetTranslation(item.LocalizationKey); + item.Display = App.API.GetTranslation(item.LocalizationKey); } } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs index 4a729b578..909011579 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs @@ -150,7 +150,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel public SearchDelaySpeedData SearchDelaySpeed { get => SearchDelaySpeeds.FirstOrDefault(x => x.Value == Settings.SearchDelaySpeed) ?? - SearchDelaySpeeds.FirstOrDefault(x => x.Value == Flow.Launcher.Plugin.SearchDelaySpeeds.Medium) ?? + SearchDelaySpeeds.FirstOrDefault(x => x.Value == Plugin.SearchDelaySpeeds.Medium) ?? SearchDelaySpeeds.FirstOrDefault(); set { diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index e63336235..7ca776512 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -13,6 +13,8 @@ namespace Flow.Launcher.ViewModel { public partial class PluginViewModel : BaseModel { + public const string DefaultLocalizationKey = "default"; + private readonly PluginPair _pluginPair; public PluginPair PluginPair { @@ -127,7 +129,7 @@ namespace Flow.Launcher.ViewModel PluginPair.Metadata.AvgQueryTime + "ms"; public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords); public int Priority => PluginPair.Metadata.Priority; - public string SearchDelaySpeedText => PluginPair.Metadata.SearchDelaySpeed == null ? App.API.GetTranslation("default") : App.API.GetTranslation($"SearchDelaySpeed{PluginPair.Metadata.SearchDelaySpeed}"); + public string SearchDelaySpeedText => PluginPair.Metadata.SearchDelaySpeed == null ? App.API.GetTranslation(DefaultLocalizationKey) : App.API.GetTranslation($"SearchDelaySpeed{PluginPair.Metadata.SearchDelaySpeed}"); public Infrastructure.UserSettings.Plugin PluginSettingsObject{ get; init; } public void OnActionKeywordsChanged() @@ -135,6 +137,11 @@ namespace Flow.Launcher.ViewModel OnPropertyChanged(nameof(ActionKeywordsText)); } + public void OnSearchDelaySpeedChanged() + { + OnPropertyChanged(nameof(SearchDelaySpeedText)); + } + public void ChangePriority(int newPriority) { PluginPair.Metadata.Priority = newPriority; @@ -180,8 +187,8 @@ namespace Flow.Launcher.ViewModel [RelayCommand] private void SetSearchDelaySpeed() { - /*var searchDelaySpeedWindow = new SearchDelaySpeedWindow(this); - searchDelaySpeedWindow.ShowDialog();*/ + var searchDelaySpeedWindow = new SearchDelaySpeedWindow(this); + searchDelaySpeedWindow.ShowDialog(); } } } From e9c1cffd3304867f24d2cd2f2c609998c75755d3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 31 Mar 2025 12:58:27 +0800 Subject: [PATCH 115/158] Code quality --- .../UserSettings/Settings.cs | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index ab0a364d2..fed4b667b 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -320,28 +320,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool LeaveCmdOpen { get; set; } public bool HideWhenDeactivated { get; set; } = true; - bool _searchQueryResultsWithDelay { get; set; } - public bool SearchQueryResultsWithDelay - { - get => _searchQueryResultsWithDelay; - set - { - _searchQueryResultsWithDelay = value; - OnPropertyChanged(); - } - } - - SearchDelaySpeeds searchDelaySpeed { get; set; } = SearchDelaySpeeds.Medium; + public bool SearchQueryResultsWithDelay { get; set; } + [JsonConverter(typeof(JsonStringEnumConverter))] - public SearchDelaySpeeds SearchDelaySpeed - { - get => searchDelaySpeed; - set - { - searchDelaySpeed = value; - OnPropertyChanged(); - } - } + public SearchDelaySpeeds SearchDelaySpeed { get; set; } = SearchDelaySpeeds.Medium; [JsonConverter(typeof(JsonStringEnumConverter))] public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor; From dec1e77b0ce7dd31aab99661b7505d3cf8bf2a7c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 31 Mar 2025 13:04:41 +0800 Subject: [PATCH 116/158] Improve code comments --- Flow.Launcher/SearchDelaySpeedWindow.xaml.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/SearchDelaySpeedWindow.xaml.cs b/Flow.Launcher/SearchDelaySpeedWindow.xaml.cs index 5889a1280..cfbde8be2 100644 --- a/Flow.Launcher/SearchDelaySpeedWindow.xaml.cs +++ b/Flow.Launcher/SearchDelaySpeedWindow.xaml.cs @@ -28,7 +28,7 @@ public partial class SearchDelaySpeedWindow : Window selected = searchDelaySpeeds.FirstOrDefault(x => x.Value == _pluginViewModel.PluginSearchDelay); } // Add default value to the beginning of the list - // This value should be null + // When _pluginViewModel.PluginSearchDelay equals null, we will select this searchDelaySpeeds.Insert(0, new SearchDelaySpeedData { Display = App.API.GetTranslation(PluginViewModel.DefaultLocalizationKey), LocalizationKey = PluginViewModel.DefaultLocalizationKey }); selected ??= searchDelaySpeeds.FirstOrDefault(); tbDelay.ItemsSource = searchDelaySpeeds; diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 309cd67bb..f43931192 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -298,7 +298,7 @@ namespace Flow.Launcher.ViewModel { if (QueryResultsSelected()) { - // When we are requiring, we should not delay the query + // When we are re-querying, we should not delay the query _ = QueryResultsAsync(false, isReQuery: true); } } @@ -306,7 +306,7 @@ namespace Flow.Launcher.ViewModel public void ReQuery(bool reselect) { BackToQueryResults(); - // When we are requiring, we should not delay the query + // When we are re-querying, we should not delay the query _ = QueryResultsAsync(false, isReQuery: true, reSelect: reselect); } @@ -660,7 +660,7 @@ namespace Flow.Launcher.ViewModel } else if (isReQuery) { - // When we are requiring, we should not delay the query + // When we are re-querying, we should not delay the query await QueryAsync(false, isReQuery: true); } From c4cd51c3126db130864d40ce05898962526a6d70 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 31 Mar 2025 13:26:09 +0800 Subject: [PATCH 117/158] Change to search delay time --- .../UserSettings/PluginSettings.cs | 12 +++---- .../UserSettings/Settings.cs | 2 +- Flow.Launcher.Plugin/PluginMetadata.cs | 4 +-- Flow.Launcher.Plugin/SearchDelaySpeeds.cs | 32 ----------------- Flow.Launcher.Plugin/SearchDelayTime.cs | 32 +++++++++++++++++ Flow.Launcher/Languages/en.xaml | 26 +++++++------- .../Controls/InstalledPluginSearchDelay.xaml | 8 ++--- Flow.Launcher/SearchDelaySpeedWindow.xaml | 16 ++++----- Flow.Launcher/SearchDelaySpeedWindow.xaml.cs | 36 +++++++++---------- .../SettingsPaneGeneralViewModel.cs | 20 +++++------ .../Views/SettingsPaneGeneral.xaml | 8 ++--- Flow.Launcher/ViewModel/MainViewModel.cs | 13 ++++--- Flow.Launcher/ViewModel/PluginViewModel.cs | 22 ++++++------ .../plugin.json | 2 +- 14 files changed, 115 insertions(+), 118 deletions(-) delete mode 100644 Flow.Launcher.Plugin/SearchDelaySpeeds.cs create mode 100644 Flow.Launcher.Plugin/SearchDelayTime.cs diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index d1c047495..da92a3583 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -51,7 +51,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings settings.Version = metadata.Version; } settings.DefaultActionKeywords = metadata.ActionKeywords; // metadata provides default values - settings.DefaultSearchDelaySpeed = metadata.SearchDelaySpeed; // metadata provides default values + settings.DefaultSearchDelayTime = metadata.SearchDelayTime; // metadata provides default values // update metadata values with settings if (settings.ActionKeywords?.Count > 0) @@ -66,7 +66,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings } metadata.Disabled = settings.Disabled; metadata.Priority = settings.Priority; - metadata.SearchDelaySpeed = settings.SearchDelaySpeed; + metadata.SearchDelayTime = settings.SearchDelayTime; } else { @@ -80,8 +80,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings ActionKeywords = metadata.ActionKeywords, // use default value Disabled = metadata.Disabled, Priority = metadata.Priority, - DefaultSearchDelaySpeed = metadata.SearchDelaySpeed, // metadata provides default values - SearchDelaySpeed = metadata.SearchDelaySpeed, // use default value + DefaultSearchDelayTime = metadata.SearchDelayTime, // metadata provides default values + SearchDelayTime = metadata.SearchDelayTime, // use default value }; } } @@ -120,10 +120,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings public int Priority { get; set; } [JsonIgnore] - public SearchDelaySpeeds? DefaultSearchDelaySpeed { get; set; } + public SearchDelayTime? DefaultSearchDelayTime { get; set; } [JsonConverter(typeof(JsonStringEnumConverter))] - public SearchDelaySpeeds? SearchDelaySpeed { get; set; } + public SearchDelayTime? SearchDelayTime { get; set; } /// /// Used only to save the state of the plugin in settings diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index fed4b667b..fcfbe8ca0 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -323,7 +323,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool SearchQueryResultsWithDelay { get; set; } [JsonConverter(typeof(JsonStringEnumConverter))] - public SearchDelaySpeeds SearchDelaySpeed { get; set; } = SearchDelaySpeeds.Medium; + public SearchDelayTime SearchDelayTime { get; set; } = SearchDelayTime.Medium; [JsonConverter(typeof(JsonStringEnumConverter))] public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor; diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 42b623717..1496765ce 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -99,10 +99,10 @@ namespace Flow.Launcher.Plugin public bool HideActionKeywordPanel { get; set; } /// - /// Plugin search delay speed. Null means use default search delay. + /// Plugin search delay time. Null means use default search delay time. /// [JsonConverter(typeof(JsonStringEnumConverter))] - public SearchDelaySpeeds? SearchDelaySpeed { get; set; } = null; + public SearchDelayTime? SearchDelayTime { get; set; } = null; /// /// Plugin icon path. diff --git a/Flow.Launcher.Plugin/SearchDelaySpeeds.cs b/Flow.Launcher.Plugin/SearchDelaySpeeds.cs deleted file mode 100644 index 543f8b3f6..000000000 --- a/Flow.Launcher.Plugin/SearchDelaySpeeds.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Flow.Launcher.Plugin; - -/// -/// Enum for search delay speeds -/// -public enum SearchDelaySpeeds -{ - /// - /// Slow search delay speed. 50ms. - /// - Slow, - - /// - /// Moderately slow search delay speed. 100ms. - /// - ModeratelySlow, - - /// - /// Medium search delay speed. 150ms. Default value. - /// - Medium, - - /// - /// Moderately fast search delay speed. 200ms. - /// - ModeratelyFast, - - /// - /// Fast search delay speed. 250ms. - /// - Fast -} diff --git a/Flow.Launcher.Plugin/SearchDelayTime.cs b/Flow.Launcher.Plugin/SearchDelayTime.cs new file mode 100644 index 000000000..8dae5997e --- /dev/null +++ b/Flow.Launcher.Plugin/SearchDelayTime.cs @@ -0,0 +1,32 @@ +namespace Flow.Launcher.Plugin; + +/// +/// Enum for search delay time +/// +public enum SearchDelayTime +{ + /// + /// Long search delay time. 250ms. + /// + Long, + + /// + /// Moderately long search delay time. 200ms. + /// + ModeratelyLong, + + /// + /// Medium search delay time. 150ms. Default value. + /// + Medium, + + /// + /// Moderately short search delay time. 100ms. + /// + ModeratelyShort, + + /// + /// Short search delay time. 50ms. + /// + Short +} diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index ae930db70..e6a764d48 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -104,13 +104,13 @@ Shadow effect is not allowed while current theme has blur effect enabled Search Delay Delay for a while to search when typing. This reduces interface jumpiness and result load. - Default Search Delay Speed - Plugins default delay time after which search results appear when typing is stopped. Default is medium. - Slow - Moderately slow - Medium - Moderately fast - Fast + Default Search Delay Time + Plugin default delay time after which search results appear when typing is stopped. Default is "Medium". + Long + Moderately long + Medium + Moderately short + Short Search Plugin @@ -127,8 +127,8 @@ Current action keyword New action keyword Change Action Keywords - Plugin seach delay speed - Change Plugin Seach Delay Speed + Plugin seach delay time + Change Plugin Seach Delay Time Current Priority New Priority Priority @@ -366,10 +366,10 @@ Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords. - Search Delay Speed Setting - Select the search delay speed you like to use for the plugin. Select Default if you don't want to specify any, and the plugin will use default search delay speed. - Current search delay speed - New search delay speed + Search Delay Time Setting + Select the search delay time you like to use for the plugin. Select "{0}" if you don't want to specify any, and the plugin will use default search delay time. + Current search delay time + New search delay time Custom Query Hotkey diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml b/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml index bd2f9a7c9..0fd98bfac 100644 --- a/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml +++ b/Flow.Launcher/Resources/Controls/InstalledPluginSearchDelay.xaml @@ -32,18 +32,18 @@ VerticalAlignment="Center" DockPanel.Dock="Left" Style="{DynamicResource SettingTitleLabel}" - Text="{DynamicResource pluginSearchDelaySpeed}" /> + Text="{DynamicResource pluginSearchDelayTime}" /> /// - private UpdateManager NewUpdateManager() + private static UpdateManager NewUpdateManager() { var applicationFolderName = Constant.ApplicationDirectory .Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None) @@ -81,20 +81,16 @@ namespace Flow.Launcher.Core.Configuration public void RemoveShortcuts() { - using (var portabilityUpdater = NewUpdateManager()) - { - portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu); - portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop); - portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup); - } + using var portabilityUpdater = NewUpdateManager(); + portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu); + portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop); + portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup); } public void RemoveUninstallerEntry() { - using (var portabilityUpdater = NewUpdateManager()) - { - portabilityUpdater.RemoveUninstallerRegistryEntry(); - } + using var portabilityUpdater = NewUpdateManager(); + portabilityUpdater.RemoveUninstallerRegistryEntry(); } public void MoveUserDataFolder(string fromLocation, string toLocation) @@ -110,12 +106,10 @@ namespace Flow.Launcher.Core.Configuration public void CreateShortcuts() { - using (var portabilityUpdater = NewUpdateManager()) - { - portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu, false); - portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop, false); - portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup, false); - } + using var portabilityUpdater = NewUpdateManager(); + portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu, false); + portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop, false); + portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup, false); } public void CreateUninstallerEntry() @@ -129,18 +123,14 @@ namespace Flow.Launcher.Core.Configuration subKey2.SetValue("DisplayIcon", Path.Combine(Constant.ApplicationDirectory, "app.ico"), RegistryValueKind.String); } - using (var portabilityUpdater = NewUpdateManager()) - { - _ = portabilityUpdater.CreateUninstallerRegistryEntry(); - } + using var portabilityUpdater = NewUpdateManager(); + _ = portabilityUpdater.CreateUninstallerRegistryEntry(); } - internal void IndicateDeletion(string filePathTodelete) + private static void IndicateDeletion(string filePathTodelete) { var deleteFilePath = Path.Combine(filePathTodelete, DataLocation.DeletionIndicatorFile); - using (var _ = File.CreateText(deleteFilePath)) - { - } + using var _ = File.CreateText(deleteFilePath); } /// From 2f53a79a26fc84cb012d580a37c9c2bab7eb7965 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 3 Apr 2025 22:56:31 +0800 Subject: [PATCH 144/158] Revert "Fix environment exit" This reverts commit 5d16216a5519a42c6f201219ddca60ef24289308. --- Flow.Launcher.Core/Configuration/Portable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index 8abdae8d8..2b570d2c0 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -159,7 +159,7 @@ namespace Flow.Launcher.Core.Configuration { FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s)); - Application.Current.Shutdown(); + Environment.Exit(0); } } // Otherwise, if the portable data folder is marked for deletion, From b725975b9898e249a7382171cf2edb07437871d0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 3 Apr 2025 23:01:16 +0800 Subject: [PATCH 145/158] Fix environment exit stuck issue --- Flow.Launcher/App.xaml.cs | 8 ++++++++ Flow.Launcher/MainWindow.xaml.cs | 13 +++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 9aee56bff..f484d4dba 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -304,6 +304,14 @@ namespace Flow.Launcher return; } + // If we call Environment.Exit(0), the application dispose will be called before _mainWindow.Close() + // Accessing _mainWindow?.Dispatcher will cause the application stuck + // So here we need to check it and just return so that we will not acees _mainWindow?.Dispatcher + if (!_mainWindow.CanClose) + { + return; + } + _disposed = true; } diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 011d46d6b..c62606743 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -32,6 +32,13 @@ namespace Flow.Launcher { public partial class MainWindow : IDisposable { + #region Public Property + + // Window Event: Close Event + public bool CanClose { get; set; } = false; + + #endregion + #region Private Fields // Dependency Injection @@ -45,8 +52,6 @@ namespace Flow.Launcher private readonly ContextMenu _contextMenu = new(); private readonly MainViewModel _viewModel; - // Window Event: Close Event - private bool _canClose = false; // Window Event: Key Event private bool _isArrowKeyPressed = false; @@ -279,7 +284,7 @@ namespace Flow.Launcher private async void OnClosing(object sender, CancelEventArgs e) { - if (!_canClose) + if (!CanClose) { _notifyIcon.Visible = false; App.API.SaveAppAllSettings(); @@ -287,7 +292,7 @@ namespace Flow.Launcher await PluginManager.DisposePluginsAsync(); Notification.Uninstall(); // After plugins are all disposed, we can close the main window - _canClose = true; + CanClose = true; // Use this instead of Close() to avoid InvalidOperationException when calling Close() in OnClosing event Application.Current.Shutdown(); } From 95b38d21af7b8790659d9eb49e2edbbba9a037b1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 09:57:03 +0800 Subject: [PATCH 146/158] Wait image cache saved before restarting --- Flow.Launcher.Infrastructure/Image/ImageLoader.cs | 8 +++++++- Flow.Launcher/PublicAPIInstance.cs | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 6f7b1cd90..0ba059b7b 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -61,7 +61,7 @@ namespace Flow.Launcher.Infrastructure.Image }); } - public static async Task Save() + public static async Task SaveAsync() { await storageLock.WaitAsync(); @@ -77,6 +77,12 @@ namespace Flow.Launcher.Infrastructure.Image } } + public static async Task WaitSaveAsync() + { + await storageLock.WaitAsync(); + storageLock.Release(); + } + private static async Task> LoadStorageToConcurrentDictionaryAsync() { await storageLock.WaitAsync(); diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index e19ad2fdc..b86e731b3 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -57,7 +57,7 @@ namespace Flow.Launcher _mainVM.ChangeQueryText(query, requery); } - public void RestartApp() + public async void RestartApp() { _mainVM.Hide(); @@ -66,6 +66,9 @@ namespace Flow.Launcher // which will cause ungraceful exit SaveAppAllSettings(); + // wait for all image caches to be saved + await ImageLoader.WaitSaveAsync(); + // Restart requires Squirrel's Update.exe to be present in the parent folder, // it is only published from the project's release pipeline. When debugging without it, // the project may not restart or just terminates. This is expected. @@ -88,7 +91,7 @@ namespace Flow.Launcher PluginManager.Save(); _mainVM.Save(); _settings.Save(); - _ = ImageLoader.Save(); + _ = ImageLoader.SaveAsync(); } public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync(); From e4577eb23edbe6ce4e13154d2588d9e42e494764 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 10:02:47 +0800 Subject: [PATCH 147/158] Improve code comment --- Flow.Launcher/PublicAPIInstance.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index b86e731b3..a9862c74c 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -57,16 +57,18 @@ namespace Flow.Launcher _mainVM.ChangeQueryText(query, requery); } +#pragma warning disable VSTHRD100 // Avoid async void methods + public async void RestartApp() { _mainVM.Hide(); - // we must manually save + // We must manually save // UpdateManager.RestartApp() will call Environment.Exit(0) // which will cause ungraceful exit SaveAppAllSettings(); - // wait for all image caches to be saved + // Wait for all image caches to be saved before restarting await ImageLoader.WaitSaveAsync(); // Restart requires Squirrel's Update.exe to be present in the parent folder, @@ -75,6 +77,8 @@ namespace Flow.Launcher UpdateManager.RestartApp(Constant.ApplicationFileName); } +#pragma warning restore VSTHRD100 // Avoid async void methods + public void ShowMainWindow() => _mainVM.Show(); public void HideMainWindow() => _mainVM.Hide(); From 043fe76a613a5768bcd0e983acb555c58a8d681f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 10:03:19 +0800 Subject: [PATCH 148/158] Add settings save lock --- Flow.Launcher/PublicAPIInstance.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index a9862c74c..d88eeb7c9 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -37,6 +37,8 @@ namespace Flow.Launcher private readonly Internationalization _translater; private readonly MainViewModel _mainVM; + private readonly object _saveSettingsLock = new(); + #region Constructor public PublicAPIInstance(Settings settings, Internationalization translater, MainViewModel mainVM) @@ -92,9 +94,12 @@ namespace Flow.Launcher public void SaveAppAllSettings() { - PluginManager.Save(); - _mainVM.Save(); - _settings.Save(); + lock (_saveSettingsLock) + { + _settings.Save(); + PluginManager.Save(); + _mainVM.Save(); + } _ = ImageLoader.SaveAsync(); } From 95a3fd36dea778911d0f4f8b9a434e4813096b6a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 10:34:59 +0800 Subject: [PATCH 149/158] Do not crash when caught exception on saving settings --- .../Plugin/JsonRPCPluginSettings.cs | 20 +++++++++-- .../Storage/FlowLauncherJsonStorage.cs | 35 ++++++++++++++++++- .../Storage/PluginJsonStorage.cs | 33 +++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs index 944b2fd10..e0a217251 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs @@ -23,6 +23,8 @@ namespace Flow.Launcher.Core.Plugin protected ConcurrentDictionary Settings { get; set; } = null!; public required IPublicAPI API { get; init; } + private static readonly string ClassName = nameof(JsonRPCPluginSettings); + private JsonStorage> _storage = null!; private static readonly Thickness SettingPanelMargin = (Thickness)Application.Current.FindResource("SettingPanelMargin"); @@ -122,12 +124,26 @@ namespace Flow.Launcher.Core.Plugin public async Task SaveAsync() { - await _storage.SaveAsync(); + try + { + await _storage.SaveAsync(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save plugin settings to path: {SettingPath}", e); + } } public void Save() { - _storage.Save(); + try + { + _storage.Save(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save plugin settings to path: {SettingPath}", e); + } } public bool NeedCreateSettingPanel() diff --git a/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs index 865041fb3..a3634a0e2 100644 --- a/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs @@ -1,10 +1,19 @@ using System.IO; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.Storage { public class FlowLauncherJsonStorage : JsonStorage where T : new() { + private static readonly string ClassName = "FlowLauncherJsonStorage"; + + // 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(); + public FlowLauncherJsonStorage() { var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName); @@ -13,5 +22,29 @@ namespace Flow.Launcher.Infrastructure.Storage var filename = typeof(T).Name; FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}"); } + + public new void Save() + { + try + { + base.Save(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e); + } + } + + public new async Task SaveAsync() + { + try + { + await base.SaveAsync(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e); + } + } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index b377c81aa..e02d51bdb 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -1,5 +1,8 @@ using System.IO; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.Storage { @@ -8,6 +11,12 @@ namespace Flow.Launcher.Infrastructure.Storage // Use assembly name to check which plugin is using this storage public readonly string AssemblyName; + private static readonly string ClassName = "PluginJsonStorage"; + + // 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(); + public PluginJsonStorage() { // C# related, add python related below @@ -23,5 +32,29 @@ namespace Flow.Launcher.Infrastructure.Storage { Data = data; } + + public new void Save() + { + try + { + base.Save(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e); + } + } + + public new async Task SaveAsync() + { + try + { + await base.SaveAsync(); + } + catch (System.Exception e) + { + API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e); + } + } } } From f6e3608f72ab28c48bf8563ea3bb58af7b9950bb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 10:42:48 +0800 Subject: [PATCH 150/158] Do not crash when saving cache --- Flow.Launcher.Infrastructure/Image/ImageLoader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 0ba059b7b..1ee033821 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -71,6 +71,10 @@ namespace Flow.Launcher.Infrastructure.Image .Select(x => x.Key) .ToList()); } + catch (System.Exception e) + { + Log.Exception($"|ImageLoader.SaveAsync|Failed to save image cache to file", e); + } finally { storageLock.Release(); From 2d6667bb53549c5580004fe194b56b90d20cdcd7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 10:42:55 +0800 Subject: [PATCH 151/158] Test save error --- Flow.Launcher.Infrastructure/Storage/JsonStorage.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 40106acd8..52bcdcfab 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -180,20 +180,24 @@ namespace Flow.Launcher.Infrastructure.Storage public void Save() { - string serialized = JsonSerializer.Serialize(Data, + throw new NotImplementedException("Save error"); + + /*string serialized = JsonSerializer.Serialize(Data, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(TempFilePath, serialized); - AtomicWriteSetting(); + AtomicWriteSetting();*/ } public async Task SaveAsync() { - await using var tempOutput = File.OpenWrite(TempFilePath); + throw new NotImplementedException("SaveAsync error"); + + /*await using var tempOutput = File.OpenWrite(TempFilePath); await JsonSerializer.SerializeAsync(tempOutput, Data, new JsonSerializerOptions { WriteIndented = true }); - AtomicWriteSetting(); + AtomicWriteSetting();*/ } private void AtomicWriteSetting() From c2f8480c049a4a818831c05558080907f2bac8d0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 11:15:13 +0800 Subject: [PATCH 152/158] Revert "Test save error" This reverts commit 2d6667bb53549c5580004fe194b56b90d20cdcd7. --- Flow.Launcher.Infrastructure/Storage/JsonStorage.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 52bcdcfab..40106acd8 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -180,24 +180,20 @@ namespace Flow.Launcher.Infrastructure.Storage public void Save() { - throw new NotImplementedException("Save error"); - - /*string serialized = JsonSerializer.Serialize(Data, + string serialized = JsonSerializer.Serialize(Data, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(TempFilePath, serialized); - AtomicWriteSetting();*/ + AtomicWriteSetting(); } public async Task SaveAsync() { - throw new NotImplementedException("SaveAsync error"); - - /*await using var tempOutput = File.OpenWrite(TempFilePath); + await using var tempOutput = File.OpenWrite(TempFilePath); await JsonSerializer.SerializeAsync(tempOutput, Data, new JsonSerializerOptions { WriteIndented = true }); - AtomicWriteSetting();*/ + AtomicWriteSetting(); } private void AtomicWriteSetting() From 2a2ef234d952be3bc44507f95248b1622b92256d Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Fri, 4 Apr 2025 11:17:30 +0800 Subject: [PATCH 153/158] Fix typos Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Flow.Launcher.Infrastructure/Win32Helper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs index 42461dc18..f2b99588d 100644 --- a/Flow.Launcher.Infrastructure/Win32Helper.cs +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -489,8 +489,7 @@ namespace Flow.Launcher.Infrastructure #endregion - #region Noticification - + #region Notification public static bool IsNotificationSupport() { // Noticification only supported Windows 10 19041+ From 834780d6e7d57577a59f35c0ad5715f99e7b61cf Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Fri, 4 Apr 2025 11:17:37 +0800 Subject: [PATCH 154/158] Fix typos Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Flow.Launcher.Infrastructure/Win32Helper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs index f2b99588d..5bec3e5e7 100644 --- a/Flow.Launcher.Infrastructure/Win32Helper.cs +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -492,7 +492,7 @@ namespace Flow.Launcher.Infrastructure #region Notification public static bool IsNotificationSupport() { - // Noticification only supported Windows 10 19041+ + // Notification only supported Windows 10 19041+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Build >= 19041; } From 8df1e6a0ce9977346327c9e9249ef2359c3a9ad1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 11:18:57 +0800 Subject: [PATCH 155/158] Add blank line --- Flow.Launcher.Infrastructure/Win32Helper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs index 5bec3e5e7..d3023a3f0 100644 --- a/Flow.Launcher.Infrastructure/Win32Helper.cs +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -490,6 +490,7 @@ namespace Flow.Launcher.Infrastructure #endregion #region Notification + public static bool IsNotificationSupport() { // Notification only supported Windows 10 19041+ From 3283adce59384003887d5d91dfa767ca6822c92e Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Fri, 4 Apr 2025 11:27:10 +0800 Subject: [PATCH 156/158] Fix typos Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- Flow.Launcher.Infrastructure/Win32Helper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs index d3023a3f0..c21849403 100644 --- a/Flow.Launcher.Infrastructure/Win32Helper.cs +++ b/Flow.Launcher.Infrastructure/Win32Helper.cs @@ -491,9 +491,9 @@ namespace Flow.Launcher.Infrastructure #region Notification - public static bool IsNotificationSupport() + public static bool IsNotificationSupported() { - // Notification only supported Windows 10 19041+ + // Notifications only supported on Windows 10 19041+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Build >= 19041; } From ec7a4e8aaa1463524ae0cacebceb5ec7f89e94b8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 11:27:50 +0800 Subject: [PATCH 157/158] Fix build issue --- Flow.Launcher/Notification.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Notification.cs b/Flow.Launcher/Notification.cs index 23125de15..30b3a0673 100644 --- a/Flow.Launcher/Notification.cs +++ b/Flow.Launcher/Notification.cs @@ -9,7 +9,7 @@ namespace Flow.Launcher { internal static class Notification { - internal static bool legacy = !Win32Helper.IsNotificationSupport(); + internal static bool legacy = !Win32Helper.IsNotificationSupported(); internal static void Uninstall() { From 28ab71f11a60304db888c465e871c24be5b18ab5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 4 Apr 2025 11:46:07 +0800 Subject: [PATCH 158/158] Fix build issue --- Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs | 1 + Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs index 0fbeed9f5..8b4062b6b 100644 --- a/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Infrastructure.Storage { diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index 910672119..e8cbd70fb 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Infrastructure.Storage {