Merge branch 'dev' into dependabot/nuget/nunit-4.3.2

This commit is contained in:
Jack Ye 2025-02-22 22:12:32 +08:00 committed by GitHub
commit 2d90481968
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
89 changed files with 1369 additions and 645 deletions

View file

@ -1,4 +1,4 @@
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Logger;
using System;
using System.Collections.Generic;
using System.Threading;
@ -21,7 +21,7 @@ namespace Flow.Launcher.Core.ExternalPlugins
public static List<UserPlugin> UserPlugins { get; private set; }
public static async Task UpdateManifestAsync(CancellationToken token = default, bool usePrimaryUrlOnly = false)
public static async Task<bool> UpdateManifestAsync(CancellationToken token = default, bool usePrimaryUrlOnly = false)
{
try
{
@ -31,8 +31,14 @@ namespace Flow.Launcher.Core.ExternalPlugins
{
var results = await mainPluginStore.FetchAsync(token, usePrimaryUrlOnly).ConfigureAwait(false);
UserPlugins = results;
lastFetchedAt = DateTime.Now;
// If the results are empty, we shouldn't update the manifest because the results are invalid.
if (results.Count != 0)
{
UserPlugins = results;
lastFetchedAt = DateTime.Now;
return true;
}
}
}
catch (Exception e)
@ -43,6 +49,8 @@ namespace Flow.Launcher.Core.ExternalPlugins
{
manifestUpdateLock.Release();
}
return false;
}
}
}

View file

@ -54,7 +54,7 @@
<ItemGroup>
<PackageReference Include="Droplex" Version="1.7.0" />
<PackageReference Include="FSharp.Core" Version="9.0.100" />
<PackageReference Include="FSharp.Core" Version="9.0.101" />
<PackageReference Include="Meziantou.Framework.Win32.Jobs" Version="3.4.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="squirrel.windows" Version="1.5.2" NoWarn="NU1701" />

View file

@ -12,6 +12,7 @@
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Height"
Topmost="True"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>

View file

@ -112,7 +112,7 @@ namespace Flow.Launcher.Core.Plugin
public Control CreateSettingPanel()
{
if (Settings == null || Settings.Count == 0)
return new();
return null;
var settingWindow = new UserControl();
var mainPanel = new Grid { Margin = settingPanelMargin, VerticalAlignment = VerticalAlignment.Center };

View file

@ -26,54 +26,33 @@ namespace Flow.Launcher.Core.Plugin
protected override async Task<bool> ExecuteResultAsync(JsonRPCResult result)
{
try
{
var res = await RPC.InvokeAsync<JsonRPCExecuteResponse>(result.JsonRPCAction.Method,
argument: result.JsonRPCAction.Parameters);
var res = await RPC.InvokeAsync<JsonRPCExecuteResponse>(result.JsonRPCAction.Method,
argument: result.JsonRPCAction.Parameters);
return res.Hide;
}
catch
{
return false;
}
return res.Hide;
}
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
public override List<Result> LoadContextMenus(Result selectedResult)
{
try
{
var res = JTF.Run(() => RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("context_menu",
new object[] { selectedResult.ContextData }));
var res = JTF.Run(() => RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("context_menu",
new object[] { selectedResult.ContextData }));
var results = ParseResults(res);
var results = ParseResults(res);
return results;
}
catch
{
return new List<Result>();
}
return results;
}
public override async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
{
try
{
var res = await RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("query",
new object[] { query, Settings.Inner },
token);
var res = await RPC.InvokeWithCancellationAsync<JsonRPCQueryResponseModel>("query",
new object[] { query, Settings.Inner },
token);
var results = ParseResults(res);
var results = ParseResults(res);
return results;
}
catch
{
return new List<Result>();
}
return results;
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
@ -121,10 +120,10 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
return _api.HttpGetStreamAsync(url, token);
}
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath,
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null,
CancellationToken token = default)
{
return _api.HttpDownloadAsync(url, filePath, token);
return _api.HttpDownloadAsync(url, filePath, reportProgress, token);
}
public void AddActionKeyword(string pluginId, string newActionKeyword)
@ -162,16 +161,19 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
_api.OpenDirectory(DirectoryPath, FileNameOrFilePath);
}
public void OpenUrl(string url, bool? inPrivate = null)
{
_api.OpenUrl(url, inPrivate);
}
public void OpenAppUri(string appUri)
{
_api.OpenAppUri(appUri);
}
public void BackToQueryResults()
{
_api.BackToQueryResults();
}
}
}

View file

@ -281,7 +281,7 @@ namespace Flow.Launcher.Core.Plugin
return results;
}
public static void UpdatePluginMetadata(List<Result> results, PluginMetadata metadata, Query query)
public static void UpdatePluginMetadata(IReadOnlyList<Result> results, PluginMetadata metadata, Query query)
{
foreach (var r in results)
{

View file

@ -1,4 +1,5 @@
using System.Diagnostics;
using System;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Threading;
@ -25,14 +26,13 @@ namespace Flow.Launcher.Core.Plugin
var path = Path.Combine(Constant.ProgramDirectory, JsonRPC);
_startInfo.EnvironmentVariables["PYTHONPATH"] = path;
// Prevent Python from writing .py[co] files.
// Because .pyc contains location infos which will prevent python portable.
_startInfo.EnvironmentVariables["PYTHONDONTWRITEBYTECODE"] = "1";
_startInfo.EnvironmentVariables["FLOW_VERSION"] = Constant.Version;
_startInfo.EnvironmentVariables["FLOW_PROGRAM_DIRECTORY"] = Constant.ProgramDirectory;
_startInfo.EnvironmentVariables["FLOW_APPLICATION_DIRECTORY"] = Constant.ApplicationDirectory;
//Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable
_startInfo.ArgumentList.Add("-B");
}
protected override Task<Stream> RequestAsync(JsonRPCRequestModel request, CancellationToken token = default)
@ -50,10 +50,53 @@ namespace Flow.Launcher.Core.Plugin
// TODO: Async Action
return Execute(_startInfo);
}
public override async Task InitAsync(PluginInitContext context)
{
_startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
_startInfo.ArgumentList.Add("");
// Run .py files via `-c <code>`
if (context.CurrentPluginMetadata.ExecuteFilePath.EndsWith(".py", StringComparison.OrdinalIgnoreCase))
{
var rootDirectory = context.CurrentPluginMetadata.PluginDirectory;
var libDirectory = Path.Combine(rootDirectory, "lib");
var libPyWin32Directory = Path.Combine(libDirectory, "win32");
var libPyWin32LibDirectory = Path.Combine(libPyWin32Directory, "lib");
var pluginDirectory = Path.Combine(rootDirectory, "plugin");
// This makes it easier for plugin authors to import their own modules.
// They won't have to add `.`, `./lib`, or `./plugin` to their sys.path manually.
// Instead of running the .py file directly, we pass the code we want to run as a CLI argument.
// This code sets sys.path for the plugin author and then runs the .py file via runpy.
_startInfo.ArgumentList.Add("-c");
_startInfo.ArgumentList.Add(
$"""
import sys
sys.path.append(r'{rootDirectory}')
sys.path.append(r'{libDirectory}')
sys.path.append(r'{libPyWin32LibDirectory}')
sys.path.append(r'{libPyWin32Directory}')
sys.path.append(r'{pluginDirectory}')
import runpy
runpy.run_path(r'{context.CurrentPluginMetadata.ExecuteFilePath}', None, '__main__')
"""
);
// Plugins always expect the JSON data to be in the third argument
// (we're always setting it as _startInfo.ArgumentList[2] = ...).
_startInfo.ArgumentList.Add("");
}
// Run .pyz files as is
else
{
// No need for -B flag because we're using PYTHONDONTWRITEBYTECODE env variable now,
// but the plugins still expect data to be sent as the third argument, so we're keeping
// the flag here, even though it's not necessary anymore.
_startInfo.ArgumentList.Add("-B");
_startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
// Plugins always expect the JSON data to be in the third argument
// (we're always setting it as _startInfo.ArgumentList[2] = ...).
_startInfo.ArgumentList.Add("");
}
await base.InitAsync(context);
_startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
}

View file

@ -26,14 +26,45 @@ namespace Flow.Launcher.Core.Plugin
var path = Path.Combine(Constant.ProgramDirectory, JsonRpc);
StartInfo.EnvironmentVariables["PYTHONPATH"] = path;
//Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable
StartInfo.ArgumentList.Add("-B");
StartInfo.EnvironmentVariables["PYTHONDONTWRITEBYTECODE"] = "1";
}
public override async Task InitAsync(PluginInitContext context)
{
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
// Run .py files via `-c <code>`
if (context.CurrentPluginMetadata.ExecuteFilePath.EndsWith(".py", StringComparison.OrdinalIgnoreCase))
{
var rootDirectory = context.CurrentPluginMetadata.PluginDirectory;
var libDirectory = Path.Combine(rootDirectory, "lib");
var libPyWin32Directory = Path.Combine(libDirectory, "win32");
var libPyWin32LibDirectory = Path.Combine(libPyWin32Directory, "lib");
var pluginDirectory = Path.Combine(rootDirectory, "plugin");
var filePath = context.CurrentPluginMetadata.ExecuteFilePath;
// This makes it easier for plugin authors to import their own modules.
// They won't have to add `.`, `./lib`, or `./plugin` to their sys.path manually.
// Instead of running the .py file directly, we pass the code we want to run as a CLI argument.
// This code sets sys.path for the plugin author and then runs the .py file via runpy.
StartInfo.ArgumentList.Add("-c");
StartInfo.ArgumentList.Add(
$"""
import sys
sys.path.append(r'{rootDirectory}')
sys.path.append(r'{libDirectory}')
sys.path.append(r'{libPyWin32LibDirectory}')
sys.path.append(r'{libPyWin32Directory}')
sys.path.append(r'{pluginDirectory}')
import runpy
runpy.run_path(r'{filePath}', None, '__main__')
"""
);
}
// Run .pyz files as is
else
{
StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
}
await base.InitAsync(context);
}

View file

@ -30,7 +30,6 @@ namespace Flow.Launcher.Core.Resource
public static Language Vietnamese = new Language("vi-vn", "Tiếng Việt");
public static Language Hebrew = new Language("he", "עברית");
public static List<Language> GetAvailableLanguages()
{
List<Language> languages = new List<Language>
@ -63,5 +62,38 @@ namespace Flow.Launcher.Core.Resource
};
return languages;
}
public static string GetSystemTranslation(string languageCode)
{
return languageCode switch
{
"en" => "System",
"zh-cn" => "系统",
"zh-tw" => "系統",
"uk-UA" => "Система",
"ru" => "Система",
"fr" => "Système",
"ja" => "システム",
"nl" => "Systeem",
"pl" => "System",
"da" => "System",
"de" => "System",
"ko" => "시스템",
"sr" => "Систем",
"pt-pt" => "Sistema",
"pt-br" => "Sistema",
"es" => "Sistema",
"es-419" => "Sistema",
"it" => "Sistema",
"nb-NO" => "System",
"sk" => "Systém",
"tr" => "Sistem",
"cs" => "Systém",
"ar" => "النظام",
"vi-vn" => "Hệ thống",
"he" => "מערכת",
_ => "System",
};
}
}
}

View file

@ -18,23 +18,52 @@ namespace Flow.Launcher.Core.Resource
{
public Settings Settings { get; set; }
private const string Folder = "Languages";
private const string DefaultLanguageCode = "en";
private const string DefaultFile = "en.xaml";
private const string Extension = ".xaml";
private readonly List<string> _languageDirectories = new List<string>();
private readonly List<ResourceDictionary> _oldResources = new List<ResourceDictionary>();
private readonly string SystemLanguageCode;
public Internationalization()
{
AddFlowLauncherLanguageDirectory();
SystemLanguageCode = GetSystemLanguageCodeAtStartup();
}
private void AddFlowLauncherLanguageDirectory()
{
var directory = Path.Combine(Constant.ProgramDirectory, Folder);
_languageDirectories.Add(directory);
}
private static string GetSystemLanguageCodeAtStartup()
{
var availableLanguages = AvailableLanguages.GetAvailableLanguages();
// Retrieve the language identifiers for the current culture.
// ChangeLanguage method overrides the CultureInfo.CurrentCulture, so this needs to
// be called at startup in order to get the correct lang code of system.
var currentCulture = CultureInfo.CurrentCulture;
var twoLetterCode = currentCulture.TwoLetterISOLanguageName;
var threeLetterCode = currentCulture.ThreeLetterISOLanguageName;
var fullName = currentCulture.Name;
// Try to find a match in the available languages list
foreach (var language in availableLanguages)
{
var languageCode = language.LanguageCode;
if (string.Equals(languageCode, twoLetterCode, StringComparison.OrdinalIgnoreCase) ||
string.Equals(languageCode, threeLetterCode, StringComparison.OrdinalIgnoreCase) ||
string.Equals(languageCode, fullName, StringComparison.OrdinalIgnoreCase))
{
return languageCode;
}
}
return DefaultLanguageCode;
}
internal void AddPluginLanguageDirectories(IEnumerable<PluginPair> plugins)
{
@ -68,8 +97,18 @@ namespace Flow.Launcher.Core.Resource
public void ChangeLanguage(string languageCode)
{
languageCode = languageCode.NonNull();
Language language = GetLanguageByLanguageCode(languageCode);
ChangeLanguage(language);
// Get actual language if language code is system
var isSystem = false;
if (languageCode == Constant.SystemLanguageCode)
{
languageCode = SystemLanguageCode;
isSystem = true;
}
// Get language by language code and change language
var language = GetLanguageByLanguageCode(languageCode);
ChangeLanguage(language, isSystem);
}
private Language GetLanguageByLanguageCode(string languageCode)
@ -87,11 +126,10 @@ namespace Flow.Launcher.Core.Resource
}
}
public void ChangeLanguage(Language language)
private void ChangeLanguage(Language language, bool isSystem)
{
language = language.NonNull();
RemoveOldLanguageFiles();
if (language != AvailableLanguages.English)
{
@ -103,7 +141,7 @@ namespace Flow.Launcher.Core.Resource
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture;
// Raise event after culture is set
Settings.Language = language.LanguageCode;
Settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
_ = Task.Run(() =>
{
UpdatePluginMetadataTranslations();
@ -167,7 +205,9 @@ namespace Flow.Launcher.Core.Resource
public List<Language> LoadAvailableLanguages()
{
return AvailableLanguages.GetAvailableLanguages();
var list = AvailableLanguages.GetAvailableLanguages();
list.Insert(0, new Language(Constant.SystemLanguageCode, AvailableLanguages.GetSystemTranslation(SystemLanguageCode)));
return list;
}
public string GetTranslation(string key)

View file

@ -11,6 +11,7 @@ using System.Windows.Media.Effects;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using System.Windows.Shell;
namespace Flow.Launcher.Core.Resource
{
@ -306,12 +307,15 @@ namespace Flow.Launcher.Core.Resource
var marginSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) as Setter;
if (marginSetter == null)
{
var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin);
marginSetter = new Setter()
{
Property = Border.MarginProperty,
Value = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin),
Value = margin,
};
windowBorderStyle.Setters.Add(marginSetter);
SetResizeBoarderThickness(margin);
}
else
{
@ -322,6 +326,8 @@ namespace Flow.Launcher.Core.Resource
baseMargin.Right + ShadowExtraMargin,
baseMargin.Bottom + ShadowExtraMargin);
marginSetter.Value = newMargin;
SetResizeBoarderThickness(newMargin);
}
windowBorderStyle.Setters.Add(effectSetter);
@ -352,9 +358,36 @@ namespace Flow.Launcher.Core.Resource
marginSetter.Value = newMargin;
}
SetResizeBoarderThickness(null);
UpdateResourceDictionary(dict);
}
// because adding drop shadow effect will change the margin of the window,
// we need to update the window chrome thickness to correct set the resize border
private static void SetResizeBoarderThickness(Thickness? effectMargin)
{
var window = Application.Current.MainWindow;
if (WindowChrome.GetWindowChrome(window) is WindowChrome windowChrome)
{
Thickness thickness;
if (effectMargin == null)
{
thickness = SystemParameters.WindowResizeBorderThickness;
}
else
{
thickness = new Thickness(
effectMargin.Value.Left + SystemParameters.WindowResizeBorderThickness.Left,
effectMargin.Value.Top + SystemParameters.WindowResizeBorderThickness.Top,
effectMargin.Value.Right + SystemParameters.WindowResizeBorderThickness.Right,
effectMargin.Value.Bottom + SystemParameters.WindowResizeBorderThickness.Bottom);
}
windowChrome.ResizeBorderThickness = thickness;
}
}
public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null);
}
}

View file

@ -52,5 +52,7 @@ namespace Flow.Launcher.Infrastructure
public const string SponsorPage = "https://github.com/sponsors/Flow-Launcher";
public const string GitHub = "https://github.com/Flow-Launcher/Flow.Launcher";
public const string Docs = "https://flowlauncher.com/docs";
public const string SystemLanguageCode = "system";
}
}

View file

@ -15,7 +15,20 @@ namespace Flow.Launcher.Infrastructure
{
var explorerWindow = GetActiveExplorer();
string locationUrl = explorerWindow?.LocationURL;
return !string.IsNullOrEmpty(locationUrl) ? new Uri(locationUrl).LocalPath + "\\" : null;
return !string.IsNullOrEmpty(locationUrl) ? GetDirectoryPath(new Uri(locationUrl).LocalPath) : null;
}
/// <summary>
/// Get directory path from a file path
/// </summary>
private static string GetDirectoryPath(string path)
{
if (!path.EndsWith("\\"))
{
return path + "\\";
}
return path;
}
/// <summary>
@ -68,7 +81,7 @@ namespace Flow.Launcher.Infrastructure
var numRemaining = hWnds.Count;
PInvoke.EnumWindows((wnd, _) =>
{
var searchIndex = hWnds.FindIndex(x => x.HWND == wnd.Value);
var searchIndex = hWnds.FindIndex(x => new IntPtr(x.HWND) == wnd);
if (searchIndex != -1)
{
z[searchIndex] = index;

View file

@ -53,7 +53,7 @@
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="BitFaster.Caching" Version="2.5.2" />
<PackageReference Include="BitFaster.Caching" Version="2.5.3" />
<PackageReference Include="Fody" Version="6.5.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View file

@ -83,15 +83,50 @@ namespace Flow.Launcher.Infrastructure.Http
}
}
public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default)
public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default)
{
try
{
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (response.StatusCode == HttpStatusCode.OK)
{
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
await response.Content.CopyToAsync(fileStream, token);
var totalBytes = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = totalBytes != -1;
if (canReportProgress && reportProgress != null)
{
await using var contentStream = await response.Content.ReadAsStreamAsync(token);
await using var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8192, true);
var buffer = new byte[8192];
long totalRead = 0;
int read;
double progressValue = 0;
reportProgress(0);
while ((read = await contentStream.ReadAsync(buffer, token)) > 0)
{
await fileStream.WriteAsync(buffer.AsMemory(0, read), token);
totalRead += read;
progressValue = totalRead * 100.0 / totalBytes;
if (token.IsCancellationRequested)
return;
else
reportProgress(progressValue);
}
if (progressValue < 100)
reportProgress(100);
}
else
{
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
await response.Content.CopyToAsync(fileStream, token);
}
}
else
{

View file

@ -84,6 +84,11 @@ namespace Flow.Launcher.Infrastructure.Image
// Fallback to IconOnly if ThumbnailOnly fails
imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap);
}
catch (FileNotFoundException) when (options == ThumbnailOptions.ThumbnailOnly)
{
// Fallback to IconOnly if files cannot be found
imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap);
}
}
finally
{

View file

@ -16,7 +16,4 @@ WM_KEYUP
WM_SYSKEYDOWN
WM_SYSKEYUP
EnumWindows
OleInitialize
OleUninitialize
EnumWindows

View file

@ -13,7 +13,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
public class Settings : BaseModel, IHotkeySettings
{
private string language = "en";
private string language = Constant.SystemLanguageCode;
private string _theme = Constant.DefaultTheme;
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
@ -230,7 +230,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
[JsonIgnore]
public ObservableCollection<BuiltinShortcutModel> BuiltinShortcuts { get; set; } = new()
{
new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", () => Win32Helper.StartSTATaskAsync(Clipboard.GetText).Result),
new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText),
new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath)
};

View file

@ -1,150 +1,12 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Interop;
using System.Windows;
using Windows.Win32;
namespace Flow.Launcher.Infrastructure
{
public static class Win32Helper
{
#region STA Thread
/*
Found on https://github.com/files-community/Files
*/
public static Task StartSTATaskAsync(Action action)
{
var taskCompletionSource = new TaskCompletionSource();
Thread thread = new(() =>
{
PInvoke.OleInitialize();
try
{
action();
taskCompletionSource.SetResult();
}
catch (System.Exception)
{
taskCompletionSource.SetResult();
}
finally
{
PInvoke.OleUninitialize();
}
})
{
IsBackground = true,
Priority = ThreadPriority.Normal
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return taskCompletionSource.Task;
}
public static Task StartSTATaskAsync(Func<Task> func)
{
var taskCompletionSource = new TaskCompletionSource();
Thread thread = new(async () =>
{
PInvoke.OleInitialize();
try
{
await func();
taskCompletionSource.SetResult();
}
catch (System.Exception)
{
taskCompletionSource.SetResult();
}
finally
{
PInvoke.OleUninitialize();
}
})
{
IsBackground = true,
Priority = ThreadPriority.Normal
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return taskCompletionSource.Task;
}
public static Task<T?> StartSTATaskAsync<T>(Func<T> func)
{
var taskCompletionSource = new TaskCompletionSource<T?>();
Thread thread = new(() =>
{
PInvoke.OleInitialize();
try
{
taskCompletionSource.SetResult(func());
}
catch (System.Exception)
{
taskCompletionSource.SetResult(default);
}
finally
{
PInvoke.OleUninitialize();
}
})
{
IsBackground = true,
Priority = ThreadPriority.Normal
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return taskCompletionSource.Task;
}
public static Task<T?> StartSTATaskAsync<T>(Func<Task<T>> func)
{
var taskCompletionSource = new TaskCompletionSource<T?>();
Thread thread = new(async () =>
{
PInvoke.OleInitialize();
try
{
taskCompletionSource.SetResult(await func());
}
catch (System.Exception)
{
taskCompletionSource.SetResult(default);
}
finally
{
PInvoke.OleUninitialize();
}
})
{
IsBackground = true,
Priority = ThreadPriority.Normal
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return taskCompletionSource.Task;
}
#endregion
#region Blur Handling
/*

View file

@ -1,4 +1,4 @@
using Flow.Launcher.Plugin.SharedModels;
using Flow.Launcher.Plugin.SharedModels;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
@ -181,9 +181,13 @@ namespace Flow.Launcher.Plugin
/// </summary>
/// <param name="url">URL to download file</param>
/// <param name="filePath">path to save downloaded file</param>
/// <param name="reportProgress">
/// Action to report progress. The input of the action is the progress value which is a double value between 0 and 100.
/// It will be called if url support range request and the reportProgress is not null.
/// </param>
/// <param name="token">place to store file</param>
/// <returns>Task showing the progress</returns>
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default);
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default);
/// <summary>
/// Add ActionKeyword for specific plugin
@ -316,5 +320,19 @@ namespace Flow.Launcher.Plugin
/// <param name="defaultResult">Specifies the default result of the message box.</param>
/// <returns>Specifies which message box button is clicked by the user.</returns>
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK);
/// <summary>
/// Displays a standardised Flow message box.
/// If there is issue when showing the message box, it will return null.
/// </summary>
/// <param name="caption">The caption of the message box.</param>
/// <param name="reportProgressAsync">
/// Time-consuming task function, whose input is the action to report progress.
/// The input of the action is the progress value which is a double value between 0 and 100.
/// If there are any exceptions, this action will be null.
/// </param>
/// <param name="forceClosed">When user closes the progress box manually by button or esc key, this action will be called.</param>
/// <returns>A progress box interface.</returns>
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null);
}
}

View file

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Plugin
{
@ -9,15 +6,6 @@ namespace Flow.Launcher.Plugin
{
public Query() { }
[Obsolete("Use the default Query constructor.")]
public Query(string rawQuery, string search, string[] terms, string[] searchTerms, string actionKeyword = "")
{
Search = search;
RawQuery = rawQuery;
SearchTerms = searchTerms;
ActionKeyword = actionKeyword;
}
/// <summary>
/// Raw query, this includes action keyword if it has
/// We didn't recommend use this property directly. You should always use Search property.

View file

@ -157,27 +157,6 @@ namespace Flow.Launcher.Plugin
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
var r = obj as Result;
var equality = string.Equals(r?.Title, Title) &&
string.Equals(r?.SubTitle, SubTitle) &&
string.Equals(r?.AutoCompleteText, AutoCompleteText) &&
string.Equals(r?.CopyText, CopyText) &&
string.Equals(r?.IcoPath, IcoPath) &&
TitleHighlightData == r.TitleHighlightData;
return equality;
}
/// <inheritdoc />
public override int GetHashCode()
{
return HashCode.Combine(Title, SubTitle, AutoCompleteText, CopyText, IcoPath);
}
/// <inheritdoc />
public override string ToString()
{
@ -206,6 +185,16 @@ namespace Flow.Launcher.Plugin
TitleHighlightData = TitleHighlightData,
OriginQuery = OriginQuery,
PluginDirectory = PluginDirectory,
ContextData = ContextData,
PluginID = PluginID,
TitleToolTip = TitleToolTip,
SubTitleToolTip = SubTitleToolTip,
PreviewPanel = PreviewPanel,
ProgressBar = ProgressBar,
ProgressBarColor = ProgressBarColor,
Preview = Preview,
AddSelectedCount = AddSelectedCount,
RecordKey = RecordKey
};
}
@ -273,6 +262,14 @@ namespace Flow.Launcher.Plugin
/// </summary>
public const int MaxScore = int.MaxValue;
/// <summary>
/// The key to identify the record. This is used when FL checks whether the result is the topmost record. Or FL calculates the hashcode of the result for user selected records.
/// This can be useful when your plugin will change the Title or SubTitle of the result dynamically.
/// If the plugin does not specific this, FL just uses Title and SubTitle to identify this result.
/// Note: Because old data does not have this key, we should use null as the default value for consistency.
/// </summary>
public string RecordKey { get; set; } = null;
/// <summary>
/// Info of the preview section of a <see cref="Result"/>
/// </summary>

View file

@ -63,28 +63,5 @@ namespace Flow.Launcher.Test.Plugins
})
};
[TestCaseSource(typeof(JsonRPCPluginTest), nameof(ResponseModelsSource))]
public async Task GivenModel_WhenSerializeWithDifferentNamingPolicy_ThenExpectSameResult_Async(JsonRPCQueryResponseModel reference)
{
var camelText = JsonSerializer.Serialize(reference, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
var pascalText = JsonSerializer.Serialize(reference);
var results1 = await QueryAsync(new Query { Search = camelText }, default);
var results2 = await QueryAsync(new Query { Search = pascalText }, default);
Assert.IsNotNull(results1);
Assert.IsNotNull(results2);
foreach (var ((result1, result2), referenceResult) in results1.Zip(results2).Zip(reference.Result))
{
Assert.AreEqual(result1, result2);
Assert.AreEqual(result1, referenceResult);
Assert.IsNotNull(result1);
Assert.IsNotNull(result1.AsyncAction);
}
}
}
}

View file

@ -13,14 +13,14 @@ namespace Flow.Launcher
public partial class CustomQueryHotkeySetting : Window
{
private SettingWindow _settingWidow;
private readonly Settings _settings;
private bool update;
private CustomPluginHotkey updateCustomHotkey;
public Settings Settings { get; }
public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings)
{
_settingWidow = settingWidow;
Settings = settings;
_settings = settings;
InitializeComponent();
}
@ -33,13 +33,13 @@ namespace Flow.Launcher
{
if (!update)
{
Settings.CustomPluginHotkeys ??= new ObservableCollection<CustomPluginHotkey>();
_settings.CustomPluginHotkeys ??= new ObservableCollection<CustomPluginHotkey>();
var pluginHotkey = new CustomPluginHotkey
{
Hotkey = HotkeyControl.CurrentHotkey.ToString(), ActionKeyword = tbAction.Text
};
Settings.CustomPluginHotkeys.Add(pluginHotkey);
_settings.CustomPluginHotkeys.Add(pluginHotkey);
HotKeyMapper.SetCustomQueryHotkey(pluginHotkey);
}
@ -59,7 +59,7 @@ namespace Flow.Launcher
public void UpdateItem(CustomPluginHotkey item)
{
updateCustomHotkey = Settings.CustomPluginHotkeys.FirstOrDefault(o =>
updateCustomHotkey = _settings.CustomPluginHotkeys.FirstOrDefault(o =>
o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey);
if (updateCustomHotkey == null)
{
@ -77,8 +77,7 @@ namespace Flow.Launcher
private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e)
{
App.API.ChangeQuery(tbAction.Text);
Application.Current.MainWindow.Show();
Application.Current.MainWindow.Opacity = 1;
App.API.ShowMainWindow();
Application.Current.MainWindow.Focus();
}

View file

@ -65,8 +65,7 @@ namespace Flow.Launcher
private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e)
{
App.API.ChangeQuery(tbExpand.Text);
Application.Current.MainWindow.Show();
Application.Current.MainWindow.Opacity = 1;
App.API.ShowMainWindow();
Application.Current.MainWindow.Focus();
}
}

View file

@ -83,6 +83,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ChefKeys" Version="0.1.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Fody" Version="6.5.4">
<PrivateAssets>all</PrivateAssets>
@ -99,8 +100,8 @@
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
<PackageReference Include="SemanticVersioning" Version="3.0.0-beta2" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.0" />
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
</ItemGroup>
<ItemGroup>

View file

@ -6,6 +6,9 @@ using NHotkey.Wpf;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.ViewModel;
using Flow.Launcher.Core;
using ChefKeys;
using System.Globalization;
using Flow.Launcher.Infrastructure.Logger;
namespace Flow.Launcher.Helper;
@ -29,21 +32,57 @@ internal static class HotKeyMapper
_mainViewModel.ToggleFlowLauncher();
}
internal static void OnToggleHotkeyWithChefKeys()
{
if (!_mainViewModel.ShouldIgnoreHotkeys())
_mainViewModel.ToggleFlowLauncher();
}
private static void SetHotkey(string hotkeyStr, EventHandler<HotkeyEventArgs> action)
{
var hotkey = new HotkeyModel(hotkeyStr);
SetHotkey(hotkey, action);
}
private static void SetWithChefKeys(string hotkeyStr)
{
try
{
ChefKeysManager.RegisterHotkey(hotkeyStr, hotkeyStr, OnToggleHotkeyWithChefKeys);
ChefKeysManager.Start();
}
catch (Exception e)
{
Log.Error(
string.Format("|HotkeyMapper.SetWithChefKeys|Error registering hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
MessageBoxEx.Show(errorMsg, errorMsgTitle);
}
}
internal static void SetHotkey(HotkeyModel hotkey, EventHandler<HotkeyEventArgs> action)
{
string hotkeyStr = hotkey.ToString();
try
{
if (hotkeyStr == "LWin" || hotkeyStr == "RWin")
{
SetWithChefKeys(hotkeyStr);
return;
}
HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action);
}
catch (Exception)
catch (Exception e)
{
Log.Error(
string.Format("|HotkeyMapper.SetHotkey|Error registering hotkey {2}: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace,
hotkeyStr));
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
MessageBoxEx.Show(errorMsg, errorMsgTitle);
@ -52,10 +91,33 @@ internal static class HotKeyMapper
internal static void RemoveHotkey(string hotkeyStr)
{
if (!string.IsNullOrEmpty(hotkeyStr))
try
{
HotkeyManager.Current.Remove(hotkeyStr);
if (hotkeyStr == "LWin" || hotkeyStr == "RWin")
{
RemoveWithChefKeys(hotkeyStr);
return;
}
if (!string.IsNullOrEmpty(hotkeyStr))
HotkeyManager.Current.Remove(hotkeyStr);
}
catch (Exception e)
{
Log.Error(
string.Format("|HotkeyMapper.RemoveHotkey|Error removing hotkey: {0} \nStackTrace:{1}",
e.Message,
e.StackTrace));
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("unregisterHotkeyFailed"), hotkeyStr);
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
MessageBoxEx.Show(errorMsg, errorMsgTitle);
}
}
private static void RemoveWithChefKeys(string hotkeyStr)
{
ChefKeysManager.UnregisterHotkey(hotkeyStr);
ChefKeysManager.Stop();
}
internal static void LoadCustomPluginHotkey()

View file

@ -10,16 +10,29 @@ public static class SingletonWindowOpener
{
var window = Application.Current.Windows.OfType<Window>().FirstOrDefault(x => x.GetType() == typeof(T))
?? (T)Activator.CreateInstance(typeof(T), args);
// Fix UI bug
// Add `window.WindowState = WindowState.Normal`
// If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar
// Not sure why this works tho
// Probably because, when `.Show()` fails, `window.WindowState == Minimized` (not `Normal`)
// https://stackoverflow.com/a/59719760/4230390
window.WindowState = WindowState.Normal;
window.Show();
// Ensure the window is not minimized before showing it
if (window.WindowState == WindowState.Minimized)
{
window.WindowState = WindowState.Normal;
}
// Ensure the window is visible
if (!window.IsVisible)
{
window.Show();
}
else
{
window.Activate(); // Bring the window to the foreground if already open
}
window.Focus();
return (T)window;

View file

@ -148,4 +148,64 @@ public class WindowsInteropHelper
return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
}
#region Alt Tab
private static int SetWindowLong(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, int dwNewLong)
{
PInvoke.SetLastError(WIN32_ERROR.NO_ERROR); // Clear any existing error
var result = PInvoke.SetWindowLong(hWnd, nIndex, dwNewLong);
if (result == 0 && Marshal.GetLastPInvokeError() != 0)
{
throw new Win32Exception(Marshal.GetLastPInvokeError());
}
return result;
}
/// <summary>
/// Hide windows in the Alt+Tab window list
/// </summary>
/// <param name="window">To hide a window</param>
public static void HideFromAltTab(Window window)
{
var exStyle = GetCurrentWindowStyle(window);
// Add TOOLWINDOW style, remove APPWINDOW style
var newExStyle = ((uint)exStyle | (uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) & ~(uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
SetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
}
/// <summary>
/// Restore window display in the Alt+Tab window list.
/// </summary>
/// <param name="window">To restore the displayed window</param>
public static void ShowInAltTab(Window window)
{
var exStyle = GetCurrentWindowStyle(window);
// Remove the TOOLWINDOW style and add the APPWINDOW style.
var newExStyle = ((uint)exStyle & ~(uint)WINDOW_EX_STYLE.WS_EX_TOOLWINDOW) | (uint)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
SetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)newExStyle);
}
/// <summary>
/// To obtain the current overridden style of a window.
/// </summary>
/// <param name="window">To obtain the style dialog window</param>
/// <returns>current extension style value</returns>
private static int GetCurrentWindowStyle(Window window)
{
var style = PInvoke.GetWindowLong(new(new WindowInteropHelper(window).Handle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
if (style == 0 && Marshal.GetLastPInvokeError() != 0)
{
throw new Win32Exception(Marshal.GetLastPInvokeError());
}
return style;
}
#endregion
}

View file

@ -154,7 +154,16 @@ namespace Flow.Launcher
{
if (triggerValidate)
{
bool hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture);
bool hotkeyAvailable = false;
// TODO: This is a temporary way to enforce changing only the open flow hotkey to Win, and will be removed by PR #3157
if (keyModel.ToString() == "LWin" || keyModel.ToString() == "RWin")
{
hotkeyAvailable = true;
}
else
{
hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture);
}
if (!hotkeyAvailable)
{

View file

@ -3,6 +3,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using ChefKeys;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure.Hotkey;
@ -33,6 +34,8 @@ public partial class HotkeyControlDialog : ContentDialog
public string ResultValue { get; private set; } = string.Empty;
public static string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none");
private static bool isOpenFlowHotkey;
public HotkeyControlDialog(string hotkey, string defaultHotkey, IHotkeySettings hotkeySettings, string windowTitle = "")
{
WindowTitle = windowTitle switch
@ -46,6 +49,14 @@ public partial class HotkeyControlDialog : ContentDialog
SetKeysToDisplay(CurrentHotkey);
InitializeComponent();
// TODO: This is a temporary way to enforce changing only the open flow hotkey to Win, and will be removed by PR #3157
isOpenFlowHotkey = _hotkeySettings.RegisteredHotkeys
.Any(x => x.DescriptionResourceKey == "flowlauncherHotkey"
&& x.Hotkey.ToString() == hotkey);
ChefKeysManager.StartMenuEnableBlocking = true;
ChefKeysManager.Start();
}
private void Reset(object sender, RoutedEventArgs routedEventArgs)
@ -61,12 +72,18 @@ public partial class HotkeyControlDialog : ContentDialog
private void Cancel(object sender, RoutedEventArgs routedEventArgs)
{
ChefKeysManager.StartMenuEnableBlocking = false;
ChefKeysManager.Stop();
ResultType = EResultType.Cancel;
Hide();
}
private void Save(object sender, RoutedEventArgs routedEventArgs)
{
ChefKeysManager.StartMenuEnableBlocking = false;
ChefKeysManager.Stop();
if (KeysToDisplay.Count == 1 && KeysToDisplay[0] == EmptyHotkey)
{
ResultType = EResultType.Delete;
@ -85,6 +102,9 @@ public partial class HotkeyControlDialog : ContentDialog
//when alt is pressed, the real key should be e.SystemKey
Key key = e.Key == Key.System ? e.SystemKey : e.Key;
if (ChefKeysManager.StartMenuBlocked && key.ToString() == ChefKeysManager.StartMenuSimulatedKey)
return;
SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers();
var hotkeyModel = new HotkeyModel(
@ -168,8 +188,13 @@ public partial class HotkeyControlDialog : ContentDialog
}
}
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>
hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture)
{
if (isOpenFlowHotkey && (hotkey.ToString() == "LWin" || hotkey.ToString() == "RWin"))
return true;
return hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
}
private void Overwrite(object sender, RoutedEventArgs e)
{

View file

@ -65,8 +65,8 @@
<system:String x:Key="LastQueryPreserved">Letzte Abfrage beibehalten</system:String>
<system:String x:Key="LastQuerySelected">Letzte Abfrage auswählen</system:String>
<system:String x:Key="LastQueryEmpty">Letzte Abfrage leeren</system:String>
<system:String x:Key="LastQueryActionKeywordPreserved">Preserve Last Action Keyword</system:String>
<system:String x:Key="LastQueryActionKeywordSelected">Select Last Action Keyword</system:String>
<system:String x:Key="LastQueryActionKeywordPreserved">Letztes Aktions-Schlüsselwort beibehalten</system:String>
<system:String x:Key="LastQueryActionKeywordSelected">Letztes Aktions-Schlüsselwort auswählen</system:String>
<system:String x:Key="KeepMaxResults">Feste Fensterhöhe</system:String>
<system:String x:Key="KeepMaxResultsToolTip">Die Fensterhöhe ist durch Ziehen nicht anpassbar.</system:String>
<system:String x:Key="maxShowResults">Maximal gezeigte Ergebnisse</system:String>

View file

@ -15,6 +15,7 @@
<!-- MainWindow -->
<system:String x:Key="registerHotkeyFailed">Failed to register hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program.</system:String>
<system:String x:Key="unregisterHotkeyFailed">Failed to unregister hotkey "{0}". Please try again or see log for details</system:String>
<system:String x:Key="MessageBoxTitle">Flow Launcher</system:String>
<system:String x:Key="couldnotStartCmd">Could not start {0}</system:String>
<system:String x:Key="invalidFlowLauncherPluginFileFormat">Invalid Flow Launcher plugin file format</system:String>

View file

@ -65,8 +65,8 @@
<system:String x:Key="LastQueryPreserved">Mantener la última consulta</system:String>
<system:String x:Key="LastQuerySelected">Seleccionar la última consulta</system:String>
<system:String x:Key="LastQueryEmpty">Limpiar la última consulta</system:String>
<system:String x:Key="LastQueryActionKeywordPreserved">Conservar palabra clave de última acción</system:String>
<system:String x:Key="LastQueryActionKeywordSelected">Seleccionar palabra clave de última acción</system:String>
<system:String x:Key="LastQueryActionKeywordPreserved">Conservar última palabra clave de acción</system:String>
<system:String x:Key="LastQueryActionKeywordSelected">Seleccionar última palabra clave de acción</system:String>
<system:String x:Key="KeepMaxResults">Altura de la ventana fija</system:String>
<system:String x:Key="KeepMaxResultsToolTip">La altura de la ventana no se puede ajustar arrastrando el ratón.</system:String>
<system:String x:Key="maxShowResults">Número máximo de resultados mostrados</system:String>

View file

@ -9,7 +9,7 @@
<system:String x:Key="runtimePluginChooseRuntimeExecutable">אנא בחר את קובץ ההפעלה {0}</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>
<system:String x:Key="failedToInitializePluginsMessage">תוספים: {0} - נכשלו בטעינה ויושבתו, אנא צור קשר עם יוצרי התוספים לקבלת עזרה</system:String>
<!-- MainWindow -->
<system:String x:Key="registerHotkeyFailed">רישום מקש הקיצור &quot;{0}&quot; נכשל. ייתכן שמקש הקיצור נמצא בשימוש על ידי תוכנה אחרת. שנה למקש קיצור אחר, או צא מהתוכנה האחרת.</system:String>
@ -54,10 +54,10 @@
<system:String x:Key="SearchWindowScreenPrimary">צג ראשי</system:String>
<system:String x:Key="SearchWindowScreenCustom">צג מותאם אישית</system:String>
<system:String x:Key="SearchWindowAlign">Search Window Position on Monitor</system:String>
<system:String x:Key="SearchWindowAlignCenter">Center</system:String>
<system:String x:Key="SearchWindowAlignCenterTop">Center Top</system:String>
<system:String x:Key="SearchWindowAlignLeftTop">Left Top</system:String>
<system:String x:Key="SearchWindowAlignRightTop">Right Top</system:String>
<system:String x:Key="SearchWindowAlignCenter">מרכז</system:String>
<system:String x:Key="SearchWindowAlignCenterTop">מרכז עליון</system:String>
<system:String x:Key="SearchWindowAlignLeftTop">שמאל עליון</system:String>
<system:String x:Key="SearchWindowAlignRightTop">ימין עליון</system:String>
<system:String x:Key="SearchWindowAlignCustom">Custom Position</system:String>
<system:String x:Key="language">שפה</system:String>
<system:String x:Key="lastQueryMode">Last Query Style</system:String>
@ -83,16 +83,16 @@
<system:String x:Key="selectPythonExecutable">Please select pythonw.exe</system:String>
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
<system:String x:Key="autoUpdates">Auto Update</system:String>
<system:String x:Key="select">Select</system:String>
<system:String x:Key="autoUpdates">עדכון אוטומטי</system:String>
<system:String x:Key="select">בחר</system:String>
<system:String x:Key="hideOnStartup">Hide Flow Launcher on startup</system:String>
<system:String x:Key="hideOnStartupToolTip">Flow Launcher search window is hidden in the tray after starting up.</system:String>
<system:String x:Key="hideNotifyIcon">Hide tray icon</system:String>
<system:String x:Key="hideNotifyIconToolTip">When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window.</system:String>
<system:String x:Key="querySearchPrecision">Query Search Precision</system:String>
<system:String x:Key="querySearchPrecisionToolTip">Changes minimum match score required for results.</system:String>
<system:String x:Key="SearchPrecisionNone">None</system:String>
<system:String x:Key="SearchPrecisionLow">Low</system:String>
<system:String x:Key="SearchPrecisionNone">ללא</system:String>
<system:String x:Key="SearchPrecisionLow">נמוך</system:String>
<system:String x:Key="SearchPrecisionRegular">Regular</system:String>
<system:String x:Key="ShouldUsePinyin">Search with Pinyin</system:String>
<system:String x:Key="ShouldUsePinyinToolTip">Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese.</system:String>
@ -120,26 +120,26 @@
<system:String x:Key="priority">Priority</system:String>
<system:String x:Key="priorityToolTip">Change Plugin Results Priority</system:String>
<system:String x:Key="pluginDirectory">Plugin Directory</system:String>
<system:String x:Key="author">by</system:String>
<system:String x:Key="author">מאת</system:String>
<system:String x:Key="plugin_init_time">Init time:</system:String>
<system:String x:Key="plugin_query_time">Query time:</system:String>
<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_query_version">גרסה</system:String>
<system:String x:Key="plugin_query_web">אתר</system:String>
<system:String x:Key="plugin_uninstall">הסר התקנה</system:String>
<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">חנות תוספים</system:String>
<system:String x:Key="pluginStore_NewRelease">New Release</system:String>
<system:String x:Key="pluginStore_NewRelease">שחרור חדש</system:String>
<system:String x:Key="pluginStore_RecentlyUpdated">Recently Updated</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">Uninstall</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="LabelNew">גרסה חדשה</system:String>
<system:String x:Key="LabelNewToolTip">This plugin has been updated within the last 7 days</system:String>
<system:String x:Key="LabelUpdateToolTip">New Update is Available</system:String>
@ -164,7 +164,7 @@
<system:String x:Key="queryBoxFont">Query Box Font</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="resetCustomize">Reset</system:String>
<system:String x:Key="resetCustomize">אפס</system:String>
<system:String x:Key="CustomizeToolTip">Customize</system:String>
<system:String x:Key="windowMode">Window Mode</system:String>
<system:String x:Key="opacity">Opacity</system:String>
@ -196,9 +196,9 @@
<!-- Setting Hotkey -->
<system:String x:Key="hotkey">Hotkey</system:String>
<system:String x:Key="hotkeys">Hotkeys</system:String>
<system:String x:Key="flowlauncherHotkey">Open Flow Launcher</system:String>
<system:String x:Key="hotkey">מקש קיצור</system:String>
<system:String x:Key="hotkeys">מקשי קיצור</system:String>
<system:String x:Key="flowlauncherHotkey">פתח את Flow Launcher</system:String>
<system:String x:Key="flowlauncherHotkeyToolTip">Enter shortcut to show/hide Flow Launcher.</system:String>
<system:String x:Key="previewHotkey">Toggle Preview</system:String>
<system:String x:Key="previewHotkeyToolTip">Enter shortcut to show/hide preview in search window.</system:String>
@ -206,24 +206,24 @@
<system:String x:Key="hotkeyPresetsToolTip">List of currently registered hotkeys</system:String>
<system:String x:Key="openResultModifiers">Open Result Modifier Key</system:String>
<system:String x:Key="openResultModifiersToolTip">Select a modifier key to open selected result via keyboard.</system:String>
<system:String x:Key="showOpenResultHotkey">Show Hotkey</system:String>
<system:String x:Key="showOpenResultHotkey">הצג מקש קיצור</system:String>
<system:String x:Key="showOpenResultHotkeyToolTip">Show result selection hotkey with results.</system:String>
<system:String x:Key="autoCompleteHotkey">Auto Complete</system:String>
<system:String x:Key="autoCompleteHotkeyToolTip">Runs autocomplete for the selected items.</system:String>
<system:String x:Key="SelectNextItemHotkey">Select Next Item</system:String>
<system:String x:Key="SelectPrevItemHotkey">Select Previous Item</system:String>
<system:String x:Key="SelectNextPageHotkey">Next Page</system:String>
<system:String x:Key="SelectPrevPageHotkey">Previous Page</system:String>
<system:String x:Key="SelectNextPageHotkey">הדף הבא</system:String>
<system:String x:Key="SelectPrevPageHotkey">הדף הקודם</system:String>
<system:String x:Key="CycleHistoryUpHotkey">Cycle Previous Query</system:String>
<system:String x:Key="CycleHistoryDownHotkey">Cycle Next Query</system:String>
<system:String x:Key="OpenContextMenuHotkey">Open Context Menu</system:String>
<system:String x:Key="OpenNativeContextMenuHotkey">Open Native Context Menu</system:String>
<system:String x:Key="SettingWindowHotkey">Open Setting Window</system:String>
<system:String x:Key="CopyFilePathHotkey">Copy File Path</system:String>
<system:String x:Key="CopyFilePathHotkey">העתק את נתיב הקובץ</system:String>
<system:String x:Key="ToggleGameModeHotkey">Toggle Game Mode</system:String>
<system:String x:Key="ToggleHistoryHotkey">Toggle History</system:String>
<system:String x:Key="OpenContainFolderHotkey">Open Containing Folder</system:String>
<system:String x:Key="RunAsAdminHotkey">Run As Admin</system:String>
<system:String x:Key="RunAsAdminHotkey">הרץ כמנהל</system:String>
<system:String x:Key="RequeryHotkey">Refresh Search Results</system:String>
<system:String x:Key="ReloadPluginHotkey">Reload Plugins Data</system:String>
<system:String x:Key="QuickWidthHotkey">Quick Adjust Window Width</system:String>
@ -234,13 +234,13 @@
<system:String x:Key="customQueryShortcut">Custom Query Shortcuts</system:String>
<system:String x:Key="builtinShortcuts">Built-in Shortcuts</system:String>
<system:String x:Key="customQuery">שאילתה</system:String>
<system:String x:Key="customShortcut">Shortcut</system:String>
<system:String x:Key="customShortcutExpansion">Expansion</system:String>
<system:String x:Key="builtinShortcutDescription">Description</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">Are you sure you want to delete {0} plugin hotkey?</system:String>
<system:String x:Key="deleteCustomShortcutWarning">Are you sure you want to delete shortcut: {0} with expansion {1}?</system:String>
@ -259,8 +259,8 @@
<system:String x:Key="enableProxy">Enable HTTP Proxy</system:String>
<system:String x:Key="server">HTTP Server</system:String>
<system:String x:Key="port">Port</system:String>
<system:String x:Key="userName">User Name</system:String>
<system:String x:Key="password">Password</system:String>
<system:String x:Key="userName">שם משתמש</system:String>
<system:String x:Key="password">סיסמא</system:String>
<system:String x:Key="testProxy">Test Proxy</system:String>
<system:String x:Key="save">שמור</system:String>
<system:String x:Key="serverCantBeEmpty">Server field can't be empty</system:String>
@ -272,11 +272,11 @@
<!-- Setting About -->
<system:String x:Key="about">אודות</system:String>
<system:String x:Key="website">Website</system:String>
<system:String x:Key="github">GitHub</system:String>
<system:String x:Key="docs">Docs</system:String>
<system:String x:Key="version">Version</system:String>
<system:String x:Key="icons">Icons</system:String>
<system:String x:Key="website">אתר אינטרנט</system:String>
<system:String x:Key="github">Github</system:String>
<system:String x:Key="docs">תיעוד</system:String>
<system:String x:Key="version">גרסה</system:String>
<system:String x:Key="icons">סמלים</system:String>
<system:String x:Key="about_activate_times">You have activated Flow Launcher {0} times</system:String>
<system:String x:Key="checkUpdates">Check for Updates</system:String>
<system:String x:Key="BecomeASponsor">Become A Sponsor</system:String>
@ -302,8 +302,8 @@
<system:String x:Key="fileManagerWindow">Select File Manager</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 fileds 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_name">מנהל קבצים</system:String>
<system:String x:Key="fileManager_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>
@ -314,9 +314,9 @@
<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_newWindow">חלון חדש</system:String>
<system:String x:Key="defaultBrowser_newTab">כרטיסייה חדשה</system:String>
<system:String x:Key="defaultBrowser_parameter">מצב פרטיות</system:String>
<!-- Priority Setting Dialog -->
<system:String x:Key="changePriorityWindow">Change Priority</system:String>
@ -362,14 +362,14 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in
<system:String x:Key="commonSave">שמור</system:String>
<system:String x:Key="commonOverwrite">Overwrite</system:String>
<system:String x:Key="commonCancel">ביטול</system:String>
<system:String x:Key="commonReset">Reset</system:String>
<system:String x:Key="commonReset">אפס</system:String>
<system:String x:Key="commonDelete">מחק</system:String>
<system:String x:Key="commonOK">OK</system:String>
<system:String x:Key="commonYes">Yes</system:String>
<system:String x:Key="commonNo">No</system:String>
<system:String x:Key="commonOK">אישור</system:String>
<system:String x:Key="commonYes">כן</system:String>
<system:String x:Key="commonNo">לא</system:String>
<!-- Crash Reporter -->
<system:String x:Key="reportWindow_version">Version</system:String>
<system:String x:Key="reportWindow_version">גרסה</system:String>
<system:String x:Key="reportWindow_time">זמן</system:String>
<system:String x:Key="reportWindow_reproduce">Please tell us how application crashed so we can fix it</system:String>
<system:String x:Key="reportWindow_send_report">שלח דיווח</system:String>
@ -377,21 +377,21 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in
<system:String x:Key="reportWindow_general">כללי</system:String>
<system:String x:Key="reportWindow_exceptions">חריגים</system:String>
<system:String x:Key="reportWindow_exception_type">Exception Type</system:String>
<system:String x:Key="reportWindow_source">Source</system:String>
<system:String x:Key="reportWindow_source">מקור</system:String>
<system:String x:Key="reportWindow_stack_trace">Stack Trace</system:String>
<system:String x:Key="reportWindow_sending">Sending</system:String>
<system:String x:Key="reportWindow_sending">שולח</system:String>
<system:String x:Key="reportWindow_report_succeed">Report sent successfully</system:String>
<system:String x:Key="reportWindow_report_failed">Failed to send report</system:String>
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcher got an error</system:String>
<!-- 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_already_on_latest">You already have the latest Flow Launcher version</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}
@ -405,7 +405,7 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in
<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_update_restart_flowlauncher_tip">This upgrade will restart Flow Launcher</system:String>
<system:String x:Key="update_flowlauncher_update_update_files">Following files will be updated</system:String>
<system:String x:Key="update_flowlauncher_update_files">Update files</system:String>
<system:String x:Key="update_flowlauncher_update_files">עדכן קבצים</system:String>
<system:String x:Key="update_flowlauncher_update_update_description">Update description</system:String>
<!-- Welcome Window -->
@ -416,7 +416,7 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in
<system:String x:Key="Welcome_Page2_Title">Search and run all files and applications on your PC</system:String>
<system:String x:Key="Welcome_Page2_Text01">Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse.</system:String>
<system:String x:Key="Welcome_Page2_Text02">Flow Launcher starts with the hotkey below, go ahead and try it out now. To change it, click on the input and press the desired hotkey on the keyboard.</system:String>
<system:String x:Key="Welcome_Page3_Title">Hotkeys</system:String>
<system:String x:Key="Welcome_Page3_Title">מקשי קיצור</system:String>
<system:String x:Key="Welcome_Page4_Title">Action Keyword and Commands</system:String>
<system:String x:Key="Welcome_Page4_Text01">Search the web, launch applications or run various functions through Flow Launcher plugins. Certain functions start with an action keyword, and if necessary, they can be used without action keywords. Try the queries below in Flow Launcher.</system:String>
<system:String x:Key="Welcome_Page5_Title">Let's Start Flow Launcher</system:String>

View file

@ -20,6 +20,7 @@
Closing="OnClosing"
Deactivated="OnDeactivated"
Icon="Images/app.png"
SourceInitialized="OnSourceInitialized"
Initialized="OnInitialized"
Left="{Binding Settings.WindowLeft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Loaded="OnLoaded"
@ -37,7 +38,7 @@
mc:Ignorable="d">
<!-- WindowChrome -->
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="9" ResizeBorderThickness="32 4 32 32" />
<WindowChrome CaptionHeight="9" />
</WindowChrome.WindowChrome>
<Window.Resources>
<converters:QuerySuggestionBoxConverter x:Key="QuerySuggestionBoxConverter" />

View file

@ -171,6 +171,11 @@ namespace Flow.Launcher
Environment.Exit(0);
}
private void OnSourceInitialized(object sender, EventArgs e)
{
WindowsInteropHelper.HideFromAltTab(this);
}
private void OnInitialized(object sender, EventArgs e)
{
}

View file

@ -14,4 +14,7 @@ FindWindowEx
WINDOW_STYLE
WM_ENTERSIZEMOVE
WM_EXITSIZEMOVE
WM_EXITSIZEMOVE
SetLastError
WINDOW_EX_STYLE

View file

@ -0,0 +1,106 @@
<Window
x:Class="Flow.Launcher.ProgressBoxEx"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="MessageBoxWindow"
Width="420"
Height="Auto"
Background="{DynamicResource PopuBGColor}"
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Height"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</WindowChrome.WindowChrome>
<Window.InputBindings>
<KeyBinding Key="Escape" Command="Close" />
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="Close" Executed="KeyEsc_OnPress" />
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition MinHeight="68" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Click="Button_Cancel"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,11 27,20 M 18,20 27,11"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
</Grid>
</StackPanel>
</StackPanel>
<Grid Grid.Row="1" Margin="30 0 30 24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
x:Name="TitleTextBlock"
Grid.Row="0"
MaxWidth="400"
Margin="0 0 26 12"
VerticalAlignment="Center"
FontFamily="Segoe UI"
FontSize="20"
FontWeight="SemiBold"
TextAlignment="Left"
TextWrapping="Wrap" />
<ProgressBar
x:Name="ProgressBar"
Grid.Row="1"
Margin="0 0 26 0"
Maximum="100"
Minimum="0"
Value="0" />
</Grid>
<Border
Grid.Row="2"
Margin="0 0 0 0"
Background="{DynamicResource PopupButtonAreaBGColor}"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="0 1 0 0">
<WrapPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="btnCancel"
MinWidth="120"
Margin="5 0 5 0"
Click="Button_Click"
Content="{DynamicResource commonCancel}" />
</WrapPanel>
</Border>
</Grid>
</Window>

View file

@ -0,0 +1,114 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Flow.Launcher.Infrastructure.Logger;
namespace Flow.Launcher
{
public partial class ProgressBoxEx : Window
{
private readonly Action _forceClosed;
private ProgressBoxEx(Action forceClosed)
{
_forceClosed = forceClosed;
InitializeComponent();
}
public static async Task ShowAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null)
{
ProgressBoxEx prgBox = null;
try
{
if (!Application.Current.Dispatcher.CheckAccess())
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
prgBox = new ProgressBoxEx(forceClosed)
{
Title = caption
};
prgBox.TitleTextBlock.Text = caption;
prgBox.Show();
});
}
else
{
prgBox = new ProgressBoxEx(forceClosed)
{
Title = caption
};
prgBox.TitleTextBlock.Text = caption;
prgBox.Show();
}
await reportProgressAsync(prgBox.ReportProgress).ConfigureAwait(false);
}
catch (Exception e)
{
Log.Error($"|ProgressBoxEx.Show|An error occurred: {e.Message}");
await reportProgressAsync(null).ConfigureAwait(false);
}
finally
{
if (!Application.Current.Dispatcher.CheckAccess())
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
prgBox?.Close();
});
}
else
{
prgBox?.Close();
}
}
}
private void ReportProgress(double progress)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.Invoke(() => ReportProgress(progress));
return;
}
if (progress < 0)
{
ProgressBar.Value = 0;
}
else if (progress >= 100)
{
ProgressBar.Value = 100;
Close();
}
else
{
ProgressBar.Value = progress;
}
}
private void KeyEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
{
ForceClose();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ForceClose();
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
ForceClose();
}
private void ForceClose()
{
Close();
_forceClosed?.Invoke();
}
}
}

View file

@ -117,38 +117,35 @@ namespace Flow.Launcher
ShellCommand.Execute(startInfo);
}
public async void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true)
public void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true)
{
if (string.IsNullOrEmpty(stringToCopy))
return;
await Win32Helper.StartSTATaskAsync(() =>
var isFile = File.Exists(stringToCopy);
if (directCopy && (isFile || Directory.Exists(stringToCopy)))
{
var isFile = File.Exists(stringToCopy);
if (directCopy && (isFile || Directory.Exists(stringToCopy)))
{
var paths = new StringCollection
var paths = new StringCollection
{
stringToCopy
};
Clipboard.SetFileDropList(paths);
Clipboard.SetFileDropList(paths);
if (showDefaultNotification)
ShowMsg(
$"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}",
GetTranslation("completedSuccessfully"));
}
else
{
Clipboard.SetDataObject(stringToCopy);
if (showDefaultNotification)
ShowMsg(
$"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}",
GetTranslation("completedSuccessfully"));
}
else
{
Clipboard.SetDataObject(stringToCopy);
if (showDefaultNotification)
ShowMsg(
$"{GetTranslation("copy")} {GetTranslation("textTitle")}",
GetTranslation("completedSuccessfully"));
}
});
if (showDefaultNotification)
ShowMsg(
$"{GetTranslation("copy")} {GetTranslation("textTitle")}",
GetTranslation("completedSuccessfully"));
}
}
public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible;
@ -167,8 +164,8 @@ namespace Flow.Launcher
public Task<Stream> HttpGetStreamAsync(string url, CancellationToken token = default) =>
Http.GetStreamAsync(url);
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath,
CancellationToken token = default) => Http.DownloadAsync(url, filePath, token);
public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null,
CancellationToken token = default) => Http.DownloadAsync(url, filePath, reportProgress, token);
public void AddActionKeyword(string pluginId, string newActionKeyword) =>
PluginManager.AddActionKeyword(pluginId, newActionKeyword);
@ -327,6 +324,8 @@ namespace Flow.Launcher
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) =>
MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult);
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, forceClosed);
#endregion
#region Private Methods

View file

@ -13,8 +13,8 @@ public partial class SettingsPanePluginStoreViewModel : BaseModel
{
public string FilterText { get; set; } = string.Empty;
public IList<PluginStoreItemViewModel> ExternalPlugins => PluginsManifest.UserPlugins
.Select(p => new PluginStoreItemViewModel(p))
public IList<PluginStoreItemViewModel> ExternalPlugins =>
PluginsManifest.UserPlugins?.Select(p => new PluginStoreItemViewModel(p))
.OrderByDescending(p => p.Category == PluginStoreItemViewModel.NewRelease)
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.RecentlyUpdated)
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.None)
@ -24,8 +24,10 @@ public partial class SettingsPanePluginStoreViewModel : BaseModel
[RelayCommand]
private async Task RefreshExternalPluginsAsync()
{
await PluginsManifest.UpdateManifestAsync();
OnPropertyChanged(nameof(ExternalPlugins));
if (await PluginsManifest.UpdateManifestAsync())
{
OnPropertyChanged(nameof(ExternalPlugins));
}
}
public bool SatisfiesFilter(PluginStoreItemViewModel plugin)

View file

@ -34,11 +34,11 @@ public partial class SettingWindow
private void OnLoaded(object sender, RoutedEventArgs e)
{
RefreshMaximizeRestoreButton();
// Fix (workaround) for the window freezes after lock screen (Win+L)
// Fix (workaround) for the window freezes after lock screen (Win+L) or sleep
// https://stackoverflow.com/questions/4951058/software-rendering-mode-wpf
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
HwndTarget hwndTarget = hwndSource.CompositionTarget;
hwndTarget.RenderMode = RenderMode.Default;
hwndTarget.RenderMode = RenderMode.SoftwareOnly; // Must use software only render mode here
InitializePosition();
}

View file

@ -12,6 +12,8 @@ namespace Flow.Launcher.Storage
internal bool IsTopMost(Result result)
{
// origin query is null when user select the context menu item directly of one item from query list
// in this case, we do not need to check if the result is top most
if (records.IsEmpty || result.OriginQuery == null ||
!records.TryGetValue(result.OriginQuery.RawQuery, out var value))
{
@ -24,24 +26,34 @@ namespace Flow.Launcher.Storage
internal void Remove(Result result)
{
// origin query is null when user select the context menu item directly of one item from query list
// in this case, we do not need to remove the record
if (result.OriginQuery == null)
{
return;
}
records.Remove(result.OriginQuery.RawQuery, out _);
}
internal void AddOrUpdate(Result result)
{
// origin query is null when user select the context menu item directly of one item from query list
// in this case, we do not need to add or update the record
if (result.OriginQuery == null)
{
return;
}
var record = new Record
{
PluginID = result.PluginID,
Title = result.Title,
SubTitle = result.SubTitle
SubTitle = result.SubTitle,
RecordKey = result.RecordKey
};
records.AddOrUpdate(result.OriginQuery.RawQuery, record, (key, oldValue) => record);
}
public void Load(Dictionary<string, Record> dictionary)
{
records = new ConcurrentDictionary<string, Record>(dictionary);
}
}
public class Record
@ -49,12 +61,21 @@ namespace Flow.Launcher.Storage
public string Title { get; set; }
public string SubTitle { get; set; }
public string PluginID { get; set; }
public string RecordKey { get; set; }
public bool Equals(Result r)
{
return Title == r.Title
&& SubTitle == r.SubTitle
&& PluginID == r.PluginID;
if (string.IsNullOrEmpty(RecordKey) || string.IsNullOrEmpty(r.RecordKey))
{
return Title == r.Title
&& SubTitle == r.SubTitle
&& PluginID == r.PluginID;
}
else
{
return RecordKey == r.RecordKey
&& PluginID == r.PluginID;
}
}
}
}

View file

@ -15,7 +15,6 @@ namespace Flow.Launcher.Storage
[JsonInclude, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary<string, int> records { get; private set; }
public UserSelectedRecord()
{
recordsWithQuery = new Dictionary<int, int>();
@ -45,12 +44,21 @@ namespace Flow.Launcher.Storage
private static int GenerateResultHashCode(Result result)
{
int hashcode = GenerateStaticHashCode(result.Title);
return GenerateStaticHashCode(result.SubTitle, hashcode);
if (string.IsNullOrEmpty(result.RecordKey))
{
int hashcode = GenerateStaticHashCode(result.Title);
return GenerateStaticHashCode(result.SubTitle, hashcode);
}
else
{
return GenerateStaticHashCode(result.RecordKey);
}
}
private static int GenerateQueryAndResultHashCode(Query query, Result result)
{
// query is null when user select the context menu item directly of one item from query list
// so we only need to consider the result
if (query == null)
{
return GenerateResultHashCode(result);
@ -58,8 +66,16 @@ namespace Flow.Launcher.Storage
int hashcode = GenerateStaticHashCode(query.ActionKeyword);
hashcode = GenerateStaticHashCode(query.Search, hashcode);
hashcode = GenerateStaticHashCode(result.Title, hashcode);
hashcode = GenerateStaticHashCode(result.SubTitle, hashcode);
if (string.IsNullOrEmpty(result.RecordKey))
{
hashcode = GenerateStaticHashCode(result.Title, hashcode);
hashcode = GenerateStaticHashCode(result.SubTitle, hashcode);
}
else
{
hashcode = GenerateStaticHashCode(result.RecordKey, hashcode);
}
return hashcode;
}

View file

@ -34,8 +34,6 @@ namespace Flow.Launcher.ViewModel
private bool _isQueryRunning;
private Query _lastQuery;
private Result lastContextMenuResult = new Result();
private List<Result> lastContextMenuResults = new List<Result>();
private string _queryTextBeforeLeaveResults;
private readonly FlowLauncherJsonStorage<History> _historyItemsStorage;
@ -233,8 +231,8 @@ namespace Flow.Launcher.ViewModel
var token = e.Token == default ? _updateToken : e.Token;
// make a copy of results to avoid plugin change the result when updating view model
var resultsCopy = e.Results.ToList();
// make a clone to avoid possible issue that plugin will also change the list and items when updating view model
var resultsCopy = DeepCloneResults(e.Results, token);
PluginManager.UpdatePluginMetadata(resultsCopy, pair.Metadata, e.Query);
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query,
@ -398,11 +396,15 @@ namespace Flow.Launcher.ViewModel
})
.ConfigureAwait(false);
if (SelectedIsFromQueryResults())
{
_userSelectedRecord.Add(result);
_history.Add(result.OriginQuery.RawQuery);
// origin query is null when user select the context menu item directly of one item from query list
// so we don't want to add it to history
if (result.OriginQuery != null)
{
_history.Add(result.OriginQuery.RawQuery);
}
lastHistoryIndex = 1;
}
@ -412,6 +414,22 @@ namespace Flow.Launcher.ViewModel
}
}
private static IReadOnlyList<Result> DeepCloneResults(IReadOnlyList<Result> results, CancellationToken token = default)
{
var resultsCopy = new List<Result>();
foreach (var result in results.ToList())
{
if (token.IsCancellationRequested)
{
break;
}
var resultCopy = result.Clone();
resultsCopy.Add(resultCopy);
}
return resultsCopy;
}
#endregion
#region BasicCommands
@ -986,19 +1004,10 @@ namespace Flow.Launcher.ViewModel
if (selected != null) // SelectedItem returns null if selection is empty.
{
List<Result> results;
if (selected == lastContextMenuResult)
{
results = lastContextMenuResults;
}
else
{
results = PluginManager.GetContextMenusForPlugin(selected);
lastContextMenuResults = results;
lastContextMenuResult = selected;
results.Add(ContextMenuTopMost(selected));
results.Add(ContextMenuPluginInfo(selected.PluginID));
}
results = PluginManager.GetContextMenusForPlugin(selected);
results.Add(ContextMenuTopMost(selected));
results.Add(ContextMenuPluginInfo(selected.PluginID));
if (!string.IsNullOrEmpty(query))
{
@ -1187,9 +1196,18 @@ namespace Flow.Launcher.ViewModel
currentCancellationToken.ThrowIfCancellationRequested();
results ??= _emptyResult;
IReadOnlyList<Result> resultsCopy;
if (results == null)
{
resultsCopy = _emptyResult;
}
else
{
// make a copy of results to avoid possible issue that FL changes some properties of the records, like score, etc.
resultsCopy = DeepCloneResults(results);
}
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query,
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query,
currentCancellationToken, reSelect)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
@ -1273,6 +1291,8 @@ namespace Flow.Launcher.ViewModel
{
_topMostRecord.Remove(result);
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success"));
App.API.BackToQueryResults();
App.API.ReQuery();
return false;
}
};
@ -1289,6 +1309,8 @@ namespace Flow.Launcher.ViewModel
{
_topMostRecord.AddOrUpdate(result);
App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success"));
App.API.BackToQueryResults();
App.API.ReQuery();
return false;
}
};
@ -1377,8 +1399,6 @@ namespace Flow.Launcher.ViewModel
lastHistoryIndex = 1;
// Trick for no delay
MainWindowOpacity = 0;
lastContextMenuResult = new Result();
lastContextMenuResults = new List<Result>();
if (ExternalPreviewVisible)
CloseExternalPreview();
@ -1476,12 +1496,31 @@ namespace Flow.Launcher.ViewModel
{
result.Score = Result.MaxScore;
}
else if (result.Score != Result.MaxScore)
else
{
var priorityScore = metaResults.Metadata.Priority * 150;
result.Score += result.AddSelectedCount ?
_userSelectedRecord.GetSelectedCount(result) + priorityScore :
priorityScore;
if (result.AddSelectedCount)
{
if ((long)result.Score + _userSelectedRecord.GetSelectedCount(result) + priorityScore > Result.MaxScore)
{
result.Score = Result.MaxScore;
}
else
{
result.Score += _userSelectedRecord.GetSelectedCount(result) + priorityScore;
}
}
else
{
if ((long)result.Score + priorityScore > Result.MaxScore)
{
result.Score = Result.MaxScore;
}
else
{
result.Score += priorityScore;
}
}
}
}
}

View file

@ -90,7 +90,7 @@ namespace Flow.Launcher.ViewModel
private Control _bottomPart2;
public Control BottomPart2 => IsExpanded ? _bottomPart2 ??= new InstalledPluginDisplayBottomData() : null;
public bool HasSettingControl => PluginPair.Plugin is ISettingProvider;
public bool HasSettingControl => PluginPair.Plugin is ISettingProvider settingProvider && settingProvider.CreateSettingPanel() != null;
public Control SettingControl
=> IsExpanded
? _settingControl

View file

@ -182,7 +182,7 @@ namespace Flow.Launcher.ViewModel
/// <summary>
/// To avoid deadlock, this method should not called from main thread
/// </summary>
public void AddResults(IEnumerable<ResultsForUpdate> resultsForUpdates, CancellationToken token, bool reselect = true)
public void AddResults(ICollection<ResultsForUpdate> resultsForUpdates, CancellationToken token, bool reselect = true)
{
var newResults = NewResults(resultsForUpdates);
@ -228,12 +228,12 @@ namespace Flow.Launcher.ViewModel
.ToList();
}
private List<ResultViewModel> NewResults(IEnumerable<ResultsForUpdate> resultsForUpdates)
private List<ResultViewModel> NewResults(ICollection<ResultsForUpdate> resultsForUpdates)
{
if (!resultsForUpdates.Any())
return Results;
return Results.Where(r => r != null && !resultsForUpdates.Any(u => u.ID == r.Result.PluginID))
return Results.Where(r => r?.Result != null && resultsForUpdates.All(u => u.ID != r.Result.PluginID))
.Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)))
.OrderByDescending(rv => rv.Result.Score)
.ToList();

View file

@ -95,7 +95,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.1" />
</ItemGroup>
</Project>

View file

@ -4,7 +4,7 @@
"Name": "Browser Bookmarks",
"Description": "Search your browser bookmarks",
"Author": "qianlifeng, Ioannis G.",
"Version": "3.3.3",
"Version": "3.3.4",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll",

View file

@ -62,7 +62,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Mages" Version="2.0.2" />
<PackageReference Include="Mages" Version="3.0.0" />
</ItemGroup>
</Project>

View file

@ -4,7 +4,7 @@
"Name": "Calculator",
"Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)",
"Author": "cxfksword",
"Version": "3.1.4",
"Version": "3.1.5",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Calculator.dll",

View file

@ -242,6 +242,7 @@ namespace Flow.Launcher.Plugin.Explorer
var name = "Plugin: Folder";
var message = $"File not found: {e.Message}";
Context.API.ShowMsgError(name, message);
return false;
}
return true;

View file

@ -29,8 +29,8 @@
<system:String x:Key="plugin_explorer_everything_setting_header">Everything Setting</system:String>
<system:String x:Key="plugin_explorer_previewpanel_setting_header">Preview Panel</system:String>
<system:String x:Key="plugin_explorer_previewpanel_display_file_size_checkbox">Size</system:String>
<system:String x:Key="plugin_explorer_previewpanel_display_file_creation_checkbox">Date Created</system:String>
<system:String x:Key="plugin_explorer_previewpanel_display_file_modification_checkbox">Date Modified</system:String>
<system:String x:Key="plugin_explorer_previewpanel_display_file_creation_checkbox">תאריך יצירה</system:String>
<system:String x:Key="plugin_explorer_previewpanel_display_file_modification_checkbox">תאריך שינוי</system:String>
<system:String x:Key="plugin_explorer_previewpanel_file_info_label">Display File Info</system:String>
<system:String x:Key="plugin_explorer_previewpanel_date_and_time_format_label">Date and time format</system:String>
<system:String x:Key="plugin_explorer_everything_sort_option">Sort Option:</system:String>
@ -126,20 +126,20 @@
<system:String x:Key="flowlauncher_plugin_everything_sdk_issue">Failed to load Everything SDK</system:String>
<system:String x:Key="flowlauncher_plugin_everything_is_not_running">אזהרה: שירות Everything אינו פועל</system:String>
<system:String x:Key="flowlauncher_plugin_everything_query_error">שגיאה במהלך שאילתה לEverything</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by">Sort By</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by">מיין לפי</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_name">Name</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_path">Path</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_path">נתיב</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_size">Size</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_extension">Extension</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_type_name">Type Name</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_created">Date Created</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_modified">Date Modified</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_attributes">Attributes</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_created">תאריך יצירה</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_modified">תאריך שינוי</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_attributes">מאפיינים</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_file_list_filename">File List FileName</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_run_count">Run Count</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_recently_changed">Date Recently Changed</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_accessed">Date Accessed</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_run">Date Run</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_recently_changed">תאריך שינוי אחרון</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_accessed">תאריך גישה</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_date_run">תאריך הרצה</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_ascending">↑</system:String>
<system:String x:Key="flowlauncher_plugin_everything_sort_by_descending">↓</system:String>
<system:String x:Key="flowlauncher_plugin_everything_nonfastsort_warning">Warning: This is not a Fast Sort option, searches may be slow</system:String>
@ -149,11 +149,11 @@
<system:String x:Key="flowlauncher_plugin_everything_click_to_launch_or_install">Click to launch or install Everything</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installing_title">Everything Installation</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installing_subtitle">Installing Everything service. Please wait...</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installationsuccess_subtitle">Successfully installed Everything service</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installationfailed_subtitle">Failed to automatically install Everything service. Please manually install it from https://www.voidtools.com</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installing_subtitle">מתקין את שירות Everything. אנא המתן...</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installationsuccess_subtitle">שירות Everything הותקן בהצלחה</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installationfailed_subtitle">התקנה אוטומטית של שירות Everything נכשלה. אנא הורד אותו ידנית מ- https://www.voidtools.com</system:String>
<system:String x:Key="flowlauncher_plugin_everything_run_service">Click here to start it</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installing_select">Unable to find an Everything installation, would you like to manually select a location?{0}{0}Click no and Everything will be automatically installed for you</system:String>
<system:String x:Key="flowlauncher_plugin_everything_installing_select">לא מצליח למצוא התקנה של Everything, האם תרצה לבחור מיקום באופן ידני?{0}{0}לחץ על לא וEverything יותקן עבורך אוטומטית</system:String>
<system:String x:Key="flowlauncher_plugin_everything_enable_content_search">Do you want to enable content search for Everything?</system:String>
<system:String x:Key="flowlauncher_plugin_everything_enable_content_search_tips">It can be very slow without index (which is only supported in Everything v1.5+)</system:String>

View file

@ -22,7 +22,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
internal const string WindowsIndexErrorImagePath = "Images\\index_error2.png";
internal const string GeneralSearchErrorImagePath = "Images\\robot_error.png";
internal const string ToolTipOpenDirectory = "Ctrl + Enter to open the directory";
internal const string ToolTipOpenContainingFolder = "Ctrl + Enter to open the containing folder";
@ -31,6 +30,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
internal const string DefaultContentSearchActionKeyword = "doc:";
internal const char UnixDirectorySeparator = '/';
internal const char DirectorySeparator = '\\';
internal const string WindowsIndexingOptions = "srchadmin.dll";

View file

@ -187,6 +187,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
var needToExpand = EnvironmentVariables.HasEnvironmentVar(querySearch);
var path = needToExpand ? Environment.ExpandEnvironmentVariables(querySearch) : querySearch;
// if user uses the unix directory separator, we need to convert it to windows directory separator
path = path.Replace(Constants.UnixDirectorySeparator, Constants.DirectorySeparator);
// Check that actual location exists, otherwise directory search will throw directory not found exception
if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path).LocationExists())
return results.ToList();

View file

@ -10,7 +10,7 @@
"Name": "Explorer",
"Description": "Find and manage files and folders via Windows Search or Everything",
"Author": "Jeremy Wu",
"Version": "3.2.3",
"Version": "3.2.4",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll",

View file

@ -4,7 +4,7 @@
"Name": "Plugin Indicator",
"Description": "Provides plugin action keyword suggestions",
"Author": "qianlifeng",
"Version": "3.0.6",
"Version": "3.0.7",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginIndicator.dll",

View file

@ -51,7 +51,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
return query.FirstSearch.ToLower() switch
{
//search could be url, no need ToLower() when passed in
Settings.InstallCommand => await pluginManager.RequestInstallOrUpdate(query.SecondToEndSearch, token, query.IsReQuery),
Settings.InstallCommand => await pluginManager.RequestInstallOrUpdateAsync(query.SecondToEndSearch, token, query.IsReQuery),
Settings.UninstallCommand => pluginManager.RequestUninstall(query.SecondToEndSearch),
Settings.UpdateCommand => await pluginManager.RequestUpdateAsync(query.SecondToEndSearch, token, query.IsReQuery),
_ => pluginManager.GetDefaultHotKeys().Where(hotkey =>

View file

@ -17,7 +17,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
internal class PluginsManager
{
const string zip = "zip";
private const string zip = "zip";
private PluginInitContext Context { get; set; }
@ -144,34 +144,43 @@ namespace Flow.Launcher.Plugin.PluginsManager
try
{
using var cts = new CancellationTokenSource();
if (!plugin.IsFromLocalInstallPath)
{
if (File.Exists(filePath))
File.Delete(filePath);
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
await DownloadFileAsync(
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {plugin.Name}",
plugin.UrlDownload, filePath, cts);
}
else
{
filePath = plugin.LocalInstallPath;
}
Install(plugin, filePath);
// check if user cancelled download before installing plugin
if (cts.IsCancellationRequested)
return;
else
Install(plugin, filePath);
}
catch (HttpRequestException e)
{
// show error message
Context.API.ShowMsgError(
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), plugin.Name),
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
return;
}
catch (Exception e)
{
// show error message
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
plugin.Name));
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
return;
}
@ -190,6 +199,41 @@ namespace Flow.Launcher.Plugin.PluginsManager
}
}
private async Task DownloadFileAsync(string prgBoxTitle, string downloadUrl, string filePath, CancellationTokenSource cts, bool deleteFile = true, bool showProgress = true)
{
if (deleteFile && File.Exists(filePath))
File.Delete(filePath);
if (showProgress)
{
var exceptionHappened = false;
await Context.API.ShowProgressBoxAsync(prgBoxTitle,
async (reportProgress) =>
{
if (reportProgress == null)
{
// when reportProgress is null, it means there is expcetion with the progress box
// so we record it with exceptionHappened and return so that progress box will close instantly
exceptionHappened = true;
return;
}
else
{
await Context.API.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 Context.API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
}
else
{
await Context.API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
}
}
internal async ValueTask<List<Result>> RequestUpdateAsync(string search, CancellationToken token,
bool usePrimaryUrlOnly = false)
{
@ -277,43 +321,48 @@ namespace Flow.Launcher.Plugin.PluginsManager
_ = Task.Run(async delegate
{
using var cts = new CancellationTokenSource();
if (!x.PluginNewUserPlugin.IsFromLocalInstallPath)
{
if (File.Exists(downloadToFilePath))
{
File.Delete(downloadToFilePath);
}
await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
.ConfigureAwait(false);
await DownloadFileAsync(
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {x.PluginNewUserPlugin.Name}",
x.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
}
else
{
downloadToFilePath = x.PluginNewUserPlugin.LocalInstallPath;
}
PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin,
downloadToFilePath);
if (Settings.AutoRestartAfterChanging)
// check if user cancelled download before installing plugin
if (cts.IsCancellationRequested)
{
Context.API.ShowMsg(
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
string.Format(
Context.API.GetTranslation(
"plugin_pluginsmanager_update_success_restart"),
x.Name));
Context.API.RestartApp();
return;
}
else
{
Context.API.ShowMsg(
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
string.Format(
Context.API.GetTranslation(
"plugin_pluginsmanager_update_success_no_restart"),
x.Name));
PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin,
downloadToFilePath);
if (Settings.AutoRestartAfterChanging)
{
Context.API.ShowMsg(
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
string.Format(
Context.API.GetTranslation(
"plugin_pluginsmanager_update_success_restart"),
x.Name));
Context.API.RestartApp();
}
else
{
Context.API.ShowMsg(
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
string.Format(
Context.API.GetTranslation(
"plugin_pluginsmanager_update_success_no_restart"),
x.Name));
}
}
}).ContinueWith(t =>
{
@ -324,7 +373,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
x.Name));
}, TaskContinuationOptions.OnlyOnFaulted);
}, token, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
return true;
},
@ -337,7 +386,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
});
// Update all result
if (resultsForUpdate.Count() > 1)
if (resultsForUpdate.Count > 1)
{
var updateAllResult = new Result
{
@ -351,13 +400,13 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
message = string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt"),
resultsForUpdate.Count(), Environment.NewLine);
resultsForUpdate.Count, Environment.NewLine);
}
else
{
message = string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt_no_restart"),
resultsForUpdate.Count());
resultsForUpdate.Count);
}
if (Context.API.ShowMsgBox(message,
@ -374,16 +423,18 @@ namespace Flow.Launcher.Plugin.PluginsManager
try
{
if (File.Exists(downloadToFilePath))
{
File.Delete(downloadToFilePath);
}
using var cts = new CancellationTokenSource();
await Http.DownloadAsync(plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
.ConfigureAwait(false);
await DownloadFileAsync(
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {plugin.PluginNewUserPlugin.Name}",
plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
PluginManager.UpdatePlugin(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin,
downloadToFilePath);
// check if user cancelled download before installing plugin
if (cts.IsCancellationRequested)
return;
else
PluginManager.UpdatePlugin(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin,
downloadToFilePath);
}
catch (Exception ex)
{
@ -401,7 +452,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_restart"),
resultsForUpdate.Count()));
resultsForUpdate.Count));
Context.API.RestartApp();
}
else
@ -409,7 +460,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
string.Format(
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_no_restart"),
resultsForUpdate.Count()));
resultsForUpdate.Count));
}
return true;
@ -483,7 +534,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
return false;
}
Application.Current.MainWindow.Hide();
Context.API.HideMainWindow();
_ = InstallOrUpdateAsync(plugin);
return ShouldHideWindow;
@ -521,7 +572,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
return false;
}
Application.Current.MainWindow.Hide();
Context.API.HideMainWindow();
_ = InstallOrUpdateAsync(plugin);
return ShouldHideWindow;
@ -545,7 +596,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
Context.API.GetAllPlugins().Any(x => x.Metadata.Website.StartsWith(constructedUrlPart));
}
internal async ValueTask<List<Result>> RequestInstallOrUpdate(string search, CancellationToken token,
internal async ValueTask<List<Result>> RequestInstallOrUpdateAsync(string search, CancellationToken token,
bool usePrimaryUrlOnly = false)
{
await PluginsManifest.UpdateManifestAsync(token, usePrimaryUrlOnly);
@ -575,7 +626,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
return ShouldHideWindow;
}
Application.Current.MainWindow.Hide();
Context.API.HideMainWindow();
_ = InstallOrUpdateAsync(x); // No need to wait
return ShouldHideWindow;
},
@ -652,7 +703,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Application.Current.MainWindow.Hide();
Context.API.HideMainWindow();
Uninstall(x.Metadata);
if (Settings.AutoRestartAfterChanging)
{

View file

@ -1,10 +1,9 @@
using Flow.Launcher.Core.ExternalPlugins;
using Flow.Launcher.Infrastructure.UserSettings;
using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.Json;
namespace Flow.Launcher.Plugin.PluginsManager
{
@ -72,12 +71,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
if (pluginJsonEntry != null)
{
using (StreamReader reader = new StreamReader(pluginJsonEntry.Open()))
{
string pluginJsonContent = reader.ReadToEnd();
plugin = JsonConvert.DeserializeObject<UserPlugin>(pluginJsonContent);
plugin.IcoPath = "Images\\zipfolder.png";
}
using Stream stream = pluginJsonEntry.Open();
plugin = JsonSerializer.Deserialize<UserPlugin>(stream);
plugin.IcoPath = "Images\\zipfolder.png";
}
}

View file

@ -6,7 +6,7 @@
"Name": "Plugins Manager",
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
"Author": "Jeremy Wu",
"Version": "3.2.3",
"Version": "3.2.4",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",

View file

@ -4,7 +4,7 @@
"Name":"Process Killer",
"Description":"Kill running processes from Flow",
"Author":"Flow-Launcher",
"Version":"3.0.7",
"Version":"3.0.8",
"Language":"csharp",
"Website":"https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller",
"IcoPath":"Images\\app.png",

View file

@ -36,6 +36,8 @@
<system:String x:Key="flowlauncher_plugin_program_enable_hideuninstallers_tooltip">Hides programs with common uninstaller names, such as unins000.exe</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_description">Search in Program Description</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_description_tooltip">Flow will search program's description</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_hideduplicatedwindowsapp">Hide duplicated apps</system:String>
<system:String x:Key="flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip">Hide duplicated Win32 programs that are already in the UWP list</system:String>
<system:String x:Key="flowlauncher_plugin_program_suffixes_header">Suffixes</system:String>
<system:String x:Key="flowlauncher_plugin_program_max_depth_header">Max Depth</system:String>

View file

@ -41,9 +41,38 @@ namespace Flow.Launcher.Plugin.Program
"uninst000.exe",
"uninstall.exe"
};
// For cases when the uninstaller is named like "Uninstall Program Name.exe"
private const string CommonUninstallerPrefix = "uninstall";
private const string CommonUninstallerSuffix = ".exe";
private static readonly string[] commonUninstallerPrefixs =
{
"uninstall",//en
"卸载",//zh-cn
"卸載",//zh-tw
"видалити",//uk-UA
"удалить",//ru
"désinstaller",//fr
"アンインストール",//ja
"deïnstalleren",//nl
"odinstaluj",//pl
"afinstallere",//da
"deinstallieren",//de
"삭제",//ko
"деинсталирај",//sr
"desinstalar",//pt-pt
"desinstalar",//pt-br
"desinstalar",//es
"desinstalar",//es-419
"disinstallare",//it
"avinstallere",//nb-NO
"odinštalovať",//sk
"kaldır",//tr
"odinstalovat",//cs
"إلغاء التثبيت",//ar
"gỡ bỏ",//vi-vn
"הסרה"//he
};
private const string ExeUninstallerSuffix = ".exe";
private const string InkUninstallerSuffix = ".lnk";
private static readonly string WindowsAppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsApps");
static Main()
{
@ -63,11 +92,20 @@ namespace Flow.Launcher.Plugin.Program
{
try
{
// Collect all UWP Windows app directories
var uwpsDirectories = _settings.HideDuplicatedWindowsApp ? _uwps
.Where(uwp => !string.IsNullOrEmpty(uwp.Location)) // Exclude invalid paths
.Where(uwp => uwp.Location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase)) // Keep system apps
.Select(uwp => uwp.Location.TrimEnd('\\')) // Remove trailing slash
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray() : null;
return _win32s.Cast<IProgram>()
.Concat(_uwps)
.AsParallel()
.WithCancellation(token)
.Where(HideUninstallersFilter)
.Where(p => HideDuplicatedWindowsAppFilter(p, uwpsDirectories))
.Where(p => p.Enabled)
.Select(p => p.Result(query.Search, Context.API))
.Where(r => r?.Score > 0)
@ -96,10 +134,50 @@ namespace Flow.Launcher.Plugin.Program
{
if (!_settings.HideUninstallers) return true;
if (program is not Win32 win32) return true;
// First check the executable path
var fileName = Path.GetFileName(win32.ExecutablePath);
return !commonUninstallerNames.Contains(fileName, StringComparer.OrdinalIgnoreCase) &&
!(fileName.StartsWith(CommonUninstallerPrefix, StringComparison.OrdinalIgnoreCase) &&
fileName.EndsWith(CommonUninstallerSuffix, StringComparison.OrdinalIgnoreCase));
// For cases when the uninstaller is named like "uninst.exe"
if (commonUninstallerNames.Contains(fileName, StringComparer.OrdinalIgnoreCase)) return false;
// For cases when the uninstaller is named like "Uninstall Program Name.exe"
foreach (var prefix in commonUninstallerPrefixs)
{
if (fileName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) &&
fileName.EndsWith(ExeUninstallerSuffix, StringComparison.OrdinalIgnoreCase))
return false;
}
// Second check the lnk path
if (!string.IsNullOrEmpty(win32.LnkResolvedPath))
{
var inkFileName = Path.GetFileName(win32.FullPath);
// For cases when the uninstaller is named like "Uninstall Program Name.ink"
foreach (var prefix in commonUninstallerPrefixs)
{
if (inkFileName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) &&
inkFileName.EndsWith(InkUninstallerSuffix, StringComparison.OrdinalIgnoreCase))
return false;
}
}
return true;
}
private static bool HideDuplicatedWindowsAppFilter(IProgram program, string[] uwpsDirectories)
{
if (uwpsDirectories == null || uwpsDirectories.Length == 0) return true;
if (program is UWPApp) return true;
var location = program.Location.TrimEnd('\\'); // Ensure trailing slash
if (string.IsNullOrEmpty(location))
return true; // Keep if location is invalid
if (!location.StartsWith(WindowsAppPath, StringComparison.OrdinalIgnoreCase))
return true; // Keep if not a Windows app
// Check if the any Win32 executable directory contains UWP Windows app location matches
return !uwpsDirectories.Any(uwpDirectory =>
location.StartsWith(uwpDirectory, StringComparison.OrdinalIgnoreCase));
}
public async Task InitAsync(PluginInitContext context)
@ -214,6 +292,8 @@ namespace Flow.Launcher.Plugin.Program
Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_success"),
Context.API.GetTranslation(
"flowlauncher_plugin_program_disable_dlgtitle_success_message"));
Context.API.BackToQueryResults();
Context.API.ReQuery();
return false;
},
IcoPath = "Images/disable.png",

View file

@ -121,6 +121,7 @@ namespace Flow.Launcher.Plugin.Program
public bool EnableRegistrySource { get; set; } = true;
public bool EnablePathSource { get; set; } = false;
public bool EnableUWP { get; set; } = true;
public bool HideDuplicatedWindowsApp { get; set; } = false;
internal const char SuffixSeparator = ';';
}

View file

@ -8,7 +8,7 @@
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<Grid Margin="0">
<Grid.RowDefinitions>
@ -18,40 +18,40 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DockPanel
Margin="70,10,0,8"
Margin="70 10 0 8"
HorizontalAlignment="Stretch"
LastChildFill="True">
<TextBlock
MinWidth="120"
Margin="0,5,10,0"
Margin="0 5 10 0"
Text="{DynamicResource flowlauncher_plugin_program_index_source}" />
<WrapPanel
Width="Auto"
Margin="0,0,14,0"
Margin="0 0 14 0"
HorizontalAlignment="Right"
DockPanel.Dock="Right">
<CheckBox
Name="UWPEnabled"
Margin="12,0,12,0"
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}"
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_uwp}"
IsChecked="{Binding EnableUWP}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}" />
ToolTip="{DynamicResource flowlauncher_plugin_program_index_uwp_tooltip}"
Visibility="{Binding ShowUWPCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox
Name="StartMenuEnabled"
Margin="12,0,12,0"
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_start}"
IsChecked="{Binding EnableStartMenuSource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_start_tooltip}" />
<CheckBox
Name="RegistryEnabled"
Margin="12,0,12,0"
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_registry}"
IsChecked="{Binding EnableRegistrySource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_registry_tooltip}" />
<CheckBox
Name="PATHEnabled"
Margin="12,0,12,0"
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_index_PATH}"
IsChecked="{Binding EnablePATHSource}"
ToolTip="{DynamicResource flowlauncher_plugin_program_index_PATH_tooltip}" />
@ -67,21 +67,20 @@
BorderBrush="{DynamicResource Color03B}"
BorderThickness="1" />
<DockPanel
Margin="70,10,0,8"
Margin="70 10 0 8"
HorizontalAlignment="Stretch"
LastChildFill="True">
<TextBlock
MinWidth="120"
Margin="0,5,10,0"
Margin="0 5 10 0"
Text="{DynamicResource flowlauncher_plugin_program_index_option}" />
<WrapPanel
Width="Auto"
Margin="0,0,14,0"
Margin="0 0 14 0"
HorizontalAlignment="Right"
DockPanel.Dock="Right">
<CheckBox
Name="HideLnkEnabled"
Margin="12,0,12,0"
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath}"
IsChecked="{Binding HideAppsPath}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hidelnkpath_tooltip}" />
@ -91,11 +90,15 @@
IsChecked="{Binding HideUninstallers}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideuninstallers_tooltip}" />
<CheckBox
Name="DescriptionEnabled"
Margin="12,0,12,0"
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_enable_description}"
IsChecked="{Binding EnableDescription}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_description_tooltip}" />
<CheckBox
Margin="12 0 12 0"
Content="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp}"
IsChecked="{Binding HideDuplicatedWindowsApp}"
ToolTip="{DynamicResource flowlauncher_plugin_program_enable_hideduplicatedwindowsapp_tooltip}" />
</WrapPanel>
</DockPanel>
<Separator
@ -103,28 +106,28 @@
BorderBrush="{DynamicResource Color03B}"
BorderThickness="1" />
<StackPanel
Margin="60,0,0,2"
Margin="60 0 0 2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="btnLoadAllProgramSource"
MinWidth="120"
Margin="10,10,5,10"
Margin="10 10 5 10"
HorizontalAlignment="Right"
Click="btnLoadAllProgramSource_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_all_programs}" />
<Button
x:Name="btnProgramSuffixes"
MinWidth="120"
Margin="5,10,5,10"
Margin="5 10 5 10"
HorizontalAlignment="Right"
Click="BtnProgramSuffixes_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_suffixes}" />
<Button
x:Name="btnReindex"
MinWidth="120"
Margin="5,10,5,10"
Margin="5 10 5 10"
HorizontalAlignment="Right"
Click="btnReindex_Click"
Content="{DynamicResource flowlauncher_plugin_program_reindex}" />
@ -142,7 +145,7 @@
Minimum="0" />
<TextBlock
Height="20"
Margin="10,0,0,0"
Margin="10 0 0 0"
HorizontalAlignment="Center"
Text="{DynamicResource flowlauncher_plugin_program_indexing}" />
</StackPanel>
@ -151,7 +154,7 @@
<ListView
x:Name="programSourceView"
Grid.Row="2"
Margin="70,0,20,0"
Margin="70 0 20 0"
AllowDrop="True"
BorderBrush="DarkGray"
BorderThickness="1"
@ -203,7 +206,7 @@
<DockPanel
Grid.Row="3"
Grid.RowSpan="1"
Margin="0,0,20,0">
Margin="0 0 20 0">
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button
x:Name="btnProgramSourceStatus"
@ -220,7 +223,7 @@
<Button
x:Name="btnAddProgramSource"
MinWidth="100"
Margin="10,10,0,10"
Margin="10 10 0 10"
Click="btnAddProgramSource_OnClick"
Content="{DynamicResource flowlauncher_plugin_program_add}" />
</StackPanel>

View file

@ -57,6 +57,16 @@ namespace Flow.Launcher.Plugin.Program.Views
}
}
public bool HideDuplicatedWindowsApp
{
get => _settings.HideDuplicatedWindowsApp;
set
{
Main.ResetCache();
_settings.HideDuplicatedWindowsApp = value;
}
}
public bool EnableRegistrySource
{
get => _settings.EnableRegistrySource;

View file

@ -4,7 +4,7 @@
"Name": "Program",
"Description": "Search programs in Flow.Launcher",
"Author": "qianlifeng",
"Version": "3.3.3",
"Version": "3.3.4",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Program.dll",

View file

@ -7,6 +7,7 @@
<system:String x:Key="flowlauncher_plugin_cmd_press_any_key_to_close">Press any key to close this window...</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_leave_cmd_open">Do not close Command Prompt after command execution</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_always_run_as_administrator">Always run as administrator</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_use_windows_terminal">Use Windows Terminal</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_run_as_different_user">Run as different user</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_plugin_name">Shell</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_plugin_description">Allows to execute system commands from Flow Launcher</system:String>
@ -15,4 +16,4 @@
<system:String x:Key="flowlauncher_plugin_cmd_run_as_administrator">Run As Administrator</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_copy">Copy the command</system:String>
<system:String x:Key="flowlauncher_plugin_cmd_history">Only show number of most used commands:</system:String>
</ResourceDictionary>
</ResourceDictionary>

View file

@ -202,28 +202,31 @@ namespace Flow.Launcher.Plugin.Shell
{
case Shell.Cmd:
{
info.FileName = "cmd.exe";
info.Arguments = $"{(_settings.LeaveShellOpen ? "/k" : "/c")} {command} {(_settings.CloseShellAfterPress ? $"&& echo {context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")} && pause > nul /c" : "")}";
//// Use info.Arguments instead of info.ArgumentList to enable users better control over the arguments they are writing.
//// Previous code using ArgumentList, commands needed to be separated correctly:
//// Incorrect:
// info.ArgumentList.Add(_settings.LeaveShellOpen ? "/k" : "/c");
// info.ArgumentList.Add(command); //<== info.ArgumentList.Add("mkdir \"c:\\test new\"");
//// Correct version should be:
//info.ArgumentList.Add(_settings.LeaveShellOpen ? "/k" : "/c");
//info.ArgumentList.Add("mkdir");
//info.ArgumentList.Add(@"c:\test new");
//https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0#remarks
if (_settings.UseWindowsTerminal)
{
info.FileName = "wt.exe";
info.ArgumentList.Add("cmd");
}
else
{
info.FileName = "cmd.exe";
}
info.ArgumentList.Add($"{(_settings.LeaveShellOpen ? "/k" : "/c")} {command} {(_settings.CloseShellAfterPress ? $"&& echo {context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")} && pause > nul /c" : "")}");
break;
}
case Shell.Powershell:
{
info.FileName = "powershell.exe";
if (_settings.UseWindowsTerminal)
{
info.FileName = "wt.exe";
info.ArgumentList.Add("powershell");
}
else
{
info.FileName = "powershell.exe";
}
if (_settings.LeaveShellOpen)
{
info.ArgumentList.Add("-NoExit");
@ -232,21 +235,28 @@ namespace Flow.Launcher.Plugin.Shell
else
{
info.ArgumentList.Add("-Command");
info.ArgumentList.Add($"{command}; {(_settings.CloseShellAfterPress ? $"Write-Host '{context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")}'; [System.Console]::ReadKey(); exit" : "")}");
info.ArgumentList.Add($"{command}\\; {(_settings.CloseShellAfterPress ? $"Write-Host '{context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")}'\\; [System.Console]::ReadKey()\\; exit" : "")}");
}
break;
}
case Shell.Pwsh:
{
info.FileName = "pwsh.exe";
if (_settings.UseWindowsTerminal)
{
info.FileName = "wt.exe";
info.ArgumentList.Add("pwsh");
}
else
{
info.FileName = "pwsh.exe";
}
if (_settings.LeaveShellOpen)
{
info.ArgumentList.Add("-NoExit");
}
info.ArgumentList.Add("-Command");
info.ArgumentList.Add($"{command}; {(_settings.CloseShellAfterPress ? $"Write-Host '{context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")}'; [System.Console]::ReadKey(); exit" : "")}");
info.ArgumentList.Add($"{command}\\; {(_settings.CloseShellAfterPress ? $"Write-Host '{context.API.GetTranslation("flowlauncher_plugin_cmd_press_any_key_to_close")}'\\; [System.Console]::ReadKey()\\; exit" : "")}");
break;
}

View file

@ -14,6 +14,8 @@ namespace Flow.Launcher.Plugin.Shell
public bool RunAsAdministrator { get; set; } = true;
public bool UseWindowsTerminal { get; set; } = false;
public bool ShowOnlyMostUsedCMDs { get; set; }
public int ShowOnlyMostUsedCMDsNumber { get; set; }

View file

@ -16,6 +16,7 @@
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<CheckBox
x:Name="ReplaceWinR"
@ -41,9 +42,15 @@
Margin="10,5,5,5"
HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_cmd_always_run_as_administrator}" />
<CheckBox
x:Name="UseWindowsTerminal"
Grid.Row="4"
Margin="10,5,5,5"
HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_cmd_use_windows_terminal}" />
<ComboBox
x:Name="ShellComboBox"
Grid.Row="4"
Grid.Row="5"
Margin="10,5,5,5"
HorizontalAlignment="Left">
<ComboBoxItem>CMD</ComboBoxItem>
@ -51,7 +58,7 @@
<ComboBoxItem>Pwsh</ComboBoxItem>
<ComboBoxItem>RunCommand</ComboBoxItem>
</ComboBox>
<StackPanel Grid.Row="5" Orientation="Horizontal">
<StackPanel Grid.Row="6" Orientation="Horizontal">
<CheckBox
x:Name="ShowOnlyMostUsedCMDs"
Margin="10,5,5,5"

View file

@ -23,6 +23,8 @@ namespace Flow.Launcher.Plugin.Shell
LeaveShellOpen.IsChecked = _settings.LeaveShellOpen;
AlwaysRunAsAdministrator.IsChecked = _settings.RunAsAdministrator;
UseWindowsTerminal.IsChecked = _settings.UseWindowsTerminal;
LeaveShellOpen.IsEnabled = _settings.Shell != Shell.RunCommand;
@ -76,6 +78,16 @@ namespace Flow.Launcher.Plugin.Shell
_settings.RunAsAdministrator = false;
};
UseWindowsTerminal.Checked += (o, e) =>
{
_settings.UseWindowsTerminal = true;
};
UseWindowsTerminal.Unchecked += (o, e) =>
{
_settings.UseWindowsTerminal = false;
};
ReplaceWinR.Checked += (o, e) =>
{
_settings.ReplaceWinR = true;

View file

@ -4,7 +4,7 @@
"Name": "Shell",
"Description": "Provide executing commands from Flow Launcher",
"Author": "qianlifeng",
"Version": "3.2.4",
"Version": "3.2.5",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Shell.dll",

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
@ -9,6 +10,7 @@ using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.SharedCommands;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Security;
using Windows.Win32.System.Shutdown;
using Application = System.Windows.Application;
using Control = System.Windows.Controls.Control;
@ -20,6 +22,10 @@ namespace Flow.Launcher.Plugin.Sys
private PluginInitContext context;
private Dictionary<string, string> KeywordTitleMappings = new Dictionary<string, string>();
// SHTDN_REASON_MAJOR_OTHER indicates a generic shutdown reason that isn't categorized under hardware failure, software updates, or other predefined reasons.
// SHTDN_REASON_FLAG_PLANNED marks the shutdown as planned rather than an unexpected shutdown or failure
private const SHUTDOWN_REASON REASON = SHUTDOWN_REASON.SHTDN_REASON_MAJOR_OTHER | SHUTDOWN_REASON.SHTDN_REASON_FLAG_PLANNED;
public Control CreateSettingPanel()
{
var results = Commands();
@ -100,6 +106,44 @@ namespace Flow.Launcher.Plugin.Sys
};
}
private static unsafe bool EnableShutdownPrivilege()
{
try
{
if (!PInvoke.OpenProcessToken(Process.GetCurrentProcess().SafeHandle, TOKEN_ACCESS_MASK.TOKEN_ADJUST_PRIVILEGES | TOKEN_ACCESS_MASK.TOKEN_QUERY, out var tokenHandle))
{
return false;
}
if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_SHUTDOWN_NAME, out var luid))
{
return false;
}
var privileges = new TOKEN_PRIVILEGES
{
PrivilegeCount = 1,
Privileges = new() { e0 = new LUID_AND_ATTRIBUTES { Luid = luid, Attributes = TOKEN_PRIVILEGES_ATTRIBUTES.SE_PRIVILEGE_ENABLED } }
};
if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, &privileges, 0, null, null))
{
return false;
}
if (Marshal.GetLastWin32Error() != (int)WIN32_ERROR.NO_ERROR)
{
return false;
}
return true;
}
catch (Exception)
{
return false;
}
}
private List<Result> Commands()
{
var results = new List<Result>();
@ -120,10 +164,12 @@ namespace Flow.Launcher.Plugin.Sys
context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_shutdown_computer"),
context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"),
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
Process.Start("shutdown", "/s /t 0");
}
if (EnableShutdownPrivilege())
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_SHUTDOWN | EXIT_WINDOWS_FLAGS.EWX_POWEROFF, REASON);
else
Process.Start("shutdown", "/s /t 0");
return true;
}
@ -140,10 +186,12 @@ namespace Flow.Launcher.Plugin.Sys
context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_restart_computer"),
context.API.GetTranslation("flowlauncher_plugin_sys_restart_computer"),
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
Process.Start("shutdown", "/r /t 0");
}
if (EnableShutdownPrivilege())
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT, REASON);
else
Process.Start("shutdown", "/r /t 0");
return true;
}
@ -162,7 +210,10 @@ namespace Flow.Launcher.Plugin.Sys
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
Process.Start("shutdown", "/r /o /t 0");
if (EnableShutdownPrivilege())
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_REBOOT | EXIT_WINDOWS_FLAGS.EWX_BOOTOPTIONS, REASON);
else
Process.Start("shutdown", "/r /o /t 0");
return true;
}
@ -181,7 +232,7 @@ namespace Flow.Launcher.Plugin.Sys
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, 0);
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, REASON);
return true;
}
@ -204,7 +255,11 @@ namespace Flow.Launcher.Plugin.Sys
SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_sleep"),
Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xec46"),
IcoPath = "Images\\sleep.png",
Action = c => PInvoke.SetSuspendState(false, false, false)
Action = c =>
{
PInvoke.SetSuspendState(false, false, false);
return true;
}
},
new Result
{
@ -214,12 +269,7 @@ namespace Flow.Launcher.Plugin.Sys
IcoPath = "Images\\hibernate.png",
Action= c =>
{
var info = ShellCommand.SetProcessStartInfo("shutdown", arguments:"/h");
info.WindowStyle = ProcessWindowStyle.Hidden;
info.UseShellExecute = true;
ShellCommand.Execute(info);
PInvoke.SetSuspendState(true, false, false);
return true;
}
},
@ -231,10 +281,7 @@ namespace Flow.Launcher.Plugin.Sys
Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xe773"),
Action = c =>
{
{
System.Diagnostics.Process.Start("control.exe", "srchadmin.dll");
}
Process.Start("control.exe", "srchadmin.dll");
return true;
}
},
@ -272,10 +319,7 @@ namespace Flow.Launcher.Plugin.Sys
CopyText = recycleBinFolder,
Action = c =>
{
{
System.Diagnostics.Process.Start("explorer", recycleBinFolder);
}
Process.Start("explorer", recycleBinFolder);
return true;
}
},
@ -333,7 +377,7 @@ namespace Flow.Launcher.Plugin.Sys
Action = c =>
{
// Hide the window first then show msg after done because sometimes the reload could take a while, so not to make user think it's frozen.
Application.Current.MainWindow.Hide();
context.API.HideMainWindow();
_ = context.API.ReloadAllPluginData().ContinueWith(_ =>
context.API.ShowMsg(
@ -352,7 +396,7 @@ namespace Flow.Launcher.Plugin.Sys
IcoPath = "Images\\checkupdate.png",
Action = c =>
{
Application.Current.MainWindow.Hide();
context.API.HideMainWindow();
context.API.CheckForNewUpdate();
return true;
}

View file

@ -3,4 +3,10 @@ LockWorkStation
SHEmptyRecycleBin
S_OK
E_UNEXPECTED
SetSuspendState
SetSuspendState
OpenProcessToken
WIN32_ERROR
LookupPrivilegeValue
AdjustTokenPrivileges
TOKEN_PRIVILEGES
SE_SHUTDOWN_NAME

View file

@ -4,7 +4,7 @@
"Name": "System Commands",
"Description": "Provide System related commands. e.g. shutdown,lock, setting etc.",
"Author": "qianlifeng",
"Version": "3.1.6",
"Version": "3.1.7",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll",

View file

@ -42,7 +42,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>

View file

@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows.Controls;
namespace Flow.Launcher.Plugin.Url
{
public class Main : ISettingProvider,IPlugin, IPluginI18n
public class Main : IPlugin, IPluginI18n
{
//based on https://gist.github.com/dperini/729294
private const string urlPattern = "^" +
@ -43,7 +42,6 @@ namespace Flow.Launcher.Plugin.Url
Regex reg = new Regex(urlPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
private PluginInitContext context;
private Settings _settings;
public List<Result> Query(Query query)
{
@ -82,12 +80,6 @@ namespace Flow.Launcher.Plugin.Url
return new List<Result>(0);
}
public Control CreateSettingPanel()
{
return new SettingsControl(context.API,_settings);
}
public bool IsURL(string raw)
{
raw = raw.ToLower();

View file

@ -1,17 +0,0 @@
<UserControl
x:Class="Flow.Launcher.Plugin.Url.SettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Height="0"
d:DesignHeight="300"
d:DesignWidth="500"
mc:Ignorable="d">
<Grid Margin="40,40,10,0">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</UserControl>

View file

@ -1,18 +0,0 @@
using System.Windows.Controls;
namespace Flow.Launcher.Plugin.Url
{
public partial class SettingsControl : UserControl
{
private Settings _settings;
private IPublicAPI _flowlauncherAPI;
public SettingsControl(IPublicAPI flowlauncherAPI,Settings settings)
{
InitializeComponent();
_settings = settings;
_flowlauncherAPI = flowlauncherAPI;
}
}
}

View file

@ -4,7 +4,7 @@
"Name": "URL",
"Description": "Open the typed URL from Flow Launcher",
"Author": "qianlifeng",
"Version": "3.0.7",
"Version": "3.0.8",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.Url.dll",

View file

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0"?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="flowlauncher_plugin_websearch_window_title">Search Source Setting</system:String>

View file

@ -26,7 +26,7 @@
"Name": "Web Searches",
"Description": "Provide the web search ability",
"Author": "qianlifeng",
"Version": "3.1.3",
"Version": "3.1.4",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll",

View file

@ -2130,7 +2130,7 @@
<value>Ändern, wie der Mauszeiger ausschaut</value>
</data>
<data name="ChangePowerSavingSettings" xml:space="preserve">
<value>Change power-saving settings</value>
<value>Energiespareinstellungen ändern</value>
</data>
<data name="OptimiseForBlindness" xml:space="preserve">
<value>Optimieren für Blindheit</value>
@ -2143,7 +2143,7 @@
<value>Windows-Features ein- oder ausschalten</value>
</data>
<data name="ShowWhichOperatingSystemYourComputerIsRunning" xml:space="preserve">
<value>Show which operating system your computer is running</value>
<value>Betriebssystem, welches auf deinem Computer läuft, anzeigen</value>
</data>
<data name="ViewLocalServices" xml:space="preserve">
<value>Lokale Dienste ansehen</value>

View file

@ -4,7 +4,7 @@
"Description": "Search settings inside Control Panel and Settings App",
"Name": "Windows Settings",
"Author": "TobiasSekan",
"Version": "4.0.11",
"Version": "4.0.12",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.WindowsSettings.dll",

View file

@ -1,4 +1,4 @@
version: '1.19.4.{build}'
version: '1.19.5.{build}'
init:
- ps: |