From 9d5f74ca8f3126d277580905a90aad4605c8e069 Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Mon, 15 Jan 2024 20:33:53 +0100 Subject: [PATCH 01/62] Add sound effect volume Signed-off-by: Florian Grabmeier --- .../UserSettings/Settings.cs | 2 + Flow.Launcher/Languages/en.xaml | 2 + Flow.Launcher/MainWindow.xaml.cs | 7 +- Flow.Launcher/Resources/open.wav | Bin 80116 -> 105884 bytes Flow.Launcher/SettingWindow.xaml | 78 +++++++++++++++--- .../ViewModel/SettingWindowViewModel.cs | 9 ++ 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index ca1674315..274f88dc6 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -54,6 +54,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool UseGlyphIcons { get; set; } = true; public bool UseAnimation { get; set; } = true; public bool UseSound { get; set; } = true; + public double SoundVolume { get; set; } = 50; + public bool UseClock { get; set; } = true; public bool UseDate { get; set; } = false; public string TimeFormat { get; set; } = "hh:mm tt"; diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index d36a49538..4bc79ccb3 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -156,6 +156,8 @@ Dark Sound Effect Play a small sound when the search window opens + Sound Effect Volume + Adjust the volume of the sound effect Animation Use Animation in UI Animation Speed diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 3a914d488..70765c1dc 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -23,8 +23,7 @@ using System.Windows.Threading; using System.Windows.Data; using ModernWpf.Controls; using Key = System.Windows.Input.Key; -using System.Media; -using static Flow.Launcher.ViewModel.SettingWindowViewModel; +using System.Windows.Media; namespace Flow.Launcher { @@ -39,7 +38,7 @@ namespace Flow.Launcher private ContextMenu contextMenu; private MainViewModel _viewModel; private bool _animating; - SoundPlayer animationSound = new SoundPlayer(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav"); + MediaPlayer animationSound = new MediaPlayer(); #endregion @@ -113,6 +112,8 @@ namespace Flow.Launcher { if (_settings.UseSound) { + animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); + animationSound.Volume = _settings.SoundVolume / 100.0; animationSound.Play(); } UpdatePosition(); diff --git a/Flow.Launcher/Resources/open.wav b/Flow.Launcher/Resources/open.wav index 4f13724f7093ad2d66c595cf8a672b6dabf008ba..0f692c75b377aec4a8682fc978d5d821ee39247b 100644 GIT binary patch literal 105884 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 z zD4b{jQZYLhfi%gnvHGlqUNBt(P=bAR1OoZ5oC02Me_gXOlY)0`VgPfkqORn+WR&1oa); zU?S@Kn&Wm@5IBj0=k&|Zhm^~9?K;5Q(|X7kZT{ZdC5-^zz)DXVY@y(>z5_*{B&jMAan#(tzqg$7(d#mRgXWNC(|>IV);=F(4=Uom_^sBX!olhb($+bRHqd0K7u-ymxAFSPR_Va zd z6li|4@;n;}m>Xud?vKad{~JKQH6tSz@GP{V-^Ud?eOBQT!$l{HqEFjl&Od{0ArDRa ze7sbWjJpv?r-$vJTvTQ|?En=gIyDf5=u79gI*|IEhXXMo`)i`0)EYTB)~9($@xyc3 zHLr&-QsbpD?`!sO2TC*ARoLCK>TsBzss>gA!LWI1rc6xh<*{<^4&0CU#JOgnrftP% zHYogTj(plbJ@t@7+lV4TwV(=pH_t@i0Eo#}vzZ}Kv>l^AmAu຾ST~1}iZ@E(N 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 diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 1e886c022..57b4e0684 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2474,27 +2474,81 @@ - - - - - - - + + + + + + + - +  - - + + + + + + + + + + + + + + + + + + + +  + + + + diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 231e65539..86a25ffb1 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -63,6 +63,9 @@ namespace Flow.Launcher.ViewModel case nameof(Settings.PreviewHotkey): OnPropertyChanged(nameof(AlwaysPreviewToolTip)); break; + case nameof(Settings.SoundVolume): + OnPropertyChanged(nameof(SoundEffectVolume)); + break; } }; @@ -631,6 +634,12 @@ namespace Flow.Launcher.ViewModel set => Settings.UseSound = value; } + public double SoundEffectVolume + { + get => Settings.SoundVolume; + set => Settings.SoundVolume = value; + } + public bool UseClock { get => Settings.UseClock; From b52b2a06228b353d3b2bbad652ee858793f3dac3 Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Wed, 17 Jan 2024 10:21:14 +0100 Subject: [PATCH 02/62] Fix open audio file only once Signed-off-by: Florian Grabmeier --- Flow.Launcher/MainWindow.xaml.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 70765c1dc..b745eaee7 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -49,7 +49,9 @@ namespace Flow.Launcher _settings = settings; InitializeComponent(); - InitializePosition(); + InitializePosition(); + + animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); } public MainWindow() @@ -112,7 +114,7 @@ namespace Flow.Launcher { if (_settings.UseSound) { - animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); + animationSound.Position = TimeSpan.Zero; animationSound.Volume = _settings.SoundVolume / 100.0; animationSound.Play(); } From 04291fd0bd7cc62520a451e207c3c52a88a7266d Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Wed, 17 Jan 2024 10:45:16 +0100 Subject: [PATCH 03/62] Update icons Signed-off-by: Florian Grabmeier --- Flow.Launcher/SettingWindow.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 57b4e0684..96f80f0c5 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2498,7 +2498,7 @@ OnContent="{DynamicResource enable}" Style="{DynamicResource SideToggleSwitch}" /> -  +  From 83d59b1103dc4439f77da8384cf24a81e814eacb Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Thu, 25 Jan 2024 22:48:44 +0100 Subject: [PATCH 04/62] Add open target for program shortcuts Signed-off-by: Florian Grabmeier --- .../Flow.Launcher.Plugin.Program/Languages/en.xaml | 1 + .../Flow.Launcher.Plugin.Program/Programs/Win32.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index 39807db2c..d7e87b50b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -73,6 +73,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 7d08d3670..788aa8724 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -261,7 +261,19 @@ namespace Flow.Launcher.Plugin.Program.Programs }, IcoPath = "Images/folder.png", Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe838"), - } + }, + new Result + { + Title = api.GetTranslation("flowlauncher_plugin_program_open_target_folder"), + Action = _ => + { + Main.Context.API.OpenDirectory(Path.GetDirectoryName(ExecutablePath), ExecutablePath); + + return true; + }, + IcoPath = "Images/folder.png", + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe8de"), + }, }; return contextMenus; } From 0a9eb09d7e869ded6ea7530425fcf006f3fa1c98 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:40:54 -0500 Subject: [PATCH 05/62] Add DeepCopy method --- Flow.Launcher.Plugin/Result.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index bfb0b0411..fc6c5d185 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -183,6 +183,31 @@ namespace Flow.Launcher.Plugin return Title + SubTitle + Score; } + /// + /// Clones the current result + /// + public Result Clone() + { + return new Result + { + Title = Title, + SubTitle = SubTitle, + ActionKeywordAssigned = ActionKeywordAssigned, + CopyText = CopyText, + AutoCompleteText = AutoCompleteText, + IcoPath = IcoPath, + RoundedIcon = RoundedIcon, + Icon = Icon, + Glyph = Glyph, + Action = Action, + AsyncAction = AsyncAction, + Score = Score, + TitleHighlightData = TitleHighlightData, + OriginQuery = OriginQuery, + PluginDirectory = PluginDirectory + }; + } + /// /// Additional data associated with this result /// From 62a26ff0ca8e6ef62c47ff6d26e1a4f8fc5302f3 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:41:42 -0500 Subject: [PATCH 06/62] Move copying to appropiate sections --- Flow.Launcher/ViewModel/MainViewModel.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index db481d410..ef533ed31 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -662,19 +662,7 @@ namespace Flow.Launcher.ViewModel if (selected == lastContextMenuResult) { // Use copy to keep the original results unchanged - results = lastContextMenuResults.ConvertAll(result => new Result - { - Title = result.Title, - SubTitle = result.SubTitle, - IcoPath = result.IcoPath, - PluginDirectory = result.PluginDirectory, - Action = result.Action, - ContextData = result.ContextData, - Glyph = result.Glyph, - OriginQuery = result.OriginQuery, - Score = result.Score, - AsyncAction = result.AsyncAction, - }); + results = lastContextMenuResults.ToList(); } else { @@ -689,7 +677,7 @@ namespace Flow.Launcher.ViewModel if (!string.IsNullOrEmpty(query)) { - var filtered = results.Where + var filtered = results.Select(x => x.Clone()).Where ( r => { From 3b8fc29d6f913e0762ba819779190e3c72db6432 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:46:57 -0500 Subject: [PATCH 07/62] Remove unused code --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ef533ed31..9717f40e1 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -661,8 +661,7 @@ namespace Flow.Launcher.ViewModel List results; if (selected == lastContextMenuResult) { - // Use copy to keep the original results unchanged - results = lastContextMenuResults.ToList(); + results = lastContextMenuResults; } else { From a27d5336dffbb0a8cbcd2090dd857171f04f37f0 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Thu, 15 Feb 2024 22:06:19 -0600 Subject: [PATCH 08/62] fix python plugin and node plugin fix job process association the program plugin --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 2 -- Flow.Launcher.Core/Plugin/NodePluginV2.cs | 12 ++------ .../Plugin/ProcessStreamPluginV2.cs | 29 ++++++++++++------- Flow.Launcher.Core/Plugin/PythonPluginV2.cs | 12 ++++---- .../Programs/Win32.cs | 2 +- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 390da072b..1d2389b4f 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -15,8 +15,6 @@ namespace Flow.Launcher.Core.Plugin { internal abstract class JsonRPCPluginV2 : JsonRPCPluginBase, IAsyncDisposable, IAsyncReloadable, IResultUpdated { - public abstract string SupportedLanguage { get; set; } - public const string JsonRpc = "JsonRPC"; protected abstract IDuplexPipe ClientPipe { get; set; } diff --git a/Flow.Launcher.Core/Plugin/NodePluginV2.cs b/Flow.Launcher.Core/Plugin/NodePluginV2.cs index 6c95777f0..9fc9ccac3 100644 --- a/Flow.Launcher.Core/Plugin/NodePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/NodePluginV2.cs @@ -10,21 +10,13 @@ namespace Flow.Launcher.Core.Plugin /// /// Execution of JavaScript & TypeScript plugins /// - internal class NodePluginV2 : ProcessStreamPluginV2 + internal sealed class NodePluginV2 : ProcessStreamPluginV2 { public NodePluginV2(string filename) { - StartInfo = new ProcessStartInfo - { - FileName = filename, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - }; + StartInfo = new ProcessStartInfo { FileName = filename, }; } - public override string SupportedLanguage { get; set; } protected override ProcessStartInfo StartInfo { get; set; } public override async Task InitAsync(PluginInitContext context) diff --git a/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs b/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs index 16f9dfbf9..a14baf271 100644 --- a/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO.Pipelines; @@ -6,6 +8,7 @@ using System.Threading.Tasks; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin; using Meziantou.Framework.Win32; +using Microsoft.VisualBasic.ApplicationServices; using Nerdbank.Streams; namespace Flow.Launcher.Core.Plugin @@ -18,18 +21,18 @@ namespace Flow.Launcher.Core.Plugin { _jobObject.SetLimits(new JobObjectLimits() { - Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException + Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException | + JobObjectLimitFlags.SilentBreakawayOk }); - + _jobObject.AssignProcess(Process.GetCurrentProcess()); } - public override string SupportedLanguage { get; set; } - protected sealed override IDuplexPipe ClientPipe { get; set; } + protected sealed override IDuplexPipe ClientPipe { get; set; } = null!; protected abstract ProcessStartInfo StartInfo { get; set; } - protected Process ClientProcess { get; set; } + protected Process ClientProcess { get; set; } = null!; public override async Task InitAsync(PluginInitContext context) { @@ -37,12 +40,18 @@ namespace Flow.Launcher.Core.Plugin StartInfo.EnvironmentVariables["FLOW_PROGRAM_DIRECTORY"] = Constant.ProgramDirectory; StartInfo.EnvironmentVariables["FLOW_APPLICATION_DIRECTORY"] = Constant.ApplicationDirectory; - StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); + StartInfo.RedirectStandardError = true; + StartInfo.RedirectStandardInput = true; + StartInfo.RedirectStandardOutput = true; + StartInfo.CreateNoWindow = true; + StartInfo.UseShellExecute = false; StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; - ClientProcess = Process.Start(StartInfo); - ArgumentNullException.ThrowIfNull(ClientProcess); - + var process = Process.Start(StartInfo); + ArgumentNullException.ThrowIfNull(process); + ClientProcess = process; + _jobObject.AssignProcess(ClientProcess); + SetupPipe(ClientProcess); ErrorStream = ClientProcess.StandardError; diff --git a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs index 4a8d8d7de..f768daf15 100644 --- a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs @@ -18,7 +18,6 @@ namespace Flow.Launcher.Core.Plugin { internal sealed class PythonPluginV2 : ProcessStreamPluginV2 { - public override string SupportedLanguage { get; set; } = AllowedLanguage.Python; protected override ProcessStartInfo StartInfo { get; set; } public PythonPluginV2(string filename) @@ -26,11 +25,6 @@ namespace Flow.Launcher.Core.Plugin StartInfo = new ProcessStartInfo { FileName = filename, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true }; var path = Path.Combine(Constant.ProgramDirectory, JsonRpc); @@ -39,5 +33,11 @@ namespace Flow.Launcher.Core.Plugin //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"); } + + public override async Task InitAsync(PluginInitContext context) + { + StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); + await base.InitAsync(context); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 7d08d3670..a7f447598 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -196,7 +196,7 @@ namespace Flow.Launcher.Plugin.Program.Programs FileName = FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true, - Verb = runAsAdmin ? "runas" : "" + Verb = runAsAdmin ? "runas" : "", }; _ = Task.Run(() => Main.StartProcess(Process.Start, info)); From f6b17023dc059fda8fd83e30a2831cee89d2b875 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Thu, 15 Feb 2024 22:14:03 -0600 Subject: [PATCH 09/62] remove redundant information to merge into the process stream plugin --- Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs index ee1b315c2..128b28638 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs @@ -12,15 +12,7 @@ namespace Flow.Launcher.Core.Plugin public ExecutablePluginV2(string filename) { - StartInfo = new ProcessStartInfo - { - FileName = filename, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - }; + StartInfo = new ProcessStartInfo { FileName = filename }; } - } } From 692eb1301124fd800b50aea21590d39b3798da01 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Thu, 15 Feb 2024 22:15:22 -0600 Subject: [PATCH 10/62] remove some duplicate code --- Flow.Launcher.Core/Plugin/NodePluginV2.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/NodePluginV2.cs b/Flow.Launcher.Core/Plugin/NodePluginV2.cs index 9fc9ccac3..d84a72864 100644 --- a/Flow.Launcher.Core/Plugin/NodePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/NodePluginV2.cs @@ -22,8 +22,6 @@ namespace Flow.Launcher.Core.Plugin public override async Task InitAsync(PluginInitContext context) { StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); - StartInfo.ArgumentList.Add(string.Empty); - StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; await base.InitAsync(context); } } From 2882d188639b59c8cab7eb87c7a8d2fb4fb179c7 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sun, 18 Feb 2024 23:46:24 -0600 Subject: [PATCH 11/62] use HeaderDeliminatedHandler for NodePlugin while NewLineDeliminated for everything else --- .../Plugin/ExecutablePluginV2.cs | 2 ++ Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 18 ++++++++++++++++-- Flow.Launcher.Core/Plugin/NodePluginV2.cs | 2 ++ Flow.Launcher.Core/Plugin/PythonPluginV2.cs | 11 +++++------ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs index 128b28638..852f57b9f 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs @@ -14,5 +14,7 @@ namespace Flow.Launcher.Core.Plugin { StartInfo = new ProcessStartInfo { FileName = filename }; } + + protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.NewLineDelimited; } } diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 1d2389b4f..1ad6edc2c 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -86,12 +86,26 @@ namespace Flow.Launcher.Core.Plugin public event ResultUpdatedEventHandler ResultsUpdated; + protected enum MessageHandlerType + { + HeaderDelimited, + LengthHeaderDelimited, + NewLineDelimited + } + + protected abstract MessageHandlerType MessageHandler { get; } + private void SetupJsonRPC() { var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption }; - var handler = new NewLineDelimitedMessageHandler(ClientPipe, - formatter); + IJsonRpcMessageHandler handler = MessageHandler switch + { + MessageHandlerType.HeaderDelimited => new HeaderDelimitedMessageHandler(ClientPipe, formatter), + MessageHandlerType.LengthHeaderDelimited => new LengthHeaderMessageHandler(ClientPipe, formatter), + MessageHandlerType.NewLineDelimited => new NewLineDelimitedMessageHandler(ClientPipe, formatter), + _ => throw new ArgumentOutOfRangeException() + }; RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API)); diff --git a/Flow.Launcher.Core/Plugin/NodePluginV2.cs b/Flow.Launcher.Core/Plugin/NodePluginV2.cs index d84a72864..c8cc37c57 100644 --- a/Flow.Launcher.Core/Plugin/NodePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/NodePluginV2.cs @@ -24,5 +24,7 @@ namespace Flow.Launcher.Core.Plugin StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); await base.InitAsync(context); } + + protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.HeaderDelimited; } } diff --git a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs index f768daf15..5c36e0eea 100644 --- a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs @@ -19,13 +19,10 @@ namespace Flow.Launcher.Core.Plugin internal sealed class PythonPluginV2 : ProcessStreamPluginV2 { protected override ProcessStartInfo StartInfo { get; set; } - + public PythonPluginV2(string filename) { - StartInfo = new ProcessStartInfo - { - FileName = filename, - }; + StartInfo = new ProcessStartInfo { FileName = filename, }; var path = Path.Combine(Constant.ProgramDirectory, JsonRpc); StartInfo.EnvironmentVariables["PYTHONPATH"] = path; @@ -33,11 +30,13 @@ namespace Flow.Launcher.Core.Plugin //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"); } - + public override async Task InitAsync(PluginInitContext context) { StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); await base.InitAsync(context); } + + protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.NewLineDelimited; } } From 38c96418332690f1332058c1ad4890878616e07f Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 19 Feb 2024 10:21:33 -0600 Subject: [PATCH 12/62] fix jsonrpcv2 plugin setting not sent --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 1ad6edc2c..74b7faa35 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -49,7 +49,7 @@ namespace Flow.Launcher.Core.Plugin try { var res = await RPC.InvokeWithCancellationAsync("query", - new[] { query }, + new object[] { query, Settings.Inner }, token); var results = ParseResults(res); From d3a05473b5fc7c917aaad7276de8f9d70df94ae3 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 19 Feb 2024 11:10:05 -0600 Subject: [PATCH 13/62] implement context_menu with JoinableTaskFactory --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 74b7faa35..46c72624a 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -39,9 +39,23 @@ namespace Flow.Launcher.Core.Plugin } } + private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext()); + public override List LoadContextMenus(Result selectedResult) { - throw new NotImplementedException(); + try + { + var res = JTF.Run(() => RPC.InvokeWithCancellationAsync("context_menu", + new object[] { selectedResult.ContextData })); + + var results = ParseResults(res); + + return results; + } + catch + { + return new List(); + } } public override async Task> QueryAsync(Query query, CancellationToken token) From f43b082e2b7ce64273773af65a1a26d7b21b646c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 22:24:41 +0000 Subject: [PATCH 14/62] Bump Microsoft.Data.Sqlite from 8.0.1 to 8.0.2 Bumps [Microsoft.Data.Sqlite](https://github.com/dotnet/efcore) from 8.0.1 to 8.0.2. - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/compare/v8.0.1...v8.0.2) --- updated-dependencies: - dependency-name: Microsoft.Data.Sqlite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../Flow.Launcher.Plugin.BrowserBookmark.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 2ee832b81..81374abe8 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -56,7 +56,7 @@ - + \ No newline at end of file From c7bb6336fb05f18d2fead70c1f33e7cd43df54d5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 22 Feb 2024 21:45:34 +1100 Subject: [PATCH 15/62] New Crowdin updates (#2566) New translation updates --- Flow.Launcher/Languages/pl.xaml | 284 +++++++++--------- .../Languages/zh-cn.xaml | 4 +- .../Languages/zh-cn.xaml | 2 +- .../Properties/Resources.fr-FR.resx | 110 +++---- .../Properties/Resources.zh-cn.resx | 8 +- 5 files changed, 204 insertions(+), 204 deletions(-) diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index 340e6c3e9..401e202c3 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -1,7 +1,7 @@  - Failed to register hotkey "{0}". The hotkey may be in use by another program. Change to a different hotkey, or exit another program. + Nie udało się zarejestrować skrótu klawiszowego "{0}". Klucz skrótu może być używany przez inny program. Zmień skrót klawiszowy lub wyjdź z innego programu. Flow Launcher Nie udało się uruchomić: {0} Niepoprawny format pliku wtyczki @@ -21,11 +21,11 @@ Zaznacz wszystko Plik Folder - Text + Tekst Tryb grania Wstrzymaj używanie skrótów. - Position Reset - Reset search window position + Resetowanie pozycji + Zresetuj pozycję okna wyszukiwania Ustawienia @@ -33,29 +33,29 @@ Tryb przenośny Przechowuj wszystkie ustawienia i dane użytkownika w jednym folderze (Przydatne, gdy używane na dyskach wymiennych lub usługach chmurowych). Uruchamiaj Flow Launcher przy starcie systemu - Error setting launch on startup + Błąd uruchamiania ustawień przy starcie Ukryj okno Flow Launcher kiedy przestanie ono być aktywne Nie pokazuj powiadomienia o nowej wersji - Search Window Position - Remember Last Position - Monitor with Mouse Cursor - Monitor with Focused Window - Primary Monitor - Custom Monitor - Search Window Position on Monitor - Center - Center Top - Left Top - Right Top - Custom Position + Pozycja Okna Wyszukiwania + Zapamiętaj Ostatnią Pozycję + Monitoruj kursorem myszy + Monitor z Dostosowanym Oknem + Monitor główny + Monitor Niestandardowy + Pozycja Okna Wyszukiwania na Monitorze + Wyśrodkowane + Środek Góra + Lewa Góra + Prawa Góra + Pozycja niestandardowa Język - Last Query Style - Show/Hide previous results when Flow Launcher is reactivated. - Preserve Last Query - Select last Query - Empty last Query + Styl ostatniego zapytania + Pokaż/Ukryj poprzednie wyniki, gdy Flow Launcher jest ponownie aktywowany. + Zachowaj ostatnie zapytanie + Wybierz ostatnie zapytanie + Puste ostatnie zapytanie Maksymalna liczba wyników - You can also quickly adjust this by using CTRL+Plus and CTRL+Minus. + Możesz to również szybko dostosować używając CTRL+Plus i CTRL+Minus. Ignoruj skróty klawiszowe w trybie pełnego ekranu Wyłącz aktywowanie Flow Launcher, gdy uruchomiona jest aplikacja pełnoekranowa (Zalecane dla gier). Domyślny menedżer plików @@ -63,45 +63,45 @@ Domyślna przeglądarka Ustawienie dla nowej karty, nowego okna i trybu prywatnego. Python Path - Node.js Path - Please select the Node.js executable - Please select pythonw.exe - Always Start Typing in English Mode - Temporarily change your input method to English mode when activating Flow. + Ścieżka Node.js + Wybierz plik wykonywalny Node.js + Wybierz pythonw.exe + Zawsze rozpoczynaj wpisywanie w trybie angielskim + Tymczasowo zmień metodę wprowadzania na tryb angielski podczas aktywacji Flow. Automatyczne aktualizacje Wybierz Uruchamiaj Flow Launcher zminimalizowany Ukryj ikonę zasobnika - When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window. - Query Search Precision - Changes minimum match score required for results. - Search with Pinyin - Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. - Always Preview - Always open preview panel when Flow activates. Press {0} to toggle preview. - Shadow effect is not allowed while current theme has blur effect enabled + Gdy ikona jest ukryta w zasobniku, menu Ustawienia można otworzyć, klikając prawym przyciskiem myszy okno wyszukiwania. + Precyzja wyszukiwania zapytań + Zmienia minimalny wynik dopasowania wymagany do uzyskania wyników. + Szukaj z Pinyin + Umożliwia wyszukiwanie przy użyciu Pinyin. Pinyin to standardowy system pisowni zromanizowanej służący do tłumaczenia języka chińskiego. + Zawsze Podgląd + Zawsze otwieraj panel podglądu, gdy aktywowany jest Flow. Naciśnij {0}, aby przełączyć podgląd. + Efekt cienia jest niedozwolony, gdy bieżący motyw ma włączony efekt rozmycia Szukaj wtyczek Ctrl+F aby wyszukać wtyczki - No results found - Please try a different search. - Plugin + Nie znaleziono wyników + Spróbuj użyć innego wyszukiwania. + Wtyczka Wtyczki Znajdź więcej wtyczek - On + Włącz Wyłącz - Action keyword Setting + Ustawienie akcji słowa kluczowego Wyzwalacze - Current action keyword - New action keyword - Change Action Keywords - Current Priority - New Priority - Priority - Change Plugin Results Priority + Bieżące słowo kluczowe akcji + Nowe słowo kluczowe akcji + Zmień słowa kluczowe akcji + Obecny Priorytet + Nowy Priorytet + Priorytet + Zmień priorytet wyników wtyczki Folder wtyczki - by + wg Czas ładowania: Czas zapytania: Wersja @@ -111,67 +111,67 @@ Sklep z wtyczkami - New Release - Recently Updated + Nowe wydanie + Ostatnio zaktualizowano Wtyczki - Installed - Refresh - Install + Zainstalowany + Odśwież + Instalacja Odinstalowywanie Aktualizuj - Plugin already installed - New Version - This plugin has been updated within the last 7 days - New Update is Available + Plugin już jest zainstalowany + Nowa wersja + Ta wtyczka została zaktualizowana w ciągu ostatnich 7 dni + Dostępna jest nowa aktualizacja Skórka - Appearance + Wygląd Znajdź więcej skórek - How to create a theme - Hi There + Jak utworzyć motyw + Cześć, Explorer - Search for files, folders and file contents - WebSearch - Search the web with different search engine support + Wyszukiwanie plików, folderów i zawartości plików + Wyszukiwarka internetowa + Wyszukiwanie w Internecie z obsługą różnych wyszukiwarek Programy - Launch programs as admin or a different user - ProcessKiller - Terminate unwanted processes + Uruchamiaj programy jako administrator lub inny użytkownik + ZabijProces + Zakończ niechciane procesy Czcionka okna zapytania Czcionka okna wyników Tryb w oknie Przeźroczystość - Theme {0} not exists, fallback to default theme - Fail to load theme {0}, fallback to default theme - Theme Folder - Open Theme Folder - Color Scheme - System Default - Light - Dark - Sound Effect - Play a small sound when the search window opens - Animation - Use Animation in UI - Animation Speed - The speed of the UI animation - Slow - Medium - Fast - Custom - Clock - Date + Motyw {0} nie istnieje, powróć do domyślnego motywu + Nie można załadować motywu {0}, wróć do motywu domyślnego + Folder Motywu + Otwórz folder motywu + Schemat kolorów + Domyślne ustawienie systemowe + Jasny + Ciemny + Efekty dźwiękowe + Odtwarzaj krótki dźwięk po otwarciu okna wyszukiwania + Animacja + Użyj animacji w interfejsie użytkownika + Szybkość animacji + Szybkość animacji interfejsu użytkownika + Wolna + Średnia + Szybka + Niestandardowa + Zegar + Data Skrót klawiszowy Skrót klawiszowy Skrót klawiszowy Flow Launcher - Enter shortcut to show/hide Flow Launcher. - Preview Hotkey - Enter shortcut to show/hide preview in search window. + Wprowadź skrót, aby pokazać/ukryć Flow Launcher. + Podgląd skrótu + Wprowadź skrót, aby pokazać/ukryć podgląd w oknie wyszukiwania. Modyfikatory klawiszów otwierających wyniki Select a modifier key to open selected result via keyboard. Pokaż skrót klawiszowy @@ -243,27 +243,27 @@ Select File Manager Please specify the file location of the file manager you using and add arguments if necessary. The default arguments are "%d", and a path is entered at that location. For example, If a command is required such as "totalcmd.exe /A c:\windows", argument is /A "%d". - "%f" is an argument that represent the file path. It is used to emphasize the file/folder name when opening a specific file location in 3rd party file manager. This argument is only available in the "Arg for File" item. If the file manager does not have that function, you can use "%d". - File Manager - Profile Name - File Manager Path - Arg For Folder - Arg For File + "%f" jest argumentem reprezentującym ścieżkę do pliku. Służy do podkreślenia nazwy pliku/folderu podczas otwierania określonej lokalizacji pliku w menedżerze plików innych firm. Ten argument jest dostępny tylko w pozycji "Arg dla pliku". Jeśli menedżer plików nie ma tej funkcji, możesz użyć "%d". + Menadżer plików + Nazwa profilu + Ścieżka menedżera plików + Arg dla folderu + Arg dla pliku Domyślna przeglądarka - The default setting follows the OS default browser setting. If specified separately, flow uses that browser. - Browser - Browser Name - Browser Path - New Window - New Tab - Private Mode + Ustawienie domyślne jest zgodne z domyślnym ustawieniem przeglądarki systemu operacyjnego. Jeśli określono to oddzielnie, flow korzysta z tej przeglądarki. + Przeglądarka + Nazwa przeglądarki + Ścieżka przeglądarki + Nowe okno + Nowa zakładka + Tryb prywatny - Change Priority - Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number - Please provide an valid integer for Priority! + Zmień priorytet + Im większa liczba, tym wyższa pozycja w rankingu. Spróbuj ustawić go jako 5. Jeśli chcesz, aby wyniki były niższe niż w przypadku jakiejkolwiek innej wtyczki, podaj liczbę ujemną + Podaj prawidłową liczbę całkowitą dla priorytetu! Stary wyzwalacz @@ -274,26 +274,26 @@ Nowy wyzwalacz nie może być pusty Ten wyzwalacz został już przypisany do innej wtyczki, musisz podać inny wyzwalacz. Sukces - Completed successfully + Zakończono pomyślnie Użyj * jeżeli nie chcesz podawać wyzwalacza Skrót klawiszowy niestandardowych zapyta - Press a custom hotkey to open Flow Launcher and input the specified query automatically. + Naciśnij niestandardowy klawisz skrótu, aby otworzyć Flow Launcher i automatycznie wprowadzić określone zapytanie. Podgląd Skrót klawiszowy jest niedostępny, musisz podać inny skrót klawiszowy Niepoprawny skrót klawiszowy Aktualizuj - Custom Query Shortcut - Enter a shortcut that automatically expands to the specified query. - A shortcut is expanded when it exactly matches the query. + Niestandardowy skrót zapytania + Wprowadź skrót, który automatycznie rozszerzy się do określonego zapytania. + Skrót jest rozwijany, gdy dokładnie pasuje do zapytania. -If you add an '@' prefix while inputting a shortcut, it matches any position in the query. Builtin shortcuts match any position in a query. +Jeśli dodasz prefiks '@' podczas wprowadzania skrótu, będzie on pasował do dowolnej pozycji w zapytaniu. Wbudowane skróty pasują do każdej pozycji w zapytaniu. - Shortcut already exists, please enter a new Shortcut or edit the existing one. - Shortcut and/or its expansion is empty. + Skrót już istnieje, wprowadź nowy skrót lub edytuj istniejący. + Skrót i/lub jego rozwinięcie jest puste. Niepoprawny skrót klawiszowy @@ -315,51 +315,51 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in W programie Flow Launcher wystąpił błąd - Please wait... + Proszę czekać... - Checking for new update - You already have the latest Flow Launcher version - Update found - Updating... + Sprawdzanie nowej aktualizacji + Masz już najnowszą wersję Flow Launcher + Znaleziono aktualizację + Aktualizacja... - Flow Launcher was not able to move your user profile data to the new update version. - Please manually move your profile data folder from {0} to {1} + Flow Launcher nie był w stanie przenieść danych profilu użytkownika do nowej wersji aktualizacji. + Ręcznie przenieś folder danych profilu z {0} do {1} - New Update + Nowa aktualizacja Nowa wersja Flow Launcher {0} jest dostępna Wystąpił błąd podczas instalowania aktualizacji programu Aktualizuj Anuluj - Update Failed - Check your connection and try updating proxy settings to github-cloud.s3.amazonaws.com. + Aktualizacja nie powiodła się + Sprawdź połączenie i spróbuj zaktualizować ustawienia proxy do github-cloud.s3.amazonaws.com. Aby dokończyć proces aktualizacji Flow Launcher musi zostać zresetowany Następujące pliki zostaną zaktualizowane Aktualizuj pliki Opis aktualizacji - Skip - Welcome to Flow Launcher - Hello, this is the first time you are running Flow Launcher! - Before starting, this wizard will assist in setting up Flow Launcher. You can skip this if you wish. Please choose a language - Search and run all files and applications on your PC - Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse. - 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. - Hotkeys - Action Keyword and Commands - 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. - Let's Start Flow Launcher - Finished. Enjoy Flow Launcher. Don't forget the hotkey to start :) + Pomiń + Witamy w Flow Launcher + Witaj, po raz pierwszy uruchamiasz Flow Launcher! + Przed rozpoczęciem ten kreator pomoże skonfigurować Flow Launcher. Jeśli chcesz, możesz to pominąć. Proszę wybierz język + Wyszukiwanie i uruchamianie wszystkich plików i aplikacji na PC + Przeszukuj wszystko, od aplikacji, plików, zakładek, YouTube, X i nie tylko. Wszystko to z komfortowej klawiatury, bez konieczności dotykania myszy. + Flow Launcher uruchamia się za pomocą poniższego skrótu klawiszowego, śmiało i wypróbuj go teraz. Aby to zmienić, kliknij dane wejściowe i naciśnij żądany klawisz skrótu na klawiaturze. + Skróty klawiszowe + Słowo kluczowe akcji i polecenia + Przeszukuj sieć, uruchamiaj aplikacje lub uruchamiaj różne funkcje za pomocą wtyczek Flow Launcher. Niektóre funkcje zaczynają się od słowa kluczowego akcji, a w razie potrzeby można ich używać bez słów kluczowych akcji. Wypróbuj poniższe zapytania w Flow Launcher. + Uruchom Flow Launcher + Zakończono. Ciesz się Flow Launcher. Nie zapomnij o klawiszu skrótu, aby rozpocząć :) - Back / Context Menu - Item Navigation - Open Context Menu - Open Containing Folder - Run as Admin / Open Folder in Default File Manager - Query History + Powrót / Menu kontekstowe + Nawigacja pozycji + Otwórz menu kontekstowe + Otwórz folder zawierający + Uruchom jako administrator / Otwórz folder w domyślnym menedżerze plików + Historia zapytań Back to Result in Context Menu Autocomplete Open / Run Selected Item diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index be73c37f4..fd6b09159 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -28,8 +28,8 @@ 此插件已安装 插件列表下载失败 请检查您是否可以连接到 github.com。这个错误意味着您可能无法安装或更新插件。 - Update all plugins - Would you like to update all plugins? + 更新所有插件 + 您想要更新所有插件吗? Would you like to update {0} plugins?{1}Flow Launcher will restart after updating all plugins. Would you like to update {0} plugins? {0} plugins successfully updated. Restarting Flow, please wait... diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml index 318903f07..ace554327 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml @@ -3,7 +3,7 @@ 替换 Win+R Close Command Prompt after pressing any key - Press any key to close this window... + 按下任意键以关闭此窗口... 执行后不关闭命令窗口 始终以管理员身份运行 以其他用户身份运行 diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.fr-FR.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.fr-FR.resx index 49644f5a3..9864cdfc1 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.fr-FR.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.fr-FR.resx @@ -1890,7 +1890,7 @@ Microsoft New Phonetic Settings - Generate a system health report + Générer un rapport de santé du système Fix problems with your computer @@ -1959,7 +1959,7 @@ Configure advanced user profile properties - Start or stop using AutoPlay for all media and devices + Démarrer ou arrêter l'utilisation de la fonction AutoPlay pour tous les médias et appareils Change Automatic Maintenance settings @@ -1971,16 +1971,16 @@ Select users who can use remote desktop - Show which programs are installed on your computer + Afficher les programmes installés sur votre ordinateur Allow remote access to your computer - View advanced system settings + Afficher les paramètres avancés du système - How to install a program + Comment installer un programme Modifier le fonctionnement de votre clavier @@ -2013,7 +2013,7 @@ Change your homepage - Group similar windows on the taskbar + Grouper les fenêtres similaires sur la barre des tâches Change Windows SideShow settings @@ -2031,7 +2031,7 @@ Change when the computer sleeps - Set up a virtual private network (VPN) connection + Configurer une connexion à un réseau privé virtuel (VPN) Accommodate learning abilities @@ -2040,10 +2040,10 @@ Set up a dial-up connection - Set up a connection or network + Configurer une connexion ou un réseau - How to change your Windows password + Comment modifier votre mot de passe Windows Make it easier to see the mouse pointer @@ -2097,13 +2097,13 @@ Show how much RAM is on this computer - Edit power plan + Modifier le mode de gestion d'alimentation - Adjust system volume + Ajuster le volume système - Defragment and optimise your drives + Défragmenter et optimiser les lecteurs Set up ODBC data sources (32-bit) @@ -2115,7 +2115,7 @@ Magnify portions of the screen using Magnifier - Change the file type associated with a file extension + Modifier le type de fichier associé à une extension de fichier View event logs @@ -2140,10 +2140,10 @@ - Turn Windows features on or off + Activer ou désactiver des fonctionnalités Windows - Show which operating system your computer is running + Afficher le système d'exploitation utilisé par l'ordinateur View local services @@ -2161,10 +2161,10 @@ Advanced printer setup - Change default printer + Changer l'imprimante par défaut - Edit environment variables for your account + Modifier les variables d'environnement pour votre compte Optimise visual display @@ -2182,7 +2182,7 @@ Clear disk space by deleting unnecessary files - View devices and printers + Afficher les appareils et les imprimantes Private Character Editor @@ -2206,16 +2206,16 @@ Choose the order of how your screen rotates - Change how Windows searches + Modifier le mode de recherche de Windows Set flicks to perform certain tasks - Change account type + Changer le type de compte - Change screen saver + Modifier l'économiseur d'écran Change User Account Control settings @@ -2224,10 +2224,10 @@ Turn on easy access keys - Identify and repair network problems + Identifier et réparer les problèmes de réseau - Find and fix networking and connection problems + Rechercher et résoudre les problèmes de réseau et de connexion Play CDs or other media automatically @@ -2236,34 +2236,34 @@ View basic information about your computer - Choose how you open links + Choisissez comment ouvrir les liens Allow Remote Assistance invitations to be sent from this computer - Task Manager + Gestionnaire des tâches Turn flicks on or off - Add a language + Ajouter une langue - View network status and tasks + Afficher l'état du réseau et les tâches - Turn Magnifier on or off + Activer ou désactiver la loupe - See the name of this computer + Afficher le nom de cet ordinateur - View network connections + Afficher les connexions réseau - Perform recommended maintenance tasks automatically + Effectuer automatiquement les tâches de maintenance recommandées Manage disk space used by your offline files @@ -2281,7 +2281,7 @@ Change the way dates and lists are displayed - Manage audio devices + Gérer les appareils audio Change security settings @@ -2290,7 +2290,7 @@ Check security status - Delete cookies or temporary files + Supprimer les cookies ou les fichiers temporaires Specify which hand you write with @@ -2323,7 +2323,7 @@ Enable or disable session cookies - Give administrative rights to a domain user + Donner des droits d'administration à un utilisateur du domaine Choose when to turn off display @@ -2332,7 +2332,7 @@ Move the pointer with the keypad using MouseKeys - Change Windows SideShow-compatible device settings + Modifier les paramètres des appareils compatibles avec Windows SideShow Adjust commonly used mobility settings @@ -2341,7 +2341,7 @@ Change text-to-speech settings - Set the time and date + Définir l'heure et la date Change location settings @@ -2350,16 +2350,16 @@ Change mouse settings - Manage Storage Spaces + Gérer les espaces de stockage Show or hide file extensions - Allow an app through Windows Firewall + Autoriser une application via le pare-feu Windows - Change system sounds + Modifier les sons du système Adjust ClearType text @@ -2368,25 +2368,25 @@ Turn screen saver on or off - Find and fix windows update problems + Rechercher et résoudre les problèmes de mise à jour de Windows Change Bluetooth settings - Connect to a network + Se connecter à un réseau - Change the search provider in Internet Explorer + Changer le moteur de recherche dans Internet Explorer Join a domain - Add a device + Ajouter un appareil - Find and fix problems with Windows Search + Rechercher et résoudre les problèmes avec Windows Search Choose a power plan @@ -2395,7 +2395,7 @@ Change how the mouse pointer looks when it’s moving - Uninstall a program + Désinstaller un programme Create and format hard disk partitions @@ -2407,7 +2407,7 @@ Change PC wake-up settings - Manage network passwords + Gérer les mots de passe réseau Change input methods @@ -2419,10 +2419,10 @@ Change battery settings - Rename this computer + Renommer cet ordinateur - Lock or unlock the taskbar + Verrouiller ou déverrouiller la barre des tâches Manage Web Credentials @@ -2431,16 +2431,16 @@ Change the time zone - Start speech recognition + Démarrer la reconnaissance vocale - View installed updates + Afficher les mises à jour installées - What's happened to the Quick Launch toolbar? + Qu'est-il arrivé à la barre d'outils de lancement rapide ? - Change search options for files and folders + Modifier les options de recherche pour les fichiers et les dossiers Adjust settings before giving a presentation @@ -2470,7 +2470,7 @@ Gérer les certificats utilisateur - Schedule tasks + Planifier des tâches Ignore repeated keystrokes using FilterKeys @@ -2485,7 +2485,7 @@ Delete browsing history - Change what the power buttons do + Changer ce que font les boutons d'alimentation Create standard user account @@ -2494,7 +2494,7 @@ Take speech tutorials - View system resource usage in Task Manager + Afficher l'utilisation des ressources du système dans le gestionnaire des tâches Créer un compte diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx index 16cd7a3f0..8e71e09a3 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx @@ -1932,7 +1932,7 @@ Turn off unnecessary animations - Create a restore point + 创建还原点 Turn off automatic window arrangement @@ -2007,7 +2007,7 @@ Set up ODBC data sources (64-bit) - Configure proxy server + 设置代理服务器 Change your homepage @@ -2242,7 +2242,7 @@ Allow Remote Assistance invitations to be sent from this computer - Task Manager + 任务管理器 Turn flicks on or off @@ -2356,7 +2356,7 @@ 是否显示文件扩展名 - Allow an app through Windows Firewall + 允许应用程序通过 Windows 防火墙 更改系统声音 From 8656eddf41432dde7f6c591f65d282e03d1d4cd6 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Fri, 23 Feb 2024 23:25:29 +0200 Subject: [PATCH 16/62] Change protection level of ReQuery instance --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index db481d410..ab93e08e2 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -208,7 +208,7 @@ namespace Flow.Launcher.ViewModel } [RelayCommand] - private void ReQuery() + public void ReQuery() { if (SelectedIsFromQueryResults()) { From 0522fc79f44f1a3c485aca252a095c30948f213f Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Fri, 23 Feb 2024 23:27:34 +0200 Subject: [PATCH 17/62] Add ReQuery Instance --- Flow.Launcher/PublicAPIInstance.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 36309a22a..5efc2324e 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -315,6 +315,8 @@ namespace Flow.Launcher public void RegisterGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Add(callback); public void RemoveGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Remove(callback); + public void ReQuery() => _mainVM.ReQuery(); + #endregion #region Private Methods From 50f71c07313bb872a20a5a82ee679616fc4e4e56 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Fri, 23 Feb 2024 23:28:16 +0200 Subject: [PATCH 18/62] Add ReQuery to API --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 49fe680f1..78f1e0afa 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -291,5 +291,11 @@ namespace Flow.Launcher.Plugin /// /// public bool IsGameModeOn(); + + /// + /// Reload Query + /// + /// + public void ReQuery(); } } From 185695b9ae290a5880bca4cf89f1e7c52b25b2b7 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Sat, 24 Feb 2024 09:52:03 +0600 Subject: [PATCH 19/62] Code cleanup: Flow.Launcher/Converters --- .../BoolToIMEConversionModeConverter.cs | 30 +++------- .../Converters/BoolToVisibilityConverter.cs | 58 +++++-------------- .../Converters/BorderClipConverter.cs | 47 +++++++-------- .../Converters/HighlightTextConverter.cs | 32 +++++----- .../Converters/IconRadiusConverter.cs | 11 +--- .../OpenResultHotkeyVisibilityConverter.cs | 2 +- Flow.Launcher/Converters/OrdinalConverter.cs | 16 ++--- .../Converters/QuerySuggestionBoxConverter.cs | 43 +++++--------- .../Converters/StringToKeyBindingConverter.cs | 18 +++--- Flow.Launcher/Converters/TextConverter.cs | 24 +++----- 10 files changed, 103 insertions(+), 178 deletions(-) diff --git a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs index 0bff23fe1..ffb7d65b8 100644 --- a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs +++ b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs @@ -9,18 +9,11 @@ namespace Flow.Launcher.Converters { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is bool v) + return value switch { - if (v) - { - return ImeConversionModeValues.Alphanumeric; - } - else - { - return ImeConversionModeValues.DoNotCare; - } - } - return ImeConversionModeValues.DoNotCare; + true => ImeConversionModeValues.Alphanumeric, + _ => ImeConversionModeValues.DoNotCare + }; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) @@ -33,18 +26,11 @@ namespace Flow.Launcher.Converters { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is bool v) + return value switch { - if (v) - { - return InputMethodState.Off; - } - else - { - return InputMethodState.DoNotCare; - } - } - return InputMethodState.DoNotCare; + true => InputMethodState.Off, + _ => InputMethodState.DoNotCare + }; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs index 627937c7f..0a5a5e338 100644 --- a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs +++ b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs @@ -8,28 +8,14 @@ namespace Flow.Launcher.Converters { public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) { - if (parameter != null) + return value switch { - if (value is true) - { - return Visibility.Collapsed; - } - - else - { - return Visibility.Visible; - } - } - else { - if (value is true) - { - return Visibility.Visible; - } - - else { - return Visibility.Collapsed; - } - } + true when parameter is not null => Visibility.Collapsed, + _ when parameter is not null => Visibility.Visible, + + true => Visibility.Visible, + _ => Visibility.Collapsed + }; } public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); @@ -40,30 +26,14 @@ namespace Flow.Launcher.Converters { public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) { - if (parameter != null) + return value switch { - if (value is true) - { - return 0; - } - - else - { - return 5; - } - } - else - { - if (value is true) - { - return 5; - } - - else - { - return 0; - } - } + true when parameter is not null => 0, + _ when parameter is not null => 5, + + true => 5, + _ => 0, + }; } public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); diff --git a/Flow.Launcher/Converters/BorderClipConverter.cs b/Flow.Launcher/Converters/BorderClipConverter.cs index c0bce2cd9..a8d9b34c6 100644 --- a/Flow.Launcher/Converters/BorderClipConverter.cs +++ b/Flow.Launcher/Converters/BorderClipConverter.cs @@ -13,34 +13,31 @@ namespace Flow.Launcher.Converters { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (values.Length == 3 && values[0] is double && values[1] is double && values[2] is CornerRadius) + if (values is not [double width, double height, CornerRadius radius]) { - var width = (double)values[0]; - var height = (double)values[1]; - Path myPath = new Path(); - if (width < Double.Epsilon || height < Double.Epsilon) - { - return Geometry.Empty; - } - var radius = (CornerRadius)values[2]; - var radiusHeight = radius.TopLeft; - - // Drawing Round box for bottom round, and rect for top area of listbox. - var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft); - var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0); - - GeometryGroup myGeometryGroup = new GeometryGroup(); - myGeometryGroup.Children.Add(corner); - myGeometryGroup.Children.Add(box); - - CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box); - myPath.Data = c1; - - myPath.Data.Freeze(); - return myPath.Data; + return DependencyProperty.UnsetValue; } - return DependencyProperty.UnsetValue; + Path myPath = new Path(); + if (width < Double.Epsilon || height < Double.Epsilon) + { + return Geometry.Empty; + } + var radiusHeight = radius.TopLeft; + + // Drawing Round box for bottom round, and rect for top area of listbox. + var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft); + var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0); + + GeometryGroup myGeometryGroup = new GeometryGroup(); + myGeometryGroup.Children.Add(corner); + myGeometryGroup.Children.Add(box); + + CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box); + myPath.Data = c1; + + myPath.Data.Freeze(); + return myPath.Data; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) diff --git a/Flow.Launcher/Converters/HighlightTextConverter.cs b/Flow.Launcher/Converters/HighlightTextConverter.cs index cb62c0d3d..3f02443e2 100644 --- a/Flow.Launcher/Converters/HighlightTextConverter.cs +++ b/Flow.Launcher/Converters/HighlightTextConverter.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Windows; using System.Windows.Data; using System.Windows.Documents; @@ -12,30 +11,27 @@ namespace Flow.Launcher.Converters { public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo) { - var text = value[0] as string; - var highlightData = value[1] as List; - - var textBlock = new Span(); - - if (highlightData == null || !highlightData.Any()) - { + if (value.Length < 2) + return new Run(string.Empty); + + if (value[0] is not string text) + return new Run(string.Empty); + + if (value[1] is not List { Count: > 0 } highlightData) // No highlight data, just return the text return new Run(text); - } + + var highlightStyle = (Style)Application.Current.FindResource("HighlightStyle"); + var textBlock = new Span(); for (var i = 0; i < text.Length; i++) { var currentCharacter = text.Substring(i, 1); - if (this.ShouldHighlight(highlightData, i)) + var run = new Run(currentCharacter) { - - textBlock.Inlines.Add(new Run(currentCharacter) { Style = (Style)Application.Current.FindResource("HighlightStyle") }); - - } - else - { - textBlock.Inlines.Add(new Run(currentCharacter)); - } + Style = ShouldHighlight(highlightData, i) ? highlightStyle : null + }; + textBlock.Inlines.Add(run); } return textBlock; } diff --git a/Flow.Launcher/Converters/IconRadiusConverter.cs b/Flow.Launcher/Converters/IconRadiusConverter.cs index c73bef8b2..c64202eed 100644 --- a/Flow.Launcher/Converters/IconRadiusConverter.cs +++ b/Flow.Launcher/Converters/IconRadiusConverter.cs @@ -8,15 +8,10 @@ namespace Flow.Launcher.Converters { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (values.Length != 2) - throw new ArgumentException("IconRadiusConverter must have 2 parameters"); + if (values is not [double size, bool half]) + throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]"); - return values[1] switch - { - true => (double)values[0] / 2, - false => (double)values[0], - _ => throw new ArgumentException("The second argument should be boolean", nameof(values)) - }; + return half ? size / 2 : size; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { diff --git a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs index a44b07cab..7b7bd0906 100644 --- a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs +++ b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs @@ -22,6 +22,6 @@ namespace Flow.Launcher.Converters return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } } diff --git a/Flow.Launcher/Converters/OrdinalConverter.cs b/Flow.Launcher/Converters/OrdinalConverter.cs index 02b9bdbde..438d4effa 100644 --- a/Flow.Launcher/Converters/OrdinalConverter.cs +++ b/Flow.Launcher/Converters/OrdinalConverter.cs @@ -1,3 +1,4 @@ +using System; using System.Globalization; using System.Windows.Controls; using System.Windows.Data; @@ -6,18 +7,19 @@ namespace Flow.Launcher.Converters { public class OrdinalConverter : IValueConverter { - public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is ListBoxItem listBoxItem - && ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox) + if (value is not ListBoxItem listBoxItem + || ItemsControl.ItemsControlFromItemContainer(listBoxItem) is not ListBox listBox) { - var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; - return res == 10 ? 0 : res; // 10th item => HOTKEY+0 + return 0; } - return 0; + var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; + return res == 10 ? 0 : res; // 10th item => HOTKEY+0 + } - public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } } diff --git a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs index 1e39473e0..a0f972ede 100644 --- a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs +++ b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs @@ -12,31 +12,20 @@ namespace Flow.Launcher.Converters { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (values.Length != 3) - { + if ( + values.Length != 3 || + values[0] is not TextBox queryTextBox || + values[1] is null || + values[2] is not string queryText || + string.IsNullOrEmpty(queryText) + ) return string.Empty; - } - var QueryTextBox = values[0] as TextBox; - - var queryText = (string)values[2]; - - if (string.IsNullOrEmpty(queryText)) - return string.Empty; - - // second prop is the current selected item result - var val = values[1]; - if (val == null) - { - return string.Empty; - } - if (!(val is ResultViewModel)) - { - return System.Windows.Data.Binding.DoNothing; - } + + if (values[1] is not ResultViewModel selectedItem) + return Binding.DoNothing; try { - var selectedItem = (ResultViewModel)val; var selectedResult = selectedItem.Result; var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " "; @@ -50,17 +39,15 @@ namespace Flow.Launcher.Converters // When user typed lower case and result title is uppercase, we still want to display suggestion selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length); - // Check if Text will be larger then our QueryTextBox - System.Windows.Media.Typeface typeface = new Typeface(QueryTextBox.FontFamily, QueryTextBox.FontStyle, QueryTextBox.FontWeight, QueryTextBox.FontStretch); + // Check if Text will be larger than our QueryTextBox + Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch); // TODO: Obsolete warning? - System.Windows.Media.FormattedText ft = new FormattedText(QueryTextBox.Text, System.Globalization.CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, QueryTextBox.FontSize, Brushes.Black); + var ft = new FormattedText(queryTextBox.Text, CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black); - var offset = QueryTextBox.Padding.Right; + var offset = queryTextBox.Padding.Right; - if ((ft.Width + offset) > QueryTextBox.ActualWidth || QueryTextBox.HorizontalOffset != 0) - { + if (ft.Width + offset > queryTextBox.ActualWidth || queryTextBox.HorizontalOffset != 0) return string.Empty; - }; return selectedItem.QuerySuggestionText; } diff --git a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs index 3675f06fc..22526bd82 100644 --- a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs +++ b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs @@ -9,19 +9,17 @@ namespace Flow.Launcher.Converters { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - var mode = parameter as string; - var hotkeyStr = value as string; + if (parameter is not string mode || value is not string hotkeyStr) + return null; + var converter = new KeyGestureConverter(); var key = (KeyGesture)converter.ConvertFromString(hotkeyStr); - if (mode == "key") + return mode switch { - return key.Key; - } - else if (mode == "modifiers") - { - return key.Modifiers; - } - return null; + "key" => key?.Key, + "modifiers" => key?.Modifiers, + _ => null + }; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/Flow.Launcher/Converters/TextConverter.cs b/Flow.Launcher/Converters/TextConverter.cs index 90d445776..4fedbf778 100644 --- a/Flow.Launcher/Converters/TextConverter.cs +++ b/Flow.Launcher/Converters/TextConverter.cs @@ -10,23 +10,17 @@ namespace Flow.Launcher.Converters { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - var ID = value.ToString(); - switch(ID) + var id = value?.ToString(); + return id switch { - case PluginStoreItemViewModel.NewRelease: - return InternationalizationManager.Instance.GetTranslation("pluginStore_NewRelease"); - case PluginStoreItemViewModel.RecentlyUpdated: - return InternationalizationManager.Instance.GetTranslation("pluginStore_RecentlyUpdated"); - case PluginStoreItemViewModel.None: - return InternationalizationManager.Instance.GetTranslation("pluginStore_None"); - case PluginStoreItemViewModel.Installed: - return InternationalizationManager.Instance.GetTranslation("pluginStore_Installed"); - default: - return ID; - } - + PluginStoreItemViewModel.NewRelease => InternationalizationManager.Instance.GetTranslation("pluginStore_NewRelease"), + PluginStoreItemViewModel.RecentlyUpdated => InternationalizationManager.Instance.GetTranslation("pluginStore_RecentlyUpdated"), + PluginStoreItemViewModel.None => InternationalizationManager.Instance.GetTranslation("pluginStore_None"), + PluginStoreItemViewModel.Installed => InternationalizationManager.Instance.GetTranslation("pluginStore_Installed"), + _ => id + }; } - public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } } From 3ddf0cf6f6b5bbfdd6f080e49db509fdef94e12a Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sat, 24 Feb 2024 12:10:25 -0600 Subject: [PATCH 20/62] match longest shortcut by default --- Flow.Launcher/ViewModel/MainViewModel.cs | 43 ++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index db481d410..f41fd8dd8 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -176,7 +176,8 @@ namespace Flow.Launcher.ViewModel var token = e.Token == default ? _updateToken : e.Token; PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, token))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, + token))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } @@ -190,7 +191,8 @@ namespace Flow.Launcher.ViewModel Hide(); await PluginManager.ReloadDataAsync().ConfigureAwait(false); - Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); + Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), + InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); } [RelayCommand] @@ -265,6 +267,7 @@ namespace Flow.Launcher.ViewModel { autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}"; } + autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText; } @@ -286,11 +289,13 @@ namespace Flow.Launcher.ViewModel { results.SelectedIndex = int.Parse(index); } + var result = results.SelectedItem?.Result; if (result == null) { return; } + var hideWindow = await result.ExecuteAsync(new ActionContext { // not null means pressing modifier key + number, should ignore the modifier key @@ -403,6 +408,7 @@ namespace Flow.Launcher.ViewModel public bool GameModeStatus { get; set; } = false; private string _queryText; + public string QueryText { get => _queryText; @@ -426,6 +432,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowSize += 100; Settings.WindowLeft -= 50; } + OnPropertyChanged(); } @@ -441,6 +448,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowLeft += 50; Settings.WindowSize -= 100; } + OnPropertyChanged(); } @@ -520,18 +528,17 @@ namespace Flow.Launcher.ViewModel { if (QueryText != queryText) { - // re-query is done in QueryText's setter method QueryText = queryText; // set to false so the subsequent set true triggers // PropertyChanged and MoveQueryTextToEnd is called QueryTextCursorMovedToEnd = false; - } else if (isReQuery) { Query(isReQuery: true); } + QueryTextCursorMovedToEnd = true; }); } @@ -601,8 +608,8 @@ namespace Flow.Launcher.ViewModel public string OpenResultCommandModifiers => Settings.OpenResultModifiers; - public string PreviewHotkey - { + public string PreviewHotkey + { get { // TODO try to patch issue #1755 @@ -616,6 +623,7 @@ namespace Flow.Launcher.ViewModel { Settings.PreviewHotkey = "F1"; } + return Settings.PreviewHotkey; } } @@ -684,7 +692,6 @@ namespace Flow.Launcher.ViewModel results.Add(ContextMenuTopMost(selected)); results.Add(ContextMenuPluginInfo(selected.PluginID)); } - if (!string.IsNullOrEmpty(query)) @@ -703,7 +710,6 @@ namespace Flow.Launcher.ViewModel r.Score = match.Score; return true; - }).ToList(); ContextMenu.AddResults(filtered, id); } @@ -730,10 +736,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query - { - RawQuery = h.Query - }, + OriginQuery = new Query { RawQuery = h.Query }, Action = _ => { SelectedResults = Results; @@ -870,20 +873,23 @@ namespace Flow.Launcher.ViewModel // Task.Yield will force it to run in ThreadPool await Task.Yield(); - IReadOnlyList results = await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); + IReadOnlyList results = + await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); currentCancellationToken.ThrowIfCancellationRequested(); results ??= _emptyResult; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, + currentCancellationToken))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } } } - private Query ConstructQuery(string queryText, IEnumerable customShortcuts, IEnumerable builtInShortcuts) + private Query ConstructQuery(string queryText, IEnumerable customShortcuts, + IEnumerable builtInShortcuts) { if (string.IsNullOrWhiteSpace(queryText)) { @@ -893,7 +899,7 @@ namespace Flow.Launcher.ViewModel StringBuilder queryBuilder = new(queryText); StringBuilder queryBuilderTmp = new(queryText); - foreach (var shortcut in customShortcuts) + foreach (var shortcut in customShortcuts.OrderByDescending(x => x.Key.Length)) { if (queryBuilder.Equals(shortcut.Key)) { @@ -920,7 +926,9 @@ namespace Flow.Launcher.ViewModel } catch (Exception e) { - Log.Exception($"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", e); + Log.Exception( + $"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", + e); } } }); @@ -1065,6 +1073,7 @@ namespace Flow.Launcher.ViewModel { SelectedResults = Results; } + switch (Settings.LastQueryMode) { case LastQueryMode.Empty: From 455b221163bd857b44421e67d120f66092617077 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Sun, 25 Feb 2024 00:36:20 +0600 Subject: [PATCH 21/62] =?UTF-8?q?Code=20cleanup:=20Flow.Launcher/Converter?= =?UTF-8?q?s=20=E2=80=94=20requested=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Converters/BoolToVisibilityConverter.cs | 24 +++++++++---------- .../Converters/IconRadiusConverter.cs | 4 ++-- .../Converters/QuerySuggestionBoxConverter.cs | 4 +++- Flow.Launcher/Converters/TextConverter.cs | 17 ++++++++----- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs index 0a5a5e338..ac3b2cfd0 100644 --- a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs +++ b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs @@ -8,13 +8,13 @@ namespace Flow.Launcher.Converters { public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) { - return value switch + return (value, parameter) switch { - true when parameter is not null => Visibility.Collapsed, - _ when parameter is not null => Visibility.Visible, - - true => Visibility.Visible, - _ => Visibility.Collapsed + (true, not null) => Visibility.Collapsed, + (_, not null) => Visibility.Visible, + + (true, null) => Visibility.Visible, + (_, null) => Visibility.Collapsed }; } @@ -26,13 +26,13 @@ namespace Flow.Launcher.Converters { public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) { - return value switch + return (value, parameter) switch { - true when parameter is not null => 0, - _ when parameter is not null => 5, - - true => 5, - _ => 0, + (true, not null) => 0, + (_, not null) => 5, + + (true, null) => 5, + (_, null) => 0 }; } diff --git a/Flow.Launcher/Converters/IconRadiusConverter.cs b/Flow.Launcher/Converters/IconRadiusConverter.cs index c64202eed..d0bdc9d1c 100644 --- a/Flow.Launcher/Converters/IconRadiusConverter.cs +++ b/Flow.Launcher/Converters/IconRadiusConverter.cs @@ -8,10 +8,10 @@ namespace Flow.Launcher.Converters { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (values is not [double size, bool half]) + if (values is not [double size, bool isIconCircular]) throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]"); - return half ? size / 2 : size; + return isIconCircular ? size / 2 : size; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { diff --git a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs index a0f972ede..4a8c9e92b 100644 --- a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs +++ b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs @@ -12,6 +12,9 @@ namespace Flow.Launcher.Converters { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { + // values[0] is TextBox: The textbox displaying the autocomplete suggestion + // values[1] is ResultViewModel: Currently selected item in the list + // values[2] is string: Query text if ( values.Length != 3 || values[0] is not TextBox queryTextBox || @@ -26,7 +29,6 @@ namespace Flow.Launcher.Converters try { - var selectedResult = selectedItem.Result; var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " "; var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title; diff --git a/Flow.Launcher/Converters/TextConverter.cs b/Flow.Launcher/Converters/TextConverter.cs index 4fedbf778..c0b34d053 100644 --- a/Flow.Launcher/Converters/TextConverter.cs +++ b/Flow.Launcher/Converters/TextConverter.cs @@ -11,14 +11,19 @@ namespace Flow.Launcher.Converters public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var id = value?.ToString(); - return id switch + var translationKey = id switch { - PluginStoreItemViewModel.NewRelease => InternationalizationManager.Instance.GetTranslation("pluginStore_NewRelease"), - PluginStoreItemViewModel.RecentlyUpdated => InternationalizationManager.Instance.GetTranslation("pluginStore_RecentlyUpdated"), - PluginStoreItemViewModel.None => InternationalizationManager.Instance.GetTranslation("pluginStore_None"), - PluginStoreItemViewModel.Installed => InternationalizationManager.Instance.GetTranslation("pluginStore_Installed"), - _ => id + PluginStoreItemViewModel.NewRelease => "pluginStore_NewRelease", + PluginStoreItemViewModel.RecentlyUpdated => "pluginStore_RecentlyUpdated", + PluginStoreItemViewModel.None => "pluginStore_None", + PluginStoreItemViewModel.Installed => "pluginStore_Installed", + _ => null }; + + if (translationKey is null) + return id; + + return InternationalizationManager.Instance.GetTranslation(translationKey); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); From e722927d9e0e1e2dd72359cc11cc80b7a54dc24b Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:05:54 +0200 Subject: [PATCH 22/62] ReQuery method overloading, reselect flag. --- Flow.Launcher/ViewModel/MainViewModel.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ab93e08e2..6e53879f8 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -33,6 +33,7 @@ namespace Flow.Launcher.ViewModel #region Private Fields private bool _isQueryRunning; + private bool _reselect = true; private Query _lastQuery; private Result lastContextMenuResult = new Result(); private List lastContextMenuResults = new List(); @@ -216,6 +217,15 @@ namespace Flow.Launcher.ViewModel } } + public void ReQuery(bool reselect) + { + if (SelectedIsFromQueryResults()) + { + _reselect = reselect; + QueryResults(isReQuery: true); + } + } + [RelayCommand] private void LoadContextMenu() { @@ -1151,7 +1161,8 @@ namespace Flow.Launcher.ViewModel } } - Results.AddResults(resultsForUpdates, token); + Results.AddResults(resultsForUpdates, token, _reselect); + _reselect = true; } #endregion From 1d6c4cee2e703510ad60e0d011d55d4d0170dc4a Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:11:30 +0200 Subject: [PATCH 23/62] AddResults & UpdateResults new param "reselect" --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index bb07ce085..d02dc9bd5 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -147,23 +147,23 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void AddResults(IEnumerable resultsForUpdates, CancellationToken token) + public void AddResults(IEnumerable resultsForUpdates, CancellationToken token, bool reselect = true) { var newResults = NewResults(resultsForUpdates); if (token.IsCancellationRequested) return; - UpdateResults(newResults, token); + UpdateResults(newResults, token, reselect); } - private void UpdateResults(List newResults, CancellationToken token = default) + private void UpdateResults(List newResults, CancellationToken token = default, bool reselect = true) { lock (_collectionLock) { // update UI in one run, so it can avoid UI flickering Results.Update(newResults, token); - if (Results.Any()) + if (reselect && Results.Any()) SelectedItem = Results[0]; } From ae84dae66dd98a7c894684d51b0fb98304242715 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:13:09 +0200 Subject: [PATCH 24/62] ReQuery new param "reselect" --- Flow.Launcher/PublicAPIInstance.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 5efc2324e..b49bf39d3 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -315,7 +315,7 @@ namespace Flow.Launcher public void RegisterGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Add(callback); public void RemoveGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Remove(callback); - public void ReQuery() => _mainVM.ReQuery(); + public void ReQuery(bool reselect = true) => _mainVM.ReQuery(reselect); #endregion From c8a2f10620e46265b879077244ccc452a7dfcff4 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:29:14 +0200 Subject: [PATCH 25/62] Introduce param "reselect" to ReQuery instance --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 78f1e0afa..c95a8ce7b 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -293,9 +293,10 @@ namespace Flow.Launcher.Plugin public bool IsGameModeOn(); /// - /// Reload Query + /// Reloads the query. + /// This method should run /// - /// - public void ReQuery(); + /// Choose the first result after reload if true; keep the last selected result if false. Default is true. + public void ReQuery(bool reselect = true); } } From a0821f101d8e67e4dd0e5bb453b5c88b9ae4c30a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 05:52:38 +0000 Subject: [PATCH 26/62] Bump StreamJsonRpc and Microsoft.VisualStudio.Threading (#2594) --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 68e7c115c..18f66b760 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -58,7 +58,7 @@ - + From 6877d7a73e39363ea439414b8d0b5f4a26a33096 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 06:21:31 +0000 Subject: [PATCH 27/62] Bump Microsoft.IO.RecyclableMemoryStream from 2.3.2 to 3.0.0 (#2593) --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 18f66b760..18101ccf0 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -56,7 +56,7 @@ - + From d0414892c386e8e46589195034fae6d85c8cc1a2 Mon Sep 17 00:00:00 2001 From: Phoenix Date: Fri, 1 Mar 2024 21:28:25 +0300 Subject: [PATCH 28/62] Fix opera bookmarks Fix opera bookmarks - comment Fix opera bookmarks - comment --- .../ChromiumBookmarkLoader.cs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 1e4f3f9ac..8ce597b30 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -33,20 +33,32 @@ namespace Flow.Launcher.Plugin.BrowserBookmark protected List LoadBookmarksFromFile(string path, string source) { - if (!File.Exists(path)) - return new List(); - var bookmarks = new List(); + + if (!File.Exists(path)) + return bookmarks; + using var jsonDocument = JsonDocument.Parse(File.ReadAllText(path)); if (!jsonDocument.RootElement.TryGetProperty("roots", out var rootElement)) - return new List(); + return bookmarks; + EnumerateRoot(rootElement, bookmarks, source); + return bookmarks; + } + + private void EnumerateRoot(JsonElement rootElement, ICollection bookmarks, string source) + { foreach (var folder in rootElement.EnumerateObject()) { - if (folder.Value.ValueKind == JsonValueKind.Object) + if (folder.Value.ValueKind != JsonValueKind.Object) + continue; + + // Fix for Opera. It stores bookmarks slightly different than chrome. See PR and bug report for this change for details. + // If various exceptions start to build up here consider splitting this Loader into multiple separate ones. + if (folder.Name == "custom_root") + EnumerateRoot(folder.Value, bookmarks, source); + else EnumerateFolderBookmark(folder.Value, bookmarks, source); } - - return bookmarks; } private void EnumerateFolderBookmark(JsonElement folderElement, ICollection bookmarks, From 0ab26d6f4fad956a8fbb259f7b148cedc4099d32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Mar 2024 07:15:39 +0000 Subject: [PATCH 29/62] Bump dorny/paths-filter from 2 to 3 (#2509) --- .github/workflows/default_plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/default_plugins.yml b/.github/workflows/default_plugins.yml index a2283defe..2cb04fe0d 100644 --- a/.github/workflows/default_plugins.yml +++ b/.github/workflows/default_plugins.yml @@ -18,7 +18,7 @@ jobs: dotnet-version: 7.0.x - name: Determine New Plugin Updates - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v3 id: changes with: filters: | From 56fb95ace36335b39ee01f71df61e3b7ba28cbaf Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 9 Mar 2024 23:03:42 +1100 Subject: [PATCH 30/62] New translations resources.resx (Chinese Simplified) [ci skip] --- .../Properties/Resources.zh-cn.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx index 8e71e09a3..2b39d48e4 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx @@ -1926,10 +1926,10 @@ 微软拼音 SimpleFast 选项 - Change what closing the lid does + 更改盖上盖子时的操作 - Turn off unnecessary animations + 关闭不必要的动画效果 创建还原点 From 6057eae05bcacf7f55de601e23b52d066bcba63d Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Wed, 13 Mar 2024 12:59:59 +0100 Subject: [PATCH 31/62] Fix missing using directive Signed-off-by: Florian Grabmeier --- Flow.Launcher/MainWindow.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 2a82dcdeb..503704046 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -26,6 +26,7 @@ using Key = System.Windows.Input.Key; using System.Media; using static Flow.Launcher.ViewModel.SettingWindowViewModel; using DataObject = System.Windows.DataObject; +using System.Windows.Media; namespace Flow.Launcher { From 10ebda8283679ca103b4585c98a951287cc11e42 Mon Sep 17 00:00:00 2001 From: asafmahlev Date: Mon, 18 Mar 2024 12:11:25 +0200 Subject: [PATCH 32/62] Added ctrl+shift+c --- Flow.Launcher/MainWindow.xaml | 4 ++++ Flow.Launcher/ViewModel/MainViewModel.cs | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 88e95aa69..e2c74d367 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -186,6 +186,10 @@ Key="F12" Command="{Binding ToggleGameModeCommand}" Modifiers="Ctrl" /> + Date: Mon, 18 Mar 2024 22:33:17 +0000 Subject: [PATCH 33/62] Bump Microsoft.NET.Test.Sdk from 17.8.0 to 17.9.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0 to 17.9.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.8.0...v17.9.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 29414baa6..fd967de4a 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -54,7 +54,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file From 2d805e53bc3adedf17fe4eb21631c039f21f3a7f Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Wed, 20 Mar 2024 00:47:14 -0500 Subject: [PATCH 34/62] revise structure --- Flow.Launcher/ViewModel/MainViewModel.cs | 53 +++++++++++++-------- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 24 ++++------ 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6e53879f8..23e746bd7 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -177,7 +177,8 @@ namespace Flow.Launcher.ViewModel var token = e.Token == default ? _updateToken : e.Token; PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, token))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, + token))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } @@ -191,7 +192,8 @@ namespace Flow.Launcher.ViewModel Hide(); await PluginManager.ReloadDataAsync().ConfigureAwait(false); - Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); + Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), + InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); } [RelayCommand] @@ -221,8 +223,7 @@ namespace Flow.Launcher.ViewModel { if (SelectedIsFromQueryResults()) { - _reselect = reselect; - QueryResults(isReQuery: true); + QueryResults(isReQuery: true, reselect: reselect); } } @@ -275,6 +276,7 @@ namespace Flow.Launcher.ViewModel { autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}"; } + autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText; } @@ -296,11 +298,13 @@ namespace Flow.Launcher.ViewModel { results.SelectedIndex = int.Parse(index); } + var result = results.SelectedItem?.Result; if (result == null) { return; } + var hideWindow = await result.ExecuteAsync(new ActionContext { // not null means pressing modifier key + number, should ignore the modifier key @@ -413,6 +417,7 @@ namespace Flow.Launcher.ViewModel public bool GameModeStatus { get; set; } = false; private string _queryText; + public string QueryText { get => _queryText; @@ -436,6 +441,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowSize += 100; Settings.WindowLeft -= 50; } + OnPropertyChanged(); } @@ -451,6 +457,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowLeft += 50; Settings.WindowSize -= 100; } + OnPropertyChanged(); } @@ -530,18 +537,17 @@ namespace Flow.Launcher.ViewModel { if (QueryText != queryText) { - // re-query is done in QueryText's setter method QueryText = queryText; // set to false so the subsequent set true triggers // PropertyChanged and MoveQueryTextToEnd is called QueryTextCursorMovedToEnd = false; - } else if (isReQuery) { Query(isReQuery: true); } + QueryTextCursorMovedToEnd = true; }); } @@ -611,8 +617,8 @@ namespace Flow.Launcher.ViewModel public string OpenResultCommandModifiers => Settings.OpenResultModifiers; - public string PreviewHotkey - { + public string PreviewHotkey + { get { // TODO try to patch issue #1755 @@ -626,6 +632,7 @@ namespace Flow.Launcher.ViewModel { Settings.PreviewHotkey = "F1"; } + return Settings.PreviewHotkey; } } @@ -694,7 +701,6 @@ namespace Flow.Launcher.ViewModel results.Add(ContextMenuTopMost(selected)); results.Add(ContextMenuPluginInfo(selected.PluginID)); } - if (!string.IsNullOrEmpty(query)) @@ -713,7 +719,6 @@ namespace Flow.Launcher.ViewModel r.Score = match.Score; return true; - }).ToList(); ContextMenu.AddResults(filtered, id); } @@ -740,10 +745,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query - { - RawQuery = h.Query - }, + OriginQuery = new Query { RawQuery = h.Query }, Action = _ => { SelectedResults = Results; @@ -771,7 +773,7 @@ namespace Flow.Launcher.ViewModel private readonly IReadOnlyList _emptyResult = new List(); - private async void QueryResults(bool isReQuery = false) + private async void QueryResults(bool isReQuery = false, bool reselect = true) { _updateSource?.Cancel(); @@ -880,20 +882,23 @@ namespace Flow.Launcher.ViewModel // Task.Yield will force it to run in ThreadPool await Task.Yield(); - IReadOnlyList results = await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); + IReadOnlyList results = + await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); currentCancellationToken.ThrowIfCancellationRequested(); results ??= _emptyResult; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, + currentCancellationToken, reselect))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } } } - private Query ConstructQuery(string queryText, IEnumerable customShortcuts, IEnumerable builtInShortcuts) + private Query ConstructQuery(string queryText, IEnumerable customShortcuts, + IEnumerable builtInShortcuts) { if (string.IsNullOrWhiteSpace(queryText)) { @@ -930,7 +935,9 @@ namespace Flow.Launcher.ViewModel } catch (Exception e) { - Log.Exception($"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", e); + Log.Exception( + $"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", + e); } } }); @@ -1075,6 +1082,7 @@ namespace Flow.Launcher.ViewModel { SelectedResults = Results; } + switch (Settings.LastQueryMode) { case LastQueryMode.Empty: @@ -1122,7 +1130,7 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void UpdateResultView(IEnumerable resultsForUpdates) + public void UpdateResultView(ICollection resultsForUpdates) { if (!resultsForUpdates.Any()) return; @@ -1161,7 +1169,10 @@ namespace Flow.Launcher.ViewModel } } - Results.AddResults(resultsForUpdates, token, _reselect); + // it should be the same for all results + bool reSelect = resultsForUpdates.First().ReSelectFirstResult; + + Results.AddResults(resultsForUpdates, token, reSelect); _reselect = true; } diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index 4cb5b1a95..c1daace56 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -4,23 +4,15 @@ using System.Threading; namespace Flow.Launcher.ViewModel { - public struct ResultsForUpdate + public record struct ResultsForUpdate( + IReadOnlyList Results, + PluginMetadata Metadata, + Query Query, + CancellationToken Token, + bool ReSelectFirstResult = true) { - public IReadOnlyList Results { get; } + public string ID { get; } = Metadata.ID; - public PluginMetadata Metadata { get; } - public string ID { get; } - - public Query Query { get; } - public CancellationToken Token { get; } - - public ResultsForUpdate(IReadOnlyList results, PluginMetadata metadata, Query query, CancellationToken token) - { - Results = results; - Metadata = metadata; - Query = query; - Token = token; - ID = metadata.ID; - } + public bool ReSelectFirstResult { get; set; } } } From b2f7d34a4d8259576e339a0f81293dbe7eeed6f6 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Wed, 20 Mar 2024 12:26:39 -0500 Subject: [PATCH 35/62] fix weird issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 10 +++++----- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 23e746bd7..46102ca92 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -223,7 +223,7 @@ namespace Flow.Launcher.ViewModel { if (SelectedIsFromQueryResults()) { - QueryResults(isReQuery: true, reselect: reselect); + QueryResults(isReQuery: true, reSelect: reselect); } } @@ -773,7 +773,7 @@ namespace Flow.Launcher.ViewModel private readonly IReadOnlyList _emptyResult = new List(); - private async void QueryResults(bool isReQuery = false, bool reselect = true) + private async void QueryResults(bool isReQuery = false, bool reSelect = true) { _updateSource?.Cancel(); @@ -848,7 +848,7 @@ namespace Flow.Launcher.ViewModel var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch { - false => QueryTask(plugin), + false => QueryTask(plugin, reSelect), true => Task.CompletedTask }).ToArray(); @@ -876,7 +876,7 @@ namespace Flow.Launcher.ViewModel } // Local function - async Task QueryTask(PluginPair plugin) + async Task QueryTask(PluginPair plugin, bool reSelect = true) { // Since it is wrapped within a ThreadPool Thread, the synchronous context is null // Task.Yield will force it to run in ThreadPool @@ -890,7 +890,7 @@ namespace Flow.Launcher.ViewModel results ??= _emptyResult; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, - currentCancellationToken, reselect))) + currentCancellationToken, reSelect))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index c1daace56..bc0be0de8 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -12,7 +12,5 @@ namespace Flow.Launcher.ViewModel bool ReSelectFirstResult = true) { public string ID { get; } = Metadata.ID; - - public bool ReSelectFirstResult { get; set; } } } From c0034d8e07d1d86c96d950ad3dae8a96dba05f1a Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:17:24 +0200 Subject: [PATCH 36/62] Remove unnecessary top class _reselect parameter --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 46102ca92..bb0505973 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -33,7 +33,6 @@ namespace Flow.Launcher.ViewModel #region Private Fields private bool _isQueryRunning; - private bool _reselect = true; private Query _lastQuery; private Result lastContextMenuResult = new Result(); private List lastContextMenuResults = new List(); @@ -1173,7 +1172,6 @@ namespace Flow.Launcher.ViewModel bool reSelect = resultsForUpdates.First().ReSelectFirstResult; Results.AddResults(resultsForUpdates, token, reSelect); - _reselect = true; } #endregion From 8b45372f660066a1fd90ffd8b0f372ed59de15f2 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 22 Jan 2024 12:06:14 -0600 Subject: [PATCH 37/62] Use FastCache.Cached as new image cache instead of implement it ourselves --- .../Flow.Launcher.Infrastructure.csproj | 1 + .../Image/ImageCache.cs | 78 +++++++------------ .../Image/ImageLoader.cs | 11 ++- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index b24f069c1..574f3686a 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,6 +49,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 7a2b57637..55545b9a7 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Windows.Media; +using FastCache; +using FastCache.Services; namespace Flow.Launcher.Infrastructure.Image { - [Serializable] public class ImageUsage { - public int usage; public ImageSource imageSource; @@ -23,16 +23,13 @@ namespace Flow.Launcher.Infrastructure.Image public class ImageCache { - private const int MaxCached = 50; - public ConcurrentDictionary<(string, bool), ImageUsage> Data { get; } = new(); - private const int permissibleFactor = 2; - private SemaphoreSlim semaphore = new(1, 1); + private const int MaxCached = 150; public void Initialize(Dictionary<(string, bool), int> usage) { foreach (var key in usage.Keys) { - Data[key] = new ImageUsage(usage[key], null); + Cached.Save(key, new ImageUsage(usage[key], null), TimeSpan.MaxValue, MaxCached); } } @@ -40,70 +37,48 @@ namespace Flow.Launcher.Infrastructure.Image { get { - if (!Data.TryGetValue((path, isFullImage), out var value)) + if (!Cached.TryGet((path, isFullImage), out var value)) { return null; } - value.usage++; - return value.imageSource; + value.Value.usage++; + return value.Value.imageSource; } set { - Data.AddOrUpdate( - (path, isFullImage), - new ImageUsage(0, value), - (k, v) => - { - v.imageSource = value; - v.usage++; - return v; - } - ); - - SliceExtra(); - - async void SliceExtra() + if (Cached.TryGet((path, isFullImage), out var cached)) { - // To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size - // This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time - if (Data.Count > permissibleFactor * MaxCached) - { - await semaphore.WaitAsync().ConfigureAwait(false); - // To delete the images from the data dictionary based on the resizing of the Usage Dictionary - // Double Check to avoid concurrent remove - if (Data.Count > permissibleFactor * MaxCached) - foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key)) - Data.TryRemove(key, out _); - semaphore.Release(); - } + cached.Value.imageSource = value; + cached.Value.usage++; } + + Cached.Save((path, isFullImage), new ImageUsage(0, value), TimeSpan.MaxValue, + MaxCached); } } public bool ContainsKey(string key, bool isFullImage) { - return key is not null && Data.ContainsKey((key, isFullImage)) && Data[(key, isFullImage)].imageSource != null; + return Cached.TryGet((key, isFullImage), out _); } public bool TryGetValue(string key, bool isFullImage, out ImageSource image) { - if (key is not null) + if (Cached.TryGet((key, isFullImage), out var value)) { - bool hasKey = Data.TryGetValue((key, isFullImage), out var imageUsage); - image = hasKey ? imageUsage.imageSource : null; - return hasKey; - } - else - { - image = null; - return false; + image = value.Value.imageSource; + value.Value.usage++; + return image != null; } + + image = null; + return false; } public int CacheSize() { - return Data.Count; + return CacheManager.TotalCount<(string, bool), ImageUsage>(); } /// @@ -111,7 +86,14 @@ namespace Flow.Launcher.Infrastructure.Image /// public int UniqueImagesInCache() { - return Data.Values.Select(x => x.imageSource).Distinct().Count(); + return CacheManager.EnumerateEntries<(string, bool), ImageUsage>().Select(x => x.Value.imageSource) + .Distinct() + .Count(); + } + + public IEnumerable> EnumerateEntries() + { + return CacheManager.EnumerateEntries<(string, bool), ImageUsage>(); } } } diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index add6d4e92..75c2a4ec9 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -49,7 +49,7 @@ namespace Flow.Launcher.Infrastructure.Image { await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () => { - foreach (var ((path, isFullImage), _) in ImageCache.Data) + foreach (var ((path, isFullImage), _) in usage) { await LoadAsync(path, isFullImage); } @@ -65,7 +65,7 @@ namespace Flow.Launcher.Infrastructure.Image try { - _storage.SaveAsync(ImageCache.Data + await _storage.SaveAsync(ImageCache.EnumerateEntries() .ToDictionary( x => x.Key, x => x.Value.usage)); @@ -125,9 +125,12 @@ namespace Flow.Launcher.Infrastructure.Image return new ImageResult(MissingImage, ImageType.Error); } - if (ImageCache.ContainsKey(path, loadFullImage)) + // extra scope for use of same variable name { - return new ImageResult(ImageCache[path, loadFullImage], ImageType.Cache); + if (ImageCache.TryGetValue(path, loadFullImage, out var imageSource)) + { + return new ImageResult(imageSource, ImageType.Cache); + } } if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var uriResult) From 267163c8adc61aa339223e046205b00a79694949 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 22 Mar 2024 22:08:18 +0800 Subject: [PATCH 38/62] Update terms --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 2d6fdb7f0..c4b1ee849 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -106,3 +106,4 @@ alreadyexists JsonRPC JsonRPCV2 Softpedia +img From ca00a8fd47f5a79751b65dd96295a1e6955303ed Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 Mar 2024 03:28:14 +1100 Subject: [PATCH 39/62] New translations en.xaml (Dutch) [ci skip] --- Flow.Launcher/Languages/nl.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index 3a2ba44c1..5b28c5712 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -182,7 +182,7 @@ Query Shortcut Expansion - Description + Beschrijving Verwijder Bewerken Toevoegen From bf35e11c40528f11b1f78e1dfa47cdf60db9f02a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 Mar 2024 03:28:14 +1100 Subject: [PATCH 40/62] New translations en.xaml (Dutch) [ci skip] --- .../Languages/nl.xaml | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml index ca798f9d7..a71e28e98 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml @@ -2,8 +2,8 @@ - Command - Description + Opdracht + Beschrijving Shutdown Restart @@ -27,37 +27,37 @@ Toggle Game Mode - Shutdown Computer - Restart Computer - Restart the computer with Advanced Boot Options for Safe and Debugging modes, as well as other options - Log off - Lock this computer - Close Flow Launcher - Restart Flow Launcher + Computer afsluiten + Computer opnieuw opstarten + Start de computer opnieuw op met geavanceerde opstartopties voor veilige en foutopsporing modi en andere opties + Afmelden + Deze computer vergrendelen + Sluit Flow Launcher + Flow Launcher herstarten Tweak Flow Launcher's settings - Put computer to sleep - Empty recycle bin + De computer in slaapstand zetten + Prullenbak leegmaken Open recycle bin Indexing Options - Hibernate computer - Save all Flow Launcher settings - Refreshes plugin data with new content - Open Flow Launcher's log location - Check for new Flow Launcher update - Visit Flow Launcher's documentation for more help and how to use tips - Open the location where Flow Launcher's settings are stored + De computer in sluimerstand zetten + Sla alle Flow Launcher instellingen op + Vernieuwt plugin data met nieuwe inhoud + Open de locatie van Flow Launcher's log bestand + Controleer op nieuwe Flow Launcher update + Bezoek Flow Launcher's documentatie voor meer hulp en tips + Open de locatie waar Flow Launcher's instellingen worden opgeslagen Toggle Game Mode Succesvol - All Flow Launcher settings saved - Reloaded all applicable plugin data - Are you sure you want to shut the computer down? - Are you sure you want to restart the computer? - Are you sure you want to restart the computer with Advanced Boot Options? + Alle Flow Launcher instellingen opgeslagen + Alle toepasselijke plugin gegevens zijn herladen + Weet u zeker dat u de computer wilt uitschakelen? + Weet u zeker dat u de computer wilt herstarten? + Weet u zeker dat u de computer wilt herstarten met geavanceerde opstartopties? Are you sure you want to log off? - System Commands - Provides System related commands. e.g. shutdown, lock, settings etc. + Systeemopdrachten + Voorziet in systeem gerelateerde opdrachten. bijv.: afsluiten, vergrendelen, instellingen, enz. From d0a5ba501b39cfec5eb183557ed7b1d47efd5423 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 Mar 2024 03:28:16 +1100 Subject: [PATCH 41/62] New translations resources.resx (Dutch) [ci skip] --- .../Properties/Resources.nl-NL.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx index e5ff84248..c28489cc8 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx @@ -456,7 +456,7 @@ Area Personalization - Command + Opdracht The command to direct start a setting From ab2f0bda2cfafc29959f7e4e8d2c4ff3eeab3dba Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 Mar 2024 21:20:27 +1100 Subject: [PATCH 42/62] New translations en.xaml (Russian) [ci skip] --- Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml index efb08c3f1..cb7f2cda5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml @@ -3,13 +3,13 @@ Сначала отметьте что-нибудь - Please select a folder link + Пожалуйста, выберите ссылку на папку Вы уверены, что хотите удалить {0}? Вы действительно хотите безвозвратно удалить этот файл? Вы действительно хотите безвозвратно удалить этот файл/папку? Удаление завершено Успешно удалено {0} - Assigning the global action keyword could bring up too many results during search. Please choose a specific action keyword + Назначение ключевого слова глобальных действий может привести к слишком большому количеству результатов поиска. Пожалуйста, выберите конкретное ключевое слово действий Quick Access can not be set to the global action keyword when enabled. Please choose a specific action keyword The required service for Windows Index Search does not appear to be running To fix this, start the Windows Search service. Select here to remove this warning From b759de47537e7ee4cd7892b488fa5a2c1426a542 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:37:24 +0800 Subject: [PATCH 43/62] Hide main window when dragging results. --- Flow.Launcher/ResultListBox.xaml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index 78720e86a..ac51b195c 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Windows; using System.Windows.Controls; @@ -137,6 +137,8 @@ namespace Flow.Launcher isDragging = false; + App.API.HideMainWindow(); + var data = new DataObject(DataFormats.FileDrop, new[] { path From 3658f1537e8a9feb4feeca92209dd8effab8aed8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 Mar 2024 04:47:58 +1100 Subject: [PATCH 44/62] New translations en.xaml (Slovak) [ci skip] --- Flow.Launcher/Languages/sk.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index 91ae87631..252bfb81d 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -171,7 +171,7 @@ Klávesová skratka pre Flow Launcher Zadajte skratku na zobrazenie/skrytie Flow Launchera. Klávesová skratka pre náhľad - Zadajte klávesovú skratku pre zobrazenie/skytie náhľadu vo vyhľadávacom okne. + Zadajte klávesovú skratku pre zobrazenie/skrytie náhľadu vo vyhľadávacom okne. Modifikačný kláves na otvorenie výsledkov Vyberte modifikačný kláves na otvorenie vybraného výsledku pomocou klávesnice. Zobraziť klávesovú skratku From d8d006fee9ccdd390598b3b910b9f1bf02db89fe Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 Mar 2024 13:56:10 +1100 Subject: [PATCH 45/62] add comment --- Flow.Launcher/ViewModel/MainViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index f41fd8dd8..58d5f1de0 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -899,6 +899,7 @@ namespace Flow.Launcher.ViewModel StringBuilder queryBuilder = new(queryText); StringBuilder queryBuilderTmp = new(queryText); + // Sorting order is important here, the reason is for matching longest shortcut by default foreach (var shortcut in customShortcuts.OrderByDescending(x => x.Key.Length)) { if (queryBuilder.Equals(shortcut.Key)) From ec7ecfacd053a102374f15c2ed7fa618ef944215 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 31 Mar 2024 01:17:05 +0800 Subject: [PATCH 46/62] Add Args property --- .../Flow.Launcher.Plugin.Program/Programs/Win32.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 788aa8724..61d3511b7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -39,15 +39,20 @@ namespace Flow.Launcher.Plugin.Program.Programs public string FullPath { get; set; } /// - /// Path of the executable for .lnk, or the URL for .url. Arguments are included if any. + /// Path of the executable for .lnk, or the URL for .url /// public string LnkResolvedPath { get; set; } /// - /// Path of the actual executable file. Args are included. + /// Path of the actual executable file /// public string ExecutablePath => LnkResolvedPath ?? FullPath; + /// + /// Arguments for the executable. + /// + public string Args { get; set; } + public string ParentDirectory { get; set; } /// @@ -339,7 +344,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var args = _helper.arguments; if (!string.IsNullOrEmpty(args)) { - program.LnkResolvedPath += " " + args; + program.Args = args; } var description = _helper.description; @@ -636,7 +641,7 @@ namespace Flow.Launcher.Plugin.Program.Programs private static IEnumerable ProgramsHasher(IEnumerable programs) { var startMenuPaths = GetStartMenuPaths(); - return programs.GroupBy(p => p.ExecutablePath.ToLowerInvariant()) + return programs.GroupBy(p => (p.ExecutablePath + p.Args).ToLowerInvariant()) .AsParallel() .SelectMany(g => { From f73e3d57d61fbb2067bb1fc2bf852f9c7bf806ef Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 31 Mar 2024 01:17:59 +0800 Subject: [PATCH 47/62] Only show target folder context menu for .lnk files --- .../Programs/Win32.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 61d3511b7..8a3e5ada0 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -267,22 +267,28 @@ namespace Flow.Launcher.Plugin.Program.Programs IcoPath = "Images/folder.png", Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe838"), }, - new Result - { - Title = api.GetTranslation("flowlauncher_plugin_program_open_target_folder"), - Action = _ => - { - Main.Context.API.OpenDirectory(Path.GetDirectoryName(ExecutablePath), ExecutablePath); - - return true; - }, - IcoPath = "Images/folder.png", - Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe8de"), - }, }; + if (Extension(FullPath) == ShortcutExtension) + { + contextMenus.Add(OpenTargetFolderContextMenuResult(api)); + } return contextMenus; } + private Result OpenTargetFolderContextMenuResult(IPublicAPI api) + { + return new Result + { + Title = api.GetTranslation("flowlauncher_plugin_program_open_target_folder"), + Action = _ => + { + api.OpenDirectory(Path.GetDirectoryName(ExecutablePath), ExecutablePath); + return true; + }, + IcoPath = "Images/folder.png", + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe8de"), + }; + } public override string ToString() { From df589cea497667f2d0c8ecd2bdedd0f96a7e3874 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 31 Mar 2024 20:07:18 +0800 Subject: [PATCH 48/62] Fix program indexing logic when cache is empty --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index c02573ed8..e0a7f23de 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -84,7 +84,7 @@ namespace Flow.Launcher.Plugin.Program Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); - bool cacheEmpty = !_win32s.Any() && !_uwps.Any(); + bool cacheEmpty = !_win32s.Any() || !_uwps.Any(); if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now) { From 2a00c48dea76529b3b9d43d83713434c1164089d Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:38:25 +0800 Subject: [PATCH 49/62] Fix typo --- Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml index 282a0e809..91f32a844 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml @@ -19,7 +19,7 @@ Open Recycle Bin Exit Save Settings - Restart Flow Launcher" + Restart Flow Launcher Settings Reload Plugin Data Check For Update @@ -62,4 +62,4 @@ System Commands Provides System related commands. e.g. shutdown, lock, settings etc. - \ No newline at end of file + From 578ad99cec599b3783af7e51d14ebe007bbb34d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:05:54 +0000 Subject: [PATCH 50/62] Bump softprops/action-gh-release from 1 to 2 (#2606) --- .github/workflows/default_plugins.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/default_plugins.yml b/.github/workflows/default_plugins.yml index 2cb04fe0d..a0a816877 100644 --- a/.github/workflows/default_plugins.yml +++ b/.github/workflows/default_plugins.yml @@ -65,7 +65,7 @@ jobs: - name: Publish BrowserBookmark if: steps.changes.outputs.browserbookmark == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.BrowserBookmark" files: "Flow.Launcher.Plugin.BrowserBookmark.zip" @@ -92,7 +92,7 @@ jobs: - name: Publish Calculator if: steps.changes.outputs.calculator == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Calculator" files: "Flow.Launcher.Plugin.Calculator.zip" @@ -119,7 +119,7 @@ jobs: - name: Publish Explorer if: steps.changes.outputs.explorer == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Explorer" files: "Flow.Launcher.Plugin.Explorer.zip" @@ -146,7 +146,7 @@ jobs: - name: Publish PluginIndicator if: steps.changes.outputs.pluginindicator == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginIndicator" files: "Flow.Launcher.Plugin.PluginIndicator.zip" @@ -173,7 +173,7 @@ jobs: - name: Publish PluginsManager if: steps.changes.outputs.pluginsmanager == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginsManager" files: "Flow.Launcher.Plugin.PluginsManager.zip" @@ -200,7 +200,7 @@ jobs: - name: Publish ProcessKiller if: steps.changes.outputs.processkiller == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller" files: "Flow.Launcher.Plugin.ProcessKiller.zip" @@ -227,7 +227,7 @@ jobs: - name: Publish Program if: steps.changes.outputs.program == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Program" files: "Flow.Launcher.Plugin.Program.zip" @@ -254,7 +254,7 @@ jobs: - name: Publish Shell if: steps.changes.outputs.shell == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Shell" files: "Flow.Launcher.Plugin.Shell.zip" @@ -281,7 +281,7 @@ jobs: - name: Publish Sys if: steps.changes.outputs.sys == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Sys" files: "Flow.Launcher.Plugin.Sys.zip" @@ -308,7 +308,7 @@ jobs: - name: Publish Url if: steps.changes.outputs.url == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Url" files: "Flow.Launcher.Plugin.Url.zip" @@ -335,7 +335,7 @@ jobs: - name: Publish WebSearch if: steps.changes.outputs.websearch == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.WebSearch" files: "Flow.Launcher.Plugin.WebSearch.zip" @@ -362,7 +362,7 @@ jobs: - name: Publish WindowsSettings if: steps.changes.outputs.windowssettings == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.WindowsSettings" files: "Flow.Launcher.Plugin.WindowsSettings.zip" From bd3608f020ae774135402f9e121dd521065f664c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:22:15 +0000 Subject: [PATCH 51/62] Bump Microsoft.Data.Sqlite from 8.0.2 to 8.0.3 Bumps [Microsoft.Data.Sqlite](https://github.com/dotnet/efcore) from 8.0.2 to 8.0.3. - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/compare/v8.0.2...v8.0.3) --- updated-dependencies: - dependency-name: Microsoft.Data.Sqlite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../Flow.Launcher.Plugin.BrowserBookmark.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 81374abe8..fe118c2c3 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -56,7 +56,7 @@ - + \ No newline at end of file From cd7673418acecf1a56e02c3da3b9125d63f25ad1 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 6 Apr 2024 11:54:52 +0800 Subject: [PATCH 52/62] Show target path in hover tooltip --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f3d971991..a64a708ef 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -183,6 +183,7 @@ namespace Flow.Launcher.Plugin.Program.Programs Score = matchResult.Score, TitleHighlightData = matchResult.MatchData, ContextData = this, + TitleToolTip = $"{title}\n{ExecutablePath}", Action = c => { // Ctrl + Enter to open containing folder From 950b4c91d3243f3e4ed61e3c9f81d6073ed7ba4a Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 11 Apr 2024 13:20:38 +0600 Subject: [PATCH 53/62] Code cleanup: Flow.Launcher/{Converters,Helper} --- .editorconfig | 4 +- .../BoolToIMEConversionModeConverter.cs | 57 +- .../Converters/BoolToVisibilityConverter.cs | 59 +- .../Converters/BorderClipConverter.cs | 58 +- .../DateTimeFormatToNowConverter.cs | 23 +- .../DiameterToCenterPointConverter.cs | 25 +- .../Converters/HighlightTextConverter.cs | 65 +- .../Converters/IconRadiusConverter.cs | 27 +- .../OpenResultHotkeyVisibilityConverter.cs | 29 +- Flow.Launcher/Converters/OrdinalConverter.cs | 25 +- .../Converters/QuerySuggestionBoxConverter.cs | 89 ++- .../Converters/StringToKeyBindingConverter.cs | 41 +- Flow.Launcher/Converters/TextConverter.cs | 43 +- Flow.Launcher/Helper/AutoStartup.cs | 77 +- Flow.Launcher/Helper/DWMDropShadow.cs | 105 ++- Flow.Launcher/Helper/DataWebRequestFactory.cs | 111 ++- Flow.Launcher/Helper/ErrorReporting.cs | 77 +- Flow.Launcher/Helper/HotKeyMapper.cs | 169 +++-- Flow.Launcher/Helper/SingleInstance.cs | 697 +++++++++--------- Flow.Launcher/Helper/SingletonWindowOpener.cs | 43 +- Flow.Launcher/Helper/SyntaxSugars.cs | 33 +- .../Helper/WallpaperPathRetrieval.cs | 63 +- Flow.Launcher/Helper/WindowsInteropHelper.cs | 236 +++--- 23 files changed, 1069 insertions(+), 1087 deletions(-) diff --git a/.editorconfig b/.editorconfig index 11a0bcdf6..e1b8ed79e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -58,7 +58,7 @@ dotnet_style_prefer_conditional_expression_over_return = true:silent ############################### # Style Definitions dotnet_naming_style.pascal_case_style.capitalization = pascal_case -# Use PascalCase for constant fields +# Use PascalCase for constant fields dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style @@ -134,7 +134,7 @@ csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true csharp_using_directive_placement = outside_namespace:silent csharp_prefer_simple_using_statement = true:suggestion -csharp_style_namespace_declarations = block_scoped:silent +csharp_style_namespace_declarations = file_scoped:silent csharp_style_prefer_method_group_conversion = true:silent csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_local_functions = false:silent diff --git a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs index ffb7d65b8..41e879913 100644 --- a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs +++ b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs @@ -3,39 +3,38 @@ using System.Globalization; using System.Windows.Data; using System.Windows.Input; -namespace Flow.Launcher.Converters -{ - internal class BoolToIMEConversionModeConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - true => ImeConversionModeValues.Alphanumeric, - _ => ImeConversionModeValues.DoNotCare - }; - } +namespace Flow.Launcher.Converters; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) +internal class BoolToIMEConversionModeConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value switch { - throw new NotImplementedException(); - } + true => ImeConversionModeValues.Alphanumeric, + _ => ImeConversionModeValues.DoNotCare + }; } - internal class BoolToIMEStateConverter : IValueConverter + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - true => InputMethodState.Off, - _ => InputMethodState.DoNotCare - }; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); + } +} + +internal class BoolToIMEStateConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value switch + { + true => InputMethodState.Off, + _ => InputMethodState.DoNotCare + }; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } diff --git a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs index ac3b2cfd0..c0fe6ab55 100644 --- a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs +++ b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs @@ -2,40 +2,39 @@ using System.Windows; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class BoolToVisibilityConverter : IValueConverter { - public class BoolToVisibilityConverter : IValueConverter + public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) + return (value, parameter) switch { - return (value, parameter) switch - { - (true, not null) => Visibility.Collapsed, - (_, not null) => Visibility.Visible, + (true, not null) => Visibility.Collapsed, + (_, not null) => Visibility.Visible, - (true, null) => Visibility.Visible, - (_, null) => Visibility.Collapsed - }; - } - - public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + (true, null) => Visibility.Visible, + (_, null) => Visibility.Collapsed + }; } - public class SplitterConverter : IValueConverter - /* Prevents the dragging part of the preview area from working when preview is turned off. */ - { - public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) - { - return (value, parameter) switch - { - (true, not null) => 0, - (_, not null) => 5, - - (true, null) => 5, - (_, null) => 0 - }; - } - - public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); - } + public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); +} + +public class SplitterConverter : IValueConverter +/* Prevents the dragging part of the preview area from working when preview is turned off. */ +{ + public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) + { + return (value, parameter) switch + { + (true, not null) => 0, + (_, not null) => 5, + + (true, null) => 5, + (_, null) => 0 + }; + } + + public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); } diff --git a/Flow.Launcher/Converters/BorderClipConverter.cs b/Flow.Launcher/Converters/BorderClipConverter.cs index a8d9b34c6..9b0579c33 100644 --- a/Flow.Launcher/Converters/BorderClipConverter.cs +++ b/Flow.Launcher/Converters/BorderClipConverter.cs @@ -7,43 +7,41 @@ using System.Windows.Shapes; // For Clipping inside listbox item -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class BorderClipConverter : IMultiValueConverter { - public class BorderClipConverter : IMultiValueConverter + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + if (values is not [double width, double height, CornerRadius radius]) { - if (values is not [double width, double height, CornerRadius radius]) - { - return DependencyProperty.UnsetValue; - } - - Path myPath = new Path(); - if (width < Double.Epsilon || height < Double.Epsilon) - { - return Geometry.Empty; - } - var radiusHeight = radius.TopLeft; - - // Drawing Round box for bottom round, and rect for top area of listbox. - var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft); - var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0); - - GeometryGroup myGeometryGroup = new GeometryGroup(); - myGeometryGroup.Children.Add(corner); - myGeometryGroup.Children.Add(box); - - CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box); - myPath.Data = c1; - - myPath.Data.Freeze(); - return myPath.Data; + return DependencyProperty.UnsetValue; } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + Path myPath = new Path(); + if (width < Double.Epsilon || height < Double.Epsilon) { - throw new NotSupportedException(); + return Geometry.Empty; } + var radiusHeight = radius.TopLeft; + + // Drawing Round box for bottom round, and rect for top area of listbox. + var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft); + var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0); + + GeometryGroup myGeometryGroup = new GeometryGroup(); + myGeometryGroup.Children.Add(corner); + myGeometryGroup.Children.Add(box); + + CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box); + myPath.Data = c1; + + myPath.Data.Freeze(); + return myPath.Data; } + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } } diff --git a/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs b/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs index 3c46fd01a..7d75ffd5c 100644 --- a/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs +++ b/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs @@ -2,18 +2,17 @@ using System.Globalization; using System.Windows.Data; -namespace Flow.Launcher.Converters -{ - public class DateTimeFormatToNowConverter : IValueConverter - { +namespace Flow.Launcher.Converters; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is not string format ? null : DateTime.Now.ToString(format); - } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } +public class DateTimeFormatToNowConverter : IValueConverter +{ + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is not string format ? null : DateTime.Now.ToString(format); + } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } diff --git a/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs index e81bb2507..24a316603 100644 --- a/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs +++ b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs @@ -3,23 +3,22 @@ using System.Globalization; using System.Windows; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class DiameterToCenterPointConverter : IValueConverter { - public class DiameterToCenterPointConverter : IValueConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + if (value is double d) { - if (value is double d) - { - return new Point(d / 2, d / 2); - } - - return new Point(0, 0); + return new Point(d / 2, d / 2); } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } + return new Point(0, 0); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); } } diff --git a/Flow.Launcher/Converters/HighlightTextConverter.cs b/Flow.Launcher/Converters/HighlightTextConverter.cs index 3f02443e2..436f5fed5 100644 --- a/Flow.Launcher/Converters/HighlightTextConverter.cs +++ b/Flow.Launcher/Converters/HighlightTextConverter.cs @@ -5,45 +5,44 @@ using System.Windows; using System.Windows.Data; using System.Windows.Documents; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class HighlightTextConverter : IMultiValueConverter { - public class HighlightTextConverter : IMultiValueConverter + public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo) { - public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo) - { - if (value.Length < 2) - return new Run(string.Empty); - - if (value[0] is not string text) - return new Run(string.Empty); - - if (value[1] is not List { Count: > 0 } highlightData) - // No highlight data, just return the text - return new Run(text); - - var highlightStyle = (Style)Application.Current.FindResource("HighlightStyle"); - var textBlock = new Span(); + if (value.Length < 2) + return new Run(string.Empty); - for (var i = 0; i < text.Length; i++) + if (value[0] is not string text) + return new Run(string.Empty); + + if (value[1] is not List { Count: > 0 } highlightData) + // No highlight data, just return the text + return new Run(text); + + var highlightStyle = (Style)Application.Current.FindResource("HighlightStyle"); + var textBlock = new Span(); + + for (var i = 0; i < text.Length; i++) + { + var currentCharacter = text.Substring(i, 1); + var run = new Run(currentCharacter) { - var currentCharacter = text.Substring(i, 1); - var run = new Run(currentCharacter) - { - Style = ShouldHighlight(highlightData, i) ? highlightStyle : null - }; - textBlock.Inlines.Add(run); - } - return textBlock; + Style = ShouldHighlight(highlightData, i) ? highlightStyle : null + }; + textBlock.Inlines.Add(run); } + return textBlock; + } - public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) - { - return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; - } + public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) + { + return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; + } - private bool ShouldHighlight(List highlightData, int index) - { - return highlightData.Contains(index); - } + private bool ShouldHighlight(List highlightData, int index) + { + return highlightData.Contains(index); } } diff --git a/Flow.Launcher/Converters/IconRadiusConverter.cs b/Flow.Launcher/Converters/IconRadiusConverter.cs index d0bdc9d1c..d26b39d6b 100644 --- a/Flow.Launcher/Converters/IconRadiusConverter.cs +++ b/Flow.Launcher/Converters/IconRadiusConverter.cs @@ -2,20 +2,19 @@ using System.Globalization; using System.Windows.Data; -namespace Flow.Launcher.Converters -{ - public class IconRadiusConverter : IMultiValueConverter - { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) - { - if (values is not [double size, bool isIconCircular]) - throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]"); +namespace Flow.Launcher.Converters; - return isIconCircular ? size / 2 : size; - } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } +public class IconRadiusConverter : IMultiValueConverter +{ + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values is not [double size, bool isIconCircular]) + throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]"); + + return isIconCircular ? size / 2 : size; + } + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); } } diff --git a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs index 7b7bd0906..7ab13190a 100644 --- a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs +++ b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs @@ -4,24 +4,23 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +[ValueConversion(typeof(bool), typeof(Visibility))] +public class OpenResultHotkeyVisibilityConverter : IValueConverter { - [ValueConversion(typeof(bool), typeof(Visibility))] - public class OpenResultHotkeyVisibilityConverter : IValueConverter + private const int MaxVisibleHotkeys = 10; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - private const int MaxVisibleHotkeys = 10; + var number = int.MaxValue; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var number = int.MaxValue; + if (value is ListBoxItem listBoxItem + && ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox) + number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; - if (value is ListBoxItem listBoxItem - && ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox) - number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; - - return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); + return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed; } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } diff --git a/Flow.Launcher/Converters/OrdinalConverter.cs b/Flow.Launcher/Converters/OrdinalConverter.cs index 438d4effa..9aed2e07b 100644 --- a/Flow.Launcher/Converters/OrdinalConverter.cs +++ b/Flow.Launcher/Converters/OrdinalConverter.cs @@ -3,23 +3,22 @@ using System.Globalization; using System.Windows.Controls; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class OrdinalConverter : IValueConverter { - public class OrdinalConverter : IValueConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + if (value is not ListBoxItem listBoxItem + || ItemsControl.ItemsControlFromItemContainer(listBoxItem) is not ListBox listBox) { - if (value is not ListBoxItem listBoxItem - || ItemsControl.ItemsControlFromItemContainer(listBoxItem) is not ListBox listBox) - { - return 0; - } - - var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; - return res == 10 ? 0 : res; // 10th item => HOTKEY+0 - + return 0; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); + var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; + return res == 10 ? 0 : res; // 10th item => HOTKEY+0 + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } diff --git a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs index 4a8c9e92b..fabd01a24 100644 --- a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs +++ b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs @@ -6,63 +6,62 @@ using System.Windows.Media; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.ViewModel; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class QuerySuggestionBoxConverter : IMultiValueConverter { - public class QuerySuggestionBoxConverter : IMultiValueConverter + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + // values[0] is TextBox: The textbox displaying the autocomplete suggestion + // values[1] is ResultViewModel: Currently selected item in the list + // values[2] is string: Query text + if ( + values.Length != 3 || + values[0] is not TextBox queryTextBox || + values[1] is null || + values[2] is not string queryText || + string.IsNullOrEmpty(queryText) + ) + return string.Empty; + + if (values[1] is not ResultViewModel selectedItem) + return Binding.DoNothing; + + try { - // values[0] is TextBox: The textbox displaying the autocomplete suggestion - // values[1] is ResultViewModel: Currently selected item in the list - // values[2] is string: Query text - if ( - values.Length != 3 || - values[0] is not TextBox queryTextBox || - values[1] is null || - values[2] is not string queryText || - string.IsNullOrEmpty(queryText) - ) + var selectedResult = selectedItem.Result; + var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " "; + var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title; + + if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)) return string.Empty; - - if (values[1] is not ResultViewModel selectedItem) - return Binding.DoNothing; - - try - { - var selectedResult = selectedItem.Result; - var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " "; - var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title; - - if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)) - return string.Empty; - // For AutocompleteQueryCommand. - // When user typed lower case and result title is uppercase, we still want to display suggestion - selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length); + // For AutocompleteQueryCommand. + // When user typed lower case and result title is uppercase, we still want to display suggestion + selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length); - // Check if Text will be larger than our QueryTextBox - Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch); - // TODO: Obsolete warning? - var ft = new FormattedText(queryTextBox.Text, CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black); + // Check if Text will be larger than our QueryTextBox + Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch); + // TODO: Obsolete warning? + var ft = new FormattedText(queryTextBox.Text, CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black); - var offset = queryTextBox.Padding.Right; + var offset = queryTextBox.Padding.Right; - if (ft.Width + offset > queryTextBox.ActualWidth || queryTextBox.HorizontalOffset != 0) - return string.Empty; - - return selectedItem.QuerySuggestionText; - } - catch (Exception e) - { - Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e); + if (ft.Width + offset > queryTextBox.ActualWidth || queryTextBox.HorizontalOffset != 0) return string.Empty; - } + + return selectedItem.QuerySuggestionText; } - - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + catch (Exception e) { - throw new NotImplementedException(); + Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e); + return string.Empty; } } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } } diff --git a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs index 22526bd82..21bf584e7 100644 --- a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs +++ b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs @@ -3,28 +3,27 @@ using System.Globalization; using System.Windows.Data; using System.Windows.Input; -namespace Flow.Launcher.Converters -{ - class StringToKeyBindingConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (parameter is not string mode || value is not string hotkeyStr) - return null; - - var converter = new KeyGestureConverter(); - var key = (KeyGesture)converter.ConvertFromString(hotkeyStr); - return mode switch - { - "key" => key?.Key, - "modifiers" => key?.Modifiers, - _ => null - }; - } +namespace Flow.Launcher.Converters; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) +class StringToKeyBindingConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (parameter is not string mode || value is not string hotkeyStr) + return null; + + var converter = new KeyGestureConverter(); + var key = (KeyGesture)converter.ConvertFromString(hotkeyStr); + return mode switch { - throw new NotImplementedException(); - } + "key" => key?.Key, + "modifiers" => key?.Modifiers, + _ => null + }; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } diff --git a/Flow.Launcher/Converters/TextConverter.cs b/Flow.Launcher/Converters/TextConverter.cs index c0b34d053..5f0e1ea82 100644 --- a/Flow.Launcher/Converters/TextConverter.cs +++ b/Flow.Launcher/Converters/TextConverter.cs @@ -4,28 +4,27 @@ using System.Windows.Data; using Flow.Launcher.Core.Resource; using Flow.Launcher.ViewModel; -namespace Flow.Launcher.Converters -{ - public class TextConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var id = value?.ToString(); - var translationKey = id switch - { - PluginStoreItemViewModel.NewRelease => "pluginStore_NewRelease", - PluginStoreItemViewModel.RecentlyUpdated => "pluginStore_RecentlyUpdated", - PluginStoreItemViewModel.None => "pluginStore_None", - PluginStoreItemViewModel.Installed => "pluginStore_Installed", - _ => null - }; - - if (translationKey is null) - return id; - - return InternationalizationManager.Instance.GetTranslation(translationKey); - } +namespace Flow.Launcher.Converters; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); +public class TextConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var id = value?.ToString(); + var translationKey = id switch + { + PluginStoreItemViewModel.NewRelease => "pluginStore_NewRelease", + PluginStoreItemViewModel.RecentlyUpdated => "pluginStore_RecentlyUpdated", + PluginStoreItemViewModel.None => "pluginStore_None", + PluginStoreItemViewModel.Installed => "pluginStore_Installed", + _ => null + }; + + if (translationKey is null) + return id; + + return InternationalizationManager.Instance.GetTranslation(translationKey); } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs index bf36b7f6f..4bff30caf 100644 --- a/Flow.Launcher/Helper/AutoStartup.cs +++ b/Flow.Launcher/Helper/AutoStartup.cs @@ -3,57 +3,56 @@ using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Microsoft.Win32; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class AutoStartup { - public class AutoStartup + private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; + + public static bool IsEnabled { - private const string StartupPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; - - public static bool IsEnabled - { - get - { - try - { - using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - var path = key?.GetValue(Constant.FlowLauncher) as string; - return path == Constant.ExecutablePath; - } - catch (Exception e) - { - Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}"); - } - - return false; - } - } - - public static void Disable() + get { try { using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - key?.DeleteValue(Constant.FlowLauncher, false); + var path = key?.GetValue(Constant.FlowLauncher) as string; + return path == Constant.ExecutablePath; } catch (Exception e) { - Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}"); - throw; + Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}"); } - } - internal static void Enable() + return false; + } + } + + public static void Disable() + { + try { - try - { - using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\""); - } - catch (Exception e) - { - Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}"); - throw; - } + using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); + key?.DeleteValue(Constant.FlowLauncher, false); + } + catch (Exception e) + { + Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}"); + throw; + } + } + + internal static void Enable() + { + try + { + using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); + key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\""); + } + catch (Exception e) + { + Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}"); + throw; } } } diff --git a/Flow.Launcher/Helper/DWMDropShadow.cs b/Flow.Launcher/Helper/DWMDropShadow.cs index 3a1f82d6f..e448acd4c 100644 --- a/Flow.Launcher/Helper/DWMDropShadow.cs +++ b/Flow.Launcher/Helper/DWMDropShadow.cs @@ -4,70 +4,69 @@ using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class DwmDropShadow { - public class DwmDropShadow + + [DllImport("dwmapi.dll", PreserveSig = true)] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); + + [DllImport("dwmapi.dll")] + private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset); + + /// + /// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven). + /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect, + /// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window). + /// + /// Window to which the shadow will be applied + public static void DropShadowToWindow(Window window) { - - [DllImport("dwmapi.dll", PreserveSig = true)] - private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); - - [DllImport("dwmapi.dll")] - private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset); - - /// - /// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven). - /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect, - /// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window). - /// - /// Window to which the shadow will be applied - public static void DropShadowToWindow(Window window) + if (!DropShadow(window)) { - if (!DropShadow(window)) - { - window.SourceInitialized += window_SourceInitialized; - } + window.SourceInitialized += window_SourceInitialized; } + } - private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo + private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo + { + Window window = (Window)sender; + + DropShadow(window); + + window.SourceInitialized -= window_SourceInitialized; + } + + /// + /// The actual method that makes API calls to drop the shadow to the window + /// + /// Window to which the shadow will be applied + /// True if the method succeeded, false if not + private static bool DropShadow(Window window) + { + try { - Window window = (Window)sender; + WindowInteropHelper helper = new WindowInteropHelper(window); + int val = 2; + int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4); - DropShadow(window); - - window.SourceInitialized -= window_SourceInitialized; - } - - /// - /// The actual method that makes API calls to drop the shadow to the window - /// - /// Window to which the shadow will be applied - /// True if the method succeeded, false if not - private static bool DropShadow(Window window) - { - try + if (ret1 == 0) { - WindowInteropHelper helper = new WindowInteropHelper(window); - int val = 2; - int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4); - - if (ret1 == 0) - { - Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 }; - int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m); - return ret2 == 0; - } - else - { - return false; - } + Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 }; + int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m); + return ret2 == 0; } - catch (Exception) + else { - // Probably dwmapi.dll not found (incompatible OS) return false; } } - + catch (Exception) + { + // Probably dwmapi.dll not found (incompatible OS) + return false; + } } -} \ No newline at end of file + +} diff --git a/Flow.Launcher/Helper/DataWebRequestFactory.cs b/Flow.Launcher/Helper/DataWebRequestFactory.cs index b3b7e9c63..2e72ee240 100644 --- a/Flow.Launcher/Helper/DataWebRequestFactory.cs +++ b/Flow.Launcher/Helper/DataWebRequestFactory.cs @@ -2,68 +2,67 @@ using System.IO; using System.Net; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class DataWebRequestFactory : IWebRequestCreate { - public class DataWebRequestFactory : IWebRequestCreate + class DataWebRequest : WebRequest { - class DataWebRequest : WebRequest + private readonly Uri _uri; + + public DataWebRequest(Uri uri) { - private readonly Uri m_uri; - - public DataWebRequest(Uri uri) - { - m_uri = uri; - } - - public override WebResponse GetResponse() - { - return new DataWebResponse(m_uri); - } + _uri = uri; } - class DataWebResponse : WebResponse + public override WebResponse GetResponse() { - private readonly string m_contentType; - private readonly byte[] m_data; - - public DataWebResponse(Uri uri) - { - string uriString = uri.AbsoluteUri; - - int commaIndex = uriString.IndexOf(','); - var headers = uriString.Substring(0, commaIndex).Split(';'); - m_contentType = headers[0]; - string dataString = uriString.Substring(commaIndex + 1); - m_data = Convert.FromBase64String(dataString); - } - - public override string ContentType - { - get { return m_contentType; } - set - { - throw new NotSupportedException(); - } - } - - public override long ContentLength - { - get { return m_data.Length; } - set - { - throw new NotSupportedException(); - } - } - - public override Stream GetResponseStream() - { - return new MemoryStream(m_data); - } - } - - public WebRequest Create(Uri uri) - { - return new DataWebRequest(uri); + return new DataWebResponse(_uri); } } + + class DataWebResponse : WebResponse + { + private readonly string _contentType; + private readonly byte[] _data; + + public DataWebResponse(Uri uri) + { + string uriString = uri.AbsoluteUri; + + int commaIndex = uriString.IndexOf(','); + var headers = uriString.Substring(0, commaIndex).Split(';'); + _contentType = headers[0]; + string dataString = uriString.Substring(commaIndex + 1); + _data = Convert.FromBase64String(dataString); + } + + public override string ContentType + { + get { return _contentType; } + set + { + throw new NotSupportedException(); + } + } + + public override long ContentLength + { + get { return _data.Length; } + set + { + throw new NotSupportedException(); + } + } + + public override Stream GetResponseStream() + { + return new MemoryStream(_data); + } + } + + public WebRequest Create(Uri uri) + { + return new DataWebRequest(uri); + } } diff --git a/Flow.Launcher/Helper/ErrorReporting.cs b/Flow.Launcher/Helper/ErrorReporting.cs index b5da2efad..5b79c520d 100644 --- a/Flow.Launcher/Helper/ErrorReporting.cs +++ b/Flow.Launcher/Helper/ErrorReporting.cs @@ -4,45 +4,52 @@ using NLog; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Exception; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public static class ErrorReporting { - public static class ErrorReporting + private static void Report(Exception e) { - private static void Report(Exception e) - { - var logger = LogManager.GetLogger("UnHandledException"); - logger.Fatal(ExceptionFormatter.FormatExcpetion(e)); - var reportWindow = new ReportWindow(e); - reportWindow.Show(); - } + var logger = LogManager.GetLogger("UnHandledException"); + logger.Fatal(ExceptionFormatter.FormatExcpetion(e)); + var reportWindow = new ReportWindow(e); + reportWindow.Show(); + } - public static void UnhandledExceptionHandle(object sender, UnhandledExceptionEventArgs e) - { - //handle non-ui thread exceptions - Report((Exception)e.ExceptionObject); - } + public static void UnhandledExceptionHandle(object sender, UnhandledExceptionEventArgs e) + { + //handle non-ui thread exceptions + Report((Exception)e.ExceptionObject); + } - public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) - { - //handle ui thread exceptions - Report(e.Exception); - //prevent application exist, so the user can copy prompted error info - e.Handled = true; - } - - public static string RuntimeInfo() - { - var info = $"\nFlow Launcher version: {Constant.Version}" + - $"\nOS Version: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()}" + - $"\nIntPtr Length: {IntPtr.Size}" + - $"\nx64: {Environment.Is64BitOperatingSystem}"; - return info; - } + public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + { + //handle ui thread exceptions + Report(e.Exception); + //prevent application exist, so the user can copy prompted error info + e.Handled = true; + } - public static string DependenciesInfo() - { - var info = $"\nPython Path: {Constant.PythonPath}\nNode Path: {Constant.NodePath}"; - return info; - } + public static string RuntimeInfo() + { + var info = + $""" + + Flow Launcher version: {Constant.Version} + OS Version: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()} + IntPtr Length: {IntPtr.Size} + x64: {Environment.Is64BitOperatingSystem} + """; + return info; + } + + public static string DependenciesInfo() + { + var info = $""" + + Python Path: {Constant.PythonPath} + Node Path: {Constant.NodePath} + """; + return info; } } diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 3949d2828..21ddf276a 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -7,97 +7,96 @@ using Flow.Launcher.Core.Resource; using System.Windows; using Flow.Launcher.ViewModel; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +internal static class HotKeyMapper { - internal static class HotKeyMapper + private static Settings _settings; + private static MainViewModel _mainViewModel; + + internal static void Initialize(MainViewModel mainVM) { - private static Settings settings; - private static MainViewModel mainViewModel; + _mainViewModel = mainVM; + _settings = _mainViewModel.Settings; - internal static void Initialize(MainViewModel mainVM) + SetHotkey(_settings.Hotkey, OnToggleHotkey); + LoadCustomPluginHotkey(); + } + + internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) + { + if (!_mainViewModel.ShouldIgnoreHotkeys()) + _mainViewModel.ToggleFlowLauncher(); + } + + private static void SetHotkey(string hotkeyStr, EventHandler action) + { + var hotkey = new HotkeyModel(hotkeyStr); + SetHotkey(hotkey, action); + } + + internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) + { + string hotkeyStr = hotkey.ToString(); + try { - mainViewModel = mainVM; - settings = mainViewModel.Settings; - - SetHotkey(settings.Hotkey, OnToggleHotkey); - LoadCustomPluginHotkey(); + HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); } - - internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) + catch (Exception) { - if (!mainViewModel.ShouldIgnoreHotkeys()) - mainViewModel.ToggleFlowLauncher(); - } - - private static void SetHotkey(string hotkeyStr, EventHandler action) - { - var hotkey = new HotkeyModel(hotkeyStr); - SetHotkey(hotkey, action); - } - - internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) - { - string hotkeyStr = hotkey.ToString(); - try - { - HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); - } - catch (Exception) - { - string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); - string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle"); - MessageBox.Show(errorMsg,errorMsgTitle); - } - } - - internal static void RemoveHotkey(string hotkeyStr) - { - if (!string.IsNullOrEmpty(hotkeyStr)) - { - HotkeyManager.Current.Remove(hotkeyStr); - } - } - - internal static void LoadCustomPluginHotkey() - { - if (settings.CustomPluginHotkeys == null) - return; - - foreach (CustomPluginHotkey hotkey in settings.CustomPluginHotkeys) - { - SetCustomQueryHotkey(hotkey); - } - } - - internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) - { - SetHotkey(hotkey.Hotkey, (s, e) => - { - if (mainViewModel.ShouldIgnoreHotkeys()) - return; - - mainViewModel.Show(); - mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true); - }); - } - - internal static bool CheckAvailability(HotkeyModel currentHotkey) - { - try - { - HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { }); - - return true; - } - catch - { - } - finally - { - HotkeyManager.Current.Remove("HotkeyAvailabilityTest"); - } - - return false; + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle"); + MessageBox.Show(errorMsg,errorMsgTitle); } } + + internal static void RemoveHotkey(string hotkeyStr) + { + if (!string.IsNullOrEmpty(hotkeyStr)) + { + HotkeyManager.Current.Remove(hotkeyStr); + } + } + + internal static void LoadCustomPluginHotkey() + { + if (_settings.CustomPluginHotkeys == null) + return; + + foreach (CustomPluginHotkey hotkey in _settings.CustomPluginHotkeys) + { + SetCustomQueryHotkey(hotkey); + } + } + + internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) + { + SetHotkey(hotkey.Hotkey, (s, e) => + { + if (_mainViewModel.ShouldIgnoreHotkeys()) + return; + + _mainViewModel.Show(); + _mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true); + }); + } + + internal static bool CheckAvailability(HotkeyModel currentHotkey) + { + try + { + HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { }); + + return true; + } + catch + { + } + finally + { + HotkeyManager.Current.Remove("HotkeyAvailabilityTest"); + } + + return false; + } } diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs index 739fed378..380da1647 100644 --- a/Flow.Launcher/Helper/SingleInstance.cs +++ b/Flow.Launcher/Helper/SingleInstance.cs @@ -12,375 +12,374 @@ using System.Windows; // http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/ // modified to allow single instace restart -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +internal enum WM { - internal enum WM + NULL = 0x0000, + CREATE = 0x0001, + DESTROY = 0x0002, + MOVE = 0x0003, + SIZE = 0x0005, + ACTIVATE = 0x0006, + SETFOCUS = 0x0007, + KILLFOCUS = 0x0008, + ENABLE = 0x000A, + SETREDRAW = 0x000B, + SETTEXT = 0x000C, + GETTEXT = 0x000D, + GETTEXTLENGTH = 0x000E, + PAINT = 0x000F, + CLOSE = 0x0010, + QUERYENDSESSION = 0x0011, + QUIT = 0x0012, + QUERYOPEN = 0x0013, + ERASEBKGND = 0x0014, + SYSCOLORCHANGE = 0x0015, + SHOWWINDOW = 0x0018, + ACTIVATEAPP = 0x001C, + SETCURSOR = 0x0020, + MOUSEACTIVATE = 0x0021, + CHILDACTIVATE = 0x0022, + QUEUESYNC = 0x0023, + GETMINMAXINFO = 0x0024, + + WINDOWPOSCHANGING = 0x0046, + WINDOWPOSCHANGED = 0x0047, + + CONTEXTMENU = 0x007B, + STYLECHANGING = 0x007C, + STYLECHANGED = 0x007D, + DISPLAYCHANGE = 0x007E, + GETICON = 0x007F, + SETICON = 0x0080, + NCCREATE = 0x0081, + NCDESTROY = 0x0082, + NCCALCSIZE = 0x0083, + NCHITTEST = 0x0084, + NCPAINT = 0x0085, + NCACTIVATE = 0x0086, + GETDLGCODE = 0x0087, + SYNCPAINT = 0x0088, + NCMOUSEMOVE = 0x00A0, + NCLBUTTONDOWN = 0x00A1, + NCLBUTTONUP = 0x00A2, + NCLBUTTONDBLCLK = 0x00A3, + NCRBUTTONDOWN = 0x00A4, + NCRBUTTONUP = 0x00A5, + NCRBUTTONDBLCLK = 0x00A6, + NCMBUTTONDOWN = 0x00A7, + NCMBUTTONUP = 0x00A8, + NCMBUTTONDBLCLK = 0x00A9, + + SYSKEYDOWN = 0x0104, + SYSKEYUP = 0x0105, + SYSCHAR = 0x0106, + SYSDEADCHAR = 0x0107, + COMMAND = 0x0111, + SYSCOMMAND = 0x0112, + + MOUSEMOVE = 0x0200, + LBUTTONDOWN = 0x0201, + LBUTTONUP = 0x0202, + LBUTTONDBLCLK = 0x0203, + RBUTTONDOWN = 0x0204, + RBUTTONUP = 0x0205, + RBUTTONDBLCLK = 0x0206, + MBUTTONDOWN = 0x0207, + MBUTTONUP = 0x0208, + MBUTTONDBLCLK = 0x0209, + MOUSEWHEEL = 0x020A, + XBUTTONDOWN = 0x020B, + XBUTTONUP = 0x020C, + XBUTTONDBLCLK = 0x020D, + MOUSEHWHEEL = 0x020E, + + + CAPTURECHANGED = 0x0215, + + ENTERSIZEMOVE = 0x0231, + EXITSIZEMOVE = 0x0232, + + IME_SETCONTEXT = 0x0281, + IME_NOTIFY = 0x0282, + IME_CONTROL = 0x0283, + IME_COMPOSITIONFULL = 0x0284, + IME_SELECT = 0x0285, + IME_CHAR = 0x0286, + IME_REQUEST = 0x0288, + IME_KEYDOWN = 0x0290, + IME_KEYUP = 0x0291, + + NCMOUSELEAVE = 0x02A2, + + DWMCOMPOSITIONCHANGED = 0x031E, + DWMNCRENDERINGCHANGED = 0x031F, + DWMCOLORIZATIONCOLORCHANGED = 0x0320, + DWMWINDOWMAXIMIZEDCHANGE = 0x0321, + + #region Windows 7 + DWMSENDICONICTHUMBNAIL = 0x0323, + DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326, + #endregion + + USER = 0x0400, + + // This is the hard-coded message value used by WinForms for Shell_NotifyIcon. + // It's relatively safe to reuse. + TRAYMOUSEMESSAGE = 0x800, //WM_USER + 1024 + APP = 0x8000 +} + +[SuppressUnmanagedCodeSecurity] +internal static class NativeMethods +{ + /// + /// Delegate declaration that matches WndProc signatures. + /// + public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled); + + [DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)] + private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs); + + + [DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)] + private static extern IntPtr _LocalFree(IntPtr hMem); + + + public static string[] CommandLineToArgvW(string cmdLine) { - NULL = 0x0000, - CREATE = 0x0001, - DESTROY = 0x0002, - MOVE = 0x0003, - SIZE = 0x0005, - ACTIVATE = 0x0006, - SETFOCUS = 0x0007, - KILLFOCUS = 0x0008, - ENABLE = 0x000A, - SETREDRAW = 0x000B, - SETTEXT = 0x000C, - GETTEXT = 0x000D, - GETTEXTLENGTH = 0x000E, - PAINT = 0x000F, - CLOSE = 0x0010, - QUERYENDSESSION = 0x0011, - QUIT = 0x0012, - QUERYOPEN = 0x0013, - ERASEBKGND = 0x0014, - SYSCOLORCHANGE = 0x0015, - SHOWWINDOW = 0x0018, - ACTIVATEAPP = 0x001C, - SETCURSOR = 0x0020, - MOUSEACTIVATE = 0x0021, - CHILDACTIVATE = 0x0022, - QUEUESYNC = 0x0023, - GETMINMAXINFO = 0x0024, + IntPtr argv = IntPtr.Zero; + try + { + int numArgs = 0; - WINDOWPOSCHANGING = 0x0046, - WINDOWPOSCHANGED = 0x0047, + argv = _CommandLineToArgvW(cmdLine, out numArgs); + if (argv == IntPtr.Zero) + { + throw new Win32Exception(); + } + var result = new string[numArgs]; - CONTEXTMENU = 0x007B, - STYLECHANGING = 0x007C, - STYLECHANGED = 0x007D, - DISPLAYCHANGE = 0x007E, - GETICON = 0x007F, - SETICON = 0x0080, - NCCREATE = 0x0081, - NCDESTROY = 0x0082, - NCCALCSIZE = 0x0083, - NCHITTEST = 0x0084, - NCPAINT = 0x0085, - NCACTIVATE = 0x0086, - GETDLGCODE = 0x0087, - SYNCPAINT = 0x0088, - NCMOUSEMOVE = 0x00A0, - NCLBUTTONDOWN = 0x00A1, - NCLBUTTONUP = 0x00A2, - NCLBUTTONDBLCLK = 0x00A3, - NCRBUTTONDOWN = 0x00A4, - NCRBUTTONUP = 0x00A5, - NCRBUTTONDBLCLK = 0x00A6, - NCMBUTTONDOWN = 0x00A7, - NCMBUTTONUP = 0x00A8, - NCMBUTTONDBLCLK = 0x00A9, + for (int i = 0; i < numArgs; i++) + { + IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr))); + result[i] = Marshal.PtrToStringUni(currArg); + } - SYSKEYDOWN = 0x0104, - SYSKEYUP = 0x0105, - SYSCHAR = 0x0106, - SYSDEADCHAR = 0x0107, - COMMAND = 0x0111, - SYSCOMMAND = 0x0112, + return result; + } + finally + { - MOUSEMOVE = 0x0200, - LBUTTONDOWN = 0x0201, - LBUTTONUP = 0x0202, - LBUTTONDBLCLK = 0x0203, - RBUTTONDOWN = 0x0204, - RBUTTONUP = 0x0205, - RBUTTONDBLCLK = 0x0206, - MBUTTONDOWN = 0x0207, - MBUTTONUP = 0x0208, - MBUTTONDBLCLK = 0x0209, - MOUSEWHEEL = 0x020A, - XBUTTONDOWN = 0x020B, - XBUTTONUP = 0x020C, - XBUTTONDBLCLK = 0x020D, - MOUSEHWHEEL = 0x020E, - - - CAPTURECHANGED = 0x0215, - - ENTERSIZEMOVE = 0x0231, - EXITSIZEMOVE = 0x0232, - - IME_SETCONTEXT = 0x0281, - IME_NOTIFY = 0x0282, - IME_CONTROL = 0x0283, - IME_COMPOSITIONFULL = 0x0284, - IME_SELECT = 0x0285, - IME_CHAR = 0x0286, - IME_REQUEST = 0x0288, - IME_KEYDOWN = 0x0290, - IME_KEYUP = 0x0291, - - NCMOUSELEAVE = 0x02A2, - - DWMCOMPOSITIONCHANGED = 0x031E, - DWMNCRENDERINGCHANGED = 0x031F, - DWMCOLORIZATIONCOLORCHANGED = 0x0320, - DWMWINDOWMAXIMIZEDCHANGE = 0x0321, - - #region Windows 7 - DWMSENDICONICTHUMBNAIL = 0x0323, - DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326, - #endregion - - USER = 0x0400, - - // This is the hard-coded message value used by WinForms for Shell_NotifyIcon. - // It's relatively safe to reuse. - TRAYMOUSEMESSAGE = 0x800, //WM_USER + 1024 - APP = 0x8000 + IntPtr p = _LocalFree(argv); + // Otherwise LocalFree failed. + // Assert.AreEqual(IntPtr.Zero, p); + } } - [SuppressUnmanagedCodeSecurity] - internal static class NativeMethods - { - /// - /// Delegate declaration that matches WndProc signatures. - /// - public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled); +} - [DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)] - private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs); +public interface ISingleInstanceApp +{ + void OnSecondAppStarted(); +} +/// +/// This class checks to make sure that only one instance of +/// this application is running at a time. +/// +/// +/// Note: this class should be used with some caution, because it does no +/// security checking. For example, if one instance of an app that uses this class +/// is running as Administrator, any other instance, even if it is not +/// running as Administrator, can activate it with command line arguments. +/// For most apps, this will not be much of an issue. +/// +public static class SingleInstance + where TApplication: Application , ISingleInstanceApp - [DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)] - private static extern IntPtr _LocalFree(IntPtr hMem); - - - public static string[] CommandLineToArgvW(string cmdLine) - { - IntPtr argv = IntPtr.Zero; - try - { - int numArgs = 0; - - argv = _CommandLineToArgvW(cmdLine, out numArgs); - if (argv == IntPtr.Zero) - { - throw new Win32Exception(); - } - var result = new string[numArgs]; - - for (int i = 0; i < numArgs; i++) - { - IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr))); - result[i] = Marshal.PtrToStringUni(currArg); - } - - return result; - } - finally - { - - IntPtr p = _LocalFree(argv); - // Otherwise LocalFree failed. - // Assert.AreEqual(IntPtr.Zero, p); - } - } - - } - - public interface ISingleInstanceApp - { - void OnSecondAppStarted(); - } +{ + #region Private Fields /// - /// This class checks to make sure that only one instance of - /// this application is running at a time. + /// String delimiter used in channel names. /// - /// - /// Note: this class should be used with some caution, because it does no - /// security checking. For example, if one instance of an app that uses this class - /// is running as Administrator, any other instance, even if it is not - /// running as Administrator, can activate it with command line arguments. - /// For most apps, this will not be much of an issue. - /// - public static class SingleInstance - where TApplication: Application , ISingleInstanceApp - + private const string Delimiter = ":"; + + /// + /// Suffix to the channel name. + /// + private const string ChannelNameSuffix = "SingeInstanceIPCChannel"; + + /// + /// Application mutex. + /// + internal static Mutex singleInstanceMutex; + + #endregion + + #region Public Properties + + #endregion + + #region Public Methods + + /// + /// Checks if the instance of the application attempting to start is the first instance. + /// If not, activates the first instance. + /// + /// True if this is the first instance of the application. + public static bool InitializeAsFirstInstance( string uniqueName ) { - #region Private Fields + // Build unique application Id and the IPC channel name. + string applicationIdentifier = uniqueName + Environment.UserName; - /// - /// String delimiter used in channel names. - /// - private const string Delimiter = ":"; + string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix); - /// - /// Suffix to the channel name. - /// - private const string ChannelNameSuffix = "SingeInstanceIPCChannel"; - - /// - /// Application mutex. - /// - internal static Mutex singleInstanceMutex; - - #endregion - - #region Public Properties - - #endregion - - #region Public Methods - - /// - /// Checks if the instance of the application attempting to start is the first instance. - /// If not, activates the first instance. - /// - /// True if this is the first instance of the application. - public static bool InitializeAsFirstInstance( string uniqueName ) + // Create mutex based on unique application Id to check if this is the first instance of the application. + bool firstInstance; + singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance); + if (firstInstance) { - // Build unique application Id and the IPC channel name. - string applicationIdentifier = uniqueName + Environment.UserName; - - string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix); - - // Create mutex based on unique application Id to check if this is the first instance of the application. - bool firstInstance; - singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance); - if (firstInstance) - { - _ = CreateRemoteService(channelName); - return true; - } - else - { - _ = SignalFirstInstance(channelName); - return false; - } + _ = CreateRemoteService(channelName); + return true; } - - /// - /// Cleans up single-instance code, clearing shared resources, mutexes, etc. - /// - public static void Cleanup() + else { - singleInstanceMutex?.ReleaseMutex(); + _ = SignalFirstInstance(channelName); + return false; } - - #endregion - - #region Private Methods - - /// - /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved. - /// - /// List of command line arg strings. - private static IList GetCommandLineArgs( string uniqueApplicationName ) - { - string[] args = null; - - try - { - // The application was not clickonce deployed, get args from standard API's - args = Environment.GetCommandLineArgs(); - } - catch (NotSupportedException) - { - - // The application was clickonce deployed - // Clickonce deployed apps cannot recieve traditional commandline arguments - // As a workaround commandline arguments can be written to a shared location before - // the app is launched and the app can obtain its commandline arguments from the - // shared location - string appFolderPath = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), uniqueApplicationName); - - string cmdLinePath = Path.Combine(appFolderPath, "cmdline.txt"); - if (File.Exists(cmdLinePath)) - { - try - { - using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) - { - args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); - } - - File.Delete(cmdLinePath); - } - catch (IOException) - { - } - } - } - - if (args == null) - { - args = new string[] { }; - } - - return new List(args); - } - - /// - /// Creates a remote server pipe for communication. - /// Once receives signal from client, will activate first instance. - /// - /// Application's IPC channel name. - private static async Task CreateRemoteService(string channelName) - { - using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In)) - { - while(true) - { - // Wait for connection to the pipe - await pipeServer.WaitForConnectionAsync(); - if (Application.Current != null) - { - // Do an asynchronous call to ActivateFirstInstance function - Application.Current.Dispatcher.Invoke(ActivateFirstInstance); - } - // Disconect client - pipeServer.Disconnect(); - } - } - } - - /// - /// Creates a client pipe and sends a signal to server to launch first instance - /// - /// Application's IPC channel name. - /// - /// Command line arguments for the second instance, passed to the first instance to take appropriate action. - /// - private static async Task SignalFirstInstance(string channelName) - { - // Create a client pipe connected to server - using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out)) - { - // Connect to the available pipe - await pipeClient.ConnectAsync(0); - } - } - - /// - /// Callback for activating first instance of the application. - /// - /// Callback argument. - /// Always null. - private static object ActivateFirstInstanceCallback(object o) - { - ActivateFirstInstance(); - return null; - } - - /// - /// Activates the first instance of the application with arguments from a second instance. - /// - /// List of arguments to supply the first instance of the application. - private static void ActivateFirstInstance() - { - // Set main window state and process command line args - if (Application.Current == null) - { - return; - } - - ((TApplication)Application.Current).OnSecondAppStarted(); - } - - #endregion } + + /// + /// Cleans up single-instance code, clearing shared resources, mutexes, etc. + /// + public static void Cleanup() + { + singleInstanceMutex?.ReleaseMutex(); + } + + #endregion + + #region Private Methods + + /// + /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved. + /// + /// List of command line arg strings. + private static IList GetCommandLineArgs( string uniqueApplicationName ) + { + string[] args = null; + + try + { + // The application was not clickonce deployed, get args from standard API's + args = Environment.GetCommandLineArgs(); + } + catch (NotSupportedException) + { + + // The application was clickonce deployed + // Clickonce deployed apps cannot recieve traditional commandline arguments + // As a workaround commandline arguments can be written to a shared location before + // the app is launched and the app can obtain its commandline arguments from the + // shared location + string appFolderPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), uniqueApplicationName); + + string cmdLinePath = Path.Combine(appFolderPath, "cmdline.txt"); + if (File.Exists(cmdLinePath)) + { + try + { + using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) + { + args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); + } + + File.Delete(cmdLinePath); + } + catch (IOException) + { + } + } + } + + if (args == null) + { + args = new string[] { }; + } + + return new List(args); + } + + /// + /// Creates a remote server pipe for communication. + /// Once receives signal from client, will activate first instance. + /// + /// Application's IPC channel name. + private static async Task CreateRemoteService(string channelName) + { + using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In)) + { + while(true) + { + // Wait for connection to the pipe + await pipeServer.WaitForConnectionAsync(); + if (Application.Current != null) + { + // Do an asynchronous call to ActivateFirstInstance function + Application.Current.Dispatcher.Invoke(ActivateFirstInstance); + } + // Disconect client + pipeServer.Disconnect(); + } + } + } + + /// + /// Creates a client pipe and sends a signal to server to launch first instance + /// + /// Application's IPC channel name. + /// + /// Command line arguments for the second instance, passed to the first instance to take appropriate action. + /// + private static async Task SignalFirstInstance(string channelName) + { + // Create a client pipe connected to server + using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out)) + { + // Connect to the available pipe + await pipeClient.ConnectAsync(0); + } + } + + /// + /// Callback for activating first instance of the application. + /// + /// Callback argument. + /// Always null. + private static object ActivateFirstInstanceCallback(object o) + { + ActivateFirstInstance(); + return null; + } + + /// + /// Activates the first instance of the application with arguments from a second instance. + /// + /// List of arguments to supply the first instance of the application. + private static void ActivateFirstInstance() + { + // Set main window state and process command line args + if (Application.Current == null) + { + return; + } + + ((TApplication)Application.Current).OnSecondAppStarted(); + } + + #endregion } diff --git a/Flow.Launcher/Helper/SingletonWindowOpener.cs b/Flow.Launcher/Helper/SingletonWindowOpener.cs index 8efc9be99..b5c2d8b55 100644 --- a/Flow.Launcher/Helper/SingletonWindowOpener.cs +++ b/Flow.Launcher/Helper/SingletonWindowOpener.cs @@ -2,27 +2,26 @@ using System.Linq; using System.Windows; -namespace Flow.Launcher.Helper -{ - public static class SingletonWindowOpener - { - public static T Open(params object[] args) where T : Window - { - var window = Application.Current.Windows.OfType().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(); - - window.Focus(); +namespace Flow.Launcher.Helper; - return (T)window; - } +public static class SingletonWindowOpener +{ + public static T Open(params object[] args) where T : Window + { + var window = Application.Current.Windows.OfType().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(); + + window.Focus(); + + return (T)window; } -} \ No newline at end of file +} diff --git a/Flow.Launcher/Helper/SyntaxSugars.cs b/Flow.Launcher/Helper/SyntaxSugars.cs index 7e0349422..41250dadf 100644 --- a/Flow.Launcher/Helper/SyntaxSugars.cs +++ b/Flow.Launcher/Helper/SyntaxSugars.cs @@ -1,24 +1,23 @@ using System; -namespace Flow.Launcher.Helper -{ - public static class SyntaxSugars - { - public static TResult CallOrRescueDefault(Func callback) - { - return CallOrRescueDefault(callback, default(TResult)); - } +namespace Flow.Launcher.Helper; - public static TResult CallOrRescueDefault(Func callback, TResult def) +public static class SyntaxSugars +{ + public static TResult CallOrRescueDefault(Func callback) + { + return CallOrRescueDefault(callback, default(TResult)); + } + + public static TResult CallOrRescueDefault(Func callback, TResult def) + { + try { - try - { - return callback(); - } - catch - { - return def; - } + return callback(); + } + catch + { + return def; } } } diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs index 9e5d77283..e08e227cc 100644 --- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs +++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs @@ -5,44 +5,43 @@ using System.Text; using System.Windows.Media; using Microsoft.Win32; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public static class WallpaperPathRetrieval { - public static class WallpaperPathRetrieval + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + private static extern Int32 SystemParametersInfo(UInt32 action, + Int32 uParam, StringBuilder vParam, UInt32 winIni); + private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73; + private static int MAX_PATH = 260; + + public static string GetWallpaperPath() { - [DllImport("user32.dll", CharSet = CharSet.Unicode)] - private static extern Int32 SystemParametersInfo(UInt32 action, - Int32 uParam, StringBuilder vParam, UInt32 winIni); - private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73; - private static int MAX_PATH = 260; + var wallpaper = new StringBuilder(MAX_PATH); + SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0); - public static string GetWallpaperPath() + var str = wallpaper.ToString(); + if (string.IsNullOrEmpty(str)) + return null; + + return str; + } + + public static Color GetWallpaperColor() + { + RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true); + var result = key?.GetValue("Background", null); + if (result is string strResult) { - var wallpaper = new StringBuilder(MAX_PATH); - SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0); - - var str = wallpaper.ToString(); - if (string.IsNullOrEmpty(str)) - return null; - - return str; - } - - public static Color GetWallpaperColor() - { - RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true); - var result = key.GetValue(@"Background", null); - if (result != null && result is string) + try + { + var parts = strResult.Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList(); + return Color.FromRgb(parts[0], parts[1], parts[2]); + } + catch { - try - { - var parts = result.ToString().Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList(); - return Color.FromRgb(parts[0], parts[1], parts[2]); - } - catch - { - } } - return Colors.Transparent; } + return Colors.Transparent; } } diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs index 16a96cd64..89fbec967 100644 --- a/Flow.Launcher/Helper/WindowsInteropHelper.cs +++ b/Flow.Launcher/Helper/WindowsInteropHelper.cs @@ -8,156 +8,152 @@ using System.Windows.Interop; using System.Windows.Media; using Point = System.Windows.Point; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class WindowsInteropHelper { - public class WindowsInteropHelper + private const int GWL_STYLE = -16; //WPF's Message code for Title Bar's Style + private const int WS_SYSMENU = 0x80000; //WPF's Message code for System Menu + private static IntPtr _hwnd_shell; + private static IntPtr _hwnd_desktop; + + //Accessors for shell and desktop handlers + //Will set the variables once and then will return them + private static IntPtr HWND_SHELL { - private const int GWL_STYLE = -16; //WPF's Message code for Title Bar's Style - private const int WS_SYSMENU = 0x80000; //WPF's Message code for System Menu - private static IntPtr _hwnd_shell; - private static IntPtr _hwnd_desktop; - - //Accessors for shell and desktop handlers - //Will set the variables once and then will return them - private static IntPtr HWND_SHELL + get { - get - { - return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = GetShellWindow(); - } + return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = GetShellWindow(); } - private static IntPtr HWND_DESKTOP + } + private static IntPtr HWND_DESKTOP + { + get { - get - { - return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = GetDesktopWindow(); - } + return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = GetDesktopWindow(); } + } - [DllImport("user32.dll", SetLastError = true)] - internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); - - [DllImport("user32.dll")] - internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + [DllImport("user32.dll", SetLastError = true)] + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); - [DllImport("user32.dll")] - internal static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] + internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - [DllImport("user32.dll")] - internal static extern IntPtr GetDesktopWindow(); + [DllImport("user32.dll")] + internal static extern IntPtr GetForegroundWindow(); - [DllImport("user32.dll")] - internal static extern IntPtr GetShellWindow(); + [DllImport("user32.dll")] + internal static extern IntPtr GetDesktopWindow(); - [DllImport("user32.dll", SetLastError = true)] - internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc); + [DllImport("user32.dll")] + internal static extern IntPtr GetShellWindow(); - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + [DllImport("user32.dll", SetLastError = true)] + internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc); - [DllImport("user32.DLL")] - public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.DLL")] + public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); - const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass"; - const string WINDOW_CLASS_WINTAB = "Flip3D"; - const string WINDOW_CLASS_PROGMAN = "Progman"; - const string WINDOW_CLASS_WORKERW = "WorkerW"; + const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass"; + const string WINDOW_CLASS_WINTAB = "Flip3D"; + const string WINDOW_CLASS_PROGMAN = "Progman"; + const string WINDOW_CLASS_WORKERW = "WorkerW"; - public static bool IsWindowFullscreen() + public static bool IsWindowFullscreen() + { + //get current active window + IntPtr hWnd = GetForegroundWindow(); + + if (hWnd.Equals(IntPtr.Zero)) { - //get current active window - IntPtr hWnd = GetForegroundWindow(); - - if (!hWnd.Equals(IntPtr.Zero)) - { - //if current active window is NOT desktop or shell - if (!(hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))) - { - StringBuilder sb = new StringBuilder(256); - GetClassName(hWnd, sb, sb.Capacity); - string windowClass = sb.ToString(); - - //for Win+Tab (Flip3D) - if (windowClass == WINDOW_CLASS_WINTAB) - { - return false; - } - - RECT appBounds; - GetWindowRect(hWnd, out appBounds); - - //for console (ConsoleWindowClass), we have to check for negative dimensions - if (windowClass == WINDOW_CLASS_CONSOLE) - { - return appBounds.Top < 0 && appBounds.Bottom < 0; - } - - //for desktop (Progman or WorkerW, depends on the system), we have to check - if (windowClass == WINDOW_CLASS_PROGMAN || windowClass == WINDOW_CLASS_WORKERW) - { - IntPtr hWndDesktop = FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null); - hWndDesktop = FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView"); - if (!hWndDesktop.Equals(IntPtr.Zero)) - { - return false; - } - } - - Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds; - if ((appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width) - { - return true; - } - } - } - return false; } - /// - /// disable windows toolbar's control box - /// this will also disable system menu with Alt+Space hotkey - /// - public static void DisableControlBox(Window win) + //if current active window is desktop or shell, exit early + if (hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL)) { - var hwnd = new WindowInteropHelper(win).Handle; - SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); + return false; } - /// - /// Transforms pixels to Device Independent Pixels used by WPF - /// - /// current window, required to get presentation source - /// horizontal position in pixels - /// vertical position in pixels - /// point containing device independent pixels - public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY) + StringBuilder sb = new StringBuilder(256); + GetClassName(hWnd, sb, sb.Capacity); + string windowClass = sb.ToString(); + + //for Win+Tab (Flip3D) + if (windowClass == WINDOW_CLASS_WINTAB) { - Matrix matrix; - var source = PresentationSource.FromVisual(visual); - if (source != null) + return false; + } + + RECT appBounds; + GetWindowRect(hWnd, out appBounds); + + //for console (ConsoleWindowClass), we have to check for negative dimensions + if (windowClass == WINDOW_CLASS_CONSOLE) + { + return appBounds.Top < 0 && appBounds.Bottom < 0; + } + + //for desktop (Progman or WorkerW, depends on the system), we have to check + if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW) + { + IntPtr hWndDesktop = FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null); + hWndDesktop = FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView"); + if (!hWndDesktop.Equals(IntPtr.Zero)) { - matrix = source.CompositionTarget.TransformFromDevice; + return false; } - else - { - using (var src = new HwndSource(new HwndSourceParameters())) - { - matrix = src.CompositionTarget.TransformFromDevice; - } - } - return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY)); } + Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds; + return (appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width; + } - [StructLayout(LayoutKind.Sequential)] - public struct RECT + /// + /// disable windows toolbar's control box + /// this will also disable system menu with Alt+Space hotkey + /// + public static void DisableControlBox(Window win) + { + var hwnd = new WindowInteropHelper(win).Handle; + SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); + } + + /// + /// Transforms pixels to Device Independent Pixels used by WPF + /// + /// current window, required to get presentation source + /// horizontal position in pixels + /// vertical position in pixels + /// point containing device independent pixels + public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY) + { + Matrix matrix; + var source = PresentationSource.FromVisual(visual); + if (source is not null) { - public int Left; - public int Top; - public int Right; - public int Bottom; + matrix = source.CompositionTarget.TransformFromDevice; } + else + { + using var src = new HwndSource(new HwndSourceParameters()); + matrix = src.CompositionTarget.TransformFromDevice; + } + return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY)); + } + + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; } } From f7bf681ee2146c19ef25ec319c455dc44462dec0 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 11 Apr 2024 13:54:50 +0600 Subject: [PATCH 54/62] Replace `using` block with a `using` declaration in `SingleInstance.cs` --- Flow.Launcher/Helper/SingleInstance.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs index 380da1647..8e72e4266 100644 --- a/Flow.Launcher/Helper/SingleInstance.cs +++ b/Flow.Launcher/Helper/SingleInstance.cs @@ -293,10 +293,8 @@ public static class SingleInstance { try { - using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) - { - args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); - } + using TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode); + args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); File.Delete(cmdLinePath); } From cc15bd1c0bc056bc386180620a00acfe2e156e46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 03:04:29 +0000 Subject: [PATCH 55/62] Bump MemoryPack from 1.10.0 to 1.21.0 (#2641) --- .../Flow.Launcher.Infrastructure.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 574f3686a..a21c7f647 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -54,7 +54,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 9a6a68483b569966d33ef08f39a8e53a703327e4 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Mon, 15 Apr 2024 09:20:26 +0600 Subject: [PATCH 56/62] Revert changes in `SingleInstance.cs` to prevent a difficult merge --- Flow.Launcher/Helper/SingleInstance.cs | 673 +++++++++++++------------ 1 file changed, 338 insertions(+), 335 deletions(-) diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs index 8e72e4266..739fed378 100644 --- a/Flow.Launcher/Helper/SingleInstance.cs +++ b/Flow.Launcher/Helper/SingleInstance.cs @@ -12,372 +12,375 @@ using System.Windows; // http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/ // modified to allow single instace restart -namespace Flow.Launcher.Helper; - -internal enum WM +namespace Flow.Launcher.Helper { - NULL = 0x0000, - CREATE = 0x0001, - DESTROY = 0x0002, - MOVE = 0x0003, - SIZE = 0x0005, - ACTIVATE = 0x0006, - SETFOCUS = 0x0007, - KILLFOCUS = 0x0008, - ENABLE = 0x000A, - SETREDRAW = 0x000B, - SETTEXT = 0x000C, - GETTEXT = 0x000D, - GETTEXTLENGTH = 0x000E, - PAINT = 0x000F, - CLOSE = 0x0010, - QUERYENDSESSION = 0x0011, - QUIT = 0x0012, - QUERYOPEN = 0x0013, - ERASEBKGND = 0x0014, - SYSCOLORCHANGE = 0x0015, - SHOWWINDOW = 0x0018, - ACTIVATEAPP = 0x001C, - SETCURSOR = 0x0020, - MOUSEACTIVATE = 0x0021, - CHILDACTIVATE = 0x0022, - QUEUESYNC = 0x0023, - GETMINMAXINFO = 0x0024, - - WINDOWPOSCHANGING = 0x0046, - WINDOWPOSCHANGED = 0x0047, - - CONTEXTMENU = 0x007B, - STYLECHANGING = 0x007C, - STYLECHANGED = 0x007D, - DISPLAYCHANGE = 0x007E, - GETICON = 0x007F, - SETICON = 0x0080, - NCCREATE = 0x0081, - NCDESTROY = 0x0082, - NCCALCSIZE = 0x0083, - NCHITTEST = 0x0084, - NCPAINT = 0x0085, - NCACTIVATE = 0x0086, - GETDLGCODE = 0x0087, - SYNCPAINT = 0x0088, - NCMOUSEMOVE = 0x00A0, - NCLBUTTONDOWN = 0x00A1, - NCLBUTTONUP = 0x00A2, - NCLBUTTONDBLCLK = 0x00A3, - NCRBUTTONDOWN = 0x00A4, - NCRBUTTONUP = 0x00A5, - NCRBUTTONDBLCLK = 0x00A6, - NCMBUTTONDOWN = 0x00A7, - NCMBUTTONUP = 0x00A8, - NCMBUTTONDBLCLK = 0x00A9, - - SYSKEYDOWN = 0x0104, - SYSKEYUP = 0x0105, - SYSCHAR = 0x0106, - SYSDEADCHAR = 0x0107, - COMMAND = 0x0111, - SYSCOMMAND = 0x0112, - - MOUSEMOVE = 0x0200, - LBUTTONDOWN = 0x0201, - LBUTTONUP = 0x0202, - LBUTTONDBLCLK = 0x0203, - RBUTTONDOWN = 0x0204, - RBUTTONUP = 0x0205, - RBUTTONDBLCLK = 0x0206, - MBUTTONDOWN = 0x0207, - MBUTTONUP = 0x0208, - MBUTTONDBLCLK = 0x0209, - MOUSEWHEEL = 0x020A, - XBUTTONDOWN = 0x020B, - XBUTTONUP = 0x020C, - XBUTTONDBLCLK = 0x020D, - MOUSEHWHEEL = 0x020E, - - - CAPTURECHANGED = 0x0215, - - ENTERSIZEMOVE = 0x0231, - EXITSIZEMOVE = 0x0232, - - IME_SETCONTEXT = 0x0281, - IME_NOTIFY = 0x0282, - IME_CONTROL = 0x0283, - IME_COMPOSITIONFULL = 0x0284, - IME_SELECT = 0x0285, - IME_CHAR = 0x0286, - IME_REQUEST = 0x0288, - IME_KEYDOWN = 0x0290, - IME_KEYUP = 0x0291, - - NCMOUSELEAVE = 0x02A2, - - DWMCOMPOSITIONCHANGED = 0x031E, - DWMNCRENDERINGCHANGED = 0x031F, - DWMCOLORIZATIONCOLORCHANGED = 0x0320, - DWMWINDOWMAXIMIZEDCHANGE = 0x0321, - - #region Windows 7 - DWMSENDICONICTHUMBNAIL = 0x0323, - DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326, - #endregion - - USER = 0x0400, - - // This is the hard-coded message value used by WinForms for Shell_NotifyIcon. - // It's relatively safe to reuse. - TRAYMOUSEMESSAGE = 0x800, //WM_USER + 1024 - APP = 0x8000 -} - -[SuppressUnmanagedCodeSecurity] -internal static class NativeMethods -{ - /// - /// Delegate declaration that matches WndProc signatures. - /// - public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled); - - [DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)] - private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs); - - - [DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)] - private static extern IntPtr _LocalFree(IntPtr hMem); - - - public static string[] CommandLineToArgvW(string cmdLine) + internal enum WM { - IntPtr argv = IntPtr.Zero; - try - { - int numArgs = 0; + NULL = 0x0000, + CREATE = 0x0001, + DESTROY = 0x0002, + MOVE = 0x0003, + SIZE = 0x0005, + ACTIVATE = 0x0006, + SETFOCUS = 0x0007, + KILLFOCUS = 0x0008, + ENABLE = 0x000A, + SETREDRAW = 0x000B, + SETTEXT = 0x000C, + GETTEXT = 0x000D, + GETTEXTLENGTH = 0x000E, + PAINT = 0x000F, + CLOSE = 0x0010, + QUERYENDSESSION = 0x0011, + QUIT = 0x0012, + QUERYOPEN = 0x0013, + ERASEBKGND = 0x0014, + SYSCOLORCHANGE = 0x0015, + SHOWWINDOW = 0x0018, + ACTIVATEAPP = 0x001C, + SETCURSOR = 0x0020, + MOUSEACTIVATE = 0x0021, + CHILDACTIVATE = 0x0022, + QUEUESYNC = 0x0023, + GETMINMAXINFO = 0x0024, - argv = _CommandLineToArgvW(cmdLine, out numArgs); - if (argv == IntPtr.Zero) - { - throw new Win32Exception(); - } - var result = new string[numArgs]; + WINDOWPOSCHANGING = 0x0046, + WINDOWPOSCHANGED = 0x0047, - for (int i = 0; i < numArgs; i++) - { - IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr))); - result[i] = Marshal.PtrToStringUni(currArg); - } + CONTEXTMENU = 0x007B, + STYLECHANGING = 0x007C, + STYLECHANGED = 0x007D, + DISPLAYCHANGE = 0x007E, + GETICON = 0x007F, + SETICON = 0x0080, + NCCREATE = 0x0081, + NCDESTROY = 0x0082, + NCCALCSIZE = 0x0083, + NCHITTEST = 0x0084, + NCPAINT = 0x0085, + NCACTIVATE = 0x0086, + GETDLGCODE = 0x0087, + SYNCPAINT = 0x0088, + NCMOUSEMOVE = 0x00A0, + NCLBUTTONDOWN = 0x00A1, + NCLBUTTONUP = 0x00A2, + NCLBUTTONDBLCLK = 0x00A3, + NCRBUTTONDOWN = 0x00A4, + NCRBUTTONUP = 0x00A5, + NCRBUTTONDBLCLK = 0x00A6, + NCMBUTTONDOWN = 0x00A7, + NCMBUTTONUP = 0x00A8, + NCMBUTTONDBLCLK = 0x00A9, - return result; - } - finally - { + SYSKEYDOWN = 0x0104, + SYSKEYUP = 0x0105, + SYSCHAR = 0x0106, + SYSDEADCHAR = 0x0107, + COMMAND = 0x0111, + SYSCOMMAND = 0x0112, - IntPtr p = _LocalFree(argv); - // Otherwise LocalFree failed. - // Assert.AreEqual(IntPtr.Zero, p); - } + MOUSEMOVE = 0x0200, + LBUTTONDOWN = 0x0201, + LBUTTONUP = 0x0202, + LBUTTONDBLCLK = 0x0203, + RBUTTONDOWN = 0x0204, + RBUTTONUP = 0x0205, + RBUTTONDBLCLK = 0x0206, + MBUTTONDOWN = 0x0207, + MBUTTONUP = 0x0208, + MBUTTONDBLCLK = 0x0209, + MOUSEWHEEL = 0x020A, + XBUTTONDOWN = 0x020B, + XBUTTONUP = 0x020C, + XBUTTONDBLCLK = 0x020D, + MOUSEHWHEEL = 0x020E, + + + CAPTURECHANGED = 0x0215, + + ENTERSIZEMOVE = 0x0231, + EXITSIZEMOVE = 0x0232, + + IME_SETCONTEXT = 0x0281, + IME_NOTIFY = 0x0282, + IME_CONTROL = 0x0283, + IME_COMPOSITIONFULL = 0x0284, + IME_SELECT = 0x0285, + IME_CHAR = 0x0286, + IME_REQUEST = 0x0288, + IME_KEYDOWN = 0x0290, + IME_KEYUP = 0x0291, + + NCMOUSELEAVE = 0x02A2, + + DWMCOMPOSITIONCHANGED = 0x031E, + DWMNCRENDERINGCHANGED = 0x031F, + DWMCOLORIZATIONCOLORCHANGED = 0x0320, + DWMWINDOWMAXIMIZEDCHANGE = 0x0321, + + #region Windows 7 + DWMSENDICONICTHUMBNAIL = 0x0323, + DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326, + #endregion + + USER = 0x0400, + + // This is the hard-coded message value used by WinForms for Shell_NotifyIcon. + // It's relatively safe to reuse. + TRAYMOUSEMESSAGE = 0x800, //WM_USER + 1024 + APP = 0x8000 } -} - -public interface ISingleInstanceApp -{ - void OnSecondAppStarted(); -} - -/// -/// This class checks to make sure that only one instance of -/// this application is running at a time. -/// -/// -/// Note: this class should be used with some caution, because it does no -/// security checking. For example, if one instance of an app that uses this class -/// is running as Administrator, any other instance, even if it is not -/// running as Administrator, can activate it with command line arguments. -/// For most apps, this will not be much of an issue. -/// -public static class SingleInstance - where TApplication: Application , ISingleInstanceApp - -{ - #region Private Fields - - /// - /// String delimiter used in channel names. - /// - private const string Delimiter = ":"; - - /// - /// Suffix to the channel name. - /// - private const string ChannelNameSuffix = "SingeInstanceIPCChannel"; - - /// - /// Application mutex. - /// - internal static Mutex singleInstanceMutex; - - #endregion - - #region Public Properties - - #endregion - - #region Public Methods - - /// - /// Checks if the instance of the application attempting to start is the first instance. - /// If not, activates the first instance. - /// - /// True if this is the first instance of the application. - public static bool InitializeAsFirstInstance( string uniqueName ) + [SuppressUnmanagedCodeSecurity] + internal static class NativeMethods { - // Build unique application Id and the IPC channel name. - string applicationIdentifier = uniqueName + Environment.UserName; + /// + /// Delegate declaration that matches WndProc signatures. + /// + public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled); - string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix); + [DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)] + private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs); - // Create mutex based on unique application Id to check if this is the first instance of the application. - bool firstInstance; - singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance); - if (firstInstance) + + [DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)] + private static extern IntPtr _LocalFree(IntPtr hMem); + + + public static string[] CommandLineToArgvW(string cmdLine) { - _ = CreateRemoteService(channelName); - return true; - } - else - { - _ = SignalFirstInstance(channelName); - return false; - } - } - - /// - /// Cleans up single-instance code, clearing shared resources, mutexes, etc. - /// - public static void Cleanup() - { - singleInstanceMutex?.ReleaseMutex(); - } - - #endregion - - #region Private Methods - - /// - /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved. - /// - /// List of command line arg strings. - private static IList GetCommandLineArgs( string uniqueApplicationName ) - { - string[] args = null; - - try - { - // The application was not clickonce deployed, get args from standard API's - args = Environment.GetCommandLineArgs(); - } - catch (NotSupportedException) - { - - // The application was clickonce deployed - // Clickonce deployed apps cannot recieve traditional commandline arguments - // As a workaround commandline arguments can be written to a shared location before - // the app is launched and the app can obtain its commandline arguments from the - // shared location - string appFolderPath = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), uniqueApplicationName); - - string cmdLinePath = Path.Combine(appFolderPath, "cmdline.txt"); - if (File.Exists(cmdLinePath)) + IntPtr argv = IntPtr.Zero; + try { - try + int numArgs = 0; + + argv = _CommandLineToArgvW(cmdLine, out numArgs); + if (argv == IntPtr.Zero) { - using TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode); - args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); - - File.Delete(cmdLinePath); + throw new Win32Exception(); } - catch (IOException) + var result = new string[numArgs]; + + for (int i = 0; i < numArgs; i++) { + IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr))); + result[i] = Marshal.PtrToStringUni(currArg); + } + + return result; + } + finally + { + + IntPtr p = _LocalFree(argv); + // Otherwise LocalFree failed. + // Assert.AreEqual(IntPtr.Zero, p); + } + } + + } + + public interface ISingleInstanceApp + { + void OnSecondAppStarted(); + } + + /// + /// This class checks to make sure that only one instance of + /// this application is running at a time. + /// + /// + /// Note: this class should be used with some caution, because it does no + /// security checking. For example, if one instance of an app that uses this class + /// is running as Administrator, any other instance, even if it is not + /// running as Administrator, can activate it with command line arguments. + /// For most apps, this will not be much of an issue. + /// + public static class SingleInstance + where TApplication: Application , ISingleInstanceApp + + { + #region Private Fields + + /// + /// String delimiter used in channel names. + /// + private const string Delimiter = ":"; + + /// + /// Suffix to the channel name. + /// + private const string ChannelNameSuffix = "SingeInstanceIPCChannel"; + + /// + /// Application mutex. + /// + internal static Mutex singleInstanceMutex; + + #endregion + + #region Public Properties + + #endregion + + #region Public Methods + + /// + /// Checks if the instance of the application attempting to start is the first instance. + /// If not, activates the first instance. + /// + /// True if this is the first instance of the application. + public static bool InitializeAsFirstInstance( string uniqueName ) + { + // Build unique application Id and the IPC channel name. + string applicationIdentifier = uniqueName + Environment.UserName; + + string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix); + + // Create mutex based on unique application Id to check if this is the first instance of the application. + bool firstInstance; + singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance); + if (firstInstance) + { + _ = CreateRemoteService(channelName); + return true; + } + else + { + _ = SignalFirstInstance(channelName); + return false; + } + } + + /// + /// Cleans up single-instance code, clearing shared resources, mutexes, etc. + /// + public static void Cleanup() + { + singleInstanceMutex?.ReleaseMutex(); + } + + #endregion + + #region Private Methods + + /// + /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved. + /// + /// List of command line arg strings. + private static IList GetCommandLineArgs( string uniqueApplicationName ) + { + string[] args = null; + + try + { + // The application was not clickonce deployed, get args from standard API's + args = Environment.GetCommandLineArgs(); + } + catch (NotSupportedException) + { + + // The application was clickonce deployed + // Clickonce deployed apps cannot recieve traditional commandline arguments + // As a workaround commandline arguments can be written to a shared location before + // the app is launched and the app can obtain its commandline arguments from the + // shared location + string appFolderPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), uniqueApplicationName); + + string cmdLinePath = Path.Combine(appFolderPath, "cmdline.txt"); + if (File.Exists(cmdLinePath)) + { + try + { + using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) + { + args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); + } + + File.Delete(cmdLinePath); + } + catch (IOException) + { + } } } - } - if (args == null) - { + if (args == null) + { args = new string[] { }; + } + + return new List(args); } - return new List(args); - } - - /// - /// Creates a remote server pipe for communication. - /// Once receives signal from client, will activate first instance. - /// - /// Application's IPC channel name. - private static async Task CreateRemoteService(string channelName) - { - using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In)) + /// + /// Creates a remote server pipe for communication. + /// Once receives signal from client, will activate first instance. + /// + /// Application's IPC channel name. + private static async Task CreateRemoteService(string channelName) { - while(true) + using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In)) { - // Wait for connection to the pipe - await pipeServer.WaitForConnectionAsync(); - if (Application.Current != null) + while(true) { - // Do an asynchronous call to ActivateFirstInstance function - Application.Current.Dispatcher.Invoke(ActivateFirstInstance); + // Wait for connection to the pipe + await pipeServer.WaitForConnectionAsync(); + if (Application.Current != null) + { + // Do an asynchronous call to ActivateFirstInstance function + Application.Current.Dispatcher.Invoke(ActivateFirstInstance); + } + // Disconect client + pipeServer.Disconnect(); } - // Disconect client - pipeServer.Disconnect(); } } - } - /// - /// Creates a client pipe and sends a signal to server to launch first instance - /// - /// Application's IPC channel name. - /// - /// Command line arguments for the second instance, passed to the first instance to take appropriate action. - /// - private static async Task SignalFirstInstance(string channelName) - { - // Create a client pipe connected to server - using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out)) + /// + /// Creates a client pipe and sends a signal to server to launch first instance + /// + /// Application's IPC channel name. + /// + /// Command line arguments for the second instance, passed to the first instance to take appropriate action. + /// + private static async Task SignalFirstInstance(string channelName) { - // Connect to the available pipe - await pipeClient.ConnectAsync(0); - } - } - - /// - /// Callback for activating first instance of the application. - /// - /// Callback argument. - /// Always null. - private static object ActivateFirstInstanceCallback(object o) - { - ActivateFirstInstance(); - return null; - } - - /// - /// Activates the first instance of the application with arguments from a second instance. - /// - /// List of arguments to supply the first instance of the application. - private static void ActivateFirstInstance() - { - // Set main window state and process command line args - if (Application.Current == null) - { - return; + // Create a client pipe connected to server + using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out)) + { + // Connect to the available pipe + await pipeClient.ConnectAsync(0); + } } - ((TApplication)Application.Current).OnSecondAppStarted(); - } + /// + /// Callback for activating first instance of the application. + /// + /// Callback argument. + /// Always null. + private static object ActivateFirstInstanceCallback(object o) + { + ActivateFirstInstance(); + return null; + } - #endregion + /// + /// Activates the first instance of the application with arguments from a second instance. + /// + /// List of arguments to supply the first instance of the application. + private static void ActivateFirstInstance() + { + // Set main window state and process command line args + if (Application.Current == null) + { + return; + } + + ((TApplication)Application.Current).OnSecondAppStarted(); + } + + #endregion + } } From 009de4f67bc7d36504cfc1a5482ccbb21e5b700a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:03:33 +0000 Subject: [PATCH 57/62] Bump MemoryPack from 1.21.0 to 1.21.1 (#2651) --- .../Flow.Launcher.Infrastructure.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index a21c7f647..831091732 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -54,7 +54,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 6e4fffe29ec3013de8b2b8395a28c541eba223fe Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:36:19 -0500 Subject: [PATCH 58/62] Bump Version 1.18.0 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b84230c43..e0f3fc104 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.17.2.{build}' +version: '1.18.0.{build}' init: - ps: | From 56bc8f75f2f4f7cc8b8edc0261a08c64d78147a4 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:36:57 -0500 Subject: [PATCH 59/62] Update Flow.Launcher.Plugin.csproj --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index cc89afbc2..7b0880b67 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -14,10 +14,10 @@ - 4.2.0 - 4.2.0 - 4.2.0 - 4.2.0 + 4.3.0 + 4.3.0 + 4.3.0 + 4.3.0 Flow.Launcher.Plugin Flow-Launcher MIT From 3b66961e810dcc0394618ffc7e06a5242f96d707 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:38:12 -0500 Subject: [PATCH 60/62] Update plugin.json --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index 0778b7ae5..c4a24a57b 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -4,7 +4,7 @@ "Name": "Browser Bookmarks", "Description": "Search your browser bookmarks", "Author": "qianlifeng, Ioannis G.", - "Version": "3.1.5", + "Version": "3.2.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll", From f52e5db39bdf6638a9f7aa3d09fe7cb25f0cc3d2 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 18 Apr 2024 20:08:54 +1000 Subject: [PATCH 61/62] bump plugin versions --- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Shell/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Sys/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index fac4621b0..60c1b11b5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -10,7 +10,7 @@ "Name": "Explorer", "Description": "Find and manage files and folders via Windows Search or Everything", "Author": "Jeremy Wu", - "Version": "3.1.5", + "Version": "3.1.6", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 4e475f171..438a407e2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "3.1.0", + "Version": "3.1.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index 7f6b5f938..9ed7edbd9 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "3.2.0", + "Version": "3.2.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json index 6c2b2a184..e06c22c54 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json @@ -4,7 +4,7 @@ "Name": "Shell", "Description": "Provide executing commands from Flow Launcher", "Author": "qianlifeng", - "Version": "3.2.0", + "Version": "3.2.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Shell.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json index 5276690e3..f7dea266a 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json @@ -4,7 +4,7 @@ "Name": "System Commands", "Description": "Provide System related commands. e.g. shutdown,lock, setting etc.", "Author": "qianlifeng", - "Version": "3.1.1", + "Version": "3.1.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll", diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json b/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json index 5e82a43c0..dc8bfa22c 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json @@ -4,7 +4,7 @@ "Description": "Search settings inside Control Panel and Settings App", "Name": "Windows Settings", "Author": "TobiasSekan", - "Version": "4.0.6", + "Version": "4.0.7", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WindowsSettings.dll", From a9b41879697681111133edcd6377f28d4c476796 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 18 Apr 2024 21:19:57 +1000 Subject: [PATCH 62/62] New Crowdin updates (#2624) * New translations en.xaml (Dutch) [ci skip] * New translations en.xaml (Slovak) [ci skip] * New translations en.xaml (French) [ci skip] * New translations en.xaml (French) [ci skip] * New translations en.xaml (Arabic) [ci skip] * New translations en.xaml (Arabic) [ci skip] * New translations en.xaml (Czech) [ci skip] * New translations en.xaml (Czech) [ci skip] * New translations en.xaml (Danish) [ci skip] * New translations en.xaml (Danish) [ci skip] * New translations en.xaml (German) [ci skip] * New translations en.xaml (German) [ci skip] * New translations en.xaml (Italian) [ci skip] * New translations en.xaml (Italian) [ci skip] * New translations en.xaml (Japanese) [ci skip] * New translations en.xaml (Japanese) [ci skip] * New translations en.xaml (Korean) [ci skip] * New translations en.xaml (Korean) [ci skip] * New translations en.xaml (Dutch) [ci skip] * New translations en.xaml (Polish) [ci skip] * New translations en.xaml (Polish) [ci skip] * New translations en.xaml (Portuguese) [ci skip] * New translations en.xaml (Portuguese) [ci skip] * New translations en.xaml (Russian) [ci skip] * New translations en.xaml (Russian) [ci skip] * New translations en.xaml (Slovak) [ci skip] * New translations en.xaml (Turkish) [ci skip] * New translations en.xaml (Turkish) [ci skip] * New translations en.xaml (Ukrainian) [ci skip] * New translations en.xaml (Ukrainian) [ci skip] * New translations en.xaml (Chinese Simplified) [ci skip] * New translations en.xaml (Chinese Simplified) [ci skip] * New translations en.xaml (Chinese Traditional) [ci skip] * New translations en.xaml (Chinese Traditional) [ci skip] * New translations en.xaml (Portuguese, Brazilian) [ci skip] * New translations en.xaml (Portuguese, Brazilian) [ci skip] * New translations en.xaml (Norwegian Bokmal) [ci skip] * New translations en.xaml (Norwegian Bokmal) [ci skip] * New translations en.xaml (Serbian (Latin)) [ci skip] * New translations en.xaml (Serbian (Latin)) [ci skip] * New translations en.xaml (Spanish (Modern)) [ci skip] * New translations en.xaml (Spanish (Modern)) [ci skip] * New translations en.xaml (Spanish, Latin America) [ci skip] * New translations en.xaml (Spanish, Latin America) [ci skip] * New translations en.xaml (Slovak) [ci skip] * New translations en.xaml (Portuguese) [ci skip] * New translations en.xaml (Portuguese) [ci skip] --- Flow.Launcher/Languages/ar.xaml | 2 ++ Flow.Launcher/Languages/cs.xaml | 2 ++ Flow.Launcher/Languages/da.xaml | 2 ++ Flow.Launcher/Languages/de.xaml | 2 ++ Flow.Launcher/Languages/es-419.xaml | 2 ++ Flow.Launcher/Languages/es.xaml | 16 +++++++------ Flow.Launcher/Languages/fr.xaml | 2 ++ Flow.Launcher/Languages/it.xaml | 2 ++ Flow.Launcher/Languages/ja.xaml | 2 ++ Flow.Launcher/Languages/ko.xaml | 2 ++ Flow.Launcher/Languages/nb.xaml | 2 ++ Flow.Launcher/Languages/nl.xaml | 2 ++ Flow.Launcher/Languages/pl.xaml | 2 ++ Flow.Launcher/Languages/pt-br.xaml | 2 ++ Flow.Launcher/Languages/pt-pt.xaml | 2 ++ Flow.Launcher/Languages/ru.xaml | 2 ++ Flow.Launcher/Languages/sk.xaml | 2 ++ Flow.Launcher/Languages/sr.xaml | 2 ++ Flow.Launcher/Languages/tr.xaml | 2 ++ Flow.Launcher/Languages/uk-UA.xaml | 2 ++ Flow.Launcher/Languages/zh-cn.xaml | 2 ++ Flow.Launcher/Languages/zh-tw.xaml | 2 ++ .../Languages/zh-cn.xaml | 8 +++---- .../Languages/ar.xaml | 1 + .../Languages/cs.xaml | 1 + .../Languages/da.xaml | 1 + .../Languages/de.xaml | 1 + .../Languages/es-419.xaml | 1 + .../Languages/es.xaml | 1 + .../Languages/fr.xaml | 1 + .../Languages/it.xaml | 1 + .../Languages/ja.xaml | 1 + .../Languages/ko.xaml | 1 + .../Languages/nb.xaml | 1 + .../Languages/nl.xaml | 1 + .../Languages/pl.xaml | 1 + .../Languages/pt-br.xaml | 1 + .../Languages/pt-pt.xaml | 1 + .../Languages/ru.xaml | 1 + .../Languages/sk.xaml | 1 + .../Languages/sr.xaml | 1 + .../Languages/tr.xaml | 1 + .../Languages/uk-UA.xaml | 1 + .../Languages/zh-cn.xaml | 1 + .../Languages/zh-tw.xaml | 1 + .../Languages/zh-cn.xaml | 2 +- .../Languages/ar.xaml | 2 +- .../Languages/cs.xaml | 2 +- .../Languages/da.xaml | 2 +- .../Languages/es-419.xaml | 2 +- .../Languages/es.xaml | 2 +- .../Languages/it.xaml | 2 +- .../Languages/ja.xaml | 2 +- .../Languages/ko.xaml | 2 +- .../Languages/nb.xaml | 2 +- .../Languages/nl.xaml | 2 +- .../Languages/pl.xaml | 2 +- .../Languages/pt-br.xaml | 2 +- .../Languages/ru.xaml | 2 +- .../Languages/sk.xaml | 2 +- .../Languages/sr.xaml | 2 +- .../Languages/tr.xaml | 2 +- .../Languages/uk-UA.xaml | 2 +- .../Languages/zh-cn.xaml | 24 +++++++++---------- .../Languages/zh-tw.xaml | 2 +- .../Properties/Resources.pt-PT.resx | 6 ++--- 66 files changed, 111 insertions(+), 45 deletions(-) diff --git a/Flow.Launcher/Languages/ar.xaml b/Flow.Launcher/Languages/ar.xaml index fbb52f9ee..7f5c067f5 100644 --- a/Flow.Launcher/Languages/ar.xaml +++ b/Flow.Launcher/Languages/ar.xaml @@ -154,6 +154,8 @@ Dark Sound Effect Play a small sound when the search window opens + Sound Effect Volume + Adjust the volume of the sound effect Animation Use Animation in UI Animation Speed diff --git a/Flow.Launcher/Languages/cs.xaml b/Flow.Launcher/Languages/cs.xaml index 90b24229b..c5ac941bc 100644 --- a/Flow.Launcher/Languages/cs.xaml +++ b/Flow.Launcher/Languages/cs.xaml @@ -154,6 +154,8 @@ Tmavý Zvukový efekt Přehrát krátký zvuk při otevření okna vyhledávání + Sound Effect Volume + Adjust the volume of the sound effect Animace Použít animaci v UI Rychlost animace diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index 364b8a2da..830f49798 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -154,6 +154,8 @@ Dark Sound Effect Play a small sound when the search window opens + Sound Effect Volume + Adjust the volume of the sound effect Animation Use Animation in UI Animation Speed diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index a360140d6..189ac678e 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -154,6 +154,8 @@ Dunkel Soundeffekt Ton abspielen, wenn das Suchfenster geöffnet wird + Sound Effect Volume + Adjust the volume of the sound effect Animation Animationen in der Oberfläche verwenden Animation Speed diff --git a/Flow.Launcher/Languages/es-419.xaml b/Flow.Launcher/Languages/es-419.xaml index 8417f7c7e..9fc54c373 100644 --- a/Flow.Launcher/Languages/es-419.xaml +++ b/Flow.Launcher/Languages/es-419.xaml @@ -154,6 +154,8 @@ Oscuro Efectos de Sonido Reproducir un sonido al abrir la ventana de búsqueda + Sound Effect Volume + Adjust the volume of the sound effect Animación Usar Animación en la Interfaz Animation Speed diff --git a/Flow.Launcher/Languages/es.xaml b/Flow.Launcher/Languages/es.xaml index bcfef1f04..5153b79f7 100644 --- a/Flow.Launcher/Languages/es.xaml +++ b/Flow.Launcher/Languages/es.xaml @@ -55,7 +55,7 @@ Seleccionar la última consulta Limpiar la última consulta Número máximo de resultados mostrados - También puede ajustarlo rápidamente usando CTRL+Más y CTRL+Menos. + También puede ajustarse rápidamente usando Ctrl+Más y Ctrl+Menos. Ignorar atajos de teclado en modo pantalla completa No permite activar Flow Launcher con aplicaciones a pantalla completa (Recomendado para juegos). Administrador de archivos predeterminado @@ -73,13 +73,13 @@ Ocultar Flow Launcher al inicio Ocultar icono en la bandeja del sistema Cuando el icono está oculto en la bandeja del sistema, se puede abrir el menú de configuración haciendo clic con el botón derecho en la ventana de búsqueda. - Precisión en la búsqueda de consultas + Precisión de búsqueda en consultas Cambia la puntuación mínima requerida para la coincidencia de los resultados. Buscar con Pinyin Permite utilizar Pinyin para la búsqueda. Pinyin es el sistema estándar de ortografía romanizado para traducir chino. Mostrar siempre vista previa Muestra siempre el panel de vista previa al iniciar Flow. Pulse {0} para mostrar/ocultar la vista previa. - El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque activado + El efecto de sombra no está permitido si el tema actual tiene activado el efecto de desenfoque Buscar complemento @@ -154,8 +154,10 @@ Oscuro Efecto de sonido Reproduce un pequeño sonido cuando se abre el cuadro de búsqueda + Volumen del efecto de sonido + Ajusta el volumen del efecto de sonido Animación - Usar animación en la Interfaz de Usuario + Usa animación en la Interfaz de Usuario Velocidad de animación Velocidad de animación de la interfaz de usuario Lenta @@ -170,7 +172,7 @@ Atajos de teclado Atajo de teclado de Flow Launcher Introduzca el atajo de teclado para mostrar/ocultar Flow Launcher. - Atajo de teclado para vista previa + Acceso directo para vista previa Introduzca el acceso directo para mostrar/ocultar la vista previa en la ventana de búsqueda. Tecla modificadora para abrir resultado Seleccione una tecla modificadora para abrir el resultado seleccionado con el teclado. @@ -194,7 +196,7 @@ Efecto de sombra de la ventana de consultas El efecto de sombra hace un uso sustancial de la GPU. No se recomienda para ordenadores de rendimiento limitado. Tamaño del ancho de la ventana - También puede ajustarlo rápidamente usando Ctrl+[y Ctrl+]. + También puede ajustarse rápidamente usando Ctrl+[ y Ctrl+]. Usar iconos Segoe Fluent Usa iconos Segoe Fluent para los resultados de la consulta cuando sean compatibles Pulsar Tecla @@ -296,7 +298,7 @@ Si añade un prefijo "@" al introducir un acceso directo, éste coinci El acceso directo y/o su expansión están vacíos. - Atajo de teclado no disponible + No disponible Versión diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index c33cefbfb..82b93b1e7 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -154,6 +154,8 @@ Sombre Effet Sonore Jouer un petit son lorsque la fenêtre de recherche s'ouvre + Volume de l'effet sonore + Ajuster le volume de l'effet sonore Animation Utiliser l'animation dans l'interface Vitesse d'animation diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index 2b5157962..a9a6e4fee 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -154,6 +154,8 @@ Scuro Effetto sonoro Riproduce un piccolo suono all'apertura della finestra di ricerca + Sound Effect Volume + Adjust the volume of the sound effect Animazione Usa l'animazione nell'interfaccia utente Velocità di animazione diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index e846613e8..f4859c806 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -154,6 +154,8 @@ Dark Sound Effect Play a small sound when the search window opens + Sound Effect Volume + Adjust the volume of the sound effect Animation Use Animation in UI Animation Speed diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index 612836bd5..bf76718e8 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -154,6 +154,8 @@ 어둡게 소리 효과 검색창을 열 때 작은 소리를 재생합니다. + Sound Effect Volume + Adjust the volume of the sound effect 애니메이션 일부 UI에 애니메이션을 사용합니다. Animation Speed diff --git a/Flow.Launcher/Languages/nb.xaml b/Flow.Launcher/Languages/nb.xaml index fbb52f9ee..7f5c067f5 100644 --- a/Flow.Launcher/Languages/nb.xaml +++ b/Flow.Launcher/Languages/nb.xaml @@ -154,6 +154,8 @@ Dark Sound Effect Play a small sound when the search window opens + Sound Effect Volume + Adjust the volume of the sound effect Animation Use Animation in UI Animation Speed diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index 5b28c5712..66383a90c 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -154,6 +154,8 @@ Donker Geluidseffect Een klein geluid afspelen wanneer het zoekvenster wordt geopend + Sound Effect Volume + Adjust the volume of the sound effect Animatie Animatie gebruiken in UI Animation Speed diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index 401e202c3..8ac419ab0 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -154,6 +154,8 @@ Ciemny Efekty dźwiękowe Odtwarzaj krótki dźwięk po otwarciu okna wyszukiwania + Sound Effect Volume + Adjust the volume of the sound effect Animacja Użyj animacji w interfejsie użytkownika Szybkość animacji diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index 23a669536..e2ddd0524 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -154,6 +154,8 @@ Escuro Efeito Sonoro Reproduzir um pequeno som ao abrir a janela de pesquisa + Sound Effect Volume + Adjust the volume of the sound effect Animação Utilizar Animação na Interface Velocidade de Animação diff --git a/Flow.Launcher/Languages/pt-pt.xaml b/Flow.Launcher/Languages/pt-pt.xaml index 5edd8a37d..ce04e907a 100644 --- a/Flow.Launcher/Languages/pt-pt.xaml +++ b/Flow.Launcher/Languages/pt-pt.xaml @@ -154,6 +154,8 @@ Escuro Efeitos sonoros Reproduzir um som ao abrir a janela de pesquisa + Volume dos efeitos sonoros + Ajustar volume dos efeitos sonoros Animação Utilizar animações na aplicação Velocidade da animação diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 88f6d060d..cf1e88c44 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -154,6 +154,8 @@ Тёмная Звуковой эффект Воспроизведение небольшого звука при открытии окна поиска + Sound Effect Volume + Adjust the volume of the sound effect Анимация Использование анимации в меню Скорость анимации diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index 252bfb81d..974895bcb 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -154,6 +154,8 @@ Tmavá Zvukový efekt Po otvorení okna vyhľadávania prehrať krátky zvuk + Hlasitosť zvukového efektu + Upraviť hlasitosť zvukového efektu Animácia Animovať používateľské rozhranie Rýchlosť animácie diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index 0c3c0f5a7..973ee60cc 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -154,6 +154,8 @@ Dark Sound Effect Play a small sound when the search window opens + Sound Effect Volume + Adjust the volume of the sound effect Animation Use Animation in UI Animation Speed diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index 8d5c595a2..16564c112 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -154,6 +154,8 @@ Koyu Ses Efekti Arama penceresi açıldığında küçük bir ses oynat + Sound Effect Volume + Adjust the volume of the sound effect Animasyon Arayüzde Animasyon Kullan Animation Speed diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index 2adbf5d2f..46ae5d2c0 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -154,6 +154,8 @@ Темна Звуковий ефект Відтворювати невеликий звук при відкритті вікна пошуку + Гучність звукового ефекту + Налаштуйте гучність звукового ефекту Анімація Використовувати анімацію в інтерфейсі Швидкість анімації diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index f3d2609b1..b2d9eeaa5 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -154,6 +154,8 @@ 深色 音效 启用激活音效 + 音效音量 + 调整音效音量 动画 启用动画 动画速度 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index 5cd0591f4..4f5db8194 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -154,6 +154,8 @@ 暗色系 音效 搜尋窗口打開時播放音效 + Sound Effect Volume + Adjust the volume of the sound effect 動畫 使用介面動畫 Animation Speed diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml index fd6b09159..749c7c3ac 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/zh-cn.xaml @@ -30,9 +30,9 @@ 请检查您是否可以连接到 github.com。这个错误意味着您可能无法安装或更新插件。 更新所有插件 您想要更新所有插件吗? - Would you like to update {0} plugins?{1}Flow Launcher will restart after updating all plugins. - Would you like to update {0} plugins? - {0} plugins successfully updated. Restarting Flow, please wait... + 您想要更新 {0} 插件吗?{1}更新所有插件后 Flow Launcher 将重启。 + 您想要更新 {0} 插件吗? + 插件 {0} 更新成功。正在重新启动 Flow Launcher,请稍候..…… 插件{0}更新成功。正在重新启动 Flow Launcher,请稍候... 从未知源安装 您正在从未知源安装此插件,它可能包含潜在风险!{0}{0}请确保您了解来源以及安全性。{0}{0}您想要继续吗?{0}{0}(您可以通过设置关闭此警告) @@ -40,7 +40,7 @@ 成功安装插件{0}。请重新启动 Flow Launcher。 成功卸载插件{0}。请重新启动 Flow Launcher。 成功更新插件{0}。请重新启动 Flow Launcher。 - {0} plugins successfully updated. Please restart Flow. + 插件 {0} 更新成功。请重新启动 Flow Launcher。 插件 {0} 已被修改。请在进行任何进一步更改之前重新启动Flow。 diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml index e62854305..dda427996 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml index 7be93ad14..9a703d3de 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml @@ -71,6 +71,7 @@ Spustit jako správce Otevřít umístění složky Zakázat zobrazování tohoto programu + Open target folder Program  Vyhledávání programů ve Flow Launcheru diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml index 905f7e787..676eb76cb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml index 5ab3eb9ec..293dbc9c8 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml @@ -71,6 +71,7 @@ Als Administrator ausführen Enthaltenden Ordner öffnen Disable this program from displaying + Open target folder Programm Suche Programme mit Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml index 4a18143bf..375dcd041 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml index 4955c4ea2..d4eccc8ac 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml @@ -71,6 +71,7 @@ Ejecutar como administrador Abrir carpeta contenedora Desactivar la visualización de este programa + Abrir carpeta de destino Programa Busca programas en Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml index 91f5e3ee5..d8b3dc5dd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml @@ -71,6 +71,7 @@ Exécuter en tant qu'administrateur Ouvrir le répertoire Désactiver l'affichage de ce programme + Ouvrir le répertoire cible Programme Rechercher des programmes dans Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml index d13f926d6..3a4d0e238 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml @@ -71,6 +71,7 @@ Run As Administrator Apri percorso file Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml index 863735769..6c35d7df1 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml index 9741a7fe2..2857194c7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml @@ -71,6 +71,7 @@ 관리자 권한으로 실행 포함된 폴더 열기 이 프로그램 표시 비활성화 + Open target folder 프로그램 Flow Launcher에서 프로그램을 검색합니다 diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml index e62854305..dda427996 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml index 3c3b20afd..972cbe8d6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml index c46a92006..d651fb0b9 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml @@ -71,6 +71,7 @@ Uruchom jako administrator Open containing folder Disable this program from displaying + Open target folder Programy Szukaj i uruchamiaj programy z poziomu Flow Launchera diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml index b943ffe97..f1cb7d7a2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml index 669fe3182..0832c2150 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml @@ -71,6 +71,7 @@ Executar como administrador Abrir pasta de destino Desativar exibição deste programa + Abrir pasta de destino Programas Pesquisa de programas com o Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml index 9fdb70d4f..ebc30e161 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml @@ -71,6 +71,7 @@ Запустить от имени администратора Открыть содержащую папку Отключить отображение этой программы + Open target folder Программа Поиск программ в Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml index ee62a823e..a669d256b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml @@ -71,6 +71,7 @@ Spustiť ako správca Otvoriť umiestnenie priečinka Zakázať zobrazovanie tohto programu + Otvoriť cieľový pričinok Program Vyhľadávanie programov vo Flow Launcheri diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml index f77bce039..6d995e1ab 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml @@ -71,6 +71,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml index 3d1f087f2..675c9f132 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml @@ -71,6 +71,7 @@ Yönetici Olarak Çalıştır Open containing folder Disable this program from displaying + Open target folder Program Programları Flow Launcher'tan arayın diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml index f4a9926e8..344695d1e 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml @@ -71,6 +71,7 @@ Запустити від імені адміністратора Відкрити папку Вимкнути відображення цієї програми + Відкрити цільову папку Програма Пошук програм у Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml index 3a6fbda5d..324bd3057 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml @@ -71,6 +71,7 @@ 以管理员身份运行 打开文件所在文件夹 禁止显示该程序 + Open target folder 程序 在 Flow Launcher 中搜索程序 diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml index d27b5f78f..7c72b1aa3 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml @@ -71,6 +71,7 @@ 以系統管理員身分執行 開啟檔案位置 Disable this program from displaying + Open target folder 程式 在 Flow Launcher 中搜尋程式 diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml index ace554327..2db287e39 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/zh-cn.xaml @@ -2,7 +2,7 @@ 替换 Win+R - Close Command Prompt after pressing any key + 按下任意键后关闭命令提示符 按下任意键以关闭此窗口... 执行后不关闭命令窗口 始终以管理员身份运行 diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml index 41e005894..ddf8a4d71 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ar.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Exit Save Settings - Restart Flow Launcher" + Restart Flow Launcher Settings Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml index 3e053b39f..b377ad3d2 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/cs.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Ukončit Save Settings - Restart Flow Launcher" + Restartovat Flow Launcher Nastavení Znovu načíst data pluginů Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml index 8c5e4d0ff..30beff7b5 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/da.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Afslut Save Settings - Restart Flow Launcher" + Restart Flow Launcher Indstillinger Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml index 05a5458e1..1275f8b74 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es-419.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Salir Save Settings - Restart Flow Launcher" + Restart Flow Launcher Ajustes Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml index f95e44dcf..c457b160c 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/es.xaml @@ -17,7 +17,7 @@ Abrir papelera de reciclaje Salir Guardar configuración - Reiniciar Flow Launcher" + Reinicia Flow Launcher Configuración Recargar datos del complemento Buscar actualizaciones diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml index a70994188..62d455c07 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/it.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Esci Save Settings - Restart Flow Launcher" + Riavvia Flow Launcher Impostazioni Ricarica i dati del plugin Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml index 9080cf314..bb00a0df6 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ja.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Save Settings - Restart Flow Launcher" + Flow Launcherを再起動する プラグインデータのリロード Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml index 5389cbb38..e2962ff34 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ko.xaml @@ -17,7 +17,7 @@ Open Recycle Bin 종료 Save Settings - Restart Flow Launcher" + Flow Launcher 재시작 설정 플러그인 데이터 새로고 Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml index 41e005894..ddf8a4d71 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nb.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Exit Save Settings - Restart Flow Launcher" + Restart Flow Launcher Settings Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml index a71e28e98..00201fa0e 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Afsluiten Save Settings - Restart Flow Launcher" + Flow Launcher herstarten Instellingen Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml index 4549f193e..4c7592411 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pl.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Wyjdź Save Settings - Restart Flow Launcher" + Uruchom ponownie Flow Launchera Ustawienia Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml index b80ddf621..a51c751df 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/pt-br.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Sair Save Settings - Restart Flow Launcher" + Reiniciar Flow Launcher Configurações Recarregar Dados de Plugin Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml index 927ccb0b6..3d3bcfc87 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/ru.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Выйти Save Settings - Restart Flow Launcher" + Restart Flow Launcher Настройки Перезагрузить данные плагинов Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml index 82d8c2fe4..c5cee316f 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sk.xaml @@ -17,7 +17,7 @@ Otvoriť kôš Ukončiť Uložiť nastavenia - Reštartovať Flow Launcher" + Reštartovať Flow Launcher Nastavenia Znova načítať údaje pluginov Skontrolovať aktualizácie diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml index fa6b54b81..a984954f9 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/sr.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Izlaz Save Settings - Restart Flow Launcher" + Restart Flow Launcher Podešavanja Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml index 9670d8297..35850cb26 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/tr.xaml @@ -17,7 +17,7 @@ Open Recycle Bin Çıkı Save Settings - Restart Flow Launcher" + Flow Launcher'u Yeniden Başlat Ayarlar Reload Plugin Data Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml index 526144b43..8f2883bce 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/uk-UA.xaml @@ -17,7 +17,7 @@ Відкрити кошик Вийти Зберегти налаштування - Перезапустити Flow Launcher" + Перезапустити Flow Launcher Налаштування Перезавантажити дані плагінів Перевірити наявність оновлень diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml index be84d0e7f..cd0b6f166 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-cn.xaml @@ -11,20 +11,20 @@ Log Off/Sign Out Lock Sleep - Hibernate - Index Option - Empty Recycle Bin - Open Recycle Bin + 休眠 + 索引选项 + 清空回收站 + 打开回收站 退出 - Save Settings - Restart Flow Launcher" + 保存设置 + 重启 Flow Launcher 设置 重新加载插件数据 - Check For Update - Open Log Location - Flow Launcher Tips - Flow Launcher UserData Folder - Toggle Game Mode + 检查更新 + 打开日志文件夹 + Flow Launcher 提示 + Flow Launcher 用户数据文件夹 + 切换游戏模式 关闭电脑 @@ -46,7 +46,7 @@ 检查新的 Flow Launcher 更新 访问 Flow Launcher 的文档以获取更多帮助以及使用技巧 打开Flow Launcher 设置文件夹 - Toggle Game Mode + 切换游戏模式 成功 diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml index 62bf8f012..9934acc9f 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/zh-tw.xaml @@ -17,7 +17,7 @@ Open Recycle Bin 結束 Save Settings - Restart Flow Launcher" + 重新啟動 Flow Launcher 設定 重新載入插件資料 Check For Update diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx index b243dafd8..45dc2c8cc 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.pt-PT.resx @@ -2443,7 +2443,7 @@ Change search options for files and folders - Adjust settings before giving a presentation + Ajustar definições antes de uma apresentação Digitalizar um documento ou imagem @@ -2467,10 +2467,10 @@ Calibrate the screen for pen or touch input - Manage user certificates + Gerir certificados do utilizador - Schedule tasks + Agendar tarefas Ignore repeated keystrokes using FilterKeys