merge dev

This commit is contained in:
01Dri 2025-10-08 21:38:10 -03:00
commit 6aa35d592e
265 changed files with 5428 additions and 11069 deletions

View file

@ -3,10 +3,8 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Microsoft.Win32;
using Squirrel;
@ -17,8 +15,6 @@ namespace Flow.Launcher.Core.Configuration
{
private static readonly string ClassName = nameof(Portable);
private readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
/// <summary>
/// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish
/// </summary>
@ -45,13 +41,13 @@ namespace Flow.Launcher.Core.Configuration
#endif
IndicateDeletion(DataLocation.PortableDataPath);
API.ShowMsgBox(API.GetTranslation("restartToDisablePortableMode"));
PublicApi.Instance.ShowMsgBox(Localize.restartToDisablePortableMode());
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
catch (Exception e)
{
API.LogException(ClassName, "Error occurred while disabling portable mode", e);
PublicApi.Instance.LogException(ClassName, "Error occurred while disabling portable mode", e);
}
}
@ -68,13 +64,13 @@ namespace Flow.Launcher.Core.Configuration
#endif
IndicateDeletion(DataLocation.RoamingDataPath);
API.ShowMsgBox(API.GetTranslation("restartToEnablePortableMode"));
PublicApi.Instance.ShowMsgBox(Localize.restartToEnablePortableMode());
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
catch (Exception e)
{
API.LogException(ClassName, "Error occurred while enabling portable mode", e);
PublicApi.Instance.LogException(ClassName, "Error occurred while enabling portable mode", e);
}
}
@ -94,13 +90,13 @@ namespace Flow.Launcher.Core.Configuration
public void MoveUserDataFolder(string fromLocation, string toLocation)
{
FilesFolders.CopyAll(fromLocation, toLocation, (s) => API.ShowMsgBox(s));
FilesFolders.CopyAll(fromLocation, toLocation, (s) => PublicApi.Instance.ShowMsgBox(s));
VerifyUserDataAfterMove(fromLocation, toLocation);
}
public void VerifyUserDataAfterMove(string fromLocation, string toLocation)
{
FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => API.ShowMsgBox(s));
FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => PublicApi.Instance.ShowMsgBox(s));
}
public void CreateShortcuts()
@ -150,12 +146,12 @@ namespace Flow.Launcher.Core.Configuration
// delete it and prompt the user to pick the portable data location
if (File.Exists(roamingDataDeleteFilePath))
{
FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s));
FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => PublicApi.Instance.ShowMsgBox(s));
if (API.ShowMsgBox(API.GetTranslation("moveToDifferentLocation"),
if (PublicApi.Instance.ShowMsgBox(Localize.moveToDifferentLocation(),
string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s));
FilesFolders.OpenPath(Constant.RootDirectory, (s) => PublicApi.Instance.ShowMsgBox(s));
Environment.Exit(0);
}
@ -164,9 +160,9 @@ namespace Flow.Launcher.Core.Configuration
// delete it and notify the user about it.
else if (File.Exists(portableDataDeleteFilePath))
{
FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s));
FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => PublicApi.Instance.ShowMsgBox(s));
API.ShowMsgBox(API.GetTranslation("shortcutsUninstallerCreated"));
PublicApi.Instance.ShowMsgBox(Localize.shortcutsUninstallerCreated());
}
}
@ -177,8 +173,7 @@ namespace Flow.Launcher.Core.Configuration
if (roamingLocationExists && portableLocationExists)
{
API.ShowMsgBox(string.Format(API.GetTranslation("userDataDuplicated"),
DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine));
PublicApi.Instance.ShowMsgBox(Localize.userDataDuplicated(DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine));
return false;
}

View file

@ -8,7 +8,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Plugin;
@ -18,13 +17,9 @@ namespace Flow.Launcher.Core.ExternalPlugins
{
private static readonly string ClassName = nameof(CommunityPluginSource);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private string latestEtag = "";
private List<UserPlugin> plugins = new();
private List<UserPlugin> plugins = [];
private static readonly JsonSerializerOptions PluginStoreItemSerializationOption = new()
{
@ -41,7 +36,7 @@ namespace Flow.Launcher.Core.ExternalPlugins
/// </remarks>
public async Task<List<UserPlugin>> FetchAsync(CancellationToken token)
{
API.LogInfo(ClassName, $"Loading plugins from {ManifestFileUrl}");
PublicApi.Instance.LogInfo(ClassName, $"Loading plugins from {ManifestFileUrl}");
var request = new HttpRequestMessage(HttpMethod.Get, ManifestFileUrl);
@ -59,40 +54,40 @@ namespace Flow.Launcher.Core.ExternalPlugins
.ConfigureAwait(false);
latestEtag = response.Headers.ETag?.Tag;
API.LogInfo(ClassName, $"Loaded {plugins.Count} plugins from {ManifestFileUrl}");
PublicApi.Instance.LogInfo(ClassName, $"Loaded {plugins.Count} plugins from {ManifestFileUrl}");
return plugins;
}
else if (response.StatusCode == HttpStatusCode.NotModified)
{
API.LogInfo(ClassName, $"Resource {ManifestFileUrl} has not been modified.");
PublicApi.Instance.LogInfo(ClassName, $"Resource {ManifestFileUrl} has not been modified.");
return plugins;
}
else
{
API.LogWarn(ClassName, $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
PublicApi.Instance.LogWarn(ClassName, $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
return null;
}
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
API.LogInfo(ClassName, $"Fetching from {ManifestFileUrl} was cancelled by caller.");
PublicApi.Instance.LogDebug(ClassName, $"Fetching from {ManifestFileUrl} was cancelled by caller.");
return null;
}
catch (TaskCanceledException)
{
// Likely an HttpClient timeout or external cancellation not requested by our token
API.LogWarn(ClassName, $"Fetching from {ManifestFileUrl} timed out.");
PublicApi.Instance.LogWarn(ClassName, $"Fetching from {ManifestFileUrl} timed out.");
return null;
}
catch (Exception e)
{
if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
{
API.LogException(ClassName, $"Check your connection and proxy settings to {ManifestFileUrl}.", e);
PublicApi.Instance.LogException(ClassName, $"Check your connection and proxy settings to {ManifestFileUrl}.", e);
}
else
{
API.LogException(ClassName, "Error Occurred", e);
PublicApi.Instance.LogException(ClassName, "Error Occurred", e);
}
return null;
}

View file

@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
@ -15,7 +14,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
private static readonly string ClassName = nameof(AbstractPluginEnvironment);
protected readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
protected readonly IPublicAPI API = PublicApi.Instance;
internal abstract string Language { get; }
@ -58,15 +57,10 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
}
var noRuntimeMessage = string.Format(
API.GetTranslation("runtimePluginInstalledChooseRuntimePrompt"),
Language,
EnvName,
Environment.NewLine
);
var noRuntimeMessage = Localize.runtimePluginInstalledChooseRuntimePrompt(Language, EnvName, Environment.NewLine);
if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
{
var msg = string.Format(API.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
var msg = Localize.runtimePluginChooseRuntimeExecutable(EnvName);
var selectedFile = GetFileFromDialog(msg, FileDialogFilter);
@ -77,12 +71,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
// Nothing selected because user pressed cancel from the file dialog window
else
{
var forceDownloadMessage = string.Format(
API.GetTranslation("runtimeExecutableInvalidChooseDownload"),
Language,
EnvName,
Environment.NewLine
);
var forceDownloadMessage = Localize.runtimeExecutableInvalidChooseDownload(Language, EnvName, Environment.NewLine);
// Let users select valid path or choose to download
while (string.IsNullOrEmpty(selectedFile))
@ -120,7 +109,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
else
{
API.ShowMsgBox(string.Format(API.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
API.ShowMsgBox(Localize.runtimePluginUnableToSetExecutablePath(Language));
API.LogError(ClassName,
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
$"{Language}Environment");
@ -248,7 +237,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
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[(index + DataLocation.PluginEnvironments.Length)..];
return $"{DataLocation.PluginEnvironmentsPath}{executablePathSubstring}";

View file

@ -51,7 +51,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
catch (System.Exception e)
{
API.ShowMsgError(API.GetTranslation("failToInstallPythonEnv"));
API.ShowMsgError(Localize.failToInstallPythonEnv());
API.LogException(ClassName, "Failed to install Python environment", e);
}
});

View file

@ -46,7 +46,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
catch (System.Exception e)
{
API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv"));
API.ShowMsgError(Localize.failToInstallTypeScriptEnv());
API.LogException(ClassName, "Failed to install TypeScript environment", e);
}
});

View file

@ -46,7 +46,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
catch (System.Exception e)
{
API.ShowMsgError(API.GetTranslation("failToInstallTypeScriptEnv"));
API.ShowMsgError(Localize.failToInstallTypeScriptEnv());
API.LogException(ClassName, "Failed to install TypeScript environment", e);
}
});

View file

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin;
using Flow.Launcher.Infrastructure;
@ -23,10 +22,6 @@ namespace Flow.Launcher.Core.ExternalPlugins
private static DateTime lastFetchedAt = DateTime.MinValue;
private static readonly TimeSpan fetchTimeout = TimeSpan.FromMinutes(2);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
public static List<UserPlugin> UserPlugins { get; private set; }
public static async Task<bool> UpdateManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default)
@ -61,7 +56,7 @@ namespace Flow.Launcher.Core.ExternalPlugins
}
catch (Exception e)
{
API.LogException(ClassName, "Http request failed", e);
PublicApi.Instance.LogException(ClassName, "Http request failed", e);
}
finally
{
@ -83,12 +78,12 @@ namespace Flow.Launcher.Core.ExternalPlugins
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to parse the minimum app version {plugin.MinimumAppVersion} for plugin {plugin.Name}. "
PublicApi.Instance.LogException(ClassName, $"Failed to parse the minimum app version {plugin.MinimumAppVersion} for plugin {plugin.Name}. "
+ "Plugin excluded from manifest", e);
return false;
}
API.LogInfo(ClassName, $"Plugin {plugin.Name} requires minimum Flow Launcher version {plugin.MinimumAppVersion}, "
PublicApi.Instance.LogInfo(ClassName, $"Plugin {plugin.Name} requires minimum Flow Launcher version {plugin.MinimumAppVersion}, "
+ $"but current version is {Constant.Version}. Plugin excluded from manifest.");
return false;

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
@ -34,6 +34,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<NoWarn>$(NoWarn);FLSG0007</NoWarn>
</PropertyGroup>
<ItemGroup>
@ -55,6 +56,7 @@
<ItemGroup>
<PackageReference Include="Droplex" Version="1.7.0" />
<PackageReference Include="Flow.Launcher.Localization" Version="0.0.6" />
<PackageReference Include="FSharp.Core" Version="9.0.303" />
<PackageReference Include="Meziantou.Framework.Win32.Jobs" Version="3.4.4" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
@ -62,6 +64,17 @@
<PackageReference Include="squirrel.windows" Version="1.5.2" NoWarn="NU1701" />
<PackageReference Include="StreamJsonRpc" Version="2.22.11" />
</ItemGroup>
<PropertyGroup>
<FLLUseDependencyInjection>true</FLLUseDependencyInjection>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Remove="Languages\en.xaml" />
<AdditionalFiles Include="..\Flow.Launcher\Languages\en.xaml">
<Link>Languages\en.xaml</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />

View file

@ -285,7 +285,7 @@ namespace Flow.Launcher.Core.Plugin
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
Margin = SettingPanelItemLeftMargin,
Content = API.GetTranslation("select")
Content = Localize.select()
};
Btn.Click += (_, _) =>

View file

@ -5,7 +5,6 @@ using System.IO;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Plugin;
using System.Text.Json;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.Plugin
{
@ -13,10 +12,6 @@ namespace Flow.Launcher.Core.Plugin
{
private static readonly string ClassName = nameof(PluginConfig);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
/// <summary>
/// Parse plugin metadata in the given directories
/// </summary>
@ -38,7 +33,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Can't delete <{directory}>", e);
PublicApi.Instance.LogException(ClassName, $"Can't delete <{directory}>", e);
}
}
else
@ -55,7 +50,7 @@ namespace Flow.Launcher.Core.Plugin
duplicateList
.ForEach(
x => API.LogWarn(ClassName,
x => PublicApi.Instance.LogWarn(ClassName,
string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " +
"not loaded due to version not the highest of the duplicates",
x.Name, x.ID, x.Version),
@ -107,7 +102,7 @@ namespace Flow.Launcher.Core.Plugin
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
if (!File.Exists(configPath))
{
API.LogError(ClassName, $"Didn't find config file <{configPath}>");
PublicApi.Instance.LogError(ClassName, $"Didn't find config file <{configPath}>");
return null;
}
@ -123,19 +118,19 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Invalid json for config <{configPath}>", e);
PublicApi.Instance.LogException(ClassName, $"Invalid json for config <{configPath}>", e);
return null;
}
if (!AllowedLanguage.IsAllowed(metadata.Language))
{
API.LogError(ClassName, $"Invalid language <{metadata.Language}> for config <{configPath}>");
PublicApi.Instance.LogError(ClassName, $"Invalid language <{metadata.Language}> for config <{configPath}>");
return null;
}
if (!File.Exists(metadata.ExecuteFilePath))
{
API.LogError(ClassName, $"Execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}");
PublicApi.Instance.LogError(ClassName, $"Execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}");
return null;
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
@ -22,10 +22,6 @@ public static class PluginInstaller
private static readonly Settings Settings = Ioc.Default.GetRequiredService<Settings>();
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
/// <summary>
/// Installs a plugin and restarts the application if required by settings. Prompts user for confirmation and handles download if needed.
/// </summary>
@ -33,18 +29,16 @@ public static class PluginInstaller
/// <returns>A Task representing the asynchronous install operation.</returns>
public static async Task InstallPluginAndCheckRestartAsync(UserPlugin newPlugin)
{
if (API.PluginModified(newPlugin.ID))
if (PublicApi.Instance.PluginModified(newPlugin.ID))
{
API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), newPlugin.Name),
API.GetTranslation("pluginModifiedAlreadyMessage"));
PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(newPlugin.Name),
Localize.pluginModifiedAlreadyMessage());
return;
}
if (API.ShowMsgBox(
string.Format(
API.GetTranslation("InstallPromptSubtitle"),
newPlugin.Name, newPlugin.Author, Environment.NewLine),
API.GetTranslation("InstallPromptTitle"),
if (PublicApi.Instance.ShowMsgBox(
Localize.InstallPromptSubtitle(newPlugin.Name, newPlugin.Author, Environment.NewLine),
Localize.InstallPromptTitle(),
button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return;
try
@ -61,7 +55,7 @@ public static class PluginInstaller
if (!newPlugin.IsFromLocalInstallPath)
{
await DownloadFileAsync(
$"{API.GetTranslation("DownloadingPlugin")} {newPlugin.Name}",
$"{Localize.DownloadingPlugin()} {newPlugin.Name}",
newPlugin.UrlDownload, filePath, cts);
}
else
@ -80,7 +74,7 @@ public static class PluginInstaller
throw new FileNotFoundException($"Plugin {newPlugin.ID} zip file not found at {filePath}", filePath);
}
if (!API.InstallPlugin(newPlugin, filePath))
if (!PublicApi.Instance.InstallPlugin(newPlugin, filePath))
{
return;
}
@ -92,23 +86,20 @@ public static class PluginInstaller
}
catch (Exception e)
{
API.LogException(ClassName, "Failed to install plugin", e);
API.ShowMsgError(API.GetTranslation("ErrorInstallingPlugin"));
PublicApi.Instance.LogException(ClassName, "Failed to install plugin", e);
PublicApi.Instance.ShowMsgError(Localize.ErrorInstallingPlugin());
return; // do not restart on failure
}
if (Settings.AutoRestartAfterChanging)
{
API.RestartApp();
PublicApi.Instance.RestartApp();
}
else
{
API.ShowMsg(
API.GetTranslation("installbtn"),
string.Format(
API.GetTranslation(
"InstallSuccessNoRestart"),
newPlugin.Name));
PublicApi.Instance.ShowMsg(
Localize.installbtn(),
Localize.InstallSuccessNoRestart(newPlugin.Name));
}
}
@ -133,24 +124,23 @@ public static class PluginInstaller
}
catch (Exception e)
{
API.LogException(ClassName, "Failed to validate zip file", e);
API.ShowMsgError(API.GetTranslation("ZipFileNotHavePluginJson"));
PublicApi.Instance.LogException(ClassName, "Failed to validate zip file", e);
PublicApi.Instance.ShowMsgError(Localize.ZipFileNotHavePluginJson());
return;
}
if (API.PluginModified(plugin.ID))
if (PublicApi.Instance.PluginModified(plugin.ID))
{
API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name),
API.GetTranslation("pluginModifiedAlreadyMessage"));
PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name),
Localize.pluginModifiedAlreadyMessage());
return;
}
if (Settings.ShowUnknownSourceWarning)
{
if (!InstallSourceKnown(plugin.Website)
&& API.ShowMsgBox(string.Format(
API.GetTranslation("InstallFromUnknownSourceSubtitle"), Environment.NewLine),
API.GetTranslation("InstallFromUnknownSourceTitle"),
&& PublicApi.Instance.ShowMsgBox(Localize.InstallFromUnknownSourceSubtitle(Environment.NewLine),
Localize.InstallFromUnknownSourceTitle(),
MessageBoxButton.YesNo) == MessageBoxResult.No)
return;
}
@ -165,51 +155,46 @@ public static class PluginInstaller
/// <returns>A Task representing the asynchronous uninstall operation.</returns>
public static async Task UninstallPluginAndCheckRestartAsync(PluginMetadata oldPlugin)
{
if (API.PluginModified(oldPlugin.ID))
if (PublicApi.Instance.PluginModified(oldPlugin.ID))
{
API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), oldPlugin.Name),
API.GetTranslation("pluginModifiedAlreadyMessage"));
PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(oldPlugin.Name),
Localize.pluginModifiedAlreadyMessage());
return;
}
if (API.ShowMsgBox(
string.Format(
API.GetTranslation("UninstallPromptSubtitle"),
oldPlugin.Name, oldPlugin.Author, Environment.NewLine),
API.GetTranslation("UninstallPromptTitle"),
if (PublicApi.Instance.ShowMsgBox(
Localize.UninstallPromptSubtitle(oldPlugin.Name, oldPlugin.Author, Environment.NewLine),
Localize.UninstallPromptTitle(),
button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return;
var removePluginSettings = API.ShowMsgBox(
API.GetTranslation("KeepPluginSettingsSubtitle"),
API.GetTranslation("KeepPluginSettingsTitle"),
var removePluginSettings = PublicApi.Instance.ShowMsgBox(
Localize.KeepPluginSettingsSubtitle(),
Localize.KeepPluginSettingsTitle(),
button: MessageBoxButton.YesNo) == MessageBoxResult.No;
try
{
if (!await API.UninstallPluginAsync(oldPlugin, removePluginSettings))
if (!await PublicApi.Instance.UninstallPluginAsync(oldPlugin, removePluginSettings))
{
return;
}
}
catch (Exception e)
{
API.LogException(ClassName, "Failed to uninstall plugin", e);
API.ShowMsgError(API.GetTranslation("ErrorUninstallingPlugin"));
PublicApi.Instance.LogException(ClassName, "Failed to uninstall plugin", e);
PublicApi.Instance.ShowMsgError(Localize.ErrorUninstallingPlugin());
return; // don not restart on failure
}
if (Settings.AutoRestartAfterChanging)
{
API.RestartApp();
PublicApi.Instance.RestartApp();
}
else
{
API.ShowMsg(
API.GetTranslation("uninstallbtn"),
string.Format(
API.GetTranslation(
"UninstallSuccessNoRestart"),
oldPlugin.Name));
PublicApi.Instance.ShowMsg(
Localize.uninstallbtn(),
Localize.UninstallSuccessNoRestart(oldPlugin.Name));
}
}
@ -221,11 +206,9 @@ public static class PluginInstaller
/// <returns>A Task representing the asynchronous update operation.</returns>
public static async Task UpdatePluginAndCheckRestartAsync(UserPlugin newPlugin, PluginMetadata oldPlugin)
{
if (API.ShowMsgBox(
string.Format(
API.GetTranslation("UpdatePromptSubtitle"),
oldPlugin.Name, oldPlugin.Author, Environment.NewLine),
API.GetTranslation("UpdatePromptTitle"),
if (PublicApi.Instance.ShowMsgBox(
Localize.UpdatePromptSubtitle(oldPlugin.Name, oldPlugin.Author, Environment.NewLine),
Localize.UpdatePromptTitle(),
button: MessageBoxButton.YesNo) != MessageBoxResult.Yes) return;
try
@ -237,7 +220,7 @@ public static class PluginInstaller
if (!newPlugin.IsFromLocalInstallPath)
{
await DownloadFileAsync(
$"{API.GetTranslation("DownloadingPlugin")} {newPlugin.Name}",
$"{Localize.DownloadingPlugin()} {newPlugin.Name}",
newPlugin.UrlDownload, filePath, cts);
}
else
@ -251,30 +234,27 @@ public static class PluginInstaller
return;
}
if (!await API.UpdatePluginAsync(oldPlugin, newPlugin, filePath))
if (!await PublicApi.Instance.UpdatePluginAsync(oldPlugin, newPlugin, filePath))
{
return;
}
}
catch (Exception e)
{
API.LogException(ClassName, "Failed to update plugin", e);
API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin"));
PublicApi.Instance.LogException(ClassName, "Failed to update plugin", e);
PublicApi.Instance.ShowMsgError(Localize.ErrorUpdatingPlugin());
return; // do not restart on failure
}
if (Settings.AutoRestartAfterChanging)
{
API.RestartApp();
PublicApi.Instance.RestartApp();
}
else
{
API.ShowMsg(
API.GetTranslation("updatebtn"),
string.Format(
API.GetTranslation(
"UpdateSuccessNoRestart"),
newPlugin.Name));
PublicApi.Instance.ShowMsg(
Localize.updatebtn(),
Localize.UpdateSuccessNoRestart(newPlugin.Name));
}
}
@ -289,17 +269,17 @@ public static class PluginInstaller
public static async Task CheckForPluginUpdatesAsync(Action<List<PluginUpdateInfo>> updateAllPlugins, bool silentUpdate = true, bool usePrimaryUrlOnly = false, CancellationToken token = default)
{
// Update the plugin manifest
await API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token);
await PublicApi.Instance.UpdatePluginManifestAsync(usePrimaryUrlOnly, token);
// Get all plugins that can be updated
var resultsForUpdate = (
from existingPlugin in API.GetAllPlugins()
join pluginUpdateSource in API.GetPluginManifest()
from existingPlugin in PublicApi.Instance.GetAllPlugins()
join pluginUpdateSource in PublicApi.Instance.GetPluginManifest()
on existingPlugin.Metadata.ID equals pluginUpdateSource.ID
where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version,
StringComparison.InvariantCulture) <
0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest)
&& !API.PluginModified(existingPlugin.Metadata.ID)
&& !PublicApi.Instance.PluginModified(existingPlugin.Metadata.ID)
select
new PluginUpdateInfo()
{
@ -314,25 +294,25 @@ public static class PluginInstaller
}).ToList();
// No updates
if (!resultsForUpdate.Any())
if (resultsForUpdate.Count == 0)
{
if (!silentUpdate)
{
API.ShowMsg(API.GetTranslation("updateNoResultTitle"), API.GetTranslation("updateNoResultSubtitle"));
PublicApi.Instance.ShowMsg(Localize.updateNoResultTitle(), Localize.updateNoResultSubtitle());
}
return;
}
// If all plugins are modified, just return
if (resultsForUpdate.All(x => API.PluginModified(x.ID)))
if (resultsForUpdate.All(x => PublicApi.Instance.PluginModified(x.ID)))
{
return;
}
// Show message box with button to update all plugins
API.ShowMsgWithButton(
API.GetTranslation("updateAllPluginsTitle"),
API.GetTranslation("updateAllPluginsButtonContent"),
PublicApi.Instance.ShowMsgWithButton(
Localize.updateAllPluginsTitle(),
Localize.updateAllPluginsButtonContent(),
() =>
{
updateAllPlugins(resultsForUpdate);
@ -357,7 +337,7 @@ public static class PluginInstaller
using var cts = new CancellationTokenSource();
await DownloadFileAsync(
$"{API.GetTranslation("DownloadingPlugin")} {plugin.PluginNewUserPlugin.Name}",
$"{Localize.DownloadingPlugin()} {plugin.PluginNewUserPlugin.Name}",
plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
// check if user cancelled download before installing plugin
@ -366,7 +346,7 @@ public static class PluginInstaller
return;
}
if (!await API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath))
if (!await PublicApi.Instance.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath))
{
return;
}
@ -375,8 +355,8 @@ public static class PluginInstaller
}
catch (Exception e)
{
API.LogException(ClassName, "Failed to update plugin", e);
API.ShowMsgError(API.GetTranslation("ErrorUpdatingPlugin"));
PublicApi.Instance.LogException(ClassName, "Failed to update plugin", e);
PublicApi.Instance.ShowMsgError(Localize.ErrorUpdatingPlugin());
}
}));
@ -384,13 +364,13 @@ public static class PluginInstaller
if (restart)
{
API.RestartApp();
PublicApi.Instance.RestartApp();
}
else
{
API.ShowMsg(
API.GetTranslation("updatebtn"),
API.GetTranslation("PluginsUpdateSuccessNoRestart"));
PublicApi.Instance.ShowMsg(
Localize.updatebtn(),
Localize.PluginsUpdateSuccessNoRestart());
}
}
@ -412,7 +392,7 @@ public static class PluginInstaller
if (showProgress)
{
var exceptionHappened = false;
await API.ShowProgressBoxAsync(progressBoxTitle,
await PublicApi.Instance.ShowProgressBoxAsync(progressBoxTitle,
async (reportProgress) =>
{
if (reportProgress == null)
@ -424,18 +404,18 @@ public static class PluginInstaller
}
else
{
await API.HttpDownloadAsync(downloadUrl, filePath, reportProgress, cts.Token).ConfigureAwait(false);
await PublicApi.Instance.HttpDownloadAsync(downloadUrl, filePath, reportProgress, cts.Token).ConfigureAwait(false);
}
}, cts.Cancel);
// if exception happened while downloading and user does not cancel downloading,
// we need to redownload the plugin
if (exceptionHappened && (!cts.IsCancellationRequested))
await API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
await PublicApi.Instance.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
}
else
{
await API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
await PublicApi.Instance.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
}
}
@ -462,7 +442,7 @@ public static class PluginInstaller
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Host != acceptedHost)
return false;
return API.GetAllPlugins().Any(x =>
return PublicApi.Instance.GetAllPlugins().Any(x =>
!string.IsNullOrEmpty(x.Metadata.Website) &&
x.Metadata.Website.StartsWith(constructedUrlPart)
);

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
@ -6,7 +6,6 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.ExternalPlugins;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.DialogJump;
@ -29,10 +28,6 @@ namespace Flow.Launcher.Core.Plugin
public static readonly HashSet<PluginPair> GlobalPlugins = new();
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new();
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private static PluginsSettings Settings;
private static readonly ConcurrentBag<string> ModifiedPlugins = new();
@ -75,12 +70,12 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to save plugin {pluginPair.Metadata.Name}", e);
PublicApi.Instance.LogException(ClassName, $"Failed to save plugin {pluginPair.Metadata.Name}", e);
}
}
API.SavePluginSettings();
API.SavePluginCaches();
PublicApi.Instance.SavePluginSettings();
PublicApi.Instance.SavePluginCaches();
}
public static async ValueTask DisposePluginsAsync()
@ -107,7 +102,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to dispose plugin {pluginPair.Metadata.Name}", e);
PublicApi.Instance.LogException(ClassName, $"Failed to dispose plugin {pluginPair.Metadata.Name}", e);
}
}
@ -218,7 +213,7 @@ namespace Flow.Launcher.Core.Plugin
{
if (string.IsNullOrEmpty(metadata.AssemblyName))
{
API.LogWarn(ClassName, $"AssemblyName is empty for plugin with metadata: {metadata.Name}");
PublicApi.Instance.LogWarn(ClassName, $"AssemblyName is empty for plugin with metadata: {metadata.Name}");
continue; // Skip if AssemblyName is not set, which can happen for erroneous plugins
}
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName);
@ -228,7 +223,7 @@ namespace Flow.Launcher.Core.Plugin
{
if (string.IsNullOrEmpty(metadata.Name))
{
API.LogWarn(ClassName, $"Name is empty for plugin with metadata: {metadata.Name}");
PublicApi.Instance.LogWarn(ClassName, $"Name is empty for plugin with metadata: {metadata.Name}");
continue; // Skip if Name is not set, which can happen for erroneous plugins
}
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name);
@ -249,28 +244,28 @@ namespace Flow.Launcher.Core.Plugin
{
try
{
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Init method time cost for <{pair.Metadata.Name}>",
() => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API)));
var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Init method time cost for <{pair.Metadata.Name}>",
() => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, PublicApi.Instance)));
pair.Metadata.InitTime += milliseconds;
API.LogInfo(ClassName,
PublicApi.Instance.LogInfo(ClassName,
$"Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
}
catch (Exception e)
{
API.LogException(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e);
PublicApi.Instance.LogException(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e);
if (pair.Metadata.Disabled && pair.Metadata.HomeDisabled)
{
// If this plugin is already disabled, do not show error message again
// Or else it will be shown every time
API.LogDebug(ClassName, $"Skipped init for <{pair.Metadata.Name}> due to error");
PublicApi.Instance.LogDebug(ClassName, $"Skipped init for <{pair.Metadata.Name}> due to error");
}
else
{
pair.Metadata.Disabled = true;
pair.Metadata.HomeDisabled = true;
failedPlugins.Enqueue(pair);
API.LogDebug(ClassName, $"Disable plugin <{pair.Metadata.Name}> because init failed");
PublicApi.Instance.LogDebug(ClassName, $"Disable plugin <{pair.Metadata.Name}> because init failed");
}
}
}));
@ -295,15 +290,12 @@ namespace Flow.Launcher.Core.Plugin
}
}
if (failedPlugins.Any())
if (!failedPlugins.IsEmpty)
{
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
API.ShowMsg(
API.GetTranslation("failedToInitializePluginsTitle"),
string.Format(
API.GetTranslation("failedToInitializePluginsMessage"),
failed
),
PublicApi.Instance.ShowMsg(
Localize.failedToInitializePluginsTitle(),
Localize.failedToInitializePluginsMessage(failed),
"",
false
);
@ -326,7 +318,7 @@ namespace Flow.Launcher.Core.Plugin
if (dialogJump && plugin.Plugin is not IAsyncDialogJump)
return Array.Empty<PluginPair>();
if (API.PluginModified(plugin.Metadata.ID))
if (PublicApi.Instance.PluginModified(plugin.Metadata.ID))
return Array.Empty<PluginPair>();
return new List<PluginPair>
@ -347,7 +339,7 @@ namespace Flow.Launcher.Core.Plugin
try
{
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
async () => results = await pair.Plugin.QueryAsync(query, token).ConfigureAwait(false));
token.ThrowIfCancellationRequested();
@ -391,7 +383,7 @@ namespace Flow.Launcher.Core.Plugin
try
{
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
async () => results = await ((IAsyncHomeQuery)pair.Plugin).HomeQueryAsync(token).ConfigureAwait(false));
token.ThrowIfCancellationRequested();
@ -408,7 +400,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to query home for plugin: {metadata.Name}", e);
PublicApi.Instance.LogException(ClassName, $"Failed to query home for plugin: {metadata.Name}", e);
return null;
}
return results;
@ -421,7 +413,7 @@ namespace Flow.Launcher.Core.Plugin
try
{
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
var milliseconds = await PublicApi.Instance.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
async () => results = await ((IAsyncDialogJump)pair.Plugin).QueryDialogJumpAsync(query, token).ConfigureAwait(false));
token.ThrowIfCancellationRequested();
@ -438,7 +430,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to query Dialog Jump for plugin: {metadata.Name}", e);
PublicApi.Instance.LogException(ClassName, $"Failed to query Dialog Jump for plugin: {metadata.Name}", e);
return null;
}
return results;
@ -505,7 +497,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName,
PublicApi.Instance.LogException(ClassName,
$"Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
e);
}
@ -636,8 +628,8 @@ namespace Flow.Launcher.Core.Plugin
{
if (PluginModified(existingVersion.ID))
{
API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), existingVersion.Name),
API.GetTranslation("pluginModifiedAlreadyMessage"));
PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(existingVersion.Name),
Localize.pluginModifiedAlreadyMessage());
return false;
}
@ -669,8 +661,8 @@ namespace Flow.Launcher.Core.Plugin
{
if (checkModified && PluginModified(plugin.ID))
{
API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name),
API.GetTranslation("pluginModifiedAlreadyMessage"));
PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name),
Localize.pluginModifiedAlreadyMessage());
return false;
}
@ -689,15 +681,15 @@ namespace Flow.Launcher.Core.Plugin
if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath))
{
API.ShowMsgError(string.Format(API.GetTranslation("failedToInstallPluginTitle"), plugin.Name),
string.Format(API.GetTranslation("fileNotFoundMessage"), pluginFolderPath));
PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name),
Localize.fileNotFoundMessage(pluginFolderPath));
return false;
}
if (SameOrLesserPluginVersionExists(metadataJsonFilePath))
{
API.ShowMsgError(string.Format(API.GetTranslation("failedToInstallPluginTitle"), plugin.Name),
API.GetTranslation("pluginExistAlreadyMessage"));
PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name),
Localize.pluginExistAlreadyMessage());
return false;
}
@ -726,7 +718,7 @@ namespace Flow.Launcher.Core.Plugin
var newPluginPath = Path.Combine(installDirectory, folderName);
FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => API.ShowMsgBox(s));
FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => PublicApi.Instance.ShowMsgBox(s));
try
{
@ -735,7 +727,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e);
PublicApi.Instance.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e);
}
if (checkModified)
@ -750,8 +742,8 @@ namespace Flow.Launcher.Core.Plugin
{
if (checkModified && PluginModified(plugin.ID))
{
API.ShowMsgError(string.Format(API.GetTranslation("pluginModifiedAlreadyTitle"), plugin.Name),
API.GetTranslation("pluginModifiedAlreadyMessage"));
PublicApi.Instance.ShowMsgError(Localize.pluginModifiedAlreadyTitle(plugin.Name),
Localize.pluginModifiedAlreadyMessage());
return false;
}
@ -770,7 +762,7 @@ namespace Flow.Launcher.Core.Plugin
if (removePluginSettings)
{
// For dotnet plugins, we need to remove their PluginJsonStorage and PluginBinaryStorage instances
if (AllowedLanguage.IsDotNet(plugin.Language) && API is IRemovable removable)
if (AllowedLanguage.IsDotNet(plugin.Language) && PublicApi.Instance is IRemovable removable)
{
removable.RemovePluginSettings(plugin.AssemblyName);
removable.RemovePluginCaches(plugin.PluginCacheDirectoryPath);
@ -784,9 +776,9 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e);
API.ShowMsgError(API.GetTranslation("failedToRemovePluginSettingsTitle"),
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
PublicApi.Instance.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e);
PublicApi.Instance.ShowMsgError(Localize.failedToRemovePluginSettingsTitle(),
Localize.failedToRemovePluginSettingsMessage(plugin.Name));
}
}
@ -800,9 +792,9 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e);
API.ShowMsgError(API.GetTranslation("failedToRemovePluginCacheTitle"),
string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name));
PublicApi.Instance.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e);
PublicApi.Instance.ShowMsgError(Localize.failedToRemovePluginCacheTitle(),
Localize.failedToRemovePluginCacheMessage(plugin.Name));
}
Settings.RemovePluginSettings(plugin.ID);
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);

View file

@ -2,9 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.ExternalPlugins.Environments;
#pragma warning disable IDE0005
using Flow.Launcher.Infrastructure.Logger;
@ -18,10 +15,6 @@ namespace Flow.Launcher.Core.Plugin
{
private static readonly string ClassName = nameof(PluginsLoader);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
{
var dotnetPlugins = DotNetPlugins(metadatas);
@ -64,7 +57,7 @@ namespace Flow.Launcher.Core.Plugin
foreach (var metadata in metadatas)
{
var milliseconds = API.StopwatchLogDebug(ClassName, $"Constructor init cost for {metadata.Name}", () =>
var milliseconds = PublicApi.Instance.StopwatchLogDebug(ClassName, $"Constructor init cost for {metadata.Name}", () =>
{
Assembly assembly = null;
IAsyncPlugin plugin = null;
@ -89,19 +82,19 @@ namespace Flow.Launcher.Core.Plugin
#else
catch (Exception e) when (assembly == null)
{
Log.Exception(ClassName, $"Couldn't load assembly for the plugin: {metadata.Name}", e);
PublicApi.Instance.LogException(ClassName, $"Couldn't load assembly for the plugin: {metadata.Name}", e);
}
catch (InvalidOperationException e)
{
Log.Exception(ClassName, $"Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
PublicApi.Instance.LogException(ClassName, $"Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
}
catch (ReflectionTypeLoadException e)
{
Log.Exception(ClassName, $"The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
PublicApi.Instance.LogException(ClassName, $"The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
}
catch (Exception e)
{
Log.Exception(ClassName, $"The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
PublicApi.Instance.LogException(ClassName, $"The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
}
#endif
@ -121,12 +114,12 @@ namespace Flow.Launcher.Core.Plugin
var errorPluginString = string.Join(Environment.NewLine, erroredPlugins);
var errorMessage = erroredPlugins.Count > 1 ?
API.GetTranslation("pluginsHaveErrored") :
API.GetTranslation("pluginHasErrored");
Localize.pluginsHaveErrored():
Localize.pluginHasErrored();
API.ShowMsgError($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
PublicApi.Instance.ShowMsgError($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
API.GetTranslation("referToLogs"));
Localize.referToLogs());
}
return plugins;

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@ -6,7 +6,6 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
@ -14,14 +13,10 @@ using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.Resource
{
public class Internationalization
public class Internationalization : IDisposable
{
private static readonly string ClassName = nameof(Internationalization);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private const string Folder = "Languages";
private const string DefaultLanguageCode = "en";
private const string DefaultFile = "en.xaml";
@ -30,6 +25,7 @@ namespace Flow.Launcher.Core.Resource
private readonly List<string> _languageDirectories = [];
private readonly List<ResourceDictionary> _oldResources = [];
private static string SystemLanguageCode;
private readonly SemaphoreSlim _langChangeLock = new(1, 1);
public Internationalization(Settings settings)
{
@ -103,7 +99,7 @@ namespace Flow.Launcher.Core.Resource
var directory = Path.Combine(Constant.ProgramDirectory, Folder);
if (!Directory.Exists(directory))
{
API.LogError(ClassName, $"Flow Launcher language directory can't be found <{directory}>");
PublicApi.Instance.LogError(ClassName, $"Flow Launcher language directory can't be found <{directory}>");
return;
}
@ -174,7 +170,7 @@ namespace Flow.Launcher.Core.Resource
FirstOrDefault(o => o.LanguageCode.Equals(languageCode, StringComparison.OrdinalIgnoreCase));
if (language == null)
{
API.LogError(ClassName, $"Language code can't be found <{languageCode}>");
PublicApi.Instance.LogError(ClassName, $"Language code can't be found <{languageCode}>");
return AvailableLanguages.English;
}
else
@ -185,20 +181,33 @@ namespace Flow.Launcher.Core.Resource
private async Task ChangeLanguageAsync(Language language, bool updateMetadata = true)
{
// Remove old language files and load language
RemoveOldLanguageFiles();
if (language != AvailableLanguages.English)
await _langChangeLock.WaitAsync();
try
{
LoadLanguage(language);
// Remove old language files and load language
RemoveOldLanguageFiles();
if (language != AvailableLanguages.English)
{
LoadLanguage(language);
}
// Change culture info
ChangeCultureInfo(language.LanguageCode);
if (updateMetadata)
{
// Raise event for plugins after culture is set
await Task.Run(UpdatePluginMetadataTranslations);
}
}
// Change culture info
ChangeCultureInfo(language.LanguageCode);
if (updateMetadata)
catch (Exception e)
{
// Raise event for plugins after culture is set
await Task.Run(UpdatePluginMetadataTranslations);
PublicApi.Instance.LogException(ClassName, $"Failed to change language to <{language.LanguageCode}>", e);
}
finally
{
_langChangeLock.Release();
}
}
@ -240,7 +249,7 @@ namespace Flow.Launcher.Core.Resource
// "Do you want to search with pinyin?"
string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?";
if (API.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
if (PublicApi.Instance.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
return false;
return true;
@ -257,6 +266,7 @@ namespace Flow.Launcher.Core.Resource
{
dicts.Remove(r);
}
_oldResources.Clear();
}
private void LoadLanguage(Language language)
@ -296,7 +306,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
API.LogError(ClassName, $"Language path can't be found <{path}>");
PublicApi.Instance.LogError(ClassName, $"Language path can't be found <{path}>");
var english = Path.Combine(folder, DefaultFile);
if (File.Exists(english))
{
@ -304,7 +314,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
API.LogError(ClassName, $"Default English Language path can't be found <{path}>");
PublicApi.Instance.LogError(ClassName, $"Default English Language path can't be found <{path}>");
return string.Empty;
}
}
@ -339,7 +349,7 @@ namespace Flow.Launcher.Core.Resource
}
else
{
API.LogError(ClassName, $"No Translation for key {key}");
PublicApi.Instance.LogError(ClassName, $"No Translation for key {key}");
return $"No Translation for key {key}";
}
}
@ -362,11 +372,21 @@ namespace Flow.Launcher.Core.Resource
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e);
PublicApi.Instance.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e);
}
}
}
#endregion
#region IDisposable
public void Dispose()
{
RemoveOldLanguageFiles();
_langChangeLock.Dispose();
}
#endregion
}
}

View file

@ -1,30 +0,0 @@
using System.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.Resource
{
public class LocalizedDescriptionAttribute : DescriptionAttribute
{
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private readonly string _resourceKey;
public LocalizedDescriptionAttribute(string resourceKey)
{
_resourceKey = resourceKey;
}
public override string Description
{
get
{
string description = API.GetTranslation(_resourceKey);
return string.IsNullOrWhiteSpace(description) ?
string.Format("[[{0}]]", _resourceKey) : description;
}
}
}
}

View file

@ -444,17 +444,27 @@ namespace Flow.Launcher.Core.Resource
_api.LogError(ClassName, $"Theme <{theme}> path can't be found");
if (theme != Constant.DefaultTheme)
{
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_path_not_exists"), theme));
_api.ShowMsgBox(Localize.theme_load_failure_path_not_exists(theme));
ChangeTheme(Constant.DefaultTheme);
}
return false;
}
catch (XamlParseException)
catch (XamlParseException e)
{
_api.LogError(ClassName, $"Theme <{theme}> fail to parse");
_api.LogException(ClassName, $"Theme <{theme}> fail to parse xaml", e);
if (theme != Constant.DefaultTheme)
{
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_parse_error"), theme));
_api.ShowMsgBox(Localize.theme_load_failure_parse_error(theme));
ChangeTheme(Constant.DefaultTheme);
}
return false;
}
catch (Exception e)
{
_api.LogException(ClassName, $"Theme <{theme}> fail to load", e);
if (theme != Constant.DefaultTheme)
{
_api.ShowMsgBox(Localize.theme_load_failure_parse_error(theme));
ChangeTheme(Constant.DefaultTheme);
}
return false;

View file

@ -41,8 +41,8 @@ namespace Flow.Launcher.Core
try
{
if (!silentUpdate)
_api.ShowMsg(_api.GetTranslation("pleaseWait"),
_api.GetTranslation("update_flowlauncher_update_check"));
_api.ShowMsg(Localize.pleaseWait(),
Localize.update_flowlauncher_update_check());
using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false);
@ -58,13 +58,13 @@ namespace Flow.Launcher.Core
if (newReleaseVersion <= currentVersion)
{
if (!silentUpdate)
_api.ShowMsgBox(_api.GetTranslation("update_flowlauncher_already_on_latest"));
_api.ShowMsgBox(Localize.update_flowlauncher_already_on_latest());
return;
}
if (!silentUpdate)
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_update_found"),
_api.GetTranslation("update_flowlauncher_updating"));
_api.ShowMsg(Localize.update_flowlauncher_update_found(),
Localize.update_flowlauncher_updating());
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
@ -77,10 +77,7 @@ namespace Flow.Launcher.Core
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s));
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination,
(s) => _api.ShowMsgBox(s)))
_api.ShowMsgBox(string.Format(
_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
DataLocation.PortableDataPath,
targetDestination));
_api.ShowMsgBox(Localize.update_flowlauncher_fail_moving_portable_user_profile_data(DataLocation.PortableDataPath, targetDestination));
}
else
{
@ -91,7 +88,7 @@ namespace Flow.Launcher.Core
_api.LogInfo(ClassName, $"Update success:{newVersionTips}");
if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"),
if (_api.ShowMsgBox(newVersionTips, Localize.update_flowlauncher_new_update(),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
UpdateManager.RestartApp(Constant.ApplicationFileName);
@ -111,8 +108,8 @@ namespace Flow.Launcher.Core
}
if (!silentUpdate)
_api.ShowMsgError(_api.GetTranslation("update_flowlauncher_fail"),
_api.GetTranslation("update_flowlauncher_check_connection"));
_api.ShowMsgError(Localize.update_flowlauncher_fail(),
Localize.update_flowlauncher_check_connection());
}
finally
{
@ -150,9 +147,9 @@ namespace Flow.Launcher.Core
return manager;
}
private string NewVersionTips(string version)
private static string NewVersionTips(string version)
{
var tips = string.Format(_api.GetTranslation("newVersionTips"), version);
var tips = Localize.newVersionTips(version);
return tips;
}

View file

@ -11,6 +11,12 @@
"YamlDotNet": "9.1.0"
}
},
"Flow.Launcher.Localization": {
"type": "Direct",
"requested": "[0.0.6, )",
"resolved": "0.0.6",
"contentHash": "WNI/TLGPDr3XdOW8gaALN0Uyz9h+bzqOaNZev2nHEuA3HW9o7XuqaM6C0PqNi96mNgxiypwWpVazBNzaylJ2Aw=="
},
"FSharp.Core": {
"type": "Direct",
"requested": "[9.0.303, )",
@ -83,6 +89,11 @@
"resolved": "1.0.0",
"contentHash": "nwbZAYd+DblXAIzlnwDSnl0CiCm8jWLfHSYnoN4wYhtIav6AegB3+T/vKzLbU2IZlPB8Bvl8U3NXpx3eaz+N5w=="
},
"ini-parser": {
"type": "Transitive",
"resolved": "2.5.2",
"contentHash": "hp3gKmC/14+6eKLgv7Jd1Z7OV86lO+tNfOXr/stQbwmRhdQuXVSvrRAuAe7G5+lwhkov0XkqZ8/bn1PYWMx6eg=="
},
"InputSimulator": {
"type": "Transitive",
"resolved": "1.0.4",
@ -254,6 +265,7 @@
"Ben.Demystifier": "[0.4.1, )",
"BitFaster.Caching": "[2.5.4, )",
"CommunityToolkit.Mvvm": "[8.4.0, )",
"Flow.Launcher.Localization": "[0.0.6, )",
"Flow.Launcher.Plugin": "[5.0.0, )",
"InputSimulator": "[1.0.4, )",
"MemoryPack": "[1.21.4, )",
@ -263,7 +275,8 @@
"NLog.OutputDebugString": "[6.0.4, )",
"SharpVectors.Wpf": "[1.8.5, )",
"System.Drawing.Common": "[7.0.0, )",
"ToolGood.Words.Pinyin": "[3.1.0.3, )"
"ToolGood.Words.Pinyin": "[3.1.0.3, )",
"ini-parser": "[2.5.2, )"
}
},
"flow.launcher.plugin": {

View file

@ -58,21 +58,17 @@ namespace Flow.Launcher.Infrastructure.DialogJump
private static readonly Settings _settings = Ioc.Default.GetRequiredService<Settings>();
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private static HWND _mainWindowHandle = HWND.Null;
private static readonly Dictionary<DialogJumpExplorerPair, IDialogJumpExplorerWindow> _dialogJumpExplorers = new();
private static DialogJumpExplorerPair _lastExplorer = null;
private static readonly object _lastExplorerLock = new();
private static readonly Lock _lastExplorerLock = new();
private static readonly Dictionary<DialogJumpDialogPair, IDialogJumpDialogWindow> _dialogJumpDialogs = new();
private static IDialogJumpDialogWindow _dialogWindow = null;
private static readonly object _dialogWindowLock = new();
private static readonly Lock _dialogWindowLock = new();
private static HWINEVENTHOOK _foregroundChangeHook = HWINEVENTHOOK.Null;
private static HWINEVENTHOOK _locationChangeHook = HWINEVENTHOOK.Null;
@ -89,8 +85,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump
private static DispatcherTimer _dragMoveTimer = null;
// A list of all file dialog windows that are auto switched already
private static readonly List<HWND> _autoSwitchedDialogs = new();
private static readonly object _autoSwitchedDialogsLock = new();
private static readonly List<HWND> _autoSwitchedDialogs = [];
private static readonly Lock _autoSwitchedDialogsLock = new();
private static HWINEVENTHOOK _moveSizeHook = HWINEVENTHOOK.Null;
private static readonly WINEVENTPROC _moveProc = MoveSizeCallBack;
@ -315,7 +311,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump
{
foreach (var explorer in _dialogJumpExplorers.Keys)
{
if (API.PluginModified(explorer.Metadata.ID) || // Plugin is modified
if (PublicApi.Instance.PluginModified(explorer.Metadata.ID) || // Plugin is modified
explorer.Metadata.Disabled) continue; // Plugin is disabled
var explorerWindow = explorer.Plugin.CheckExplorerWindow(hWnd);
@ -493,7 +489,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump
var dialogWindowChanged = false;
foreach (var dialog in _dialogJumpDialogs.Keys)
{
if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified
if (PublicApi.Instance.PluginModified(dialog.Metadata.ID) || // Plugin is modified
dialog.Metadata.Disabled) continue; // Plugin is disabled
IDialogJumpDialogWindow dialogWindow;
@ -596,7 +592,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump
{
foreach (var explorer in _dialogJumpExplorers.Keys)
{
if (API.PluginModified(explorer.Metadata.ID) || // Plugin is modified
if (PublicApi.Instance.PluginModified(explorer.Metadata.ID) || // Plugin is modified
explorer.Metadata.Disabled) continue; // Plugin is disabled
var explorerWindow = explorer.Plugin.CheckExplorerWindow(hwnd);
@ -871,7 +867,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump
// Then check all dialog windows
foreach (var dialog in _dialogJumpDialogs.Keys)
{
if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified
if (PublicApi.Instance.PluginModified(dialog.Metadata.ID) || // Plugin is modified
dialog.Metadata.Disabled) continue; // Plugin is disabled
var dialogWindow = _dialogJumpDialogs[dialog];
@ -884,7 +880,7 @@ namespace Flow.Launcher.Infrastructure.DialogJump
// Finally search for the dialog window again
foreach (var dialog in _dialogJumpDialogs.Keys)
{
if (API.PluginModified(dialog.Metadata.ID) || // Plugin is modified
if (PublicApi.Instance.PluginModified(dialog.Metadata.ID) || // Plugin is modified
dialog.Metadata.Disabled) continue; // Plugin is disabled
IDialogJumpDialogWindow dialogWindow;
@ -1067,11 +1063,8 @@ namespace Flow.Launcher.Infrastructure.DialogJump
_navigationLock.Dispose();
// Stop drag move timer
if (_dragMoveTimer != null)
{
_dragMoveTimer.Stop();
_dragMoveTimer = null;
}
_dragMoveTimer?.Stop();
_dragMoveTimer = null;
}
#endregion

View file

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Win32;
namespace Flow.Launcher.Infrastructure
{
@ -13,9 +9,10 @@ namespace Flow.Launcher.Infrastructure
/// </summary>
public static string GetActiveExplorerPath()
{
var explorerWindow = GetActiveExplorer();
string locationUrl = explorerWindow?.LocationURL;
return !string.IsNullOrEmpty(locationUrl) ? GetDirectoryPath(new Uri(locationUrl).LocalPath) : null;
var explorerPath = DialogJump.DialogJump.GetActiveExplorerPath();
return !string.IsNullOrEmpty(explorerPath) ?
GetDirectoryPath(new Uri(explorerPath).LocalPath) :
null;
}
/// <summary>
@ -23,74 +20,12 @@ namespace Flow.Launcher.Infrastructure
/// </summary>
private static string GetDirectoryPath(string path)
{
if (!path.EndsWith("\\"))
if (!path.EndsWith('\\'))
{
return path + "\\";
}
return path;
}
/// <summary>
/// Gets the file explorer that is currently in the foreground
/// </summary>
private static dynamic GetActiveExplorer()
{
Type type = Type.GetTypeFromProgID("Shell.Application");
if (type == null) return null;
dynamic shell = Activator.CreateInstance(type);
if (shell == null)
{
return null;
}
var explorerWindows = new List<dynamic>();
var openWindows = shell.Windows();
for (int i = 0; i < openWindows.Count; i++)
{
var window = openWindows.Item(i);
if (window == null) continue;
// find the desired window and make sure that it is indeed a file explorer
// we don't want the Internet Explorer or the classic control panel
// ToLower() is needed, because Windows can report the path as "C:\\Windows\\Explorer.EXE"
if (Path.GetFileName((string)window.FullName)?.ToLower() == "explorer.exe")
{
explorerWindows.Add(window);
}
}
if (explorerWindows.Count == 0) return null;
var zOrders = GetZOrder(explorerWindows);
return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First;
}
/// <summary>
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
/// </summary>
private static IEnumerable<int> GetZOrder(List<dynamic> hWnds)
{
var z = new int[hWnds.Count];
for (var i = 0; i < hWnds.Count; i++) z[i] = -1;
var index = 0;
var numRemaining = hWnds.Count;
PInvoke.EnumWindows((wnd, _) =>
{
var searchIndex = hWnds.FindIndex(x => new IntPtr(x.HWND) == wnd);
if (searchIndex != -1)
{
z[searchIndex] = index;
numRemaining--;
if (numRemaining == 0) return false;
}
index++;
return true;
}, IntPtr.Zero);
return z;
}
}
}

View file

@ -34,6 +34,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<NoWarn>$(NoWarn);FLSG0007</NoWarn>
</PropertyGroup>
<ItemGroup>
@ -56,10 +57,12 @@
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="BitFaster.Caching" Version="2.5.4" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Flow.Launcher.Localization" Version="0.0.6" />
<PackageReference Include="Fody" Version="6.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="ini-parser" Version="2.5.2" />
<PackageReference Include="InputSimulator" Version="1.0.4" />
<PackageReference Include="MemoryPack" Version="1.21.4" />
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.14.15" />
@ -80,4 +83,15 @@
<PackageReference Include="ToolGood.Words.Pinyin" Version="3.1.0.3" />
</ItemGroup>
<PropertyGroup>
<FLLUseDependencyInjection>true</FLLUseDependencyInjection>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Remove="Languages\en.xaml" />
<AdditionalFiles Include="..\Flow.Launcher\Languages\en.xaml">
<Link>Languages\en.xaml</Link>
</AdditionalFiles>
</ItemGroup>
</Project>

View file

@ -4,10 +4,8 @@ using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using JetBrains.Annotations;
namespace Flow.Launcher.Infrastructure.Http
@ -20,10 +18,6 @@ namespace Flow.Launcher.Infrastructure.Http
private static readonly HttpClient client = new();
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
static Http()
{
// need to be added so it would work on a win10 machine
@ -82,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Http
}
catch (UriFormatException e)
{
API.ShowMsgError(API.GetTranslation("pleaseTryAgain"), API.GetTranslation("parseProxyFailed"));
PublicApi.Instance.ShowMsgError(Localize.pleaseTryAgain(), Localize.parseProxyFailed());
Log.Exception(ClassName, "Unable to parse Uri", e);
}
}

View file

@ -22,7 +22,7 @@ namespace Flow.Launcher.Infrastructure.Image
private static Lock storageLock { get; } = new();
private static BinaryStorage<List<(string, bool)>> _storage;
private static readonly ConcurrentDictionary<string, string> GuidToKey = new();
private static IImageHashGenerator _hashGenerator;
private static ImageHashGenerator _hashGenerator;
private static readonly bool EnableImageHash = true;
public static ImageSource Image => ImageCache[Constant.ImageIcon, false];
public static ImageSource MissingImage => ImageCache[Constant.MissingImgIcon, false];
@ -31,7 +31,7 @@ namespace Flow.Launcher.Infrastructure.Image
public const int FullIconSize = 256;
public const int FullImageSize = 320;
private static readonly string[] ImageExtensions = { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico" };
private static readonly string[] ImageExtensions = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico"];
private static readonly string SvgExtension = ".svg";
public static async Task InitializeAsync()
@ -327,7 +327,7 @@ namespace Flow.Launcher.Infrastructure.Image
return img;
}
private static ImageSource LoadFullImage(string path)
private static BitmapImage LoadFullImage(string path)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
@ -364,7 +364,7 @@ namespace Flow.Launcher.Infrastructure.Image
return image;
}
private static ImageSource LoadSvgImage(string path, bool loadFullImage = false)
private static RenderTargetBitmap LoadSvgImage(string path, bool loadFullImage = false)
{
// Set up drawing settings
var desiredHeight = loadFullImage ? FullImageSize : SmallIconSize;

View file

@ -1,13 +1,14 @@
using System;
using System.Runtime.InteropServices;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using IniParser;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.Shell;
namespace Flow.Launcher.Infrastructure.Image
{
@ -35,9 +36,32 @@ namespace Flow.Launcher.Infrastructure.Image
private static readonly HRESULT S_PATHNOTFOUND = (HRESULT)0x8004B205;
private const string UrlExtension = ".url";
/// <summary>
/// Obtains a BitmapSource thumbnail for the specified file.
/// </summary>
/// <remarks>
/// If the file is a Windows URL shortcut (".url"), the method attempts to resolve the shortcut's icon and use that for the thumbnail; otherwise it requests a thumbnail for the file path. The native HBITMAP used to create the BitmapSource is always released to avoid native memory leaks.
/// </remarks>
/// <param name="fileName">Path to the file (can be a regular file or a ".url" shortcut).</param>
/// <param name="width">Requested thumbnail width in pixels.</param>
/// <param name="height">Requested thumbnail height in pixels.</param>
/// <param name="options">Thumbnail extraction options (flags) controlling fallback and caching behavior.</param>
/// <returns>A BitmapSource representing the requested thumbnail.</returns>
public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
{
HBITMAP hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
HBITMAP hBitmap;
var extension = Path.GetExtension(fileName);
if (string.Equals(extension, UrlExtension, StringComparison.OrdinalIgnoreCase))
{
hBitmap = GetHBitmapForUrlFile(fileName, width, height, options);
}
else
{
hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
}
try
{
@ -50,6 +74,21 @@ namespace Flow.Launcher.Infrastructure.Image
}
}
/// <summary>
/// Obtains a native HBITMAP for the specified file at the requested size using the Windows Shell image factory.
/// </summary>
/// <remarks>
/// If <paramref name="options"/> is <see cref="ThumbnailOptions.ThumbnailOnly"/> and thumbnail extraction fails
/// due to extraction errors or a missing path, the method falls back to requesting an icon (<see cref="ThumbnailOptions.IconOnly"/>).
/// The returned HBITMAP is a raw GDI handle; the caller is responsible for releasing it (e.g., via DeleteObject) to avoid native memory leaks.
/// </remarks>
/// <param name="fileName">Path to the file to thumbnail.</param>
/// <param name="width">Requested thumbnail width in pixels.</param>
/// <param name="height">Requested thumbnail height in pixels.</param>
/// <param name="options">Thumbnail request flags that control behavior (e.g., ThumbnailOnly, IconOnly).</param>
/// <returns>An HBITMAP handle containing the image. Caller must free the handle when finished.</returns>
/// <exception cref="COMException">If creating the shell item fails (HRESULT returned by SHCreateItemFromParsingName).</exception>
/// <exception cref="InvalidOperationException">If the shell item does not expose IShellItemImageFactory or if an unexpected error occurs while obtaining the image.</exception>
private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
{
var retCode = PInvoke.SHCreateItemFromParsingName(
@ -108,5 +147,44 @@ namespace Flow.Launcher.Infrastructure.Image
return hBitmap;
}
/// <summary>
/// Obtains an HBITMAP for a Windows .url shortcut by resolving its IconFile entry and delegating to GetHBitmap.
/// </summary>
/// <remarks>
/// The method parses the .url file as an INI, looks in the "InternetShortcut" section for the "IconFile" entry,
/// and requests a bitmap for that icon path. If no IconFile is present or any error occurs while reading or
/// resolving the icon, it falls back to requesting a thumbnail for the .url file itself.
/// </remarks>
/// <param name="fileName">Path to the .url shortcut file.</param>
/// <param name="width">Requested thumbnail width (pixels).</param>
/// <param name="height">Requested thumbnail height (pixels).</param>
/// <param name="options">ThumbnailOptions flags controlling extraction behavior.</param>
/// <returns>An HBITMAP containing the requested image; callers are responsible for freeing the native handle.</returns>
private static unsafe HBITMAP GetHBitmapForUrlFile(string fileName, int width, int height, ThumbnailOptions options)
{
HBITMAP hBitmap;
try
{
var parser = new FileIniDataParser();
var data = parser.ReadFile(fileName);
var urlSection = data["InternetShortcut"];
var iconPath = urlSection?["IconFile"];
if (!File.Exists(iconPath))
{
// If the IconFile is missing, throw exception to fallback to the default icon
throw new FileNotFoundException("Icon file not specified in Internet shortcut (.url) file.");
}
hBitmap = GetHBitmap(Path.GetFullPath(iconPath), width, height, options);
}
catch
{
hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
}
return hBitmap;
}
}
}

View file

@ -85,5 +85,10 @@ QueryFullProcessImageName
EVENT_OBJECT_HIDE
EVENT_SYSTEM_DIALOGEND
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
WM_POWERBROADCAST
PBT_APMRESUMEAUTOMATIC
PBT_APMRESUMEAUTOMATIC
PBT_APMRESUMESUSPEND
PowerRegisterSuspendResumeNotification
PowerUnregisterSuspendResumeNotification
DeviceNotifyCallbackRoutine

View file

@ -1,11 +1,13 @@
using System.Text.Json.Serialization;
using Flow.Launcher.Plugin;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Infrastructure.UserSettings
{
public class CustomBrowserViewModel : BaseModel
{
public string Name { get; set; }
[JsonIgnore]
public string DisplayName => Name == "Default" ? Localize.defaultBrowser_default() : Name;
public string Path { get; set; }
public string PrivateArg { get; set; }
public bool EnablePrivate { get; set; }
@ -26,8 +28,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings
Editable = Editable
};
}
public void OnDisplayNameChanged()
{
OnPropertyChanged(nameof(DisplayName));
}
}
}

View file

@ -1,10 +1,13 @@
using Flow.Launcher.Plugin;
using System.Text.Json.Serialization;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.ViewModel
namespace Flow.Launcher.Infrastructure.UserSettings
{
public class CustomExplorerViewModel : BaseModel
{
public string Name { get; set; }
[JsonIgnore]
public string DisplayName => Name == "Explorer" ? Localize.fileManagerExplorer() : Name;
public string Path { get; set; }
public string FileArgument { get; set; } = "\"%d\"";
public string DirectoryArgument { get; set; } = "\"%d\"";
@ -21,5 +24,10 @@ namespace Flow.Launcher.ViewModel
Editable = Editable
};
}
public void OnDisplayNameChanged()
{
OnPropertyChanged(nameof(DisplayName));
}
}
}

View file

@ -1,8 +1,6 @@
using System;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Infrastructure.UserSettings
{
@ -55,11 +53,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
public string Description { get; set; }
public string LocalizedDescription => API.GetTranslation(Description);
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
public string LocalizedDescription => PublicApi.Instance.GetTranslation(Description);
public BaseBuiltinShortcutModel(string key, string description)
{

View file

@ -7,8 +7,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
public const string PortableFolderName = "UserData";
public const string DeletionIndicatorFile = ".dead";
public static string PortableDataPath = Path.Combine(Constant.ProgramDirectory, PortableFolderName);
public static string RoamingDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
public static readonly string PortableDataPath = Path.Combine(Constant.ProgramDirectory, PortableFolderName);
public static readonly string RoamingDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher");
public static string DataDirectory()
{
if (PortableDataLocationInUse())
@ -19,7 +19,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public static bool PortableDataLocationInUse()
{
if (Directory.Exists(PortableDataPath) && !File.Exists(DeletionIndicatorFile))
if (Directory.Exists(PortableDataPath) &&
!File.Exists(Path.Combine(PortableDataPath, DeletionIndicatorFile)))
return true;
return false;

View file

@ -9,7 +9,6 @@ using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
using Flow.Launcher.ViewModel;
namespace Flow.Launcher.Infrastructure.UserSettings
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@ -19,6 +19,7 @@ using Microsoft.Win32.SafeHandles;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.System.Power;
using Windows.Win32.System.Threading;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.Shell.Common;
@ -338,9 +339,6 @@ namespace Flow.Launcher.Infrastructure
public const int SC_MAXIMIZE = (int)PInvoke.SC_MAXIMIZE;
public const int SC_MINIMIZE = (int)PInvoke.SC_MINIMIZE;
public const int WM_POWERBROADCAST = (int)PInvoke.WM_POWERBROADCAST;
public const int PBT_APMRESUMEAUTOMATIC = (int)PInvoke.PBT_APMRESUMEAUTOMATIC;
#endregion
#region Window Handle
@ -904,5 +902,119 @@ namespace Flow.Launcher.Infrastructure
}
#endregion
#region File / Folder Dialog
public static string SelectFile()
{
var dlg = new OpenFileDialog();
var result = dlg.ShowDialog();
if (result == true)
return dlg.FileName;
return string.Empty;
}
#endregion
#region Sleep Mode Listener
private static Action _func;
private static PDEVICE_NOTIFY_CALLBACK_ROUTINE _callback = null;
private static DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS _recipient;
private static SafeHandle _recipientHandle;
private static HPOWERNOTIFY _handle = HPOWERNOTIFY.Null;
/// <summary>
/// Registers a listener for sleep mode events.
/// Inspired from: https://github.com/XKaguya/LenovoLegionToolkit
/// https://blog.csdn.net/mochounv/article/details/114668594
/// </summary>
/// <param name="func"></param>
/// <exception cref="Win32Exception"></exception>
public static unsafe void RegisterSleepModeListener(Action func)
{
if (_callback != null)
{
// Only register if not already registered
return;
}
_func = func;
_callback = new PDEVICE_NOTIFY_CALLBACK_ROUTINE(DeviceNotifyCallback);
_recipient = new DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS()
{
Callback = _callback,
Context = null
};
_recipientHandle = new StructSafeHandle<DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS>(_recipient);
_handle = PInvoke.PowerRegisterSuspendResumeNotification(
REGISTER_NOTIFICATION_FLAGS.DEVICE_NOTIFY_CALLBACK,
_recipientHandle,
out var handle) == WIN32_ERROR.ERROR_SUCCESS ?
new HPOWERNOTIFY(new IntPtr(handle)) :
HPOWERNOTIFY.Null;
if (_handle.IsNull)
{
throw new Win32Exception("Error registering for power notifications: " + Marshal.GetLastWin32Error());
}
}
/// <summary>
/// Unregisters the sleep mode listener.
/// </summary>
public static void UnregisterSleepModeListener()
{
if (!_handle.IsNull)
{
PInvoke.PowerUnregisterSuspendResumeNotification(_handle);
_handle = HPOWERNOTIFY.Null;
_func = null;
_callback = null;
_recipientHandle = null;
}
}
private static unsafe uint DeviceNotifyCallback(void* context, uint type, void* setting)
{
switch (type)
{
case PInvoke.PBT_APMRESUMEAUTOMATIC:
// Operation is resuming automatically from a low-power state.This message is sent every time the system resumes
_func?.Invoke();
break;
case PInvoke.PBT_APMRESUMESUSPEND:
// Operation is resuming from a low-power state.This message is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered by user input, such as pressing a key
_func?.Invoke();
break;
}
return 0;
}
private sealed class StructSafeHandle<T> : SafeHandle where T : struct
{
private readonly nint _ptr = nint.Zero;
public StructSafeHandle(T recipient) : base(nint.Zero, true)
{
var pRecipient = Marshal.AllocHGlobal(Marshal.SizeOf<T>());
Marshal.StructureToPtr(recipient, pRecipient, false);
SetHandle(pRecipient);
_ptr = pRecipient;
}
public override bool IsInvalid => handle == nint.Zero;
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(_ptr);
return true;
}
}
#endregion
}
}

View file

@ -23,12 +23,24 @@
"resolved": "8.4.0",
"contentHash": "tqVU8yc/ADO9oiTRyTnwhFN68hCwvkliMierptWOudIAvWY1mWCh5VFh+guwHJmpMwfg0J0rY+yyd5Oy7ty9Uw=="
},
"Flow.Launcher.Localization": {
"type": "Direct",
"requested": "[0.0.6, )",
"resolved": "0.0.6",
"contentHash": "WNI/TLGPDr3XdOW8gaALN0Uyz9h+bzqOaNZev2nHEuA3HW9o7XuqaM6C0PqNi96mNgxiypwWpVazBNzaylJ2Aw=="
},
"Fody": {
"type": "Direct",
"requested": "[6.9.3, )",
"resolved": "6.9.3",
"contentHash": "1CUGgFdyECDKgi5HaUBhdv6k+VG9Iy4OCforGfHyar3xQXAJypZkzymgKtWj/4SPd6nSG0Qi7NH71qHrDSZLaA=="
},
"ini-parser": {
"type": "Direct",
"requested": "[2.5.2, )",
"resolved": "2.5.2",
"contentHash": "hp3gKmC/14+6eKLgv7Jd1Z7OV86lO+tNfOXr/stQbwmRhdQuXVSvrRAuAe7G5+lwhkov0XkqZ8/bn1PYWMx6eg=="
},
"InputSimulator": {
"type": "Direct",
"requested": "[1.0.4, )",

View file

@ -150,6 +150,16 @@ namespace Flow.Launcher.Plugin.SharedCommands
return File.Exists(filePath);
}
/// <summary>
/// Checks if a file or directory exists
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static bool FileOrLocationExists(this string path)
{
return LocationExists(path) || FileExists(path);
}
/// <summary>
/// Open a directory window (using the OS's default handler, usually explorer)
/// </summary>

View file

@ -47,7 +47,7 @@ namespace Flow.Launcher
if (addedActionKeywords.Any(App.API.ActionKeywordAssigned))
{
App.API.ShowMsgBox(App.API.GetTranslation("newActionKeywordsHasBeenAssigned"));
App.API.ShowMsgBox(Localize.newActionKeywordsHasBeenAssigned());
return;
}
@ -63,7 +63,7 @@ namespace Flow.Launcher
if (sortedOldActionKeywords.SequenceEqual(sortedNewActionKeywords))
{
// User just changes the sequence of action keywords
App.API.ShowMsgBox(App.API.GetTranslation("newActionKeywordsSameAsOld"));
App.API.ShowMsgBox(Localize.newActionKeywordsSameAsOld());
}
else
{

View file

@ -2,7 +2,8 @@
x:Class="Flow.Launcher.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
ShutdownMode="OnMainWindowClose"
Startup="OnStartup">
<Application.Resources>
@ -10,17 +11,17 @@
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources>
<ui:ThemeResources.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<ResourceDictionary x:Key="Light" ui:ThemeDictionary.Key="Light">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/Light.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<ResourceDictionary x:Key="Dark" ui:ThemeDictionary.Key="Dark">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/Dark.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<ResourceDictionary x:Key="HighContrast" ui:ThemeDictionary.Key="HighContrast">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources/Dark.xaml" />
</ResourceDictionary.MergedDictionaries>
@ -33,6 +34,15 @@
<ResourceDictionary Source="pack://application:,,,/Themes/Win11Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/Languages/en.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Override styles in UI.Modern.WPF -->
<Thickness x:Key="ListViewItemCompactSelectedBorderThemeThickness">2</Thickness>
<sys:Double x:Key="CheckBoxMinWidth">0</sys:Double>
<sys:Double x:Key="GridViewItemMinWidth">0</sys:Double>
<sys:Double x:Key="GridViewItemMinHeight">40</sys:Double>
<sys:Double x:Key="ListViewItemMinWidth">0</sys:Double>
<sys:Double x:Key="ListViewItemMinHeight">36</sys:Double>
<SolidColorBrush x:Key="NavigationViewSelectionIndicatorForeground" Color="#FF0063B1" />
</ResourceDictionary>
</Application.Resources>
</Application>

View file

@ -22,6 +22,7 @@ using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.SettingPages.ViewModels;
using Flow.Launcher.ViewModel;
using iNKORE.UI.WPF.Modern.Common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.Threading;
@ -45,6 +46,7 @@ namespace Flow.Launcher
private static Settings _settings;
private static MainWindow _mainWindow;
private readonly MainViewModel _mainVM;
private readonly Internationalization _internationalization;
// To prevent two disposals running at the same time.
private static readonly object _disposingLock = new();
@ -55,6 +57,9 @@ namespace Flow.Launcher
public App()
{
// Do not use bitmap cache since it can cause WPF second window freezing issue
ShadowAssist.UseBitmapCache = false;
// Initialize settings
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
@ -107,6 +112,7 @@ namespace Flow.Launcher
API = Ioc.Default.GetRequiredService<IPublicAPI>();
_settings.Initialize();
_mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
_internationalization = Ioc.Default.GetRequiredService<Internationalization>();
}
catch (Exception e)
{
@ -193,7 +199,7 @@ namespace Flow.Launcher
Win32Helper.EnableWin32DarkMode(_settings.ColorScheme);
// Initialize language before portable clean up since it needs translations
await Ioc.Default.GetRequiredService<Internationalization>().InitializeLanguageAsync();
await _internationalization.InitializeLanguageAsync();
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();
@ -274,7 +280,7 @@ namespace Flow.Launcher
// but if it fails (permissions, etc) then don't keep retrying
// this also gives the user a visual indication in the Settings widget
_settings.StartFlowLauncherOnSystemStartup = false;
API.ShowMsgError(API.GetTranslation("setAutoStartFailed"), e.Message);
API.ShowMsgError(Localize.setAutoStartFailed(), e.Message);
}
}
}
@ -421,6 +427,7 @@ namespace Flow.Launcher
_mainWindow?.Dispatcher.Invoke(_mainWindow.Dispose);
_mainVM?.Dispose();
DialogJump.Dispose();
_internationalization.Dispose();
}
API.LogInfo(ClassName, "End Flow Launcher dispose ----------------------------------------------------");

View file

@ -5,7 +5,7 @@ using System.Windows.Input;
namespace Flow.Launcher.Converters;
internal class BoolToIMEConversionModeConverter : IValueConverter
public class BoolToIMEConversionModeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
@ -22,7 +22,7 @@ internal class BoolToIMEConversionModeConverter : IValueConverter
}
}
internal class BoolToIMEStateConverter : IValueConverter
public class BoolToIMEStateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{

View file

@ -0,0 +1,91 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Flow.Launcher.Converters;
public class CornerRadiusFilterConverter : DependencyObject, IValueConverter
{
public CornerRadiusFilterKind Filter { get; set; }
public double Scale { get; set; } = 1.0;
public static CornerRadius Convert(CornerRadius radius, CornerRadiusFilterKind filterKind)
{
CornerRadius result = radius;
switch (filterKind)
{
case CornerRadiusFilterKind.Top:
result.BottomLeft = 0;
result.BottomRight = 0;
break;
case CornerRadiusFilterKind.Right:
result.TopLeft = 0;
result.BottomLeft = 0;
break;
case CornerRadiusFilterKind.Bottom:
result.TopLeft = 0;
result.TopRight = 0;
break;
case CornerRadiusFilterKind.Left:
result.TopRight = 0;
result.BottomRight = 0;
break;
}
return result;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var cornerRadius = (CornerRadius)value;
var scale = Scale;
if (!double.IsNaN(scale))
{
cornerRadius.TopLeft *= scale;
cornerRadius.TopRight *= scale;
cornerRadius.BottomRight *= scale;
cornerRadius.BottomLeft *= scale;
}
var filterType = Filter;
if (filterType == CornerRadiusFilterKind.TopLeftValue ||
filterType == CornerRadiusFilterKind.BottomRightValue)
{
return GetDoubleValue(cornerRadius, filterType);
}
return Convert(cornerRadius, filterType);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private static double GetDoubleValue(CornerRadius radius, CornerRadiusFilterKind filterKind)
{
switch (filterKind)
{
case CornerRadiusFilterKind.TopLeftValue:
return radius.TopLeft;
case CornerRadiusFilterKind.BottomRightValue:
return radius.BottomRight;
}
return 0;
}
}
public enum CornerRadiusFilterKind
{
None,
Top,
Right,
Bottom,
Left,
TopLeftValue,
BottomRightValue
}

View file

@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Flow.Launcher.Converters;
public class PlacementRectangleConverter : IMultiValueConverter
{
public Thickness Margin { get; set; }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2 &&
values[0] is double width &&
values[1] is double height)
{
var margin = Margin;
var topLeft = new Point(margin.Left, margin.Top);
var bottomRight = new Point(width - margin.Right, height - margin.Bottom);
var rect = new Rect(topLeft, bottomRight);
return rect;
}
return Rect.Empty;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Flow.Launcher.Converters;
public class SharedSizeGroupConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (Visibility)value != Visibility.Collapsed ? (string)parameter : null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View file

@ -5,7 +5,7 @@ using System.Windows.Input;
namespace Flow.Launcher.Converters;
class StringToKeyBindingConverter : IValueConverter
public class StringToKeyBindingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{

View file

@ -41,7 +41,7 @@ namespace Flow.Launcher
if (string.IsNullOrEmpty(Hotkey) && string.IsNullOrEmpty(ActionKeyword))
{
App.API.ShowMsgBox(App.API.GetTranslation("emptyPluginHotkey"));
App.API.ShowMsgBox(Localize.emptyPluginHotkey());
return;
}

View file

@ -40,14 +40,14 @@ namespace Flow.Launcher
{
if (string.IsNullOrEmpty(Key) || string.IsNullOrEmpty(Value))
{
App.API.ShowMsgBox(App.API.GetTranslation("emptyShortcut"));
App.API.ShowMsgBox(Localize.emptyShortcut());
return;
}
// Check if key is modified or adding a new one
if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key))
{
App.API.ShowMsgBox(App.API.GetTranslation("duplicateShortcut"));
App.API.ShowMsgBox(Localize.duplicateShortcut());
return;
}

View file

@ -37,14 +37,53 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<NoWarn>$(NoWarn);FLSG0007</NoWarn>
</PropertyGroup>
<Target Name="RemoveUnnecessaryRuntimesAfterBuild" AfterTargets="Build">
<RemoveDir Directories="$(OutputPath)runtimes\browser-wasm;&#xD;&#xA; $(OutputPath)runtimes\linux-arm;&#xD;&#xA; $(OutputPath)runtimes\linux-arm64;&#xD;&#xA; $(OutputPath)runtimes\linux-armel;&#xD;&#xA; $(OutputPath)runtimes\linux-mips64;&#xD;&#xA; $(OutputPath)runtimes\linux-musl-arm;&#xD;&#xA; $(OutputPath)runtimes\linux-musl-arm64;&#xD;&#xA; $(OutputPath)runtimes\linux-musl-x64;&#xD;&#xA; $(OutputPath)runtimes\linux-musl-s390x;&#xD;&#xA; $(OutputPath)runtimes\linux-ppc64le;&#xD;&#xA; $(OutputPath)runtimes\linux-s390x;&#xD;&#xA; $(OutputPath)runtimes\linux-x64;&#xD;&#xA; $(OutputPath)runtimes\linux-x86;&#xD;&#xA; $(OutputPath)runtimes\maccatalyst-arm64;&#xD;&#xA; $(OutputPath)runtimes\maccatalyst-x64;&#xD;&#xA; $(OutputPath)runtimes\osx;&#xD;&#xA; $(OutputPath)runtimes\osx-arm64;&#xD;&#xA; $(OutputPath)runtimes\osx-x64;&#xD;&#xA; $(OutputPath)runtimes\win-arm;&#xD;&#xA; $(OutputPath)runtimes\win-arm64;" />
<RemoveDir Directories="$(OutputPath)runtimes\browser-wasm;
$(OutputPath)runtimes\linux-arm;
$(OutputPath)runtimes\linux-arm64;
$(OutputPath)runtimes\linux-armel;
$(OutputPath)runtimes\linux-mips64;
$(OutputPath)runtimes\linux-musl-arm;
$(OutputPath)runtimes\linux-musl-arm64;
$(OutputPath)runtimes\linux-musl-x64;
$(OutputPath)runtimes\linux-musl-s390x;
$(OutputPath)runtimes\linux-ppc64le;
$(OutputPath)runtimes\linux-s390x;
$(OutputPath)runtimes\linux-x64;
$(OutputPath)runtimes\linux-x86;
$(OutputPath)runtimes\maccatalyst-arm64;
$(OutputPath)runtimes\maccatalyst-x64;
$(OutputPath)runtimes\osx;
$(OutputPath)runtimes\osx-arm64;
$(OutputPath)runtimes\osx-x64;
$(OutputPath)runtimes\win-arm;
$(OutputPath)runtimes\win-arm64;"/>
</Target>
<Target Name="RemoveUnnecessaryRuntimesAfterPublish" AfterTargets="Publish">
<RemoveDir Directories="$(PublishDir)runtimes\browser-wasm;&#xD;&#xA; $(PublishDir)runtimes\linux-arm;&#xD;&#xA; $(PublishDir)runtimes\linux-arm64;&#xD;&#xA; $(PublishDir)runtimes\linux-armel;&#xD;&#xA; $(PublishDir)runtimes\linux-mips64;&#xD;&#xA; $(PublishDir)runtimes\linux-musl-arm;&#xD;&#xA; $(PublishDir)runtimes\linux-musl-arm64;&#xD;&#xA; $(PublishDir)runtimes\linux-musl-x64;&#xD;&#xA; $(PublishDir)runtimes\linux-musl-s390x;&#xD;&#xA; $(PublishDir)runtimes\linux-ppc64le;&#xD;&#xA; $(PublishDir)runtimes\linux-s390x;&#xD;&#xA; $(PublishDir)runtimes\linux-x64;&#xD;&#xA; $(PublishDir)runtimes\linux-x86;&#xD;&#xA; $(PublishDir)runtimes\maccatalyst-arm64;&#xD;&#xA; $(PublishDir)runtimes\maccatalyst-x64;&#xD;&#xA; $(PublishDir)runtimes\osx;&#xD;&#xA; $(PublishDir)runtimes\osx-arm64;&#xD;&#xA; $(PublishDir)runtimes\osx-x64;&#xD;&#xA; $(PublishDir)runtimes\win-arm;&#xD;&#xA; $(PublishDir)runtimes\win-arm64;" />
<RemoveDir Directories="$(PublishDir)runtimes\browser-wasm;
$(PublishDir)runtimes\linux-arm;
$(PublishDir)runtimes\linux-arm64;
$(PublishDir)runtimes\linux-armel;
$(PublishDir)runtimes\linux-mips64;
$(PublishDir)runtimes\linux-musl-arm;
$(PublishDir)runtimes\linux-musl-arm64;
$(PublishDir)runtimes\linux-musl-x64;
$(PublishDir)runtimes\linux-musl-s390x;
$(PublishDir)runtimes\linux-ppc64le;
$(PublishDir)runtimes\linux-s390x;
$(PublishDir)runtimes\linux-x64;
$(PublishDir)runtimes\linux-x86;
$(PublishDir)runtimes\maccatalyst-arm64;
$(PublishDir)runtimes\maccatalyst-x64;
$(PublishDir)runtimes\osx;
$(PublishDir)runtimes\osx-arm64;
$(PublishDir)runtimes\osx-x64;
$(PublishDir)runtimes\win-arm;
$(PublishDir)runtimes\win-arm64;"/>
</Target>
<ItemGroup>
@ -94,10 +133,12 @@
<ItemGroup>
<PackageReference Include="ChefKeys" Version="0.1.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Flow.Launcher.Localization" Version="0.0.6" />
<PackageReference Include="Fody" Version="6.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.1" />
<PackageReference Include="MdXaml" Version="1.27.0" />
<PackageReference Include="MdXaml.AnimatedGif" Version="1.27.0" />
<PackageReference Include="MdXaml.Html" Version="1.27.0" />
@ -106,9 +147,6 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.9" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<!-- ModernWpfUI v0.9.5 introduced WinRT changes that causes Notification platform unavailable error on some machines -->
<!-- https://github.com/Flow-Launcher/Flow.Launcher/issues/1772#issuecomment-1502440801 -->
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
@ -123,6 +161,10 @@
<ProjectReference Include="..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
<PropertyGroup>
<FLLUseDependencyInjection>true</FLLUseDependencyInjection>
</PropertyGroup>
<ItemGroup>
<Content Include="Resources\open.wav">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

View file

@ -0,0 +1,33 @@
using System.Windows;
using System.Windows.Controls;
namespace Flow.Launcher.Helper;
public static class BorderHelper
{
#region Child
public static readonly DependencyProperty ChildProperty =
DependencyProperty.RegisterAttached(
"Child",
typeof(UIElement),
typeof(BorderHelper),
new PropertyMetadata(default(UIElement), OnChildChanged));
public static UIElement GetChild(Border border)
{
return (UIElement)border.GetValue(ChildProperty);
}
public static void SetChild(Border border, UIElement value)
{
border.SetValue(ChildProperty, value);
}
private static void OnChildChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Border)d).Child = (UIElement)e.NewValue;
}
#endregion
}

View file

@ -61,8 +61,8 @@ internal static class HotKeyMapper
string.Format("|HotkeyMapper.SetWithChefKeys|Error registering hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr);
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
string errorMsg = Localize.registerHotkeyFailed(hotkeyStr);
string errorMsgTitle = Localize.MessageBoxTitle();
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}
@ -87,8 +87,8 @@ internal static class HotKeyMapper
e.Message,
e.StackTrace,
hotkeyStr));
string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr);
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
string errorMsg = Localize.registerHotkeyFailed(hotkeyStr);
string errorMsgTitle = Localize.MessageBoxTitle();
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}
@ -112,8 +112,8 @@ internal static class HotKeyMapper
string.Format("|HotkeyMapper.RemoveHotkey|Error removing hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
string errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr);
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
string errorMsg = Localize.unregisterHotkeyFailed(hotkeyStr);
string errorMsgTitle = Localize.MessageBoxTitle();
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
@ -16,7 +17,7 @@ public static class WallpaperPathRetrieval
private const int MaxCacheSize = 3;
private static readonly Dictionary<(string, DateTime), ImageBrush> WallpaperCache = new();
private static readonly object CacheLock = new();
private static readonly Lock CacheLock = new();
public static Brush GetWallpaperBrush()
{
@ -31,7 +32,7 @@ public static class WallpaperPathRetrieval
var wallpaperPath = Win32Helper.GetWallpaperPath();
if (string.IsNullOrEmpty(wallpaperPath) || !File.Exists(wallpaperPath))
{
App.API.LogInfo(ClassName, $"Wallpaper path is invalid: {wallpaperPath}");
App.API.LogError(ClassName, $"Wallpaper path is invalid: {wallpaperPath}");
var wallpaperColor = GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
@ -47,17 +48,22 @@ public static class WallpaperPathRetrieval
return cachedWallpaper;
}
}
using var fileStream = File.OpenRead(wallpaperPath);
var decoder = BitmapDecoder.Create(fileStream, BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);
var frame = decoder.Frames[0];
var originalWidth = frame.PixelWidth;
var originalHeight = frame.PixelHeight;
int originalWidth, originalHeight;
// Use `using ()` instead of `using var` sentence here to ensure the wallpaper file is not locked
using (var fileStream = File.OpenRead(wallpaperPath))
{
var decoder = BitmapDecoder.Create(fileStream, BitmapCreateOptions.DelayCreation, BitmapCacheOption.None);
var frame = decoder.Frames[0];
originalWidth = frame.PixelWidth;
originalHeight = frame.PixelHeight;
}
if (originalWidth == 0 || originalHeight == 0)
{
App.API.LogInfo(ClassName, $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
return new SolidColorBrush(Colors.Transparent);
App.API.LogError(ClassName, $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
var wallpaperColor = GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
// Calculate the scaling factor to fit the image within 800x600 while preserving aspect ratio
@ -70,7 +76,9 @@ public static class WallpaperPathRetrieval
// Set DecodePixelWidth and DecodePixelHeight to resize the image while preserving aspect ratio
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad; // Use OnLoad to ensure the wallpaper file is not locked
bitmap.UriSource = new Uri(wallpaperPath);
bitmap.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bitmap.DecodePixelWidth = decodedPixelWidth;
bitmap.DecodePixelHeight = decodedPixelHeight;
bitmap.EndInit();
@ -104,13 +112,13 @@ public static class WallpaperPathRetrieval
private static Color GetWallpaperColor()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", false);
using var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", false);
var result = key?.GetValue("Background", null);
if (result is string strResult)
{
try
{
var parts = strResult.Trim().Split(new[] { ' ' }, 3).Select(byte.Parse).ToList();
var parts = strResult.Trim().Split([' '], 3).Select(byte.Parse).ToList();
return Color.FromRgb(parts[0], parts[1], parts[2]);
}
catch (Exception ex)

View file

@ -1,4 +1,4 @@
using System.Collections.ObjectModel;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
@ -234,7 +234,7 @@ namespace Flow.Launcher
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>
hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
public string EmptyHotkey => App.API.GetTranslation("none");
public string EmptyHotkey => Localize.none();
public ObservableCollection<string> KeysToDisplay { get; set; } = new();

View file

@ -2,7 +2,7 @@
x:Class="Flow.Launcher.HotkeyControlDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
Background="{DynamicResource PopuBGColor}"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="0 1 0 0"

View file

@ -9,7 +9,7 @@ using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using ModernWpf.Controls;
using iNKORE.UI.WPF.Modern.Controls;
namespace Flow.Launcher;
@ -33,7 +33,7 @@ public partial class HotkeyControlDialog : ContentDialog
public EResultType ResultType { get; private set; } = EResultType.Cancel;
public string ResultValue { get; private set; } = string.Empty;
public static string EmptyHotkey => App.API.GetTranslation("none");
public static string EmptyHotkey => Localize.none();
private static bool isOpenFlowHotkey;
@ -41,7 +41,7 @@ public partial class HotkeyControlDialog : ContentDialog
{
WindowTitle = windowTitle switch
{
"" or null => App.API.GetTranslation("hotkeyRegTitle"),
"" or null => Localize.hotkeyRegTitle(),
_ => windowTitle
};
DefaultHotkey = defaultHotkey;
@ -146,10 +146,7 @@ public partial class HotkeyControlDialog : ContentDialog
Alert.Visibility = Visibility.Visible;
if (registeredHotkeyData.RemoveHotkey is not null)
{
tbMsg.Text = string.Format(
App.API.GetTranslation("hotkeyUnavailableEditable"),
description
);
tbMsg.Text = Localize.hotkeyUnavailableEditable(description);
SaveBtn.IsEnabled = false;
SaveBtn.Visibility = Visibility.Collapsed;
OverwriteBtn.IsEnabled = true;
@ -158,10 +155,7 @@ public partial class HotkeyControlDialog : ContentDialog
}
else
{
tbMsg.Text = string.Format(
App.API.GetTranslation("hotkeyUnavailableUneditable"),
description
);
tbMsg.Text = Localize.hotkeyUnavailableUneditable(description);
SaveBtn.IsEnabled = false;
SaveBtn.Visibility = Visibility.Visible;
OverwriteBtn.IsEnabled = false;
@ -175,7 +169,7 @@ public partial class HotkeyControlDialog : ContentDialog
if (!CheckHotkeyAvailability(hotkey.Value, true))
{
tbMsg.Text = App.API.GetTranslation("hotkeyUnavailable");
tbMsg.Text = Localize.hotkeyUnavailable();
Alert.Visibility = Visibility.Visible;
SaveBtn.IsEnabled = false;
SaveBtn.Visibility = Visibility.Visible;

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">متجر الإضافات</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">فتح المجلد</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">خطأ</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">حجة للملف</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">متصفح الويب الافتراضي</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">نافذة جديدة</system:String>
<system:String x:Key="defaultBrowser_newTab">تبويب جديد</system:String>
<system:String x:Key="defaultBrowser_parameter">الوضع الخاص</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">تغيير الأولوية</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Obchod s pluginy</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Chyba</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Argumenty pro Soubor</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Výchozí prohlížeč</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nové okno</system:String>
<system:String x:Key="defaultBrowser_newTab">Nová karta</system:String>
<system:String x:Key="defaultBrowser_parameter">Soukromý režim</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Změnit prioritu</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Plugin-butik</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg for fil</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
<system:String x:Key="defaultBrowser_parameter">Privattilstand</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Skift prioritet</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Plug-in-Store</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Ordner öffnen</system:String>
<system:String x:Key="advanced">Erweitert</system:String>
<system:String x:Key="logLevel">Log-Ebene</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Fehler</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Einstellung der Fensterschriftart</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
<system:String x:Key="fileManagerPathNotFound">Der Dateimanager '{0}' konnte nicht unter '{1}' gefunden werden. Möchten Sie fortfahren?</system:String>
<system:String x:Key="fileManagerPathError">Pfadfehler bei Dateimanager</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Webbrowser per Default</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Neues Fenster</system:String>
<system:String x:Key="defaultBrowser_newTab">Neuer Tab</system:String>
<system:String x:Key="defaultBrowser_parameter">Privater Modus</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Priorität ändern</system:String>

View file

@ -214,6 +214,8 @@
<system:String x:Key="plugin_query_version">Version</system:String>
<system:String x:Key="plugin_query_web">Website</system:String>
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
<system:String x:Key="plugin_default_search_delay_time">Search delay time: default</system:String>
<system:String x:Key="plugin_search_delay_time">Search delay time: {0}ms</system:String>
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
@ -224,6 +226,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Plugin Store</system:String>
@ -492,6 +495,7 @@
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
@ -502,6 +506,8 @@
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
<system:String x:Key="defaultBrowser_parameter">Private Mode</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Change Priority</system:String>
@ -589,8 +595,9 @@
The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General.
</system:String>
<system:String x:Key="errorTitle">Error</system:String>
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
<system:String x:Key="folderOpenError">An error occurred while opening the folder.</system:String>
<system:String x:Key="browserOpenError">An error occurred while opening the URL in the browser. Please check your Default Web Browser configuration in the General section of the settings window</system:String>
<system:String x:Key="fileNotFoundError">File or directory not found: {0}</system:String>
<!-- General Notice -->
<system:String x:Key="pleaseWait">Please wait...</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Tienda de Plugins</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg para Archivo</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Navegador Web Predeterminado</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nueva Ventana</system:String>
<system:String x:Key="defaultBrowser_newTab">Nueva Pestaña</system:String>
<system:String x:Key="defaultBrowser_parameter">Modo Privado</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Cambiar Prioridad</system:String>

View file

@ -24,8 +24,8 @@
<system:String x:Key="userDataDuplicated">Flow Launcher ha detectado que los datos de usario existen tanto en {0} como en {1}. {2}{2}Por favor, elimine {1} para continuar. No se han producido cambios.</system:String>
<!-- Plugin Loader -->
<system:String x:Key="pluginHasErrored">El siguiente complemento ha sufrido un error y no puede cargarse:</system:String>
<system:String x:Key="pluginsHaveErrored">Los siguientes complementos han sufrido un error y no pueden cargarse:</system:String>
<system:String x:Key="pluginHasErrored">El siguiente complemento ha sufrido un fallo y no se puede cargar:</system:String>
<system:String x:Key="pluginsHaveErrored">Los siguientes complementos han sufrido un fallo y no se pueden cargar:</system:String>
<system:String x:Key="referToLogs">Por favor, consulte los registros para más información</system:String>
<!-- Http -->
@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fallo al desinstalar {0}</system:String>
<system:String x:Key="fileNotFoundMessage">No se puede encontrar plugin.json en el archivo zip extraído, o esta ruta {0} no existe</system:String>
<system:String x:Key="pluginExistAlreadyMessage">Ya existe un complemento con el mismo ID y versión, o la versión es superior a la de este complemento descargado</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Tienda complementos</system:String>
@ -332,7 +333,7 @@
<system:String x:Key="PlaceholderTextTip">Cambia el texto del marcador de posición. La entrada vacía utilizará: {0}</system:String>
<system:String x:Key="KeepMaxResults">Tamaño fijo de la ventana</system:String>
<system:String x:Key="KeepMaxResultsToolTip">El tamaño de la ventana no se puede ajustar mediante arrastre.</system:String>
<system:String x:Key="MaxShowResultsCannotWorkWithAlwaysPreview">Since Always Preview is on, maximum results shown may not take effect because preview panel requires a certain minimum height</system:String>
<system:String x:Key="MaxShowResultsCannotWorkWithAlwaysPreview">Dado que la vista previa está siempre activada, es posible que no se muestren los resultados máximos, ya que el panel de vista previa requiere una altura mínima determinada</system:String>
<!-- Setting Hotkey -->
<system:String x:Key="hotkey">Atajo de teclado</system:String>
@ -395,7 +396,7 @@
<system:String x:Key="showBadges">Mostrar distintivos en resultados</system:String>
<system:String x:Key="showBadgesToolTip">Para los complementos compatibles, se muestran distintivos que ayudan a distinguirlos más fácilmente.</system:String>
<system:String x:Key="showBadgesGlobalOnly">Mostrar distintivos en resultados solo para consulta global</system:String>
<system:String x:Key="showBadgesGlobalOnlyToolTip">Mostrar distintivos solo para los resultados de consultas globales</system:String>
<system:String x:Key="showBadgesGlobalOnlyToolTip">Muestra distintivos solo para los resultados de consultas globales</system:String>
<system:String x:Key="dialogJumpHotkey">Salto de diálogo</system:String>
<system:String x:Key="dialogJumpHotkeyToolTip">Introducir atajo de teclado para acceder rápidamente a la ventana de diálogo Abrir/Guardar como en la ruta del administrador de archivos actual.</system:String>
<system:String x:Key="dialogJump">Salto de diálogo</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Abrir carpeta</system:String>
<system:String x:Key="advanced">Avanzado</system:String>
<system:String x:Key="logLevel">Nivel de registro</system:String>
<system:String x:Key="LogLevelDEBUG">Depuración</system:String>
<system:String x:Key="LogLevelNONE">Silencioso</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Información</system:String>
<system:String x:Key="LogLevelDEBUG">Depuración</system:String>
<system:String x:Key="settingWindowFontTitle">Configuración de fuente de la ventana</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Argumentos del archivo</system:String>
<system:String x:Key="fileManagerPathNotFound">El administrador de archivos '{0}' no pudo ser localizado en '{1}'. ¿Desea continuar?</system:String>
<system:String x:Key="fileManagerPathError">Error de ruta del administrador de archivos</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Navegador web predeterminado</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nueva ventana</system:String>
<system:String x:Key="defaultBrowser_newTab">Nueva pestaña</system:String>
<system:String x:Key="defaultBrowser_parameter">Modo privado</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Cambiar la prioridad</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Échec de la désinstallation de {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Impossible de trouver le fichier plugin.json dans le fichier zip extrait, ou ce chemin {0} n'existe pas</system:String>
<system:String x:Key="pluginExistAlreadyMessage">Un plugin avec le même ID et la même version existe déjà, ou la version est supérieure à ce plugin téléchargé</system:String>
<system:String x:Key="errorCreatingSettingPanel">Erreur lors de la création du panneau de configuration pour le plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Magasin des Plugins</system:String>
@ -466,8 +467,10 @@
<system:String x:Key="userdatapathButton">Ouvrir le dossier</system:String>
<system:String x:Key="advanced">Avancé</system:String>
<system:String x:Key="logLevel">Niveau de journalisation</system:String>
<system:String x:Key="LogLevelDEBUG">Débogage</system:String>
<system:String x:Key="LogLevelNONE">Silencieux</system:String>
<system:String x:Key="LogLevelERROR">Erreur</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Débogage</system:String>
<system:String x:Key="settingWindowFontTitle">Réglage de la police de la fenêtre</system:String>
<!-- Release Notes Window -->
@ -489,6 +492,7 @@
<system:String x:Key="fileManager_file_arg">Arguments pour le fichier</system:String>
<system:String x:Key="fileManagerPathNotFound">Le gestionnaire de fichiers '{0}' n'a pas pu être situé à '{1}'. Souhaitez-vous continuer ?</system:String>
<system:String x:Key="fileManagerPathError">Erreur de chemin du gestionnaire de fichiers</system:String>
<system:String x:Key="fileManagerExplorer">Explorateur de fichiers</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Navigateur web par défaut</system:String>
@ -499,6 +503,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nouvelle fenêtre</system:String>
<system:String x:Key="defaultBrowser_newTab">Nouvel onglet</system:String>
<system:String x:Key="defaultBrowser_parameter">Mode privé</system:String>
<system:String x:Key="defaultBrowser_default">Par défaut</system:String>
<system:String x:Key="defaultBrowser_new_profile">Nouveau profil</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Changer la priorité</system:String>

View file

@ -223,6 +223,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">חנות תוספים</system:String>
@ -466,8 +467,10 @@
<system:String x:Key="userdatapathButton">פתח תיקיה</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">רמת יומן</system:String>
<system:String x:Key="LogLevelDEBUG">ניפוי שגיאות</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">שגיאה</system:String>
<system:String x:Key="LogLevelINFO">מידע</system:String>
<system:String x:Key="LogLevelDEBUG">ניפוי שגיאות</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -489,6 +492,7 @@
<system:String x:Key="fileManager_file_arg">ארגומנט לקובץ</system:String>
<system:String x:Key="fileManagerPathNotFound">לא ניתן היה לאתר את מנהל הקבצים '{0}' ב-'{1}'. האם ברצונך להמשיך?</system:String>
<system:String x:Key="fileManagerPathError">שגיאת נתיב למנהל הקבצים</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">דפדפן ברירת מחדל</system:String>
@ -499,6 +503,8 @@
<system:String x:Key="defaultBrowser_newWindow">חלון חדש</system:String>
<system:String x:Key="defaultBrowser_newTab">כרטיסייה חדשה</system:String>
<system:String x:Key="defaultBrowser_parameter">מצב פרטיות</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">שנה עדיפות</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Negozio dei Plugin</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Apri Cartella</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg Per Cartella</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Browser predefinito</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nuova Finestra</system:String>
<system:String x:Key="defaultBrowser_newTab">Nuova Scheda</system:String>
<system:String x:Key="defaultBrowser_parameter">Modalità Privata</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Cambia Priorità</system:String>

View file

@ -2,43 +2,43 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib">
<!-- Startup -->
<system:String x:Key="runtimePluginInstalledChooseRuntimePrompt">
Flow detected you have installed {0} plugins, which will require {1} to run. Would you like to download {1}?
Flow はあなたが {0} プラグインをインストールしており、実行するために {1} が必要であることを検知しました。{1} をインストールしますか?
{2}{2}
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
{1}がすでにインストールされている場合は「いいえ」をクリックし、それが入っているフォルダーを選択してください
</system:String>
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Please select the {0} executable</system:String>
<system:String x:Key="runtimePluginChooseRuntimeExecutable">{0} の実行ファイルを選択してください</system:String>
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
Your selected {0} executable is invalid.
あなたが選択した {0} の実行ファイルが不正です。
{2}{2}
Click yes if you would like select the {0} executable again. Click no if you would like to download {1}
{0} の実行ファイルをもう一度選択する場合は「はい」を、{1} をダウンロードする場合は「いいえ」を選択してください
</system:String>
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).</system:String>
<system:String x:Key="failedToInitializePluginsTitle">Fail to Init Plugins</system:String>
<system:String x:Key="failedToInitializePluginsMessage">Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help</system:String>
<system:String x:Key="runtimePluginUnableToSetExecutablePath">{0} の実行可能ファイルのパスを設定できません。Flow の設定から試してください(下までスクロールしてください)。</system:String>
<system:String x:Key="failedToInitializePluginsTitle">プラグインの起動失敗</system:String>
<system:String x:Key="failedToInitializePluginsMessage">プラグイン: {0} の読み込みに失敗したため、無効になりました。プラグインの作成者にお問い合わせください</system:String>
<!-- Portable -->
<system:String x:Key="restartToDisablePortableMode">Flow Launcherはポータブルモードの無効化のために再起動する必要があります。再起動の後、ポータブルな形式の設定項目は削除され、あなたのパソコンのフォルダに保存されます</system:String>
<system:String x:Key="restartToEnablePortableMode">Flow Launcherはポータブルモードの有効化のために再起動する必要があります。再起動の後、パソコンに保存された設定項目は削除され、ポータブルな形式で保存されます</system:String>
<system:String x:Key="moveToDifferentLocation">Flow Launcherはポータブルモードの有効化を検知しました。Flow Launcherを別の場所に移動しますか</system:String>
<system:String x:Key="shortcutsUninstallerCreated">Flow Launcherはポータブルモードの無効化を検知しました。関連するショートカットやアンインストーラーが配置されます</system:String>
<system:String x:Key="userDataDuplicated">Flow Launcher detected your user data exists both in {0} and {1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.</system:String>
<system:String x:Key="userDataDuplicated">Flow Launcherはあなたのユーザーデータが{0} と {1} の両方に存在することを検知しました。{2}{2}続行するには、{1}を削除してください。処理は中断されました。</system:String>
<!-- Plugin Loader -->
<system:String x:Key="pluginHasErrored">The following plugin has errored and cannot be loaded:</system:String>
<system:String x:Key="pluginsHaveErrored">The following plugins have errored and cannot be loaded:</system:String>
<system:String x:Key="referToLogs">Please refer to the logs for more information</system:String>
<system:String x:Key="pluginHasErrored">以下のプラグインにエラーがあるためロードできません:</system:String>
<system:String x:Key="pluginsHaveErrored">以下のプラグインにエラーがあるためロードできません:</system:String>
<system:String x:Key="referToLogs">詳細はログを参照してください</system:String>
<!-- Http -->
<system:String x:Key="pleaseTryAgain">Please try again</system:String>
<system:String x:Key="parseProxyFailed">Unable to parse Http Proxy</system:String>
<system:String x:Key="pleaseTryAgain">もう一度お試しください</system:String>
<system:String x:Key="parseProxyFailed">Http プロキシをパースできません</system:String>
<!-- AbstractPluginEnvironment -->
<system:String x:Key="failToInstallTypeScriptEnv">Failed to install TypeScript environment. Please try again later</system:String>
<system:String x:Key="failToInstallPythonEnv">Failed to install Python environment. Please try again later.</system:String>
<system:String x:Key="failToInstallTypeScriptEnv">TypeScript環境のインストールに失敗しました。後でもう一度お試しください</system:String>
<system:String x:Key="failToInstallPythonEnv">Python 環境のインストールに失敗しました。後でもう一度お試しください。</system:String>
<!-- MainWindow -->
<system:String x:Key="registerHotkeyFailed">ホットキー &quot;{0}&quot; の登録に失敗しました。このホットキーは別のプログラムで使用されている可能性があります。別のホットキーに変更するか、このホットキーを使用しているプログラムを終了してください。</system:String>
<system:String x:Key="unregisterHotkeyFailed">Failed to unregister hotkey &quot;{0}&quot;. Please try again or see log for details</system:String>
<system:String x:Key="unregisterHotkeyFailed">ホットキー「{0}」の登録解除に失敗しました。もう一度試すか、ログを参照して詳細を確認してください</system:String>
<system:String x:Key="MessageBoxTitle">Flow Launcher</system:String>
<system:String x:Key="couldnotStartCmd">{0}の起動に失敗しました</system:String>
<system:String x:Key="invalidFlowLauncherPluginFileFormat">Flow Launcherプラグインの形式が正しくありません</system:String>
@ -58,7 +58,7 @@
<system:String x:Key="selectAll">全て選択</system:String>
<system:String x:Key="fileTitle">ファイル</system:String>
<system:String x:Key="folderTitle">フォルダー</system:String>
<system:String x:Key="textTitle">Text</system:String>
<system:String x:Key="textTitle">テキスト</system:String>
<system:String x:Key="GameMode">ゲームモード</system:String>
<system:String x:Key="GameModeToolTip">ホットキーの使用を一時停止します。</system:String>
<system:String x:Key="PositionReset">位置のリセット</system:String>
@ -73,7 +73,7 @@
<system:String x:Key="startFlowLauncherOnSystemStartup">スタートアップ時にFlow Launcherを起動する</system:String>
<system:String x:Key="useLogonTaskForStartup">起動の高速化のためにスタートアップではなくログオンタスクを使用</system:String>
<system:String x:Key="useLogonTaskForStartupTooltip">アンインストール後は、「タスク スケジューラ」からこのタスクFlow.Launcher Startupを手動で削除する必要があります。</system:String>
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
<system:String x:Key="setAutoStartFailed">スタートアップ時に起動の設定失敗</system:String>
<system:String x:Key="hideFlowLauncherWhenLoseFocus">フォーカスを失った時にFlow Launcherを隠す</system:String>
<system:String x:Key="dontPromptUpdateMsg">最新版が入手可能であっても、アップグレードメッセージを表示しない</system:String>
<system:String x:Key="SearchWindowPosition">検索ウィンドウの位置</system:String>
@ -111,7 +111,7 @@
<system:String x:Key="typingStartEn">常に英語モードで入力を開始する</system:String>
<system:String x:Key="typingStartEnTooltip">Flowを起動したとき、一時的に入力方法を英語モードに変更します。</system:String>
<system:String x:Key="autoUpdates">自動更新</system:String>
<system:String x:Key="autoUpdatesTooltip">Automatically check and update the app when available</system:String>
<system:String x:Key="autoUpdatesTooltip">利用可能な場合、Flow Launcherを自動的に確認して更新します</system:String>
<system:String x:Key="select">選択</system:String>
<system:String x:Key="hideOnStartup">起動時にFlow Launcherを隠す</system:String>
<system:String x:Key="hideOnStartupToolTip">起動後、Flow Launcher の検索ウィンドウは非表示になり、トレイに格納されます。</system:String>
@ -123,10 +123,10 @@
<system:String x:Key="SearchPrecisionLow">低</system:String>
<system:String x:Key="SearchPrecisionRegular">標準</system:String>
<system:String x:Key="ShouldUsePinyin">ピンインによる検索</system:String>
<system:String x:Key="ShouldUsePinyinToolTip">Pinyin is the standard system of romanized spelling for translating Chinese. Please note, enabling this can significantly increase memory usage during search.</system:String>
<system:String x:Key="ShouldUseDoublePinyin">Use Double Pinyin</system:String>
<system:String x:Key="ShouldUseDoublePinyinToolTip">Use Double Pinyin instead of Full Pinyin to search.</system:String>
<system:String x:Key="DoublePinyinSchema">Double Pinyin Schema</system:String>
<system:String x:Key="ShouldUsePinyinToolTip">Pinyinは中国語を翻訳するためのローマ字入力の標準的な方法です。有効にすると、検索時のメモリ使用量が大幅に増加する可能性があります。</system:String>
<system:String x:Key="ShouldUseDoublePinyin">双拼入力を使用</system:String>
<system:String x:Key="ShouldUseDoublePinyinToolTip">検索するときに全拼の代わりに双拼を使用する。</system:String>
<system:String x:Key="DoublePinyinSchema">双拼の入力方式</system:String>
<system:String x:Key="DoublePinyinSchemasXiaoHe">Xiao He</system:String>
<system:String x:Key="DoublePinyinSchemasZiRanMa">Zi Ran Ma</system:String>
<system:String x:Key="DoublePinyinSchemasWeiRuan">Wei Ruan</system:String>
@ -142,10 +142,10 @@
<system:String x:Key="shadowEffectNotAllowed">現在のテーマでぼかしの効果が有効になっている場合、影の効果を有効にすることはできません</system:String>
<system:String x:Key="searchDelay">検索遅延</system:String>
<system:String x:Key="searchDelayToolTip">入力中に短い遅延を追加することで、UIのちらつきや結果の読み込みを軽減します。平均的なタイピング速度のユーザーにおすすめです。</system:String>
<system:String x:Key="searchDelayNumberBoxToolTip">Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled.</system:String>
<system:String x:Key="searchDelayNumberBoxToolTip">入力中の結果表示までの待ち時間をミリ秒単位で入力します。これは、検索遅延が有効な場合にのみ編集できます。</system:String>
<system:String x:Key="searchDelayTime">デフォルトの検索遅延時間</system:String>
<system:String x:Key="searchDelayTimeToolTip">入力が停止した後に結果が表示されるまでの待ち時間。値が大きいほど長く待機します。(単位 ms)</system:String>
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
<system:String x:Key="KoreanImeTitle">韓国語IMEユーザーへの情報</system:String>
<system:String x:Key="KoreanImeGuide">
The Korean input method used in Windows 11 may cause some issues in Flow Launcher.
@ -160,29 +160,29 @@
</system:String>
<system:String x:Key="KoreanImeOpenLink">Open Language and Region System Settings</system:String>
<system:String x:Key="KoreanImeOpenLink">システムの言語と地域設定を開く</system:String>
<system:String x:Key="KoreanImeOpenLinkToolTip">Opens the Korean IME setting location. Go to Korean &gt; Language Options &gt; Keyboard - Microsoft IME &gt; Compatibility</system:String>
<system:String x:Key="KoreanImeOpenLinkButton">開く</system:String>
<system:String x:Key="KoreanImeRegistry">Use Previous Korean IME</system:String>
<system:String x:Key="KoreanImeRegistry">前の韓国語IMEを使用</system:String>
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
<system:String x:Key="KoreanImeSettingChangeFailTitle">Failed to change Korean IME setting</system:String>
<system:String x:Key="KoreanImeSettingChangeFailSubTitle">Please check your system registry access or contact support.</system:String>
<system:String x:Key="KoreanImeSettingChangeFailSubTitle">システムのレジストリへのアクセスが可能か確認するか、サポートにお問い合わせください。</system:String>
<system:String x:Key="homePage">ホームページ</system:String>
<system:String x:Key="homePageToolTip">検索文字列が空の場合、ホームページの結果を表示します。</system:String>
<system:String x:Key="historyResultsForHomePage">クエリの履歴をホームページに表示</system:String>
<system:String x:Key="historyResultsCountForHomePage">ホームページに表示される最大の履歴の数</system:String>
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
<system:String x:Key="showAtTopmost">Show Search Window at Foremost</system:String>
<system:String x:Key="homeToggleBoxToolTip">これは、プラグインがホーム機能をサポートし、ホームページが有効な場合にのみ編集することができます。</system:String>
<system:String x:Key="showAtTopmost">検索ウィンドウを最前面に表示</system:String>
<system:String x:Key="showAtTopmostToolTip">他のプログラムの 'Always on Top' (最前面に表示)設定を上書きし、常に最前面のウィンドウで Flow を表示します。</system:String>
<system:String x:Key="autoRestartAfterChanging">プラグインストアでプラグインを変更した後に再起動します</system:String>
<system:String x:Key="autoRestartAfterChanging">プラグインストアでプラグインを変更した後に再起動</system:String>
<system:String x:Key="autoRestartAfterChangingToolTip">プラグインストア経由でプラグインをインストール、アンインストール、または更新した後、Flow Lancherを自動的に再起動します</system:String>
<system:String x:Key="showUnknownSourceWarning">不明なソースの警告を表示</system:String>
<system:String x:Key="showUnknownSourceWarningToolTip">不明なソースからプラグインをインストールするときに警告を表示する</system:String>
<system:String x:Key="autoUpdatePlugins">Auto update plugins</system:String>
<system:String x:Key="autoUpdatePluginsToolTip">Automatically check plugin updates and notify if there are any updates available</system:String>
<system:String x:Key="autoUpdatePlugins">プラグインの自動アップデート</system:String>
<system:String x:Key="autoUpdatePluginsToolTip">プラグインの更新を自動的にチェックし、利用可能な更新がある場合に通知します</system:String>
<!-- Setting Plugin -->
<system:String x:Key="searchplugin">Search Plugin</system:String>
<system:String x:Key="searchplugin">プラグインの検索</system:String>
<system:String x:Key="searchpluginToolTip">Ctrl+F でプラグインを検索します</system:String>
<system:String x:Key="searchplugin_Noresult_Title">検索結果が見つかりませんでした</system:String>
<system:String x:Key="searchplugin_Noresult_Subtitle">別の検索を試してみてください。</system:String>
@ -191,20 +191,20 @@
<system:String x:Key="browserMorePlugins">プラグインを探す</system:String>
<system:String x:Key="enable">有効</system:String>
<system:String x:Key="disable">無効</system:String>
<system:String x:Key="actionKeywordsTitle">Action keyword Setting</system:String>
<system:String x:Key="actionKeywordsTitle">アクションキーワードの設定</system:String>
<system:String x:Key="actionKeywords">キーワード</system:String>
<system:String x:Key="currentActionKeywords">Current action keyword</system:String>
<system:String x:Key="newActionKeyword">New action keyword</system:String>
<system:String x:Key="actionKeywordsTooltip">Change Action Keywords</system:String>
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
<system:String x:Key="currentActionKeywords">現在のアクションキーワード</system:String>
<system:String x:Key="newActionKeyword">新しいアクションキーワード</system:String>
<system:String x:Key="actionKeywordsTooltip">アクションキーワードの変更</system:String>
<system:String x:Key="pluginSearchDelayTime">プラグインの検索遅延時間</system:String>
<system:String x:Key="pluginSearchDelayTimeTooltip">プラグインの検索遅延時間を変更</system:String>
<system:String x:Key="FilterComboboxLabel">詳細設定:</system:String>
<system:String x:Key="DisplayModeOnOff">有効</system:String>
<system:String x:Key="DisplayModePriority">重要度</system:String>
<system:String x:Key="DisplayModeSearchDelay">検索遅延</system:String>
<system:String x:Key="DisplayModeHomeOnOff">ホームページ</system:String>
<system:String x:Key="currentPriority">Current Priority</system:String>
<system:String x:Key="newPriority">New Priority</system:String>
<system:String x:Key="currentPriority">現在の優先度</system:String>
<system:String x:Key="newPriority">新しい優先度</system:String>
<system:String x:Key="priority">重要度</system:String>
<system:String x:Key="priorityToolTip">プラグインの結果の優先度を変更します。</system:String>
<system:String x:Key="pluginDirectory">プラグイン・ディレクトリ</system:String>
@ -214,59 +214,60 @@
<system:String x:Key="plugin_query_version">バージョン</system:String>
<system:String x:Key="plugin_query_web">ウェブサイト</system:String>
<system:String x:Key="plugin_uninstall">アンインストール</system:String>
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
<system:String x:Key="pluginModifiedAlreadyTitle">{0} modified already</system:String>
<system:String x:Key="pluginModifiedAlreadyMessage">Please restart Flow before making any further changes</system:String>
<system:String x:Key="failedToInstallPluginTitle">Fail to install {0}</system:String>
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="failedToRemovePluginSettingsTitle">プラグイン設定の削除に失敗</system:String>
<system:String x:Key="failedToRemovePluginSettingsMessage">プラグイン: {0} - プラグイン設定ファイルの削除に失敗しました。手動で削除してください</system:String>
<system:String x:Key="failedToRemovePluginCacheTitle">プラグインキャッシュの削除に失敗</system:String>
<system:String x:Key="failedToRemovePluginCacheMessage">プラグイン: {0} - プラグインキャッシュファイルの削除に失敗しました。手動で削除してください</system:String>
<system:String x:Key="pluginModifiedAlreadyTitle">{0} は既に変更されています</system:String>
<system:String x:Key="pluginModifiedAlreadyMessage">これ以上変更を加える前に Flow Launcher を再起動してください</system:String>
<system:String x:Key="failedToInstallPluginTitle">{0} のインストールに失敗</system:String>
<system:String x:Key="failedToUninstallPluginTitle">{0} のアンインストールに失敗</system:String>
<system:String x:Key="fileNotFoundMessage">展開されたzipファイルからplugin.jsonが見つからないか、このパス {0} が存在しません</system:String>
<system:String x:Key="pluginExistAlreadyMessage">同じIDとバージョンのプラグインがすでに存在するか、またはこのダウンロードしたプラグインよりもバージョンが大きいです</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">プラグインストア</system:String>
<system:String x:Key="pluginStore_NewRelease">新規リリース</system:String>
<system:String x:Key="pluginStore_RecentlyUpdated">最近の更新</system:String>
<system:String x:Key="pluginStore_None">プラグイン</system:String>
<system:String x:Key="pluginStore_Installed">Installed</system:String>
<system:String x:Key="pluginStore_Installed">インストール済み</system:String>
<system:String x:Key="refresh">更新</system:String>
<system:String x:Key="installbtn">インストール</system:String>
<system:String x:Key="uninstallbtn">アンインストール</system:String>
<system:String x:Key="updatebtn">更新</system:String>
<system:String x:Key="LabelInstalledToolTip">Plugin already installed</system:String>
<system:String x:Key="LabelNew">New Version</system:String>
<system:String x:Key="LabelNewToolTip">This plugin has been updated within the last 7 days</system:String>
<system:String x:Key="LabelInstalledToolTip">プラグインは既にインストールされています</system:String>
<system:String x:Key="LabelNew">新しいバージョン</system:String>
<system:String x:Key="LabelNewToolTip">このプラグインは過去1週間以内に更新されました</system:String>
<system:String x:Key="LabelUpdateToolTip">新しいアップデートが利用可能です</system:String>
<system:String x:Key="ErrorInstallingPlugin">プラグインのインストール失敗</system:String>
<system:String x:Key="ErrorUninstallingPlugin">プラグインのアンインストール失敗</system:String>
<system:String x:Key="ErrorUpdatingPlugin">Error updating plugin</system:String>
<system:String x:Key="ErrorUpdatingPlugin">プラグインの更新に失敗</system:String>
<system:String x:Key="KeepPluginSettingsTitle">プラグインの設定を維持</system:String>
<system:String x:Key="KeepPluginSettingsSubtitle">再びインストールして使用するときのためにプラグインの設定を維持しますか?</system:String>
<system:String x:Key="InstallSuccessNoRestart">Plugin {0} successfully installed. Please restart Flow.</system:String>
<system:String x:Key="UninstallSuccessNoRestart">Plugin {0} successfully uninstalled. Please restart Flow.</system:String>
<system:String x:Key="UpdateSuccessNoRestart">Plugin {0} successfully updated. Please restart Flow.</system:String>
<system:String x:Key="InstallSuccessNoRestart">プラグイン {0} のインストールに成功しました。Flow を再起動してください。</system:String>
<system:String x:Key="UninstallSuccessNoRestart">プラグイン {0} のアンインストールに成功しました。Flow を再起動してください。</system:String>
<system:String x:Key="UpdateSuccessNoRestart">プラグイン {0} が正常に更新されました。Flow を再起動してください。</system:String>
<system:String x:Key="InstallPromptTitle">プラグインのインストール</system:String>
<system:String x:Key="InstallPromptSubtitle">{0} by {1} {2}{2}このプラグインをインストールしますか?</system:String>
<system:String x:Key="UninstallPromptTitle">プラグインのアンインストール</system:String>
<system:String x:Key="UninstallPromptSubtitle">{0} by {1} {2}{2}このプラグインをアンインストールしますか?</system:String>
<system:String x:Key="UpdatePromptTitle">Plugin update</system:String>
<system:String x:Key="UpdatePromptSubtitle">{0} by {1} {2}{2}Would you like to update this plugin?</system:String>
<system:String x:Key="DownloadingPlugin">Downloading plugin</system:String>
<system:String x:Key="AutoRestartAfterChange">Automatically restart after installing/uninstalling/updating plugins in plugin store</system:String>
<system:String x:Key="ZipFileNotHavePluginJson">Zip file does not have a valid plugin.json configuration</system:String>
<system:String x:Key="UpdatePromptTitle">プラグインの更新</system:String>
<system:String x:Key="UpdatePromptSubtitle">{0} by {1} {2}{2}このプラグインを更新しますか?</system:String>
<system:String x:Key="DownloadingPlugin">プラグインをダウンロード中</system:String>
<system:String x:Key="AutoRestartAfterChange">プラグインストア経由でのプラグインのインストール 、アンインストール、または更新後に自動的に再起動します</system:String>
<system:String x:Key="ZipFileNotHavePluginJson">Zipファイルに有効なplugin.jsonファイルがありません</system:String>
<system:String x:Key="InstallFromUnknownSourceTitle">不明なソースからのインストール</system:String>
<system:String x:Key="InstallFromUnknownSourceSubtitle">このプラグインは不明なソースから提供されており、潜在的なリスクを含んでいる可能性があります!{0}{0}このプラグインの開発元をよく調べ、安全であることをご自身で確かめてください。{0}{0}それでもあなたはこのプラグインをインストールしますか?{0}{0}(この警告は設定の「一般」セクションで無効にすることができます)</system:String>
<system:String x:Key="ZipFiles">Zip files</system:String>
<system:String x:Key="SelectZipFile">Please select zip file</system:String>
<system:String x:Key="ZipFiles">Zip ファイル</system:String>
<system:String x:Key="SelectZipFile">zipファイルを選択してください</system:String>
<system:String x:Key="installLocalPluginTooltip">ローカルパスからプラグインをインストール</system:String>
<system:String x:Key="updateNoResultTitle">No update available</system:String>
<system:String x:Key="updateNoResultSubtitle">All plugins are up to date</system:String>
<system:String x:Key="updateAllPluginsTitle">Plugin updates available</system:String>
<system:String x:Key="updateAllPluginsButtonContent">Update plugins</system:String>
<system:String x:Key="checkPluginUpdatesTooltip">Check plugin updates</system:String>
<system:String x:Key="PluginsUpdateSuccessNoRestart">Plugins are successfully updated. Please restart Flow.</system:String>
<system:String x:Key="updateNoResultTitle">利用可能な更新はありません</system:String>
<system:String x:Key="updateNoResultSubtitle">すべてのプラグインが最新です</system:String>
<system:String x:Key="updateAllPluginsTitle">プラグインの更新が利用可能</system:String>
<system:String x:Key="updateAllPluginsButtonContent">プラグインを更新</system:String>
<system:String x:Key="checkPluginUpdatesTooltip">プラグインの更新を確認</system:String>
<system:String x:Key="PluginsUpdateSuccessNoRestart">プラグインが正常に更新されました。Flow を再起動してください。</system:String>
<!-- Setting Theme -->
<system:String x:Key="theme">テーマ</system:String>
@ -285,13 +286,13 @@
<system:String x:Key="SearchBarHeight">検索バーの高さ</system:String>
<system:String x:Key="ItemHeight">アイテムの高さ</system:String>
<system:String x:Key="queryBoxFont">検索ボックスのフォント</system:String>
<system:String x:Key="resultItemFont">Result Title Font</system:String>
<system:String x:Key="resultSubItemFont">Result Subtitle Font</system:String>
<system:String x:Key="resultItemFont">結果のタイトルのフォント</system:String>
<system:String x:Key="resultSubItemFont">結果のサブタイトルのフォント</system:String>
<system:String x:Key="resetCustomize">リセット</system:String>
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>
<system:String x:Key="ImportThemeSizeToolTip">If a size value intended by the theme designer is available, it will be retrieved and applied.</system:String>
<system:String x:Key="CustomizeToolTip">Customize</system:String>
<system:String x:Key="resetCustomizeToolTip">推奨されるフォントとサイズの設定にリセットします。</system:String>
<system:String x:Key="ImportThemeSize">テーマ中のサイズをインポート</system:String>
<system:String x:Key="ImportThemeSizeToolTip">テーマのデザイナーによって意図されたサイズ値が利用可能なとき、それを取得して適用します。</system:String>
<system:String x:Key="CustomizeToolTip">カスタマイズ</system:String>
<system:String x:Key="windowMode">ウィンドウモード</system:String>
<system:String x:Key="opacity">透過度</system:String>
<system:String x:Key="theme_load_failure_path_not_exists">テーマ {0} が存在しません、デフォルトのテーマに戻します。</system:String>
@ -306,7 +307,7 @@
<system:String x:Key="SoundEffectTip">検索ウィンドウが開いたとき、小さな音を鳴らします</system:String>
<system:String x:Key="SoundEffectVolume">効果音の音量</system:String>
<system:String x:Key="SoundEffectVolumeTip">効果音の音量を調整します</system:String>
<system:String x:Key="SoundEffectWarning">Windows Media Player is unavailable and is required for Flow's volume adjustment. Please check your installation if you need to adjust volume.</system:String>
<system:String x:Key="SoundEffectWarning">Windows Media Player は Flow を使った音量調整に必要です。ボリュームを調整する必要がある場合は、Windows Media Player がインストールされているかどうか確認してください。</system:String>
<system:String x:Key="Animation">アニメーション</system:String>
<system:String x:Key="AnimationTip">UIでアニメーションを使用します</system:String>
<system:String x:Key="AnimationSpeed">アニメーション速度</system:String>
@ -324,15 +325,15 @@
<system:String x:Key="BackdropTypesAcrylic">アクリル</system:String>
<system:String x:Key="BackdropTypesMica">マイカ</system:String>
<system:String x:Key="BackdropTypesMicaAlt">マイカ(代替)</system:String>
<system:String x:Key="TypeIsDarkToolTip">This theme supports two (light/dark) modes.</system:String>
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
<system:String x:Key="TypeIsDarkToolTip">このテーマはライトダークの2モードに対応しています。</system:String>
<system:String x:Key="TypeHasBlurToolTip">このテーマは背景をぼかした透明効果をサポートしています。</system:String>
<system:String x:Key="ShowPlaceholder">プレースホルダーを表示</system:String>
<system:String x:Key="ShowPlaceholderTip">クエリが空の場合にプレースホルダを表示します</system:String>
<system:String x:Key="PlaceholderText">検索欄の案内文</system:String>
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: {0}</system:String>
<system:String x:Key="PlaceholderTextTip">プレースホルダのテキストを変更します。空にすると、 {0} が使用されます</system:String>
<system:String x:Key="KeepMaxResults">ウィンドウサイズの固定</system:String>
<system:String x:Key="KeepMaxResultsToolTip">ウィンドウのサイズを固定し、ドラッグでの変更を無効にします。</system:String>
<system:String x:Key="MaxShowResultsCannotWorkWithAlwaysPreview">Since Always Preview is on, maximum results shown may not take effect because preview panel requires a certain minimum height</system:String>
<system:String x:Key="MaxShowResultsCannotWorkWithAlwaysPreview">「常にプレビューする」が有効になっているため、プレビューパネルの高さの確保のために「結果の最大表示件数」設定は無視される可能性があります</system:String>
<!-- Setting Hotkey -->
<system:String x:Key="hotkey">ホットキー</system:String>
@ -372,51 +373,51 @@
<system:String x:Key="customQueryHotkey">カスタムクエリ ホットキー</system:String>
<system:String x:Key="customQueryShortcut">Custom Query Shortcut</system:String>
<system:String x:Key="builtinShortcuts">組み込みショートカット</system:String>
<system:String x:Key="customQuery">Query</system:String>
<system:String x:Key="customQuery">クエリー</system:String>
<system:String x:Key="customShortcut">ショートカット</system:String>
<system:String x:Key="customShortcutExpansion">展開</system:String>
<system:String x:Key="builtinShortcutDescription">説明</system:String>
<system:String x:Key="delete">削除</system:String>
<system:String x:Key="edit">編集</system:String>
<system:String x:Key="add">追加</system:String>
<system:String x:Key="none">None</system:String>
<system:String x:Key="none">なし</system:String>
<system:String x:Key="pleaseSelectAnItem">項目を選択してください</system:String>
<system:String x:Key="deleteCustomHotkeyWarning">{0} プラグインのホットキーを本当に削除しますか?</system:String>
<system:String x:Key="deleteCustomShortcutWarning">本当にこのショートカットを削除しますか?: {0} を {1} に展開</system:String>
<system:String x:Key="shortcut_clipboard_description">Get text from clipboard.</system:String>
<system:String x:Key="shortcut_clipboard_description">クリップボードからテキストを取得します。</system:String>
<system:String x:Key="shortcut_active_explorer_path">アクティブなエクスプローラーからパスを取得します。</system:String>
<system:String x:Key="queryWindowShadowEffect">検索ウィンドウの落陰効果</system:String>
<system:String x:Key="shadowEffectCPUUsage">Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited.</system:String>
<system:String x:Key="windowWidthSize">Window Width Size</system:String>
<system:String x:Key="windowWidthSizeToolTip">You can also quickly adjust this by using Ctrl+[ and Ctrl+].</system:String>
<system:String x:Key="shadowEffectCPUUsage">影の効果は GPU に大きな負荷をかけます。お使いのコンピューターの性能が限定的な場合、無効にすることをおすすめします。</system:String>
<system:String x:Key="windowWidthSize">ウィンドウ幅のサイズ</system:String>
<system:String x:Key="windowWidthSizeToolTip">Ctrl+Plus と Ctrl+Minus を使用すれば、簡単に調整することもできます。</system:String>
<system:String x:Key="useGlyphUI">Segoe Fluent アイコンを使用する</system:String>
<system:String x:Key="useGlyphUIEffect">サポートされているクエリ結果にSegoe Fluentアイコンを使用する</system:String>
<system:String x:Key="flowlauncherPressHotkey">Press Key</system:String>
<system:String x:Key="showBadges">Show Result Badges</system:String>
<system:String x:Key="flowlauncherPressHotkey">キーを入力</system:String>
<system:String x:Key="showBadges">結果のバッジを表示</system:String>
<system:String x:Key="showBadgesToolTip">サポートされているプラグインでは、バッジが表示され、より簡単に区別できます。</system:String>
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
<system:String x:Key="showBadgesGlobalOnlyToolTip">Show badges for global query results only</system:String>
<system:String x:Key="dialogJumpHotkey">Dialog Jump</system:String>
<system:String x:Key="dialogJumpHotkeyToolTip">Enter shortcut to quickly navigate the Open/Save As dialog window to the path of the current file manager.</system:String>
<system:String x:Key="dialogJump">Dialog Jump</system:String>
<system:String x:Key="dialogJumpToolTip">When Open/Save As dialog window opens, quickly navigate to the current path of the file manager.</system:String>
<system:String x:Key="autoDialogJump">Dialog Jump Automatically</system:String>
<system:String x:Key="autoDialogJumpToolTip">When Open/Save As dialog window is displayed, automatically navigate to the path of the current file manager. (Experimental)</system:String>
<system:String x:Key="showDialogJumpWindow">Show Dialog Jump Window</system:String>
<system:String x:Key="showDialogJumpWindowToolTip">Display Dialog Jump search window when the open/save dialog window is shown to quickly navigate to file/folder locations.</system:String>
<system:String x:Key="dialogJumpWindowPosition">Dialog Jump Window Position</system:String>
<system:String x:Key="dialogJumpWindowPositionToolTip">Select position for the Dialog Jump search window</system:String>
<system:String x:Key="DialogJumpWindowPositionUnderDialog">Fixed under the Open/Save As dialog window. Displayed on open and stays until the window is closed</system:String>
<system:String x:Key="DialogJumpWindowPositionFollowDefault">Default search window position. Displayed when triggered by search window hotkey</system:String>
<system:String x:Key="dialogJumpResultBehaviour">Dialog Jump Result Navigation Behaviour</system:String>
<system:String x:Key="dialogJumpResultBehaviourToolTip">Behaviour to navigate Open/Save As dialog window to the selected result path</system:String>
<system:String x:Key="DialogJumpResultBehaviourLeftClick">Left click or Enter key</system:String>
<system:String x:Key="DialogJumpResultBehaviourRightClick">Right click</system:String>
<system:String x:Key="dialogJumpFileResultBehaviour">Dialog Jump File Navigation Behaviour</system:String>
<system:String x:Key="dialogJumpFileResultBehaviourToolTip">Behaviour to navigate Open/Save As dialog window when the result is a file path</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPath">Fill full path in file name box</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPathOpen">Fill full path in file name box and open</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourDirectory">Fill directory in path box</system:String>
<system:String x:Key="showBadgesGlobalOnly">グローバルクエリのみ、結果のバッジを表示</system:String>
<system:String x:Key="showBadgesGlobalOnlyToolTip">グローバルクエリの結果にのみバッジを表示する</system:String>
<system:String x:Key="dialogJumpHotkey">ダイアログジャンプ</system:String>
<system:String x:Key="dialogJumpHotkeyToolTip">ショートカットを入力して、「名前を付けて開く/保存」ダイアログ・ウィンドウを現在のファイルマネージャのパスにすばやくナビゲートします。</system:String>
<system:String x:Key="dialogJump">ダイアログジャンプ</system:String>
<system:String x:Key="dialogJumpToolTip">「名前を付けて開く/保存」ダイアログウィンドウが開いたら、すぐにファイルマネージャの現在のパスに移動します。</system:String>
<system:String x:Key="autoDialogJump">自動ダイアログジャンプ</system:String>
<system:String x:Key="autoDialogJumpToolTip">開く/名前を付けて保存ダイアログが表示されると、自動的に現在のファイルマネージャのパスに移動させます。 (実験的)</system:String>
<system:String x:Key="showDialogJumpWindow">ダイアログジャンプウィンドウを表示</system:String>
<system:String x:Key="showDialogJumpWindowToolTip">「名前をつけて保存/開く」ダイアログウィンドウが表示されたときにダイアログジャンプのウィンドウを開いて、ファイルやフォルダーを素早く開く。</system:String>
<system:String x:Key="dialogJumpWindowPosition">ダイアログジャンプのウィンドウの位置</system:String>
<system:String x:Key="dialogJumpWindowPositionToolTip">ダイアログジャンプ検索ウィンドウの位置を選択します</system:String>
<system:String x:Key="DialogJumpWindowPositionUnderDialog">「名前を付けて開く/保存」ダイアログウィンドウの下に固定。ウィンドウが閉じるまで開いたまま表示されます</system:String>
<system:String x:Key="DialogJumpWindowPositionFollowDefault">デフォルトの検索ウィンドウの位置。検索ウィンドウのホットキーによってトリガーされたときに表示されます</system:String>
<system:String x:Key="dialogJumpResultBehaviour">ダイアログジャンプの検索結果の開き方</system:String>
<system:String x:Key="dialogJumpResultBehaviourToolTip">「開く/名前を付けて保存」ダイアログウィンドウの選択した結果パスに移動する動作</system:String>
<system:String x:Key="DialogJumpResultBehaviourLeftClick">左クリックまたはEnter キー</system:String>
<system:String x:Key="DialogJumpResultBehaviourRightClick">右クリック</system:String>
<system:String x:Key="dialogJumpFileResultBehaviour">ダイアログジャンプのファイルに対する動作</system:String>
<system:String x:Key="dialogJumpFileResultBehaviourToolTip">結果がファイルパスの場合の、「開く/名前を付けて保存」ダイアログウィンドウに対する動作</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPath">フルパスをファイル名ボックスに入力</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPathOpen">フルパスをファイル名ボックスに入力して開く</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourDirectory">パスボックスに含まれるフォルダを入力</system:String>
<!-- Setting Proxy -->
<system:String x:Key="proxy">HTTP プロキシ</system:String>
@ -467,44 +468,49 @@
<system:String x:Key="userdatapathButton">フォルダーを開く</system:String>
<system:String x:Key="advanced">上級者向け機能</system:String>
<system:String x:Key="logLevel">ログレベル</system:String>
<system:String x:Key="LogLevelDEBUG">デバッグ</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">エラー</system:String>
<system:String x:Key="LogLevelINFO">情報</system:String>
<system:String x:Key="LogLevelDEBUG">デバッグ</system:String>
<system:String x:Key="settingWindowFontTitle">設定ウィンドウで使用するフォント</system:String>
<!-- Release Notes Window -->
<system:String x:Key="seeMoreReleaseNotes">See more release notes on GitHub</system:String>
<system:String x:Key="checkNetworkConnectionTitle">Failed to fetch release notes</system:String>
<system:String x:Key="checkNetworkConnectionSubTitle">Please check your network connection or ensure GitHub is accessible</system:String>
<system:String x:Key="appUpdateTitle">Flow Launcher has been updated to {0}</system:String>
<system:String x:Key="appUpdateButtonContent">Click here to view the release notes</system:String>
<system:String x:Key="seeMoreReleaseNotes">GitHub で詳細なリリース ノートを見る</system:String>
<system:String x:Key="checkNetworkConnectionTitle">リリースノートの取得に失敗</system:String>
<system:String x:Key="checkNetworkConnectionSubTitle">ネットワーク接続を確認するか、GitHubにアクセスできることを確認してください</system:String>
<system:String x:Key="appUpdateTitle">Flow Launcher が {0}に更新されました</system:String>
<system:String x:Key="appUpdateButtonContent">ここをクリックしてリリースノートを表示</system:String>
<!-- FileManager Setting Dialog -->
<system:String x:Key="fileManagerWindow">デフォルトのファイルマネージャー</system:String>
<system:String x:Key="fileManager_learnMore">Learn more</system:String>
<system:String x:Key="fileManager_tips">Please specify the file location of the file manager you using and add arguments as required. The &quot;%d&quot; represents the directory path to open for, used by the Arg for Folder field and for commands opening specific directories. The &quot;%f&quot; represents the file path to open for, used by the Arg for File field and for commands opening specific files.</system:String>
<system:String x:Key="fileManager_tips2">For example, if the file manager uses a command such as &quot;totalcmd.exe /A c:\windows&quot; to open the c:\windows directory, the File Manager Path will be totalcmd.exe, and the Arg For Folder will be /A &quot;%d&quot;. Certain file managers like QTTabBar may just require a path to be supplied, in this instance use &quot;%d&quot; as the File Manager Path and leave the rest of the fields blank.</system:String>
<system:String x:Key="fileManager_name">File Manager</system:String>
<system:String x:Key="fileManager_profile_name">Profile Name</system:String>
<system:String x:Key="fileManager_path">File Manager Path</system:String>
<system:String x:Key="fileManager_directory_arg">Arg For Folder</system:String>
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManager_learnMore">詳細を見る</system:String>
<system:String x:Key="fileManager_tips">使用したいファイルマネージャーのファイルの位置を指定し、コマンドライン引数を入力してください。&quot;%d&quot; は開こうとしているフォルダーのパスを表し、「フォルダー用の引数」の欄で特定のフォルダーを開くために使用されます。&quot;%f&quot; は開こうとしているファイルのパスを表し、「ファイル用の引数」の欄で特定のファイルを開くために使用されます。</system:String>
<system:String x:Key="fileManager_tips2">例として、ファイルマネージャーが &quot;totalcmd.exe /A c:\windows&quot; というコマンドを c:\windows というフォルダを開くために使用する場合を考えます。この場合、ファイルマネージャーのパスは totalcmd.exe で、フォルダー用の引数は /A &quot;%d&quot; になります。QTTabBarのように、パスのみを要求するファイルマネージャーの場合、”%d” をファイルマネージャーのパスの欄に指定し、残りを空欄にしてください。</system:String>
<system:String x:Key="fileManager_name">ファイル マネージャー</system:String>
<system:String x:Key="fileManager_profile_name">プロファイル名</system:String>
<system:String x:Key="fileManager_path">ファイルマネージャーのパス</system:String>
<system:String x:Key="fileManager_directory_arg">フォルダー用の引数</system:String>
<system:String x:Key="fileManager_file_arg">ファイル用の引数</system:String>
<system:String x:Key="fileManagerPathNotFound">ファイルマネージャー '{0}' は、'{1}' に見つかりませんでした。続行しますか?</system:String>
<system:String x:Key="fileManagerPathError">ファイルマネージャのパスエラー</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">デフォルトのウェブブラウザー</system:String>
<system:String x:Key="defaultBrowser_tips">The default setting follows the OS default browser setting. If specified separately, flow uses that browser.</system:String>
<system:String x:Key="defaultBrowser_name">Browser</system:String>
<system:String x:Key="defaultBrowser_profile_name">Browser Name</system:String>
<system:String x:Key="defaultBrowser_path">Browser Path</system:String>
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
<system:String x:Key="defaultBrowser_parameter">Private Mode</system:String>
<system:String x:Key="defaultBrowser_tips">デフォルトの設定は、OS のデフォルトのブラウザ設定に従います。別々に指定すると、Flow はそのブラウザを使用します。</system:String>
<system:String x:Key="defaultBrowser_name">ブラウザー</system:String>
<system:String x:Key="defaultBrowser_profile_name">ブラウザー名</system:String>
<system:String x:Key="defaultBrowser_path">ブラウザーのパス</system:String>
<system:String x:Key="defaultBrowser_newWindow">新しいウィンドウ</system:String>
<system:String x:Key="defaultBrowser_newTab">新しいタブ</system:String>
<system:String x:Key="defaultBrowser_parameter">プライベートモード</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Change Priority</system:String>
<system:String x:Key="priority_tips">Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number</system:String>
<system:String x:Key="invalidPriority">Please provide an valid integer for Priority!</system:String>
<system:String x:Key="changePriorityWindow">優先度の変更</system:String>
<system:String x:Key="priority_tips">数値が大きいほど、結果の上の方に表示されます。試しに5として設定してみてください。 結果を他のプラグインよりも低くしたい場合は、負の数字を入力してください</system:String>
<system:String x:Key="invalidPriority">優先度には有効な整数を入力してください!</system:String>
<!-- Action Keyword Setting Dialog -->
<system:String x:Key="oldActionKeywords">古いアクションキーワード</system:String>
@ -514,33 +520,33 @@
<system:String x:Key="cannotFindSpecifiedPlugin">指定されたプラグインが見つかりません</system:String>
<system:String x:Key="newActionKeywordsCannotBeEmpty">新しいアクションキーワードを空にすることはできません</system:String>
<system:String x:Key="newActionKeywordsHasBeenAssigned">新しいアクションキーワードは他のプラグインに割り当てられています。他のアクションキーワードを入力してください</system:String>
<system:String x:Key="newActionKeywordsSameAsOld">This new Action Keyword is the same as old, please choose a different one</system:String>
<system:String x:Key="newActionKeywordsSameAsOld">そのアクションキーワードは以前のものと同じです。他のアクションキーワードを入力してください</system:String>
<system:String x:Key="success">成功しました</system:String>
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
<system:String x:Key="failedToCopy">Failed to copy</system:String>
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
<system:String x:Key="completedSuccessfully">正常に完了しました</system:String>
<system:String x:Key="failedToCopy">コピーに失敗</system:String>
<system:String x:Key="actionkeyword_tips">プラグインを起動するためのアクションキーワードを、空白区切りで入力してください。特定のキーワードを使用せずにプラグインを使用したい場合、* を入力してください。</system:String>
<!-- Search Delay Settings Dialog -->
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
<system:String x:Key="searchDelayTimeTitle">検索の遅延時間の設定</system:String>
<system:String x:Key="searchDelayTimeTips">プラグインに使用したい検索の遅延時間をミリ秒で入力します。 何も指定したくない場合は空にしておくと、プラグインはデフォルトの検索の遅延時間を使用します。</system:String>
<!-- Search Delay Settings Dialog -->
<system:String x:Key="homeTitle">ホームページ</system:String>
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
<system:String x:Key="homeTips">クエリが空のときにプラグインの結果を表示したい場合は、プラグインのホームページの設定を有効にします。</system:String>
<!-- Custom Query Hotkey Dialog -->
<system:String x:Key="customeQueryHotkeyTitle">カスタムクエリのホットキー</system:String>
<system:String x:Key="customeQueryHotkeyTips">Press a custom hotkey to open Flow Launcher and input the specified query automatically.</system:String>
<system:String x:Key="customeQueryHotkeyTips">カスタムホットキーを押して Flow Launcher を開き、指定したクエリを自動的に入力します。</system:String>
<system:String x:Key="preview">プレビュー</system:String>
<system:String x:Key="hotkeyIsNotUnavailable">ホットキーは使用できません。新しいホットキーを選択してください</system:String>
<system:String x:Key="invalidPluginHotkey">Hotkey is invalid</system:String>
<system:String x:Key="invalidPluginHotkey">そのホットキーは無効です</system:String>
<system:String x:Key="update">更新</system:String>
<system:String x:Key="hotkeyRegTitle">Binding Hotkey</system:String>
<system:String x:Key="hotkeyUnavailable">Current hotkey is unavailable.</system:String>
<system:String x:Key="hotkeyUnavailableUneditable">This hotkey is reserved for &quot;{0}&quot; and can't be used. Please choose another hotkey.</system:String>
<system:String x:Key="hotkeyUnavailableEditable">This hotkey is already in use by &quot;{0}&quot;. If you press &quot;Overwrite&quot;, it will be removed from &quot;{0}&quot;.</system:String>
<system:String x:Key="hotkeyRegGuide">Press the keys you want to use for this function.</system:String>
<system:String x:Key="emptyPluginHotkey">Hotkey and action keyword are empty</system:String>
<system:String x:Key="hotkeyRegTitle">ホットキーの設定</system:String>
<system:String x:Key="hotkeyUnavailable">現在のホットキーは使用できません。</system:String>
<system:String x:Key="hotkeyUnavailableUneditable">このホットキーは &quot;{0}&quot; で予約されており、使用できません。別のホットキーを選択してください。</system:String>
<system:String x:Key="hotkeyUnavailableEditable">このホットキーは &quot;{0}&quot; によってすでに使用されています。「上書き」を押すと、&quot;{0}&quot;から削除されます。</system:String>
<system:String x:Key="hotkeyRegGuide">この機能に使用するキーを押してください。</system:String>
<system:String x:Key="emptyPluginHotkey">ホットキーとアクションキーワードが空です</system:String>
<!-- Custom Query Shortcut Dialog -->
<system:String x:Key="customeQueryShortcutTitle">カスタムクエリのショートカット</system:String>
@ -551,11 +557,11 @@
</system:String>
<system:String x:Key="duplicateShortcut">そのショートカットは既に存在します。新しいショートカットを入力するか、既存のショートカットを編集してください。</system:String>
<system:String x:Key="emptyShortcut">ショートカット、展開の少なくとも一方が空です。</system:String>
<system:String x:Key="invalidShortcut">Shortcut is invalid</system:String>
<system:String x:Key="invalidShortcut">ショートカットが無効です</system:String>
<!-- Common Action -->
<system:String x:Key="commonSave">保存</system:String>
<system:String x:Key="commonOverwrite">Overwrite</system:String>
<system:String x:Key="commonOverwrite">上書き</system:String>
<system:String x:Key="commonCancel">キャンセル</system:String>
<system:String x:Key="commonReset">リセット</system:String>
<system:String x:Key="commonDelete">削除</system:String>
@ -580,46 +586,46 @@
<system:String x:Key="reportWindow_report_failed">クラッシュレポートの送信に失敗しました</system:String>
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcherにエラーが発生しました</system:String>
<system:String x:Key="reportWindow_please_open_issue">Please open new issue in</system:String>
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
<system:String x:Key="reportWindow_upload_log">1. ログファイルをアップロード: {0}</system:String>
<system:String x:Key="reportWindow_copy_below">2. 例外メッセージ以下をコピー</system:String>
<!-- File Open Error -->
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
<system:String x:Key="fileManagerNotFoundTitle">ファイルマネージャのエラー</system:String>
<system:String x:Key="fileManagerNotFound">
The specified file manager could not be found. Please check the Custom File Manager setting under Settings &gt; General.
指定されたファイルマネージャーが見つかりませんでした。設定 &gt; 一般でカスタムファイルマネージャの設定を確認してください。
</system:String>
<system:String x:Key="errorTitle">Error</system:String>
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
<system:String x:Key="browserOpenError">An error occurred while opening the URL in the browser. Please check your Default Web Browser configuration in the General section of the settings window</system:String>
<system:String x:Key="errorTitle">エラー</system:String>
<system:String x:Key="folderOpenError">フォルダを開く際にエラーが発生しました。 {0}</system:String>
<system:String x:Key="browserOpenError">ブラウザでURLを開く際にエラーが発生しました。設定ウィンドウの一般セクションでデフォルトのウェブブラウザ設定を確認してください</system:String>
<!-- General Notice -->
<system:String x:Key="pleaseWait">Please wait...</system:String>
<system:String x:Key="pleaseWait">しばらくお待ちください…</system:String>
<!-- Update -->
<system:String x:Key="update_flowlauncher_update_check">Checking for new update</system:String>
<system:String x:Key="update_flowlauncher_update_check">新しい更新を確認中</system:String>
<system:String x:Key="update_flowlauncher_already_on_latest">Flow Launcherは既に最新です</system:String>
<system:String x:Key="update_flowlauncher_update_found">Update found</system:String>
<system:String x:Key="update_flowlauncher_updating">Updating...</system:String>
<system:String x:Key="update_flowlauncher_update_found">更新が見つかりました</system:String>
<system:String x:Key="update_flowlauncher_updating">更新中…</system:String>
<system:String x:Key="update_flowlauncher_fail_moving_portable_user_profile_data">
Flow Launcher was not able to move your user profile data to the new update version.
Please manually move your profile data folder from {0} to {1}
Flow Launcherはユーザープロファイルデータを新しいバージョンに移動できませんでした。
手動で {0} から {1}にプロフィールデータフォルダを移動してください
</system:String>
<system:String x:Key="update_flowlauncher_new_update">New Update</system:String>
<system:String x:Key="update_flowlauncher_new_update">新しい更新</system:String>
<system:String x:Key="update_flowlauncher_update_new_version_available">Flow Launcher の最新バージョン V{0} が入手可能です</system:String>
<system:String x:Key="update_flowlauncher_update_error">Flow Launcherのアップデート中にエラーが発生しました</system:String>
<system:String x:Key="update_flowlauncher_update">更新</system:String>
<system:String x:Key="update_flowlauncher_update_cancel">キャンセル</system:String>
<system:String x:Key="update_flowlauncher_fail">Update Failed</system:String>
<system:String x:Key="update_flowlauncher_check_connection">Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com.</system:String>
<system:String x:Key="update_flowlauncher_fail">アップデート失敗</system:String>
<system:String x:Key="update_flowlauncher_check_connection">接続を確認し、その後プロキシ設定を github-cloud.s3.amazonaws.com に更新してみてください。</system:String>
<system:String x:Key="update_flowlauncher_update_restart_flowlauncher_tip">このアップデートでは、Flow Launcherの再起動が必要です</system:String>
<system:String x:Key="update_flowlauncher_update_update_files">次のファイルがアップデートされます</system:String>
<system:String x:Key="update_flowlauncher_update_files">更新ファイル一覧</system:String>
<system:String x:Key="update_flowlauncher_update_update_description">アップデートの詳細</system:String>
<!-- Plugin Update Window -->
<system:String x:Key="restartAfterUpdating">Restart Flow Launcher after updating plugins</system:String>
<system:String x:Key="updatePluginCheckboxContent">{0}: Update from v{1} to v{2}</system:String>
<system:String x:Key="updatePluginNoSelected">No plugin selected</system:String>
<system:String x:Key="restartAfterUpdating">プラグインを更新した後、Flow Launcher を再起動する</system:String>
<system:String x:Key="updatePluginCheckboxContent">{0}: v{1} から v{2} へ更新</system:String>
<system:String x:Key="updatePluginNoSelected">プラグインが選択されていません</system:String>
<!-- Welcome Window -->
<system:String x:Key="Skip">スキップ</system:String>
@ -642,18 +648,18 @@
<system:String x:Key="HotkeyShiftEnterDesc">コンテキストメニューを開く</system:String>
<system:String x:Key="HotkeyCtrlEnterDesc">ファイルのあるフォルダを開く</system:String>
<system:String x:Key="HotkeyCtrlShiftEnterDesc">管理者として実行、または、 デフォルトのファイルマネージャでフォルダを開く</system:String>
<system:String x:Key="HotkeyCtrlHDesc">Query History</system:String>
<system:String x:Key="HotkeyCtrlHDesc">クエリの履歴</system:String>
<system:String x:Key="HotkeyESCDesc">コンテキストメニューから検索結果に戻る</system:String>
<system:String x:Key="HotkeyTabDesc">Autocomplete</system:String>
<system:String x:Key="HotkeyTabDesc">自動補完</system:String>
<system:String x:Key="HotkeyRunDesc">選択したアイテムを開く、または、実行する</system:String>
<system:String x:Key="HotkeyCtrlIDesc">Flow Launcherの設定ウインドウを開く</system:String>
<system:String x:Key="HotkeyF5Desc">プラグインデータのリロード</system:String>
<system:String x:Key="HotkeySelectFirstResult">Select first result</system:String>
<system:String x:Key="HotkeySelectLastResult">Select last result</system:String>
<system:String x:Key="HotkeyRequery">Run current query again</system:String>
<system:String x:Key="HotkeySelectFirstResult">最初の結果を選択</system:String>
<system:String x:Key="HotkeySelectLastResult">最後の結果を選択</system:String>
<system:String x:Key="HotkeyRequery">現在のクエリをもう一度実行</system:String>
<system:String x:Key="HotkeyOpenResult">結果を開く</system:String>
<system:String x:Key="HotkeyOpenResultN">Open result #{0}</system:String>
<system:String x:Key="HotkeyOpenResultN">#{0} を開く</system:String>
<system:String x:Key="RecommendWeather">天気</system:String>
<system:String x:Key="RecommendWeatherDesc">天気についてのGoogle検索</system:String>

View file

@ -215,6 +215,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">플러그인 스토어</system:String>
@ -458,8 +459,10 @@
<system:String x:Key="userdatapathButton">폴더 열기</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">로그 레벨</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">설정창 글꼴</system:String>
<!-- Release Notes Window -->
@ -481,6 +484,7 @@
<system:String x:Key="fileManager_file_arg">파일경로 인수</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">기본 웹 브라우저</system:String>
@ -491,6 +495,8 @@
<system:String x:Key="defaultBrowser_newWindow">새 창</system:String>
<system:String x:Key="defaultBrowser_newTab">새 탭</system:String>
<system:String x:Key="defaultBrowser_parameter">사생활 보호 모드</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">중요도 변경</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Programtillegg butikk</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Åpne mappe</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Feil</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg for fil</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Standard nettleser</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nytt vindu</system:String>
<system:String x:Key="defaultBrowser_newTab">Ny fane</system:String>
<system:String x:Key="defaultBrowser_parameter">Privat modus</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Endre prioritet</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Plugin Winkel</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Map openen</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg voor bestand</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Standaard webbrowser</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nieuw Venster</system:String>
<system:String x:Key="defaultBrowser_newTab">Nieuw tabblad</system:String>
<system:String x:Key="defaultBrowser_parameter">Privé modus</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Prioriteit wijzigen</system:String>

View file

@ -223,6 +223,7 @@ Kliknij &quot;nie&quot;, jeśli jest już zainstalowany. Zostaniesz wtedy popros
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Sklep z wtyczkami</system:String>
@ -466,8 +467,10 @@ Kliknij &quot;nie&quot;, jeśli jest już zainstalowany. Zostaniesz wtedy popros
<system:String x:Key="userdatapathButton">Otwórz folder</system:String>
<system:String x:Key="advanced">Zaawansowane</system:String>
<system:String x:Key="logLevel">Poziom logowania</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Błąd</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Ustawienia czcionki okna</system:String>
<!-- Release Notes Window -->
@ -489,6 +492,7 @@ Kliknij &quot;nie&quot;, jeśli jest już zainstalowany. Zostaniesz wtedy popros
<system:String x:Key="fileManager_file_arg">Arg dla pliku</system:String>
<system:String x:Key="fileManagerPathNotFound">Menedżer plików „{0}” nie został znaleziony w lokalizacji „{1}”. Czy chcesz kontynuować?</system:String>
<system:String x:Key="fileManagerPathError">Błąd ścieżki do menedżera plików</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Domyślna przeglądarka</system:String>
@ -499,6 +503,8 @@ Kliknij &quot;nie&quot;, jeśli jest już zainstalowany. Zostaniesz wtedy popros
<system:String x:Key="defaultBrowser_newWindow">Nowe okno</system:String>
<system:String x:Key="defaultBrowser_newTab">Nowa zakładka</system:String>
<system:String x:Key="defaultBrowser_parameter">Tryb prywatny</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Zmień priorytet</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Loja de Plugins</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg para Arquivo</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Navegador da Web Padrão</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nova Janela</system:String>
<system:String x:Key="defaultBrowser_newTab">Nova Aba</system:String>
<system:String x:Key="defaultBrowser_parameter">Modo Privado</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Alterar Prioridade</system:String>

View file

@ -223,6 +223,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Falha ao desinstalar {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Não foi possível encontrar plugin.json no ficheiro zip ou, então, o caminho {0} não existe.</system:String>
<system:String x:Key="pluginExistAlreadyMessage">Já existe um plugin com a mesma ID e versão ou, então, a versão instalada é superior à do plugin descarregado.</system:String>
<system:String x:Key="errorCreatingSettingPanel">Erro ao criar o painel de definição para o plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Loja de plugins</system:String>
@ -331,7 +332,7 @@
<system:String x:Key="PlaceholderTextTip">O texto do marcador de posição. Se vazio, será utilizado: {0}</system:String>
<system:String x:Key="KeepMaxResults">Janela com tamanho fixo</system:String>
<system:String x:Key="KeepMaxResultsToolTip">Não pode ajustar o tamanho da janela por arrasto.</system:String>
<system:String x:Key="MaxShowResultsCannotWorkWithAlwaysPreview">Since Always Preview is on, maximum results shown may not take effect because preview panel requires a certain minimum height</system:String>
<system:String x:Key="MaxShowResultsCannotWorkWithAlwaysPreview">Como a opção &quot;Pré-visualizar sempre&quot; está ativa, os resultados máximos mostrados podem não ter efeito porque o painel de visualização requer uma altura mínima</system:String>
<!-- Setting Hotkey -->
<system:String x:Key="hotkey">Tecla de atalho</system:String>
@ -465,8 +466,10 @@
<system:String x:Key="userdatapathButton">Abrir pasta</system:String>
<system:String x:Key="advanced">Avançado</system:String>
<system:String x:Key="logLevel">Nível de registo</system:String>
<system:String x:Key="LogLevelDEBUG">Depuração</system:String>
<system:String x:Key="LogLevelNONE">Silencioso</system:String>
<system:String x:Key="LogLevelERROR">Erro</system:String>
<system:String x:Key="LogLevelINFO">Informação</system:String>
<system:String x:Key="LogLevelDEBUG">Depuração</system:String>
<system:String x:Key="settingWindowFontTitle">Tipo de letra da aplicação</system:String>
<!-- Release Notes Window -->
@ -488,6 +491,7 @@
<system:String x:Key="fileManager_file_arg">Argumento para ficheiro</system:String>
<system:String x:Key="fileManagerPathNotFound">Não foi possível encontrar o gestor de ficheiros '{0}' em '{1}'. Deseja continuar?</system:String>
<system:String x:Key="fileManagerPathError">Erro no caminho do gestor de ficheiros</system:String>
<system:String x:Key="fileManagerExplorer">Gestor de ficheiros</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Navegador web padrão</system:String>
@ -498,6 +502,8 @@
<system:String x:Key="defaultBrowser_newWindow">Nova janela</system:String>
<system:String x:Key="defaultBrowser_newTab">Novo separador</system:String>
<system:String x:Key="defaultBrowser_parameter">Modo privado</system:String>
<system:String x:Key="defaultBrowser_default">Padrão</system:String>
<system:String x:Key="defaultBrowser_new_profile">Novo perfil</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Alterar prioridade</system:String>

View file

@ -167,7 +167,7 @@
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
<system:String x:Key="KoreanImeSettingChangeFailTitle">Failed to change Korean IME setting</system:String>
<system:String x:Key="KoreanImeSettingChangeFailSubTitle">Please check your system registry access or contact support.</system:String>
<system:String x:Key="homePage">Home Page</system:String>
<system:String x:Key="homePage">Главная страница</system:String>
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
@ -199,10 +199,10 @@
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
<system:String x:Key="FilterComboboxLabel">Advanced Settings:</system:String>
<system:String x:Key="DisplayModeOnOff">Enabled</system:String>
<system:String x:Key="DisplayModeOnOff">Включено</system:String>
<system:String x:Key="DisplayModePriority">Приоритет</system:String>
<system:String x:Key="DisplayModeSearchDelay">Search Delay</system:String>
<system:String x:Key="DisplayModeHomeOnOff">Home Page</system:String>
<system:String x:Key="DisplayModeHomeOnOff">Главная страница</system:String>
<system:String x:Key="currentPriority">Текущий приоритет</system:String>
<system:String x:Key="newPriority">Новый приоритет</system:String>
<system:String x:Key="priority">Приоритет</system:String>
@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Магазин плагинов</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Ошибка</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Аргумент для файла</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Браузер по умолчанию</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Новое окно</system:String>
<system:String x:Key="defaultBrowser_newTab">Новая вкладка</system:String>
<system:String x:Key="defaultBrowser_parameter">Приватный режим</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Изменить приоритет</system:String>
@ -525,7 +531,7 @@
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
<!-- Search Delay Settings Dialog -->
<system:String x:Key="homeTitle">Home Page</system:String>
<system:String x:Key="homeTitle">Главная страница</system:String>
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
<!-- Custom Query Hotkey Dialog -->

View file

@ -176,7 +176,7 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="showAtTopmost">Zobraziť vyhľadávacie okno v popredí</system:String>
<system:String x:Key="showAtTopmostToolTip">Prepíše nastavenie &quot;Vždy na vrchu&quot; ostatných programov a zobrazí navrchu Flow.</system:String>
<system:String x:Key="autoRestartAfterChanging">Reštartovať po úprave pluginu cez Repozitár pluginov</system:String>
<system:String x:Key="autoRestartAfterChangingToolTip">Automaticky reštartovať Flow Launcher po inštalácii/odinštalácii/aktualizáciu pluginu cez Repozitár pluginov</system:String>
<system:String x:Key="autoRestartAfterChangingToolTip">Automaticky reštartovať Flow Launcher po inštalácii/odinštalácii/aktualizácii pluginu cez Repozitár pluginov</system:String>
<system:String x:Key="showUnknownSourceWarning">Zobraziť upozornenie na neznámy zdroj</system:String>
<system:String x:Key="showUnknownSourceWarningToolTip">Zobraziť upozornenie pri inštalácii z neznámych zdrojov</system:String>
<system:String x:Key="autoUpdatePlugins">Automaticky aktualizovať pluginy</system:String>
@ -225,6 +225,7 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="failedToUninstallPluginTitle">Nepodarilo sa odinštalovať {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Súbor plugin.json sa nenašiel v rozbalenom zip súbore, alebo táto cesta {0} neexistuje</system:String>
<system:String x:Key="pluginExistAlreadyMessage">Plugin s rovnakým ID už existuje, alebo ide o vyššiu verziu ako stiahnutý plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Chyba pri vytváraní panelu nastavení pre plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Repozitár pluginov</system:String>
@ -255,7 +256,7 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="UpdatePromptTitle">Aktualizácia pluginu</system:String>
<system:String x:Key="UpdatePromptSubtitle">{0} od {1} {2}{2}Chcete aktualizovať tento plugin?</system:String>
<system:String x:Key="DownloadingPlugin">Sťahovanie pluginu</system:String>
<system:String x:Key="AutoRestartAfterChange">Automaticky reštartovať po inštalácii/odinštalácii/aktualizáciu pluginov cez Repozitár pluginov</system:String>
<system:String x:Key="AutoRestartAfterChange">Automaticky reštartovať po inštalácii/odinštalácii/aktualizácii pluginov cez Repozitár pluginov</system:String>
<system:String x:Key="ZipFileNotHavePluginJson">V zipe sa nenachádza platná konfigurácia plugin.json</system:String>
<system:String x:Key="InstallFromUnknownSourceTitle">Inštalácia z neznámeho zdroja</system:String>
<system:String x:Key="InstallFromUnknownSourceSubtitle">Tento plugin pochádza z neznámeho zdroja a môže predstavovať potenciálne riziká!{0}{0}Uistite sa, že viete, odkiaľ tento plugin pochádza, a že je bezpečný.{0}{0}Stále chcete pokračovať?{0}{0}(Toto upozornenie môžete vypnúť sekcii Všeobecné v nastaveniach)</system:String>
@ -267,7 +268,7 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="updateAllPluginsTitle">Dostupná aktualizácia pluginu</system:String>
<system:String x:Key="updateAllPluginsButtonContent">Aktualizovať pluginy</system:String>
<system:String x:Key="checkPluginUpdatesTooltip">Skontrolovať dostupnosť aktualizácií</system:String>
<system:String x:Key="PluginsUpdateSuccessNoRestart">Pluginy {0} boli úspešne aktualizované. Prosím, reštartuje Flow.</system:String>
<system:String x:Key="PluginsUpdateSuccessNoRestart">Pluginy boli úspešne aktualizované. Prosím, reštartuje Flow.</system:String>
<!-- Setting Theme -->
<system:String x:Key="theme">Motív</system:String>
@ -396,28 +397,28 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="showBadges">Zobraziť výsledok v odznaku</system:String>
<system:String x:Key="showBadgesToolTip">Ak to plugin podporuje, zobrazí sa jeho ikona v odznaku na jednoduchšie odlíšenie.</system:String>
<system:String x:Key="showBadgesGlobalOnly">Zobraziť výsledok v odznaku len pre globálne vyhľadávanie</system:String>
<system:String x:Key="showBadgesGlobalOnlyToolTip">Show badges for global query results only</system:String>
<system:String x:Key="dialogJumpHotkey">Dialog Jump</system:String>
<system:String x:Key="dialogJumpHotkeyToolTip">Enter shortcut to quickly navigate the Open/Save As dialog window to the path of the current file manager.</system:String>
<system:String x:Key="dialogJump">Dialog Jump</system:String>
<system:String x:Key="dialogJumpToolTip">When Open/Save As dialog window opens, quickly navigate to the current path of the file manager.</system:String>
<system:String x:Key="autoDialogJump">Dialog Jump Automatically</system:String>
<system:String x:Key="autoDialogJumpToolTip">When Open/Save As dialog window is displayed, automatically navigate to the path of the current file manager. (Experimental)</system:String>
<system:String x:Key="showDialogJumpWindow">Show Dialog Jump Window</system:String>
<system:String x:Key="showDialogJumpWindowToolTip">Display Dialog Jump search window when the open/save dialog window is shown to quickly navigate to file/folder locations.</system:String>
<system:String x:Key="dialogJumpWindowPosition">Dialog Jump Window Position</system:String>
<system:String x:Key="dialogJumpWindowPositionToolTip">Select position for the Dialog Jump search window</system:String>
<system:String x:Key="DialogJumpWindowPositionUnderDialog">Fixed under the Open/Save As dialog window. Displayed on open and stays until the window is closed</system:String>
<system:String x:Key="DialogJumpWindowPositionFollowDefault">Default search window position. Displayed when triggered by search window hotkey</system:String>
<system:String x:Key="dialogJumpResultBehaviour">Dialog Jump Result Navigation Behaviour</system:String>
<system:String x:Key="dialogJumpResultBehaviourToolTip">Behaviour to navigate Open/Save As dialog window to the selected result path</system:String>
<system:String x:Key="DialogJumpResultBehaviourLeftClick">Left click or Enter key</system:String>
<system:String x:Key="DialogJumpResultBehaviourRightClick">Right click</system:String>
<system:String x:Key="dialogJumpFileResultBehaviour">Dialog Jump File Navigation Behaviour</system:String>
<system:String x:Key="dialogJumpFileResultBehaviourToolTip">Behaviour to navigate Open/Save As dialog window when the result is a file path</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPath">Fill full path in file name box</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPathOpen">Fill full path in file name box and open</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourDirectory">Fill directory in path box</system:String>
<system:String x:Key="showBadgesGlobalOnlyToolTip">Zobrazí výsledok v odznaku len pre výsledky globálneho vyhľadávania</system:String>
<system:String x:Key="dialogJumpHotkey">Rýchly prechod</system:String>
<system:String x:Key="dialogJumpHotkeyToolTip">Zadajte skratku na rýchly prechod na aktuálnu cestu správcu súborov v dialógovom okne Otvoriť/Uložiť.</system:String>
<system:String x:Key="dialogJump">Rýchly prechod</system:String>
<system:String x:Key="dialogJumpToolTip">Keď sa otvorí dialógové okno Otvoriť/Uložiť, rýchlo prejdete na aktuálnu cestu správcu súborov.</system:String>
<system:String x:Key="autoDialogJump">Automatický rýchly prechod</system:String>
<system:String x:Key="autoDialogJumpToolTip">Keď je otvorené dialógové okno Otvoriť/Uložiť, automaticky prejsť na cestu v aktuálnom správcovi súborov (Experimentálne)</system:String>
<system:String x:Key="showDialogJumpWindow">Zobraziť okno na rýchly prechod</system:String>
<system:String x:Key="showDialogJumpWindowToolTip">Zobraziť okno rýchleho prechodu, keď je zobrazené dialógové okno Ovoriť/Uložiť na rýchlu navigáciu do umiestnenia súborov/priečinkov.</system:String>
<system:String x:Key="dialogJumpWindowPosition">Umiestnenie okna &quot;rýchly prechod&quot;</system:String>
<system:String x:Key="dialogJumpWindowPositionToolTip">Vyberte umiestnenie vyhľadávacieho okna pre &quot;rýchly prechod&quot;</system:String>
<system:String x:Key="DialogJumpWindowPositionUnderDialog">Fixné pod oknom Otvoriť/Uložiť. Zostane zobrazené po otvorení až do uzavretia okna</system:String>
<system:String x:Key="DialogJumpWindowPositionFollowDefault">Predvolená pozícia vyhľadávacieho okna. Zobrazí sa po zadaní skratky na otvorenie vyhľadávacieho okna</system:String>
<system:String x:Key="dialogJumpResultBehaviour">Akcia na prechod k výsledku rýchleho prechodu</system:String>
<system:String x:Key="dialogJumpResultBehaviourToolTip">Ako prejsť na vybranú cestu v otvorenom dialógovom okne Otvoriť/Uložiť</system:String>
<system:String x:Key="DialogJumpResultBehaviourLeftClick">Kliknutie ľavým tlačidlom myši alebo klávesom Enter</system:String>
<system:String x:Key="DialogJumpResultBehaviourRightClick">Kliknutie pravým tlačidlom myši</system:String>
<system:String x:Key="dialogJumpFileResultBehaviour">Akcia na prechod k súboru rýchleho prechodu</system:String>
<system:String x:Key="dialogJumpFileResultBehaviourToolTip">Akcia, ktorá sa vykoná na navigáciu v dialógovom okne Otvoriť/Uložiť, ak výsledkom je súbor</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPath">Vložiť celú cestu k súboru do poľa názvu súboru</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPathOpen">Vložiť celú cestu k súboru do poľa názvu súboru a otvoriť</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourDirectory">Vložiť priečinok do poľa s cestou</system:String>
<!-- Setting Proxy -->
<system:String x:Key="proxy">HTTP proxy</system:String>
@ -468,8 +469,10 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="userdatapathButton">Otvoriť priečinok</system:String>
<system:String x:Key="advanced">Rozšírené</system:String>
<system:String x:Key="logLevel">Úroveň logovania</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Žiadne</system:String>
<system:String x:Key="LogLevelERROR">Chyba</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Nastavenie písma okna</system:String>
<!-- Release Notes Window -->
@ -482,8 +485,8 @@ Nevykonali sa žiadne zmeny.</system:String>
<!-- FileManager Setting Dialog -->
<system:String x:Key="fileManagerWindow">Vyberte správcu súborov</system:String>
<system:String x:Key="fileManager_learnMore">Viac informácií</system:String>
<system:String x:Key="fileManager_tips">Zadajte umiestnenie súboru správcu súborov, ktorý používate, a podľa potreby pridajte argumenty. &quot;%d&quot; predstavuje cestu k priečinku, ktorý sa má otvoriť, používa sa v poli Arg pre priečinok a pri príkazoch na otvorenie konkrétnych priečinkov. &quot;%f&quot; predstavuje cestu k súboru, ktorá sa má otvoriť a používa sa v poli Arg pre súbor a pri príkazoch na otvorenie konkrétnych súborov.</system:String>
<system:String x:Key="fileManager_tips2">Napríklad, ak správca súborov používa príkaz ako &quot;totalcmd.exe /A c:\windows&quot; na otvorenie priečinka c:\windows, cesta správcu súborov bude totalcmd.exe a Arg pre priečinok bude /A &quot;%d&quot;. Niektorí správcovia súborov, ako napríklad QTTabBar, môžu vyžadovať len zadanie cesty, v tomto prípade použite &quot;%d&quot; ako cestu správcu súborov a zvyšok súborov nechajte prázdny.</system:String>
<system:String x:Key="fileManager_tips">Zadajte umiestnenie súboru správcu súborov, ktorý používate, a podľa potreby pridajte argumenty. &quot;%d&quot; predstavuje cestu k priečinku, ktorý sa má otvoriť, používa sa v poli Arg. pre priečinok a pri príkazoch na otvorenie konkrétnych priečinkov. &quot;%f&quot; predstavuje cestu k súboru, ktorá sa má otvoriť a používa sa v poli Arg. pre súbor a pri príkazoch na otvorenie konkrétnych súborov.</system:String>
<system:String x:Key="fileManager_tips2">Napríklad, ak správca súborov používa príkaz ako &quot;totalcmd.exe /A c:\windows&quot; na otvorenie priečinka c:\windows, cesta správcu súborov bude totalcmd.exe a Arg. pre priečinok bude /A &quot;%d&quot;. Niektorí správcovia súborov, ako napríklad QTTabBar, môžu vyžadovať len zadanie cesty, v tomto prípade použite &quot;%d&quot; ako cestu správcu súborov a zvyšok súborov nechajte prázdny.</system:String>
<system:String x:Key="fileManager_name">Správca súborov</system:String>
<system:String x:Key="fileManager_profile_name">Názov profilu</system:String>
<system:String x:Key="fileManager_path">Cesta k správcovi súborov</system:String>
@ -491,6 +494,7 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="fileManager_file_arg">Arg. pre súbor</system:String>
<system:String x:Key="fileManagerPathNotFound">Správca súborov '{0}' sa nenachádza na '{1}'. Chcete pokračovať?</system:String>
<system:String x:Key="fileManagerPathError">Chyba v ceste k správcovi súborov</system:String>
<system:String x:Key="fileManagerExplorer">Prieskumník</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Predvolený webový prehliadač</system:String>
@ -501,6 +505,8 @@ Nevykonali sa žiadne zmeny.</system:String>
<system:String x:Key="defaultBrowser_newWindow">Nové okno</system:String>
<system:String x:Key="defaultBrowser_newTab">Nová karta</system:String>
<system:String x:Key="defaultBrowser_parameter">Privátny režim</system:String>
<system:String x:Key="defaultBrowser_default">Predvolené</system:String>
<system:String x:Key="defaultBrowser_new_profile">Nový profil</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Zmena priority</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Plugin Store</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
<system:String x:Key="defaultBrowser_parameter">Private Mode</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Change Priority</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Plugin Store</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
<system:String x:Key="defaultBrowser_parameter">Private Mode</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Change Priority</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">{0} kaldırılamıyor</system:String>
<system:String x:Key="fileNotFoundMessage">plugin.json dosyası çıkarılan zip dosyasında bulunamadı veya {0} yolu mevcut değil</system:String>
<system:String x:Key="pluginExistAlreadyMessage">Bu eklentiyle aynı ID ve sürüme sahip bir eklenti zaten var, ya da mevcut sürüm daha yüksek</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Eklenti Mağazası</system:String>
@ -405,14 +406,14 @@
<system:String x:Key="showDialogJumpWindow">Diyalog Atlama Penceresini Göster</system:String>
<system:String x:Key="showDialogJumpWindowToolTip">Dosya/klasör konumlarına hızlı erişim için aç/kaydet penceresi gösterildiğinde Diyalog Atlama arama penceresini görüntüle.</system:String>
<system:String x:Key="dialogJumpWindowPosition">Diyalog Atlama Penceresi Konumu</system:String>
<system:String x:Key="dialogJumpWindowPositionToolTip">Select position for the Dialog Jump search window</system:String>
<system:String x:Key="dialogJumpWindowPositionToolTip">Diyalog Atlama arama penceresi için konum seçin</system:String>
<system:String x:Key="DialogJumpWindowPositionUnderDialog">Farklı Aç/Kaydet iletişim penceresinin altında düzeltildi. Açıldığında görüntülenir ve pencere kapatılana kadar kalır</system:String>
<system:String x:Key="DialogJumpWindowPositionFollowDefault">Varsayılan arama penceresi konumu. Arama penceresi kısayol tuşu tarafından tetiklendiğinde görüntülenir</system:String>
<system:String x:Key="dialogJumpResultBehaviour">Dialog Jump Result Navigation Behaviour</system:String>
<system:String x:Key="dialogJumpResultBehaviour">Diyalog Atlama Sonucu Gezinme Davranışı</system:String>
<system:String x:Key="dialogJumpResultBehaviourToolTip">Farklı Aç/Kaydet iletişim penceresini seçilen sonuç yoluna yönlendirmek için davranış</system:String>
<system:String x:Key="DialogJumpResultBehaviourLeftClick">Sol tık veya Enter tuşu</system:String>
<system:String x:Key="DialogJumpResultBehaviourRightClick">Sağ tık</system:String>
<system:String x:Key="dialogJumpFileResultBehaviour">Dialog Jump File Navigation Behaviour</system:String>
<system:String x:Key="dialogJumpFileResultBehaviour">Dialog Jump Dosya Gezinme Davranışı</system:String>
<system:String x:Key="dialogJumpFileResultBehaviourToolTip">Sonuç bir dosya yolu olduğunda Farklı Aç/Kaydet iletişim penceresinde gezinme davranışı</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPath">Dosya adı kutusuna tam yolu girin</system:String>
<system:String x:Key="DialogJumpFileResultBehaviourFullPathOpen">Dosya adı kutusuna tam yolu girin ve açın</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Klasörü Aç</system:String>
<system:String x:Key="advanced">Gelişmiş</system:String>
<system:String x:Key="logLevel">Günlük Düzeyi</system:String>
<system:String x:Key="LogLevelDEBUG">Hata ayıklama</system:String>
<system:String x:Key="LogLevelNONE">Sessiz</system:String>
<system:String x:Key="LogLevelERROR">Hata</system:String>
<system:String x:Key="LogLevelINFO">Bilgi</system:String>
<system:String x:Key="LogLevelDEBUG">Hata ayıklama</system:String>
<system:String x:Key="settingWindowFontTitle">Pencere Yazı Tipini Ayarla</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Dosya Açarken</system:String>
<system:String x:Key="fileManagerPathNotFound">'{0}' dosya yöneticisi '{1}' konumunda bulunamadı. Devam etmek ister misiniz?</system:String>
<system:String x:Key="fileManagerPathError">Dosya Yöneticisi Yol Hatası</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">İnternet Tarayıcı Seçenekleri</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Yeni Pencere</system:String>
<system:String x:Key="defaultBrowser_newTab">Yeni Sekme</system:String>
<system:String x:Key="defaultBrowser_parameter">Gizli Mod için Bağımsız Değişken</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Önceliği Ayarla</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Не вдалося видалити {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Не вдалося знайти файл plugin.json у розпакованому zip-файлі або цей шлях {0} не існує.</system:String>
<system:String x:Key="pluginExistAlreadyMessage">Вже існує плагін з таким самим ідентифікатором та версією, або версія цього плагіну вища за версію завантаженого.</system:String>
<system:String x:Key="errorCreatingSettingPanel">Помилка створення панелі налаштувань для плагіну {0}: {1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Магазин плагінів</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Відкрити теку</system:String>
<system:String x:Key="advanced">Розширені</system:String>
<system:String x:Key="logLevel">Рівень журналювання</system:String>
<system:String x:Key="LogLevelDEBUG">Налагодження</system:String>
<system:String x:Key="LogLevelNONE">Без звуку</system:String>
<system:String x:Key="LogLevelERROR">Помилка</system:String>
<system:String x:Key="LogLevelINFO">Інформація</system:String>
<system:String x:Key="LogLevelDEBUG">Налагодження</system:String>
<system:String x:Key="settingWindowFontTitle">Встановлення шрифту вікна</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">Аргумент для файлу</system:String>
<system:String x:Key="fileManagerPathNotFound">Не вдалося знайти файловий менеджер «{0}» за адресою «{1}». Чи бажаєте продовжити?</system:String>
<system:String x:Key="fileManagerPathError">Помилка шляху до файлового менеджера</system:String>
<system:String x:Key="fileManagerExplorer">Файловий провідник</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Типовий веббраузер</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">Нове вікно</system:String>
<system:String x:Key="defaultBrowser_newTab">Нова вкладка</system:String>
<system:String x:Key="defaultBrowser_parameter">Приватний режим</system:String>
<system:String x:Key="defaultBrowser_default">Типово</system:String>
<system:String x:Key="defaultBrowser_new_profile">Новий профіль</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Змінити пріоритет</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Tải tiện ích mở rộng</system:String>
@ -469,8 +470,10 @@
<system:String x:Key="userdatapathButton">Mở thư mục</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Lỗi</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -492,6 +495,7 @@
<system:String x:Key="fileManager_file_arg">Đối số cho tệp</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">Trình duyệt web tiêu chuẩn</system:String>
@ -502,6 +506,8 @@
<system:String x:Key="defaultBrowser_newWindow">Cửa sổ mới</system:String>
<system:String x:Key="defaultBrowser_newTab">Thẻ Mới</system:String>
<system:String x:Key="defaultBrowser_parameter">Chế độ riêng tư</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Thay đổi mức độ ưu tiên</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">卸载 {0} 失败</system:String>
<system:String x:Key="fileNotFoundMessage">无法从提取的zip文件中找到plugin.json或者此路径 {0} 不存在</system:String>
<system:String x:Key="pluginExistAlreadyMessage">已存在相同ID和版本的插件或者存在版本大于此下载的插件</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">插件商店</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">打开文件夹</system:String>
<system:String x:Key="advanced">高级</system:String>
<system:String x:Key="logLevel">日志等级</system:String>
<system:String x:Key="LogLevelDEBUG">调试</system:String>
<system:String x:Key="LogLevelNONE">静默</system:String>
<system:String x:Key="LogLevelERROR">错误</system:String>
<system:String x:Key="LogLevelINFO">信息</system:String>
<system:String x:Key="LogLevelDEBUG">调试</system:String>
<system:String x:Key="settingWindowFontTitle">设置窗口字体</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">选中文件路径参数</system:String>
<system:String x:Key="fileManagerPathNotFound">文件管理器 '{0}' 不能在 '{1}'中定位。您想要继续吗?</system:String>
<system:String x:Key="fileManagerPathError">文件管理器路径错误</system:String>
<system:String x:Key="fileManagerExplorer">文件资源管理器</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">默认浏览器</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">新窗口</system:String>
<system:String x:Key="defaultBrowser_newTab">新标签</system:String>
<system:String x:Key="defaultBrowser_parameter">隐身模式</system:String>
<system:String x:Key="defaultBrowser_default">默认</system:String>
<system:String x:Key="defaultBrowser_new_profile">新配置</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">更改优先级</system:String>

View file

@ -224,6 +224,7 @@
<system:String x:Key="failedToUninstallPluginTitle">Fail to uninstall {0}</system:String>
<system:String x:Key="fileNotFoundMessage">Unable to find plugin.json from the extracted zip file, or this path {0} does not exist</system:String>
<system:String x:Key="pluginExistAlreadyMessage">A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin</system:String>
<system:String x:Key="errorCreatingSettingPanel">Error creating setting panel for plugin {0}:{1}{2}</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">插件商店</system:String>
@ -467,8 +468,10 @@
<system:String x:Key="userdatapathButton">Open Folder</system:String>
<system:String x:Key="advanced">Advanced</system:String>
<system:String x:Key="logLevel">Log Level</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="LogLevelNONE">Silent</system:String>
<system:String x:Key="LogLevelERROR">Error</system:String>
<system:String x:Key="LogLevelINFO">Info</system:String>
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
<!-- Release Notes Window -->
@ -490,6 +493,7 @@
<system:String x:Key="fileManager_file_arg">檔案參數</system:String>
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
<system:String x:Key="fileManagerExplorer">File Explorer</system:String>
<!-- DefaultBrowser Setting Dialog -->
<system:String x:Key="defaultBrowserTitle">預設瀏覽器</system:String>
@ -500,6 +504,8 @@
<system:String x:Key="defaultBrowser_newWindow">新增視窗</system:String>
<system:String x:Key="defaultBrowser_newTab">新增分頁</system:String>
<system:String x:Key="defaultBrowser_parameter">無痕模式</system:String>
<system:String x:Key="defaultBrowser_default">Default</system:String>
<system:String x:Key="defaultBrowser_new_profile">New Profile</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">更改優先度</system:String>

View file

@ -6,7 +6,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
Name="FlowMainWindow"
Title="Flow Launcher"

View file

@ -1,7 +1,8 @@
using System;
using System;
using System.ComponentModel;
using System.Linq;
using System.Media;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@ -24,7 +25,8 @@ using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Plugin.SharedModels;
using Flow.Launcher.ViewModel;
using ModernWpf.Controls;
using iNKORE.UI.WPF.Modern;
using iNKORE.UI.WPF.Modern.Controls;
using DataObject = System.Windows.DataObject;
using Key = System.Windows.Input.Key;
using MouseButtons = System.Windows.Forms.MouseButtons;
@ -61,8 +63,9 @@ namespace Flow.Launcher
private bool _isArrowKeyPressed = false;
// Window Sound Effects
private MediaPlayer animationSoundWMP;
private SoundPlayer animationSoundWPF;
private MediaPlayer _animationSoundWMP;
private SoundPlayer _animationSoundWPF;
private readonly Lock _soundLock = new();
// Window WndProc
private HwndSource _hwndSource;
@ -93,6 +96,7 @@ namespace Flow.Launcher
UpdatePosition();
InitSoundEffects();
RegisterSoundEffectsEvent();
DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste);
_viewModel.ActualApplicationThemeChanged += ViewModel_ActualApplicationThemeChanged;
}
@ -145,8 +149,8 @@ namespace Flow.Launcher
_settings.ReleaseNotesVersion = Constant.Version;
// Show release note popup with button
App.API.ShowMsgWithButton(
string.Format(App.API.GetTranslation("appUpdateTitle"), Constant.Version),
App.API.GetTranslation("appUpdateButtonContent"),
Localize.appUpdateTitle(Constant.Version),
Localize.appUpdateButtonContent(),
() =>
{
Application.Current.Dispatcher.Invoke(() =>
@ -188,11 +192,11 @@ namespace Flow.Launcher
// Initialize color scheme
if (_settings.ColorScheme == Constant.Light)
{
ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Light;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light;
}
else if (_settings.ColorScheme == Constant.Dark)
{
ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Dark;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
}
// Initialize position
@ -666,16 +670,6 @@ namespace Flow.Launcher
handled = true;
}
break;
case Win32Helper.WM_POWERBROADCAST: // Handle power broadcast messages
// https://learn.microsoft.com/en-us/windows/win32/power/wm-powerbroadcast
if (wParam.ToInt32() == Win32Helper.PBT_APMRESUMEAUTOMATIC)
{
// Fix for sound not playing after sleep / hibernate
// https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps
InitSoundEffects();
}
handled = true;
break;
}
return IntPtr.Zero;
@ -687,31 +681,78 @@ namespace Flow.Launcher
private void InitSoundEffects()
{
if (_settings.WMPInstalled)
lock (_soundLock)
{
animationSoundWMP?.Close();
animationSoundWMP = new MediaPlayer();
animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav"));
}
else
{
animationSoundWPF?.Dispose();
animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav");
animationSoundWPF.Load();
if (_settings.WMPInstalled)
{
_animationSoundWMP?.Close();
_animationSoundWMP = new MediaPlayer();
_animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav"));
}
else
{
_animationSoundWPF?.Dispose();
_animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav");
_animationSoundWPF.Load();
}
}
}
private void SoundPlay()
{
if (_settings.WMPInstalled)
lock (_soundLock)
{
animationSoundWMP.Position = TimeSpan.Zero;
animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
animationSoundWMP.Play();
if (_settings.WMPInstalled)
{
_animationSoundWMP.Position = TimeSpan.Zero;
_animationSoundWMP.Volume = _settings.SoundVolume / 100.0;
_animationSoundWMP.Play();
}
else
{
_animationSoundWPF.Play();
}
}
else
}
private void RegisterSoundEffectsEvent()
{
// Fix for sound not playing after sleep / hibernate for both modern standby and legacy standby
// https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps
try
{
animationSoundWPF.Play();
Win32Helper.RegisterSleepModeListener(() =>
{
if (Application.Current == null)
{
return;
}
// We must run InitSoundEffects on UI thread because MediaPlayer is a DispatcherObject
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.Invoke(InitSoundEffects);
return;
}
InitSoundEffects();
});
}
catch (Exception e)
{
App.API.LogException(ClassName, "Failed to register sound effect event", e);
}
}
private static void UnregisterSoundEffectsEvent()
{
try
{
Win32Helper.UnregisterSleepModeListener();
}
catch (Exception e)
{
App.API.LogException(ClassName, "Failed to unregister sound effect event", e);
}
}
@ -753,12 +794,12 @@ namespace Flow.Launcher
private void UpdateNotifyIconText()
{
var menu = _contextMenu;
((MenuItem)menu.Items[0]).Header = App.API.GetTranslation("iconTrayOpen") +
((MenuItem)menu.Items[0]).Header = Localize.iconTrayOpen() +
" (" + _settings.Hotkey + ")";
((MenuItem)menu.Items[1]).Header = App.API.GetTranslation("GameMode");
((MenuItem)menu.Items[2]).Header = App.API.GetTranslation("PositionReset");
((MenuItem)menu.Items[3]).Header = App.API.GetTranslation("iconTraySettings");
((MenuItem)menu.Items[4]).Header = App.API.GetTranslation("iconTrayExit");
((MenuItem)menu.Items[1]).Header = Localize.GameMode();
((MenuItem)menu.Items[2]).Header = Localize.PositionReset();
((MenuItem)menu.Items[3]).Header = Localize.iconTraySettings();
((MenuItem)menu.Items[4]).Header = Localize.iconTrayExit();
}
private void InitializeContextMenu()
@ -768,31 +809,31 @@ namespace Flow.Launcher
var openIcon = new FontIcon { Glyph = "\ue71e" };
var open = new MenuItem
{
Header = App.API.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")",
Header = Localize.iconTrayOpen() + " (" + _settings.Hotkey + ")",
Icon = openIcon
};
var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" };
var gamemode = new MenuItem
{
Header = App.API.GetTranslation("GameMode"),
Header = Localize.GameMode(),
Icon = gamemodeIcon
};
var positionresetIcon = new FontIcon { Glyph = "\ue73f" };
var positionreset = new MenuItem
{
Header = App.API.GetTranslation("PositionReset"),
Header = Localize.PositionReset(),
Icon = positionresetIcon
};
var settingsIcon = new FontIcon { Glyph = "\ue713" };
var settings = new MenuItem
{
Header = App.API.GetTranslation("iconTraySettings"),
Header = Localize.iconTraySettings(),
Icon = settingsIcon
};
var exitIcon = new FontIcon { Glyph = "\ue7e8" };
var exit = new MenuItem
{
Header = App.API.GetTranslation("iconTrayExit"),
Header = Localize.iconTrayExit(),
Icon = exitIcon
};
@ -802,8 +843,8 @@ namespace Flow.Launcher
settings.Click += (o, e) => App.API.OpenSettingDialog();
exit.Click += (o, e) => Close();
gamemode.ToolTip = App.API.GetTranslation("GameModeToolTip");
positionreset.ToolTip = App.API.GetTranslation("PositionResetToolTip");
gamemode.ToolTip = Localize.GameModeToolTip();
positionreset.ToolTip = Localize.PositionResetToolTip();
_contextMenu.Items.Add(open);
_contextMenu.Items.Add(gamemode);
@ -1436,9 +1477,10 @@ namespace Flow.Launcher
{
_hwndSource?.Dispose();
_notifyIcon?.Dispose();
animationSoundWMP?.Close();
animationSoundWPF?.Dispose();
_animationSoundWMP?.Close();
_animationSoundWPF?.Dispose();
_viewModel.ActualApplicationThemeChanged -= ViewModel_ActualApplicationThemeChanged;
UnregisterSoundEffectsEvent();
}
_disposed = true;

View file

@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
Title="{DynamicResource updateAllPluginsButtonContent}"
Width="530"
Background="{DynamicResource PopuBGColor}"
@ -66,13 +67,13 @@
Text="{DynamicResource updateAllPluginsButtonContent}"
TextAlignment="Left" />
<ScrollViewer
<ui:ScrollViewerEx
MaxHeight="300"
Margin="0 5 0 5"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="UpdatePluginStackPanel" />
</ScrollViewer>
</ui:ScrollViewerEx>
<Rectangle
Height="1"

View file

@ -23,7 +23,7 @@ namespace Flow.Launcher
{
var checkBox = new CheckBox
{
Content = string.Format(App.API.GetTranslation("updatePluginCheckboxContent"), plugin.Name, plugin.CurrentVersion, plugin.NewVersion),
Content = Localize.updatePluginCheckboxContent(plugin.Name, plugin.CurrentVersion, plugin.NewVersion),
IsChecked = true,
Margin = new Thickness(0, 5, 0, 5),
Tag = plugin,
@ -50,10 +50,7 @@ namespace Flow.Launcher
{
if (sender is not CheckBox cb) return;
if (cb.Tag is not PluginUpdateInfo plugin) return;
if (Plugins.Contains(plugin))
{
Plugins.Remove(plugin);
}
Plugins.Remove(plugin);
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
@ -66,7 +63,7 @@ namespace Flow.Launcher
{
if (Plugins.Count == 0)
{
App.API.ShowMsgBox(App.API.GetTranslation("updatePluginNoSelected"));
App.API.ShowMsgBox(Localize.updatePluginNoSelected());
return;
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
@ -31,8 +31,8 @@ using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Plugin.SharedModels;
using Flow.Launcher.ViewModel;
using iNKORE.UI.WPF.Modern;
using JetBrains.Annotations;
using ModernWpf;
using Squirrel;
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
@ -74,7 +74,6 @@ namespace Flow.Launcher
_mainVM.ChangeQueryText(query, requery);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
public void RestartApp()
{
_mainVM.Hide();
@ -179,20 +178,20 @@ namespace Flow.Launcher
Clipboard.SetFileDropList(paths);
});
if (exception == null)
{
if (showDefaultNotification)
{
ShowMsg(
$"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}",
GetTranslation("completedSuccessfully"));
$"{Localize.copy()} {(isFile ? Localize.fileTitle(): Localize.folderTitle())}",
Localize.completedSuccessfully());
}
}
else
{
LogException(nameof(PublicAPIInstance), "Failed to copy file/folder to clipboard", exception);
ShowMsgError(GetTranslation("failedToCopy"));
ShowMsgError(Localize.failedToCopy());
}
}
else
@ -210,15 +209,15 @@ namespace Flow.Launcher
if (showDefaultNotification)
{
ShowMsg(
$"{GetTranslation("copy")} {GetTranslation("textTitle")}",
GetTranslation("completedSuccessfully"));
$"{Localize.copy()} {Localize.textTitle()}",
Localize.completedSuccessfully());
}
}
else
{
LogException(nameof(PublicAPIInstance), "Failed to copy text to clipboard", exception);
ShowMsgError(GetTranslation("failedToCopy"));
}
ShowMsgError(Localize.failedToCopy());
}
}
}
@ -327,7 +326,7 @@ namespace Flow.Launcher
((PluginJsonStorage<T>)_pluginJsonStorages[type]).Save();
}
public void OpenDirectory(string directoryPath, string fileNameOrFilePath = null)
{
try
@ -394,24 +393,30 @@ namespace Flow.Launcher
}
catch (Win32Exception ex) when (ex.NativeErrorCode == 2)
{
LogError(ClassName, "File Manager not found");
LogException(ClassName, "File Manager not found", ex);
ShowMsgError(
GetTranslation("fileManagerNotFoundTitle"),
string.Format(GetTranslation("fileManagerNotFound"), ex.Message)
Localize.fileManagerNotFoundTitle(),
Localize.fileManagerNotFound()
);
}
catch (Exception ex)
{
LogException(ClassName, "Failed to open folder", ex);
ShowMsgError(
GetTranslation("errorTitle"),
string.Format(GetTranslation("folderOpenError"), ex.Message)
Localize.errorTitle(),
Localize.folderOpenError()
);
}
}
private void OpenUri(Uri uri, bool? inPrivate = null, bool forceBrowser = false)
{
if (uri.IsFile && !FilesFolders.FileOrLocationExists(uri.LocalPath))
{
ShowMsgError(Localize.errorTitle(), Localize.fileNotFoundError(uri.LocalPath));
return;
}
if (forceBrowser || uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
{
var browserInfo = _settings.CustomBrowser;
@ -434,20 +439,26 @@ namespace Flow.Launcher
var tabOrWindow = browserInfo.OpenInTab ? "tab" : "window";
LogException(ClassName, $"Failed to open URL in browser {tabOrWindow}: {path}, {inPrivate ?? browserInfo.EnablePrivate}, {browserInfo.PrivateArg}", e);
ShowMsgError(
GetTranslation("errorTitle"),
GetTranslation("browserOpenError")
Localize.errorTitle(),
Localize.browserOpenError()
);
}
}
else
{
Process.Start(new ProcessStartInfo()
try
{
FileName = uri.AbsoluteUri,
UseShellExecute = true
})?.Dispose();
return;
Process.Start(new ProcessStartInfo()
{
FileName = uri.AbsoluteUri,
UseShellExecute = true
})?.Dispose();
}
catch (Exception e)
{
LogException(ClassName, $"Failed to open: {uri.AbsoluteUri}", e);
ShowMsgError(Localize.errorTitle(), e.Message);
}
}
}
@ -481,7 +492,7 @@ namespace Flow.Launcher
OpenUri(appUri);
}
public void ToggleGameMode()
public void ToggleGameMode()
{
_mainVM.ToggleGameMode();
}

View file

@ -7,7 +7,7 @@
xmlns:local="clr-namespace:Flow.Launcher"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
Title="{DynamicResource releaseNotes}"
Width="940"
@ -16,6 +16,7 @@
MinHeight="600"
Background="{DynamicResource PopuBGColor}"
Closed="Window_Closed"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Foreground="{DynamicResource PopupTextColor}"
Loaded="Window_Loaded"
ResizeMode="CanResize"
@ -44,7 +45,7 @@
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="24" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- TitleBar and Control -->
@ -161,18 +162,23 @@
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="5"
Margin="18 0 18 0">
<cc:HyperLink x:Name="SeeMore" Text="{DynamicResource seeMoreReleaseNotes}" />
Margin="6 0 18 0">
<ui:HyperlinkButton
x:Name="SeeMore"
Content="{DynamicResource seeMoreReleaseNotes}"
NavigateUri="{Binding ReleaseNotes}" />
</Grid>
<!-- Do not use scroll function of MarkdownViewer because it does not support smooth scroll -->
<ScrollViewer
<ui:ScrollViewerEx
x:Name="MarkdownScrollViewer"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="5"
Width="500"
Height="500">
Height="500"
Margin="15 0 0 0"
Padding="0 0 15 0"
HorizontalAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@ -193,11 +199,11 @@
VerticalScrollBarVisibility="Disabled"
Visibility="Collapsed" />
</Grid>
</ScrollViewer>
</ui:ScrollViewerEx>
<!-- This Grid is for display progress ring and refresh button. -->
<!-- And it is also for changing the size of the MarkdownViewer. -->
<!-- Because VerticalAlignment="Stretch" can cause size issue with MarkdownScrollViewer. -->
<!-- Because VerticalAlignment="Stretch" can cause height issue with MarkdownScrollViewer. -->
<Grid
Grid.Row="2"
Grid.Column="0"

View file

@ -10,27 +10,27 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Flow.Launcher.Infrastructure.Http;
using iNKORE.UI.WPF.Modern;
namespace Flow.Launcher
{
public partial class ReleaseNotesWindow : Window
{
private static readonly string ReleaseNotes = Properties.Settings.Default.GithubRepo + "/releases";
public string ReleaseNotes => Properties.Settings.Default.GithubRepo + "/releases";
public ReleaseNotesWindow()
{
InitializeComponent();
SeeMore.Uri = ReleaseNotes;
ModernWpf.ThemeManager.Current.ActualApplicationThemeChanged += ThemeManager_ActualApplicationThemeChanged;
ThemeManager.Current.ActualApplicationThemeChanged += ThemeManager_ActualApplicationThemeChanged;
}
#region Window Events
private void ThemeManager_ActualApplicationThemeChanged(ModernWpf.ThemeManager sender, object args)
private void ThemeManager_ActualApplicationThemeChanged(ThemeManager sender, object args)
{
Application.Current.Dispatcher.Invoke(() =>
{
if (ModernWpf.ThemeManager.Current.ActualApplicationTheme == ModernWpf.ApplicationTheme.Light)
if (ThemeManager.Current.ActualApplicationTheme == ApplicationTheme.Light)
{
MarkdownViewer.MarkdownStyle = (Style)Application.Current.Resources["DocumentStyleGithubLikeLight"];
MarkdownViewer.Foreground = Brushes.Black;
@ -58,7 +58,7 @@ namespace Flow.Launcher
private void Window_Closed(object sender, EventArgs e)
{
ModernWpf.ThemeManager.Current.ActualApplicationThemeChanged -= ThemeManager_ActualApplicationThemeChanged;
ThemeManager.Current.ActualApplicationThemeChanged -= ThemeManager_ActualApplicationThemeChanged;
}
#endregion
@ -132,8 +132,8 @@ namespace Flow.Launcher
RefreshButton.Visibility = Visibility.Visible;
MarkdownViewer.Visibility = Visibility.Collapsed;
App.API.ShowMsgError(
App.API.GetTranslation("checkNetworkConnectionTitle"),
App.API.GetTranslation("checkNetworkConnectionSubTitle"));
Localize.checkNetworkConnectionTitle(),
Localize.checkNetworkConnectionSubTitle());
}
else
{
@ -147,7 +147,6 @@ namespace Flow.Launcher
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
MarkdownScrollViewer.Height = e.NewSize.Height;
MarkdownScrollViewer.Width = e.NewSize.Width;
}
private void MarkdownViewer_MouseWheel(object sender, MouseWheelEventArgs e)

View file

@ -48,10 +48,10 @@ namespace Flow.Launcher
_ => Constant.IssuesUrl
};
var paragraph = Hyperlink(App.API.GetTranslation("reportWindow_please_open_issue"), websiteUrl);
paragraph.Inlines.Add(string.Format(App.API.GetTranslation("reportWindow_upload_log"), log.FullName));
var paragraph = Hyperlink(Localize.reportWindow_please_open_issue(), websiteUrl);
paragraph.Inlines.Add(Localize.reportWindow_upload_log(log.FullName));
paragraph.Inlines.Add("\n");
paragraph.Inlines.Add(App.API.GetTranslation("reportWindow_copy_below"));
paragraph.Inlines.Add(Localize.reportWindow_copy_below());
ErrorTextbox.Document.Blocks.Add(paragraph);
StringBuilder content = new StringBuilder();

View file

@ -1,139 +0,0 @@
<UserControl
x:Class="Flow.Launcher.Resources.Controls.Card"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Flow.Launcher.Resources.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ui="http://schemas.modernwpf.com/2019"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Border x:Name="BD" HorizontalAlignment="Stretch">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="{DynamicResource Color00B}" />
<Setter Property="BorderBrush" Value="{DynamicResource Color03B}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="MinHeight" Value="68" />
<Setter Property="Padding" Value="0 15 0 15" />
<Setter Property="Margin" Value="0 4 0 0" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="Inside">
<Setter Property="BorderThickness" Value="0 1 0 0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Margin" Value="0 0 0 0" />
<Setter Property="Background" Value="Transparent" />
</DataTrigger>
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="InsideFit">
<Setter Property="BorderThickness" Value="0 1 0 0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Margin" Value="0 0 0 0" />
<Setter Property="Padding" Value="35 0 26 0" />
<Setter Property="Background" Value="Transparent" />
</DataTrigger>
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="First">
<Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</DataTrigger>
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="Middle">
<Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0 1 0 0" />
</DataTrigger>
<DataTrigger Binding="{Binding Type, RelativeSource={RelativeSource AncestorType=local:Card}}" Value="Last">
<Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0 1 0 0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="auto"
MinWidth="20"
MaxWidth="60" />
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="Auto" MinWidth="30" />
</Grid.ColumnDefinitions>
<ContentControl
Grid.Row="0"
Grid.Column="2"
Margin="0 0 16 0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
<StackPanel>
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Width" Value="Auto" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
</StackPanel.Style>
<TextBlock x:Name="ItemTitle" Text="{Binding Title, RelativeSource={RelativeSource AncestorType=local:Card}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
<Setter Property="Margin" Value="0 0 0 0" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock x:Name="SubTitle" Text="{Binding Sub, RelativeSource={RelativeSource AncestorType=local:Card}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SubTitle, Path=Text}" Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Foreground" Value="{DynamicResource Color04B}" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Margin" Value="0 0 0 0" />
<Setter Property="Padding" Value="0 0 24 0" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
<TextBlock x:Name="ItemIcon" Text="{Binding Icon, RelativeSource={RelativeSource AncestorType=local:Card}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ItemIcon, Path=Text}" Value="{x:Static sys:String.Empty}">
<Setter Property="Margin" Value="24 0 0 0" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Grid.Column" Value="0" />
<Setter Property="Margin" Value="24 0 16 0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontSize" Value="20" />
<Setter Property="FontFamily" Value="/Resources/#Segoe Fluent Icons" />
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Border>
</ControlTemplate>
</UserControl.Template>
</UserControl>

View file

@ -1,67 +0,0 @@
using System.Windows;
using UserControl = System.Windows.Controls.UserControl;
namespace Flow.Launcher.Resources.Controls
{
public partial class Card : UserControl
{
public enum CardType
{
Default,
Inside,
InsideFit,
First,
Middle,
Last
}
public Card()
{
InitializeComponent();
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(nameof(Title), typeof(string), typeof(Card), new PropertyMetadata(string.Empty));
public string Sub
{
get { return (string)GetValue(SubProperty); }
set { SetValue(SubProperty, value); }
}
public static readonly DependencyProperty SubProperty =
DependencyProperty.Register(nameof(Sub), typeof(string), typeof(Card), new PropertyMetadata(string.Empty));
public string Icon
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(nameof(Icon), typeof(string), typeof(Card), new PropertyMetadata(string.Empty));
/// <summary>
/// Gets or sets additional content for the UserControl
/// </summary>
public object AdditionalContent
{
get { return (object)GetValue(AdditionalContentProperty); }
set { SetValue(AdditionalContentProperty, value); }
}
public static readonly DependencyProperty AdditionalContentProperty =
DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(Card),
new PropertyMetadata(null));
public CardType Type
{
get { return (CardType)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register(nameof(Type), typeof(CardType), typeof(Card),
new PropertyMetadata(CardType.Default));
}
}

View file

@ -1,32 +0,0 @@
<UserControl x:Class="Flow.Launcher.Resources.Controls.CardGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance cc:CardGroup}"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="cc:Card" x:Key="FirstStyle">
<Setter Property="cc:CardGroup.Position" Value="First" />
</Style>
<Style TargetType="cc:Card" x:Key="MiddleStyle">
<Setter Property="cc:CardGroup.Position" Value="Middle" />
</Style>
<Style TargetType="cc:Card" x:Key="LastStyle">
<Setter Property="cc:CardGroup.Position" Value="Last" />
</Style>
<cc:CardGroupCardStyleSelector
x:Key="CardStyleSelector"
FirstStyle="{StaticResource FirstStyle}"
MiddleStyle="{StaticResource MiddleStyle}"
LastStyle="{StaticResource LastStyle}" />
</UserControl.Resources>
<Border Background="{DynamicResource Color00B}" BorderBrush="{DynamicResource Color03B}" BorderThickness="1"
CornerRadius="5">
<ItemsControl ItemsSource="{Binding Content, RelativeSource={RelativeSource AncestorType=cc:CardGroup}}"
ItemContainerStyleSelector="{StaticResource CardStyleSelector}" />
</Border>
</UserControl>

View file

@ -1,47 +0,0 @@
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace Flow.Launcher.Resources.Controls;
public partial class CardGroup : UserControl
{
public enum CardGroupPosition
{
NotInGroup,
First,
Middle,
Last
}
public new ObservableCollection<Card> Content
{
get { return (ObservableCollection<Card>)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static new readonly DependencyProperty ContentProperty =
DependencyProperty.Register(nameof(Content), typeof(ObservableCollection<Card>), typeof(CardGroup));
public static readonly DependencyProperty PositionProperty = DependencyProperty.RegisterAttached(
"Position", typeof(CardGroupPosition), typeof(CardGroup),
new FrameworkPropertyMetadata(CardGroupPosition.NotInGroup, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetPosition(UIElement element, CardGroupPosition value)
{
element.SetValue(PositionProperty, value);
}
public static CardGroupPosition GetPosition(UIElement element)
{
return (CardGroupPosition)element.GetValue(PositionProperty);
}
public CardGroup()
{
InitializeComponent();
Content = new ObservableCollection<Card>();
}
}

View file

@ -1,21 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace Flow.Launcher.Resources.Controls;
public class CardGroupCardStyleSelector : StyleSelector
{
public Style FirstStyle { get; set; }
public Style MiddleStyle { get; set; }
public Style LastStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
var itemsControl = ItemsControl.ItemsControlFromItemContainer(container);
var index = itemsControl.ItemContainerGenerator.IndexFromContainer(container);
if (index == 0) return FirstStyle;
if (index == itemsControl.Items.Count - 1) return LastStyle;
return MiddleStyle;
}
}

View file

@ -0,0 +1,253 @@
using iNKORE.UI.WPF.Modern.Controls;
using iNKORE.UI.WPF.Modern.Controls.Helpers;
using iNKORE.UI.WPF.Modern.Controls.Primitives;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Flow.Launcher.Resources.Controls
{
// TODO: Use IsScrollAnimationEnabled property in future: https://github.com/iNKORE-NET/UI.WPF.Modern/pull/347
public class CustomScrollViewerEx : ScrollViewer
{
private double LastVerticalLocation = 0;
private double LastHorizontalLocation = 0;
public CustomScrollViewerEx()
{
Loaded += OnLoaded;
var valueSource = DependencyPropertyHelper.GetValueSource(this, AutoPanningMode.IsEnabledProperty).BaseValueSource;
if (valueSource == BaseValueSource.Default)
{
AutoPanningMode.SetIsEnabled(this, true);
}
}
#region Orientation
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register(
nameof(Orientation),
typeof(Orientation),
typeof(CustomScrollViewerEx),
new PropertyMetadata(Orientation.Vertical));
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
#endregion
#region AutoHideScrollBars
public static readonly DependencyProperty AutoHideScrollBarsProperty =
ScrollViewerHelper.AutoHideScrollBarsProperty
.AddOwner(
typeof(CustomScrollViewerEx),
new PropertyMetadata(true, OnAutoHideScrollBarsChanged));
public bool AutoHideScrollBars
{
get => (bool)GetValue(AutoHideScrollBarsProperty);
set => SetValue(AutoHideScrollBarsProperty, value);
}
private static void OnAutoHideScrollBarsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CustomScrollViewerEx sv)
{
sv.UpdateVisualState();
}
}
#endregion
private void OnLoaded(object sender, RoutedEventArgs e)
{
LastVerticalLocation = VerticalOffset;
LastHorizontalLocation = HorizontalOffset;
UpdateVisualState(false);
}
/// <inheritdoc/>
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
if (Style == null && ReadLocalValue(StyleProperty) == DependencyProperty.UnsetValue)
{
SetResourceReference(StyleProperty, typeof(ScrollViewer));
}
}
/// <inheritdoc/>
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
var Direction = GetDirection();
ScrollViewerBehavior.SetIsAnimating(this, true);
if (Direction == Orientation.Vertical)
{
if (ScrollableHeight > 0)
{
e.Handled = true;
}
var WheelChange = e.Delta * (ViewportHeight / 1.5) / ActualHeight;
var newOffset = LastVerticalLocation - WheelChange;
if (newOffset < 0)
{
newOffset = 0;
}
if (newOffset > ScrollableHeight)
{
newOffset = ScrollableHeight;
}
if (newOffset == LastVerticalLocation)
{
return;
}
ScrollToVerticalOffset(LastVerticalLocation);
ScrollToValue(newOffset, Direction);
LastVerticalLocation = newOffset;
}
else
{
if (ScrollableWidth > 0)
{
e.Handled = true;
}
var WheelChange = e.Delta * (ViewportWidth / 1.5) / ActualWidth;
var newOffset = LastHorizontalLocation - WheelChange;
if (newOffset < 0)
{
newOffset = 0;
}
if (newOffset > ScrollableWidth)
{
newOffset = ScrollableWidth;
}
if (newOffset == LastHorizontalLocation)
{
return;
}
ScrollToHorizontalOffset(LastHorizontalLocation);
ScrollToValue(newOffset, Direction);
LastHorizontalLocation = newOffset;
}
}
/// <inheritdoc/>
protected override void OnScrollChanged(ScrollChangedEventArgs e)
{
base.OnScrollChanged(e);
if (!ScrollViewerBehavior.GetIsAnimating(this))
{
LastVerticalLocation = VerticalOffset;
LastHorizontalLocation = HorizontalOffset;
}
}
private Orientation GetDirection()
{
var isShiftDown = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
if (Orientation == Orientation.Horizontal)
{
return isShiftDown ? Orientation.Vertical : Orientation.Horizontal;
}
else
{
return isShiftDown ? Orientation.Horizontal : Orientation.Vertical;
}
}
/// <summary>
/// Causes the <see cref="ScrollViewerEx"/> to load a new view into the viewport using the specified offsets and zoom factor.
/// </summary>
/// <param name="horizontalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableWidth"/> that specifies the distance the content should be scrolled horizontally.</param>
/// <param name="verticalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableHeight"/> that specifies the distance the content should be scrolled vertically.</param>
/// <param name="zoomFactor">A value between MinZoomFactor and MaxZoomFactor that specifies the required target ZoomFactor.</param>
/// <returns><see langword="true"/> if the view is changed; otherwise, <see langword="false"/>.</returns>
public bool ChangeView(double? horizontalOffset, double? verticalOffset, float? zoomFactor)
{
return ChangeView(horizontalOffset, verticalOffset, zoomFactor, false);
}
/// <summary>
/// Causes the <see cref="ScrollViewerEx"/> to load a new view into the viewport using the specified offsets and zoom factor, and optionally disables scrolling animation.
/// </summary>
/// <param name="horizontalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableWidth"/> that specifies the distance the content should be scrolled horizontally.</param>
/// <param name="verticalOffset">A value between 0 and <see cref="ScrollViewer.ScrollableHeight"/> that specifies the distance the content should be scrolled vertically.</param>
/// <param name="zoomFactor">A value between MinZoomFactor and MaxZoomFactor that specifies the required target ZoomFactor.</param>
/// <param name="disableAnimation"><see langword="true"/> to disable zoom/pan animations while changing the view; otherwise, <see langword="false"/>. The default is false.</param>
/// <returns><see langword="true"/> if the view is changed; otherwise, <see langword="false"/>.</returns>
public bool ChangeView(double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation)
{
if (disableAnimation)
{
if (horizontalOffset.HasValue)
{
ScrollToHorizontalOffset(horizontalOffset.Value);
}
if (verticalOffset.HasValue)
{
ScrollToVerticalOffset(verticalOffset.Value);
}
}
else
{
if (horizontalOffset.HasValue)
{
ScrollToHorizontalOffset(LastHorizontalLocation);
ScrollToValue(Math.Min(ScrollableWidth, horizontalOffset.Value), Orientation.Horizontal);
LastHorizontalLocation = horizontalOffset.Value;
}
if (verticalOffset.HasValue)
{
ScrollToVerticalOffset(LastVerticalLocation);
ScrollToValue(Math.Min(ScrollableHeight, verticalOffset.Value), Orientation.Vertical);
LastVerticalLocation = verticalOffset.Value;
}
}
return true;
}
private void ScrollToValue(double value, Orientation Direction)
{
if (Direction == Orientation.Vertical)
{
ScrollToVerticalOffset(value);
}
else
{
ScrollToHorizontalOffset(value);
}
ScrollViewerBehavior.SetIsAnimating(this, false);
}
private void UpdateVisualState(bool useTransitions = true)
{
var stateName = AutoHideScrollBars ? "NoIndicator" : "MouseIndicator";
VisualStateManager.GoToState(this, stateName, useTransitions);
}
}
}

View file

@ -1,312 +0,0 @@
<UserControl
x:Class="Flow.Launcher.Resources.Controls.ExCard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Flow.Launcher.Resources.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ui="http://schemas.modernwpf.com/2019"
mc:Ignorable="d">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Expander
x:Name="expanderHeader"
Padding="0"
BorderThickness="1"
SnapsToDevicePixels="False">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="Background" Value="{DynamicResource Color00B}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="BorderBrush" Value="{DynamicResource Color03B}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<Border
x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="5"
SnapsToDevicePixels="true">
<DockPanel>
<ToggleButton
x:Name="HeaderSite"
MinWidth="0"
MinHeight="68"
Margin="0,0,0,0"
Padding="0,0,0,0"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
DockPanel.Dock="Top"
FocusVisualStyle="{DynamicResource ExpanderHeaderFocusVisual}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontStretch="{TemplateBinding FontStretch}"
FontStyle="{TemplateBinding FontStyle}"
FontWeight="{TemplateBinding FontWeight}"
Foreground="{TemplateBinding Foreground}"
IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=Expander}}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border
x:Name="ToggleBtn"
Padding="{TemplateBinding Padding}"
Background="{DynamicResource Color00B}"
ClipToBounds="True"
CornerRadius="5">
<Grid SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<ContentPresenter
Grid.Column="0"
Margin="0,0,0,0"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
RecognizesAccessKey="True"
SnapsToDevicePixels="True" />
<Grid
x:Name="ChevronGrid"
Grid.Column="2"
Margin="0,0,18,0"
VerticalAlignment="Center"
Background="Transparent"
RenderTransformOrigin="0.5, 0.5">
<Grid.RenderTransform>
<RotateTransform Angle="0" />
</Grid.RenderTransform>
<Ellipse
x:Name="circle"
Width="19"
Height="19"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stroke="Transparent" />
<Path
x:Name="arrow"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 1,1.5 L 4.5,5 L 8,1.5"
SnapsToDevicePixels="false"
Stroke="#666"
StrokeThickness="1" />
</Grid>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="arrow" Property="Data" Value="M 1,4.5 L 4.5,1 L 8,4.5" />
<Setter TargetName="ToggleBtn" Property="CornerRadius" Value="5 5 0 0" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="circle" Property="Stroke" Value="Transparent" />
<Setter TargetName="arrow" Property="Stroke" Value="{DynamicResource Color05B}" />
<Setter TargetName="ToggleBtn" Property="Background" Value="{DynamicResource CustomExpanderHover}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="circle" Property="Stroke" Value="Transparent" />
<Setter TargetName="circle" Property="StrokeThickness" Value="1.5" />
<Setter TargetName="arrow" Property="Stroke" Value="{DynamicResource Color17B}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Border x:Name="ContentPresenterBorder" BorderThickness="0">
<ContentPresenter
x:Name="ExpandSite"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
DockPanel.Dock="Bottom"
Focusable="false" />
<Border.LayoutTransform>
<ScaleTransform ScaleY="0" />
</Border.LayoutTransform>
</Border>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter TargetName="ExpandSite" Property="Visibility" Value="Visible" />
<Setter TargetName="ContentPresenterBorder" Property="BorderThickness" Value="0,0,0,0" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.LayoutTransform).(ScaleTransform.ScaleY)"
From="0.0"
To="1.0"
Duration="00:00:00.00" />
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.Opacity)"
From="0.0"
To="1.0"
Duration="00:00:00.00" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.LayoutTransform).(ScaleTransform.ScaleY)"
From="1.0"
To="0"
Duration="00:00:00.00" />
<!-- Animation 00:00:00.167 -->
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.Opacity)"
From="1.0"
To="0.0"
Duration="00:00:00.00" />
<!-- Animation 00:00:00.167 -->
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="ExpandDirection" Value="Right">
<Setter TargetName="ExpandSite" Property="DockPanel.Dock" Value="Right" />
<Setter TargetName="HeaderSite" Property="DockPanel.Dock" Value="Left" />
<Setter TargetName="HeaderSite" Property="Style" Value="{StaticResource ExpanderRightHeaderStyle}" />
</Trigger>
<Trigger Property="ExpandDirection" Value="Up">
<Setter TargetName="ExpandSite" Property="DockPanel.Dock" Value="Top" />
<Setter TargetName="HeaderSite" Property="DockPanel.Dock" Value="Bottom" />
<Setter TargetName="HeaderSite" Property="Style" Value="{StaticResource ExpanderUpHeaderStyle}" />
</Trigger>
<Trigger Property="ExpandDirection" Value="Left">
<Setter TargetName="ExpandSite" Property="DockPanel.Dock" Value="Left" />
<Setter TargetName="HeaderSite" Property="DockPanel.Dock" Value="Right" />
<Setter TargetName="HeaderSite" Property="Style" Value="{StaticResource ExpanderLeftHeaderStyle}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Expander.Style>
<Expander.Header>
<Border Margin="0" Padding="0,12,0,12">
<Grid Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}, Path=ActualWidth}" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="auto"
MinWidth="20"
MaxWidth="60" />
<ColumnDefinition Width="7*" />
<ColumnDefinition Width="Auto" MinWidth="30" />
<ColumnDefinition Width="Auto" MinWidth="30" />
</Grid.ColumnDefinitions>
<ContentControl
x:Name="firstContentPresenter"
Grid.Column="2"
Margin="0,0,14,0"
HorizontalAlignment="Right"
Content="{Binding SideContent, RelativeSource={RelativeSource AncestorType=local:ExCard}}" />
<TextBlock
x:Name="ItemIcon"
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Icon, RelativeSource={RelativeSource AncestorType=local:ExCard}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ItemIcon, Path=Text}" Value="{x:Static sys:String.Empty}">
<Setter Property="Margin" Value="24,0,0,0" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Grid.Column" Value="0" />
<Setter Property="Margin" Value="24,0,16,0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontSize" Value="20" />
<Setter Property="FontFamily" Value="/Resources/#Segoe Fluent Icons" />
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
</Style>
</TextBlock.Style>
</TextBlock>
<StackPanel Grid.Column="1" Margin="0,0,14,0">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Width" Value="Auto" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
</StackPanel.Style>
<TextBlock x:Name="ItemTitle" Text="{Binding Title, RelativeSource={RelativeSource AncestorType=local:ExCard}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock x:Name="SubTitle" Text="{Binding Sub, RelativeSource={RelativeSource AncestorType=local:ExCard}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SubTitle, Path=Text}" Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Foreground" Value="{DynamicResource Color04B}" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Padding" Value="0,0,24,0" />
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</Grid>
</Border>
</Expander.Header>
<Grid
Grid.Column="0"
Grid.ColumnSpan="4"
HorizontalAlignment="Stretch"
FlowDirection="LeftToRight">
<StackPanel Margin="0,0,0,0" Orientation="Vertical">
<ContentControl
Grid.Column="0"
Grid.ColumnSpan="4"
Margin="0,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</StackPanel>
</Grid>
</Expander>
</ControlTemplate>
</UserControl.Template>
</UserControl>

View file

@ -1,57 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace Flow.Launcher.Resources.Controls
{
public partial class ExCard : UserControl
{
public ExCard()
{
InitializeComponent();
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(nameof(Title), typeof(string), typeof(ExCard), new PropertyMetadata(string.Empty));
public string Sub
{
get { return (string)GetValue(SubProperty); }
set { SetValue(SubProperty, value); }
}
public static readonly DependencyProperty SubProperty =
DependencyProperty.Register(nameof(Sub), typeof(string), typeof(ExCard), new PropertyMetadata(string.Empty));
public string Icon
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(nameof(Icon), typeof(string), typeof(ExCard), new PropertyMetadata(string.Empty));
/// <summary>
/// Gets or sets additional content for the UserControl
/// </summary>
public object AdditionalContent
{
get { return (object)GetValue(AdditionalContentProperty); }
set { SetValue(AdditionalContentProperty, value); }
}
public static readonly DependencyProperty AdditionalContentProperty =
DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(ExCard),
new PropertyMetadata(null));
public object SideContent
{
get { return (object)GetValue(SideContentProperty); }
set { SetValue(SideContentProperty, value); }
}
public static readonly DependencyProperty SideContentProperty =
DependencyProperty.Register(nameof(SideContent), typeof(object), typeof(ExCard),
new PropertyMetadata(null));
}
}

View file

@ -1,14 +0,0 @@
<UserControl x:Class="Flow.Launcher.Resources.Controls.HyperLink"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<TextBlock>
<Hyperlink NavigateUri="{Binding Uri, RelativeSource={RelativeSource AncestorType=UserControl}}"
RequestNavigate="Hyperlink_OnRequestNavigate">
<Run Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Hyperlink>
</TextBlock>
</UserControl>

View file

@ -1,39 +0,0 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
namespace Flow.Launcher.Resources.Controls;
public partial class HyperLink : UserControl
{
public static readonly DependencyProperty UriProperty = DependencyProperty.Register(
nameof(Uri), typeof(string), typeof(HyperLink), new PropertyMetadata(default(string))
);
public string Uri
{
get => (string)GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
nameof(Text), typeof(string), typeof(HyperLink), new PropertyMetadata(default(string))
);
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public HyperLink()
{
InitializeComponent();
}
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
App.API.OpenUrl(e.Uri);
e.Handled = true;
}
}

View file

@ -1,81 +0,0 @@
<UserControl
x:Class="Flow.Launcher.Resources.Controls.InfoBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
d:DesignHeight="45"
d:DesignWidth="400"
mc:Ignorable="d">
<UserControl.Resources />
<Grid>
<Border
x:Name="PART_Border"
MinHeight="48"
Padding="18 18 18 18"
Background="{DynamicResource InfoBarInfoBG}"
BorderBrush="{DynamicResource Color03B}"
BorderThickness="1"
CornerRadius="5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" MinWidth="24" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<Border
x:Name="PART_IconBorder"
Width="16"
Height="16"
Margin="0 0 12 0"
VerticalAlignment="Top"
CornerRadius="10">
<ui:FontIcon
x:Name="PART_Icon"
Margin="1 0 0 1"
VerticalAlignment="Center"
FontFamily="Segoe MDL2 Assets"
FontSize="13"
Foreground="{DynamicResource Color01B}"
Visibility="Visible" />
</Border>
</StackPanel>
<StackPanel
x:Name="PART_StackPanel"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock
x:Name="PART_Title"
Margin="0 0 12 0"
FontWeight="SemiBold"
Foreground="{DynamicResource Color05B}"
Text="{Binding RelativeSource={RelativeSource AncestorType=cc:InfoBar}, Path=Title}" />
<TextBlock
x:Name="PART_Message"
Foreground="{DynamicResource Color05B}"
Text="{Binding RelativeSource={RelativeSource AncestorType=cc:InfoBar}, Path=Message}"
TextWrapping="Wrap" />
</StackPanel>
<Button
x:Name="PART_CloseButton"
Grid.Column="2"
Width="32"
Height="32"
VerticalAlignment="Center"
AutomationProperties.Name="Close InfoBar"
Click="PART_CloseButton_Click"
Content="&#xE10A;"
FontFamily="Segoe MDL2 Assets"
FontSize="12"
ToolTip="Close"
Visibility="Visible" />
</Grid>
</Border>
</Grid>
</UserControl>

View file

@ -1,222 +0,0 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Flow.Launcher.Resources.Controls
{
public partial class InfoBar : UserControl
{
public InfoBar()
{
InitializeComponent();
Loaded += InfoBar_Loaded;
}
private void InfoBar_Loaded(object sender, RoutedEventArgs e)
{
UpdateStyle();
UpdateTitleVisibility();
UpdateMessageVisibility();
UpdateOrientation();
UpdateIconAlignmentAndMargin();
UpdateIconVisibility();
UpdateCloseButtonVisibility();
}
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register(nameof(Type), typeof(InfoBarType), typeof(InfoBar), new PropertyMetadata(InfoBarType.Info, OnTypeChanged));
public InfoBarType Type
{
get => (InfoBarType)GetValue(TypeProperty);
set => SetValue(TypeProperty, value);
}
private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is InfoBar infoBar)
{
infoBar.UpdateStyle();
}
}
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register(nameof(Message), typeof(string), typeof(InfoBar), new PropertyMetadata(string.Empty, OnMessageChanged));
public string Message
{
get => (string)GetValue(MessageProperty);
set
{
SetValue(MessageProperty, value);
}
}
private static void OnMessageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is InfoBar infoBar)
{
infoBar.UpdateMessageVisibility();
}
}
private void UpdateMessageVisibility()
{
PART_Message.Visibility = string.IsNullOrEmpty(Message) ? Visibility.Collapsed : Visibility.Visible;
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(nameof(Title), typeof(string), typeof(InfoBar), new PropertyMetadata(string.Empty, OnTitleChanged));
public string Title
{
get => (string)GetValue(TitleProperty);
set
{
SetValue(TitleProperty, value);
UpdateTitleVisibility(); // Visibility update when change Title
}
}
private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is InfoBar infoBar)
{
infoBar.UpdateTitleVisibility();
}
}
private void UpdateTitleVisibility()
{
PART_Title.Visibility = string.IsNullOrEmpty(Title) ? Visibility.Collapsed : Visibility.Visible;
}
public static readonly DependencyProperty IsIconVisibleProperty =
DependencyProperty.Register(nameof(IsIconVisible), typeof(bool), typeof(InfoBar), new PropertyMetadata(true, OnIsIconVisibleChanged));
public bool IsIconVisible
{
get => (bool)GetValue(IsIconVisibleProperty);
set => SetValue(IsIconVisibleProperty, value);
}
public static readonly DependencyProperty LengthProperty =
DependencyProperty.Register(nameof(Length), typeof(InfoBarLength), typeof(InfoBar), new PropertyMetadata(InfoBarLength.Short, OnLengthChanged));
public InfoBarLength Length
{
get { return (InfoBarLength)GetValue(LengthProperty); }
set { SetValue(LengthProperty, value); }
}
private static void OnLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is InfoBar infoBar)
{
infoBar.UpdateOrientation();
infoBar.UpdateIconAlignmentAndMargin();
}
}
private void UpdateOrientation()
{
PART_StackPanel.Orientation = Length == InfoBarLength.Long ? Orientation.Vertical : Orientation.Horizontal;
}
private void UpdateIconAlignmentAndMargin()
{
if (Length == InfoBarLength.Short)
{
PART_IconBorder.VerticalAlignment = VerticalAlignment.Center;
PART_IconBorder.Margin = new Thickness(0, 0, 12, 0);
}
else
{
PART_IconBorder.VerticalAlignment = VerticalAlignment.Top;
PART_IconBorder.Margin = new Thickness(0, 2, 12, 0);
}
}
public static readonly DependencyProperty ClosableProperty =
DependencyProperty.Register(nameof(Closable), typeof(bool), typeof(InfoBar), new PropertyMetadata(true, OnClosableChanged));
public bool Closable
{
get => (bool)GetValue(ClosableProperty);
set => SetValue(ClosableProperty, value);
}
private void PART_CloseButton_Click(object sender, RoutedEventArgs e)
{
Visibility = Visibility.Collapsed;
}
private void UpdateStyle()
{
switch (Type)
{
case InfoBarType.Info:
PART_Border.Background = (Brush)FindResource("InfoBarInfoBG");
PART_IconBorder.Background = (Brush)FindResource("InfoBarInfoIcon");
PART_Icon.Glyph = "\xF13F";
break;
case InfoBarType.Success:
PART_Border.Background = (Brush)FindResource("InfoBarSuccessBG");
PART_IconBorder.Background = (Brush)FindResource("InfoBarSuccessIcon");
PART_Icon.Glyph = "\xF13E";
break;
case InfoBarType.Warning:
PART_Border.Background = (Brush)FindResource("InfoBarWarningBG");
PART_IconBorder.Background = (Brush)FindResource("InfoBarWarningIcon");
PART_Icon.Glyph = "\xF13C";
break;
case InfoBarType.Error:
PART_Border.Background = (Brush)FindResource("InfoBarErrorBG");
PART_IconBorder.Background = (Brush)FindResource("InfoBarErrorIcon");
PART_Icon.Glyph = "\xF13D";
break;
default:
PART_Border.Background = (Brush)FindResource("InfoBarInfoBG");
PART_IconBorder.Background = (Brush)FindResource("InfoBarInfoIcon");
PART_Icon.Glyph = "\xF13F";
break;
}
}
private static void OnIsIconVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var infoBar = (InfoBar)d;
infoBar.UpdateIconVisibility();
}
private static void OnClosableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var infoBar = (InfoBar)d;
infoBar.UpdateCloseButtonVisibility();
}
private void UpdateIconVisibility()
{
PART_IconBorder.Visibility = IsIconVisible ? Visibility.Visible : Visibility.Collapsed;
}
private void UpdateCloseButtonVisibility()
{
PART_CloseButton.Visibility = Closable ? Visibility.Visible : Visibility.Collapsed;
}
}
public enum InfoBarType
{
Info,
Success,
Warning,
Error
}
public enum InfoBarLength
{
Short,
Long
}
}

View file

@ -6,7 +6,7 @@
xmlns:converters="clr-namespace:Flow.Launcher.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:viewModel="clr-namespace:Flow.Launcher.ViewModel"
d:DataContext="{d:DesignInstance viewModel:PluginViewModel}"
d:DesignHeight="300"
@ -66,6 +66,7 @@
Text="{DynamicResource priority}"
ToolTip="{DynamicResource priorityToolTip}" />
<ui:NumberBox
MinWidth="120"
Margin="0 0 8 0"
Maximum="999"
Minimum="-999"
@ -89,6 +90,7 @@
ToolTip="{DynamicResource searchDelayToolTip}" />
<ui:NumberBox
Width="120"
MinWidth="120"
Margin="0 0 8 0"
IsEnabled="{Binding SearchDelayEnabled}"
Maximum="1000"

View file

@ -1,4 +1,4 @@
using ModernWpf.Controls;
using iNKORE.UI.WPF.Modern.Controls;
namespace Flow.Launcher.Resources.Controls;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more