mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge Dev
This commit is contained in:
commit
4426bed134
72 changed files with 944 additions and 532 deletions
|
|
@ -9,11 +9,15 @@ using Flow.Launcher.Infrastructure.Logger;
|
|||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.Configuration
|
||||
{
|
||||
public class Portable : IPortable
|
||||
{
|
||||
private readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
/// <summary>
|
||||
/// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish
|
||||
/// </summary>
|
||||
|
|
@ -40,7 +44,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
#endif
|
||||
IndicateDeletion(DataLocation.PortableDataPath);
|
||||
|
||||
MessageBoxEx.Show("Flow Launcher needs to restart to finish disabling portable mode, " +
|
||||
API.ShowMsgBox("Flow Launcher needs to restart to finish disabling portable mode, " +
|
||||
"after the restart your portable data profile will be deleted and roaming data profile kept");
|
||||
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
|
|
@ -64,7 +68,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
#endif
|
||||
IndicateDeletion(DataLocation.RoamingDataPath);
|
||||
|
||||
MessageBoxEx.Show("Flow Launcher needs to restart to finish enabling portable mode, " +
|
||||
API.ShowMsgBox("Flow Launcher needs to restart to finish enabling portable mode, " +
|
||||
"after the restart your roaming data profile will be deleted and portable data profile kept");
|
||||
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
|
|
@ -95,13 +99,13 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
public void MoveUserDataFolder(string fromLocation, string toLocation)
|
||||
{
|
||||
FilesFolders.CopyAll(fromLocation, toLocation, MessageBoxEx.Show);
|
||||
FilesFolders.CopyAll(fromLocation, toLocation, (s) => API.ShowMsgBox(s));
|
||||
VerifyUserDataAfterMove(fromLocation, toLocation);
|
||||
}
|
||||
|
||||
public void VerifyUserDataAfterMove(string fromLocation, string toLocation)
|
||||
{
|
||||
FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, MessageBoxEx.Show);
|
||||
FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => API.ShowMsgBox(s));
|
||||
}
|
||||
|
||||
public void CreateShortcuts()
|
||||
|
|
@ -157,13 +161,13 @@ namespace Flow.Launcher.Core.Configuration
|
|||
// delete it and prompt the user to pick the portable data location
|
||||
if (File.Exists(roamingDataDeleteFilePath))
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(roamingDataDir, MessageBoxEx.Show);
|
||||
FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s));
|
||||
|
||||
if (MessageBoxEx.Show("Flow Launcher has detected you enabled portable mode, " +
|
||||
if (API.ShowMsgBox("Flow Launcher has detected you enabled portable mode, " +
|
||||
"would you like to move it to a different location?", string.Empty,
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
FilesFolders.OpenPath(Constant.RootDirectory, MessageBoxEx.Show);
|
||||
FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s));
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
|
@ -172,9 +176,9 @@ namespace Flow.Launcher.Core.Configuration
|
|||
// delete it and notify the user about it.
|
||||
else if (File.Exists(portableDataDeleteFilePath))
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(portableDataDir, MessageBoxEx.Show);
|
||||
FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s));
|
||||
|
||||
MessageBoxEx.Show("Flow Launcher has detected you disabled portable mode, " +
|
||||
API.ShowMsgBox("Flow Launcher has detected you disabled portable mode, " +
|
||||
"the relevant shortcuts and uninstaller entry have been created");
|
||||
}
|
||||
}
|
||||
|
|
@ -186,7 +190,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
|
||||
if (roamingLocationExists && portableLocationExists)
|
||||
{
|
||||
MessageBoxEx.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " +
|
||||
API.ShowMsgBox(string.Format("Flow Launcher detected your user data exists both in {0} and " +
|
||||
"{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.",
|
||||
DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine));
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@ using System.Linq;
|
|||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
public abstract class AbstractPluginEnvironment
|
||||
{
|
||||
protected readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
internal abstract string Language { get; }
|
||||
|
||||
internal abstract string EnvName { get; }
|
||||
|
|
@ -25,7 +28,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
|
||||
internal virtual string FileDialogFilter => string.Empty;
|
||||
|
||||
internal abstract string PluginsSettingsFilePath { get; set; }
|
||||
internal abstract string PluginsSettingsFilePath { get; set; }
|
||||
|
||||
internal List<PluginMetadata> PluginMetadataList;
|
||||
|
||||
|
|
@ -57,7 +60,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
EnvName,
|
||||
Environment.NewLine
|
||||
);
|
||||
if (MessageBoxEx.Show(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
{
|
||||
var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
|
||||
string selectedFile;
|
||||
|
|
@ -82,7 +85,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
}
|
||||
else
|
||||
{
|
||||
MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
|
||||
API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
|
||||
Log.Error("PluginsLoader",
|
||||
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
|
||||
$"{Language}Environment");
|
||||
|
|
@ -98,7 +101,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
if (expectedPath == currentPath)
|
||||
return;
|
||||
|
||||
FilesFolders.RemoveFolderIfExists(installedDirPath, MessageBoxEx.Show);
|
||||
FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
InstallEnvironment();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show);
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
// Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and
|
||||
// uses Python plugin they need to custom install and use v3.8.9
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show);
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show);
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();
|
||||
|
||||
|
|
|
|||
|
|
@ -44,8 +44,10 @@ namespace Flow.Launcher.Core.Plugin
|
|||
private string SettingConfigurationPath =>
|
||||
Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
|
||||
|
||||
private string SettingPath => Path.Combine(DataLocation.PluginSettingsDirectory,
|
||||
Context.CurrentPluginMetadata.Name, "Settings.json");
|
||||
private string SettingDirectory => Path.Combine(DataLocation.PluginSettingsDirectory,
|
||||
Context.CurrentPluginMetadata.Name);
|
||||
|
||||
private string SettingPath => Path.Combine(SettingDirectory, "Settings.json");
|
||||
|
||||
public abstract List<Result> LoadContextMenus(Result selectedResult);
|
||||
|
||||
|
|
@ -159,5 +161,13 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
return Settings.CreateSettingPanel();
|
||||
}
|
||||
|
||||
public void DeletePluginSettingsDirectory()
|
||||
{
|
||||
if (Directory.Exists(SettingDirectory))
|
||||
{
|
||||
Directory.Delete(SettingDirectory, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,10 +112,15 @@ namespace Flow.Launcher.Core.Plugin
|
|||
RPC.StartListening();
|
||||
}
|
||||
|
||||
public virtual Task ReloadDataAsync()
|
||||
public virtual async Task ReloadDataAsync()
|
||||
{
|
||||
SetupJsonRPC();
|
||||
return Task.CompletedTask;
|
||||
try
|
||||
{
|
||||
await RPC.InvokeAsync("reload_data", Context);
|
||||
}
|
||||
catch (RemoteMethodNotFoundException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async ValueTask DisposeAsync()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using ISavable = Flow.Launcher.Plugin.ISavable;
|
|||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System.Text.Json;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -28,7 +29,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
public static readonly HashSet<PluginPair> GlobalPlugins = new();
|
||||
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new();
|
||||
|
||||
public static IPublicAPI API { private set; get; }
|
||||
public static IPublicAPI API { get; private set; } = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
private static PluginsSettings Settings;
|
||||
private static List<PluginMetadata> _metadatas;
|
||||
|
|
@ -158,9 +159,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// Call initialize for all plugins
|
||||
/// </summary>
|
||||
/// <returns>return the list of failed to init plugins or null for none</returns>
|
||||
public static async Task InitializePluginsAsync(IPublicAPI api)
|
||||
public static async Task InitializePluginsAsync()
|
||||
{
|
||||
API = api;
|
||||
var failedPlugins = new ConcurrentQueue<PluginPair>();
|
||||
|
||||
var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate
|
||||
|
|
@ -204,15 +204,15 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
|
||||
InternationalizationManager.Instance.AddPluginLanguageDirectories(GetPluginsForInterface<IPluginI18n>());
|
||||
InternationalizationManager.Instance.ChangeLanguage(InternationalizationManager.Instance.Settings.Language);
|
||||
InternationalizationManager.Instance.ChangeLanguage(Ioc.Default.GetRequiredService<Settings>().Language);
|
||||
|
||||
if (failedPlugins.Any())
|
||||
{
|
||||
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
|
||||
API.ShowMsg(
|
||||
InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsTitle"),
|
||||
API.GetTranslation("failedToInitializePluginsTitle"),
|
||||
string.Format(
|
||||
InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsMessage"),
|
||||
API.GetTranslation("failedToInitializePluginsMessage"),
|
||||
failed
|
||||
),
|
||||
"",
|
||||
|
|
@ -439,7 +439,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
|
||||
{
|
||||
InstallPlugin(newVersion, zipFilePath, checkModified:false);
|
||||
UninstallPlugin(existingVersion, removeSettings:false, checkModified:false);
|
||||
UninstallPlugin(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false);
|
||||
_modifiedPlugins.Add(existingVersion.ID);
|
||||
}
|
||||
|
||||
|
|
@ -454,9 +454,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// <summary>
|
||||
/// Uninstall a plugin.
|
||||
/// </summary>
|
||||
public static void UninstallPlugin(PluginMetadata plugin, bool removeSettings = true)
|
||||
public static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false)
|
||||
{
|
||||
UninstallPlugin(plugin, removeSettings, true);
|
||||
UninstallPlugin(plugin, removePluginFromSettings, removePluginSettings, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -519,9 +519,17 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
var newPluginPath = Path.Combine(installDirectory, folderName);
|
||||
|
||||
FilesFolders.CopyAll(pluginFolderPath, newPluginPath, MessageBoxEx.Show);
|
||||
FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
Directory.Delete(tempFolderPluginPath, true);
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(tempFolderPluginPath))
|
||||
Directory.Delete(tempFolderPluginPath, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.InstallPlugin|Failed to delete temp folder {tempFolderPluginPath}", e);
|
||||
}
|
||||
|
||||
if (checkModified)
|
||||
{
|
||||
|
|
@ -529,14 +537,62 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
internal static void UninstallPlugin(PluginMetadata plugin, bool removeSettings, bool checkModified)
|
||||
internal static 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 (removeSettings)
|
||||
if (removePluginSettings)
|
||||
{
|
||||
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 });
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
else // the plugin with json prc interface
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removePluginFromSettings)
|
||||
{
|
||||
Settings.Plugins.Remove(plugin.ID);
|
||||
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core.ExternalPlugins.Environments;
|
||||
#pragma warning disable IDE0005
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
|
@ -119,7 +120,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
MessageBoxEx.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
|
||||
Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Please refer to the logs for more information", "",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
|
|
|
|||
|
|
@ -11,22 +11,24 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
|||
using Flow.Launcher.Plugin;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
public class Internationalization
|
||||
{
|
||||
public Settings Settings { get; set; }
|
||||
private const string Folder = "Languages";
|
||||
private const string DefaultLanguageCode = "en";
|
||||
private const string DefaultFile = "en.xaml";
|
||||
private const string Extension = ".xaml";
|
||||
private readonly Settings _settings;
|
||||
private readonly List<string> _languageDirectories = new List<string>();
|
||||
private readonly List<ResourceDictionary> _oldResources = new List<ResourceDictionary>();
|
||||
private readonly string SystemLanguageCode;
|
||||
|
||||
public Internationalization()
|
||||
public Internationalization(Settings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
AddFlowLauncherLanguageDirectory();
|
||||
SystemLanguageCode = GetSystemLanguageCodeAtStartup();
|
||||
}
|
||||
|
|
@ -141,7 +143,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture;
|
||||
|
||||
// Raise event after culture is set
|
||||
Settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
|
||||
_settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
UpdatePluginMetadataTranslations();
|
||||
|
|
@ -152,7 +154,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
var languageToSet = GetLanguageByLanguageCode(languageCodeToSet);
|
||||
|
||||
if (Settings.ShouldUsePinyin)
|
||||
if (_settings.ShouldUsePinyin)
|
||||
return false;
|
||||
|
||||
if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW)
|
||||
|
|
@ -162,7 +164,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
// "Do you want to search with pinyin?"
|
||||
string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ;
|
||||
|
||||
if (MessageBoxEx.Show(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
if (Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,12 @@
|
|||
namespace Flow.Launcher.Core.Resource
|
||||
using System;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
[Obsolete("InternationalizationManager.Instance is obsolete. Use Ioc.Default.GetRequiredService<Internationalization>() instead.")]
|
||||
public static class InternationalizationManager
|
||||
{
|
||||
private static Internationalization instance;
|
||||
private static object syncObject = new object();
|
||||
|
||||
public static Internationalization Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new Internationalization();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
=> Ioc.Default.GetRequiredService<Internationalization>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Windows.Controls;
|
|||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Effects;
|
||||
using System.Windows.Shell;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
|
@ -17,6 +18,8 @@ using System.Runtime.InteropServices;
|
|||
using System.Windows.Interop;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Win32;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
|
|
@ -28,10 +31,11 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
private const int ShadowExtraMargin = 32;
|
||||
|
||||
private readonly List<string> _themeDirectories = new List<string>();
|
||||
private readonly IPublicAPI _api;
|
||||
private readonly Settings _settings;
|
||||
private readonly List<string> _themeDirectories = new();
|
||||
private ResourceDictionary _oldResource;
|
||||
private string _oldTheme;
|
||||
public Settings Settings { get; set; }
|
||||
private const string Folder = Constant.Themes;
|
||||
private const string Extension = ".xaml";
|
||||
private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
|
||||
|
|
@ -41,8 +45,11 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
private double mainWindowWidth;
|
||||
|
||||
public Theme()
|
||||
public Theme(IPublicAPI publicAPI, Settings settings)
|
||||
{
|
||||
_api = publicAPI;
|
||||
_settings = settings;
|
||||
|
||||
_themeDirectories.Add(DirectoryPath);
|
||||
_themeDirectories.Add(UserDirectoryPath);
|
||||
MakeSureThemeDirectoriesExist();
|
||||
|
|
@ -403,7 +410,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
// to things like fonts
|
||||
UpdateResourceDictionary(GetResourceDictionary(theme));
|
||||
|
||||
Settings.Theme = theme;
|
||||
_settings.Theme = theme;
|
||||
|
||||
|
||||
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme
|
||||
|
|
@ -414,7 +421,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
BlurEnabled = Win32Helper.IsBlurTheme();
|
||||
|
||||
if (Settings.UseDropShadowEffect && !BlurEnabled)
|
||||
if (_settings.UseDropShadowEffect && !BlurEnabled)
|
||||
AddDropShadowEffectToCurrentTheme();
|
||||
|
||||
//Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled);
|
||||
|
|
@ -425,7 +432,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
|
||||
if (theme != defaultTheme)
|
||||
{
|
||||
MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme));
|
||||
_api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme));
|
||||
ChangeTheme(defaultTheme);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -435,7 +442,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse");
|
||||
if (theme != defaultTheme)
|
||||
{
|
||||
MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme));
|
||||
_api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme));
|
||||
ChangeTheme(defaultTheme);
|
||||
}
|
||||
return false;
|
||||
|
|
@ -463,7 +470,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
return dict;
|
||||
}
|
||||
|
||||
private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(Settings.Theme);
|
||||
private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(_settings.Theme);
|
||||
|
||||
public ResourceDictionary GetResourceDictionary(string theme)
|
||||
{
|
||||
|
|
@ -472,10 +479,10 @@ namespace Flow.Launcher.Core.Resource
|
|||
if (dict["QueryBoxStyle"] is Style queryBoxStyle &&
|
||||
dict["QuerySuggestionBoxStyle"] is Style querySuggestionBoxStyle)
|
||||
{
|
||||
var fontFamily = new FontFamily(Settings.QueryBoxFont);
|
||||
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.QueryBoxFontStyle);
|
||||
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.QueryBoxFontWeight);
|
||||
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.QueryBoxFontStretch);
|
||||
var fontFamily = new FontFamily(_settings.QueryBoxFont);
|
||||
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.QueryBoxFontStyle);
|
||||
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight);
|
||||
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch);
|
||||
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily));
|
||||
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle));
|
||||
|
|
@ -500,10 +507,10 @@ namespace Flow.Launcher.Core.Resource
|
|||
dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle &&
|
||||
dict["ItemHotkeySelectedStyle"] is Style resultHotkeyItemSelectedStyle)
|
||||
{
|
||||
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultFont));
|
||||
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultFontStyle));
|
||||
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultFontWeight));
|
||||
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultFontStretch));
|
||||
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont));
|
||||
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle));
|
||||
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight));
|
||||
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch));
|
||||
|
||||
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
|
||||
Array.ForEach(
|
||||
|
|
@ -515,10 +522,10 @@ namespace Flow.Launcher.Core.Resource
|
|||
dict["ItemSubTitleStyle"] is Style resultSubItemStyle &&
|
||||
dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle)
|
||||
{
|
||||
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultSubFont));
|
||||
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultSubFontStyle));
|
||||
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultSubFontWeight));
|
||||
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultSubFontStretch));
|
||||
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont));
|
||||
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle));
|
||||
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight));
|
||||
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch));
|
||||
|
||||
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
|
||||
Array.ForEach(
|
||||
|
|
@ -528,7 +535,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
/* Ignore Theme Window Width and use setting */
|
||||
var windowStyle = dict["WindowStyle"] as Style;
|
||||
var width = Settings.WindowSize;
|
||||
var width = _settings.WindowSize;
|
||||
windowStyle.Setters.Add(new Setter(Window.WidthProperty, width));
|
||||
mainWindowWidth = (double)width;
|
||||
return dict;
|
||||
|
|
@ -536,7 +543,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
private ResourceDictionary GetCurrentResourceDictionary( )
|
||||
{
|
||||
return GetResourceDictionary(Settings.Theme);
|
||||
return GetResourceDictionary(_settings.Theme);
|
||||
}
|
||||
|
||||
public List<ThemeData> LoadAvailableThemes()
|
||||
|
|
|
|||
|
|
@ -1,26 +1,12 @@
|
|||
namespace Flow.Launcher.Core.Resource
|
||||
using System;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
[Obsolete("ThemeManager.Instance is obsolete. Use Ioc.Default.GetRequiredService<Theme>() instead.")]
|
||||
public class ThemeManager
|
||||
{
|
||||
private static Theme instance;
|
||||
private static object syncObject = new object();
|
||||
|
||||
public static Theme Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new Theme();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
=> Ioc.Default.GetRequiredService<Theme>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,23 +22,26 @@ namespace Flow.Launcher.Core
|
|||
{
|
||||
public class Updater
|
||||
{
|
||||
public string GitHubRepository { get; }
|
||||
public string GitHubRepository { get; init; }
|
||||
|
||||
public Updater(string gitHubRepository)
|
||||
private readonly IPublicAPI _api;
|
||||
|
||||
public Updater(IPublicAPI publicAPI, string gitHubRepository)
|
||||
{
|
||||
_api = publicAPI;
|
||||
GitHubRepository = gitHubRepository;
|
||||
}
|
||||
|
||||
private SemaphoreSlim UpdateLock { get; } = new SemaphoreSlim(1);
|
||||
|
||||
public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true)
|
||||
public async Task UpdateAppAsync(bool silentUpdate = true)
|
||||
{
|
||||
await UpdateLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg(api.GetTranslation("pleaseWait"),
|
||||
api.GetTranslation("update_flowlauncher_update_check"));
|
||||
_api.ShowMsg(_api.GetTranslation("pleaseWait"),
|
||||
_api.GetTranslation("update_flowlauncher_update_check"));
|
||||
|
||||
using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false);
|
||||
|
||||
|
|
@ -53,13 +56,13 @@ namespace Flow.Launcher.Core
|
|||
if (newReleaseVersion <= currentVersion)
|
||||
{
|
||||
if (!silentUpdate)
|
||||
MessageBoxEx.Show(api.GetTranslation("update_flowlauncher_already_on_latest"));
|
||||
_api.ShowMsgBox(_api.GetTranslation("update_flowlauncher_already_on_latest"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg(api.GetTranslation("update_flowlauncher_update_found"),
|
||||
api.GetTranslation("update_flowlauncher_updating"));
|
||||
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_update_found"),
|
||||
_api.GetTranslation("update_flowlauncher_updating"));
|
||||
|
||||
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
|
||||
|
||||
|
|
@ -68,9 +71,9 @@ namespace Flow.Launcher.Core
|
|||
if (DataLocation.PortableDataLocationInUse())
|
||||
{
|
||||
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
|
||||
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show);
|
||||
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show))
|
||||
MessageBoxEx.Show(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
|
||||
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s));
|
||||
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)))
|
||||
_api.ShowMsgBox(string.Format(_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
|
||||
DataLocation.PortableDataPath,
|
||||
targetDestination));
|
||||
}
|
||||
|
|
@ -83,7 +86,7 @@ namespace Flow.Launcher.Core
|
|||
|
||||
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
|
||||
|
||||
if (MessageBoxEx.Show(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
}
|
||||
|
|
@ -96,8 +99,8 @@ namespace Flow.Launcher.Core
|
|||
Log.Exception($"|Updater.UpdateApp|Error Occurred", e);
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"),
|
||||
api.GetTranslation("update_flowlauncher_check_connection"));
|
||||
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"),
|
||||
_api.GetTranslation("update_flowlauncher_check_connection"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
|
||||
<PackageReference Include="BitFaster.Caching" Version="2.5.3" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Fody" Version="6.5.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
|||
using System;
|
||||
using System.Threading;
|
||||
using Flow.Launcher.Plugin;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Http
|
||||
{
|
||||
|
|
@ -17,8 +18,6 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
|
||||
private static HttpClient client = new HttpClient();
|
||||
|
||||
public static IPublicAPI API { get; set; }
|
||||
|
||||
static Http()
|
||||
{
|
||||
// need to be added so it would work on a win10 machine
|
||||
|
|
@ -78,7 +77,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
}
|
||||
catch (UriFormatException e)
|
||||
{
|
||||
API.ShowMsg("Please try again", "Unable to parse Http Proxy");
|
||||
Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsg("Please try again", "Unable to parse Http Proxy");
|
||||
Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Text;
|
|||
using JetBrains.Annotations;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using ToolGood.Words.Pinyin;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
|
|
@ -129,7 +130,12 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
private Settings _settings;
|
||||
|
||||
public void Initialize([NotNull] Settings settings)
|
||||
public PinyinAlphabet()
|
||||
{
|
||||
Initialize(Ioc.Default.GetRequiredService<Settings>());
|
||||
}
|
||||
|
||||
private void Initialize([NotNull] Settings settings)
|
||||
{
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
protected JsonStorage()
|
||||
{
|
||||
}
|
||||
|
||||
public JsonStorage(string filePath)
|
||||
{
|
||||
FilePath = filePath;
|
||||
DirectoryPath = Path.GetDirectoryName(filePath) ?? throw new ArgumentException("Invalid file path");
|
||||
|
||||
|
||||
Helper.ValidateDirectory(DirectoryPath);
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +98,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreBackup()
|
||||
{
|
||||
Log.Info($"|JsonStorage.Load|Failed to load settings.json, {BackupFilePath} restored successfully");
|
||||
|
|
@ -179,25 +181,21 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
public void Save()
|
||||
{
|
||||
string serialized = JsonSerializer.Serialize(Data,
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
new JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
File.WriteAllText(TempFilePath, serialized);
|
||||
|
||||
AtomicWriteSetting();
|
||||
}
|
||||
|
||||
public async Task SaveAsync()
|
||||
{
|
||||
var tempOutput = File.OpenWrite(TempFilePath);
|
||||
await using var tempOutput = File.OpenWrite(TempFilePath);
|
||||
await JsonSerializer.SerializeAsync(tempOutput, Data,
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
new JsonSerializerOptions { WriteIndented = true });
|
||||
AtomicWriteSetting();
|
||||
}
|
||||
|
||||
private void AtomicWriteSetting()
|
||||
{
|
||||
if (!File.Exists(FilePath))
|
||||
|
|
@ -206,9 +204,9 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
}
|
||||
else
|
||||
{
|
||||
File.Replace(TempFilePath, FilePath, BackupFilePath);
|
||||
var finalFilePath = new FileInfo(FilePath).LinkTarget ?? FilePath;
|
||||
File.Replace(TempFilePath, finalFilePath, BackupFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
|||
|
||||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
{
|
||||
public class PluginJsonStorage<T> :JsonStorage<T> where T : new()
|
||||
public class PluginJsonStorage<T> : JsonStorage<T> where T : new()
|
||||
{
|
||||
// Use assembly name to check which plugin is using this storage
|
||||
public readonly string AssemblyName;
|
||||
|
||||
public PluginJsonStorage()
|
||||
{
|
||||
// C# related, add python related below
|
||||
var dataType = typeof(T);
|
||||
var assemblyName = dataType.Assembly.GetName().Name;
|
||||
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, assemblyName);
|
||||
AssemblyName = dataType.Assembly.GetName().Name;
|
||||
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, AssemblyName);
|
||||
Helper.ValidateDirectory(DirectoryPath);
|
||||
|
||||
FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
|
||||
|
|
@ -20,6 +23,13 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public void DeleteDirectory()
|
||||
{
|
||||
if (Directory.Exists(DirectoryPath))
|
||||
{
|
||||
Directory.Delete(DirectoryPath, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,28 +1,35 @@
|
|||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
public class StringMatcher
|
||||
{
|
||||
private readonly MatchOption _defaultMatchOption = new MatchOption();
|
||||
private readonly MatchOption _defaultMatchOption = new();
|
||||
|
||||
public SearchPrecisionScore UserSettingSearchPrecision { get; set; }
|
||||
|
||||
private readonly IAlphabet _alphabet;
|
||||
|
||||
public StringMatcher(IAlphabet alphabet = null)
|
||||
public StringMatcher(IAlphabet alphabet, Settings settings)
|
||||
{
|
||||
_alphabet = alphabet;
|
||||
UserSettingSearchPrecision = settings.QuerySearchPrecision;
|
||||
}
|
||||
|
||||
// This is a workaround to allow unit tests to set the instance
|
||||
public StringMatcher(IAlphabet alphabet)
|
||||
{
|
||||
_alphabet = alphabet;
|
||||
}
|
||||
|
||||
public static StringMatcher Instance { get; internal set; }
|
||||
|
||||
public static MatchResult FuzzySearch(string query, string stringToCompare)
|
||||
{
|
||||
return Instance.FuzzyMatch(query, stringToCompare);
|
||||
return Ioc.Default.GetRequiredService<StringMatcher>().FuzzyMatch(query, stringToCompare);
|
||||
}
|
||||
|
||||
public MatchResult FuzzyMatch(string query, string stringToCompare)
|
||||
|
|
@ -241,16 +248,16 @@ namespace Flow.Launcher.Infrastructure
|
|||
return false;
|
||||
}
|
||||
|
||||
private bool IsAcronymChar(string stringToCompare, int compareStringIndex)
|
||||
private static bool IsAcronymChar(string stringToCompare, int compareStringIndex)
|
||||
=> char.IsUpper(stringToCompare[compareStringIndex]) ||
|
||||
compareStringIndex == 0 || // 0 index means char is the start of the compare string, which is an acronym
|
||||
char.IsWhiteSpace(stringToCompare[compareStringIndex - 1]);
|
||||
|
||||
private bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
|
||||
private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
|
||||
=> stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9;
|
||||
|
||||
// To get the index of the closest space which preceeds the first matching index
|
||||
private int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchIndex)
|
||||
private static int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchIndex)
|
||||
{
|
||||
var closestSpaceIndex = -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
|
@ -13,6 +14,24 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
{
|
||||
public class Settings : BaseModel, IHotkeySettings
|
||||
{
|
||||
private FlowLauncherJsonStorage<Settings> _storage;
|
||||
private StringMatcher _stringMatcher = null;
|
||||
|
||||
public void SetStorage(FlowLauncherJsonStorage<Settings> storage)
|
||||
{
|
||||
_storage = storage;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_stringMatcher = Ioc.Default.GetRequiredService<StringMatcher>();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_storage.Save();
|
||||
}
|
||||
|
||||
private string language = Constant.SystemLanguageCode;
|
||||
private string _theme = Constant.DefaultTheme;
|
||||
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
|
||||
|
|
@ -180,7 +199,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// when false Alphabet static service will always return empty results
|
||||
/// </summary>
|
||||
|
|
@ -198,8 +216,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
set
|
||||
{
|
||||
_querySearchPrecision = value;
|
||||
if (StringMatcher.Instance != null)
|
||||
StringMatcher.Instance.UserSettingSearchPrecision = value;
|
||||
if (_stringMatcher != null)
|
||||
_stringMatcher.UserSettingSearchPrecision = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +256,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool EnableUpdateLog { get; set; }
|
||||
|
||||
public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
|
||||
public bool UseLogonTaskForStartup { get; set; } = false;
|
||||
public bool HideOnStartup { get; set; } = true;
|
||||
bool _hideNotifyIcon { get; set; }
|
||||
public bool HideNotifyIcon
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ namespace Flow.Launcher.Plugin
|
|||
public interface IPublicAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// Change Flow.Launcher query
|
||||
/// Change Flow.Launcher query.
|
||||
/// When current results are from context menu or history, it will go back to query results before changing query.
|
||||
/// </summary>
|
||||
/// <param name="query">query text</param>
|
||||
/// <param name="requery">
|
||||
|
|
@ -299,7 +300,7 @@ namespace Flow.Launcher.Plugin
|
|||
|
||||
/// <summary>
|
||||
/// Reloads the query.
|
||||
/// This method should run when selected item is from query results.
|
||||
/// When current results are from context menu or history, it will go back to query results before requerying.
|
||||
/// </summary>
|
||||
/// <param name="reselect">Choose the first result after reload if true; keep the last selected result if false. Default is true.</param>
|
||||
public void ReQuery(bool reselect = true);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
|
||||
namespace Flow.Launcher.Test
|
||||
{
|
||||
|
|
@ -35,7 +36,7 @@ namespace Flow.Launcher.Test
|
|||
[TestCase(@"c:\barr", @"c:\foo\..\bar\baz", false)]
|
||||
public void GivenTwoPaths_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path));
|
||||
ClassicAssert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path));
|
||||
}
|
||||
|
||||
// Equality
|
||||
|
|
@ -47,7 +48,7 @@ namespace Flow.Launcher.Test
|
|||
[TestCase(@"c:\foo", @"c:\foo\", true)]
|
||||
public void GivenTwoPathsAreTheSame_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, allowEqual: expectedResult));
|
||||
ClassicAssert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, allowEqual: expectedResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
<PackageReference Include="nunit" Version="3.14.0" />
|
||||
<PackageReference Include="nunit" Version="4.3.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
|
|
@ -21,6 +22,8 @@ namespace Flow.Launcher.Test
|
|||
private const string MicrosoftSqlServerManagementStudio = "Microsoft SQL Server Management Studio";
|
||||
private const string VisualStudioCode = "Visual Studio Code";
|
||||
|
||||
private readonly IAlphabet alphabet = null;
|
||||
|
||||
public List<string> GetSearchStrings()
|
||||
=> new List<string>
|
||||
{
|
||||
|
|
@ -34,7 +37,7 @@ namespace Flow.Launcher.Test
|
|||
OneOneOneOne
|
||||
};
|
||||
|
||||
public List<int> GetPrecisionScores()
|
||||
public static List<int> GetPrecisionScores()
|
||||
{
|
||||
var listToReturn = new List<int>();
|
||||
|
||||
|
|
@ -59,7 +62,7 @@ namespace Flow.Launcher.Test
|
|||
};
|
||||
|
||||
var results = new List<Result>();
|
||||
var matcher = new StringMatcher();
|
||||
var matcher = new StringMatcher(alphabet);
|
||||
foreach (var str in sources)
|
||||
{
|
||||
results.Add(new Result
|
||||
|
|
@ -71,20 +74,20 @@ namespace Flow.Launcher.Test
|
|||
|
||||
results = results.Where(x => x.Score > 0).OrderByDescending(x => x.Score).ToList();
|
||||
|
||||
Assert.IsTrue(results.Count == 3);
|
||||
Assert.IsTrue(results[0].Title == "Inste");
|
||||
Assert.IsTrue(results[1].Title == "Install Package");
|
||||
Assert.IsTrue(results[2].Title == "file open in browser-test");
|
||||
ClassicAssert.IsTrue(results.Count == 3);
|
||||
ClassicAssert.IsTrue(results[0].Title == "Inste");
|
||||
ClassicAssert.IsTrue(results[1].Title == "Install Package");
|
||||
ClassicAssert.IsTrue(results[2].Title == "file open in browser-test");
|
||||
}
|
||||
|
||||
[TestCase("Chrome")]
|
||||
public void WhenNotAllCharactersFoundInSearchString_ThenShouldReturnZeroScore(string searchString)
|
||||
{
|
||||
var compareString = "Can have rum only in my glass";
|
||||
var matcher = new StringMatcher();
|
||||
var matcher = new StringMatcher(alphabet);
|
||||
var scoreResult = matcher.FuzzyMatch(searchString, compareString).RawScore;
|
||||
|
||||
Assert.True(scoreResult == 0);
|
||||
ClassicAssert.True(scoreResult == 0);
|
||||
}
|
||||
|
||||
[TestCase("chr")]
|
||||
|
|
@ -97,7 +100,7 @@ namespace Flow.Launcher.Test
|
|||
string searchTerm)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
var matcher = new StringMatcher();
|
||||
var matcher = new StringMatcher(alphabet);
|
||||
foreach (var str in GetSearchStrings())
|
||||
{
|
||||
results.Add(new Result
|
||||
|
|
@ -125,7 +128,7 @@ namespace Flow.Launcher.Test
|
|||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
Assert.IsFalse(filteredResult.Any(x => x.Score < precisionScore));
|
||||
ClassicAssert.IsFalse(filteredResult.Any(x => x.Score < precisionScore));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,11 +150,11 @@ namespace Flow.Launcher.Test
|
|||
string queryString, string compareString, int expectedScore)
|
||||
{
|
||||
// When, Given
|
||||
var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
|
||||
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
|
||||
var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore;
|
||||
|
||||
// Should
|
||||
Assert.AreEqual(expectedScore, rawScore,
|
||||
ClassicAssert.AreEqual(expectedScore, rawScore,
|
||||
$"Expected score for compare string '{compareString}': {expectedScore}, Actual: {rawScore}");
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +184,7 @@ namespace Flow.Launcher.Test
|
|||
bool expectedPrecisionResult)
|
||||
{
|
||||
// When
|
||||
var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore};
|
||||
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore};
|
||||
|
||||
// Given
|
||||
var matchResult = matcher.FuzzyMatch(queryString, compareString);
|
||||
|
|
@ -190,12 +193,12 @@ namespace Flow.Launcher.Test
|
|||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}");
|
||||
Debug.WriteLine(
|
||||
$"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})");
|
||||
$"RAW SCORE: {matchResult.RawScore}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
// Should
|
||||
Assert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(),
|
||||
ClassicAssert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(),
|
||||
$"Query: {queryString}{Environment.NewLine} " +
|
||||
$"Compare: {compareString}{Environment.NewLine}" +
|
||||
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
|
||||
|
|
@ -232,7 +235,7 @@ namespace Flow.Launcher.Test
|
|||
bool expectedPrecisionResult)
|
||||
{
|
||||
// When
|
||||
var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore};
|
||||
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore};
|
||||
|
||||
// Given
|
||||
var matchResult = matcher.FuzzyMatch(queryString, compareString);
|
||||
|
|
@ -241,12 +244,12 @@ namespace Flow.Launcher.Test
|
|||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}");
|
||||
Debug.WriteLine(
|
||||
$"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})");
|
||||
$"RAW SCORE: {matchResult.RawScore}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
// Should
|
||||
Assert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(),
|
||||
ClassicAssert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(),
|
||||
$"Query:{queryString}{Environment.NewLine} " +
|
||||
$"Compare:{compareString}{Environment.NewLine}" +
|
||||
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
|
||||
|
|
@ -260,7 +263,7 @@ namespace Flow.Launcher.Test
|
|||
string queryString, string compareString1, string compareString2)
|
||||
{
|
||||
// When
|
||||
var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
|
||||
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
|
||||
|
||||
// Given
|
||||
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
|
||||
|
|
@ -277,7 +280,7 @@ namespace Flow.Launcher.Test
|
|||
Debug.WriteLine("");
|
||||
|
||||
// Should
|
||||
Assert.True(compareString1Result.Score > compareString2Result.Score,
|
||||
ClassicAssert.True(compareString1Result.Score > compareString2Result.Score,
|
||||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
|
||||
$"Should be greater than{Environment.NewLine}" +
|
||||
|
|
@ -293,7 +296,7 @@ namespace Flow.Launcher.Test
|
|||
string queryString, string compareString1, string compareString2)
|
||||
{
|
||||
// When
|
||||
var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
|
||||
var matcher = new StringMatcher(alphabet) { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
|
||||
|
||||
// Given
|
||||
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
|
||||
|
|
@ -310,7 +313,7 @@ namespace Flow.Launcher.Test
|
|||
Debug.WriteLine("");
|
||||
|
||||
// Should
|
||||
Assert.True(compareString1Result.Score > compareString2Result.Score,
|
||||
ClassicAssert.True(compareString1Result.Score > compareString2Result.Score,
|
||||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
|
||||
$"Should be greater than{Environment.NewLine}" +
|
||||
|
|
@ -323,7 +326,7 @@ namespace Flow.Launcher.Test
|
|||
string secondName, string secondDescription, string secondExecutableName)
|
||||
{
|
||||
// Act
|
||||
var matcher = new StringMatcher();
|
||||
var matcher = new StringMatcher(alphabet);
|
||||
var firstNameMatch = matcher.FuzzyMatch(queryString, firstName).RawScore;
|
||||
var firstDescriptionMatch = matcher.FuzzyMatch(queryString, firstDescription).RawScore;
|
||||
var firstExecutableNameMatch = matcher.FuzzyMatch(queryString, firstExecutableName).RawScore;
|
||||
|
|
@ -336,7 +339,7 @@ namespace Flow.Launcher.Test
|
|||
var secondScore = new[] {secondNameMatch, secondDescriptionMatch, secondExecutableNameMatch}.Max();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(firstScore > secondScore,
|
||||
ClassicAssert.IsTrue(firstScore > secondScore,
|
||||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"Name of first: \"{firstName}\", Final Score: {firstScore}{Environment.NewLine}" +
|
||||
$"Should be greater than{Environment.NewLine}" +
|
||||
|
|
@ -358,9 +361,9 @@ namespace Flow.Launcher.Test
|
|||
public void WhenGivenAnAcronymQuery_ShouldReturnAcronymScore(string queryString, string compareString,
|
||||
int desiredScore)
|
||||
{
|
||||
var matcher = new StringMatcher();
|
||||
var matcher = new StringMatcher(alphabet);
|
||||
var score = matcher.FuzzyMatch(queryString, compareString).Score;
|
||||
Assert.IsTrue(score == desiredScore,
|
||||
ClassicAssert.IsTrue(score == desiredScore,
|
||||
$@"Query: ""{queryString}""
|
||||
CompareString: ""{compareString}""
|
||||
Score: {score}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using System;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
|
|
@ -16,16 +17,16 @@ namespace Flow.Launcher.Test
|
|||
|
||||
proxy.Enabled = true;
|
||||
proxy.Server = "127.0.0.1";
|
||||
Assert.AreEqual(Http.WebProxy.Address, new Uri($"http://{proxy.Server}:{proxy.Port}"));
|
||||
Assert.IsNull(Http.WebProxy.Credentials);
|
||||
ClassicAssert.AreEqual(Http.WebProxy.Address, new Uri($"http://{proxy.Server}:{proxy.Port}"));
|
||||
ClassicAssert.IsNull(Http.WebProxy.Credentials);
|
||||
|
||||
proxy.UserName = "test";
|
||||
Assert.NotNull(Http.WebProxy.Credentials);
|
||||
Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").UserName, proxy.UserName);
|
||||
Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, "");
|
||||
ClassicAssert.NotNull(Http.WebProxy.Credentials);
|
||||
ClassicAssert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").UserName, proxy.UserName);
|
||||
ClassicAssert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, "");
|
||||
|
||||
proxy.Password = "test password";
|
||||
Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, proxy.Password);
|
||||
ClassicAssert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, proxy.Password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -15,37 +16,37 @@ namespace Flow.Launcher.Test
|
|||
// Given
|
||||
var duplicateList = new List<PluginMetadata>
|
||||
{
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.1"
|
||||
},
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.2"
|
||||
},
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "ABC0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "ABC0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
|
|
@ -56,11 +57,11 @@ namespace Flow.Launcher.Test
|
|||
(var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList);
|
||||
|
||||
// Then
|
||||
Assert.True(unique.FirstOrDefault().ID == "CEA0TYUC6D3B4085823D60DC76F28855" && unique.FirstOrDefault().Version == "1.0.2");
|
||||
Assert.True(unique.Count() == 1);
|
||||
ClassicAssert.True(unique.FirstOrDefault().ID == "CEA0TYUC6D3B4085823D60DC76F28855" && unique.FirstOrDefault().Version == "1.0.2");
|
||||
ClassicAssert.True(unique.Count == 1);
|
||||
|
||||
Assert.False(duplicates.Any(x => x.Version == "1.0.2" && x.ID == "CEA0TYUC6D3B4085823D60DC76F28855"));
|
||||
Assert.True(duplicates.Count() == 6);
|
||||
ClassicAssert.False(duplicates.Any(x => x.Version == "1.0.2" && x.ID == "CEA0TYUC6D3B4085823D60DC76F28855"));
|
||||
ClassicAssert.True(duplicates.Count == 6);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -69,12 +70,12 @@ namespace Flow.Launcher.Test
|
|||
// Given
|
||||
var duplicateList = new List<PluginMetadata>
|
||||
{
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "CEA0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
new()
|
||||
{
|
||||
ID = "CEA0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
|
|
@ -85,8 +86,8 @@ namespace Flow.Launcher.Test
|
|||
(var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList);
|
||||
|
||||
// Then
|
||||
Assert.True(unique.Count() == 0);
|
||||
Assert.True(duplicates.Count() == 2);
|
||||
ClassicAssert.True(unique.Count == 0);
|
||||
ClassicAssert.True(duplicates.Count == 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@ using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo;
|
|||
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Flow.Launcher.Plugin.Explorer.Search.SearchManager;
|
||||
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
|
|
@ -22,28 +20,6 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestFixture]
|
||||
public class ExplorerTest
|
||||
{
|
||||
#pragma warning disable CS1998 // async method with no await (more readable to leave it async to match the tested signature)
|
||||
private async Task<List<Result>> MethodWindowsIndexSearchReturnsZeroResultsAsync(Query dummyQuery, string dummyString, CancellationToken dummyToken)
|
||||
{
|
||||
return new List<Result>();
|
||||
}
|
||||
#pragma warning restore CS1998
|
||||
|
||||
private List<Result> MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString, CancellationToken token)
|
||||
{
|
||||
return new List<Result>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title = "Result 1"
|
||||
},
|
||||
new Result
|
||||
{
|
||||
Title = "Result 2"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private bool PreviousLocationExistsReturnsTrue(string dummyString) => true;
|
||||
|
||||
private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false;
|
||||
|
|
@ -57,7 +33,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = QueryConstructor.TopLevelDirectoryConstraint(folderPath);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(result == expectedString,
|
||||
ClassicAssert.IsTrue(result == expectedString,
|
||||
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual: {result}{Environment.NewLine}");
|
||||
}
|
||||
|
|
@ -74,7 +50,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryString = queryConstructor.Directory(folderPath);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(queryString.Replace(" ", " ") == expectedString.Replace(" ", " "),
|
||||
ClassicAssert.IsTrue(queryString.Replace(" ", " ") == expectedString.Replace(" ", " "),
|
||||
$"Expected string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {queryString}{Environment.NewLine}");
|
||||
}
|
||||
|
|
@ -94,7 +70,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryString = queryConstructor.Directory(folderPath, userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(expectedString, queryString);
|
||||
ClassicAssert.AreEqual(expectedString, queryString);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
|
|
@ -105,7 +81,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
const string resultString = QueryConstructor.RestrictionsForAllFilesAndFoldersSearch;
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
ClassicAssert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
|
|
@ -128,7 +104,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var resultString = queryConstructor.FilesAndFolders(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
ClassicAssert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -138,13 +114,13 @@ namespace Flow.Launcher.Test.Plugins
|
|||
string querySearchString, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
_ = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = QueryConstructor.RestrictionsForFileContentSearch(querySearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
ClassicAssert.IsTrue(resultString == expectedString,
|
||||
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
|
@ -162,12 +138,12 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var resultString = queryConstructor.FileContent(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
ClassicAssert.IsTrue(resultString == expectedString,
|
||||
$"Expected query string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue()
|
||||
public static void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue()
|
||||
{
|
||||
// Given
|
||||
var query = new Query
|
||||
|
|
@ -181,7 +157,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = searchManager.IsFileContentSearch(query.ActionKeyword);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(result,
|
||||
ClassicAssert.IsTrue(result,
|
||||
$"Expected True for file content search. {Environment.NewLine} " +
|
||||
$"Actual result was: {result}{Environment.NewLine}");
|
||||
}
|
||||
|
|
@ -206,7 +182,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = FilesFolders.IsLocationPathString(querySearchString);
|
||||
|
||||
//Then
|
||||
Assert.IsTrue(result == expectedResult,
|
||||
ClassicAssert.IsTrue(result == expectedResult,
|
||||
$"Expected query search string check result is: {expectedResult} {Environment.NewLine} " +
|
||||
$"Actual check result is {result} {Environment.NewLine}");
|
||||
|
||||
|
|
@ -233,7 +209,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var previousDirectoryPath = FilesFolders.GetPreviousExistingDirectory(previousLocationExists, path);
|
||||
|
||||
//Then
|
||||
Assert.IsTrue(previousDirectoryPath == expectedString,
|
||||
ClassicAssert.IsTrue(previousDirectoryPath == expectedString,
|
||||
$"Expected path string: {expectedString} {Environment.NewLine} " +
|
||||
$"Actual path string is {previousDirectoryPath} {Environment.NewLine}");
|
||||
}
|
||||
|
|
@ -246,7 +222,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var returnedPath = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path);
|
||||
|
||||
//Then
|
||||
Assert.IsTrue(returnedPath == expectedString,
|
||||
ClassicAssert.IsTrue(returnedPath == expectedString,
|
||||
$"Expected path string: {expectedString} {Environment.NewLine} " +
|
||||
$"Actual path string is {returnedPath} {Environment.NewLine}");
|
||||
}
|
||||
|
|
@ -260,7 +236,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var resultString = QueryConstructor.RecursiveDirectoryConstraint(path);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
ClassicAssert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
|
|
@ -274,7 +250,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var resultString = DirectoryInfoSearch.ConstructSearchCriteria(path);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
ClassicAssert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
[TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", false, true, "c:\\somefolder\\someotherfolder\\")]
|
||||
|
|
@ -305,7 +281,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
ClassicAssert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, true, "e c:\\somefolder\\somefile")]
|
||||
|
|
@ -334,7 +310,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
ClassicAssert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "q", false, false, "q somefolder")]
|
||||
|
|
@ -366,7 +342,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = ResultManager.GetAutoCompleteText(title, query, path, resultType);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
ClassicAssert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "q", false, false, "q somefile")]
|
||||
|
|
@ -398,7 +374,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = ResultManager.GetAutoCompleteText(title, query, path, resultType);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
ClassicAssert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo", @"c:\foo", true)]
|
||||
|
|
@ -420,7 +396,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
};
|
||||
|
||||
// When, Then
|
||||
Assert.AreEqual(expectedResult, comparator.Equals(result1, result2));
|
||||
ClassicAssert.AreEqual(expectedResult, comparator.Equals(result1, result2));
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo\", @"c:\foo\")]
|
||||
|
|
@ -444,7 +420,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var hash2 = comparator.GetHashCode(result2);
|
||||
|
||||
// When, Then
|
||||
Assert.IsTrue(hash1 == hash2);
|
||||
ClassicAssert.IsTrue(hash1 == hash2);
|
||||
}
|
||||
|
||||
[TestCase(@"%appdata%", true)]
|
||||
|
|
@ -461,7 +437,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var result = EnvironmentVariables.HasEnvironmentVar(path);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
ClassicAssert.AreEqual(result, expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
|
|
@ -40,13 +39,13 @@ namespace Flow.Launcher.Test.Plugins
|
|||
Search = resultText
|
||||
}, default);
|
||||
|
||||
Assert.IsNotNull(results);
|
||||
ClassicAssert.IsNotNull(results);
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
Assert.IsNotNull(result);
|
||||
Assert.IsNotNull(result.AsyncAction);
|
||||
Assert.IsNotNull(result.Title);
|
||||
ClassicAssert.IsNotNull(result);
|
||||
ClassicAssert.IsNotNull(result.AsyncAction);
|
||||
ClassicAssert.IsNotNull(result.Title);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -56,12 +55,11 @@ namespace Flow.Launcher.Test.Plugins
|
|||
new JsonRPCQueryResponseModel(0, new List<JsonRPCResult>()),
|
||||
new JsonRPCQueryResponseModel(0, new List<JsonRPCResult>
|
||||
{
|
||||
new JsonRPCResult
|
||||
new()
|
||||
{
|
||||
Title = "Test1", SubTitle = "Test2"
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using Flow.Launcher.Plugin.Url;
|
||||
|
||||
namespace Flow.Launcher.Test
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
{
|
||||
[TestFixture]
|
||||
public class UrlPluginTest
|
||||
|
|
@ -10,23 +11,23 @@ namespace Flow.Launcher.Test
|
|||
public void URLMatchTest()
|
||||
{
|
||||
var plugin = new Main();
|
||||
Assert.IsTrue(plugin.IsURL("http://www.google.com"));
|
||||
Assert.IsTrue(plugin.IsURL("https://www.google.com"));
|
||||
Assert.IsTrue(plugin.IsURL("http://google.com"));
|
||||
Assert.IsTrue(plugin.IsURL("www.google.com"));
|
||||
Assert.IsTrue(plugin.IsURL("google.com"));
|
||||
Assert.IsTrue(plugin.IsURL("http://localhost"));
|
||||
Assert.IsTrue(plugin.IsURL("https://localhost"));
|
||||
Assert.IsTrue(plugin.IsURL("http://localhost:80"));
|
||||
Assert.IsTrue(plugin.IsURL("https://localhost:80"));
|
||||
Assert.IsTrue(plugin.IsURL("http://110.10.10.10"));
|
||||
Assert.IsTrue(plugin.IsURL("110.10.10.10"));
|
||||
Assert.IsTrue(plugin.IsURL("ftp://110.10.10.10"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("http://www.google.com"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("https://www.google.com"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("http://google.com"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("www.google.com"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("google.com"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("http://localhost"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("https://localhost"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("http://localhost:80"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("https://localhost:80"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("http://110.10.10.10"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("110.10.10.10"));
|
||||
ClassicAssert.IsTrue(plugin.IsURL("ftp://110.10.10.10"));
|
||||
|
||||
|
||||
Assert.IsFalse(plugin.IsURL("wwww"));
|
||||
Assert.IsFalse(plugin.IsURL("wwww.c"));
|
||||
Assert.IsFalse(plugin.IsURL("wwww.c"));
|
||||
ClassicAssert.IsFalse(plugin.IsURL("wwww"));
|
||||
ClassicAssert.IsFalse(plugin.IsURL("wwww.c"));
|
||||
ClassicAssert.IsFalse(plugin.IsURL("wwww.c"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Legacy;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -17,17 +18,17 @@ namespace Flow.Launcher.Test
|
|||
|
||||
Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins);
|
||||
|
||||
Assert.AreEqual("> ping google.com -n 20 -6", q.RawQuery);
|
||||
Assert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword.");
|
||||
Assert.AreEqual(">", q.ActionKeyword);
|
||||
ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.RawQuery);
|
||||
ClassicAssert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword.");
|
||||
ClassicAssert.AreEqual(">", q.ActionKeyword);
|
||||
|
||||
Assert.AreEqual(5, q.SearchTerms.Length, "The length of SearchTerms should match.");
|
||||
ClassicAssert.AreEqual(5, q.SearchTerms.Length, "The length of SearchTerms should match.");
|
||||
|
||||
Assert.AreEqual("ping", q.FirstSearch);
|
||||
Assert.AreEqual("google.com", q.SecondSearch);
|
||||
Assert.AreEqual("-n", q.ThirdSearch);
|
||||
ClassicAssert.AreEqual("ping", q.FirstSearch);
|
||||
ClassicAssert.AreEqual("google.com", q.SecondSearch);
|
||||
ClassicAssert.AreEqual("-n", q.ThirdSearch);
|
||||
|
||||
Assert.AreEqual("google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters");
|
||||
ClassicAssert.AreEqual("google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -40,11 +41,11 @@ namespace Flow.Launcher.Test
|
|||
|
||||
Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins);
|
||||
|
||||
Assert.AreEqual("> ping google.com -n 20 -6", q.Search);
|
||||
Assert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search.");
|
||||
Assert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match.");
|
||||
Assert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin.");
|
||||
Assert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters");
|
||||
ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.Search);
|
||||
ClassicAssert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search.");
|
||||
ClassicAssert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match.");
|
||||
ClassicAssert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin.");
|
||||
ClassicAssert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -52,13 +53,13 @@ namespace Flow.Launcher.Test
|
|||
{
|
||||
Query q = QueryBuilder.Build("file.txt file2 file3", new Dictionary<string, PluginPair>());
|
||||
|
||||
Assert.AreEqual("file.txt file2 file3", q.Search);
|
||||
Assert.AreEqual("", q.ActionKeyword);
|
||||
ClassicAssert.AreEqual("file.txt file2 file3", q.Search);
|
||||
ClassicAssert.AreEqual("", q.ActionKeyword);
|
||||
|
||||
Assert.AreEqual("file.txt", q.FirstSearch);
|
||||
Assert.AreEqual("file2", q.SecondSearch);
|
||||
Assert.AreEqual("file3", q.ThirdSearch);
|
||||
Assert.AreEqual("file2 file3", q.SecondToEndSearch);
|
||||
ClassicAssert.AreEqual("file.txt", q.FirstSearch);
|
||||
ClassicAssert.AreEqual("file2", q.SecondSearch);
|
||||
ClassicAssert.AreEqual("file3", q.ThirdSearch);
|
||||
ClassicAssert.AreEqual("file2 file3", q.SecondToEndSearch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace Flow.Launcher
|
|||
else
|
||||
{
|
||||
string msg = translater.GetTranslation("newActionKeywordsHasBeenAssigned");
|
||||
MessageBoxEx.Show(msg);
|
||||
App.API.ShowMsgBox(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Configuration;
|
||||
using Flow.Launcher.Core.ExternalPlugins.Environments;
|
||||
|
|
@ -14,24 +15,52 @@ using Flow.Launcher.Infrastructure;
|
|||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class App : IDisposable, ISingleInstanceApp
|
||||
{
|
||||
public static PublicAPIInstance API { get; private set; }
|
||||
public static IPublicAPI API { get; private set; }
|
||||
private const string Unique = "Flow.Launcher_Unique_Application_Mutex";
|
||||
private static bool _disposed;
|
||||
private Settings _settings;
|
||||
private MainViewModel _mainVM;
|
||||
private SettingWindowViewModel _settingsVM;
|
||||
private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo);
|
||||
private readonly Portable _portable = new Portable();
|
||||
private readonly PinyinAlphabet _alphabet = new PinyinAlphabet();
|
||||
private StringMatcher _stringMatcher;
|
||||
private readonly Settings _settings;
|
||||
|
||||
public App()
|
||||
{
|
||||
// Initialize settings
|
||||
var storage = new FlowLauncherJsonStorage<Settings>();
|
||||
_settings = storage.Load();
|
||||
_settings.SetStorage(storage);
|
||||
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
|
||||
|
||||
// Configure the dependency injection container
|
||||
var host = Host.CreateDefaultBuilder()
|
||||
.UseContentRoot(AppContext.BaseDirectory)
|
||||
.ConfigureServices(services => services
|
||||
.AddSingleton(_ => _settings)
|
||||
.AddSingleton(sp => new Updater(sp.GetRequiredService<IPublicAPI>(), Launcher.Properties.Settings.Default.GithubRepo))
|
||||
.AddSingleton<Portable>()
|
||||
.AddSingleton<SettingWindowViewModel>()
|
||||
.AddSingleton<IAlphabet, PinyinAlphabet>()
|
||||
.AddSingleton<StringMatcher>()
|
||||
.AddSingleton<Internationalization>()
|
||||
.AddSingleton<IPublicAPI, PublicAPIInstance>()
|
||||
.AddSingleton<MainViewModel>()
|
||||
.AddSingleton<Theme>()
|
||||
).Build();
|
||||
Ioc.Default.ConfigureServices(host.Services);
|
||||
|
||||
// Initialize the public API and Settings first
|
||||
API = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
_settings.Initialize();
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
|
|
@ -50,52 +79,40 @@ namespace Flow.Launcher
|
|||
{
|
||||
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
|
||||
{
|
||||
_portable.PreStartCleanUpAfterPortabilityUpdate();
|
||||
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();
|
||||
|
||||
Log.Info(
|
||||
"|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
|
||||
Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
|
||||
Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}");
|
||||
|
||||
RegisterAppDomainExceptions();
|
||||
RegisterDispatcherUnhandledException();
|
||||
|
||||
var imageLoadertask = ImageLoader.InitializeAsync();
|
||||
|
||||
_settingsVM = new SettingWindowViewModel(_updater, _portable);
|
||||
_settings = _settingsVM.Settings;
|
||||
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
|
||||
|
||||
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
|
||||
|
||||
_alphabet.Initialize(_settings);
|
||||
_stringMatcher = new StringMatcher(_alphabet);
|
||||
StringMatcher.Instance = _stringMatcher;
|
||||
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
|
||||
|
||||
InternationalizationManager.Instance.Settings = _settings;
|
||||
// TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future
|
||||
InternationalizationManager.Instance.ChangeLanguage(_settings.Language);
|
||||
|
||||
PluginManager.LoadPlugins(_settings.PluginSettings);
|
||||
_mainVM = new MainViewModel(_settings);
|
||||
|
||||
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
|
||||
|
||||
Http.API = API;
|
||||
Http.Proxy = _settings.Proxy;
|
||||
|
||||
await PluginManager.InitializePluginsAsync(API);
|
||||
await PluginManager.InitializePluginsAsync();
|
||||
await imageLoadertask;
|
||||
|
||||
var window = new MainWindow(_settings, _mainVM);
|
||||
var mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
|
||||
var window = new MainWindow(_settings, mainVM);
|
||||
|
||||
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
|
||||
|
||||
Current.MainWindow = window;
|
||||
Current.MainWindow.Title = Constant.FlowLauncher;
|
||||
|
||||
HotKeyMapper.Initialize(_mainVM);
|
||||
HotKeyMapper.Initialize();
|
||||
|
||||
// main windows needs initialized before theme change because of blur settings
|
||||
ThemeManager.Instance.Settings = _settings;
|
||||
// TODO: Clean ThemeManager.Instance in future
|
||||
ThemeManager.Instance.ChangeTheme(_settings.Theme);
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
|
@ -119,7 +136,14 @@ namespace Flow.Launcher
|
|||
{
|
||||
try
|
||||
{
|
||||
Helper.AutoStartup.Enable();
|
||||
if (_settings.UseLogonTaskForStartup)
|
||||
{
|
||||
Helper.AutoStartup.EnableViaLogonTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
Helper.AutoStartup.EnableViaRegistry();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -141,11 +165,11 @@ namespace Flow.Launcher
|
|||
{
|
||||
// check update every 5 hours
|
||||
var timer = new PeriodicTimer(TimeSpan.FromHours(5));
|
||||
await _updater.UpdateAppAsync(API);
|
||||
await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync();
|
||||
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
// check updates on startup
|
||||
await _updater.UpdateAppAsync(API);
|
||||
await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -188,7 +212,7 @@ namespace Flow.Launcher
|
|||
|
||||
public void OnSecondAppStarted()
|
||||
{
|
||||
_mainVM.Show();
|
||||
Ioc.Default.GetRequiredService<MainViewModel>().Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,14 +32,11 @@
|
|||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Grid.Column="1"
|
||||
Click="BtnCancel_OnClick"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
|
|
|
|||
|
|
@ -6,20 +6,17 @@ using System.Linq;
|
|||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Controls;
|
||||
using Flow.Launcher.Core;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class CustomQueryHotkeySetting : Window
|
||||
{
|
||||
private SettingWindow _settingWidow;
|
||||
private readonly Settings _settings;
|
||||
private bool update;
|
||||
private CustomPluginHotkey updateCustomHotkey;
|
||||
|
||||
public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings)
|
||||
public CustomQueryHotkeySetting(Settings settings)
|
||||
{
|
||||
_settingWidow = settingWidow;
|
||||
_settings = settings;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
|
@ -63,7 +60,7 @@ namespace Flow.Launcher
|
|||
o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey);
|
||||
if (updateCustomHotkey == null)
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey"));
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,14 +30,11 @@
|
|||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Grid.Column="1"
|
||||
Click="BtnCancel_OnClick"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value))
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
|
||||
return;
|
||||
}
|
||||
// Check if key is modified or adding a new one
|
||||
if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key))
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
|
||||
return;
|
||||
}
|
||||
DialogResult = !update || originalKey != Key || originalValue != Value;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
|
@ -90,6 +90,9 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="InputSimulator" Version="1.0.4" />
|
||||
<!-- Do not upgrade Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting since we are .Net7.0 -->
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
@ -101,6 +104,7 @@
|
|||
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.11.0" />
|
||||
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,31 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
public class AutoStartup
|
||||
{
|
||||
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
|
||||
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
|
||||
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
|
||||
|
||||
public static bool IsEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
// Check if logon task is enabled
|
||||
if (CheckLogonTask())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if registry is enabled
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
|
|
@ -28,12 +41,74 @@ public class AutoStartup
|
|||
}
|
||||
}
|
||||
|
||||
public static void Disable()
|
||||
private static bool CheckLogonTask()
|
||||
{
|
||||
using var taskService = new TaskService();
|
||||
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
|
||||
if (task != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the action is the same as the current executable path
|
||||
var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim();
|
||||
if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action))
|
||||
{
|
||||
UnscheduleLogonTask();
|
||||
ScheduleLogonTask();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void DisableViaLogonTaskAndRegistry()
|
||||
{
|
||||
Disable(true);
|
||||
Disable(false);
|
||||
}
|
||||
|
||||
public static void EnableViaLogonTask()
|
||||
{
|
||||
Enable(true);
|
||||
}
|
||||
|
||||
public static void EnableViaRegistry()
|
||||
{
|
||||
Enable(false);
|
||||
}
|
||||
|
||||
public static void ChangeToViaLogonTask()
|
||||
{
|
||||
Disable(false);
|
||||
Enable(true);
|
||||
}
|
||||
|
||||
public static void ChangeToViaRegistry()
|
||||
{
|
||||
Disable(true);
|
||||
Enable(false);
|
||||
}
|
||||
|
||||
private static void Disable(bool logonTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.DeleteValue(Constant.FlowLauncher, false);
|
||||
if (logonTask)
|
||||
{
|
||||
UnscheduleLogonTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.DeleteValue(Constant.FlowLauncher, false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -42,12 +117,19 @@ public class AutoStartup
|
|||
}
|
||||
}
|
||||
|
||||
internal static void Enable()
|
||||
private static void Enable(bool logonTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
|
||||
if (logonTask)
|
||||
{
|
||||
ScheduleLogonTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -55,4 +137,54 @@ public class AutoStartup
|
|||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ScheduleLogonTask()
|
||||
{
|
||||
using var td = TaskService.Instance.NewTask();
|
||||
td.RegistrationInfo.Description = LogonTaskDesc;
|
||||
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
|
||||
td.Actions.Add(Constant.ExecutablePath);
|
||||
|
||||
if (IsCurrentUserIsAdmin())
|
||||
{
|
||||
td.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
}
|
||||
|
||||
td.Settings.StopIfGoingOnBatteries = false;
|
||||
td.Settings.DisallowStartIfOnBatteries = false;
|
||||
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool UnscheduleLogonTask()
|
||||
{
|
||||
using var taskService = new TaskService();
|
||||
try
|
||||
{
|
||||
taskService.RootFolder.DeleteTask(LogonTaskName);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsCurrentUserIsAdmin()
|
||||
{
|
||||
var identity = WindowsIdentity.GetCurrent();
|
||||
var principal = new WindowsPrincipal(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ using NHotkey;
|
|||
using NHotkey.Wpf;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using Flow.Launcher.Core;
|
||||
using ChefKeys;
|
||||
using System.Globalization;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
|
|
@ -17,10 +16,10 @@ internal static class HotKeyMapper
|
|||
private static Settings _settings;
|
||||
private static MainViewModel _mainViewModel;
|
||||
|
||||
internal static void Initialize(MainViewModel mainVM)
|
||||
internal static void Initialize()
|
||||
{
|
||||
_mainViewModel = mainVM;
|
||||
_settings = _mainViewModel.Settings;
|
||||
_mainViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
|
||||
_settings = Ioc.Default.GetService<Settings>();
|
||||
|
||||
SetHotkey(_settings.Hotkey, OnToggleHotkey);
|
||||
LoadCustomPluginHotkey();
|
||||
|
|
@ -85,7 +84,7 @@ internal static class HotKeyMapper
|
|||
hotkeyStr));
|
||||
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
|
||||
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
|
||||
MessageBoxEx.Show(errorMsg, errorMsgTitle);
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@
|
|||
<system:String x:Key="portableMode">Portable Mode</system:String>
|
||||
<system:String x:Key="portableModeToolTIp">Store all settings and user data in one folder (Useful when used with removable drives or cloud services).</system:String>
|
||||
<system:String x:Key="startFlowLauncherOnSystemStartup">Start Flow Launcher on system startup</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartup">Use logon task instead of startup entry for faster startup experience</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
|
||||
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>
|
||||
|
|
@ -129,7 +131,8 @@
|
|||
<system:String x:Key="plugin_query_version">Version</system:String>
|
||||
<system:String x:Key="plugin_query_web">Website</system:String>
|
||||
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
|
||||
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Plugin Store</system:String>
|
||||
|
|
@ -146,8 +149,6 @@
|
|||
<system:String x:Key="LabelNewToolTip">This plugin has been updated within the last 7 days</system:String>
|
||||
<system:String x:Key="LabelUpdateToolTip">New Update is Available</system:String>
|
||||
|
||||
|
||||
|
||||
<!-- Setting Theme -->
|
||||
<system:String x:Key="theme">Theme</system:String>
|
||||
<system:String x:Key="appearance">Appearance</system:String>
|
||||
|
|
@ -197,7 +198,6 @@
|
|||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
|
||||
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Hotkey</system:String>
|
||||
<system:String x:Key="hotkeys">Hotkeys</system:String>
|
||||
|
|
@ -384,6 +384,9 @@
|
|||
<system:String x:Key="reportWindow_report_succeed">Report sent successfully</system:String>
|
||||
<system:String x:Key="reportWindow_report_failed">Failed to send report</system:String>
|
||||
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcher got an error</system:String>
|
||||
<system:String x:Key="reportWindow_please_open_issue">Please open new issue in</system:String>
|
||||
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Please wait...</system:String>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<Window
|
||||
x:Class="Flow.Launcher.Core.MessageBoxEx"
|
||||
x:Class="Flow.Launcher.MessageBoxEx"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Flow.Launcher.Core"
|
||||
xmlns:local="clr-namespace:Flow.Launcher"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Name="MessageBoxWindow"
|
||||
Width="420"
|
||||
|
|
@ -7,7 +7,7 @@ using Flow.Launcher.Infrastructure;
|
|||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
namespace Flow.Launcher.Core
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class MessageBoxEx : Window
|
||||
{
|
||||
|
|
@ -29,14 +29,11 @@
|
|||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Grid.Column="1"
|
||||
Click="BtnCancel_OnClick"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace Flow.Launcher
|
|||
this.pluginViewModel = pluginViewModel;
|
||||
if (plugin == null)
|
||||
{
|
||||
MessageBoxEx.Show(translater.GetTranslation("cannotFindSpecifiedPlugin"));
|
||||
App.API.ShowMsgBox(translater.GetTranslation("cannotFindSpecifiedPlugin"));
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ namespace Flow.Launcher
|
|||
else
|
||||
{
|
||||
string msg = translater.GetTranslation("invalidPriority");
|
||||
MessageBoxEx.Show(msg);
|
||||
App.API.ShowMsgBox(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,23 +25,23 @@ using Flow.Launcher.Infrastructure.Storage;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Specialized;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public class PublicAPIInstance : IPublicAPI
|
||||
{
|
||||
private readonly SettingWindowViewModel _settingsVM;
|
||||
private readonly Settings _settings;
|
||||
private readonly MainViewModel _mainVM;
|
||||
private readonly PinyinAlphabet _alphabet;
|
||||
|
||||
#region Constructor
|
||||
|
||||
public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, PinyinAlphabet alphabet)
|
||||
public PublicAPIInstance(Settings settings, MainViewModel mainVM)
|
||||
{
|
||||
_settingsVM = settingsVM;
|
||||
_settings = settings;
|
||||
_mainVM = mainVM;
|
||||
_alphabet = alphabet;
|
||||
GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
|
||||
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
|
||||
}
|
||||
|
|
@ -78,14 +78,15 @@ namespace Flow.Launcher
|
|||
|
||||
public event VisibilityChangedEventHandler VisibilityChanged { add => _mainVM.VisibilityChanged += value; remove => _mainVM.VisibilityChanged -= value; }
|
||||
|
||||
public void CheckForNewUpdate() => _settingsVM.UpdateApp();
|
||||
// Must use Ioc.Default.GetRequiredService<Updater>() to avoid circular dependency
|
||||
public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(false);
|
||||
|
||||
public void SaveAppAllSettings()
|
||||
{
|
||||
PluginManager.Save();
|
||||
_mainVM.Save();
|
||||
_settingsVM.Save();
|
||||
ImageLoader.Save();
|
||||
_settings.Save();
|
||||
_ = ImageLoader.Save();
|
||||
}
|
||||
|
||||
public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync();
|
||||
|
|
@ -105,7 +106,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
SettingWindow sw = SingletonWindowOpener.Open<SettingWindow>(this, _settingsVM);
|
||||
SettingWindow sw = SingletonWindowOpener.Open<SettingWindow>();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -189,6 +190,23 @@ namespace Flow.Launcher
|
|||
|
||||
private readonly ConcurrentDictionary<Type, object> _pluginJsonStorages = new();
|
||||
|
||||
public object RemovePluginSettings(string assemblyName)
|
||||
{
|
||||
foreach (var keyValuePair in _pluginJsonStorages)
|
||||
{
|
||||
var key = keyValuePair.Key;
|
||||
var value = keyValuePair.Value;
|
||||
var name = value.GetType().GetField("AssemblyName")?.GetValue(value)?.ToString();
|
||||
if (name == assemblyName)
|
||||
{
|
||||
_pluginJsonStorages.Remove(key, out var pluginJsonStorage);
|
||||
return pluginJsonStorage;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save plugin settings.
|
||||
/// </summary>
|
||||
|
|
@ -230,7 +248,7 @@ namespace Flow.Launcher
|
|||
public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null)
|
||||
{
|
||||
using var explorer = new Process();
|
||||
var explorerInfo = _settingsVM.Settings.CustomExplorer;
|
||||
var explorerInfo = _settings.CustomExplorer;
|
||||
|
||||
explorer.StartInfo = new ProcessStartInfo
|
||||
{
|
||||
|
|
@ -251,7 +269,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||
{
|
||||
var browserInfo = _settingsVM.Settings.CustomBrowser;
|
||||
var browserInfo = _settings.CustomBrowser;
|
||||
|
||||
var path = browserInfo.Path == "*" ? "" : browserInfo.Path;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,82 @@
|
|||
<Window x:Class="Flow.Launcher.ReportWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Icon="Images/app_error.png"
|
||||
Topmost="True"
|
||||
ResizeMode="NoResize"
|
||||
Width="600"
|
||||
Height="455"
|
||||
Title="{DynamicResource reportWindow_flowlauncher_got_an_error}"
|
||||
d:DesignHeight="300" d:DesignWidth="600" x:ClassModifier="internal">
|
||||
<RichTextBox x:Name="ErrorTextbox"
|
||||
IsDocumentEnabled="True"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
FontSize="14"
|
||||
Margin="10"
|
||||
BorderThickness="0"/>
|
||||
<Window
|
||||
x:Class="Flow.Launcher.ReportWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="{DynamicResource reportWindow_flowlauncher_got_an_error}"
|
||||
Width="600"
|
||||
Height="455"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="600"
|
||||
x:ClassModifier="internal"
|
||||
Background="{DynamicResource PopuBGColor}"
|
||||
Foreground="{DynamicResource PopupTextColor}"
|
||||
Icon="/Images/app_error.png"
|
||||
ResizeMode="NoResize"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Margin="10 4 4 4"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Source="/Images/app_error.png" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="4 0 0 0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource Color05B}"
|
||||
Text="{DynamicResource reportWindow_flowlauncher_got_an_error}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Click="BtnCancel_OnClick"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
Width="46"
|
||||
Height="32"
|
||||
Data="M 18,11 27,20 M 18,20 27,11"
|
||||
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
StrokeThickness="1">
|
||||
<Path.Style>
|
||||
<Style TargetType="Path">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
</Grid>
|
||||
<RichTextBox
|
||||
x:Name="ErrorTextbox"
|
||||
Grid.Row="1"
|
||||
Margin="10"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
IsDocumentEnabled="True"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
</Grid>
|
||||
|
||||
</Window>
|
||||
|
|
|
|||
|
|
@ -43,20 +43,21 @@ namespace Flow.Launcher
|
|||
var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();
|
||||
|
||||
var websiteUrl = exception switch
|
||||
{
|
||||
FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website),
|
||||
_ => Constant.IssuesUrl
|
||||
};
|
||||
|
||||
{
|
||||
FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website),
|
||||
_ => Constant.IssuesUrl
|
||||
};
|
||||
|
||||
var paragraph = Hyperlink("Please open new issue in: ", websiteUrl);
|
||||
paragraph.Inlines.Add($"1. upload log file: {log.FullName}\n");
|
||||
paragraph.Inlines.Add($"2. copy below exception message");
|
||||
var paragraph = Hyperlink(App.API.GetTranslation("reportWindow_please_open_issue"), websiteUrl);
|
||||
paragraph.Inlines.Add(string.Format(App.API.GetTranslation("reportWindow_upload_log"), log.FullName));
|
||||
paragraph.Inlines.Add("\n");
|
||||
paragraph.Inlines.Add(App.API.GetTranslation("reportWindow_copy_below"));
|
||||
ErrorTextbox.Document.Blocks.Add(paragraph);
|
||||
|
||||
StringBuilder content = new StringBuilder();
|
||||
content.AppendLine(ErrorReporting.RuntimeInfo());
|
||||
content.AppendLine(ErrorReporting.DependenciesInfo());
|
||||
content.AppendLine();
|
||||
content.AppendLine($"Date: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}");
|
||||
content.AppendLine("Exception:");
|
||||
content.AppendLine(exception.ToString());
|
||||
|
|
@ -65,10 +66,12 @@ namespace Flow.Launcher
|
|||
ErrorTextbox.Document.Blocks.Add(paragraph);
|
||||
}
|
||||
|
||||
private Paragraph Hyperlink(string textBeforeUrl, string url)
|
||||
private static Paragraph Hyperlink(string textBeforeUrl, string url)
|
||||
{
|
||||
var paragraph = new Paragraph();
|
||||
paragraph.Margin = new Thickness(0);
|
||||
var paragraph = new Paragraph
|
||||
{
|
||||
Margin = new Thickness(0)
|
||||
};
|
||||
|
||||
var link = new Hyperlink
|
||||
{
|
||||
|
|
@ -79,10 +82,16 @@ namespace Flow.Launcher
|
|||
link.Click += (s, e) => SearchWeb.OpenInBrowserTab(url);
|
||||
|
||||
paragraph.Inlines.Add(textBeforeUrl);
|
||||
paragraph.Inlines.Add(" ");
|
||||
paragraph.Inlines.Add(link);
|
||||
paragraph.Inlines.Add("\n");
|
||||
|
||||
return paragraph;
|
||||
}
|
||||
|
||||
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@
|
|||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Navigation;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Flow.Launcher.Resources.Pages
|
||||
{
|
||||
|
|
@ -29,5 +30,27 @@ namespace Flow.Launcher.Resources.Pages
|
|||
{
|
||||
HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey);
|
||||
}
|
||||
|
||||
public Brush PreviewBackground
|
||||
{
|
||||
get
|
||||
{
|
||||
var wallpaper = WallpaperPathRetrieval.GetWallpaperPath();
|
||||
if (wallpaper is not null && File.Exists(wallpaper))
|
||||
{
|
||||
var memStream = new MemoryStream(File.ReadAllBytes(wallpaper));
|
||||
var bitmap = new BitmapImage();
|
||||
bitmap.BeginInit();
|
||||
bitmap.StreamSource = memStream;
|
||||
bitmap.DecodePixelWidth = 800;
|
||||
bitmap.DecodePixelHeight = 600;
|
||||
bitmap.EndInit();
|
||||
return new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
|
||||
}
|
||||
|
||||
var wallpaperColor = WallpaperPathRetrieval.GetWallpaperColor();
|
||||
return new SolidColorBrush(wallpaperColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,14 +28,11 @@
|
|||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Grid.Column="1"
|
||||
Click="btnCancel_Click"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
|
|
|
|||
|
|
@ -28,14 +28,11 @@
|
|||
<StackPanel>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Grid.Column="1"
|
||||
Click="btnCancel_Click"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
|||
[RelayCommand]
|
||||
private void AskClearLogFolderConfirmation()
|
||||
{
|
||||
var confirmResult = MessageBoxEx.Show(
|
||||
var confirmResult = App.API.ShowMsgBox(
|
||||
InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"),
|
||||
InternationalizationManager.Instance.GetTranslation("clearlogfolder"),
|
||||
MessageBoxButton.YesNo
|
||||
|
|
@ -96,7 +96,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
|
|||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task UpdateApp() => _updater.UpdateAppAsync(App.API, false);
|
||||
private Task UpdateApp() => _updater.UpdateAppAsync(false);
|
||||
|
||||
private void ClearLogFolder()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,9 +42,20 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
try
|
||||
{
|
||||
if (value)
|
||||
AutoStartup.Enable();
|
||||
{
|
||||
if (UseLogonTaskForStartup)
|
||||
{
|
||||
AutoStartup.EnableViaLogonTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoStartup.EnableViaRegistry();
|
||||
}
|
||||
}
|
||||
else
|
||||
AutoStartup.Disable();
|
||||
{
|
||||
AutoStartup.DisableViaLogonTaskAndRegistry();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -54,6 +65,34 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
}
|
||||
}
|
||||
|
||||
public bool UseLogonTaskForStartup
|
||||
{
|
||||
get => Settings.UseLogonTaskForStartup;
|
||||
set
|
||||
{
|
||||
Settings.UseLogonTaskForStartup = value;
|
||||
|
||||
if (StartFlowLauncherOnSystemStartup)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UseLogonTaskForStartup)
|
||||
{
|
||||
AutoStartup.ChangeToViaLogonTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoStartup.ChangeToViaRegistry();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
|
||||
e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
|
||||
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");
|
||||
|
|
@ -160,7 +199,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
|
||||
private void UpdateApp()
|
||||
{
|
||||
_ = _updater.UpdateAppAsync(App.API, false);
|
||||
_ = _updater.UpdateAppAsync(false);
|
||||
}
|
||||
|
||||
public bool AutoUpdates
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using Flow.Launcher.Infrastructure;
|
|||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Core;
|
||||
|
||||
namespace Flow.Launcher.SettingPages.ViewModels;
|
||||
|
||||
|
|
@ -42,11 +41,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
var item = SelectedCustomPluginHotkey;
|
||||
if (item is null)
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBoxEx.Show(
|
||||
var result = App.API.ShowMsgBox(
|
||||
string.Format(
|
||||
InternationalizationManager.Instance.GetTranslation("deleteCustomHotkeyWarning"), item.Hotkey
|
||||
),
|
||||
|
|
@ -67,11 +66,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
var item = SelectedCustomPluginHotkey;
|
||||
if (item is null)
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
return;
|
||||
}
|
||||
|
||||
var window = new CustomQueryHotkeySetting(null, Settings);
|
||||
var window = new CustomQueryHotkeySetting(Settings);
|
||||
window.UpdateItem(item);
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
|
@ -79,7 +78,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
[RelayCommand]
|
||||
private void CustomHotkeyAdd()
|
||||
{
|
||||
new CustomQueryHotkeySetting(null, Settings).ShowDialog();
|
||||
new CustomQueryHotkeySetting(Settings).ShowDialog();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -88,11 +87,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
var item = SelectedCustomShortcut;
|
||||
if (item is null)
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBoxEx.Show(
|
||||
var result = App.API.ShowMsgBox(
|
||||
string.Format(
|
||||
InternationalizationManager.Instance.GetTranslation("deleteCustomShortcutWarning"), item.Key, item.Value
|
||||
),
|
||||
|
|
@ -112,7 +111,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
|
|||
var item = SelectedCustomShortcut;
|
||||
if (item is null)
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public partial class SettingsPaneProxyViewModel : BaseModel
|
|||
private void OnTestProxyClicked()
|
||||
{
|
||||
var message = TestProxy();
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation(message));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation(message));
|
||||
}
|
||||
|
||||
private string TestProxy()
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
{
|
||||
if (ThemeManager.Instance.BlurEnabled && value)
|
||||
{
|
||||
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed"));
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,13 @@
|
|||
OnContent="{DynamicResource enable}" />
|
||||
</cc:Card>
|
||||
|
||||
<cc:Card Title="{DynamicResource useLogonTaskForStartup}" Sub="{DynamicResource useLogonTaskForStartupTooltip}">
|
||||
<ui:ToggleSwitch
|
||||
IsOn="{Binding UseLogonTaskForStartup}"
|
||||
OffContent="{DynamicResource disable}"
|
||||
OnContent="{DynamicResource enable}" />
|
||||
</cc:Card>
|
||||
|
||||
<cc:Card
|
||||
Title="{DynamicResource hideOnStartup}"
|
||||
Icon=""
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Windows;
|
|||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Configuration;
|
||||
using Flow.Launcher.Helper;
|
||||
|
|
@ -17,16 +18,21 @@ namespace Flow.Launcher;
|
|||
|
||||
public partial class SettingWindow
|
||||
{
|
||||
private readonly Updater _updater;
|
||||
private readonly IPortable _portable;
|
||||
private readonly IPublicAPI _api;
|
||||
private readonly Settings _settings;
|
||||
private readonly SettingWindowViewModel _viewModel;
|
||||
|
||||
public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel)
|
||||
public SettingWindow()
|
||||
{
|
||||
_settings = viewModel.Settings;
|
||||
var viewModel = Ioc.Default.GetRequiredService<SettingWindowViewModel>();
|
||||
_settings = Ioc.Default.GetRequiredService<Settings>();
|
||||
DataContext = viewModel;
|
||||
_viewModel = viewModel;
|
||||
_api = api;
|
||||
_updater = Ioc.Default.GetRequiredService<Updater>();
|
||||
_portable = Ioc.Default.GetRequiredService<Portable>();
|
||||
_api = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
InitializePosition();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
|
@ -125,7 +131,7 @@ public partial class SettingWindow
|
|||
WindowState = _settings.SettingWindowState;
|
||||
}
|
||||
|
||||
private bool IsPositionValid(double top, double left)
|
||||
private static bool IsPositionValid(double top, double left)
|
||||
{
|
||||
foreach (var screen in Screen.AllScreens)
|
||||
{
|
||||
|
|
@ -145,7 +151,7 @@ public partial class SettingWindow
|
|||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
||||
var left = (dip2.X - this.ActualWidth) / 2 + dip1.X;
|
||||
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
|
||||
return left;
|
||||
}
|
||||
|
||||
|
|
@ -154,13 +160,13 @@ public partial class SettingWindow
|
|||
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
||||
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
||||
var top = (dip2.Y - this.ActualHeight) / 2 + dip1.Y - 20;
|
||||
var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20;
|
||||
return top;
|
||||
}
|
||||
|
||||
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||
{
|
||||
var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable);
|
||||
var paneData = new PaneData(_settings, _updater, _portable);
|
||||
if (args.IsSettingsSelected)
|
||||
{
|
||||
ContentFrame.Navigate(typeof(SettingsPaneGeneral), paneData);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ using System.Windows.Input;
|
|||
using System.ComponentModel;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
|
|
@ -56,13 +57,13 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
#region Constructor
|
||||
|
||||
public MainViewModel(Settings settings)
|
||||
public MainViewModel()
|
||||
{
|
||||
_queryTextBeforeLeaveResults = "";
|
||||
_queryText = "";
|
||||
_lastQuery = new Query();
|
||||
|
||||
Settings = settings;
|
||||
Settings = Ioc.Default.GetRequiredService<Settings>();
|
||||
Settings.PropertyChanged += (_, args) =>
|
||||
{
|
||||
switch (args.PropertyName)
|
||||
|
|
@ -279,10 +280,8 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public void ReQuery(bool reselect)
|
||||
{
|
||||
if (SelectedIsFromQueryResults())
|
||||
{
|
||||
QueryResults(isReQuery: true, reSelect: reselect);
|
||||
}
|
||||
BackToQueryResults();
|
||||
QueryResults(isReQuery: true, reSelect: reselect);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -626,6 +625,8 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
BackToQueryResults();
|
||||
|
||||
if (QueryText != queryText)
|
||||
{
|
||||
// re-query is done in QueryText's setter method
|
||||
|
|
@ -1291,7 +1292,6 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
_topMostRecord.Remove(result);
|
||||
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success"));
|
||||
App.API.BackToQueryResults();
|
||||
App.API.ReQuery();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1309,7 +1309,6 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
_topMostRecord.AddOrUpdate(result);
|
||||
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success"));
|
||||
App.API.BackToQueryResults();
|
||||
App.API.ReQuery();
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,5 +159,4 @@ namespace Flow.Launcher.ViewModel
|
|||
changeKeywordsWindow.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,66 +1,46 @@
|
|||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Configuration;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.ViewModel;
|
||||
|
||||
public class SettingWindowViewModel : BaseModel
|
||||
public partial class SettingWindowViewModel : BaseModel
|
||||
{
|
||||
private readonly FlowLauncherJsonStorage<Settings> _storage;
|
||||
private readonly Settings _settings;
|
||||
|
||||
public Updater Updater { get; }
|
||||
|
||||
public IPortable Portable { get; }
|
||||
|
||||
public Settings Settings { get; }
|
||||
|
||||
public SettingWindowViewModel(Updater updater, IPortable portable)
|
||||
public SettingWindowViewModel(Settings settings)
|
||||
{
|
||||
_storage = new FlowLauncherJsonStorage<Settings>();
|
||||
|
||||
Updater = updater;
|
||||
Portable = portable;
|
||||
Settings = _storage.Load();
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public async void UpdateApp()
|
||||
{
|
||||
await Updater.UpdateAppAsync(App.API, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save Flow settings. Plugins settings are not included.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
_storage.Save();
|
||||
_settings.Save();
|
||||
}
|
||||
|
||||
public double SettingWindowWidth
|
||||
{
|
||||
get => Settings.SettingWindowWidth;
|
||||
set => Settings.SettingWindowWidth = value;
|
||||
get => _settings.SettingWindowWidth;
|
||||
set => _settings.SettingWindowWidth = value;
|
||||
}
|
||||
|
||||
public double SettingWindowHeight
|
||||
{
|
||||
get => Settings.SettingWindowHeight;
|
||||
set => Settings.SettingWindowHeight = value;
|
||||
get => _settings.SettingWindowHeight;
|
||||
set => _settings.SettingWindowHeight = value;
|
||||
}
|
||||
|
||||
public double? SettingWindowTop
|
||||
{
|
||||
get => Settings.SettingWindowTop;
|
||||
set => Settings.SettingWindowTop = value;
|
||||
get => _settings.SettingWindowTop;
|
||||
set => _settings.SettingWindowTop = value;
|
||||
}
|
||||
|
||||
public double? SettingWindowLeft
|
||||
{
|
||||
get => Settings.SettingWindowLeft;
|
||||
set => Settings.SettingWindowLeft = value;
|
||||
get => _settings.SettingWindowLeft;
|
||||
set => _settings.SettingWindowLeft = value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
|
|
@ -53,9 +51,8 @@
|
|||
FontSize="12"
|
||||
Foreground="{DynamicResource Color05B}"
|
||||
Text="{DynamicResource Welcome_Page1_Title}" />
|
||||
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Grid.Column="2"
|
||||
Click="BtnCancel_OnClick"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ namespace Flow.Launcher.Plugin.Explorer.Views
|
|||
}
|
||||
|
||||
private string actionKeyword;
|
||||
private readonly IPublicAPI api;
|
||||
private readonly IPublicAPI _api;
|
||||
private bool _keywordEnabled;
|
||||
|
||||
public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword, IPublicAPI api)
|
||||
{
|
||||
CurrentActionKeyword = selectedActionKeyword;
|
||||
this.api = api;
|
||||
_api = api;
|
||||
ActionKeyword = selectedActionKeyword.Keyword;
|
||||
KeywordEnabled = selectedActionKeyword.Enabled;
|
||||
|
||||
|
|
@ -62,14 +62,14 @@ namespace Flow.Launcher.Plugin.Explorer.Views
|
|||
switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled)
|
||||
{
|
||||
case (Settings.ActionKeyword.FileContentSearchActionKeyword, true):
|
||||
api.ShowMsgBox(api.GetTranslation("plugin_explorer_globalActionKeywordInvalid"));
|
||||
_api.ShowMsgBox(_api.GetTranslation("plugin_explorer_globalActionKeywordInvalid"));
|
||||
return;
|
||||
case (Settings.ActionKeyword.QuickAccessActionKeyword, true):
|
||||
api.ShowMsgBox(api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid"));
|
||||
_api.ShowMsgBox(_api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!KeywordEnabled || !api.ActionKeywordAssigned(ActionKeyword))
|
||||
if (!KeywordEnabled || !_api.ActionKeywordAssigned(ActionKeyword))
|
||||
{
|
||||
DialogResult = true;
|
||||
Close();
|
||||
|
|
@ -77,7 +77,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
|
|||
}
|
||||
|
||||
// The keyword is not valid, so show message
|
||||
api.ShowMsgBox(api.GetTranslation("newActionKeywordsHasBeenAssigned"));
|
||||
_api.ShowMsgBox(_api.GetTranslation("newActionKeywordsHasBeenAssigned"));
|
||||
}
|
||||
|
||||
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
var link = pluginManifestInfo.UrlSourceCode.StartsWith("https://github.com")
|
||||
? Regex.Replace(pluginManifestInfo.UrlSourceCode, @"\/tree\/\w+$", "") + "/issues"
|
||||
: pluginManifestInfo.UrlSourceCode;
|
||||
|
||||
Context.API.OpenUrl(link);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
<system:String x:Key="plugin_pluginsmanager_installing_plugin">Installing Plugin</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_from_web">Download and install {0}</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_uninstall_title">Plugin Uninstall</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_keep_plugin_settings_title">Keep plugin settings</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_keep_plugin_settings_subtitle">Do you want to keep the settings of the plugin for the next usage?</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_success_restart">Plugin {0} successfully installed. Restarting Flow, please wait...</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_errormetadatafile">Unable to find the plugin.json metadata file from the extracted zip file.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_error_duplicate">Error: A plugin which has the same or greater version with {0} already exists.</system:String>
|
||||
|
|
@ -37,13 +39,13 @@
|
|||
<system:String x:Key="plugin_pluginsmanager_update_success_restart">Plugin {0} successfully updated. Restarting Flow, please wait...</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_unknown_source_warning_title">Installing from an unknown source</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_unknown_source_warning">You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings)</system:String>
|
||||
|
||||
|
||||
<system:String x:Key="plugin_pluginsmanager_install_success_no_restart">Plugin {0} successfully installed. Please restart Flow.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_uninstall_success_no_restart">Plugin {0} successfully uninstalled. Please restart Flow.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_success_no_restart">Plugin {0} successfully updated. Please restart Flow.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_all_success_no_restart">{0} plugins successfully updated. Please restart Flow.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_modified_error">Plugin {0} has already been modified. Please restart Flow before making any further changes.</system:String>
|
||||
|
||||
|
||||
<!-- Plugin Infos -->
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_name">Plugins Manager</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_description">Management of installing, uninstalling or updating Flow Launcher plugins</system:String>
|
||||
|
|
|
|||
|
|
@ -733,7 +733,11 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
{
|
||||
try
|
||||
{
|
||||
PluginManager.UninstallPlugin(plugin, removeSettings: true);
|
||||
var removePluginSettings = Context.API.ShowMsgBox(
|
||||
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);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
<system:String x:Key="flowlauncher_plugin_program_enable_hideuninstallers_tooltip">Hides programs with common uninstaller names, such as unins000.exe</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable_description">Search in Program Description</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable_description_tooltip">Flow will search program's description</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable_hideduplicatedwindowsapp">Hide duplicated apps</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip">Hide duplicated Win32 programs that are already in the UWP list</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_header">Suffixes</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_max_depth_header">Max Depth</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ namespace Flow.Launcher.Plugin.Program
|
|||
private const string ExeUninstallerSuffix = ".exe";
|
||||
private const string InkUninstallerSuffix = ".lnk";
|
||||
|
||||
private static readonly string WindowsAppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsApps");
|
||||
|
||||
static Main()
|
||||
{
|
||||
}
|
||||
|
|
@ -90,11 +92,20 @@ namespace Flow.Launcher.Plugin.Program
|
|||
{
|
||||
try
|
||||
{
|
||||
// Collect all UWP Windows app directories
|
||||
var uwpsDirectories = _settings.HideDuplicatedWindowsApp ? _uwps
|
||||
.Where(uwp => !string.IsNullOrEmpty(uwp.Location)) // Exclude invalid paths
|
||||
.Where(uwp => uwp.Location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase)) // Keep system apps
|
||||
.Select(uwp => uwp.Location.TrimEnd('\\')) // Remove trailing slash
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray() : null;
|
||||
|
||||
return _win32s.Cast<IProgram>()
|
||||
.Concat(_uwps)
|
||||
.AsParallel()
|
||||
.WithCancellation(token)
|
||||
.Where(HideUninstallersFilter)
|
||||
.Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories))
|
||||
.Where(p => p.Enabled)
|
||||
.Select(p => p.Result(query.Search, Context.API))
|
||||
.Where(r => r?.Score > 0)
|
||||
|
|
@ -152,6 +163,23 @@ namespace Flow.Launcher.Plugin.Program
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool HideDuplicatedWindowsAppFilter(IProgram program, string[] uwpsDirectories)
|
||||
{
|
||||
if (uwpsDirectories == null || uwpsDirectories.Length == 0) return true;
|
||||
if (program is UWPApp) return true;
|
||||
|
||||
var location = program.Location.TrimEnd('\\'); // Ensure trailing slash
|
||||
if (string.IsNullOrEmpty(location))
|
||||
return true; // Keep if location is invalid
|
||||
|
||||
if (!location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase))
|
||||
return true; // Keep if not a Windows app
|
||||
|
||||
// Check if the any Win32 executable directory contains UWP Windows app location matches
|
||||
return !uwpsDirectories.Any(uwpDirectory =>
|
||||
location.StartsWith(uwpDirectory, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public async Task InitAsync(PluginInitContext context)
|
||||
{
|
||||
Context = context;
|
||||
|
|
@ -264,7 +292,6 @@ namespace Flow.Launcher.Plugin.Program
|
|||
Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"),
|
||||
Context.API.GetTranslation(
|
||||
"flowlauncher_plugin_program_disable_dlgtitle_success_message"));
|
||||
Context.API.BackToQueryResults();
|
||||
Context.API.ReQuery();
|
||||
return false;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ namespace Flow.Launcher.Plugin.Program
|
|||
public bool EnableRegistrySource { get; set; } = true;
|
||||
public bool EnablePathSource { get; set; } = false;
|
||||
public bool EnableUWP { get; set; } = true;
|
||||
public bool HideDuplicatedWindowsApp { get; set; } = false;
|
||||
|
||||
internal const char SuffixSeparator = ';';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="0">
|
||||
<Grid.RowDefinitions>
|
||||
|
|
@ -18,40 +18,40 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel
|
||||
Margin="70,10,0,8"
|
||||
Margin="70 10 0 8"
|
||||
HorizontalAlignment="Stretch"
|
||||
LastChildFill="True">
|
||||
<TextBlock
|
||||
MinWidth="120"
|
||||
Margin="0,5,10,0"
|
||||
Margin="0 5 10 0"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_index_source}" />
|
||||
<WrapPanel
|
||||
Width="Auto"
|
||||
Margin="0,0,14,0"
|
||||
Margin="0 0 14 0"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right">
|
||||
<CheckBox
|
||||
Name="UWPEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_uwp}"
|
||||
IsChecked="{Binding EnableUWP}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}" />
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}"
|
||||
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<CheckBox
|
||||
Name="StartMenuEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_start}"
|
||||
IsChecked="{Binding EnableStartMenuSource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" />
|
||||
<CheckBox
|
||||
Name="RegistryEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_registry}"
|
||||
IsChecked="{Binding EnableRegistrySource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" />
|
||||
<CheckBox
|
||||
Name="PATHEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_index_PATH}"
|
||||
IsChecked="{Binding EnablePATHSource}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" />
|
||||
|
|
@ -67,21 +67,20 @@
|
|||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
<DockPanel
|
||||
Margin="70,10,0,8"
|
||||
Margin="70 10 0 8"
|
||||
HorizontalAlignment="Stretch"
|
||||
LastChildFill="True">
|
||||
<TextBlock
|
||||
MinWidth="120"
|
||||
Margin="0,5,10,0"
|
||||
Margin="0 5 10 0"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_index_option}" />
|
||||
<WrapPanel
|
||||
Width="Auto"
|
||||
Margin="0,0,14,0"
|
||||
Margin="0 0 14 0"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right">
|
||||
<CheckBox
|
||||
Name="HideLnkEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}"
|
||||
IsChecked="{Binding HideAppsPath}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" />
|
||||
|
|
@ -91,11 +90,15 @@
|
|||
IsChecked="{Binding HideUninstallers}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" />
|
||||
<CheckBox
|
||||
Name="DescriptionEnabled"
|
||||
Margin="12,0,12,0"
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_description}"
|
||||
IsChecked="{Binding EnableDescription}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" />
|
||||
<CheckBox
|
||||
Margin="12 0 12 0"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp}"
|
||||
IsChecked="{Binding HideDuplicatedWindowsApp}"
|
||||
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip}" />
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
<Separator
|
||||
|
|
@ -103,28 +106,28 @@
|
|||
BorderBrush="{DynamicResource Color03B}"
|
||||
BorderThickness="1" />
|
||||
<StackPanel
|
||||
Margin="60,0,0,2"
|
||||
Margin="60 0 0 2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnLoadAllProgramSource"
|
||||
MinWidth="120"
|
||||
Margin="10,10,5,10"
|
||||
Margin="10 10 5 10"
|
||||
HorizontalAlignment="Right"
|
||||
Click="btnLoadAllProgramSource_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_all_programs}" />
|
||||
<Button
|
||||
x:Name="btnProgramSuffixes"
|
||||
MinWidth="120"
|
||||
Margin="5,10,5,10"
|
||||
Margin="5 10 5 10"
|
||||
HorizontalAlignment="Right"
|
||||
Click="BtnProgramSuffixes_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_suffixes}" />
|
||||
<Button
|
||||
x:Name="btnReindex"
|
||||
MinWidth="120"
|
||||
Margin="5,10,5,10"
|
||||
Margin="5 10 5 10"
|
||||
HorizontalAlignment="Right"
|
||||
Click="btnReindex_Click"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_reindex}" />
|
||||
|
|
@ -142,7 +145,7 @@
|
|||
Minimum="0" />
|
||||
<TextBlock
|
||||
Height="20"
|
||||
Margin="10,0,0,0"
|
||||
Margin="10 0 0 0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_indexing}" />
|
||||
</StackPanel>
|
||||
|
|
@ -151,7 +154,7 @@
|
|||
<ListView
|
||||
x:Name="programSourceView"
|
||||
Grid.Row="2"
|
||||
Margin="70,0,20,0"
|
||||
Margin="70 0 20 0"
|
||||
AllowDrop="True"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
|
|
@ -203,7 +206,7 @@
|
|||
<DockPanel
|
||||
Grid.Row="3"
|
||||
Grid.RowSpan="1"
|
||||
Margin="0,0,20,0">
|
||||
Margin="0 0 20 0">
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnProgramSourceStatus"
|
||||
|
|
@ -220,7 +223,7 @@
|
|||
<Button
|
||||
x:Name="btnAddProgramSource"
|
||||
MinWidth="100"
|
||||
Margin="10,10,0,10"
|
||||
Margin="10 10 0 10"
|
||||
Click="btnAddProgramSource_OnClick"
|
||||
Content="{DynamicResource flowlauncher_plugin_program_add}" />
|
||||
</StackPanel>
|
||||
|
|
|
|||
|
|
@ -57,6 +57,16 @@ namespace Flow.Launcher.Plugin.Program.Views
|
|||
}
|
||||
}
|
||||
|
||||
public bool HideDuplicatedWindowsApp
|
||||
{
|
||||
get => _settings.HideDuplicatedWindowsApp;
|
||||
set
|
||||
{
|
||||
Main.ResetCache();
|
||||
_settings.HideDuplicatedWindowsApp = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableRegistrySource
|
||||
{
|
||||
get => _settings.EnableRegistrySource;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using WindowsInput;
|
||||
using WindowsInput.Native;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
|
|
@ -379,7 +378,7 @@ namespace Flow.Launcher.Plugin.Shell
|
|||
private void OnWinRPressed()
|
||||
{
|
||||
// show the main window and set focus to the query box
|
||||
Task.Run(() =>
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
context.API.ShowMainWindow();
|
||||
context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeparator}");
|
||||
|
|
|
|||
Loading…
Reference in a new issue