mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge remote-tracking branch 'origin/dev' into AcronymFuzzy
This commit is contained in:
commit
cb1398575f
152 changed files with 2934 additions and 2735 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -300,4 +300,8 @@ migrateToAutomaticPackageRestore.ps1
|
|||
*.pyc
|
||||
*.diagsession
|
||||
Output-Performance.txt
|
||||
*.diff
|
||||
*.diff
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
.history
|
||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWpf>true</UseWpf>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<OutputType>Library</OutputType>
|
||||
|
|
@ -55,7 +55,6 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="FSharp.Core" Version="4.7.1" />
|
||||
<PackageReference Include="squirrel.windows" Version="1.5.2" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Infrastructure.Exception;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
|
@ -65,7 +65,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
List<Result> results = new List<Result>();
|
||||
|
||||
JsonRPCQueryResponseModel queryResponseModel = JsonConvert.DeserializeObject<JsonRPCQueryResponseModel>(output);
|
||||
JsonRPCQueryResponseModel queryResponseModel = JsonSerializer.Deserialize<JsonRPCQueryResponseModel>(output);
|
||||
if (queryResponseModel.Result == null) return null;
|
||||
|
||||
foreach (JsonRPCResult result in queryResponseModel.Result)
|
||||
|
|
@ -84,7 +84,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
else
|
||||
{
|
||||
string actionReponse = ExecuteCallback(result1.JsonRPCAction);
|
||||
JsonRPCRequestModel jsonRpcRequestModel = JsonConvert.DeserializeObject<JsonRPCRequestModel>(actionReponse);
|
||||
JsonRPCRequestModel jsonRpcRequestModel = JsonSerializer.Deserialize<JsonRPCRequestModel>(actionReponse);
|
||||
if (jsonRpcRequestModel != null
|
||||
&& !String.IsNullOrEmpty(jsonRpcRequestModel.Method)
|
||||
&& jsonRpcRequestModel.Method.StartsWith("Flow.Launcher."))
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath);
|
||||
assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath));
|
||||
|
||||
referencedPluginPackageDependencyResolver =
|
||||
referencedPluginPackageDependencyResolver =
|
||||
new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.Plugin.dll"));
|
||||
}
|
||||
|
||||
|
|
@ -38,15 +38,15 @@ namespace Flow.Launcher.Core.Plugin
|
|||
// that use Newtonsoft.Json
|
||||
if (assemblyPath == null || ExistsInReferencedPluginPackage(assemblyName))
|
||||
return null;
|
||||
|
||||
|
||||
return LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
|
||||
internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type)
|
||||
internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, params Type[] types)
|
||||
{
|
||||
var allTypes = assembly.ExportedTypes;
|
||||
|
||||
return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(type));
|
||||
return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Intersect(types).Any());
|
||||
}
|
||||
|
||||
internal bool ExistsInReferencedPluginPackage(AssemblyName assemblyName)
|
||||
|
|
@ -54,4 +54,4 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -61,7 +61,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
PluginMetadata metadata;
|
||||
try
|
||||
{
|
||||
metadata = JsonConvert.DeserializeObject<PluginMetadata>(File.ReadAllText(configPath));
|
||||
metadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(configPath));
|
||||
metadata.PluginDirectory = pluginDirectory;
|
||||
// for plugins which doesn't has ActionKeywords key
|
||||
metadata.ActionKeywords = metadata.ActionKeywords ?? new List<string> { metadata.ActionKeyword };
|
||||
|
|
|
|||
|
|
@ -1,169 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
internal class PluginInstaller
|
||||
{
|
||||
internal static void Install(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string tempFolder = Path.Combine(Path.GetTempPath(), "flowlauncher", "plugins");
|
||||
if (Directory.Exists(tempFolder))
|
||||
{
|
||||
Directory.Delete(tempFolder, true);
|
||||
}
|
||||
UnZip(path, tempFolder, true);
|
||||
|
||||
string jsonPath = Path.Combine(tempFolder, Constant.PluginMetadataFileName);
|
||||
if (!File.Exists(jsonPath))
|
||||
{
|
||||
MessageBox.Show("Install failed: plugin config is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
PluginMetadata plugin = GetMetadataFromJson(tempFolder);
|
||||
if (plugin == null || plugin.Name == null)
|
||||
{
|
||||
MessageBox.Show("Install failed: plugin config is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
string pluginFolderPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory;
|
||||
|
||||
string newPluginName = plugin.Name
|
||||
.Replace("/", "_")
|
||||
.Replace("\\", "_")
|
||||
.Replace(":", "_")
|
||||
.Replace("<", "_")
|
||||
.Replace(">", "_")
|
||||
.Replace("?", "_")
|
||||
.Replace("*", "_")
|
||||
.Replace("|", "_")
|
||||
+ "-" + Guid.NewGuid();
|
||||
|
||||
string newPluginPath = Path.Combine(pluginFolderPath, newPluginName);
|
||||
|
||||
string content = $"Do you want to install following plugin?{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Name: {plugin.Name}{Environment.NewLine}" +
|
||||
$"Version: {plugin.Version}{Environment.NewLine}" +
|
||||
$"Author: {plugin.Author}";
|
||||
PluginPair existingPlugin = PluginManager.GetPluginForId(plugin.ID);
|
||||
|
||||
if (existingPlugin != null)
|
||||
{
|
||||
content = $"Do you want to update following plugin?{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Name: {plugin.Name}{Environment.NewLine}" +
|
||||
$"Old Version: {existingPlugin.Metadata.Version}" +
|
||||
$"{Environment.NewLine}New Version: {plugin.Version}" +
|
||||
$"{Environment.NewLine}Author: {plugin.Author}";
|
||||
}
|
||||
|
||||
var result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirectory))
|
||||
{
|
||||
//when plugin is in use, we can't delete them. That's why we need to make plugin folder a random name
|
||||
File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close();
|
||||
}
|
||||
|
||||
Directory.Move(tempFolder, newPluginPath);
|
||||
|
||||
//exsiting plugins may be has loaded by application,
|
||||
//if we try to delelte those kind of plugins, we will get a error that indicate the
|
||||
//file is been used now.
|
||||
//current solution is to restart Flow Launcher. Ugly.
|
||||
//if (MainWindow.Initialized)
|
||||
//{
|
||||
// Plugins.Initialize();
|
||||
//}
|
||||
if (MessageBox.Show($"You have installed plugin {plugin.Name} successfully.{Environment.NewLine}" +
|
||||
"Restart Flow Launcher to take effect?",
|
||||
"Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
|
||||
{
|
||||
PluginManager.API.RestartApp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PluginMetadata GetMetadataFromJson(string pluginDirectory)
|
||||
{
|
||||
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
|
||||
PluginMetadata metadata;
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
metadata = JsonConvert.DeserializeObject<PluginMetadata>(File.ReadAllText(configPath));
|
||||
metadata.PluginDirectory = pluginDirectory;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid json format", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!AllowedLanguage.IsAllowed(metadata.Language))
|
||||
{
|
||||
Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid language {metadata.Language}");
|
||||
return null;
|
||||
}
|
||||
if (!File.Exists(metadata.ExecuteFilePath))
|
||||
{
|
||||
Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: file {metadata.ExecuteFilePath} doesn't exist");
|
||||
return null;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// unzip plugin contents to the given directory.
|
||||
/// </summary>
|
||||
/// <param name="zipFile">The path to the zip file.</param>
|
||||
/// <param name="strDirectory">The output directory.</param>
|
||||
/// <param name="overWrite">overwirte</param>
|
||||
private static void UnZip(string zipFile, string strDirectory, bool overWrite)
|
||||
{
|
||||
if (strDirectory == "")
|
||||
strDirectory = Directory.GetCurrentDirectory();
|
||||
|
||||
using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFile)))
|
||||
{
|
||||
ZipEntry theEntry;
|
||||
|
||||
while ((theEntry = zipStream.GetNextEntry()) != null)
|
||||
{
|
||||
var pathToZip = theEntry.Name;
|
||||
var directoryName = String.IsNullOrEmpty(pathToZip) ? "" : Path.GetDirectoryName(pathToZip);
|
||||
var fileName = Path.GetFileName(pathToZip);
|
||||
var destinationDir = Path.Combine(strDirectory, directoryName);
|
||||
var destinationFile = Path.Combine(destinationDir, fileName);
|
||||
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
|
||||
if (String.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overWrite))
|
||||
continue;
|
||||
|
||||
using (FileStream streamWriter = File.Create(destinationFile))
|
||||
{
|
||||
zipStream.CopyTo(streamWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
|
@ -52,13 +53,14 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
public static void ReloadData()
|
||||
public static async Task ReloadData()
|
||||
{
|
||||
foreach(var plugin in AllPlugins)
|
||||
await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch
|
||||
{
|
||||
var reloadablePlugin = plugin.Plugin as IReloadable;
|
||||
reloadablePlugin?.ReloadData();
|
||||
}
|
||||
IReloadable p => Task.Run(p.ReloadData),
|
||||
IAsyncReloadable p => p.ReloadDataAsync(),
|
||||
_ => Task.CompletedTask,
|
||||
}).ToArray());
|
||||
}
|
||||
|
||||
static PluginManager()
|
||||
|
|
@ -86,58 +88,65 @@ 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 void InitializePlugins(IPublicAPI api)
|
||||
public static async Task InitializePlugins(IPublicAPI api)
|
||||
{
|
||||
API = api;
|
||||
var failedPlugins = new ConcurrentQueue<PluginPair>();
|
||||
Parallel.ForEach(AllPlugins, pair =>
|
||||
|
||||
var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () =>
|
||||
var milliseconds = pair.Plugin switch
|
||||
{
|
||||
pair.Plugin.Init(new PluginInitContext
|
||||
{
|
||||
CurrentPluginMetadata = pair.Metadata,
|
||||
API = API
|
||||
});
|
||||
});
|
||||
IAsyncPlugin plugin
|
||||
=> await Stopwatch.DebugAsync($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>",
|
||||
() => plugin.InitAsync(new PluginInitContext(pair.Metadata, API))),
|
||||
IPlugin plugin
|
||||
=> Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>",
|
||||
() => plugin.Init(new PluginInitContext(pair.Metadata, API))),
|
||||
_ => throw new ArgumentException(),
|
||||
};
|
||||
pair.Metadata.InitTime += milliseconds;
|
||||
Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
|
||||
Log.Info(
|
||||
$"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e);
|
||||
pair.Metadata.Disabled = true;
|
||||
pair.Metadata.Disabled = true;
|
||||
failedPlugins.Enqueue(pair);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
await Task.WhenAll(InitTasks);
|
||||
|
||||
_contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
|
||||
foreach (var plugin in AllPlugins)
|
||||
{
|
||||
if (IsGlobalPlugin(plugin.Metadata))
|
||||
GlobalPlugins.Add(plugin);
|
||||
|
||||
// Plugins may have multiple ActionKeywords, eg. WebSearch
|
||||
plugin.Metadata.ActionKeywords
|
||||
.Where(x => x != Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.ForEach(x => NonGlobalPlugins[x] = plugin);
|
||||
foreach (var actionKeyword in plugin.Metadata.ActionKeywords)
|
||||
{
|
||||
switch (actionKeyword)
|
||||
{
|
||||
case Query.GlobalPluginWildcardSign:
|
||||
GlobalPlugins.Add(plugin);
|
||||
break;
|
||||
default:
|
||||
NonGlobalPlugins[actionKeyword] = plugin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failedPlugins.Any())
|
||||
{
|
||||
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
|
||||
API.ShowMsg($"Fail to Init Plugins", $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", "", false);
|
||||
API.ShowMsg($"Fail to Init Plugins",
|
||||
$"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help",
|
||||
"", false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InstallPlugin(string path)
|
||||
{
|
||||
PluginInstaller.Install(path);
|
||||
}
|
||||
|
||||
public static List<PluginPair> ValidPluginsForQuery(Query query)
|
||||
{
|
||||
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
||||
|
|
@ -151,24 +160,46 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
public static List<Result> QueryForPlugin(PluginPair pair, Query query)
|
||||
public static async Task<List<Result>> QueryForPlugin(PluginPair pair, Query query, CancellationToken token)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
try
|
||||
{
|
||||
var metadata = pair.Metadata;
|
||||
var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () =>
|
||||
|
||||
long milliseconds = -1L;
|
||||
|
||||
switch (pair.Plugin)
|
||||
{
|
||||
results = pair.Plugin.Query(query) ?? new List<Result>();
|
||||
UpdatePluginMetadata(results, metadata, query);
|
||||
});
|
||||
case IAsyncPlugin plugin:
|
||||
milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}",
|
||||
async () => results = await plugin.QueryAsync(query, token).ConfigureAwait(false));
|
||||
break;
|
||||
case IPlugin plugin:
|
||||
await Task.Run(() => milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}",
|
||||
() => results = plugin.Query(query)), token).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
token.ThrowIfCancellationRequested();
|
||||
UpdatePluginMetadata(results, metadata, query);
|
||||
|
||||
metadata.QueryCount += 1;
|
||||
metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
|
||||
metadata.AvgQueryTime =
|
||||
metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// null will be fine since the results will only be added into queue if the token hasn't been cancelled
|
||||
return results = null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
@ -187,11 +218,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsGlobalPlugin(PluginMetadata metadata)
|
||||
{
|
||||
return metadata.ActionKeywords.Contains(Query.GlobalPluginWildcardSign);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get specified plugin, return null if not found
|
||||
/// </summary>
|
||||
|
|
@ -227,16 +253,19 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e);
|
||||
Log.Exception(
|
||||
$"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static bool ActionKeywordRegistered(string actionKeyword)
|
||||
{
|
||||
return actionKeyword != Query.GlobalPluginWildcardSign
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -254,6 +283,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
NonGlobalPlugins[newActionKeyword] = plugin;
|
||||
}
|
||||
|
||||
plugin.Metadata.ActionKeywords.Add(newActionKeyword);
|
||||
}
|
||||
|
||||
|
|
@ -267,16 +297,16 @@ namespace Flow.Launcher.Core.Plugin
|
|||
if (oldActionkeyword == Query.GlobalPluginWildcardSign
|
||||
&& // Plugins may have multiple ActionKeywords that are global, eg. WebSearch
|
||||
plugin.Metadata.ActionKeywords
|
||||
.Where(x => x == Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.Count == 1)
|
||||
.Where(x => x == Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.Count == 1)
|
||||
{
|
||||
GlobalPlugins.Remove(plugin);
|
||||
}
|
||||
|
||||
|
||||
if (oldActionkeyword != Query.GlobalPluginWildcardSign)
|
||||
NonGlobalPlugins.Remove(oldActionkeyword);
|
||||
|
||||
|
||||
|
||||
plugin.Metadata.ActionKeywords.Remove(oldActionkeyword);
|
||||
}
|
||||
|
|
@ -290,4 +320,4 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,56 +37,59 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
foreach (var metadata in metadatas)
|
||||
{
|
||||
var milliseconds = Stopwatch.Debug($"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () =>
|
||||
{
|
||||
|
||||
var milliseconds = Stopwatch.Debug(
|
||||
$"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () =>
|
||||
{
|
||||
#if DEBUG
|
||||
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
|
||||
var assembly = assemblyLoader.LoadAssemblyAndDependencies();
|
||||
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin));
|
||||
var plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
#else
|
||||
Assembly assembly = null;
|
||||
IPlugin plugin = null;
|
||||
|
||||
try
|
||||
{
|
||||
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
|
||||
assembly = assemblyLoader.LoadAssemblyAndDependencies();
|
||||
var assembly = assemblyLoader.LoadAssemblyAndDependencies();
|
||||
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin),
|
||||
typeof(IAsyncPlugin));
|
||||
|
||||
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin));
|
||||
var plugin = Activator.CreateInstance(type);
|
||||
#else
|
||||
Assembly assembly = null;
|
||||
object plugin = null;
|
||||
|
||||
plugin = (IPlugin)Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception e) when (assembly == null)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
|
||||
}
|
||||
try
|
||||
{
|
||||
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
|
||||
assembly = assemblyLoader.LoadAssemblyAndDependencies();
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
return;
|
||||
}
|
||||
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin),
|
||||
typeof(IAsyncPlugin));
|
||||
|
||||
plugin = Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception e) when (assembly == null)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
|
||||
}
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
erroredPlugins.Add(metadata.Name);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
plugins.Add(new PluginPair
|
||||
{
|
||||
Plugin = plugin,
|
||||
Metadata = metadata
|
||||
plugins.Add(new PluginPair
|
||||
{
|
||||
Plugin = plugin,
|
||||
Metadata = metadata
|
||||
});
|
||||
});
|
||||
});
|
||||
metadata.InitTime += milliseconds;
|
||||
}
|
||||
|
||||
|
|
@ -95,15 +98,15 @@ namespace Flow.Launcher.Core.Plugin
|
|||
var errorPluginString = String.Join(Environment.NewLine, erroredPlugins);
|
||||
|
||||
var errorMessage = "The following "
|
||||
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
|
||||
+ "errored and cannot be loaded:";
|
||||
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
|
||||
+ "errored and cannot be loaded:";
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Please refer to the logs for more information","",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
|
||||
$"Please refer to the logs for more information", "",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +182,5 @@ namespace Flow.Launcher.Core.Plugin
|
|||
Metadata = metadata
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,6 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
language = language.NonNull();
|
||||
|
||||
Settings.Language = language.LanguageCode;
|
||||
|
||||
RemoveOldLanguageFiles();
|
||||
if (language != AvailableLanguages.English)
|
||||
|
|
@ -96,6 +95,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
LoadLanguage(language);
|
||||
}
|
||||
UpdatePluginMetadataTranslations();
|
||||
Settings.Language = language.LanguageCode;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@ using System.Threading.Tasks;
|
|||
using System.Windows;
|
||||
using JetBrains.Annotations;
|
||||
using Squirrel;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using System.IO;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Core
|
||||
{
|
||||
|
|
@ -29,101 +28,80 @@ namespace Flow.Launcher.Core
|
|||
GitHubRepository = gitHubRepository;
|
||||
}
|
||||
|
||||
public async Task UpdateApp(IPublicAPI api , bool silentUpdate = true)
|
||||
public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true)
|
||||
{
|
||||
UpdateManager updateManager;
|
||||
UpdateInfo newUpdateInfo;
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg("Please wait...", "Checking for new update");
|
||||
|
||||
try
|
||||
{
|
||||
updateManager = await GitHubUpdateManager(GitHubRepository);
|
||||
}
|
||||
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
|
||||
{
|
||||
Log.Exception($"|Updater.UpdateApp|Please check your connection and proxy settings to api.github.com.", e);
|
||||
return;
|
||||
}
|
||||
UpdateInfo newUpdateInfo;
|
||||
|
||||
try
|
||||
{
|
||||
// UpdateApp CheckForUpdate will return value only if the app is squirrel installed
|
||||
newUpdateInfo = await updateManager.CheckForUpdate().NonNull();
|
||||
}
|
||||
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
|
||||
{
|
||||
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to api.github.com.", e);
|
||||
updateManager.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
|
||||
var currentVersion = Version.Parse(Constant.Version);
|
||||
|
||||
Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>");
|
||||
|
||||
if (newReleaseVersion <= currentVersion)
|
||||
{
|
||||
if (!silentUpdate)
|
||||
MessageBox.Show("You already have the latest Flow Launcher version");
|
||||
updateManager.Dispose();
|
||||
return;
|
||||
}
|
||||
api.ShowMsg("Please wait...", "Checking for new update");
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg("Update found", "Updating...");
|
||||
using var updateManager = await GitHubUpdateManager(GitHubRepository).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply);
|
||||
|
||||
// UpdateApp CheckForUpdate will return value only if the app is squirrel installed
|
||||
newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false);
|
||||
|
||||
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
|
||||
var currentVersion = Version.Parse(Constant.Version);
|
||||
|
||||
Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>");
|
||||
|
||||
if (newReleaseVersion <= currentVersion)
|
||||
{
|
||||
if (!silentUpdate)
|
||||
MessageBox.Show("You already have the latest Flow Launcher version");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg("Update found", "Updating...");
|
||||
|
||||
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
|
||||
|
||||
await updateManager.ApplyReleases(newUpdateInfo).ConfigureAwait(false);
|
||||
|
||||
if (DataLocation.PortableDataLocationInUse())
|
||||
{
|
||||
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
|
||||
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
|
||||
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
|
||||
MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " +
|
||||
$"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
|
||||
|
||||
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
|
||||
|
||||
if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
|
||||
{
|
||||
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
|
||||
updateManager.Dispose();
|
||||
api.ShowMsg("Update Failed", "Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com.");
|
||||
return;
|
||||
}
|
||||
|
||||
await updateManager.ApplyReleases(newUpdateInfo);
|
||||
|
||||
if (DataLocation.PortableDataLocationInUse())
|
||||
{
|
||||
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
|
||||
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination);
|
||||
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination))
|
||||
MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " +
|
||||
$"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await updateManager.CreateUninstallerRegistryEntry();
|
||||
}
|
||||
|
||||
var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
|
||||
|
||||
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
|
||||
|
||||
// always dispose UpdateManager
|
||||
updateManager.Dispose();
|
||||
|
||||
if (MessageBox.Show(newVersionTips, "New Update", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private class GithubRelease
|
||||
{
|
||||
[JsonProperty("prerelease")]
|
||||
[JsonPropertyName("prerelease")]
|
||||
public bool Prerelease { get; [UsedImplicitly] set; }
|
||||
|
||||
[JsonProperty("published_at")]
|
||||
[JsonPropertyName("published_at")]
|
||||
public DateTime PublishedAt { get; [UsedImplicitly] set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
[JsonPropertyName("html_url")]
|
||||
public string HtmlUrl { get; [UsedImplicitly] set; }
|
||||
}
|
||||
|
||||
|
|
@ -133,13 +111,13 @@ namespace Flow.Launcher.Core
|
|||
var uri = new Uri(repository);
|
||||
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
|
||||
|
||||
var json = await Http.Get(api);
|
||||
var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false);
|
||||
|
||||
var releases = JsonConvert.DeserializeObject<List<GithubRelease>>(json);
|
||||
var releases = await System.Text.Json.JsonSerializer.DeserializeAsync<List<GithubRelease>>(jsonStream).ConfigureAwait(false);
|
||||
var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First();
|
||||
var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/");
|
||||
|
||||
var client = new WebClient { Proxy = Http.WebProxy() };
|
||||
var client = new WebClient { Proxy = Http.WebProxy };
|
||||
var downloader = new FileDownloader(client);
|
||||
|
||||
var manager = new UpdateManager(latestUrl, urlDownloader: downloader);
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
public static string PythonPath;
|
||||
|
||||
public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.png";
|
||||
public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.svg";
|
||||
|
||||
public const string DefaultTheme = "Darker";
|
||||
|
||||
public const string Themes = "Themes";
|
||||
|
||||
public const string Website = "https://flow-launcher.github.io";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<ProjectGuid>{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<UseWpf>true</UseWpf>
|
||||
|
|
@ -49,7 +48,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NLog.Schema" Version="4.7.0-rc1" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
public static class Helper
|
||||
{
|
||||
static Helper()
|
||||
{
|
||||
jsonFormattedSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy
|
||||
/// </summary>
|
||||
|
|
@ -65,13 +71,18 @@ namespace Flow.Launcher.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly JsonSerializerOptions jsonFormattedSerializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
public static string Formatted<T>(this T t)
|
||||
{
|
||||
var formatted = JsonConvert.SerializeObject(
|
||||
t,
|
||||
Formatting.Indented,
|
||||
new StringEnumConverter()
|
||||
);
|
||||
var formatted = JsonSerializer.Serialize(t, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
|||
using JetBrains.Annotations;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Http
|
||||
{
|
||||
|
|
@ -13,6 +16,14 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
{
|
||||
private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko";
|
||||
|
||||
private static HttpClient client;
|
||||
|
||||
private static SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler()
|
||||
{
|
||||
UseProxy = true,
|
||||
Proxy = WebProxy
|
||||
};
|
||||
|
||||
static Http()
|
||||
{
|
||||
// need to be added so it would work on a win10 machine
|
||||
|
|
@ -20,64 +31,117 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
|
||||
| SecurityProtocolType.Tls11
|
||||
| SecurityProtocolType.Tls12;
|
||||
|
||||
client = new HttpClient(socketsHttpHandler, false);
|
||||
client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
|
||||
}
|
||||
|
||||
public static HttpProxy Proxy { private get; set; }
|
||||
public static IWebProxy WebProxy()
|
||||
private static HttpProxy proxy;
|
||||
|
||||
public static HttpProxy Proxy
|
||||
{
|
||||
if (Proxy != null && Proxy.Enabled && !string.IsNullOrEmpty(Proxy.Server))
|
||||
private get { return proxy; }
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(Proxy.UserName) || string.IsNullOrEmpty(Proxy.Password))
|
||||
proxy = value;
|
||||
proxy.PropertyChanged += UpdateProxy;
|
||||
}
|
||||
}
|
||||
|
||||
public static WebProxy WebProxy { get; } = new WebProxy();
|
||||
|
||||
/// <summary>
|
||||
/// Update the Address of the Proxy to modify the client Proxy
|
||||
/// </summary>
|
||||
public static void UpdateProxy(ProxyProperty property)
|
||||
{
|
||||
(WebProxy.Address, WebProxy.Credentials) = property switch
|
||||
{
|
||||
ProxyProperty.Enabled => Proxy.Enabled switch
|
||||
{
|
||||
var webProxy = new WebProxy(Proxy.Server, Proxy.Port);
|
||||
return webProxy;
|
||||
true => Proxy.UserName switch
|
||||
{
|
||||
var userName when !string.IsNullOrEmpty(userName) =>
|
||||
(new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null),
|
||||
_ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"),
|
||||
new NetworkCredential(Proxy.UserName, Proxy.Password))
|
||||
},
|
||||
false => (null, null)
|
||||
},
|
||||
ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
|
||||
ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
|
||||
ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var response = await client.GetAsync(url, token);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
|
||||
await response.Content.CopyToAsync(fileStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
var webProxy = new WebProxy(Proxy.Server, Proxy.Port)
|
||||
{
|
||||
Credentials = new NetworkCredential(Proxy.UserName, Proxy.Password)
|
||||
};
|
||||
return webProxy;
|
||||
throw new HttpRequestException($"Error code <{response.StatusCode}> returned from <{url}>");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
Log.Exception("Infrastructure.Http", "Http Request Error", e, "DownloadAsync");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchrously get the result as string from url.
|
||||
/// When supposing the result larger than 83kb, try using GetStreamAsync to avoid reading as string
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns>The Http result as string. Null if cancellation requested</returns>
|
||||
public static Task<string> GetAsync([NotNull] string url, CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
return GetAsync(new Uri(url.Replace("#", "%23")), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>The Http result as string. Null if cancellation requested</returns>
|
||||
public static async Task<string> GetAsync([NotNull] Uri url, CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
using var response = await client.GetAsync(url, token);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return content;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WebRequest.GetSystemWebProxy();
|
||||
throw new HttpRequestException(
|
||||
$"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Download([NotNull] string url, [NotNull] string filePath)
|
||||
{
|
||||
var client = new WebClient { Proxy = WebProxy() };
|
||||
client.Headers.Add("user-agent", UserAgent);
|
||||
client.DownloadFile(url, filePath);
|
||||
}
|
||||
|
||||
public static async Task<string> Get([NotNull] string url, string encoding = "UTF-8")
|
||||
/// <summary>
|
||||
/// Asynchrously get the result as stream from url.
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<Stream> GetStreamAsync([NotNull] string url, CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
var request = WebRequest.CreateHttp(url);
|
||||
request.Method = "GET";
|
||||
request.Timeout = 1000;
|
||||
request.Proxy = WebProxy();
|
||||
request.UserAgent = UserAgent;
|
||||
var response = await request.GetResponseAsync() as HttpWebResponse;
|
||||
response = response.NonNull();
|
||||
var stream = response.GetResponseStream().NonNull();
|
||||
|
||||
using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)))
|
||||
{
|
||||
var content = await reader.ReadToEndAsync();
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return content;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
|
||||
}
|
||||
}
|
||||
var response = await client.GetAsync(url, token);
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
private const int MaxCached = 50;
|
||||
public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>();
|
||||
private const int permissibleFactor = 2;
|
||||
|
||||
|
||||
public void Initialization(Dictionary<string, int> usage)
|
||||
{
|
||||
foreach (var key in usage.Keys)
|
||||
|
|
@ -44,14 +44,14 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
value.usage++;
|
||||
return value.imageSource;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
Data.AddOrUpdate(
|
||||
path,
|
||||
new ImageUsage(0, value),
|
||||
path,
|
||||
new ImageUsage(0, value),
|
||||
(k, v) =>
|
||||
{
|
||||
v.imageSource = value;
|
||||
|
|
@ -65,23 +65,15 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
if (Data.Count > permissibleFactor * MaxCached)
|
||||
{
|
||||
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
|
||||
|
||||
|
||||
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
|
||||
{
|
||||
if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
|
||||
{
|
||||
Data.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
Data.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
var contains = Data.ContainsKey(key);
|
||||
return contains;
|
||||
return Data.ContainsKey(key) && Data[key].imageSource != null;
|
||||
}
|
||||
|
||||
public int CacheSize()
|
||||
|
|
@ -97,5 +89,4 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
return Data.Values.Select(x => x.imageSource).Distinct().Count();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,8 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
private static readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
|
||||
private static IImageHashGenerator _hashGenerator;
|
||||
private static bool EnableImageHash = true;
|
||||
public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
|
||||
|
||||
|
||||
private static readonly string[] ImageExtensions =
|
||||
{
|
||||
|
|
@ -37,6 +39,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
|
||||
var usage = LoadStorageToConcurrentDictionary();
|
||||
|
||||
|
||||
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
|
||||
{
|
||||
ImageSource img = new BitmapImage(new Uri(icon));
|
||||
|
|
@ -61,7 +64,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
lock (_storage)
|
||||
{
|
||||
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage));
|
||||
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, x => x.usage));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,6 +214,11 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
option);
|
||||
}
|
||||
|
||||
public static bool CacheContainImage(string path)
|
||||
{
|
||||
return ImageCache.ContainsKey(path) && ImageCache[path] != null;
|
||||
}
|
||||
|
||||
public static ImageSource Load(string path, bool loadFullImage = false)
|
||||
{
|
||||
var imageResult = LoadInternal(path, loadFullImage);
|
||||
|
|
@ -221,7 +229,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
|
||||
if (hash != null)
|
||||
{
|
||||
|
||||
|
||||
if (GuidToKey.TryGetValue(hash, out string key))
|
||||
{ // image already exists
|
||||
img = ImageCache[key] ?? img;
|
||||
|
|
|
|||
|
|
@ -50,14 +50,18 @@ namespace Flow.Launcher.Infrastructure.Logger
|
|||
return valid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
#if DEBUG
|
||||
throw exception;
|
||||
#else
|
||||
var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName);
|
||||
|
||||
ExceptionInternal(classNameWithMethod, message, exception);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string CheckClassAndMessageAndReturnFullClassWithMethod(string className, string message,
|
||||
|
|
@ -128,7 +132,7 @@ namespace Flow.Launcher.Infrastructure.Logger
|
|||
public static void Exception(string message, System.Exception e)
|
||||
{
|
||||
#if DEBUG
|
||||
throw e;
|
||||
throw e;
|
||||
#else
|
||||
if (FormatValid(message))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,22 +4,117 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using ToolGood.Words.Pinyin;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
public class TranslationMapping
|
||||
{
|
||||
private bool constructed;
|
||||
|
||||
private List<int> originalIndexs = new List<int>();
|
||||
private List<int> translatedIndexs = new List<int>();
|
||||
private int translaedLength = 0;
|
||||
|
||||
public string key { get; private set; }
|
||||
|
||||
public void setKey(string key)
|
||||
{
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void AddNewIndex(int originalIndex, int translatedIndex, int length)
|
||||
{
|
||||
if (constructed)
|
||||
throw new InvalidOperationException("Mapping shouldn't be changed after constructed");
|
||||
|
||||
originalIndexs.Add(originalIndex);
|
||||
translatedIndexs.Add(translatedIndex);
|
||||
translatedIndexs.Add(translatedIndex + length);
|
||||
translaedLength += length - 1;
|
||||
}
|
||||
|
||||
public int MapToOriginalIndex(int translatedIndex)
|
||||
{
|
||||
if (translatedIndex > translatedIndexs.Last())
|
||||
return translatedIndex - translaedLength - 1;
|
||||
|
||||
int lowerBound = 0;
|
||||
int upperBound = originalIndexs.Count - 1;
|
||||
|
||||
int count = 0;
|
||||
|
||||
|
||||
// Corner case handle
|
||||
if (translatedIndex < translatedIndexs[0])
|
||||
return translatedIndex;
|
||||
if (translatedIndex > translatedIndexs.Last())
|
||||
{
|
||||
int indexDef = 0;
|
||||
for (int k = 0; k < originalIndexs.Count; k++)
|
||||
{
|
||||
indexDef += translatedIndexs[k * 2 + 1] - translatedIndexs[k * 2];
|
||||
}
|
||||
|
||||
return translatedIndex - indexDef - 1;
|
||||
}
|
||||
|
||||
// Binary Search with Range
|
||||
for (int i = originalIndexs.Count / 2;; count++)
|
||||
{
|
||||
if (translatedIndex < translatedIndexs[i * 2])
|
||||
{
|
||||
// move to lower middle
|
||||
upperBound = i;
|
||||
i = (i + lowerBound) / 2;
|
||||
}
|
||||
else if (translatedIndex > translatedIndexs[i * 2 + 1] - 1)
|
||||
{
|
||||
lowerBound = i;
|
||||
// move to upper middle
|
||||
// due to floor of integer division, move one up on corner case
|
||||
i = (i + upperBound + 1) / 2;
|
||||
}
|
||||
else
|
||||
return originalIndexs[i];
|
||||
|
||||
if (upperBound - lowerBound <= 1 &&
|
||||
translatedIndex > translatedIndexs[lowerBound * 2 + 1] &&
|
||||
translatedIndex < translatedIndexs[upperBound * 2])
|
||||
{
|
||||
int indexDef = 0;
|
||||
|
||||
for (int j = 0; j < upperBound; j++)
|
||||
{
|
||||
indexDef += translatedIndexs[j * 2 + 1] - translatedIndexs[j * 2];
|
||||
}
|
||||
|
||||
return translatedIndex - indexDef - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void endConstruct()
|
||||
{
|
||||
if (constructed)
|
||||
throw new InvalidOperationException("Mapping has already been constructed");
|
||||
constructed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IAlphabet
|
||||
{
|
||||
string Translate(string stringToTranslate);
|
||||
public (string translation, TranslationMapping map) Translate(string stringToTranslate);
|
||||
}
|
||||
|
||||
public class PinyinAlphabet : IAlphabet
|
||||
{
|
||||
private ConcurrentDictionary<string, string> _pinyinCache = new ConcurrentDictionary<string, string>();
|
||||
private ConcurrentDictionary<string, (string translation, TranslationMapping map)> _pinyinCache =
|
||||
new ConcurrentDictionary<string, (string translation, TranslationMapping map)>();
|
||||
|
||||
|
||||
private Settings _settings;
|
||||
|
||||
public void Initialize([NotNull] Settings settings)
|
||||
|
|
@ -27,8 +122,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
}
|
||||
|
||||
|
||||
public string Translate(string content)
|
||||
public (string translation, TranslationMapping map) Translate(string content)
|
||||
{
|
||||
if (_settings.ShouldUsePinyin)
|
||||
{
|
||||
|
|
@ -36,14 +130,44 @@ namespace Flow.Launcher.Infrastructure
|
|||
{
|
||||
if (WordsHelper.HasChinese(content))
|
||||
{
|
||||
var result = WordsHelper.GetPinyin(content, ";");
|
||||
result = GetFirstPinyinChar(result) + result.Replace(";", "");
|
||||
_pinyinCache[content] = result;
|
||||
return result;
|
||||
var resultList = WordsHelper.GetPinyinList(content);
|
||||
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
TranslationMapping map = new TranslationMapping();
|
||||
|
||||
bool pre = false;
|
||||
|
||||
for (int i = 0; i < resultList.Length; i++)
|
||||
{
|
||||
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
|
||||
{
|
||||
map.AddNewIndex(i, resultBuilder.Length, resultList[i].Length + 1);
|
||||
resultBuilder.Append(' ');
|
||||
resultBuilder.Append(resultList[i]);
|
||||
pre = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pre)
|
||||
{
|
||||
pre = false;
|
||||
resultBuilder.Append(' ');
|
||||
}
|
||||
|
||||
resultBuilder.Append(resultList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
map.endConstruct();
|
||||
|
||||
var key = resultBuilder.ToString();
|
||||
map.setKey(key);
|
||||
|
||||
return _pinyinCache[content] = (key, map);
|
||||
}
|
||||
else
|
||||
{
|
||||
return content;
|
||||
return (content, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -53,13 +177,8 @@ namespace Flow.Launcher.Infrastructure
|
|||
}
|
||||
else
|
||||
{
|
||||
return content;
|
||||
return (content, null);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFirstPinyinChar(string content)
|
||||
{
|
||||
return string.Concat(content.Split(';').Select(x => x.First()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
|
|
@ -22,7 +23,22 @@ namespace Flow.Launcher.Infrastructure
|
|||
Log.Debug(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This stopwatch will appear only in Debug mode
|
||||
/// </summary>
|
||||
public static async Task<long> DebugAsync(string message, Func<Task> action)
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
await action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Debug(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
public static long Normal(string message, Action action)
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
|
|
@ -34,6 +50,20 @@ namespace Flow.Launcher.Infrastructure
|
|||
Log.Info(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
public static async Task<long> NormalAsync(string message, Func<Task> action)
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
await action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Info(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void StartCount(string name, Action action)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
|
|
@ -11,7 +11,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
/// </summary>
|
||||
public class JsonStrorage<T>
|
||||
{
|
||||
private readonly JsonSerializerSettings _serializerSettings;
|
||||
private readonly JsonSerializerOptions _serializerSettings;
|
||||
private T _data;
|
||||
// need a new directory name
|
||||
public const string DirectoryName = "Settings";
|
||||
|
|
@ -24,10 +24,9 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
{
|
||||
// use property initialization instead of DefaultValueAttribute
|
||||
// easier and flexible for default value of object
|
||||
_serializerSettings = new JsonSerializerSettings
|
||||
_serializerSettings = new JsonSerializerOptions
|
||||
{
|
||||
ObjectCreationHandling = ObjectCreationHandling.Replace,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
IgnoreNullValues = false
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +55,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
{
|
||||
try
|
||||
{
|
||||
_data = JsonConvert.DeserializeObject<T>(searlized, _serializerSettings);
|
||||
_data = JsonSerializer.Deserialize<T>(searlized, _serializerSettings);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
|
|
@ -77,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
BackupOriginFile();
|
||||
}
|
||||
|
||||
_data = JsonConvert.DeserializeObject<T>("{}", _serializerSettings);
|
||||
_data = JsonSerializer.Deserialize<T>("{}", _serializerSettings);
|
||||
Save();
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +93,8 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
|
||||
public void Save()
|
||||
{
|
||||
string serialized = JsonConvert.SerializeObject(_data, Formatting.Indented);
|
||||
string serialized = JsonSerializer.Serialize(_data, new JsonSerializerOptions() { WriteIndented = true });
|
||||
|
||||
File.WriteAllText(FilePath, serialized);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,62 +44,24 @@ namespace Flow.Launcher.Infrastructure
|
|||
/// </summary>
|
||||
public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption opt)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult(false, UserSettingSearchPrecision);
|
||||
if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query))
|
||||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
|
||||
query = query.Trim();
|
||||
TranslationMapping map;
|
||||
(stringToCompare, map) = _alphabet?.Translate(stringToCompare) ?? (stringToCompare, null);
|
||||
|
||||
stringToCompare = _alphabet?.Translate(stringToCompare) ?? stringToCompare;
|
||||
|
||||
// This also can be done by spliting the query
|
||||
|
||||
//(var spaceSplit, var upperSplit) = stringToCompare switch
|
||||
//{
|
||||
// string s when s.Contains(' ') => (s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(w => w.First()),
|
||||
// default(IEnumerable<char>)),
|
||||
// string s when s.Any(c => char.IsUpper(c)) && s.Any(c => char.IsLower(c)) =>
|
||||
// (null, Regex.Split(s, @"(?<!^)(?=[A-Z])").Select(w => w.First())),
|
||||
// _ => ((IEnumerable<char>)null, (IEnumerable<char>)null)
|
||||
//};
|
||||
|
||||
var currentQueryIndex = 0;
|
||||
var currentAcronymQueryIndex = 0;
|
||||
var acronymMatchData = new List<int>();
|
||||
var queryWithoutCase = opt.IgnoreCase ? query.ToLower() : query;
|
||||
|
||||
// preset acronymScore
|
||||
int acronymScore = 100;
|
||||
|
||||
for (int compareIndex = 0; compareIndex < stringToCompare.Length; compareIndex++)
|
||||
{
|
||||
if (currentQueryIndex >= queryWithoutCase.Length)
|
||||
break;
|
||||
|
||||
|
||||
switch (stringToCompare[compareIndex])
|
||||
{
|
||||
case char c when (compareIndex == 0 && queryWithoutCase[currentQueryIndex] == char.ToLower(stringToCompare[compareIndex]))
|
||||
|| (char.IsUpper(c) && char.ToLower(c) == queryWithoutCase[currentQueryIndex])
|
||||
|| (char.IsWhiteSpace(c) && char.ToLower(stringToCompare[++compareIndex]) == queryWithoutCase[currentQueryIndex])
|
||||
|| (char.IsNumber(c) && c == queryWithoutCase[currentQueryIndex]):
|
||||
acronymMatchData.Add(compareIndex);
|
||||
currentQueryIndex++;
|
||||
continue;
|
||||
|
||||
case char c when char.IsWhiteSpace(c):
|
||||
compareIndex++;
|
||||
acronymScore -= 10;
|
||||
break;
|
||||
case char c when char.IsUpper(c) || char.IsNumber(c):
|
||||
acronymScore -= 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (acronymMatchData.Count == query.Length && acronymScore >= 60)
|
||||
return new MatchResult(true, UserSettingSearchPrecision, acronymMatchData, acronymScore);
|
||||
|
||||
var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare;
|
||||
|
||||
|
||||
var querySubstrings = queryWithoutCase.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var querySubstrings = queryWithoutCase.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
|
||||
int currentQuerySubstringIndex = 0;
|
||||
var currentQuerySubstring = querySubstrings[currentQuerySubstringIndex];
|
||||
var currentQuerySubstringCharacterIndex = 0;
|
||||
|
|
@ -114,22 +76,72 @@ namespace Flow.Launcher.Infrastructure
|
|||
var indexList = new List<int>();
|
||||
List<int> spaceIndices = new List<int>();
|
||||
|
||||
for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++)
|
||||
bool spaceMet = false;
|
||||
|
||||
for (var compareStringIndex = 0;
|
||||
compareStringIndex < fullStringToCompareWithoutCase.Length;
|
||||
compareStringIndex++)
|
||||
{
|
||||
if (currentAcronymQueryIndex >= queryWithoutCase.Length
|
||||
|| allQuerySubstringsMatched && acronymScore < (int) UserSettingSearchPrecision)
|
||||
break;
|
||||
|
||||
|
||||
// To maintain a list of indices which correspond to spaces in the string to compare
|
||||
// To populate the list only for the first query substring
|
||||
if (fullStringToCompareWithoutCase[compareStringIndex].Equals(' ') && currentQuerySubstringIndex == 0)
|
||||
if (fullStringToCompareWithoutCase[compareStringIndex] == ' ' && currentQuerySubstringIndex == 0)
|
||||
{
|
||||
spaceIndices.Add(compareStringIndex);
|
||||
}
|
||||
|
||||
if (fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex])
|
||||
// Acronym check
|
||||
if (char.IsUpper(stringToCompare[compareStringIndex]) ||
|
||||
char.IsNumber(stringToCompare[compareStringIndex]) ||
|
||||
char.IsWhiteSpace(stringToCompare[compareStringIndex]) ||
|
||||
spaceMet)
|
||||
{
|
||||
if (fullStringToCompareWithoutCase[compareStringIndex] ==
|
||||
queryWithoutCase[currentAcronymQueryIndex])
|
||||
{
|
||||
currentAcronymQueryIndex++;
|
||||
|
||||
if (!spaceMet)
|
||||
{
|
||||
char currentCompareChar = stringToCompare[compareStringIndex];
|
||||
spaceMet = char.IsWhiteSpace(currentCompareChar);
|
||||
// if is space, no need to check whether upper or digit, though insignificant
|
||||
if (!spaceMet && compareStringIndex == 0 || char.IsUpper(currentCompareChar) ||
|
||||
char.IsDigit(currentCompareChar))
|
||||
{
|
||||
acronymMatchData.Add(compareStringIndex);
|
||||
}
|
||||
}
|
||||
else if (!(spaceMet = char.IsWhiteSpace(stringToCompare[compareStringIndex])))
|
||||
{
|
||||
acronymMatchData.Add(compareStringIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
spaceMet = char.IsWhiteSpace(stringToCompare[compareStringIndex]);
|
||||
// Acronym Penalty
|
||||
if (!spaceMet)
|
||||
{
|
||||
acronymScore -= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Acronym end
|
||||
|
||||
if (allQuerySubstringsMatched || fullStringToCompareWithoutCase[compareStringIndex] !=
|
||||
currentQuerySubstring[currentQuerySubstringCharacterIndex])
|
||||
{
|
||||
matchFoundInPreviousLoop = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (firstMatchIndex < 0)
|
||||
{
|
||||
// first matched char will become the start of the compared string
|
||||
|
|
@ -148,14 +160,16 @@ namespace Flow.Launcher.Infrastructure
|
|||
// in order to do so we need to verify all previous chars are part of the pattern
|
||||
var startIndexToVerify = compareStringIndex - currentQuerySubstringCharacterIndex;
|
||||
|
||||
if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, fullStringToCompareWithoutCase, currentQuerySubstring))
|
||||
if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex,
|
||||
fullStringToCompareWithoutCase, currentQuerySubstring))
|
||||
{
|
||||
matchFoundInPreviousLoop = true;
|
||||
|
||||
// if it's the beginning character of the first query substring that is matched then we need to update start index
|
||||
firstMatchIndex = currentQuerySubstringIndex == 0 ? startIndexToVerify : firstMatchIndex;
|
||||
|
||||
indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex, firstMatchIndexInWord, indexList);
|
||||
indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex,
|
||||
firstMatchIndexInWord, indexList);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,13 +182,16 @@ namespace Flow.Launcher.Infrastructure
|
|||
if (currentQuerySubstringCharacterIndex == currentQuerySubstring.Length)
|
||||
{
|
||||
// if any of the substrings was not matched then consider as all are not matched
|
||||
allSubstringsContainedInCompareString = matchFoundInPreviousLoop && allSubstringsContainedInCompareString;
|
||||
allSubstringsContainedInCompareString =
|
||||
matchFoundInPreviousLoop && allSubstringsContainedInCompareString;
|
||||
|
||||
currentQuerySubstringIndex++;
|
||||
|
||||
allQuerySubstringsMatched = AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length);
|
||||
allQuerySubstringsMatched =
|
||||
AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length);
|
||||
|
||||
if (allQuerySubstringsMatched)
|
||||
break;
|
||||
continue;
|
||||
|
||||
// otherwise move to the next query substring
|
||||
currentQuerySubstring = querySubstrings[currentQuerySubstringIndex];
|
||||
|
|
@ -182,13 +199,22 @@ namespace Flow.Launcher.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
// return acronym Match if possible
|
||||
if (acronymMatchData.Count == query.Length && acronymScore >= (int) UserSettingSearchPrecision)
|
||||
{
|
||||
acronymMatchData = acronymMatchData.Select(x => map?.MapToOriginalIndex(x) ?? x).Distinct().ToList();
|
||||
return new MatchResult(true, UserSettingSearchPrecision, acronymMatchData, acronymScore);
|
||||
}
|
||||
|
||||
// proceed to calculate score if every char or substring without whitespaces matched
|
||||
if (allQuerySubstringsMatched)
|
||||
{
|
||||
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1,
|
||||
lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
|
||||
|
||||
return new MatchResult(true, UserSettingSearchPrecision, indexList, score);
|
||||
var resultList = indexList.Select(x => map?.MapToOriginalIndex(x) ?? x).Distinct().ToList();
|
||||
return new MatchResult(true, UserSettingSearchPrecision, resultList, score);
|
||||
}
|
||||
|
||||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
|
|
@ -203,14 +229,15 @@ namespace Flow.Launcher.Infrastructure
|
|||
}
|
||||
else
|
||||
{
|
||||
int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item)).Where(item => firstMatchIndex > item).FirstOrDefault();
|
||||
int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item))
|
||||
.FirstOrDefault(item => firstMatchIndex > item);
|
||||
int closestSpaceIndex = ind ?? -1;
|
||||
return closestSpaceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
|
||||
string fullStringToCompareWithoutCase, string currentQuerySubstring)
|
||||
string fullStringToCompareWithoutCase, string currentQuerySubstring)
|
||||
{
|
||||
var allMatch = true;
|
||||
for (int indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++)
|
||||
|
|
@ -225,7 +252,8 @@ namespace Flow.Launcher.Infrastructure
|
|||
return allMatch;
|
||||
}
|
||||
|
||||
private static List<int> GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex, int firstMatchIndexInWord, List<int> indexList)
|
||||
private static List<int> GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
|
||||
int firstMatchIndexInWord, List<int> indexList)
|
||||
{
|
||||
var updatedList = new List<int>();
|
||||
|
||||
|
|
@ -243,10 +271,12 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
private static bool AllQuerySubstringsMatched(int currentQuerySubstringIndex, int querySubstringsLength)
|
||||
{
|
||||
// Acronym won't utilize the substring to match
|
||||
return currentQuerySubstringIndex >= querySubstringsLength;
|
||||
}
|
||||
|
||||
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, bool allSubstringsContainedInCompareString)
|
||||
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen,
|
||||
bool allSubstringsContainedInCompareString)
|
||||
{
|
||||
// A match found near the beginning of a string is scored more than a match found near the end
|
||||
// A match is scored more if the characters in the patterns are closer to each other,
|
||||
|
|
@ -341,7 +371,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
private bool IsSearchPrecisionScoreMet(int rawScore)
|
||||
{
|
||||
return rawScore >= (int)SearchPrecision;
|
||||
return rawScore >= (int) SearchPrecision;
|
||||
}
|
||||
|
||||
private int ScoreAfterSearchPrecisionFilter(int rawScore)
|
||||
|
|
@ -354,4 +384,4 @@ namespace Flow.Launcher.Infrastructure
|
|||
{
|
||||
public bool IgnoreCase { get; set; } = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,80 @@
|
|||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
public enum ProxyProperty
|
||||
{
|
||||
Enabled,
|
||||
Server,
|
||||
Port,
|
||||
UserName,
|
||||
Password
|
||||
}
|
||||
|
||||
public class HttpProxy
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
public string Server { get; set; }
|
||||
public int Port { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
private bool _enabled = false;
|
||||
private string _server;
|
||||
private int _port;
|
||||
private string _userName;
|
||||
private string _password;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
_enabled = value;
|
||||
OnPropertyChanged(ProxyProperty.Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
public string Server
|
||||
{
|
||||
get => _server;
|
||||
set
|
||||
{
|
||||
_server = value;
|
||||
OnPropertyChanged(ProxyProperty.Server);
|
||||
}
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get => _port;
|
||||
set
|
||||
{
|
||||
_port = value;
|
||||
OnPropertyChanged(ProxyProperty.Port);
|
||||
}
|
||||
}
|
||||
|
||||
public string UserName
|
||||
{
|
||||
get => _userName;
|
||||
set
|
||||
{
|
||||
_userName = value;
|
||||
OnPropertyChanged(ProxyProperty.UserName);
|
||||
}
|
||||
}
|
||||
|
||||
public string Password
|
||||
{
|
||||
get => _password;
|
||||
set
|
||||
{
|
||||
_password = value;
|
||||
OnPropertyChanged(ProxyProperty.Password);
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void ProxyPropertyChangedHandler(ProxyProperty property);
|
||||
public event ProxyPropertyChangedHandler PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged(ProxyProperty property)
|
||||
{
|
||||
PropertyChanged?.Invoke(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
metadata.ActionKeyword = settings.ActionKeywords[0];
|
||||
}
|
||||
metadata.Disabled = settings.Disabled;
|
||||
metadata.Priority = settings.Priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -40,7 +41,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
Name = metadata.Name,
|
||||
Version = metadata.Version,
|
||||
ActionKeywords = metadata.ActionKeywords,
|
||||
Disabled = metadata.Disabled
|
||||
Disabled = metadata.Disabled,
|
||||
Priority = metadata.Priority
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +54,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public string Name { get; set; }
|
||||
public string Version { get; set; }
|
||||
public List<string> ActionKeywords { get; set; } // a reference of the action keywords from plugin manager
|
||||
public int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used only to save the state of the plugin in settings
|
||||
|
|
|
|||
|
|
@ -1,18 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.Text.Json.Serialization;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
public class Settings : BaseModel
|
||||
{
|
||||
private string language = "en";
|
||||
|
||||
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
|
||||
public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
|
||||
public bool ShowOpenResultHotkey { get; set; } = true;
|
||||
public string Language { get; set; } = "en";
|
||||
public string Language
|
||||
{
|
||||
get => language; set
|
||||
{
|
||||
language = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public string Theme { get; set; } = Constant.DefaultTheme;
|
||||
public bool UseDropShadowEffect { get; set; } = false;
|
||||
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;
|
||||
|
|
@ -65,9 +73,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public int MaxResultsToShow { get; set; } = 5;
|
||||
public int ActivateTimes { get; set; }
|
||||
|
||||
// Order defaults to 0 or -1, so 1 will let this property appear last
|
||||
[JsonProperty(Order = 1)]
|
||||
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
|
||||
|
||||
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new ObservableCollection<CustomPluginHotkey>();
|
||||
|
||||
public bool DontPromptUpdateMsg { get; set; }
|
||||
|
|
@ -92,8 +98,12 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public HttpProxy Proxy { get; set; } = new HttpProxy();
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public LastQueryMode LastQueryMode { get; set; } = LastQueryMode.Selected;
|
||||
|
||||
|
||||
// This needs to be loaded last by staying at the bottom
|
||||
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
|
||||
}
|
||||
|
||||
public enum LastQueryMode
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<ProjectGuid>{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}</ProjectGuid>
|
||||
<UseWPF>true</UseWPF>
|
||||
<OutputType>Library</OutputType>
|
||||
|
|
@ -14,10 +14,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.3.0</Version>
|
||||
<PackageVersion>1.3.0</PackageVersion>
|
||||
<AssemblyVersion>1.3.0</AssemblyVersion>
|
||||
<FileVersion>1.3.0</FileVersion>
|
||||
<Version>1.3.1</Version>
|
||||
<PackageVersion>1.3.1</PackageVersion>
|
||||
<AssemblyVersion>1.3.1</AssemblyVersion>
|
||||
<FileVersion>1.3.1</FileVersion>
|
||||
<PackageId>Flow.Launcher.Plugin</PackageId>
|
||||
<Authors>Flow-Launcher</Authors>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
|
@ -62,7 +62,6 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
12
Flow.Launcher.Plugin/IAsyncPlugin.cs
Normal file
12
Flow.Launcher.Plugin/IAsyncPlugin.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
public interface IAsyncPlugin
|
||||
{
|
||||
Task<List<Result>> QueryAsync(Query query, CancellationToken token);
|
||||
Task InitAsync(PluginInitContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ namespace Flow.Launcher.Plugin
|
|||
public interface IPlugin
|
||||
{
|
||||
List<Result> Query(Query query);
|
||||
|
||||
void Init(PluginInitContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
|
|
@ -34,7 +35,7 @@ namespace Flow.Launcher.Plugin
|
|||
/// Plugin's in memory data with new content
|
||||
/// added by user.
|
||||
/// </summary>
|
||||
void ReloadAllPluginData();
|
||||
Task ReloadAllPluginData();
|
||||
|
||||
/// <summary>
|
||||
/// Check for new Flow Launcher update
|
||||
|
|
@ -63,12 +64,6 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
void OpenSettingDialog();
|
||||
|
||||
/// <summary>
|
||||
/// Install Flow Launcher plugin
|
||||
/// </summary>
|
||||
/// <param name="path">Plugin path (ends with .flowlauncher)</param>
|
||||
void InstallPlugin(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Get translation of current language
|
||||
/// You need to implement IPluginI18n if you want to support multiple languages for your plugin
|
||||
|
|
|
|||
9
Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs
Normal file
9
Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
public interface IAsyncReloadable
|
||||
{
|
||||
Task ReloadDataAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,16 @@ namespace Flow.Launcher.Plugin
|
|||
{
|
||||
public class PluginInitContext
|
||||
{
|
||||
public PluginInitContext()
|
||||
{
|
||||
}
|
||||
|
||||
public PluginInitContext(PluginMetadata currentPluginMetadata, IPublicAPI api)
|
||||
{
|
||||
CurrentPluginMetadata = currentPluginMetadata;
|
||||
API = api;
|
||||
}
|
||||
|
||||
public PluginMetadata CurrentPluginMetadata { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptOut)]
|
||||
public class PluginMetadata : BaseModel
|
||||
{
|
||||
private string _pluginDirectory;
|
||||
|
|
@ -37,12 +36,15 @@ namespace Flow.Launcher.Plugin
|
|||
public List<string> ActionKeywords { get; set; }
|
||||
|
||||
public string IcoPath { get; set;}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Init time include both plugin load time and init time
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
public class PluginPair
|
||||
{
|
||||
public IPlugin Plugin { get; internal set; }
|
||||
public object Plugin { get; internal set; }
|
||||
public PluginMetadata Metadata { get; internal set; }
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
|
||||
<ProjectGuid>{FF742965-9A80-41A5-B042-D6C7D3A21708}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
|
|
@ -54,7 +54,6 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -7,6 +7,8 @@ using Flow.Launcher.Plugin.SharedCommands;
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
{
|
||||
|
|
@ -17,15 +19,15 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestFixture]
|
||||
public class ExplorerTest
|
||||
{
|
||||
private List<Result> MethodWindowsIndexSearchReturnsZeroResults(Query dummyQuery, string dummyString)
|
||||
private async Task<List<Result>> MethodWindowsIndexSearchReturnsZeroResultsAsync(Query dummyQuery, string dummyString, CancellationToken dummyToken)
|
||||
{
|
||||
return new List<Result>();
|
||||
}
|
||||
|
||||
private List<Result> MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString)
|
||||
{
|
||||
return new List<Result>
|
||||
{
|
||||
return new List<Result>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title="Result 1"
|
||||
|
|
@ -64,10 +66,10 @@ namespace Flow.Launcher.Test.Plugins
|
|||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
|
||||
//When
|
||||
var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath);
|
||||
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(queryString == expectedString,
|
||||
$"Expected string: {expectedString}{Environment.NewLine} " +
|
||||
|
|
@ -112,7 +114,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[TestCase("scope='file:'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString)
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
|
@ -130,7 +132,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
"FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
|
||||
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
|
@ -145,18 +147,19 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[TestCase]
|
||||
public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearch()
|
||||
public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearch()
|
||||
{
|
||||
// Given
|
||||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
|
||||
// When
|
||||
var results = searchManager.TopLevelDirectorySearchBehaviour(
|
||||
MethodWindowsIndexSearchReturnsZeroResults,
|
||||
MethodDirectoryInfoClassSearchReturnsTwoResults,
|
||||
false,
|
||||
var results = await searchManager.TopLevelDirectorySearchBehaviourAsync(
|
||||
MethodWindowsIndexSearchReturnsZeroResultsAsync,
|
||||
MethodDirectoryInfoClassSearchReturnsTwoResults,
|
||||
false,
|
||||
new Query(),
|
||||
"string not used");
|
||||
"string not used",
|
||||
default);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(results.Count == 2,
|
||||
|
|
@ -165,18 +168,19 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[TestCase]
|
||||
public void GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch()
|
||||
public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch()
|
||||
{
|
||||
// Given
|
||||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
// When
|
||||
var results = searchManager.TopLevelDirectorySearchBehaviour(
|
||||
MethodWindowsIndexSearchReturnsZeroResults,
|
||||
var results = await searchManager.TopLevelDirectorySearchBehaviourAsync(
|
||||
MethodWindowsIndexSearchReturnsZeroResultsAsync,
|
||||
MethodDirectoryInfoClassSearchReturnsTwoResults,
|
||||
true,
|
||||
new Query(),
|
||||
"string not used");
|
||||
"string not used",
|
||||
default);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(results.Count == 0,
|
||||
|
|
@ -223,7 +227,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var query = new Query { ActionKeyword = "doc:", Search = "search term" };
|
||||
|
||||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
|
||||
// When
|
||||
var result = searchManager.IsFileContentSearch(query.ActionKeyword);
|
||||
|
||||
|
|
@ -250,7 +254,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual check result is {result} {Environment.NewLine}");
|
||||
|
||||
}
|
||||
|
||||
|
||||
[TestCase(@"C:\SomeFolder\SomeApp", true, @"C:\SomeFolder\")]
|
||||
[TestCase(@"C:\SomeFolder\SomeApp\SomeFile", true, @"C:\SomeFolder\SomeApp\")]
|
||||
[TestCase(@"C:\NonExistentFolder\SomeApp", false, "")]
|
||||
|
|
@ -294,7 +298,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " +
|
||||
"OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " +
|
||||
"scope='file:c:\\SomeFolder'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString)
|
||||
public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
|
@ -308,7 +312,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("c:\\somefolder\\>somefile","*somefile*")]
|
||||
[TestCase("c:\\somefolder\\>somefile", "*somefile*")]
|
||||
[TestCase("c:\\somefolder\\somefile", "somefile*")]
|
||||
[TestCase("c:\\somefolder\\", "*")]
|
||||
public void GivenDirectoryInfoSearch_WhenSearchPatternHotKeyIsSearchAll_ThenSearchCriteriaShouldUseCriteriaString(string path, string expectedString)
|
||||
|
|
|
|||
|
|
@ -15,23 +15,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc
|
|||
ProjectSection(ProjectDependencies) = postProject
|
||||
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
|
||||
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
|
||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {F35190AA-4758-4D9E-A193-E3BDF6AD3567}
|
||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
|
||||
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {049490F0-ECD2-4148-9B39-2135EC346EBE}
|
||||
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginManagement", "Plugins\Flow.Launcher.Plugin.PluginManagement\Flow.Launcher.Plugin.PluginManagement.csproj", "{049490F0-ECD2-4148-9B39-2135EC346EBE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Core", "Flow.Launcher.Core\Flow.Launcher.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Program", "Plugins\Flow.Launcher.Plugin.Program\Flow.Launcher.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}"
|
||||
|
|
@ -46,8 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Sys",
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Url", "Plugins\Flow.Launcher.Plugin.Url\Flow.Launcher.Plugin.Url.csproj", "{A3DCCBCA-ACC1-421D-B16E-210896234C26}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Color", "Plugins\Flow.Launcher.Plugin.Color\Flow.Launcher.Plugin.Color.csproj", "{F35190AA-4758-4D9E-A193-E3BDF6AD3567}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFD651C7-0546-441F-BC8C-D4EE8FD01EA7}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.gitattributes = .gitattributes
|
||||
|
|
@ -71,6 +66,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explor
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ProcessKiller", "Plugins\Flow.Launcher.Plugin.ProcessKiller\Flow.Launcher.Plugin.ProcessKiller.csproj", "{588088F4-3262-4F9F-9663-A05DE12534C3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginsManager", "Plugins\Flow.Launcher.Plugin.PluginsManager\Flow.Launcher.Plugin.PluginsManager.csproj", "{4792A74A-0CEA-4173-A8B2-30E6764C6217}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -129,18 +126,6 @@ Global
|
|||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -226,18 +211,6 @@ Global
|
|||
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -298,24 +271,35 @@ Global
|
|||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
ShutdownMode="OnMainWindowClose"
|
||||
Startup="OnStartup">
|
||||
Startup="OnStartupAsync">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
private void OnStartup(object sender, StartupEventArgs e)
|
||||
private async void OnStartupAsync(object sender, StartupEventArgs e)
|
||||
{
|
||||
Stopwatch.Normal("|App.OnStartup|Startup cost", () =>
|
||||
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
|
||||
{
|
||||
_portable.PreStartCleanUpAfterPortabilityUpdate();
|
||||
|
||||
|
|
@ -68,9 +68,10 @@ namespace Flow.Launcher
|
|||
|
||||
PluginManager.LoadPlugins(_settings.PluginSettings);
|
||||
_mainVM = new MainViewModel(_settings);
|
||||
var window = new MainWindow(_settings, _mainVM);
|
||||
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
|
||||
PluginManager.InitializePlugins(API);
|
||||
await PluginManager.InitializePlugins(API);
|
||||
var window = new MainWindow(_settings, _mainVM);
|
||||
|
||||
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
|
||||
|
||||
Current.MainWindow = window;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<StartupObject>Flow.Launcher.App</StartupObject>
|
||||
|
|
@ -60,6 +60,12 @@
|
|||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Images\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Images\*.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -72,13 +78,14 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="InputSimulator" Version="1.0.4" />
|
||||
<PackageReference Include="ModernWpfUI" Version="0.8.3" />
|
||||
<PackageReference Include="ModernWpfUI" Version="0.9.2" />
|
||||
<PackageReference Include="NHotkey.Wpf" Version="1.2.1" />
|
||||
<PackageReference Include="NuGet.CommandLine" Version="5.4.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="2.5.13" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.3.1" />
|
||||
<PackageReference Include="SharpVectors" Version="1.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -87,116 +94,7 @@
|
|||
<ProjectReference Include="..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Images\app.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Resource Include="Images\app.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<None Update="Images\app_error.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\Browser.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\calculator.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\cancel.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\close.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\cmd.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\color.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\copy.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\down.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\EXE.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\file.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\find.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\folder.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\history.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\image.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\Link.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\lock.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\logoff.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\mainsearch.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\New Message.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\ok.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\open.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\plugin.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\recyclebin.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\restart.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\search.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\settings.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\shutdown.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\sleep.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\up.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\update.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Images\warning.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="taskkill /f /fi "IMAGENAME eq Flow.Launcher.exe"" />
|
||||
</Target>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="powershell.exe -NoProfile -ExecutionPolicy Bypass -File $(SolutionDir)Scripts\post_build.ps1 $(ConfigurationName) $(SolutionDir) $(TargetPath)" />
|
||||
</Target>
|
||||
</Project>
|
||||
10
Flow.Launcher/Images/mainsearch.svg
Normal file
10
Flow.Launcher/Images/mainsearch.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="1200px" height="1200px" viewBox="0 0 12000 12000" preserveAspectRatio="xMidYMid meet">
|
||||
<g id="layer101" fill="#000000" stroke="none">
|
||||
</g>
|
||||
<g id="layer102" fill="#555555" stroke="none">
|
||||
<path d="M10354 10962 c-28 -11 -75 -35 -105 -55 -33 -21 -773 -754 -1879 -1861 -1004 -1004 -1829 -1826 -1834 -1826 -4 0 -38 22 -75 48 -248 179 -578 351 -869 453 -398 139 -790 198 -1232 185 -748 -20 -1407 -259 -2020 -732 -209 -161 -508 -475 -676 -709 -270 -377 -476 -847 -567 -1295 -53 -261 -67 -406 -67 -700 0 -340 26 -556 105 -861 128 -499 376 -976 715 -1374 86 -101 312 -324 410 -406 521 -434 1162 -709 1830 -784 181 -20 577 -20 758 0 657 75 1252 323 1782 744 144 114 451 426 556 566 176 233 281 404 393 635 223 465 332 947 332 1470 0 394 -50 705 -174 1082 -53 160 -62 182 -135 343 -85 186 -212 407 -332 575 -28 39 -50 73 -50 78 0 4 826 833 1835 1842 1386 1386 1843 1849 1869 1894 21 34 42 90 52 134 14 64 15 85 4 146 -28 163 -140 311 -290 383 -69 34 -83 37 -180 40 -85 3 -115 0 -156 -15z m-5669 -3912 c529 -49 1009 -241 1415 -566 109 -88 296 -275 384 -384 667 -833 762 -1990 237 -2910 -352 -619 -923 -1053 -1621 -1234 -394 -101 -878 -101 -1270 1 -382 99 -690 253 -992 496 -501 402 -828 974 -930 1627 -31 194 -31 576 0 770 40 255 120 520 225 740 326 682 944 1192 1677 1383 157 41 275 61 480 80 81 8 293 6 395 -3z"/>
|
||||
</g>
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -43,6 +43,8 @@
|
|||
<system:String x:Key="actionKeywords">Action keyword:</system:String>
|
||||
<system:String x:Key="currentActionKeywords">Current action keyword:</system:String>
|
||||
<system:String x:Key="newActionKeyword">New action keyword:</system:String>
|
||||
<system:String x:Key="currentPriority">Current Priority: </system:String>
|
||||
<system:String x:Key="newPriority">New Priority: </system:String>
|
||||
<system:String x:Key="pluginDirectory">Plugin Directory</system:String>
|
||||
<system:String x:Key="author">Author</system:String>
|
||||
<system:String x:Key="plugin_init_time">Init time:</system:String>
|
||||
|
|
@ -72,7 +74,7 @@
|
|||
<system:String x:Key="deleteCustomHotkeyWarning">Are you sure you want to delete {0} plugin hotkey?</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Query window shadow effect</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">Shadow effect has a substantial usage of GPU.</system:String>
|
||||
<system:String x:Key="shadowEffectPerformance">Not recommended if you computer performance is limited.</system:String>
|
||||
<system:String x:Key="shadowEffectPerformance">Not recommended if your computer performance is limited.</system:String>
|
||||
|
||||
<!--Setting Proxy-->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
@ -104,6 +106,10 @@
|
|||
</system:String>
|
||||
<system:String x:Key="releaseNotes">Release Notes</system:String>
|
||||
|
||||
<!--Priority Setting Dialog-->
|
||||
<system:String x:Key="priority_tips">Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number</system:String>
|
||||
<system:String x:Key="invalidPriority">Please provide an valid integer for Priority!</system:String>
|
||||
|
||||
<!--Action Keyword Setting Dialog-->
|
||||
<system:String x:Key="oldActionKeywords">Old Action Keyword</system:String>
|
||||
<system:String x:Key="newActionKeywords">New Action Keyword</system:String>
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
<system:String x:Key="registerHotkeyFailed">Nepodarilo sa registrovať klávesovú skratku {0}</system:String>
|
||||
<system:String x:Key="couldnotStartCmd">Nepodarilo sa spustiť {0}</system:String>
|
||||
<system:String x:Key="invalidFlowLauncherPluginFileFormat">Neplatný formát súboru pre plugin Flow Launchera</system:String>
|
||||
<system:String x:Key="setAsTopMostInThisQuery">Pri tomto dopyte umiestniť navrchu</system:String>
|
||||
<system:String x:Key="cancelTopMostInThisQuery">Zrušiť umiestnenie navrchu pri tomto dopyte</system:String>
|
||||
<system:String x:Key="setAsTopMostInThisQuery">Pri tomto zadaní umiestniť navrchu</system:String>
|
||||
<system:String x:Key="cancelTopMostInThisQuery">Zrušiť umiestnenie navrchu pri tomto zadaní</system:String>
|
||||
<system:String x:Key="executeQuery">Spustiť dopyt: {0}</system:String>
|
||||
<system:String x:Key="lastExecuteTime">Posledný čas realizácie: {0}</system:String>
|
||||
<system:String x:Key="iconTrayOpen">Otvoriť</system:String>
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
<system:String x:Key="dontPromptUpdateMsg">Nezobrazovať upozornenia na novú verziu</system:String>
|
||||
<system:String x:Key="rememberLastLocation">Zapamätať si posledné umiestnenie</system:String>
|
||||
<system:String x:Key="language">Jazyk</system:String>
|
||||
<system:String x:Key="lastQueryMode">Posledný dopyt</system:String>
|
||||
<system:String x:Key="lastQueryMode">Posledné vyhľadávanie</system:String>
|
||||
<system:String x:Key="LastQueryPreserved">Ponechať</system:String>
|
||||
<system:String x:Key="LastQuerySelected">Označiť</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Vymazať</system:String>
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
<system:String x:Key="hideOnStartup">Schovať Flow Launcher po spustení</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Schovať ikonu z oblasti oznámení</system:String>
|
||||
<system:String x:Key="querySearchPrecision">Presnosť vyhľadávania</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Dá sa použiť Pinyin</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Použiť Pinyin</system:String>
|
||||
|
||||
<!--Setting Plugin-->
|
||||
<system:String x:Key="plugin">Plugin</system:String>
|
||||
|
|
@ -45,8 +45,8 @@
|
|||
<system:String x:Key="newActionKeyword">Nová akcia skratky:</system:String>
|
||||
<system:String x:Key="pluginDirectory">Priečinok s pluginmi</system:String>
|
||||
<system:String x:Key="author">Autor</system:String>
|
||||
<system:String x:Key="plugin_init_time">Príprava: {0}ms</system:String>
|
||||
<system:String x:Key="plugin_query_time">Čas dopytu: {0}ms</system:String>
|
||||
<system:String x:Key="plugin_init_time">Príprava:</system:String>
|
||||
<system:String x:Key="plugin_query_time">Čas dopytu:</system:String>
|
||||
|
||||
<!--Setting Theme-->
|
||||
<system:String x:Key="theme">Motív</system:String>
|
||||
|
|
@ -96,11 +96,11 @@
|
|||
<system:String x:Key="version">Verzia</system:String>
|
||||
<system:String x:Key="about_activate_times">Flow Launcher bol aktivovaný {0}-krát</system:String>
|
||||
<system:String x:Key="checkUpdates">Skontrolovať aktualizácie</system:String>
|
||||
<system:String x:Key="newVersionTips">Je dostupná nová verzia {0}, prosím, reštartujte Flow Launcher.</system:String>
|
||||
<system:String x:Key="newVersionTips">Je dostupná nová verzia {0}, chcete reštartovať Flow Launcher, aby sa mohol aktualizovať?</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">Kontrola aktualizácií zlyhala, prosím, skontrolujte pripojenie na internet a nastavenie proxy k api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com,
|
||||
alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácií.
|
||||
alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácie.
|
||||
</system:String>
|
||||
<system:String x:Key="releaseNotes">Poznámky k vydaniu</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:converters="clr-namespace:Flow.Launcher.Converters"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
mc:Ignorable="d"
|
||||
Title="Flow Launcher"
|
||||
Topmost="True"
|
||||
|
|
@ -22,7 +23,6 @@
|
|||
Loaded="OnLoaded"
|
||||
Initialized="OnInitialized"
|
||||
Closing="OnClosing"
|
||||
Drop="OnDrop"
|
||||
LocationChanged="OnLocationChanged"
|
||||
Deactivated="OnDeactivated"
|
||||
PreviewKeyDown="OnKeyDown"
|
||||
|
|
@ -93,7 +93,8 @@
|
|||
</ContextMenu>
|
||||
</TextBox.ContextMenu>
|
||||
</TextBox>
|
||||
<Image Source="{Binding Image, IsAsync=True}" Width="48" HorizontalAlignment="Right" />
|
||||
<svgc:SvgControl Source="{Binding Image}" HorizontalAlignment="Right" Width="48" Height="48"
|
||||
Background="Transparent"/>
|
||||
</Grid>
|
||||
<Line x:Name="ProgressBar" HorizontalAlignment="Right"
|
||||
Style="{DynamicResource PendingLineStyle}" Visibility="{Binding ProgressBarVisibility, Mode=TwoWay}"
|
||||
|
|
|
|||
|
|
@ -52,12 +52,14 @@ namespace Flow.Launcher
|
|||
|
||||
private void OnInitialized(object sender, EventArgs e)
|
||||
{
|
||||
// show notify icon when flowlauncher is hided
|
||||
InitializeNotifyIcon();
|
||||
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs _)
|
||||
{
|
||||
// show notify icon when flowlauncher is hidden
|
||||
InitializeNotifyIcon();
|
||||
|
||||
// todo is there a way to set blur only once?
|
||||
ThemeManager.Instance.SetBlurForWindow();
|
||||
WindowsInteropHelper.DisableControlBox(this);
|
||||
|
|
@ -87,11 +89,17 @@ namespace Flow.Launcher
|
|||
};
|
||||
_settings.PropertyChanged += (o, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(Settings.HideNotifyIcon))
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
_notifyIcon.Visible = !_settings.HideNotifyIcon;
|
||||
case nameof(Settings.HideNotifyIcon):
|
||||
_notifyIcon.Visible = !_settings.HideNotifyIcon;
|
||||
break;
|
||||
case nameof(Settings.Language):
|
||||
UpdateNotifyIconText();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
InitializePosition();
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +111,18 @@ namespace Flow.Launcher
|
|||
_settings.WindowLeft = Left;
|
||||
}
|
||||
|
||||
private void UpdateNotifyIconText()
|
||||
{
|
||||
var menu = _notifyIcon.ContextMenuStrip;
|
||||
var open = menu.Items[0];
|
||||
var setting = menu.Items[1];
|
||||
var exit = menu.Items[2];
|
||||
|
||||
open.Text = InternationalizationManager.Instance.GetTranslation("iconTrayOpen");
|
||||
setting.Text = InternationalizationManager.Instance.GetTranslation("iconTraySettings");
|
||||
exit.Text = InternationalizationManager.Instance.GetTranslation("iconTrayExit");
|
||||
}
|
||||
|
||||
private void InitializeNotifyIcon()
|
||||
{
|
||||
_notifyIcon = new NotifyIcon
|
||||
|
|
@ -179,25 +199,6 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
{
|
||||
// Note that you can have more than one file.
|
||||
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
if (files[0].ToLower().EndsWith(".flowlauncher"))
|
||||
{
|
||||
PluginManager.InstallPlugin(files[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidFlowLauncherPluginFileFormat"));
|
||||
}
|
||||
}
|
||||
e.Handled = false;
|
||||
}
|
||||
|
||||
private void OnPreviewDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
|
|
@ -294,7 +295,5 @@ namespace Flow.Launcher
|
|||
_viewModel.QueryTextCursorMovedToEnd = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
45
Flow.Launcher/PriorityChangeWindow.xaml
Normal file
45
Flow.Launcher/PriorityChangeWindow.xaml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<Window x:Class="Flow.Launcher.PriorityChangeWindow"
|
||||
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"
|
||||
xmlns:local="clr-namespace:Flow.Launcher"
|
||||
Loaded="PriorityChangeWindow_Loaded"
|
||||
mc:Ignorable="d"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Title="PriorityChangeWindow" Height="250" Width="300">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="60"/>
|
||||
<RowDefinition Height="75"/>
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock FontSize="14" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left" Text="{DynamicResource currentPriority}" />
|
||||
<TextBlock x:Name="OldPriority" Grid.Row="0" Grid.Column="1" Margin="170 10 10 10" FontSize="14"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock FontSize="14" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left" Text="{DynamicResource newPriority}" />
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="1">
|
||||
<TextBox x:Name="tbAction" Margin="140 10 15 10" Width="105" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.ColumnSpan="1" Grid.Column="1" Foreground="Gray"
|
||||
Text="{DynamicResource priority_tips}" TextWrapping="Wrap"
|
||||
Margin="0,0,20,0"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="3" Grid.Column="1">
|
||||
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="30"
|
||||
Content="{DynamicResource cancel}" />
|
||||
<Button x:Name="btnDone" Margin="10 0 10 0" Width="80" Height="30" Click="btnDone_OnClick">
|
||||
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
69
Flow.Launcher/PriorityChangeWindow.xaml.cs
Normal file
69
Flow.Launcher/PriorityChangeWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction Logic of PriorityChangeWindow.xaml
|
||||
/// </summary>
|
||||
public partial class PriorityChangeWindow : Window
|
||||
{
|
||||
private readonly PluginPair plugin;
|
||||
private Settings settings;
|
||||
private readonly Internationalization translater = InternationalizationManager.Instance;
|
||||
private readonly PluginViewModel pluginViewModel;
|
||||
|
||||
public PriorityChangeWindow(string pluginId, Settings settings, PluginViewModel pluginViewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
plugin = PluginManager.GetPluginForId(pluginId);
|
||||
this.settings = settings;
|
||||
this.pluginViewModel = pluginViewModel;
|
||||
if (plugin == null)
|
||||
{
|
||||
MessageBox.Show(translater.GetTranslation("cannotFindSpecifiedPlugin"));
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnDone_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (int.TryParse(tbAction.Text.Trim(), out var newPriority))
|
||||
{
|
||||
pluginViewModel.ChangePriority(newPriority);
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
string msg = translater.GetTranslation("invalidPriority");
|
||||
MessageBox.Show(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void PriorityChangeWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OldPriority.Text = pluginViewModel.Priority.ToString();
|
||||
tbAction.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,12 +7,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
|
||||
<PublishDir>..\Output\Release\</PublishDir>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
<PublishReadyToRun>False</PublishReadyToRun>
|
||||
<PublishReadyToRun>True</PublishReadyToRun>
|
||||
<PublishTrimmed>False</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -78,9 +78,9 @@ namespace Flow.Launcher
|
|||
ImageLoader.Save();
|
||||
}
|
||||
|
||||
public void ReloadAllPluginData()
|
||||
public Task ReloadAllPluginData()
|
||||
{
|
||||
PluginManager.ReloadData();
|
||||
return PluginManager.ReloadData();
|
||||
}
|
||||
|
||||
public void ShowMsg(string title, string subTitle = "", string iconPath = "")
|
||||
|
|
@ -92,7 +92,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var msg = useMainWindowAsOwner ? new Msg {Owner = Application.Current.MainWindow} : new Msg();
|
||||
var msg = useMainWindowAsOwner ? new Msg { Owner = Application.Current.MainWindow } : new Msg();
|
||||
msg.Show(title, subTitle, iconPath);
|
||||
});
|
||||
}
|
||||
|
|
@ -115,11 +115,6 @@ namespace Flow.Launcher
|
|||
_mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public void InstallPlugin(string path)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() => PluginManager.InstallPlugin(path));
|
||||
}
|
||||
|
||||
public string GetTranslation(string key)
|
||||
{
|
||||
return InternationalizationManager.Instance.GetTranslation(key);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
d:DataContext="{d:DesignInstance vm:ResultsViewModel}"
|
||||
MaxHeight="{Binding MaxHeight}"
|
||||
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=OneWayToSource}"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||
HorizontalContentAlignment="Stretch" ItemsSource="{Binding Results}"
|
||||
Margin="{Binding Margin}"
|
||||
Visibility="{Binding Visbility}"
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
<ColumnDefinition Width="0" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="ImageIcon" Width="32" Height="32" HorizontalAlignment="Left"
|
||||
Source="{Binding Image ,IsAsync=True}" />
|
||||
Source="{Binding Image.Value}" />
|
||||
<Grid Margin="5 0 5 0" Grid.Column="1" HorizontalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
xmlns:userSettings="clr-namespace:Flow.Launcher.Infrastructure.UserSettings;assembly=Flow.Launcher.Infrastructure"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
x:Class="Flow.Launcher.SettingWindow"
|
||||
mc:Ignorable="d"
|
||||
Icon="Images\app.png"
|
||||
|
|
@ -172,10 +173,14 @@
|
|||
<TextBlock Text="{Binding PluginPair.Metadata.Description}"
|
||||
Grid.Row="1" Opacity="0.5" />
|
||||
<DockPanel Grid.Row="2" Margin="0 10 0 8" HorizontalAlignment="Right">
|
||||
|
||||
<TextBlock Text="Priority" Margin="20,0,0,0"/>
|
||||
<TextBlock Text="{Binding Priority}"
|
||||
ToolTip="Change Plugin Results Priority"
|
||||
Margin="5 0 0 0" Cursor="Hand" Foreground="Blue"
|
||||
MouseUp="OnPluginPriorityClick"/>
|
||||
<TextBlock Text="{DynamicResource actionKeywords}"
|
||||
Visibility="{Binding ActionKeywordsVisibility}"
|
||||
Margin="20 0 0 0"/>
|
||||
Margin="5 0 0 0"/>
|
||||
<TextBlock Text="{Binding ActionKeywordsText}"
|
||||
Visibility="{Binding ActionKeywordsVisibility}"
|
||||
ToolTip="Change Action Keywords"
|
||||
|
|
@ -252,7 +257,8 @@
|
|||
Text="{DynamicResource hiThere}" IsReadOnly="True"
|
||||
Style="{DynamicResource QueryBoxStyle}"
|
||||
Margin="18 0 56 0" />
|
||||
<Image Source="{Binding ThemeImage}" HorizontalAlignment="Right" />
|
||||
<svgc:SvgControl Source="{Binding ThemeImage}" Height="48" Width="48" HorizontalAlignment="Right"
|
||||
Background="Transparent" />
|
||||
<ContentControl Grid.Row="1">
|
||||
<flowlauncher:ResultListBox DataContext="{Binding PreviewResults, Mode=OneTime}" Visibility="Visible" />
|
||||
</ContentControl>
|
||||
|
|
@ -429,8 +435,8 @@
|
|||
<TextBlock Grid.Row="0" Grid.ColumnSpan="2" Text="{Binding ActivatedTimes, Mode=OneWay}" FontSize="12" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{DynamicResource website}"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left">
|
||||
<Hyperlink NavigateUri="{Binding Github, Mode=OneWay}" RequestNavigate="OnRequestNavigate">
|
||||
<Run Text="{Binding Github, Mode=OneWay}" />
|
||||
<Hyperlink NavigateUri="{Binding Website, Mode=OneWay}" RequestNavigate="OnRequestNavigate">
|
||||
<Run Text="{Binding Website, Mode=OneWay}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Version" />
|
||||
|
|
|
|||
|
|
@ -206,7 +206,16 @@ namespace Flow.Launcher
|
|||
{
|
||||
var id = viewModel.SelectedPlugin.PluginPair.Metadata.ID;
|
||||
// used to sync the current status from the plugin manager into the setting to keep consistency after save
|
||||
settings.PluginSettings.Plugins[id].Disabled = viewModel.SelectedPlugin.PluginPair.Metadata.Disabled;
|
||||
settings.PluginSettings.Plugins[id].Disabled = viewModel.SelectedPlugin.PluginPair.Metadata.Disabled;
|
||||
}
|
||||
|
||||
private void OnPluginPriorityClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left)
|
||||
{
|
||||
PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(viewModel.SelectedPlugin.PluginPair.Metadata.ID, settings, viewModel.SelectedPlugin);
|
||||
priorityChangeWindow.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPluginActionKeywordsClick(object sender, MouseButtonEventArgs e)
|
||||
|
|
@ -281,5 +290,6 @@ namespace Flow.Launcher
|
|||
{
|
||||
FilesFolders.OpenPath(Path.Combine(DataLocation.DataDirectory(), Constant.Themes));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Storage
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Storage
|
||||
|
|
@ -8,7 +8,6 @@ namespace Flow.Launcher.Storage
|
|||
// todo this class is not thread safe.... but used from multiple threads.
|
||||
public class TopMostRecord
|
||||
{
|
||||
[JsonProperty]
|
||||
private Dictionary<string, Record> records = new Dictionary<string, Record>();
|
||||
|
||||
internal bool IsTopMost(Result result)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -7,7 +6,6 @@ namespace Flow.Launcher.Storage
|
|||
{
|
||||
public class UserSelectedRecord
|
||||
{
|
||||
[JsonProperty]
|
||||
private Dictionary<string, int> records = new Dictionary<string, int>();
|
||||
|
||||
public void Add(Result result)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using NHotkey;
|
||||
|
|
@ -21,6 +22,7 @@ using Flow.Launcher.Plugin.SharedCommands;
|
|||
using Flow.Launcher.Storage;
|
||||
using System.Windows.Media;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
|
|
@ -48,6 +50,9 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
private readonly Internationalization _translator = InternationalizationManager.Instance;
|
||||
|
||||
private BufferBlock<ResultsForUpdate> _resultsUpdateQueue;
|
||||
private Task _resultsViewUpdateTask;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
|
@ -75,12 +80,44 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
InitializeKeyCommands();
|
||||
RegisterResultsUpdatedEvent();
|
||||
RegisterViewUpdate();
|
||||
|
||||
SetHotkey(_settings.Hotkey, OnHotkey);
|
||||
SetCustomPluginHotkey();
|
||||
SetOpenResultModifiers();
|
||||
}
|
||||
|
||||
private void RegisterViewUpdate()
|
||||
{
|
||||
_resultsUpdateQueue = new BufferBlock<ResultsForUpdate>();
|
||||
_resultsViewUpdateTask =
|
||||
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
|
||||
async Task updateAction()
|
||||
{
|
||||
while (await _resultsUpdateQueue.OutputAvailableAsync())
|
||||
{
|
||||
await Task.Delay(20);
|
||||
_resultsUpdateQueue.TryReceiveAll(out var queue);
|
||||
UpdateResultView(queue.Where(r => !r.Token.IsCancellationRequested));
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
void continueAction(Task t)
|
||||
{
|
||||
#if DEBUG
|
||||
throw t.Exception;
|
||||
#else
|
||||
Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}");
|
||||
_resultsViewUpdateTask =
|
||||
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterResultsUpdatedEvent()
|
||||
{
|
||||
foreach (var pair in PluginManager.GetPluginsForInterface<IResultUpdated>())
|
||||
|
|
@ -88,11 +125,11 @@ namespace Flow.Launcher.ViewModel
|
|||
var plugin = (IResultUpdated)pair.Plugin;
|
||||
plugin.ResultsUpdated += (s, e) =>
|
||||
{
|
||||
Task.Run(() =>
|
||||
if (e.Query.RawQuery == QueryText) // TODO: allow cancellation
|
||||
{
|
||||
PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query);
|
||||
UpdateResultView(e.Results, pair.Metadata, e.Query);
|
||||
}, _updateToken);
|
||||
_resultsUpdateQueue.Post(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -112,25 +149,13 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
});
|
||||
|
||||
SelectNextItemCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SelectedResults.SelectNextResult();
|
||||
});
|
||||
SelectNextItemCommand = new RelayCommand(_ => { SelectedResults.SelectNextResult(); });
|
||||
|
||||
SelectPrevItemCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SelectedResults.SelectPrevResult();
|
||||
});
|
||||
SelectPrevItemCommand = new RelayCommand(_ => { SelectedResults.SelectPrevResult(); });
|
||||
|
||||
SelectNextPageCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SelectedResults.SelectNextPage();
|
||||
});
|
||||
SelectNextPageCommand = new RelayCommand(_ => { SelectedResults.SelectNextPage(); });
|
||||
|
||||
SelectPrevPageCommand = new RelayCommand(_ =>
|
||||
{
|
||||
SelectedResults.SelectPrevPage();
|
||||
});
|
||||
SelectPrevPageCommand = new RelayCommand(_ => { SelectedResults.SelectPrevPage(); });
|
||||
|
||||
SelectFirstResultCommand = new RelayCommand(_ => SelectedResults.SelectFirstResult());
|
||||
|
||||
|
|
@ -208,6 +233,7 @@ namespace Flow.Launcher.ViewModel
|
|||
public ResultsViewModel History { get; private set; }
|
||||
|
||||
private string _queryText;
|
||||
|
||||
public string QueryText
|
||||
{
|
||||
get { return _queryText; }
|
||||
|
|
@ -228,10 +254,12 @@ namespace Flow.Launcher.ViewModel
|
|||
QueryTextCursorMovedToEnd = true;
|
||||
QueryText = queryText;
|
||||
}
|
||||
|
||||
public bool LastQuerySelected { get; set; }
|
||||
public bool QueryTextCursorMovedToEnd { get; set; }
|
||||
|
||||
private ResultsViewModel _selectedResults;
|
||||
|
||||
private ResultsViewModel SelectedResults
|
||||
{
|
||||
get { return _selectedResults; }
|
||||
|
|
@ -263,6 +291,7 @@ namespace Flow.Launcher.ViewModel
|
|||
QueryText = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
_selectedResults.Visbility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
|
@ -284,7 +313,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public string OpenResultCommandModifiers { get; private set; }
|
||||
|
||||
public ImageSource Image => ImageLoader.Load(Constant.QueryTextBoxIconImagePath);
|
||||
public string Image => Constant.QueryTextBoxIconImagePath;
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -323,7 +352,7 @@ namespace Flow.Launcher.ViewModel
|
|||
var filtered = results.Where
|
||||
(
|
||||
r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet()
|
||||
|| StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet()
|
||||
|| StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet()
|
||||
).ToList();
|
||||
ContextMenu.AddResults(filtered, id);
|
||||
}
|
||||
|
|
@ -395,48 +424,80 @@ namespace Flow.Launcher.ViewModel
|
|||
RemoveOldQueryResults(query);
|
||||
|
||||
_lastQuery = query;
|
||||
Task.Delay(200, currentCancellationToken).ContinueWith(_ =>
|
||||
{ // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
|
||||
if (currentUpdateSource == _updateSource && _isQueryRunning)
|
||||
{
|
||||
ProgressBarVisibility = Visibility.Visible;
|
||||
}
|
||||
}, currentCancellationToken);
|
||||
|
||||
var plugins = PluginManager.ValidPluginsForQuery(query);
|
||||
Task.Run(() =>
|
||||
Task.Run(async () =>
|
||||
{
|
||||
if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
|
||||
{
|
||||
// Wait 45 millisecond for query change in global query
|
||||
// if query changes, return so that it won't be calculated
|
||||
await Task.Delay(45, currentCancellationToken);
|
||||
if (currentCancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
}
|
||||
|
||||
_ = Task.Delay(200, currentCancellationToken).ContinueWith(_ =>
|
||||
{
|
||||
// start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
|
||||
if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning)
|
||||
{
|
||||
ProgressBarVisibility = Visibility.Visible;
|
||||
}
|
||||
}, currentCancellationToken);
|
||||
|
||||
// so looping will stop once it was cancelled
|
||||
var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken };
|
||||
|
||||
Task[] tasks = new Task[plugins.Count];
|
||||
|
||||
try
|
||||
{
|
||||
Parallel.ForEach(plugins, parallelOptions, plugin =>
|
||||
for (var i = 0; i < plugins.Count; i++)
|
||||
{
|
||||
if (!plugin.Metadata.Disabled)
|
||||
{
|
||||
var results = PluginManager.QueryForPlugin(plugin, query);
|
||||
UpdateResultView(results, plugin.Metadata, query);
|
||||
}
|
||||
});
|
||||
if (!plugins[i].Metadata.Disabled)
|
||||
tasks[i] = QueryTask(plugins[i], query, currentCancellationToken);
|
||||
else tasks[i] = Task.CompletedTask; // Avoid Null
|
||||
}
|
||||
|
||||
// Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
|
||||
if (currentCancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// this should happen once after all queries are done so progress bar should continue
|
||||
// until the end of all querying
|
||||
_isQueryRunning = false;
|
||||
if (currentUpdateSource == _updateSource)
|
||||
{ // update to hidden if this is still the current query
|
||||
if (!currentCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// update to hidden if this is still the current query
|
||||
ProgressBarVisibility = Visibility.Hidden;
|
||||
}
|
||||
}, currentCancellationToken);
|
||||
|
||||
// Local Function
|
||||
async Task QueryTask(PluginPair plugin, Query query, CancellationToken token)
|
||||
{
|
||||
// Since it is wrapped within a Task.Run, the synchronous context is null
|
||||
// Task.Yield will force it to run in ThreadPool
|
||||
await Task.Yield();
|
||||
|
||||
var results = await PluginManager.QueryForPlugin(plugin, query, token);
|
||||
if (!currentCancellationToken.IsCancellationRequested)
|
||||
_resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, query, token));
|
||||
}
|
||||
}, currentCancellationToken).ContinueWith(
|
||||
t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception),
|
||||
TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_updateSource?.Cancel();
|
||||
Results.Clear();
|
||||
Results.Visbility = Visibility.Collapsed;
|
||||
}
|
||||
|
|
@ -450,18 +511,18 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (!string.IsNullOrEmpty(keyword))
|
||||
{
|
||||
Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
|
||||
Results.KeepResultsFor(PluginManager.NonGlobalPlugins[keyword].Metadata);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(keyword))
|
||||
{
|
||||
Results.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata);
|
||||
Results.KeepResultsExcept(PluginManager.NonGlobalPlugins[lastKeyword].Metadata);
|
||||
}
|
||||
else if (lastKeyword != keyword)
|
||||
{
|
||||
Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
|
||||
Results.KeepResultsFor(PluginManager.NonGlobalPlugins[keyword].Metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -499,6 +560,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
|
@ -544,6 +606,7 @@ namespace Flow.Launcher.ViewModel
|
|||
var selected = SelectedResults == History;
|
||||
return selected;
|
||||
}
|
||||
|
||||
#region Hotkey
|
||||
|
||||
private void SetHotkey(string hotkeyStr, EventHandler<HotkeyEventArgs> action)
|
||||
|
|
@ -562,7 +625,8 @@ namespace Flow.Launcher.ViewModel
|
|||
catch (Exception)
|
||||
{
|
||||
string errorMsg =
|
||||
string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
|
||||
string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"),
|
||||
hotkeyStr);
|
||||
MessageBox.Show(errorMsg);
|
||||
}
|
||||
}
|
||||
|
|
@ -612,7 +676,6 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (!ShouldIgnoreHotkeys())
|
||||
{
|
||||
|
||||
if (_settings.LastQueryMode == LastQueryMode.Empty)
|
||||
{
|
||||
ChangeQueryText(string.Empty);
|
||||
|
|
@ -666,9 +729,31 @@ namespace Flow.Launcher.ViewModel
|
|||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
public void UpdateResultView(List<Result> list, PluginMetadata metadata, Query originQuery)
|
||||
public void UpdateResultView(IEnumerable<ResultsForUpdate> resultsForUpdates)
|
||||
{
|
||||
foreach (var result in list)
|
||||
if (!resultsForUpdates.Any())
|
||||
return;
|
||||
CancellationToken token;
|
||||
|
||||
try
|
||||
{
|
||||
// Don't know why sometimes even resultsForUpdates is empty, the method won't return;
|
||||
token = resultsForUpdates.Select(r => r.Token).Distinct().SingleOrDefault();
|
||||
}
|
||||
#if DEBUG
|
||||
catch
|
||||
{
|
||||
throw new ArgumentException("Unacceptable token");
|
||||
}
|
||||
#else
|
||||
catch
|
||||
{
|
||||
token = default;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
foreach (var result in resultsForUpdates.SelectMany(u => u.Results))
|
||||
{
|
||||
if (_topMostRecord.IsTopMost(result))
|
||||
{
|
||||
|
|
@ -680,15 +765,31 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
Results.AddResults(resultsForUpdates, token);
|
||||
}
|
||||
|
||||
/// <summary>U
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
public void UpdateResultView(List<Result> list, PluginMetadata metadata, Query originQuery)
|
||||
{
|
||||
foreach (var result in list)
|
||||
{
|
||||
if (_topMostRecord.IsTopMost(result))
|
||||
{
|
||||
result.Score = int.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var priorityScore = metadata.Priority * 150;
|
||||
result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore;
|
||||
}
|
||||
}
|
||||
|
||||
if (originQuery.RawQuery == _lastQuery.RawQuery)
|
||||
{
|
||||
Results.AddResults(list, metadata.ID);
|
||||
}
|
||||
|
||||
if (Results.Visbility != Visibility.Visible && list.Count > 0)
|
||||
{
|
||||
Results.Visbility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace Flow.Launcher.ViewModel
|
|||
public string InitilizaTime => PluginPair.Metadata.InitTime.ToString() + "ms";
|
||||
public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms";
|
||||
public string ActionKeywordsText => string.Join(Query.ActionKeywordSeperater, PluginPair.Metadata.ActionKeywords);
|
||||
public int Priority => PluginPair.Metadata.Priority;
|
||||
|
||||
public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword)
|
||||
{
|
||||
|
|
@ -34,6 +35,12 @@ namespace Flow.Launcher.ViewModel
|
|||
OnPropertyChanged(nameof(ActionKeywordsText));
|
||||
}
|
||||
|
||||
public void ChangePriority(int newPriority)
|
||||
{
|
||||
PluginPair.Metadata.Priority = newPriority;
|
||||
OnPropertyChanged(nameof(Priority));
|
||||
}
|
||||
|
||||
public bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,116 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
using Microsoft.FSharp.Core;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
public class ResultViewModel : BaseModel
|
||||
{
|
||||
public class LazyAsync<T> : Lazy<ValueTask<T>>
|
||||
{
|
||||
private readonly T defaultValue;
|
||||
|
||||
private readonly Action _updateCallback;
|
||||
public new T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValueCreated)
|
||||
{
|
||||
_ = Exercute(); // manually use callback strategy
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (!base.Value.IsCompletedSuccessfully)
|
||||
return defaultValue;
|
||||
|
||||
return base.Value.Result;
|
||||
|
||||
// If none of the variables captured by the local function are captured by other lambdas,
|
||||
// the compiler can avoid heap allocations.
|
||||
async ValueTask Exercute()
|
||||
{
|
||||
await base.Value.ConfigureAwait(false);
|
||||
_updateCallback();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public LazyAsync(Func<ValueTask<T>> factory, T defaultValue, Action updateCallback) : base(factory)
|
||||
{
|
||||
if (defaultValue != null)
|
||||
{
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
_updateCallback = updateCallback;
|
||||
}
|
||||
}
|
||||
|
||||
public ResultViewModel(Result result, Settings settings)
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
|
||||
Image = new LazyAsync<ImageSource>(
|
||||
SetImage,
|
||||
ImageLoader.DefaultImage,
|
||||
() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(Image));
|
||||
});
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
public Settings Settings { get; private set; }
|
||||
|
||||
public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
|
||||
public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
|
||||
|
||||
public string OpenResultModifiers => Settings.OpenResultModifiers;
|
||||
|
||||
public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip)
|
||||
? Result.Title
|
||||
? Result.Title
|
||||
: Result.TitleToolTip;
|
||||
|
||||
public string ShowSubTitleToolTip => string.IsNullOrEmpty(Result.SubTitleToolTip)
|
||||
? Result.SubTitle
|
||||
? Result.SubTitle
|
||||
: Result.SubTitleToolTip;
|
||||
|
||||
public ImageSource Image
|
||||
public LazyAsync<ImageSource> Image { get; set; }
|
||||
|
||||
private async ValueTask<ImageSource> SetImage()
|
||||
{
|
||||
get
|
||||
var imagePath = Result.IcoPath;
|
||||
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
|
||||
{
|
||||
var imagePath = Result.IcoPath;
|
||||
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
return Result.Icon();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
|
||||
imagePath = Constant.MissingImgIcon;
|
||||
}
|
||||
return Result.Icon();
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
|
||||
return ImageLoader.DefaultImage;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImageLoader.CacheContainImage(imagePath))
|
||||
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
|
||||
return ImageLoader.Load(imagePath);
|
||||
}
|
||||
|
||||
return await Task.Run(() => ImageLoader.Load(imagePath));
|
||||
}
|
||||
|
||||
public Result Result { get; }
|
||||
|
|
@ -75,6 +128,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Result.GetHashCode();
|
||||
|
|
@ -84,6 +138,5 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
return Result.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
Flow.Launcher/ViewModel/ResultsForUpdate.cs
Normal file
36
Flow.Launcher/ViewModel/ResultsForUpdate.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using Flow.Launcher.Plugin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
public class ResultsForUpdate
|
||||
{
|
||||
public List<Result> Results { get; }
|
||||
|
||||
public PluginMetadata Metadata { get; }
|
||||
public string ID { get; }
|
||||
|
||||
public Query Query { get; }
|
||||
public CancellationToken Token { get; }
|
||||
|
||||
public ResultsForUpdate(List<Result> results, string resultID, CancellationToken token)
|
||||
{
|
||||
Results = results;
|
||||
ID = resultID;
|
||||
Token = token;
|
||||
}
|
||||
|
||||
|
||||
public ResultsForUpdate(List<Result> results, PluginMetadata metadata, Query query, CancellationToken token)
|
||||
{
|
||||
Results = results;
|
||||
Metadata = metadata;
|
||||
Query = query;
|
||||
Token = token;
|
||||
ID = metadata.ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Forms;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -17,7 +23,6 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public ResultCollection Results { get; }
|
||||
|
||||
private readonly object _addResultsLock = new object();
|
||||
private readonly object _collectionLock = new object();
|
||||
private readonly Settings _settings;
|
||||
private int MaxResults => _settings?.MaxResultsToShow ?? 6;
|
||||
|
|
@ -116,91 +121,131 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public void Clear()
|
||||
{
|
||||
Results.Clear();
|
||||
lock (_collectionLock)
|
||||
Results.RemoveAll();
|
||||
}
|
||||
|
||||
public void RemoveResultsExcept(PluginMetadata metadata)
|
||||
public void KeepResultsFor(PluginMetadata metadata)
|
||||
{
|
||||
Results.RemoveAll(r => r.Result.PluginID != metadata.ID);
|
||||
lock (_collectionLock)
|
||||
Results.Update(Results.Where(r => r.Result.PluginID == metadata.ID).ToList());
|
||||
}
|
||||
|
||||
public void RemoveResultsFor(PluginMetadata metadata)
|
||||
public void KeepResultsExcept(PluginMetadata metadata)
|
||||
{
|
||||
Results.RemoveAll(r => r.Result.PluginID == metadata.ID);
|
||||
lock (_collectionLock)
|
||||
Results.Update(Results.Where(r => r.Result.PluginID != metadata.ID).ToList());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
public void AddResults(List<Result> newRawResults, string resultId)
|
||||
{
|
||||
lock (_addResultsLock)
|
||||
|
||||
lock (_collectionLock)
|
||||
{
|
||||
var newResults = NewResults(newRawResults, resultId);
|
||||
|
||||
// update UI in one run, so it can avoid UI flickering
|
||||
Results.Update(newResults);
|
||||
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/5ff71969-f183-4744-909d-50f7cd414954/binding-a-tabcontrols-selectedindex-not-working?forum=wpf
|
||||
// fix selected index flow
|
||||
var updateTask = Task.Run(() =>
|
||||
{
|
||||
// update UI in one run, so it can avoid UI flickering
|
||||
|
||||
if (Results.Count > 0)
|
||||
Results.Update(newResults);
|
||||
if (Results.Any())
|
||||
SelectedItem = Results[0];
|
||||
});
|
||||
if (!updateTask.Wait(300))
|
||||
{
|
||||
Margin = new Thickness { Top = 8 };
|
||||
SelectedIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Margin = new Thickness { Top = 0 };
|
||||
updateTask.Dispose();
|
||||
throw new TimeoutException("Update result use too much time.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (Visbility != Visibility.Visible && Results.Count > 0)
|
||||
{
|
||||
Margin = new Thickness { Top = 8 };
|
||||
SelectedIndex = 0;
|
||||
Visbility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
Margin = new Thickness { Top = 0 };
|
||||
Visbility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
public void AddResults(IEnumerable<ResultsForUpdate> resultsForUpdates, CancellationToken token)
|
||||
{
|
||||
var newResults = NewResults(resultsForUpdates);
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
lock (_collectionLock)
|
||||
{
|
||||
// update UI in one run, so it can avoid UI flickering
|
||||
|
||||
Results.Update(newResults, token);
|
||||
if (Results.Any())
|
||||
SelectedItem = Results[0];
|
||||
|
||||
|
||||
}
|
||||
|
||||
switch (Visbility)
|
||||
{
|
||||
case Visibility.Collapsed when Results.Count > 0:
|
||||
Margin = new Thickness { Top = 8 };
|
||||
SelectedIndex = 0;
|
||||
Visbility = Visibility.Visible;
|
||||
break;
|
||||
case Visibility.Visible when Results.Count == 0:
|
||||
Margin = new Thickness { Top = 0 };
|
||||
Visbility = Visibility.Collapsed;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private List<ResultViewModel> NewResults(List<Result> newRawResults, string resultId)
|
||||
{
|
||||
var results = Results.ToList();
|
||||
if (newRawResults.Count == 0)
|
||||
return Results.ToList();
|
||||
|
||||
var results = Results as IEnumerable<ResultViewModel>;
|
||||
|
||||
var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)).ToList();
|
||||
var oldResults = results.Where(r => r.Result.PluginID == resultId).ToList();
|
||||
|
||||
// Find the same results in A (old results) and B (new newResults)
|
||||
var sameResults = oldResults
|
||||
.Where(t1 => newResults.Any(x => x.Result.Equals(t1.Result)))
|
||||
.ToList();
|
||||
|
||||
// remove result of relative complement of B in A
|
||||
foreach (var result in oldResults.Except(sameResults))
|
||||
{
|
||||
results.Remove(result);
|
||||
}
|
||||
|
||||
// update result with B's score and index position
|
||||
foreach (var sameResult in sameResults)
|
||||
{
|
||||
int oldIndex = results.IndexOf(sameResult);
|
||||
int oldScore = results[oldIndex].Result.Score;
|
||||
var newResult = newResults[newResults.IndexOf(sameResult)];
|
||||
int newScore = newResult.Result.Score;
|
||||
if (newScore != oldScore)
|
||||
{
|
||||
var oldResult = results[oldIndex];
|
||||
return results.Where(r => r.Result.PluginID != resultId)
|
||||
.Concat(results.Intersect(newResults).Union(newResults))
|
||||
.OrderByDescending(r => r.Result.Score)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
oldResult.Result.Score = newScore;
|
||||
oldResult.Result.OriginQuery = newResult.Result.OriginQuery;
|
||||
private List<ResultViewModel> NewResults(IEnumerable<ResultsForUpdate> resultsForUpdates)
|
||||
{
|
||||
if (!resultsForUpdates.Any())
|
||||
return Results.ToList();
|
||||
|
||||
results.RemoveAt(oldIndex);
|
||||
int newIndex = InsertIndexOf(newScore, results);
|
||||
results.Insert(newIndex, oldResult);
|
||||
}
|
||||
}
|
||||
var results = Results as IEnumerable<ResultViewModel>;
|
||||
|
||||
// insert result in relative complement of A in B
|
||||
foreach (var result in newResults.Except(sameResults))
|
||||
{
|
||||
int newIndex = InsertIndexOf(result.Result.Score, results);
|
||||
results.Insert(newIndex, result);
|
||||
}
|
||||
|
||||
return results;
|
||||
return results.Where(r => r != null && !resultsForUpdates.Any(u => u.Metadata.ID == r.Result.PluginID))
|
||||
.Concat(
|
||||
resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)))
|
||||
.OrderByDescending(rv => rv.Result.Score)
|
||||
.ToList();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region FormattedText Dependency Property
|
||||
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
|
||||
"FormattedText",
|
||||
|
|
@ -235,57 +280,74 @@ namespace Flow.Launcher.ViewModel
|
|||
public class ResultCollection : ObservableCollection<ResultViewModel>
|
||||
{
|
||||
|
||||
public void RemoveAll(Predicate<ResultViewModel> predicate)
|
||||
{
|
||||
CheckReentrancy();
|
||||
private long editTime = 0;
|
||||
|
||||
for (int i = Count - 1; i >= 0; i--)
|
||||
private bool _suppressNotifying = false;
|
||||
|
||||
private CancellationToken _token;
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (!_suppressNotifying)
|
||||
{
|
||||
if (predicate(this[i]))
|
||||
{
|
||||
RemoveAt(i);
|
||||
}
|
||||
base.OnCollectionChanged(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void BulkAddRange(IEnumerable<ResultViewModel> resultViews)
|
||||
{
|
||||
// suppress notifying before adding all element
|
||||
_suppressNotifying = true;
|
||||
foreach (var item in resultViews)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
_suppressNotifying = false;
|
||||
// manually update event
|
||||
// wpf use directx / double buffered already, so just reset all won't cause ui flickering
|
||||
if (_token.IsCancellationRequested)
|
||||
return;
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
public void AddRange(IEnumerable<ResultViewModel> Items)
|
||||
{
|
||||
foreach (var item in Items)
|
||||
{
|
||||
if (_token.IsCancellationRequested)
|
||||
return;
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
public void RemoveAll()
|
||||
{
|
||||
ClearItems();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Update the results collection with new results, try to keep identical results
|
||||
/// </summary>
|
||||
/// <param name="newItems"></param>
|
||||
public void Update(List<ResultViewModel> newItems)
|
||||
public void Update(List<ResultViewModel> newItems, CancellationToken token = default)
|
||||
{
|
||||
int newCount = newItems.Count;
|
||||
int oldCount = Items.Count;
|
||||
int location = newCount > oldCount ? oldCount : newCount;
|
||||
_token = token;
|
||||
if (Count == 0 && newItems.Count == 0 || _token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < location; i++)
|
||||
if (editTime < 5 || newItems.Count < 30)
|
||||
{
|
||||
ResultViewModel oldResult = this[i];
|
||||
ResultViewModel newResult = newItems[i];
|
||||
if (!oldResult.Equals(newResult))
|
||||
{ // result is not the same update it in the current index
|
||||
this[i] = newResult;
|
||||
}
|
||||
else if (oldResult.Result.Score != newResult.Result.Score)
|
||||
{
|
||||
this[i].Result.Score = newResult.Result.Score;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (newCount >= oldCount)
|
||||
{
|
||||
for (int i = oldCount; i < newCount; i++)
|
||||
{
|
||||
Add(newItems[i]);
|
||||
}
|
||||
if (Count != 0) ClearItems();
|
||||
AddRange(newItems);
|
||||
editTime++;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = oldCount - 1; i >= newCount; i--)
|
||||
{
|
||||
RemoveAt(i);
|
||||
}
|
||||
Clear();
|
||||
BulkAddRange(newItems);
|
||||
editTime++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ namespace Flow.Launcher.ViewModel
|
|||
var id = vm.PluginPair.Metadata.ID;
|
||||
|
||||
Settings.PluginSettings.Plugins[id].Disabled = vm.PluginPair.Metadata.Disabled;
|
||||
Settings.PluginSettings.Plugins[id].Priority = vm.Priority;
|
||||
}
|
||||
|
||||
PluginManager.Save();
|
||||
|
|
@ -213,7 +214,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
#region plugin
|
||||
|
||||
public static string Plugin => "http://www.wox.one/plugin";
|
||||
public static string Plugin => @"https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest";
|
||||
public PluginViewModel SelectedPlugin { get; set; }
|
||||
|
||||
public IList<PluginViewModel> PluginViewModels
|
||||
|
|
@ -438,7 +439,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
public ImageSource ThemeImage => ImageLoader.Load(Constant.QueryTextBoxIconImagePath);
|
||||
public string ThemeImage => Constant.QueryTextBoxIconImagePath;
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -450,7 +451,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
#region about
|
||||
|
||||
public string Github => _updater.GitHubRepository;
|
||||
public string Website => Constant.Website;
|
||||
public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest";
|
||||
public static string Version => Constant.Version;
|
||||
public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes);
|
||||
|
|
|
|||
115
JsonRPC/wox.py
115
JsonRPC/wox.py
|
|
@ -1,115 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
import json
|
||||
import sys
|
||||
import inspect
|
||||
|
||||
class FlowLauncher(object):
|
||||
"""
|
||||
Flow.Launcher python plugin base
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
rpc_request = json.loads(sys.argv[1])
|
||||
# proxy is not working now
|
||||
self.proxy = rpc_request.get("proxy",{})
|
||||
request_method_name = rpc_request.get("method")
|
||||
request_parameters = rpc_request.get("parameters")
|
||||
methods = inspect.getmembers(self, predicate=inspect.ismethod)
|
||||
|
||||
request_method = dict(methods)[request_method_name]
|
||||
results = request_method(*request_parameters)
|
||||
|
||||
if request_method_name == "query" or request_method_name == "context_menu":
|
||||
print(json.dumps({"result": results}))
|
||||
|
||||
def query(self,query):
|
||||
"""
|
||||
sub class need to override this method
|
||||
"""
|
||||
return []
|
||||
|
||||
def context_menu(self, data):
|
||||
"""
|
||||
optional context menu entries for a result
|
||||
"""
|
||||
return []
|
||||
|
||||
def debug(self,msg):
|
||||
"""
|
||||
alert msg
|
||||
"""
|
||||
print("DEBUG:{}".format(msg))
|
||||
sys.exit()
|
||||
|
||||
class FlowLauncherAPI(object):
|
||||
|
||||
@classmethod
|
||||
def change_query(cls,query,requery = False):
|
||||
"""
|
||||
change flowlauncher query
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.ChangeQuery","parameters":[query,requery]}))
|
||||
|
||||
@classmethod
|
||||
def shell_run(cls,cmd):
|
||||
"""
|
||||
run shell commands
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.ShellRun","parameters":[cmd]}))
|
||||
|
||||
@classmethod
|
||||
def close_app(cls):
|
||||
"""
|
||||
close flowlauncher
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.CloseApp","parameters":[]}))
|
||||
|
||||
@classmethod
|
||||
def hide_app(cls):
|
||||
"""
|
||||
hide flowlauncher
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.HideApp","parameters":[]}))
|
||||
|
||||
@classmethod
|
||||
def show_app(cls):
|
||||
"""
|
||||
show flowlauncher
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.ShowApp","parameters":[]}))
|
||||
|
||||
@classmethod
|
||||
def show_msg(cls,title,sub_title,ico_path=""):
|
||||
"""
|
||||
show messagebox
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.ShowMsg","parameters":[title,sub_title,ico_path]}))
|
||||
|
||||
@classmethod
|
||||
def open_setting_dialog(cls):
|
||||
"""
|
||||
open setting dialog
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.OpenSettingDialog","parameters":[]}))
|
||||
|
||||
@classmethod
|
||||
def start_loadingbar(cls):
|
||||
"""
|
||||
start loading animation in flowlauncher
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.StartLoadingBar","parameters":[]}))
|
||||
|
||||
@classmethod
|
||||
def stop_loadingbar(cls):
|
||||
"""
|
||||
stop loading animation in flowlauncher
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.StopLoadingBar","parameters":[]}))
|
||||
|
||||
@classmethod
|
||||
def reload_plugins(cls):
|
||||
"""
|
||||
reload all flowlauncher plugins
|
||||
"""
|
||||
print(json.dumps({"method": "Flow.Launcher.ReloadPlugins","parameters":[]}))
|
||||
1
LICENSE
1
LICENSE
|
|
@ -1,5 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Flow-Launcher
|
||||
Copyright (c) 2015 Wox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ProjectGuid>{9B130CC5-14FB-41FF-B310-0A95B6894C37}</ProjectGuid>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flow.Launcher.Plugin.BrowserBookmark</RootNamespace>
|
||||
|
|
@ -40,14 +41,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Images\bookmark.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Languages\en.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\SQLite.Interop.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
@ -60,22 +53,18 @@
|
|||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Content Include="Languages\*.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Images\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Include="Views\SettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.112" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.112" />
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Name": "Browser Bookmarks",
|
||||
"Description": "Search your browser bookmarks",
|
||||
"Author": "qianlifeng, Ioannis G.",
|
||||
"Version": "1.3.1",
|
||||
"Version": "1.3.2",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<ProjectGuid>{59BD9891-3837-438A-958D-ADC7F91F6F7E}</ProjectGuid>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flow.Launcher.Plugin.Caculator</RootNamespace>
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
|
|
@ -43,57 +44,14 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Images\calculator.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\en.xaml">
|
||||
<Content Include="Languages\*.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-cn.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-tw.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\de.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\pl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<Content Include="Images\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@
|
|||
<system:String x:Key="flowlauncher_plugin_calculator_decimal_seperator_use_system_locale">Použiť podľa systému</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_calculator_decimal_seperator_comma">Čiarka (,)</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_calculator_decimal_seperator_dot">Bodka (.)</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_calculator_max_decimal_places">Max. desatinných miest</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_calculator_max_decimal_places">Desatinné miesta</system:String>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -91,7 +91,7 @@ namespace Flow.Launcher.Plugin.Caculator
|
|||
};
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Name": "Calculator",
|
||||
"Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)",
|
||||
"Author": "cxfksword",
|
||||
"Version": "1.1.2",
|
||||
"Version": "1.1.4",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll",
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<ProjectGuid>{F35190AA-4758-4D9E-A193-E3BDF6AD3567}</ProjectGuid>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flow.Launcher.Plugin.Color</RootNamespace>
|
||||
<AssemblyName>Flow.Launcher.Plugin.Color</AssemblyName>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Color\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.Color\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Images\color.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\en.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-cn.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-tw.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\de.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\pl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Farben</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Stellt eine HEX-Farben Vorschau bereit. (Versuche #000 in Flow Launcher)</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Colors</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Allows to preview colors using hex values.(Try #000 in Flow Launcher)</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Kolory</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Podgląd kolorów po wpisaniu ich kodu szesnastkowego. (Spróbuj wpisać #000 w oknie Flow Launchera)</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Farby</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Zobrazuje náhľad farieb v HEX formáte. (Skúste #000 vo Flow Launcheri)</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_name">Renkler</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_description">Hex kodunu girdiğiniz renkleri görüntülemeye yarar.(#000 yazmayı deneyin)</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_name">颜色</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_description">提供在Flow Launcher查询hex颜色。(尝试在Flow Launcher中输入#000)</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_name">顏色</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_color_plugin_description">提供在 Flow Launcher 查詢 hex 顏色。(試著在 Flow Launcher 中輸入 #000)</system:String>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Color
|
||||
{
|
||||
public sealed class ColorsPlugin : IPlugin, IPluginI18n
|
||||
{
|
||||
private string DIR_PATH = Path.Combine(Path.GetTempPath(), @"Plugins\Colors\");
|
||||
private PluginInitContext context;
|
||||
private const int IMG_SIZE = 32;
|
||||
|
||||
private DirectoryInfo ColorsDirectory { get; set; }
|
||||
|
||||
public ColorsPlugin()
|
||||
{
|
||||
if (!Directory.Exists(DIR_PATH))
|
||||
{
|
||||
ColorsDirectory = Directory.CreateDirectory(DIR_PATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorsDirectory = new DirectoryInfo(DIR_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Result> Query(Query query)
|
||||
{
|
||||
var raw = query.Search;
|
||||
if (!IsAvailable(raw)) return new List<Result>(0);
|
||||
try
|
||||
{
|
||||
var cached = Find(raw);
|
||||
if (cached.Length == 0)
|
||||
{
|
||||
var path = CreateImage(raw);
|
||||
return new List<Result>
|
||||
{
|
||||
new Result
|
||||
{
|
||||
Title = raw,
|
||||
IcoPath = path,
|
||||
Action = _ =>
|
||||
{
|
||||
Clipboard.SetText(raw);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return cached.Select(x => new Result
|
||||
{
|
||||
Title = raw,
|
||||
IcoPath = x.FullName,
|
||||
Action = _ =>
|
||||
{
|
||||
Clipboard.SetText(raw);
|
||||
return true;
|
||||
}
|
||||
}).ToList();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// todo: log
|
||||
return new List<Result>(0);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAvailable(string query)
|
||||
{
|
||||
// todo: rgb, names
|
||||
var length = query.Length - 1; // minus `#` sign
|
||||
return query.StartsWith("#") && (length == 3 || length == 6);
|
||||
}
|
||||
|
||||
public FileInfo[] Find(string name)
|
||||
{
|
||||
var file = string.Format("{0}.png", name.Substring(1));
|
||||
return ColorsDirectory.GetFiles(file, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
private string CreateImage(string name)
|
||||
{
|
||||
using (var bitmap = new Bitmap(IMG_SIZE, IMG_SIZE))
|
||||
using (var graphics = Graphics.FromImage(bitmap))
|
||||
{
|
||||
var color = ColorTranslator.FromHtml(name);
|
||||
graphics.Clear(color);
|
||||
|
||||
var path = CreateFileName(name);
|
||||
bitmap.Save(path, ImageFormat.Png);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateFileName(string name)
|
||||
{
|
||||
return string.Format("{0}{1}.png", ColorsDirectory.FullName, name.Substring(1));
|
||||
}
|
||||
|
||||
public void Init(PluginInitContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
public string GetTranslatedPluginTitle()
|
||||
{
|
||||
return context.API.GetTranslation("flowlauncher_plugin_color_plugin_name");
|
||||
}
|
||||
|
||||
public string GetTranslatedPluginDescription()
|
||||
{
|
||||
return context.API.GetTranslation("flowlauncher_plugin_color_plugin_description");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"ID": "9B36CE6181FC47FBB597AA2C29CD9B0A",
|
||||
"ActionKeyword": "*",
|
||||
"Name": "Colors",
|
||||
"Description": "Provide hex color preview.(Try #000 in Flow Launcher)",
|
||||
"Author": "qianlifeng",
|
||||
"Version": "1.1.1",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.Color.dll",
|
||||
"IcoPath": "Images\\color.png"
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ namespace Flow.Launcher.Plugin.ControlPanel
|
|||
int cxDesired, int cyDesired, uint fuLoad);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
extern static bool DestroyIcon(IntPtr handle);
|
||||
static extern bool DestroyIcon(IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<ProjectGuid>{1EE20B48-82FB-48A2-8086-675D6DDAB4F0}</ProjectGuid>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flow.Launcher.Plugin.ControlPanel</RootNamespace>
|
||||
|
|
@ -45,53 +45,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Images\ControlPanel.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\en.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<Content Include="Images\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-cn.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-tw.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\de.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\pl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Content Include="Languages\*.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Name": "Control Panel",
|
||||
"Description": "Search within the Control Panel.",
|
||||
"Author": "CoenraadS",
|
||||
"Version": "1.1.1",
|
||||
"Version": "1.1.2",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.ControlPanel.dll",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
|
|
@ -26,73 +27,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Images\explorer.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<None Include="Images\index.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<None Include="Images\excludeindexpath.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<None Include="Images\copy.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<None Include="Images\deletefilefolder.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<Content Include="Images\file.png">
|
||||
<Content Include="Images\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
<None Include="Images\folder.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<None Include="Images\user.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<None Include="Images\windowsindexingoptions.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
<Content Include="Languages\en.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
<Content Include="Languages\zh-cn.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
<Content Include="Languages\zh-tw.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
<Content Include="Languages\de.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
<Content Include="Languages\pl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Content Include="Languages\*.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ using Flow.Launcher.Plugin.Explorer.Search;
|
|||
using Flow.Launcher.Plugin.Explorer.ViewModels;
|
||||
using Flow.Launcher.Plugin.Explorer.Views;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer
|
||||
{
|
||||
public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n
|
||||
public class Main : ISettingProvider, IAsyncPlugin, ISavable, IContextMenu, IPluginI18n
|
||||
{
|
||||
internal PluginInitContext Context { get; set; }
|
||||
|
||||
|
|
@ -17,17 +19,21 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
|
||||
private IContextMenu contextMenu;
|
||||
|
||||
private SearchManager searchManager;
|
||||
|
||||
public Control CreateSettingPanel()
|
||||
{
|
||||
return new ExplorerSettings(viewModel);
|
||||
}
|
||||
|
||||
public void Init(PluginInitContext context)
|
||||
public async Task InitAsync(PluginInitContext context)
|
||||
{
|
||||
Context = context;
|
||||
viewModel = new SettingsViewModel(context);
|
||||
await viewModel.LoadStorage();
|
||||
Settings = viewModel.Settings;
|
||||
contextMenu = new ContextMenu(Context, Settings);
|
||||
searchManager = new SearchManager(Settings, Context);
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
|
|
@ -35,9 +41,9 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
return contextMenu.LoadContextMenus(selectedResult);
|
||||
}
|
||||
|
||||
public List<Result> Query(Query query)
|
||||
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
|
||||
{
|
||||
return new SearchManager(Settings, Context).Search(query);
|
||||
return await searchManager.SearchAsync(query, token);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
|
||||
{
|
||||
|
|
@ -22,7 +23,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
|
|||
|
||||
if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > search.LastIndexOf(Constants.DirectorySeperator))
|
||||
return DirectorySearch(SearchOption.AllDirectories, query, search, criteria);
|
||||
|
||||
|
||||
return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria);
|
||||
}
|
||||
|
||||
|
|
@ -57,9 +58,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
|
|||
try
|
||||
{
|
||||
var directoryInfo = new System.IO.DirectoryInfo(path);
|
||||
var fileSystemInfos = directoryInfo.GetFileSystemInfos(searchCriteria, searchOption);
|
||||
|
||||
foreach (var fileSystemInfo in fileSystemInfos)
|
||||
foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, searchOption))
|
||||
{
|
||||
if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class FolderLink
|
||||
{
|
||||
[JsonProperty]
|
||||
public string Path { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string Nickname
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ using Flow.Launcher.Plugin.SharedCommands;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search
|
||||
{
|
||||
|
|
@ -28,20 +30,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
this.settings = settings;
|
||||
}
|
||||
|
||||
internal List<Result> Search(Query query)
|
||||
internal async Task<List<Result>> SearchAsync(Query query, CancellationToken token)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
|
||||
var querySearch = query.Search;
|
||||
|
||||
if (IsFileContentSearch(query.ActionKeyword))
|
||||
return WindowsIndexFileContentSearch(query, querySearch);
|
||||
return await WindowsIndexFileContentSearchAsync(query, querySearch, token).ConfigureAwait(false);
|
||||
|
||||
// This allows the user to type the assigned action keyword and only see the list of quick folder links
|
||||
if (settings.QuickFolderAccessLinks.Count > 0
|
||||
&& query.ActionKeyword == settings.SearchActionKeyword
|
||||
&& string.IsNullOrEmpty(query.Search))
|
||||
return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context);
|
||||
return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context);
|
||||
|
||||
var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks, context);
|
||||
|
||||
|
|
@ -54,11 +56,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, context);
|
||||
|
||||
// Query is a location path with a full environment variable, eg. %appdata%\somefolder\
|
||||
var isEnvironmentVariablePath = querySearch.Substring(1).Contains("%\\");
|
||||
var isEnvironmentVariablePath = querySearch[1..].Contains("%\\");
|
||||
|
||||
if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariablePath)
|
||||
{
|
||||
results.AddRange(WindowsIndexFilesAndFoldersSearch(query, querySearch));
|
||||
results.AddRange(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
|
@ -72,29 +74,34 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
return results;
|
||||
|
||||
var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath);
|
||||
|
||||
|
||||
results.Add(resultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch));
|
||||
|
||||
results.AddRange(TopLevelDirectorySearchBehaviour(WindowsIndexTopLevelFolderSearch,
|
||||
if (token.IsCancellationRequested)
|
||||
return null;
|
||||
|
||||
results.AddRange(await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync,
|
||||
DirectoryInfoClassSearch,
|
||||
useIndexSearch,
|
||||
query,
|
||||
locationPath));
|
||||
locationPath,
|
||||
token).ConfigureAwait(false));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<Result> WindowsIndexFileContentSearch(Query query, string querySearchString)
|
||||
private async Task<List<Result>> WindowsIndexFileContentSearchAsync(Query query, string querySearchString, CancellationToken token)
|
||||
{
|
||||
var queryConstructor = new QueryConstructor(settings);
|
||||
|
||||
if (string.IsNullOrEmpty(querySearchString))
|
||||
return new List<Result>();
|
||||
|
||||
return indexSearch.WindowsIndexSearch(querySearchString,
|
||||
return await indexSearch.WindowsIndexSearchAsync(querySearchString,
|
||||
queryConstructor.CreateQueryHelper().ConnectionString,
|
||||
queryConstructor.QueryForFileContentSearch,
|
||||
query);
|
||||
query,
|
||||
token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public bool IsFileContentSearch(string actionKeyword)
|
||||
|
|
@ -109,37 +116,40 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch);
|
||||
}
|
||||
|
||||
public List<Result> TopLevelDirectorySearchBehaviour(
|
||||
Func<Query, string, List<Result>> windowsIndexSearch,
|
||||
public async Task<List<Result>> TopLevelDirectorySearchBehaviourAsync(
|
||||
Func<Query, string, CancellationToken, Task<List<Result>>> windowsIndexSearch,
|
||||
Func<Query, string, List<Result>> directoryInfoClassSearch,
|
||||
bool useIndexSearch,
|
||||
Query query,
|
||||
string querySearchString)
|
||||
string querySearchString,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (!useIndexSearch)
|
||||
return directoryInfoClassSearch(query, querySearchString);
|
||||
|
||||
return windowsIndexSearch(query, querySearchString);
|
||||
return await windowsIndexSearch(query, querySearchString, token);
|
||||
}
|
||||
|
||||
private List<Result> WindowsIndexFilesAndFoldersSearch(Query query, string querySearchString)
|
||||
private async Task<List<Result>> WindowsIndexFilesAndFoldersSearchAsync(Query query, string querySearchString, CancellationToken token)
|
||||
{
|
||||
var queryConstructor = new QueryConstructor(settings);
|
||||
|
||||
return indexSearch.WindowsIndexSearch(querySearchString,
|
||||
return await indexSearch.WindowsIndexSearchAsync(querySearchString,
|
||||
queryConstructor.CreateQueryHelper().ConnectionString,
|
||||
queryConstructor.QueryForAllFilesAndFolders,
|
||||
query);
|
||||
query,
|
||||
token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private List<Result> WindowsIndexTopLevelFolderSearch(Query query, string path)
|
||||
|
||||
private async Task<List<Result>> WindowsIndexTopLevelFolderSearchAsync(Query query, string path, CancellationToken token)
|
||||
{
|
||||
var queryConstructor = new QueryConstructor(settings);
|
||||
|
||||
return indexSearch.WindowsIndexSearch(path,
|
||||
return await indexSearch.WindowsIndexSearchAsync(path,
|
||||
queryConstructor.CreateQueryHelper().ConnectionString,
|
||||
queryConstructor.QueryForTopLevelDirectorySearch,
|
||||
query);
|
||||
query,
|
||||
token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private bool UseWindowsIndexForDirectorySearch(string locationPath)
|
||||
|
|
|
|||
|
|
@ -5,19 +5,13 @@ using System.Collections.Generic;
|
|||
using System.Data.OleDb;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
||||
{
|
||||
internal class IndexSearch
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
|
||||
private OleDbConnection conn;
|
||||
|
||||
private OleDbCommand command;
|
||||
|
||||
private OleDbDataReader dataReaderResults;
|
||||
|
||||
private readonly ResultManager resultManager;
|
||||
|
||||
// Reserved keywords in oleDB
|
||||
|
|
@ -28,7 +22,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
resultManager = new ResultManager(context);
|
||||
}
|
||||
|
||||
internal List<Result> ExecuteWindowsIndexSearch(string indexQueryString, string connectionString, Query query)
|
||||
internal async Task<List<Result>> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token)
|
||||
{
|
||||
var folderResults = new List<Result>();
|
||||
var fileResults = new List<Result>();
|
||||
|
|
@ -36,47 +30,49 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
|
||||
try
|
||||
{
|
||||
using (conn = new OleDbConnection(connectionString))
|
||||
using var conn = new OleDbConnection(connectionString);
|
||||
await conn.OpenAsync(token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
using var command = new OleDbCommand(indexQueryString, conn);
|
||||
// Results return as an OleDbDataReader.
|
||||
using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader;
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (dataReaderResults.HasRows)
|
||||
{
|
||||
conn.Open();
|
||||
|
||||
using (command = new OleDbCommand(indexQueryString, conn))
|
||||
while (await dataReaderResults.ReadAsync(token))
|
||||
{
|
||||
// Results return as an OleDbDataReader.
|
||||
using (dataReaderResults = command.ExecuteReader())
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value)
|
||||
{
|
||||
if (dataReaderResults.HasRows)
|
||||
{
|
||||
while (dataReaderResults.Read())
|
||||
{
|
||||
if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value)
|
||||
{
|
||||
// # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path
|
||||
var encodedFragmentPath = dataReaderResults
|
||||
.GetString(1)
|
||||
.Replace("#", "%23", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var path = new Uri(encodedFragmentPath).LocalPath;
|
||||
// # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path
|
||||
var encodedFragmentPath = dataReaderResults
|
||||
.GetString(1)
|
||||
.Replace("#", "%23", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (dataReaderResults.GetString(2) == "Directory")
|
||||
{
|
||||
folderResults.Add(resultManager.CreateFolderResult(
|
||||
dataReaderResults.GetString(0),
|
||||
path,
|
||||
path,
|
||||
query, true, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
fileResults.Add(resultManager.CreateFileResult(path, query, true, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
var path = new Uri(encodedFragmentPath).LocalPath;
|
||||
|
||||
if (dataReaderResults.GetString(2) == "Directory")
|
||||
{
|
||||
folderResults.Add(resultManager.CreateFolderResult(
|
||||
dataReaderResults.GetString(0),
|
||||
path,
|
||||
path,
|
||||
query, true, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
fileResults.Add(resultManager.CreateFileResult(path, query, true, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return new List<Result>(); // The source code indicates that without adding members, it won't allocate an array
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
// Internal error from ExecuteReader(): Connection closed.
|
||||
|
|
@ -91,18 +87,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
return results.Concat(folderResults.OrderBy(x => x.Title)).Concat(fileResults.OrderBy(x => x.Title)).ToList(); ;
|
||||
}
|
||||
|
||||
internal List<Result> WindowsIndexSearch(string searchString, string connectionString, Func<string, string> constructQuery, Query query)
|
||||
internal async Task<List<Result>> WindowsIndexSearchAsync(string searchString, string connectionString,
|
||||
Func<string, string> constructQuery, Query query,
|
||||
CancellationToken token)
|
||||
{
|
||||
var regexMatch = Regex.Match(searchString, reservedStringPattern);
|
||||
|
||||
if (regexMatch.Success)
|
||||
return new List<Result>();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var constructedQuery = constructQuery(searchString);
|
||||
return ExecuteWindowsIndexSearch(constructedQuery, connectionString, query);
|
||||
}
|
||||
var constructedQuery = constructQuery(searchString);
|
||||
return await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token);
|
||||
|
||||
}
|
||||
|
||||
internal bool PathIsIndexed(string path)
|
||||
|
|
|
|||
|
|
@ -1,28 +1,22 @@
|
|||
using Flow.Launcher.Plugin.Explorer.Search;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer
|
||||
{
|
||||
public class Settings
|
||||
{
|
||||
[JsonProperty]
|
||||
public int MaxResult { get; set; } = 100;
|
||||
|
||||
[JsonProperty]
|
||||
public List<FolderLink> QuickFolderAccessLinks { get; set; } = new List<FolderLink>();
|
||||
|
||||
[JsonProperty]
|
||||
public bool UseWindowsIndexForDirectorySearch { get; set; } = true;
|
||||
|
||||
[JsonProperty]
|
||||
public List<FolderLink> IndexSearchExcludedSubdirectoryPaths { get; set; } = new List<FolderLink>();
|
||||
|
||||
[JsonProperty]
|
||||
public string SearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign;
|
||||
|
||||
[JsonProperty]
|
||||
public string FileContentSearchActionKeyword { get; set; } = Constants.DefaultContentSearchActionKeyword;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using Flow.Launcher.Infrastructure.Storage;
|
|||
using Flow.Launcher.Plugin.Explorer.Search;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.FolderLinks;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Explorer.ViewModels
|
||||
{
|
||||
|
|
@ -21,6 +22,11 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
|
|||
Settings = storage.Load();
|
||||
}
|
||||
|
||||
public Task LoadStorage()
|
||||
{
|
||||
return Task.Run(() => Settings = storage.Load());
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
storage.Save();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
"Name": "Explorer",
|
||||
"Description": "Search and manage files and folders. Explorer utilises Windows Index Search",
|
||||
"Author": "Jeremy Wu",
|
||||
"Version": "1.2.5",
|
||||
"Version": "1.2.6",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<ProjectGuid>{FDED22C8-B637-42E8-824A-63B5B6E05A3A}</ProjectGuid>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flow.Launcher.Plugin.PluginIndicator</RootNamespace>
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
|
|
@ -46,55 +47,12 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Images\work.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\en.xaml">
|
||||
<Content Include="Languages\*.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-cn.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-tw.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\de.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\pl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<Content Include="Images\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Name": "Plugin Indicator",
|
||||
"Description": "Provide plugin actionword suggestion",
|
||||
"Author": "qianlifeng",
|
||||
"Version": "1.1.1",
|
||||
"Version": "1.1.2",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.PluginIndicator.dll",
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<ProjectGuid>{049490F0-ECD2-4148-9B39-2135EC346EBE}</ProjectGuid>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flow.Launcher.Plugin.PluginManagement</RootNamespace>
|
||||
<AssemblyName>Flow.Launcher.Plugin.PluginManagement</AssemblyName>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginManagement\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.PluginManagement\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="plugin.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Images\plugin.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\en.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-cn.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\zh-tw.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\de.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\pl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Languages\tr.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
namespace Flow.Launcher.Plugin.PluginManagement
|
||||
{
|
||||
public class FlowLauncherPluginResult
|
||||
{
|
||||
public string plugin_file;
|
||||
public string description;
|
||||
public int liked_count;
|
||||
public string name;
|
||||
public string version;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 269 B |
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher Plugin Verwaltung</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Installiere/Entferne/Aktualisiere Flow Launcher Plugins</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Plugin Management</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Install, remove or update Flow Launcher plugins</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Zarządzanie wtyczkami Flow Launcher</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Pozwala na instalacje, usuwanie i aktualizacje wtyczek</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Správca pluginov</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Inštalácia, odinštalácia alebo aktualizácia pluginov Flow Launchera</system:String>
|
||||
|
||||
</ResourceDictionary>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue