mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'dev' into fix_win32_loading
This commit is contained in:
commit
f78ba8e79e
27 changed files with 294 additions and 108 deletions
|
|
@ -53,6 +53,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Droplex" Version="1.2.0" />
|
||||
<PackageReference Include="FSharp.Core" Version="4.7.1" />
|
||||
<PackageReference Include="squirrel.windows" Version="1.5.2" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Droplex;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -22,7 +23,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
|
||||
{
|
||||
var dotnetPlugins = DotNetPlugins(metadatas);
|
||||
var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory);
|
||||
var pythonPlugins = PythonPlugins(metadatas, settings);
|
||||
var executablePlugins = ExecutablePlugins(metadatas);
|
||||
var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
|
||||
return plugins;
|
||||
|
|
@ -113,11 +114,14 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return plugins;
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, string pythonDirectory)
|
||||
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, PluginsSettings settings)
|
||||
{
|
||||
// try to set Constant.PythonPath, either from
|
||||
if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python))
|
||||
return new List<PluginPair>();
|
||||
|
||||
// Try setting Constant.PythonPath first, either from
|
||||
// PATH or from the given pythonDirectory
|
||||
if (string.IsNullOrEmpty(pythonDirectory))
|
||||
if (string.IsNullOrEmpty(settings.PythonDirectory))
|
||||
{
|
||||
var paths = Environment.GetEnvironmentVariable(PATH);
|
||||
if (paths != null)
|
||||
|
|
@ -129,47 +133,103 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
if (pythonInPath)
|
||||
{
|
||||
Constant.PythonPath = PythonExecutable;
|
||||
Constant.PythonPath =
|
||||
Path.Combine(paths.Split(';').Where(p => p.ToLower().Contains(Python)).FirstOrDefault(), PythonExecutable);
|
||||
settings.PythonDirectory = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, Constant.PythonPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|Python can't be found in PATH.");
|
||||
Log.Error("PluginsLoader","Failed to set Python path despite the environment variable PATH is found", "PythonPlugins");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("|PluginsLoader.PythonPlugins|PATH environment variable is not set.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = Path.Combine(pythonDirectory, PythonExecutable);
|
||||
var path = Path.Combine(settings.PythonDirectory, PythonExecutable);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
Constant.PythonPath = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|PluginsLoader.PythonPlugins|Can't find python executable in {path}");
|
||||
Log.Error("PluginsLoader",$"Tried to automatically set from Settings.PythonDirectory " +
|
||||
$"but can't find python executable in {path}", "PythonPlugins");
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a path to the python executable,
|
||||
// load every python plugin pair.
|
||||
if (String.IsNullOrEmpty(Constant.PythonPath))
|
||||
if (string.IsNullOrEmpty(settings.PythonDirectory))
|
||||
{
|
||||
if (MessageBox.Show("Flow detected you have installed Python plugins, " +
|
||||
"would you like to install Python to run them? " +
|
||||
Environment.NewLine + Environment.NewLine +
|
||||
"Click no if it's already installed, " +
|
||||
"and you will be prompted to select the folder that contains the Python executable",
|
||||
string.Empty, MessageBoxButtons.YesNo) == DialogResult.No
|
||||
&& string.IsNullOrEmpty(settings.PythonDirectory))
|
||||
{
|
||||
var dlg = new FolderBrowserDialog
|
||||
{
|
||||
SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
|
||||
};
|
||||
|
||||
var result = dlg.ShowDialog();
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
string pythonDirectory = dlg.SelectedPath;
|
||||
if (!string.IsNullOrEmpty(pythonDirectory))
|
||||
{
|
||||
var pythonPath = Path.Combine(pythonDirectory, PythonExecutable);
|
||||
if (File.Exists(pythonPath))
|
||||
{
|
||||
settings.PythonDirectory = pythonDirectory;
|
||||
Constant.PythonPath = pythonPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Can't find python in given directory");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DroplexPackage.Drop(App.python3_9_1).Wait();
|
||||
|
||||
var installedPythonDirectory =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39");
|
||||
var pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable);
|
||||
if (FilesFolders.FileExists(pythonPath))
|
||||
{
|
||||
settings.PythonDirectory = installedPythonDirectory;
|
||||
Constant.PythonPath = pythonPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("PluginsLoader",
|
||||
$"Failed to set Python path after Droplex install, {pythonPath} does not exist",
|
||||
"PythonPlugins");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(settings.PythonDirectory))
|
||||
{
|
||||
MessageBox.Show("Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom).");
|
||||
Log.Error("PluginsLoader",
|
||||
$"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.",
|
||||
"PythonPlugins");
|
||||
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return source
|
||||
.Where(o => o.Language.ToUpper() == AllowedLanguage.Python)
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new PythonPlugin(Constant.PythonPath),
|
||||
Metadata = metadata
|
||||
});
|
||||
}
|
||||
|
||||
return source
|
||||
.Where(o => o.Language.ToUpper() == AllowedLanguage.Python)
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new PythonPlugin(Constant.PythonPath),
|
||||
Metadata = metadata
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
{
|
||||
try
|
||||
{
|
||||
using var response = await client.GetAsync(url, token);
|
||||
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
|
||||
|
|
@ -135,7 +135,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
public static async Task<Stream> GetStreamAsync([NotNull] string url, CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
var response = await client.GetAsync(url, token);
|
||||
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
public class JsonStrorage<T> where T : new()
|
||||
{
|
||||
private readonly JsonSerializerOptions _serializerSettings;
|
||||
private T _data;
|
||||
protected T _data;
|
||||
// need a new directory name
|
||||
public const string DirectoryName = "Settings";
|
||||
public const string FileSuffix = ".json";
|
||||
|
|
|
|||
|
|
@ -15,5 +15,10 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
|
||||
FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
|
||||
}
|
||||
|
||||
public PluginJsonStorage(T data) : this()
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using JetBrains.Annotations;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -88,17 +89,86 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Fuzzy Search the string with the given query. This is the core search mechanism Flow uses
|
||||
/// </summary>
|
||||
/// <param name="query">Query string</param>
|
||||
/// <param name="stringToCompare">The string that will be compared against the query</param>
|
||||
/// <returns>Match results</returns>
|
||||
MatchResult FuzzySearch(string query, string stringToCompare);
|
||||
|
||||
/// <summary>
|
||||
/// Http download the spefic url and return as string
|
||||
/// </summary>
|
||||
/// <param name="url">URL to call Http Get</param>
|
||||
/// <param name="token">Cancellation Token</param>
|
||||
/// <returns>Task to get string result</returns>
|
||||
Task<string> HttpGetStringAsync(string url, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Http download the spefic url and return as stream
|
||||
/// </summary>
|
||||
/// <param name="url">URL to call Http Get</param>
|
||||
/// <param name="token">Cancellation Token</param>
|
||||
/// <returns>Task to get stream result</returns>
|
||||
Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default);
|
||||
|
||||
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath);
|
||||
/// <summary>
|
||||
/// Download the specific url to a cretain file path
|
||||
/// </summary>
|
||||
/// <param name="url">URL to download file</param>
|
||||
/// <param name="token">place to store file</param>
|
||||
/// <returns>Task showing the progress</returns>
|
||||
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Add ActionKeyword for specific plugin
|
||||
/// </summary>
|
||||
/// <param name="pluginId">ID for plugin that needs to add action keyword</param>
|
||||
/// <param name="newActionKeyword">The actionkeyword that is supposed to be added</param>
|
||||
void AddActionKeyword(string pluginId, string newActionKeyword);
|
||||
|
||||
/// <summary>
|
||||
/// Remove ActionKeyword for specific plugin
|
||||
/// </summary>
|
||||
/// <param name="pluginId">ID for plugin that needs to remove action keyword</param>
|
||||
/// <param name="newActionKeyword">The actionkeyword that is supposed to be removed</param>
|
||||
void RemoveActionKeyword(string pluginId, string oldActionKeyword);
|
||||
|
||||
/// <summary>
|
||||
/// Log debug message
|
||||
/// Message will only be logged in Debug mode
|
||||
/// </summary>
|
||||
void LogDebug(string className, string message, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log info message
|
||||
/// </summary>
|
||||
void LogInfo(string className, string message, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log warning message
|
||||
/// </summary>
|
||||
void LogWarn(string className, string message, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log an Exception. Will throw if in debug mode so developer will be aware,
|
||||
/// otherwise logs the eror message. This is the primary logging method used for Flow
|
||||
/// </summary>
|
||||
void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Load JsonStorage for current plugin. This is the method used to load settings from json in Flow
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type for deserialization</typeparam>
|
||||
/// <returns></returns>
|
||||
T LoadJsonStorage<T>() where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// Save JsonStorage for current plugin. This is the method used to save settings to json in Flow
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type for Serialization</typeparam>
|
||||
/// <returns></returns>
|
||||
void SaveJsonStorage<T>(T setting) where T : new();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Save plugin settings/cache,
|
||||
|
|
@ -66,6 +66,9 @@
|
|||
<Content Include="Images\*.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Images\*.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
BIN
Flow.Launcher/Images/app.ico
Normal file
BIN
Flow.Launcher/Images/app.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
|
|
@ -121,6 +121,7 @@
|
|||
<system:String x:Key="newActionKeywordsCannotBeEmpty">New Action Keyword can't be empty</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">This new Action Keyword is already assigned to another plugin, please choose a different one</system:String>
|
||||
<system:String x:Key="success">Success</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Use * if you don't want to specify an action keyword</system:String>
|
||||
|
||||
<!--Custom Query Hotkey Dialog-->
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
<Window.InputBindings>
|
||||
<KeyBinding Key="Escape" Command="{Binding EscCommand}"></KeyBinding>
|
||||
<KeyBinding Key="F1" Command="{Binding StartHelpCommand}"></KeyBinding>
|
||||
<KeyBinding Key="F5" Command="{Binding ReloadPluginDataCommand}"></KeyBinding>
|
||||
<KeyBinding Key="Tab" Command="{Binding SelectNextItemCommand}"></KeyBinding>
|
||||
<KeyBinding Key="Tab" Modifiers="Shift" Command="{Binding SelectPrevItemCommand}"></KeyBinding>
|
||||
<KeyBinding Key="N" Modifiers="Ctrl" Command="{Binding SelectNextItemCommand}"></KeyBinding>
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ namespace Flow.Launcher
|
|||
}
|
||||
if (!File.Exists(iconPath))
|
||||
{
|
||||
imgIco.Source = ImageLoader.Load(Path.Combine(Infrastructure.Constant.ProgramDirectory, "Images\\app.png"));
|
||||
imgIco.Source = ImageLoader.Load(Path.Combine(Constant.ProgramDirectory, "Images\\app.png"));
|
||||
}
|
||||
else {
|
||||
imgIco.Source = ImageLoader.Load(iconPath);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ using System.Threading;
|
|||
using System.IO;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using JetBrains.Annotations;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
|
|
@ -61,18 +64,15 @@ namespace Flow.Launcher
|
|||
// which will cause ungraceful exit
|
||||
SaveAppAllSettings();
|
||||
|
||||
// Restart requires Squirrel's Update.exe to be present in the parent folder,
|
||||
// it is only published from the project's release pipeline. When debugging without it,
|
||||
// the project may not restart or just terminates. This is expected.
|
||||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
}
|
||||
|
||||
public void RestarApp()
|
||||
{
|
||||
RestartApp();
|
||||
}
|
||||
public void RestarApp() => RestartApp();
|
||||
|
||||
public void CheckForNewUpdate()
|
||||
{
|
||||
_settingsVM.UpdateApp();
|
||||
}
|
||||
public void CheckForNewUpdate() => _settingsVM.UpdateApp();
|
||||
|
||||
public void SaveAppAllSettings()
|
||||
{
|
||||
|
|
@ -82,15 +82,9 @@ namespace Flow.Launcher
|
|||
ImageLoader.Save();
|
||||
}
|
||||
|
||||
public Task ReloadAllPluginData()
|
||||
{
|
||||
return PluginManager.ReloadData();
|
||||
}
|
||||
public Task ReloadAllPluginData() => PluginManager.ReloadData();
|
||||
|
||||
public void ShowMsg(string title, string subTitle = "", string iconPath = "")
|
||||
{
|
||||
ShowMsg(title, subTitle, iconPath, true);
|
||||
}
|
||||
public void ShowMsg(string title, string subTitle = "", string iconPath = "") => ShowMsg(title, subTitle, iconPath, true);
|
||||
|
||||
public void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true)
|
||||
{
|
||||
|
|
@ -109,54 +103,40 @@ namespace Flow.Launcher
|
|||
});
|
||||
}
|
||||
|
||||
public void StartLoadingBar()
|
||||
{
|
||||
_mainVM.ProgressBarVisibility = Visibility.Visible;
|
||||
}
|
||||
public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible;
|
||||
|
||||
public void StopLoadingBar()
|
||||
{
|
||||
_mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
||||
}
|
||||
public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
||||
|
||||
public string GetTranslation(string key)
|
||||
{
|
||||
return InternationalizationManager.Instance.GetTranslation(key);
|
||||
}
|
||||
public string GetTranslation(string key) => InternationalizationManager.Instance.GetTranslation(key);
|
||||
|
||||
public List<PluginPair> GetAllPlugins()
|
||||
{
|
||||
return PluginManager.AllPlugins.ToList();
|
||||
}
|
||||
|
||||
public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent;
|
||||
public List<PluginPair> GetAllPlugins() => PluginManager.AllPlugins.ToList();
|
||||
|
||||
public MatchResult FuzzySearch(string query, string stringToCompare) => StringMatcher.FuzzySearch(query, stringToCompare);
|
||||
|
||||
public Task<string> HttpGetStringAsync(string url, CancellationToken token = default)
|
||||
{
|
||||
return Http.GetAsync(url);
|
||||
}
|
||||
public Task<string> HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url);
|
||||
|
||||
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default)
|
||||
{
|
||||
return Http.GetStreamAsync(url);
|
||||
}
|
||||
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default) => Http.GetStreamAsync(url);
|
||||
|
||||
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath)
|
||||
{
|
||||
return Http.DownloadAsync(url, filePath);
|
||||
}
|
||||
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default) => Http.DownloadAsync(url, filePath, token);
|
||||
|
||||
public void AddActionKeyword(string pluginId, string newActionKeyword)
|
||||
{
|
||||
PluginManager.AddActionKeyword(pluginId, newActionKeyword);
|
||||
}
|
||||
public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword);
|
||||
|
||||
public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword);
|
||||
|
||||
public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") => Log.Debug(className, message, methodName);
|
||||
|
||||
public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName);
|
||||
|
||||
public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") => Log.Warn(className, message, methodName);
|
||||
|
||||
public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName);
|
||||
|
||||
public T LoadJsonStorage<T>() where T : new() => new PluginJsonStorage<T>().Load();
|
||||
|
||||
public void SaveJsonStorage<T>(T setting) where T : new() => new PluginJsonStorage<T>(setting).Save();
|
||||
|
||||
public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent;
|
||||
|
||||
public void RemoveActionKeyword(string pluginId, string oldActionKeyword)
|
||||
{
|
||||
PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
x:Class="Flow.Launcher.SettingWindow"
|
||||
mc:Ignorable="d"
|
||||
Icon="Images\app.png"
|
||||
Icon="Images\app.ico"
|
||||
Title="{DynamicResource flowlauncher_settings}"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ namespace Flow.Launcher.ViewModel
|
|||
_resultsViewUpdateTask =
|
||||
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
|
||||
async Task updateAction()
|
||||
{
|
||||
var queue = new Dictionary<string, ResultsForUpdate>();
|
||||
|
|
@ -105,9 +104,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
UpdateResultView(queue.Values);
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
};
|
||||
|
||||
void continueAction(Task t)
|
||||
{
|
||||
|
|
@ -115,8 +112,8 @@ namespace Flow.Launcher.ViewModel
|
|||
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);
|
||||
_resultsViewUpdateTask =
|
||||
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -225,6 +222,25 @@ namespace Flow.Launcher.ViewModel
|
|||
SelectedResults = Results;
|
||||
}
|
||||
});
|
||||
|
||||
ReloadPluginDataCommand = new RelayCommand(_ =>
|
||||
{
|
||||
var msg = new Msg { Owner = Application.Current.MainWindow };
|
||||
|
||||
MainWindowVisibility = Visibility.Collapsed;
|
||||
|
||||
PluginManager
|
||||
.ReloadData()
|
||||
.ContinueWith(_ =>
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
msg.Show(
|
||||
InternationalizationManager.Instance.GetTranslation("success"),
|
||||
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"),
|
||||
"");
|
||||
}))
|
||||
.ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -313,6 +329,7 @@ namespace Flow.Launcher.ViewModel
|
|||
public ICommand LoadContextMenuCommand { get; set; }
|
||||
public ICommand LoadHistoryCommand { get; set; }
|
||||
public ICommand OpenResultCommand { get; set; }
|
||||
public ICommand ReloadPluginDataCommand { get; set; }
|
||||
|
||||
public string OpenResultCommandModifiers { get; private set; }
|
||||
|
||||
|
|
|
|||
BIN
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png
Normal file
BIN
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -12,4 +12,6 @@
|
|||
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_newTab">New tab</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_setBrowserFromPath">Set browser from path:</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_browserbookmark_settings_choose">Choose</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_title">Copy url</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_browserbookmark_copyurl_subtitle">Copy the bookmark's url to clipboard</system:String>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Plugin.BrowserBookmark.Commands;
|
||||
using Flow.Launcher.Plugin.BrowserBookmark.Models;
|
||||
|
|
@ -9,7 +12,7 @@ using Flow.Launcher.Plugin.SharedCommands;
|
|||
|
||||
namespace Flow.Launcher.Plugin.BrowserBookmark
|
||||
{
|
||||
public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable
|
||||
public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable, IContextMenu
|
||||
{
|
||||
private PluginInitContext context;
|
||||
|
||||
|
|
@ -60,7 +63,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
ContextData = new BookmarkAttributes { Url = c.Url }
|
||||
}).Where(r => r.Score > 0);
|
||||
return returnList.ToList();
|
||||
}
|
||||
|
|
@ -84,7 +88,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
ContextData = new BookmarkAttributes { Url = c.Url }
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
|
|
@ -115,5 +120,39 @@ namespace Flow.Launcher.Plugin.BrowserBookmark
|
|||
{
|
||||
_storage.Save();
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
return new List<Result>() {
|
||||
new Result
|
||||
{
|
||||
Title = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"),
|
||||
SubTitle = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"),
|
||||
Action = _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetDataObject(((BookmarkAttributes)selectedResult.ContextData).Url);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var message = "Failed to set url in clipboard";
|
||||
Log.Exception("Main",message, e, "LoadContextMenus");
|
||||
|
||||
context.API.ShowMsg(message);
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
IcoPath = "Images\\copylink.png"
|
||||
}};
|
||||
}
|
||||
|
||||
internal class BookmarkAttributes
|
||||
{
|
||||
internal string Url { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Name": "Browser Bookmarks",
|
||||
"Description": "Search your browser bookmarks",
|
||||
"Author": "qianlifeng, Ioannis G.",
|
||||
"Version": "1.3.2",
|
||||
"Version": "1.4.0",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll",
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
{
|
||||
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_title"),
|
||||
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_openwebsite_subtitle"),
|
||||
IcoPath = "Images\\website.png",
|
||||
IcoPath = selectedResult.IcoPath,
|
||||
Action = _ =>
|
||||
{
|
||||
SharedCommands.SearchWeb.NewTabInBrowser(pluginManifestInfo.Website);
|
||||
|
|
@ -63,7 +63,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
{
|
||||
Title = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title"),
|
||||
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle"),
|
||||
IcoPath = selectedResult.IcoPath,
|
||||
IcoPath = "Images\\manifestsite.png",
|
||||
Action = _ =>
|
||||
{
|
||||
SharedCommands.SearchWeb.NewTabInBrowser("https://github.com/Flow-Launcher/Flow.Launcher.PluginsManifest");
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Core\Flow.Launcher.Core.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
|
|
@ -1,3 +1,4 @@
|
|||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
|
@ -400,6 +401,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
private void Uninstall(PluginMetadata plugin)
|
||||
{
|
||||
PluginManager.Settings.Plugins.Remove(plugin.ID);
|
||||
PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
|
||||
|
||||
// Marked for deletion. Will be deleted on next start up
|
||||
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"Name": "Plugins Manager",
|
||||
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
|
||||
"Author": "Jeremy Wu",
|
||||
"Version": "1.6.2",
|
||||
"Version": "1.6.4",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
|
||||
|
|
|
|||
|
|
@ -38,10 +38,11 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be
|
|||
Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up.
|
||||
|
||||
**Integrations**
|
||||
- If you use Python plugins:
|
||||
- Install [Python3](https://www.python.org/downloads/), download `.exe` installer.
|
||||
- Python plugins:
|
||||
- Once a Python plugin has been detected at start up, you will be prompted to either select the location or allow Python 3.9.1 to automatic download and install.
|
||||
- Add Python to `%PATH%` or set it in flow's settings.
|
||||
- Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`.
|
||||
- Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`.
|
||||
- The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`.
|
||||
- Start to launch your Python plugins.
|
||||
- Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest).
|
||||
|
||||
|
|
@ -51,6 +52,7 @@ Windows may complain about security due to code not being signed, this will be c
|
|||
- Open context menu: on the selected result, press <kbd>Ctrl</kbd>+<kbd>O</kbd>/<kbd>Shift</kbd>+<kbd>Enter</kbd>.
|
||||
- Cancel/Return to previous screen: <kbd>Esc</kbd>.
|
||||
- Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name.
|
||||
- Press `F5` while in the query window to reload all plugin data.
|
||||
- Saved user settings are located:
|
||||
- If using roaming: `%APPDATA%\FlowLauncher`
|
||||
- If using portable, by default: `%localappdata%\FlowLauncher\app-<VersionOfYourFlowLauncher>\UserData`
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.0")]
|
||||
[assembly: AssemblyInformationalVersion("1.7.0")]
|
||||
[assembly: AssemblyVersion("1.7.1")]
|
||||
[assembly: AssemblyFileVersion("1.7.1")]
|
||||
[assembly: AssemblyInformationalVersion("1.7.1")]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
version: '1.7.0.{build}'
|
||||
version: '1.7.1.{build}'
|
||||
|
||||
init:
|
||||
- ps: |
|
||||
|
|
|
|||
Loading…
Reference in a new issue