diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 454c4e976f3..d9b39eb89e5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,3 +15,7 @@ updates: - "jjw24" - "taooceros" - "JohnTheGr8" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml new file mode 100644 index 00000000000..fe1b5a5fc5c --- /dev/null +++ b/.github/workflows/winget.yml @@ -0,0 +1,15 @@ +name: Publish to Winget + +on: + release: + types: [released] + +jobs: + publish: + # Action can only be run on windows + runs-on: windows-latest + steps: + - uses: vedantmgoyal2009/winget-releaser@v1 + with: + identifier: Flow-Launcher.Flow-Launcher + token: ${{ secrets.WINGET_TOKEN }} diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs new file mode 100644 index 00000000000..9ebacc9422b --- /dev/null +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -0,0 +1,234 @@ +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.SharedCommands; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; + +namespace Flow.Launcher.Core.ExternalPlugins.Environments +{ + public abstract class AbstractPluginEnvironment + { + internal abstract string Language { get; } + + internal abstract string EnvName { get; } + + internal abstract string EnvPath { get; } + + internal abstract string InstallPath { get; } + + internal abstract string ExecutablePath { get; } + + internal virtual string FileDialogFilter => string.Empty; + + internal abstract string PluginsSettingsFilePath { get; set; } + + internal List PluginMetadataList; + + internal PluginsSettings PluginSettings; + + internal AbstractPluginEnvironment(List pluginMetadataList, PluginsSettings pluginSettings) + { + PluginMetadataList = pluginMetadataList; + PluginSettings = pluginSettings; + } + + internal IEnumerable Setup() + { + if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase))) + return new List(); + + // TODO: Remove. This is backwards compatibility for 1.10.0 release- changed PythonEmbeded to Environments/Python + if (Language.Equals(AllowedLanguage.Python, StringComparison.OrdinalIgnoreCase)) + { + FilesFolders.RemoveFolderIfExists(Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable")); + + if (!string.IsNullOrEmpty(PluginSettings.PythonDirectory) && PluginSettings.PythonDirectory.StartsWith(Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable"))) + { + InstallEnvironment(); + PluginSettings.PythonDirectory = string.Empty; + } + } + + if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath)) + { + // Ensure latest only if user is using Flow's environment setup. + if (PluginsSettingsFilePath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase)) + EnsureLatestInstalled(ExecutablePath, PluginsSettingsFilePath, EnvPath); + + return SetPathForPluginPairs(PluginsSettingsFilePath, Language); + } + + if (MessageBox.Show($"Flow detected you have installed {Language} plugins, which " + + $"will require {EnvName} to run. Would you like to download {EnvName}? " + + Environment.NewLine + Environment.NewLine + + "Click no if it's already installed, " + + $"and you will be prompted to select the folder that contains the {EnvName} executable", + string.Empty, MessageBoxButtons.YesNo) == DialogResult.No) + { + var msg = $"Please select the {EnvName} executable"; + var selectedFile = string.Empty; + + selectedFile = GetFileFromDialog(msg, FileDialogFilter); + + if (!string.IsNullOrEmpty(selectedFile)) + PluginsSettingsFilePath = selectedFile; + + // Nothing selected because user pressed cancel from the file dialog window + if (string.IsNullOrEmpty(selectedFile)) + InstallEnvironment(); + } + else + { + InstallEnvironment(); + } + + if (FilesFolders.FileExists(PluginsSettingsFilePath)) + { + return SetPathForPluginPairs(PluginsSettingsFilePath, Language); + } + else + { + MessageBox.Show( + $"Unable to set {Language} executable path, please try from Flow's settings (scroll down to the bottom)."); + Log.Error("PluginsLoader", + $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", + $"{Language}Environment"); + + return new List(); + } + } + + internal abstract void InstallEnvironment(); + + private void EnsureLatestInstalled(string expectedPath, string currentPath, string installedDirPath) + { + if (expectedPath == currentPath) + return; + + FilesFolders.RemoveFolderIfExists(installedDirPath); + + InstallEnvironment(); + + } + + internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata); + + private IEnumerable SetPathForPluginPairs(string filePath, string languageToSet) + { + var pluginPairs = new List(); + + foreach (var metadata in PluginMetadataList) + { + if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) + pluginPairs.Add(CreatePluginPair(filePath, metadata)); + } + + return pluginPairs; + } + + private string GetFileFromDialog(string title, string filter = "") + { + var dlg = new OpenFileDialog + { + InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + Multiselect = false, + CheckFileExists = true, + CheckPathExists = true, + Title = title, + Filter = filter + }; + + var result = dlg.ShowDialog(); + if (result == DialogResult.OK) + { + return dlg.FileName; + } + else + { + return string.Empty; + } + } + + /// + /// After app updated while in portable mode or switched between portable/roaming mode, + /// need to update each plugin's executable path so user will not be prompted again to reinstall the environments. + /// + /// + public static void PreStartPluginExecutablePathUpdate(Settings settings) + { + if (DataLocation.PortableDataLocationInUse()) + { + // When user is using portable but has moved flow to a different location + if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName) + && !settings.PluginSettings.PythonExecutablePath.StartsWith(DataLocation.PortableDataPath)) + { + settings.PluginSettings.PythonExecutablePath + = GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath); + } + + if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName) + && !settings.PluginSettings.NodeExecutablePath.StartsWith(DataLocation.PortableDataPath)) + { + settings.PluginSettings.NodeExecutablePath + = GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath); + } + + // When user has switched from roaming to portable + if (IsUsingRoamingPath(settings.PluginSettings.PythonExecutablePath)) + { + settings.PluginSettings.PythonExecutablePath + = settings.PluginSettings.PythonExecutablePath.Replace(DataLocation.RoamingDataPath, DataLocation.PortableDataPath); + } + + if (IsUsingRoamingPath(settings.PluginSettings.NodeExecutablePath)) + { + settings.PluginSettings.NodeExecutablePath + = settings.PluginSettings.NodeExecutablePath.Replace(DataLocation.RoamingDataPath, DataLocation.PortableDataPath); + } + } + else + { + if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName)) + settings.PluginSettings.PythonExecutablePath + = GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath); + + if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName)) + settings.PluginSettings.NodeExecutablePath + = GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath); + } + } + + private static bool IsUsingPortablePath(string filePath, string pluginEnvironmentName) + { + if (string.IsNullOrEmpty(filePath)) + return false; + + // DataLocation.PortableDataPath returns the current portable path, this determines if an out + // of date path is also a portable path. + var portableAppEnvLocation = $"UserData\\{DataLocation.PluginEnvironments}\\{pluginEnvironmentName}"; + + return filePath.Contains(portableAppEnvLocation); + } + + private static bool IsUsingRoamingPath(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + return false; + + return filePath.StartsWith(DataLocation.RoamingDataPath); + } + + private static string GetUpdatedEnvironmentPath(string filePath) + { + var index = filePath.IndexOf(DataLocation.PluginEnvironments); + + // get the substring after "Environments" because we can not determine it dynamically + var ExecutablePathSubstring = filePath.Substring(index + DataLocation.PluginEnvironments.Count()); + return $"{DataLocation.PluginEnvironmentsPath}{ExecutablePathSubstring}"; + } + } +} diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/JavaScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/JavaScriptEnvironment.cs new file mode 100644 index 00000000000..b67059b1b6b --- /dev/null +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/JavaScriptEnvironment.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; + +namespace Flow.Launcher.Core.ExternalPlugins.Environments +{ + + internal class JavaScriptEnvironment : TypeScriptEnvironment + { + internal override string Language => AllowedLanguage.JavaScript; + + internal JavaScriptEnvironment(List pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { } + } +} diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs new file mode 100644 index 00000000000..55b3b60bdcf --- /dev/null +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs @@ -0,0 +1,48 @@ +using Droplex; +using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.SharedCommands; +using System.Collections.Generic; +using System.IO; + +namespace Flow.Launcher.Core.ExternalPlugins.Environments +{ + internal class PythonEnvironment : AbstractPluginEnvironment + { + internal override string Language => AllowedLanguage.Python; + + internal override string EnvName => DataLocation.PythonEnvironmentName; + + internal override string EnvPath => Path.Combine(DataLocation.PluginEnvironmentsPath, EnvName); + + internal override string InstallPath => Path.Combine(EnvPath, "PythonEmbeddable-v3.8.9"); + + internal override string ExecutablePath => Path.Combine(InstallPath, "pythonw.exe"); + + internal override string FileDialogFilter => "Python|pythonw.exe"; + + internal override string PluginsSettingsFilePath { get => PluginSettings.PythonExecutablePath; set => PluginSettings.PythonExecutablePath = value; } + + internal PythonEnvironment(List pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { } + + internal override void InstallEnvironment() + { + FilesFolders.RemoveFolderIfExists(InstallPath); + + // Python 3.8.9 is used for Windows 7 compatibility + DroplexPackage.Drop(App.python_3_8_9_embeddable, InstallPath).Wait(); + + PluginsSettingsFilePath = ExecutablePath; + } + + internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) + { + return new PluginPair + { + Plugin = new PythonPlugin(filePath), + Metadata = metadata + }; + } + } +} diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs new file mode 100644 index 00000000000..70341f711f1 --- /dev/null +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using Droplex; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin.SharedCommands; +using Flow.Launcher.Plugin; +using System.IO; +using Flow.Launcher.Core.Plugin; + +namespace Flow.Launcher.Core.ExternalPlugins.Environments +{ + internal class TypeScriptEnvironment : AbstractPluginEnvironment + { + internal override string Language => AllowedLanguage.TypeScript; + + internal override string EnvName => DataLocation.NodeEnvironmentName; + + internal override string EnvPath => Path.Combine(DataLocation.PluginEnvironmentsPath, EnvName); + + internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0"); + internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe"); + + internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; } + + internal TypeScriptEnvironment(List pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { } + + internal override void InstallEnvironment() + { + FilesFolders.RemoveFolderIfExists(InstallPath); + + DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait(); + + PluginsSettingsFilePath = ExecutablePath; + } + + internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata) + { + return new PluginPair + { + Plugin = new NodePlugin(filePath), + Metadata = metadata + }; + } + } +} diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index ec99dc520ef..beb2925bff0 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -53,7 +53,7 @@ - + diff --git a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs index 049d1c5833e..6b55bb3e3d2 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs @@ -9,7 +9,6 @@ namespace Flow.Launcher.Core.Plugin internal class ExecutablePlugin : JsonRPCPlugin { private readonly ProcessStartInfo _startInfo; - public override string SupportedLanguage { get; set; } = AllowedLanguage.Executable; public ExecutablePlugin(string filename) { diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 4df5037a574..28d57501baa 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -37,10 +37,7 @@ internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu, ISettingProv { protected PluginInitContext context; public const string JsonRPC = "JsonRPC"; - /// - /// The language this JsonRPCPlugin support - /// - public abstract string SupportedLanguage { get; set; } + protected abstract Task RequestAsync(JsonRPCRequestModel rpcRequest, CancellationToken token = default); protected abstract string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default); diff --git a/Flow.Launcher.Core/Plugin/NodePlugin.cs b/Flow.Launcher.Core/Plugin/NodePlugin.cs new file mode 100644 index 00000000000..1247143fabe --- /dev/null +++ b/Flow.Launcher.Core/Plugin/NodePlugin.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Flow.Launcher.Plugin; + +namespace Flow.Launcher.Core.Plugin +{ + /// + /// Execution of JavaScript & TypeScript plugins + /// + internal class NodePlugin : JsonRPCPlugin + { + private readonly ProcessStartInfo _startInfo; + + public NodePlugin(string filename) + { + _startInfo = new ProcessStartInfo + { + FileName = filename, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + } + + protected override Task RequestAsync(JsonRPCRequestModel request, CancellationToken token = default) + { + _startInfo.ArgumentList[1] = request.ToString(); + return ExecuteAsync(_startInfo, token); + } + + protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default) + { + // since this is not static, request strings will build up in ArgumentList if index is not specified + _startInfo.ArgumentList[1] = rpcRequest.ToString(); + return Execute(_startInfo); + } + + public override async Task InitAsync(PluginInitContext context) + { + _startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); + _startInfo.ArgumentList.Add(string.Empty); + await base.InitAsync(context); + _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; + } + } +} diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 7521742636a..e6329aba170 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -1,30 +1,38 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; -using Droplex; -using Flow.Launcher.Infrastructure; +using Flow.Launcher.Core.ExternalPlugins.Environments; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Flow.Launcher.Plugin.SharedCommands; using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Core.Plugin { public static class PluginsLoader { - public const string PythonExecutable = "pythonw.exe"; - public static List Plugins(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); - var pythonPlugins = PythonPlugins(metadatas, settings); + + var pythonEnv = new PythonEnvironment(metadatas, settings); + var tsEnv = new TypeScriptEnvironment(metadatas, settings); + var jsEnv = new JavaScriptEnvironment(metadatas, settings); + var pythonPlugins = pythonEnv.Setup(); + var tsPlugins = tsEnv.Setup(); + var jsPlugins = jsEnv.Setup(); + var executablePlugins = ExecutablePlugins(metadatas); - var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList(); + + var plugins = dotnetPlugins + .Concat(pythonPlugins) + .Concat(tsPlugins) + .Concat(jsPlugins) + .Concat(executablePlugins) + .ToList(); return plugins; } @@ -108,97 +116,10 @@ public static IEnumerable DotNetPlugins(List source) return plugins; } - public static IEnumerable PythonPlugins(List source, PluginsSettings settings) - { - if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python)) - return new List(); - - if (!string.IsNullOrEmpty(settings.PythonDirectory) && FilesFolders.LocationExists(settings.PythonDirectory)) - return SetPythonPathForPluginPairs(source, Path.Combine(settings.PythonDirectory, PythonExecutable)); - - var pythonPath = string.Empty; - - if (MessageBox.Show("Flow detected you have installed Python plugins, which " + - "will need Python to run. Would you like to download Python? " + - 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)) - { - 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 - { - var installedPythonDirectory = Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable"); - - // Python 3.8.9 is used for Windows 7 compatibility - DroplexPackage.Drop(App.python_3_8_9_embeddable, installedPythonDirectory).Wait(); - - 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) || string.IsNullOrEmpty(pythonPath)) - { - 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(); - } - - return SetPythonPathForPluginPairs(source, pythonPath); - } - - private static IEnumerable SetPythonPathForPluginPairs(List source, string pythonPath) - => source - .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) - .Select(metadata => new PluginPair - { - Plugin = new PythonPlugin(pythonPath), - Metadata = metadata - }) - .ToList(); - public static IEnumerable ExecutablePlugins(IEnumerable source) { return source - .Where(o => o.Language.ToUpper() == AllowedLanguage.Executable) + .Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) .Select(metadata => new PluginPair { Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 8f7e5760af4..2bbf6d11088 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Threading; @@ -11,7 +11,6 @@ namespace Flow.Launcher.Core.Plugin internal class PythonPlugin : JsonRPCPlugin { private readonly ProcessStartInfo _startInfo; - public override string SupportedLanguage { get; set; } = AllowedLanguage.Python; public PythonPlugin(string filename) { @@ -46,6 +45,7 @@ protected override Task RequestAsync(JsonRPCRequestModel request, Cancel protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default) { + // since this is not static, request strings will build up in ArgumentList if index is not specified _startInfo.ArgumentList[2] = rpcRequest.ToString(); _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; // TODO: Async Action diff --git a/Flow.Launcher.Core/Resource/LocalizationConverter.cs b/Flow.Launcher.Core/Resource/LocalizationConverter.cs index 1d835a8317a..81600e023e1 100644 --- a/Flow.Launcher.Core/Resource/LocalizationConverter.cs +++ b/Flow.Launcher.Core/Resource/LocalizationConverter.cs @@ -4,7 +4,7 @@ using System.Reflection; using System.Windows.Data; -namespace Flow.Launcher.Core +namespace Flow.Launcher.Core.Resource { public class LocalizationConverter : IValueConverter { diff --git a/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs b/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs index af8b23136af..52a23233441 100644 --- a/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs +++ b/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs @@ -1,7 +1,6 @@ using System.ComponentModel; -using Flow.Launcher.Core.Resource; -namespace Flow.Launcher.Core +namespace Flow.Launcher.Core.Resource { public class LocalizedDescriptionAttribute : DescriptionAttribute { diff --git a/Flow.Launcher/Converters/TranslationConverter.cs b/Flow.Launcher.Core/Resource/TranslationConverter.cs similarity index 81% rename from Flow.Launcher/Converters/TranslationConverter.cs rename to Flow.Launcher.Core/Resource/TranslationConverter.cs index e1e8a58e368..ebab99e5b81 100644 --- a/Flow.Launcher/Converters/TranslationConverter.cs +++ b/Flow.Launcher.Core/Resource/TranslationConverter.cs @@ -1,11 +1,10 @@ using System; using System.Globalization; using System.Windows.Data; -using Flow.Launcher.Core.Resource; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Core.Resource { - public class TranlationConverter : IValueConverter + public class TranslationConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 44c47cf289d..91de8298c8e 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -114,7 +114,7 @@ private class GithubRelease public string HtmlUrl { get; [UsedImplicitly] set; } } - /// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs + // https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs private async Task GitHubUpdateManagerAsync(string repository) { var uri = new Uri(repository); @@ -144,6 +144,5 @@ public string NewVersionTips(string version) return tips; } - } } diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index 56f421e30cc..ab5e4722bf0 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -32,6 +32,7 @@ public static class Constant public static readonly string LoadingImgIcon = Path.Combine(ImagesDirectory, "loading.png"); public static string PythonPath; + public static string NodePath; public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.svg"; diff --git a/Flow.Launcher.Infrastructure/Exception/ExceptionFormatter.cs b/Flow.Launcher.Infrastructure/Exception/ExceptionFormatter.cs index 40ac6b1216d..54c19c0482d 100644 --- a/Flow.Launcher.Infrastructure/Exception/ExceptionFormatter.cs +++ b/Flow.Launcher.Infrastructure/Exception/ExceptionFormatter.cs @@ -67,6 +67,7 @@ private static string CreateExceptionReport(System.Exception ex) sb.AppendLine($"* IntPtr Length: {IntPtr.Size}"); sb.AppendLine($"* x64: {Environment.Is64BitOperatingSystem}"); sb.AppendLine($"* Python Path: {Constant.PythonPath}"); + sb.AppendLine($"* Node Path: {Constant.NodePath}"); sb.AppendLine($"* CLR Version: {Environment.Version}"); sb.AppendLine($"* Installed .NET Framework: "); foreach (var result in GetFrameworkVersionFromRegistry()) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index fb773f562d0..80c3bbec5af 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -53,7 +53,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 2fd2291d44f..e7b1f46f7e6 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; using System.Windows.Media; namespace Flow.Launcher.Infrastructure.Image @@ -87,6 +86,21 @@ public bool ContainsKey(string key, bool isFullImage) return key is not null && Data.ContainsKey((key, isFullImage)) && Data[(key, isFullImage)].imageSource != null; } + public bool TryGetValue(string key, bool isFullImage, out ImageSource image) + { + if (key is not null) + { + bool hasKey = Data.TryGetValue((key, isFullImage), out var imageUsage); + image = hasKey ? imageUsage.imageSource : null; + return hasKey; + } + else + { + image = null; + return false; + } + } + public int CacheSize() { return Data.Count; diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index deb858a79c8..62524f03a8b 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -248,7 +248,12 @@ private static BitmapSource GetThumbnail(string path, ThumbnailOptions option = public static bool CacheContainImage(string path, bool loadFullImage = false) { - return ImageCache.ContainsKey(path, false) && ImageCache[path, loadFullImage] != null; + return ImageCache.ContainsKey(path, loadFullImage); + } + + public static bool TryGetValue(string path, bool loadFullImage, out ImageSource image) + { + return ImageCache.TryGetValue(path, loadFullImage, out image); } public static async ValueTask LoadAsync(string path, bool loadFullImage = false) @@ -259,10 +264,6 @@ public static async ValueTask LoadAsync(string path, bool loadFullI if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache) { // we need to get image hash string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null; - if (imageResult.ImageType == ImageType.FullImageFile) - { - path = $"{path}_{ImageType.FullImageFile}"; - } if (hash != null) { @@ -278,7 +279,7 @@ public static async ValueTask LoadAsync(string path, bool loadFullI } // update cache - ImageCache[path, false] = img; + ImageCache[path, loadFullImage] = img; } return img; diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index e93c341dcd7..e294f52b8c5 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; +using System; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Flow.Launcher.Infrastructure.UserSettings { @@ -31,5 +27,10 @@ public static bool PortableDataLocationInUse() public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), "Settings", Constant.Plugins); + + public const string PythonEnvironmentName = "Python"; + public const string NodeEnvironmentName = "Node.js"; + public const string PluginEnvironments = "Environments"; + public static readonly string PluginEnvironmentsPath = Path.Combine(DataDirectory(), PluginEnvironments); } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index c06e1587dca..130e25d7bf1 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -1,11 +1,34 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.UserSettings { public class PluginsSettings : BaseModel { + private string pythonExecutablePath = string.Empty; + public string PythonExecutablePath { + get { return pythonExecutablePath; } + set + { + pythonExecutablePath = value; + Constant.PythonPath = value; + } + } + + private string nodeExecutablePath = string.Empty; + public string NodeExecutablePath + { + get { return nodeExecutablePath; } + set + { + nodeExecutablePath = value; + Constant.NodePath = value; + } + } + + // TODO: Remove. This is backwards compatibility for 1.10.0 release. public string PythonDirectory { get; set; } + public Dictionary Plugins { get; set; } = new Dictionary(); public void UpdatePluginSettings(List metadatas) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 09fad990bdd..a184520d081 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -18,6 +18,7 @@ public class Settings : BaseModel public string ColorScheme { get; set; } = "System"; public bool ShowOpenResultHotkey { get; set; } = true; public double WindowSize { get; set; } = 580; + public string PreviewHotkey { get; set; } = $"F1"; public string Language { @@ -147,6 +148,7 @@ public CustomBrowserViewModel CustomBrowser /// public bool ShouldUsePinyin { get; set; } = false; public bool AlwaysPreview { get; set; } = false; + public bool AlwaysStartEn { get; set; } = false; [JsonInclude, JsonConverter(typeof(JsonStringEnumConverter))] public SearchPrecisionScore QuerySearchPrecision { get; private set; } = SearchPrecisionScore.Regular; diff --git a/Flow.Launcher.Plugin/AllowedLanguage.cs b/Flow.Launcher.Plugin/AllowedLanguage.cs index 94c645d2761..d5eea4fa56b 100644 --- a/Flow.Launcher.Plugin/AllowedLanguage.cs +++ b/Flow.Launcher.Plugin/AllowedLanguage.cs @@ -1,4 +1,6 @@ -namespace Flow.Launcher.Plugin +using System; + +namespace Flow.Launcher.Plugin { /// /// Allowed plugin languages @@ -8,34 +10,32 @@ public static class AllowedLanguage /// /// Python /// - public static string Python - { - get { return "PYTHON"; } - } + public const string Python = "Python"; /// /// C# /// - public static string CSharp - { - get { return "CSHARP"; } - } + public const string CSharp = "CSharp"; /// /// F# /// - public static string FSharp - { - get { return "FSHARP"; } - } + public const string FSharp = "FSharp"; /// /// Standard .exe /// - public static string Executable - { - get { return "EXECUTABLE"; } - } + public const string Executable = "Executable"; + + /// + /// TypeScript + /// + public const string TypeScript = "TypeScript"; + + /// + /// JavaScript + /// + public const string JavaScript = "JavaScript"; /// /// Determines if this language is a .NET language @@ -44,8 +44,8 @@ public static string Executable /// public static bool IsDotNet(string language) { - return language.ToUpper() == CSharp - || language.ToUpper() == FSharp; + return language.Equals(CSharp, StringComparison.OrdinalIgnoreCase) + || language.Equals(FSharp, StringComparison.OrdinalIgnoreCase); } /// @@ -56,8 +56,10 @@ public static bool IsDotNet(string language) public static bool IsAllowed(string language) { return IsDotNet(language) - || language.ToUpper() == Python.ToUpper() - || language.ToUpper() == Executable.ToUpper(); + || language.Equals(Python, StringComparison.OrdinalIgnoreCase) + || language.Equals(Executable, StringComparison.OrdinalIgnoreCase) + || language.Equals(TypeScript, StringComparison.OrdinalIgnoreCase) + || language.Equals(JavaScript, StringComparison.OrdinalIgnoreCase); } } } diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 41072993cc6..571ac2a2344 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -14,10 +14,10 @@ - 3.0.0 - 3.0.0 - 3.0.0 - 3.0.0 + 3.0.1 + 3.0.1 + 3.0.1 + 3.0.1 Flow.Launcher.Plugin Flow-Launcher MIT diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 912a23a6f47..dadf220e38e 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -246,12 +246,14 @@ public record PreviewInfo /// public bool IsMedia { get; set; } public string Description { get; set; } + public IconDelegate PreviewDelegate { get; set; } public static PreviewInfo Default { get; } = new() { PreviewImagePath = null, Description = null, IsMedia = false, + PreviewDelegate = null, }; } } diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index c4341288f19..c67a5cf228c 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -49,7 +49,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index e0cc9b4c2f1..36f0294a93e 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -269,5 +269,129 @@ public void GivenDirectoryInfoSearch_WhenSearchPatternHotKeyIsSearchAll_ThenSear // Then Assert.AreEqual(expectedString, resultString); } + + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", false, true, "c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder\\", ResultType.Folder, "irrelevant", true, true, "c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", true, false, "p c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder\\", ResultType.Folder, "irrelevant", false, false, "c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "p", true, false, "p c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "", true, true, "c:\\somefolder\\someotherfolder\\")] + public void GivenFolderResult_WhenGetPath_ThenPathShouldBeExpectedString( + string path, + ResultType type, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var settings = new Settings() + { + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + PathSearchActionKeyword = "p", + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = Query.GlobalPluginWildcardSign + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword); + + // Then + Assert.AreEqual(result, expectedResult); + } + + [TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, true, "e c:\\somefolder\\somefile")] + [TestCase("c:\\somefolder\\somefile", ResultType.File, "p", true, false, "p c:\\somefolder\\somefile")] + [TestCase("c:\\somefolder\\somefile", ResultType.File, "e", true, true, "e c:\\somefolder\\somefile")] + [TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, false, "e c:\\somefolder\\somefile")] + public void GivenFileResult_WhenGetPath_ThenPathShouldBeExpectedString( + string path, + ResultType type, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var settings = new Settings() + { + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + PathSearchActionKeyword = "p", + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = "e" + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword); + + // Then + Assert.AreEqual(result, expectedResult); + } + + [TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "q", false, false, "q somefolder")] + [TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "i", true, false, "p c:\\somefolder\\")] + [TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "irrelevant", true, true, "c:\\somefolder\\")] + public void GivenQueryWithFolderTypeResult_WhenGetAutoComplete_ThenResultShouldBeExpectedString( + string title, + string path, + ResultType resultType, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var query = new Query() { ActionKeyword = actionKeyword }; + var settings = new Settings() + { + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + PathSearchActionKeyword = "p", + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = Query.GlobalPluginWildcardSign, + QuickAccessActionKeyword = "q", + IndexSearchActionKeyword = "i" + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetAutoCompleteText(title, query, path, resultType); + + // Then + Assert.AreEqual(result, expectedResult); + } + + [TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "q", false, false, "q somefile")] + [TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "i", true, false, "p c:\\somefolder\\somefile")] + [TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "irrelevant", true, true, "c:\\somefolder\\somefile")] + public void GivenQueryWithFileTypeResult_WhenGetAutoComplete_ThenResultShouldBeExpectedString( + string title, + string path, + ResultType resultType, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var query = new Query() { ActionKeyword = actionKeyword }; + var settings = new Settings() + { + QuickAccessActionKeyword = "q", + IndexSearchActionKeyword = "i", + PathSearchActionKeyword = "p", + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = Query.GlobalPluginWildcardSign + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetAutoCompleteText(title, query, path, resultType); + + // Then + Assert.AreEqual(result, expectedResult); + } } } diff --git a/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs b/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs index fb91c6388bd..765280e08f7 100644 --- a/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs +++ b/Flow.Launcher.Test/Plugins/JsonRPCPluginTest.cs @@ -1,4 +1,4 @@ -using NUnit; +using NUnit; using NUnit.Framework; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Plugin; @@ -16,8 +16,6 @@ namespace Flow.Launcher.Test.Plugins // ReSharper disable once InconsistentNaming internal class JsonRPCPluginTest : JsonRPCPlugin { - public override string SupportedLanguage { get; set; } = AllowedLanguage.Executable; - protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default) { throw new System.NotImplementedException(); diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 35037146123..43fa0eddb1f 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -6,6 +6,7 @@ using System.Windows; using Flow.Launcher.Core; using Flow.Launcher.Core.Configuration; +using Flow.Launcher.Core.ExternalPlugins.Environments; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; @@ -61,6 +62,8 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => _settingsVM = new SettingWindowViewModel(_updater, _portable); _settings = _settingsVM.Settings; + AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings); + _alphabet.Initialize(_settings); _stringMatcher = new StringMatcher(_alphabet); StringMatcher.Instance = _stringMatcher; diff --git a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs new file mode 100644 index 00000000000..0bff23fe178 --- /dev/null +++ b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs @@ -0,0 +1,55 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Input; + +namespace Flow.Launcher.Converters +{ + internal class BoolToIMEConversionModeConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is bool v) + { + if (v) + { + return ImeConversionModeValues.Alphanumeric; + } + else + { + return ImeConversionModeValues.DoNotCare; + } + } + return ImeConversionModeValues.DoNotCare; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + + internal class BoolToIMEStateConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is bool v) + { + if (v) + { + return InputMethodState.Off; + } + else + { + return InputMethodState.DoNotCare; + } + } + return InputMethodState.DoNotCare; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs new file mode 100644 index 00000000000..3675f06fc73 --- /dev/null +++ b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Input; + +namespace Flow.Launcher.Converters +{ + class StringToKeyBindingConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var mode = parameter as string; + var hotkeyStr = value as string; + var converter = new KeyGestureConverter(); + var key = (KeyGesture)converter.ConvertFromString(hotkeyStr); + if (mode == "key") + { + return key.Key; + } + else if (mode == "modifiers") + { + return key.Modifiers; + } + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Flow.Launcher/CustomShortcutSetting.xaml b/Flow.Launcher/CustomShortcutSetting.xaml index bbf6ff9f239..5a40a77b12e 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml +++ b/Flow.Launcher/CustomShortcutSetting.xaml @@ -64,7 +64,6 @@ { - if (mainViewModel.ShouldIgnoreHotkeys() || mainViewModel.GameModeStatus) + if (mainViewModel.ShouldIgnoreHotkeys()) return; mainViewModel.Show(); diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index e6eec4560b2..ec89377e77f 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -16,6 +16,8 @@ Copy Cut Paste + Undo + Select All File Folder Text @@ -53,9 +55,14 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. - Python bibliotek + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Autoopdatering - Vælg + Vælg Skjul Flow Launcher ved opstart Hide tray icon When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ Genvejstast Flow Launcher genvejstast Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Åbn resultatmodifikatorer Select a modifier key to open selected result via keyboard. Vis hotkey @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index 2073fd42470..2b5d2c1496c 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -16,6 +16,8 @@ Kopieren Ausschneiden Einfügen + Rückgängig + Alle auswählen Datei Ordner Text @@ -53,9 +55,14 @@ Wählen Sie den Dateimanager, der beim Öffnen des Ordners verwendet werden soll. Standardbrowser Einstellung für neuen Tab, neues Fenster und dem Privatmodus. - Python-Verzeichnis + Python-Pfad + Node.js-Pfad + Bitte wählen Sie das Programm Node.js aus + Bitte wählen Sie pythonw.exe aus + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Automatische Aktualisierung - Auswählen + Auswählen Verstecke Flow Launcher bei Systemstart Statusleistensymbol ausblenden When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Pinyin aktivieren Ermöglicht die Verwendung von Pinyin für die Suche. Pinyin ist das Standardsystem der romanisierten Schreibweise für die Übersetzung von chinesischen Texten. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Der Schatteneffekt ist nicht zulässig, wenn das aktuelle Thema den Weichzeichneffekt aktiviert hat @@ -147,6 +154,8 @@ Tastenkombination Flow Launcher Tastenkombination Verknüpfung eingeben, um Flow Launcher anzuzeigen/auszublenden. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Öffnen Sie die Ergebnismodifikatoren Select a modifier key to open selected result via keyboard. Hotkey anzeigen @@ -287,7 +296,7 @@ Bitte warten... - + Nach Updates suchen! Sie haben bereits die neuste Version Update gefunden diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index d0bdfee9b2c..430bdef2699 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -18,6 +18,8 @@ Copy Cut Paste + Undo + Select All File Folder Text @@ -55,9 +57,14 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. - Python Directory + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Auto Update - Select + Select Hide Flow Launcher on startup Hide tray icon When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -66,7 +73,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -149,6 +156,8 @@ Hotkey Flow Launcher Hotkey Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Open Result Modifier Key Select a modifier key to open selected result via keyboard. Show Hotkey @@ -289,7 +298,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/es-419.xaml b/Flow.Launcher/Languages/es-419.xaml index 3a4317ca9cb..85da4404561 100644 --- a/Flow.Launcher/Languages/es-419.xaml +++ b/Flow.Launcher/Languages/es-419.xaml @@ -16,6 +16,8 @@ Copiar Cortar Pegar + Undo + Select All Archivo Carpeta Texto @@ -53,9 +55,14 @@ Seleccione el gestor de archivos a utilizar al abrir la carpeta. Navegador web predeterminado Configuración para Nueva Pestaña, Nueva Ventana, Modo Privado. - Directorio de Python + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Actualización automática - Seleccionar + Seleccionar Ocultar Flow Launcher al arrancar el sistema Ocultar icono de la bandeja When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque habilitado @@ -147,6 +154,8 @@ Tecla Rápida Tecla de acceso a Flow Launcher Introduzca el acceso directo para mostrar/ocultar Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Abrir Tecla de Modificación de Resultado Seleccione una tecla de modificación para abrir el resultado seleccionado vía teclado. Mostrar tecla de acceso directo @@ -287,7 +296,7 @@ Por favor espere... - + Buscando nueva actualización Ya esta instalada la última versión de Flow Launcher Actualización encontrada diff --git a/Flow.Launcher/Languages/es.xaml b/Flow.Launcher/Languages/es.xaml index b3dbaf4fff6..49ae75d76be 100644 --- a/Flow.Launcher/Languages/es.xaml +++ b/Flow.Launcher/Languages/es.xaml @@ -16,6 +16,8 @@ Copiar Cortar Pegar + Deshacer + Seleccionar todo Archivo Carpeta Texto @@ -53,9 +55,14 @@ Selecciona el administrador de archivos que se desea utilizar para abrir la carpeta. Navegador web predeterminado Configuración para Nueva Pestaña, Nueva Ventana, Modo Privado. - Carpeta de Python + Ruta de Python + Ruta de Node.js + Seleccionar el ejecutable de Node.js + Seleccionar pythonw.exe + Empezar a escribir siempre en modo inglés + Cambia temporalmente el método de entrada al modo inglés cuando se activa Flow. Actualización automática - Seleccionar + Seleccionar Ocultar Flow Launcher al inicio Ocultar icono de la bandeja del sistema Cuando el icono está oculto en la bandeja del sistema, se puede abrir el menú de configuración haciendo clic con el botón derecho en la ventana de búsqueda. @@ -64,7 +71,7 @@ Buscar con Pinyin Permite utilizar Pinyin para la búsqueda. Pinyin es el sistema estándar de ortografía romanizado para traducir chino. Mostrar siempre vista previa - Muestra siempre el panel de vista previa al iniciar Flow. Pulse F1 para mostrar/ocultar la vista previa. + Muestra siempre el panel de vista previa al iniciar Flow. Pulse {0} para mostrar/ocultar la vista previa. El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque activado @@ -147,6 +154,8 @@ Atajos de teclado Atajo de teclado de Flow Launcher Introduzca el atajo de teclado para mostrar/ocultar Flow Launcher. + Atajo de teclado para vista previa + Introduzca el acceso directo para mostrar/ocultar la vista previa en la ventana de búsqueda. Tecla modificadora para abrir resultado Seleccione una tecla modificadora para abrir el resultado seleccionado con el teclado. Mostrar atajo de teclado @@ -261,7 +270,7 @@ Acceso directo de consulta personalizada - Introduzca acceso directo para mostrar directamente la consulta especificada. + Introduzca un acceso directo para expandir automáticamente la consulta especificada. El acceso directo ya existe, por favor introduzca uno nuevo o edite el existente. El acceso directo y/o su expansión están vacíos. @@ -287,7 +296,7 @@ Por favor espere... - + Comprobando actualizaciones Ya tiene la última versión de Flow Launcher Actualización encontrada diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index 4664b871dc8..ce922b70b55 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -16,6 +16,8 @@ Copier Couper Coller + Undo + Select All Fichier Dossier Texte @@ -53,9 +55,14 @@ Select the file manager to use when opening the folder. Navigateur web par défaut Setting for New Tab, New Window, Private Mode. - Répertoire de Python + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Mettre à jour automatiquement - Sélectionner + Sélectionner Cacher Flow Launcher au démarrage Masquer icône du plateau When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Devrait utiliser le pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ Raccourcis Ouvrir Flow Launcher Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Modificateurs de résultats ouverts Select a modifier key to open selected result via keyboard. Afficher le raccourci clavier @@ -286,7 +295,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index 84fcad8c89c..361bc0dcf70 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -16,6 +16,8 @@ Copia Taglia Incolla + Undo + Select All File Cartella Testo @@ -53,9 +55,14 @@ Selezionare il Gestore file da usare all'apertura della cartella. Browser predefinito Impostazione per Nuova scheda, Nuova finestra, Modalità privata. - Cartella Python + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Aggiornamento automatico - Seleziona + Seleziona Nascondi Flow Launcher all'avvio Nascondi Icona nell'Area di Notifica When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Dovrebbe usare il Pinyin Consente di utilizzare il Pinyin per la ricerca. Il Pinyin è il sistema standard di ortografia romanizzata per la traduzione del cinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. L'effetto ombra non è consentito mentre il tema corrente ha un effetto di sfocatura abilitato @@ -147,6 +154,8 @@ Tasti scelta rapida Tasto scelta rapida Flow Launcher Immettere la scorciatoia per mostrare/nascondere Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Apri modificatori di risultato Select a modifier key to open selected result via keyboard. Mostra tasto di scelta rapida @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index 816602f1c53..6bfabf93220 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -16,6 +16,8 @@ Copy 切り取り 貼り付け + Undo + Select All File Folder Text @@ -53,9 +55,14 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. - Pythonのディレクトリ + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. 自動更新 - 選択 + 選択 起動時にFlow Launcherを隠す トレイアイコンを隠す When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ ホットキー Flow Launcher ホットキー Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. 結果修飾子を開く Select a modifier key to open selected result via keyboard. ホットキーを表示 @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index f89a2845481..cfdd8574747 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -16,6 +16,8 @@ 복사하기 잘라내기 붙여넣기 + 되돌리기 + 모두 선택 파일 폴더 텍스트 @@ -53,9 +55,14 @@ 폴더를 열 때 사용할 파일관리자를 선택하세요. 기본 웹 브라우저 새 탭, 새 창, 사생활 보호 모드 - Python 디렉토리 + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + 항상 영어입력 모드에서 시작 + Flow 시작시 영어 상태에서 입력을 시작합니다. 자동 업데이트 - 선택 + 선택 시작 시 Flow Launcher 숨김 트레이 아이콘 숨기기 트레이에서 아이콘을 숨길 경우, 검색창 우클릭으로 설정창을 열 수 있습니다. @@ -64,7 +71,7 @@ 항상 Pinyin 사용 Pinyin을 사용하여 검색할 수 있습니다. Pinyin (병음) 은 로마자 중국어 입력 방식입니다. 항상 미리보기 - 항상 미리보기 패널이 열린 상태로 Flow를 시작합니다. F1키로 미리보기를 on/off 합니다. + Flow 사용시 항상 미리보기 패널을 열어둡니다. {0} 키를 눌러 프리뷰창을 켜고 끌 수 있습니다. 반투명 흐림 효과를 사용하는 경우, 그림자 효과를 쓸 수 없습니다. @@ -146,14 +153,16 @@ 단축키 Flow Launcher 단축키 - Flow Launcher를 열 때 사용할 단축키를 입력합니다. + Flow Launcher를 열 때 사용할 단축키를 입력하세요. + 미리보기 단축키 + 미리보기 패널을 켜고 끌 때 사용할 단축키를 입력하세요. 결과 선택 단축키 결과 항목을 선택하는 단축키입니다. 단축키 표시 결과창에서 결과 선택 단축키를 표시합니다. 사용자지정 쿼리 단축키 사용자 지정 쿼리 단축어 - Built-in Shortcut + 내장 단축어 쿼리 단축어 확장 @@ -164,7 +173,7 @@ 항목을 선택하세요. {0} 플러그인 단축키를 삭제하시겠습니까? Are you sure you want to delete shortcut: {0} with expansion {1}? - Get text from clipboard. + 클립보드에서 텍스트 가져오기 그림자 효과 그림자 효과는 GPU를 사용합니다. 컴퓨터 퍼포먼스가 제한적인 경우 사용을 추천하지 않습니다. 창 넓이 @@ -287,7 +296,7 @@ 잠시 기다려주세요... - + 새 업데이트 확인 중 이미 가장 최신 버전의 Flow Launcher를 사용중입니다. 업데이트 발견 diff --git a/Flow.Launcher/Languages/nb-NO.xaml b/Flow.Launcher/Languages/nb-NO.xaml index 859ec20a0bb..c8b1e4456b6 100644 --- a/Flow.Launcher/Languages/nb-NO.xaml +++ b/Flow.Launcher/Languages/nb-NO.xaml @@ -28,9 +28,8 @@ Tøm siste spørring Maks antall resultater vist Ignorer hurtigtaster i fullskjermsmodus - Python-mappe Oppdater automatisk - Velg + Velg Skjul Flow Launcher ved oppstart diff --git a/Flow.Launcher/Languages/nb.xaml b/Flow.Launcher/Languages/nb.xaml index 5356890e314..a247f84d3a9 100644 --- a/Flow.Launcher/Languages/nb.xaml +++ b/Flow.Launcher/Languages/nb.xaml @@ -16,6 +16,8 @@ Copy Cut Paste + Undo + Select All File Folder Text @@ -53,9 +55,14 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. - Python Directory + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Auto Update - Select + Select Hide Flow Launcher on startup Hide tray icon When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ Hotkey Flow Launcher Hotkey Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Open Result Modifier Key Select a modifier key to open selected result via keyboard. Show Hotkey @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index a7af3cc57af..ab9b3ac0762 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -16,6 +16,8 @@ Kopiëren Knippen Plakken + Undo + Select All Bestand Map Tekst @@ -53,9 +55,14 @@ Selecteer de bestandsbeheerder voor het openen van de map. Standaard webbrowser Instelling voor Nieuw tabblad, Nieuw Venster, Privémodus. - Python map + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Automatische Update - Selecteer + Selecteer Verberg Flow Launcher als systeem opstart Systeemvakpictogram verbergen When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Zou Pinyin moeten gebruiken Zorgt ervoor dat Pinyin gebruikt kan worden om te zoeken. Pinyin is het standaard systeem van geromaniseerde spelling voor het vertalen van Chinees. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Schaduw effect is niet toegestaan omdat het huidige thema een vervagingseffect heeft @@ -147,6 +154,8 @@ Sneltoets Flow Launcher Sneltoets Voer snelkoppeling in om Flow Launcher te tonen/verbergen. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Open resultaatmodificatoren Kies een aanpassingstoets om het geselecteerde resultaat te openen via het toetsenbord. Sneltoets weergeven @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index 0a7ccf07061..ab8e6d001b4 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -13,10 +13,12 @@ O programie Wyjdź Zamknij - Copy + Kopiuj Wytnij Wklej - File + Cofnij + Zaznacz wszystko + Plik Folder Text Tryb grania @@ -53,9 +55,14 @@ Wybierz menedżer plików używany do otwierania folderów. Domyślna przeglądarka Ustawienie dla nowej karty, nowego okna i trybu prywatnego. - Folder biblioteki Python + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Automatyczne aktualizacje - Wybierz + Wybierz Uruchamiaj Flow Launcher zminimalizowany Ukryj ikonę zasobnika When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,12 +71,12 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled - Search Plugin - Ctrl+F to search plugins + Szukaj wtyczek + Ctrl+F aby wyszukać wtyczki No results found Please try a different search. Plugin @@ -90,12 +97,12 @@ Czas ładowania: Czas zapytania: Wersja - Website + Strona Odinstalowywanie - Plugin Store + Sklep z wtyczkami New Release Recently Updated Wtyczki @@ -147,6 +154,8 @@ Skrót klawiszowy Skrót klawiszowy Flow Launcher Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Modyfikatory klawiszów otwierających wyniki Select a modifier key to open selected result via keyboard. Pokaż skrót klawiszowy @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index c308d336747..e6c6ade0bc2 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -16,6 +16,8 @@ Copy Cortar Colar + Undo + Select All File Folder Text @@ -53,9 +55,14 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. - Diretório Python + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Atualizar Automaticamente - Selecionar + Selecionar Esconder Flow Launcher na inicialização Hide tray icon When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ Atalho Atalho do Flow Launcher Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Modificadores de resultado aberto Select a modifier key to open selected result via keyboard. Mostrar tecla de atalho @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/pt-pt.xaml b/Flow.Launcher/Languages/pt-pt.xaml index a06d0e935d6..b19383e4484 100644 --- a/Flow.Launcher/Languages/pt-pt.xaml +++ b/Flow.Launcher/Languages/pt-pt.xaml @@ -16,6 +16,8 @@ Copiar Cortar Colar + Desfazer + Selecionar tudo Ficheiro Pasta Texto @@ -53,9 +55,14 @@ Selecione o gestor de ficheiros utilizado para abrir a página Navegador web padrão Definições para Novo separador, Nova Janela e Modo privado - Diretório Python + Caminho Python + Caminho Node.js + Selecione o executável Node.js + Selecione o executável pythonw.exe + Escrever sempre em inglês + Alterar, temporariamente, o método de introdução para inglês ao iniciar Flow launcher. Atualização automática - Selecionar + Selecionar Ocultar Flow Launcher ao arrancar Ocultar ícone na bandeja Se o ícone da bandeja estiver oculto, pode abrir as Definições com um clique com o botão direito do rato na caixa de pesquisa. @@ -64,7 +71,7 @@ Pesquisar com Pinyin Permite a utilização de Pinyin para pesquisar. Pinyin é um sistema normalizado de ortografia romanizada para tradução de mandarim. Pré-visualizar sempre - Abrir painel de pré-visualização ao iniciar a aplicação. Prima F1 para comutar esta opção. + Abrir painel de pré-visualização ao ativar Flow Launcher. Prima {0} para comutar a pré-visualização. O efeito sombra não é permitido com este tema porque o efeito desfocar está ativo @@ -85,7 +92,7 @@ Nova prioridade Prioridade Alterar prioridade dos resultados do plugin - Diretório de plugins + Pasta de plugins de Tempo de arranque: Tempo de consulta: @@ -147,6 +154,8 @@ Tecla de atalho Tecla de atalho Flow Launcher Introduza o atalho para mostrar/ocultar Flow Launcher + Preview Hotkey + Introduza o atalho para mostrar/ocultar a pré-visualização na janela de pesquisa. Tecla modificadora para os resultados Selecione a tecla modificadora para abrir o resultado com o teclado Mostrar tecla de atalho @@ -286,7 +295,7 @@ Por favor aguarde... - + A procurar atualizações... A sua versão de Flow Launcher é a mais recente Atualização encontrada diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 471f663eb59..aa55091be4e 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -16,6 +16,8 @@ Copy Cut Paste + Undo + Select All File Folder Text @@ -53,9 +55,14 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. - Python Directory + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Auto Update - Select + Select Hide Flow Launcher on startup Hide tray icon When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ Горячая клавиша Горячая клавиша Flow Launcher Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Открыть ключ модификации результата Select a modifier key to open selected result via keyboard. Показать горячую клавишу @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index 97223e46c03..af20a2ee937 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -16,6 +16,8 @@ Kopírovať Vystrihnúť Prilepiť + Späť + Vybrať všetko Súbor Priečinok Text @@ -53,9 +55,14 @@ Vyberte správcu súborov, ktorý sa má použiť pri otváraní priečinka. Predvolený webový prehliadač Nastavenie pre novú kartu, nové okno, privátny režim. - Priečinok s Pythonom + Cesta k Pythonu + Cesta k Node.js + Vyberte spustiteľný súbor Node.js + Prosím, vyberte pythonw.exe + Vždy písať s anglickou klávesnicou + Pri aktivácii Flowu sa dočasne zmení klávesnica na anglickú. Automatická aktualizácia - Vybrať + Vybrať Schovať Flow Launcher po spustení Schovať ikonu z oblasti oznámení Keď je ikona skrytá z oblasti oznámení, nastavenia možno otvoriť kliknutím pravým tlačidlom myši na okno vyhľadávania. @@ -64,7 +71,7 @@ Vyhľadávanie pomocou pchin-jin Umožňuje vyhľadávanie pomocou pchin-jin. Pchin-jin je systém zápisu čínskeho jazyka pomocou písmen latinky. Vždy zobraziť náhľad - Pri spustení Flowu vždy otvoriť panel s náhľadom. Stlačením klávesu F1 prepnete náhľad. + Pri aktivácii Flowu vždy otvoriť panel s náhľadom. Stlačením klávesu {0} prepnete náhľad. Efekt tieňa nie je povolený, kým má aktuálny motív povolený efekt rozostrenia @@ -98,7 +105,7 @@ Repozitár pluginov Nová verzia Nedávno aktualizované - Pluginy + Plugin Nainštalované Obnoviť Inštalovať @@ -147,6 +154,8 @@ Klávesové skratky Klávesová skratka pre Flow Launcher Zadajte skratku na zobrazenie/skrytie Flow Launchera. + Klávesová skratka pre náhľad + Zadajte klávesovú skratku pre zobrazenie/skytie náhľadu vo vyhľadávacom okne. Modifikačný kláves na otvorenie výsledkov Vyberte modifikačný kláves na otvorenie vybraného výsledku pomocou klávesnice. Zobraziť klávesovú skratku @@ -287,7 +296,7 @@ Čakajte, prosím... - + Vyhľadávajú sa aktualizácie Už máte najnovšiu verziu Flow Launchera Bola nájdená aktualizácia diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index 0ffb86fac60..f621bbf0df4 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -16,6 +16,8 @@ Copy Cut Paste + Undo + Select All File Folder Text @@ -53,9 +55,14 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. - Python direktorijum + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Auto ažuriranje - Izaberi + Izaberi Sakrij Flow Launcher pri podizanju sistema Hide tray icon When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ Prečica Flow Launcher prečica Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Отворите модификаторе резултата Select a modifier key to open selected result via keyboard. покажи хоткеи @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index c9c11dbd5f2..1f87f8fd6ca 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -16,6 +16,8 @@ Kopyala Kes Yapıştır + Undo + Select All Dosya Klasör Yazı @@ -53,9 +55,14 @@ Klasör açarken kullanılacak dosya yöneticisini seçin. Varsayılan Tarayıcısı Yeni Sekme, Yeni Pencere, Gizli Mod için Ayar. - Python Konumu + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Otomatik Güncelle - Seç + Seç Başlangıçta Flow Launcher'u gizle Sistem çekmecesi simgesini gizle When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Pinyin kullanılmalı Arama yapmak için Pinyin'in kullanılmasına izin verir. Pinyin, Çince'yi çevirmek için standart romanlaştırılmış yazım sistemidir. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Mevcut temada bulanıklık efekti etkinken gölge efektine izin verilmez @@ -147,6 +154,8 @@ Kısayol Tuşu Flow Launcher Kısayolu Enter shortcut to show/hide Flow Launcher. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Açık Sonuç Değiştiricileri Select a modifier key to open selected result via keyboard. Kısayol Tuşunu Göster @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index e8b2048d83f..e20fa7ca7c6 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -16,6 +16,8 @@ Копіювати Вирізати Вставити + Undo + Select All Файл Тека Текст @@ -53,9 +55,14 @@ Виберіть файловий менеджер для використання під час відкриття теки. Браузер за замовчуванням Налаштування нової вкладки, нового вікна, приватного режиму. - Директорія Python + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Автоматичне оновлення - Вибрати + Вибрати Сховати Flow Launcher при запуску системи Приховати значок в системному лотку When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ Використовувати піньїнь Дозволяє використовувати пінїнь для пошуку. Піньїнь - це стандартна система написання для перекладу китайської. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Ефект тіні не дозволено, коли поточна тема має ефект розмиття @@ -147,6 +154,8 @@ Гаряча клавіша Гаряча клавіша Flow Launcher Введіть ярлик для відображення/приховання потокового запуску. + Preview Hotkey + Enter shortcut to show/hide preview in search window. Відкрити ключ зміни результатів Виберіть ключ модифікатора для відкриття вибраних результатів за допомогою клавіатури. Показати гарячу клавішу @@ -287,7 +296,7 @@ Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index e4518d6c388..7fb46dd3583 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -16,6 +16,8 @@ 复制 剪切 粘贴 + 撤销 + 全选 文件 目录 文本 @@ -53,18 +55,23 @@ 选择打开文件夹时要使用的文件管理器。 默认浏览器 新标签/窗口及隐身模式设置。 - Python 路径 + Python 路径 + Node.js 路径 + 请选择 Node.js 可执行文件 + 请选择 pythonw.exe + 以英文模式开始输入 + 激活 Flow 时暂时将输入法更改为英文模式。 自动更新 - 选择 + 选择 系统启动时不显示主窗口 隐藏任务栏图标 任务栏图标被隐藏时,右键点击搜索窗口即可打开设置菜单。 查询搜索精度 更改匹配成功所需的最低分数。 - 使用 Pinyin 搜索 - 允许使用拼音进行搜索. + 使用拼音搜索 + 允许使用拼音进行搜索。 始终打开预览 - Flow 启动时总是打开预览面板。按 F1 以切换预览。 + Flow 启动时总是打开预览面板。按 {0} 以切换预览。 当前主题已启用模糊效果,不允许启用阴影效果 @@ -147,6 +154,8 @@ 热键 Flow Launcher 激活热键 输入显示/隐藏 Flow Launcher 的快捷键。 + 预览快捷键 + 输入在搜索窗口中开启/关闭预览的快捷键。 打开结果快捷键修饰符 选择一个用以打开搜索结果的按键修饰符。 显示热键 @@ -160,7 +169,7 @@ 描述 删除 编辑 - 增加 + 添加 请选择一项 你确定要删除插件 {0} 的热键吗? 你确定要删除捷径 {0} (展开为 {1})? @@ -287,7 +296,7 @@ 请稍等... - + 检查新的更新 您已经拥有最新的 Flow Launcher 版本 检查到更新 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index e5a8800f783..7d4f5d94841 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -16,6 +16,8 @@ 複製 剪下 貼上 + Undo + Select All 檔案 資料夾 文字 @@ -53,9 +55,14 @@ 選擇開啟資料夾時要使用的檔案管理器。 預設瀏覽器 設定新增分頁、視窗和無痕模式。 - Python 路徑 + Python Path + Node.js Path + Please select the Node.js executable + Please select pythonw.exe + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. 自動更新 - 選擇 + 選擇 啟動時不顯示主視窗 隱藏任務欄圖標 When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. @@ -64,7 +71,7 @@ 拼音搜索 允許使用拼音來搜索. Always Preview - Always open preview panel when Flow starts. Press F1 to toggle preview. + Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -147,6 +154,8 @@ 快捷鍵 Flow Launcher 快捷鍵 執行快捷鍵以顯示 / 隱藏 Flow Launcher。 + Preview Hotkey + Enter shortcut to show/hide preview in search window. 開放結果修飾符 Select a modifier key to open selected result via keyboard. 顯示快捷鍵 @@ -287,7 +296,7 @@ 請稍後... - + 正在檢查更新 您已經擁有最新的 Flow Launcher 版本 找到更新 diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 9f8d523e018..6ef8353d3cb 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -9,6 +9,7 @@ xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:ui="http://schemas.modernwpf.com/2019" xmlns:vm="clr-namespace:Flow.Launcher.ViewModel" + d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" Name="FlowMainWindow" Title="Flow Launcher" MinWidth="{Binding MainWindowWidth, Mode=OneWay}" @@ -37,10 +38,12 @@ + + + - - - - - + + @@ -204,6 +211,8 @@ PreviewKeyUp="QueryTextBox_KeyUp" Style="{DynamicResource QueryBoxStyle}" Text="{Binding QueryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + InputMethod.PreferredImeConversionMode="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEConversionModeConverter}}" + InputMethod.PreferredImeState="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEStateConverter}}" Visibility="Visible"> @@ -244,6 +253,7 @@ + + Grid.ColumnSpan="{Binding ResultAreaColumn}"> @@ -391,11 +401,13 @@ x:Name="Preview" Grid.Column="1" VerticalAlignment="Stretch" - d:DataContext="{d:DesignInstance vm:ResultViewModel}" - DataContext="{Binding SelectedItem, ElementName=ResultListBox}" Style="{DynamicResource PreviewArea}" - Visibility="Collapsed"> - + Visibility="{Binding PreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}"> + + Visibility="{Binding ShowPreviewImage}"> - + + + + diff --git a/Flow.Launcher/SelectBrowserWindow.xaml b/Flow.Launcher/SelectBrowserWindow.xaml index 88fd581b450..d8807dbef90 100644 --- a/Flow.Launcher/SelectBrowserWindow.xaml +++ b/Flow.Launcher/SelectBrowserWindow.xaml @@ -166,7 +166,7 @@ HorizontalAlignment="Right" VerticalAlignment="Center" Click="btnBrowseFile_Click" - Content="{DynamicResource selectPythonDirectory}" + Content="{DynamicResource select}" DockPanel.Dock="Right">