From 71376b8bebd157f7139b001d18a5b73b328bbea2 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 21 Apr 2023 22:13:33 +0800 Subject: [PATCH 01/44] Add FilePath property for PreviewInfo --- Flow.Launcher.Plugin/Result.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 1c4467762d5..da5b3c77494 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -241,6 +241,7 @@ public record PreviewInfo /// Full image used for preview panel /// public string PreviewImagePath { get; set; } + /// /// Determines if the preview image should occupy the full width of the preview panel. /// @@ -248,12 +249,18 @@ public record PreviewInfo public string Description { get; set; } public IconDelegate PreviewDelegate { get; set; } + /// + /// File path of the result. For third-party preview programs such as QuickLook. + /// + public string FilePath { get; set; } + public static PreviewInfo Default { get; } = new() { PreviewImagePath = null, Description = null, IsMedia = false, PreviewDelegate = null, + FilePath = null, }; } } From 96f52314efc4b391f5e0f165fab7dc34d8f9f281 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 21 Apr 2023 22:16:44 +0800 Subject: [PATCH 02/44] Add FilePath for Explorer results preview --- .../Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 83c173ac6b7..62e41a41c47 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -235,7 +235,12 @@ internal static Result CreateOpenCurrentFolderResult(string path, string actionK internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false) { Result.PreviewInfo preview = IsMedia(Path.GetExtension(filePath)) - ? new Result.PreviewInfo { IsMedia = true, PreviewImagePath = filePath, } + ? new Result.PreviewInfo + { + IsMedia = true, + PreviewImagePath = filePath, + FilePath = filePath, + } : Result.PreviewInfo.Default; var title = Path.GetFileName(filePath); From 0c4f50a89f2e7b9375c2371e1bca56ea0adfb9f0 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 21 Apr 2023 23:45:09 +0800 Subject: [PATCH 03/44] Default values for preview info --- Flow.Launcher.Plugin/Result.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index da5b3c77494..f618645c41b 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -240,19 +240,19 @@ public record PreviewInfo /// /// Full image used for preview panel /// - public string PreviewImagePath { get; set; } - + public string PreviewImagePath { get; set; } = null; + /// /// Determines if the preview image should occupy the full width of the preview panel. /// - public bool IsMedia { get; set; } - public string Description { get; set; } - public IconDelegate PreviewDelegate { get; set; } + public bool IsMedia { get; set; } = false; + public string Description { get; set; } = null; + public IconDelegate PreviewDelegate { get; set; } = null; /// /// File path of the result. For third-party preview programs such as QuickLook. /// - public string FilePath { get; set; } + public string FilePath { get; set; } = null; public static PreviewInfo Default { get; } = new() { From 1cedb6e619bd544379010dc7bbc06c460add7bab Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 21 Apr 2023 23:52:00 +0800 Subject: [PATCH 04/44] Add file path in preview --- .../Search/ResultManager.cs | 25 +++++++++++-------- .../Programs/Win32.cs | 4 +++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 62e41a41c47..ab6f7a7f6e9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -77,6 +77,10 @@ internal static Result CreateFolderResult(string title, string subtitle, string AutoCompleteText = GetAutoCompleteText(title, query, path, ResultType.Folder), TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData, CopyText = path, + Preview = new Result.PreviewInfo + { + FilePath = path, + }, Action = c => { // open folder @@ -162,6 +166,10 @@ internal static Result CreateDriveSpaceDisplayResult(string path, string actionK Score = 500, ProgressBar = progressValue, ProgressBarColor = progressBarColor, + Preview = new Result.PreviewInfo + { + FilePath = path, + }, Action = _ => { OpenFolder(path); @@ -234,15 +242,7 @@ internal static Result CreateOpenCurrentFolderResult(string path, string actionK internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false) { - Result.PreviewInfo preview = IsMedia(Path.GetExtension(filePath)) - ? new Result.PreviewInfo - { - IsMedia = true, - PreviewImagePath = filePath, - FilePath = filePath, - } - : Result.PreviewInfo.Default; - + bool isMedia = IsMedia(Path.GetExtension(filePath)); var title = Path.GetFileName(filePath); var result = new Result @@ -250,7 +250,12 @@ internal static Result CreateFileResult(string filePath, Query query, int score Title = title, SubTitle = Path.GetDirectoryName(filePath), IcoPath = filePath, - Preview = preview, + Preview = new Result.PreviewInfo + { + IsMedia = isMedia, + PreviewImagePath = isMedia ? filePath : null, + FilePath = filePath, + }, AutoCompleteText = GetAutoCompleteText(title, query, filePath, ResultType.File), TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData, Score = score, diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 6da6006ae5b..2194671aaaa 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -168,6 +168,10 @@ public Result Result(string query, IPublicAPI api) Score = matchResult.Score, TitleHighlightData = matchResult.MatchData, ContextData = this, + Preview = new Result.PreviewInfo + { + FilePath = ExecutablePath, + }, Action = c => { // Ctrl + Enter to open containing folder From f4609135a201ccbd4cdef08e53099dbd3a858916 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 11:12:48 +0800 Subject: [PATCH 05/44] Move QuickLook support to QuickLookHelper - Adapted from Files --- Flow.Launcher/Helper/QuickLookHelper.cs | 91 +++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Flow.Launcher/Helper/QuickLookHelper.cs diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs new file mode 100644 index 00000000000..fe12f481ca4 --- /dev/null +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -0,0 +1,91 @@ +// Adapted from Files +// https://github.com/files-community/Files/blob/ad33c75c53382fcb9b16fa9cd66ae5399f3dff0b/src/Files.App/Helpers/QuickLookHelpers.cs +using System; +using System.IO.Pipes; +using System.IO; +using System.Security.Principal; +using System.Threading.Tasks; +using Flow.Launcher.Infrastructure.Logger; + +namespace Flow.Launcher.Helper +{ + internal class QuickLookHelper + { + private const int TIMEOUT = 500; + private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; + private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; + private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; + + /// + /// Toggle QuickLook + /// + /// File path to preview + /// Is swtiching file + /// + public static async Task ToggleQuickLookPreviewAsync(string path, bool switchPreview = false) + { + bool isQuickLookAvailable = await DetectQuickLookAvailabilityAsync(); + + if (!isQuickLookAvailable) + { + if (!switchPreview) + { + Log.Warn($"{nameof(QuickLookHelper)}", "QuickLook not detected"); + } + return; + } + + string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; + string message = switchPreview ? pipeMessageSwitch : pipeMessageToggle; + + await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); + try + { + await client.ConnectAsync(TIMEOUT); + + await using var writer = new StreamWriter(client); + await writer.WriteLineAsync($"{message}|{path}"); + await writer.FlushAsync(); + } + catch (TimeoutException) + { + client.Close(); + } + } + + private static async Task DetectQuickLookAvailabilityAsync() + { + static async Task QuickLookServerAvailable() + { + await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); + try + { + await client.ConnectAsync(TIMEOUT); + var serverInstances = client.NumberOfServerInstances; + + await using var writer = new StreamWriter(client); + await writer.WriteLineAsync($"{pipeMessageSwitch}|"); + await writer.FlushAsync(); + + return serverInstances; + } + catch (TimeoutException) + { + client.Close(); + return 0; + } + } + + try + { + var result = await QuickLookServerAvailable(); + return result != 0; + } + catch (Exception e) + { + Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook unavailable", e); + return false; + } + } + } +} From 0b6f737436d11763c8745a4e022ca0a6ec6dbf18 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 13:09:17 +0800 Subject: [PATCH 06/44] Add open/close QuickLook function --- Flow.Launcher/Helper/QuickLookHelper.cs | 55 ++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs index fe12f481ca4..1003e731f9e 100644 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -9,12 +9,14 @@ namespace Flow.Launcher.Helper { - internal class QuickLookHelper + internal static class QuickLookHelper { private const int TIMEOUT = 500; private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; + private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; + private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; /// /// Toggle QuickLook @@ -22,35 +24,60 @@ internal class QuickLookHelper /// File path to preview /// Is swtiching file /// - public static async Task ToggleQuickLookPreviewAsync(string path, bool switchPreview = false) + public static async Task ToggleQuickLookAsync(string path, bool switchPreview = false) { - bool isQuickLookAvailable = await DetectQuickLookAvailabilityAsync(); + //bool isQuickLookAvailable = await DetectQuickLookAvailabilityAsync(); - if (!isQuickLookAvailable) - { - if (!switchPreview) - { - Log.Warn($"{nameof(QuickLookHelper)}", "QuickLook not detected"); - } - return; - } + //if (!isQuickLookAvailable) + //{ + // if (!switchPreview) + // { + // Log.Warn($"{nameof(QuickLookHelper)}", "QuickLook not detected"); + // } + // return; + //} - string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; - string message = switchPreview ? pipeMessageSwitch : pipeMessageToggle; + if (string.IsNullOrEmpty(path)) + return false; + return await SendQuickLookPipeMsgAsync(switchPreview ? pipeMessageSwitch : pipeMessageToggle, path); + } + + public static async Task CloseQuickLookAsync() + { + return await SendQuickLookPipeMsgAsync(pipeMessageClose); + } + + public static async Task OpenQuickLookAsync(string path) + { + if (string.IsNullOrEmpty(path)) + return false; + return await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); + } + + private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") + { await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); try { await client.ConnectAsync(TIMEOUT); await using var writer = new StreamWriter(client); - await writer.WriteLineAsync($"{message}|{path}"); + await writer.WriteLineAsync($"{message}|{arg}"); await writer.FlushAsync(); } catch (TimeoutException) { client.Close(); + Log.Error($"{nameof(QuickLookHelper)}", "QuickLook timeout"); + return false; + } + catch (Exception e) + { + Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook error", e); + return false; } + return true; } private static async Task DetectQuickLookAvailabilityAsync() From ab20dcca7c0f90381e7c35a9ceb47fd6438f4e15 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 14:12:12 +0800 Subject: [PATCH 07/44] Reorganize preview code --- Flow.Launcher/ViewModel/MainViewModel.cs | 100 ++++++++++++----------- 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 27809f67b89..3ac7a190282 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -293,6 +293,8 @@ private async Task OpenResultAsync(string index) } } + #region BasicCommands + [RelayCommand] private void OpenSetting() { @@ -356,6 +358,8 @@ public void ToggleGameMode() #endregion + #endregion + #region ViewModel Properties public Settings Settings { get; } @@ -444,52 +448,6 @@ private void DecreaseMaxResult() Settings.MaxResultsToShow -= 1; } - [RelayCommand] - public void TogglePreview() - { - if (!PreviewVisible) - { - ShowPreview(); - } - else - { - HidePreview(); - } - } - - private void ShowPreview() - { - ResultAreaColumn = 1; - PreviewVisible = true; - Results.SelectedItem?.LoadPreviewImage(); - } - - private void HidePreview() - { - ResultAreaColumn = 3; - PreviewVisible = false; - } - - public void ResetPreview() - { - if (Settings.AlwaysPreview == true) - { - ShowPreview(); - } - else - { - HidePreview(); - } - } - - private void UpdatePreview() - { - if (PreviewVisible) - { - Results.SelectedItem?.LoadPreviewImage(); - } - } - /// /// we need move cursor to end when we manually changed query /// but we don't want to move cursor to end when query is updated from TextBox @@ -610,6 +568,56 @@ public string PreviewHotkey #endregion + #region Preview + + [RelayCommand] + public void TogglePreview() + { + if (!PreviewVisible) + { + ShowPreview(); + } + else + { + HidePreview(); + } + } + + private void ShowPreview() + { + ResultAreaColumn = 1; + PreviewVisible = true; + Results.SelectedItem?.LoadPreviewImage(); + } + + private void HidePreview() + { + ResultAreaColumn = 3; + PreviewVisible = false; + } + + public void ResetPreview() + { + if (Settings.AlwaysPreview == true) + { + ShowPreview(); + } + else + { + HidePreview(); + } + } + + private void UpdatePreview() + { + if (PreviewVisible) + { + Results.SelectedItem?.LoadPreviewImage(); + } + } + + #endregion + #region Query public void Query() From 98991fe1039fe63fbdb86b7289d429593ac64fa1 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 14:17:05 +0800 Subject: [PATCH 08/44] Implement open QuickLook feature --- Flow.Launcher/ViewModel/MainViewModel.cs | 71 +++++++++++++++++++++++- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3ac7a190282..14b5577a16f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -564,6 +564,8 @@ public string PreviewHotkey public bool PreviewVisible { get; set; } = false; + public bool ExternalPreviewOpen { get; set; } = false; + public int ResultAreaColumn { get; set; } = 1; #endregion @@ -571,9 +573,21 @@ public string PreviewHotkey #region Preview [RelayCommand] - public void TogglePreview() + private void TogglePreview() { - if (!PreviewVisible) + bool useThirdPartyPreview = true; + if (useThirdPartyPreview) + { + if (!ExternalPreviewOpen) + { + _ = OpenQuickLookPreviewAsync(); + } + else + { + _ = CloseQuickLookPreviewAsync(); + } + } + else if (!PreviewVisible) { ShowPreview(); } @@ -610,12 +624,63 @@ public void ResetPreview() private void UpdatePreview() { - if (PreviewVisible) + bool useThirdPartyPreview = true; + if (useThirdPartyPreview) + { + if (ExternalPreviewOpen) + { + _ = ToggleQuickLookPreviewAsync(true); + } + } + else if (PreviewVisible) { Results.SelectedItem?.LoadPreviewImage(); } } + private async Task ToggleQuickLookPreviewAsync(bool switchFile = false) + { + if (!SelectedIsFromQueryResults()) + return; + + var result = Results.SelectedItem?.Result; + + if (result is null || string.IsNullOrEmpty(result.Preview.FilePath)) + return; + + bool success = await QuickLookHelper.ToggleQuickLookAsync(result.Preview.FilePath, switchFile); + if (success) + { + ExternalPreviewOpen = !ExternalPreviewOpen; + } + } + + private async Task OpenQuickLookPreviewAsync() + { + if (!SelectedIsFromQueryResults()) + return; + + var result = Results.SelectedItem?.Result; + + if (result is null || string.IsNullOrEmpty(result.Preview.FilePath)) + return; + + bool success = await QuickLookHelper.OpenQuickLookAsync(result.Preview.FilePath); + if (success) + { + ExternalPreviewOpen = true; + } + } + + private async Task CloseQuickLookPreviewAsync() + { + bool success = await QuickLookHelper.CloseQuickLookAsync(); + if (success) + { + ExternalPreviewOpen = false; + } + } + #endregion #region Query From 99b51973721c26a2b5c64bf59c0cbde709d5f73a Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 14:36:39 +0800 Subject: [PATCH 09/44] Fix case when switching file --- 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 14b5577a16f..02dcd2248ab 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -651,7 +651,7 @@ private async Task ToggleQuickLookPreviewAsync(bool switchFile = false) bool success = await QuickLookHelper.ToggleQuickLookAsync(result.Preview.FilePath, switchFile); if (success) { - ExternalPreviewOpen = !ExternalPreviewOpen; + ExternalPreviewOpen = switchFile || !ExternalPreviewOpen; } } From 1e1f29b163d1ec1a124c76a1c81eab36056aa5d5 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 15:00:21 +0800 Subject: [PATCH 10/44] Add UI to toggle QuickLook usage --- .../UserSettings/Settings.cs | 4 ++++ Flow.Launcher/Languages/en.xaml | 2 ++ Flow.Launcher/SettingWindow.xaml | 18 ++++++++++++++++++ Flow.Launcher/ViewModel/MainViewModel.cs | 6 ++---- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 7f62d82c8ef..17abec8043e 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -159,7 +159,11 @@ public CustomBrowserViewModel CustomBrowser /// when false Alphabet static service will always return empty results /// public bool ShouldUsePinyin { get; set; } = false; + + public bool UseQuickLook { get; set; } = false; + public bool AlwaysPreview { get; set; } = false; + public bool AlwaysStartEn { get; set; } = false; [JsonInclude, JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 30c1173780c..e3433b98f3e 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -80,6 +80,8 @@ 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. + Use QuickLook + Use QuickLook to preview file results. Shadow effect is not allowed while current theme has blur effect enabled diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 665d16b8f8e..2b39b3b9443 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -859,6 +859,24 @@ + + + + + + + + +  + + + + diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 02dcd2248ab..9f055d640d7 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -575,8 +575,7 @@ public string PreviewHotkey [RelayCommand] private void TogglePreview() { - bool useThirdPartyPreview = true; - if (useThirdPartyPreview) + if (Settings.UseQuickLook) { if (!ExternalPreviewOpen) { @@ -624,8 +623,7 @@ public void ResetPreview() private void UpdatePreview() { - bool useThirdPartyPreview = true; - if (useThirdPartyPreview) + if (Settings.UseQuickLook) { if (ExternalPreviewOpen) { From afa8a348911e957b279126a7df42832c72b064d8 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 15:01:17 +0800 Subject: [PATCH 11/44] Tweak Win32 preview path --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 2194671aaaa..3a59601edbf 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -170,7 +170,7 @@ public Result Result(string query, IPublicAPI api) ContextData = this, Preview = new Result.PreviewInfo { - FilePath = ExecutablePath, + FilePath = FullPath, }, Action = c => { From a1600fabcc7aee0a3edc382c0eb8b9263c977653 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 15:53:43 +0800 Subject: [PATCH 12/44] Implement preview switching logic - Internal and external preview can't open at the same time - Always preview option doesn't control external --- Flow.Launcher.Plugin/Result.cs | 5 ++- Flow.Launcher/ViewModel/MainViewModel.cs | 55 ++++++++++++------------ 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index f618645c41b..a97db47813d 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -230,8 +230,11 @@ public ValueTask ExecuteAsync(ActionContext context) /// #26a0da (blue) public string ProgressBarColor { get; set; } = "#26a0da"; + /// + /// Preview info of the result to show on preview panel. + /// public PreviewInfo Preview { get; set; } = PreviewInfo.Default; - + /// /// Info of the preview image. /// diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 9f055d640d7..3f749f02457 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -575,11 +575,15 @@ public string PreviewHotkey [RelayCommand] private void TogglePreview() { - if (Settings.UseQuickLook) + if (Settings.UseQuickLook && CanExternalPreviewSelectedResult(out var path)) { - if (!ExternalPreviewOpen) + if (Settings.AlwaysPreview == true && PreviewVisible) { - _ = OpenQuickLookPreviewAsync(); + HidePreview(); // When Always preview, toggle off rather than open external + } + else if (!ExternalPreviewOpen) + { + _ = OpenQuickLookPreviewAsync(path); } else { @@ -620,50 +624,41 @@ public void ResetPreview() HidePreview(); } } - + private void UpdatePreview() { - if (Settings.UseQuickLook) + if (Settings.UseQuickLook && CanExternalPreviewSelectedResult(out var path)) { - if (ExternalPreviewOpen) + _ = ToggleQuickLookPreviewAsync(path, ExternalPreviewOpen); + if (PreviewVisible) { - _ = ToggleQuickLookPreviewAsync(true); + HidePreview(); } } else if (PreviewVisible) { Results.SelectedItem?.LoadPreviewImage(); } + else + { + // When external is open and select a result that can't be previewed by external program + _ = CloseQuickLookPreviewAsync(); + ShowPreview(); + } } - private async Task ToggleQuickLookPreviewAsync(bool switchFile = false) + private async Task ToggleQuickLookPreviewAsync(string path, bool switchFile = false) { - if (!SelectedIsFromQueryResults()) - return; - - var result = Results.SelectedItem?.Result; - - if (result is null || string.IsNullOrEmpty(result.Preview.FilePath)) - return; - - bool success = await QuickLookHelper.ToggleQuickLookAsync(result.Preview.FilePath, switchFile); + bool success = await QuickLookHelper.ToggleQuickLookAsync(path, switchFile); if (success) { ExternalPreviewOpen = switchFile || !ExternalPreviewOpen; } } - private async Task OpenQuickLookPreviewAsync() + private async Task OpenQuickLookPreviewAsync(string path) { - if (!SelectedIsFromQueryResults()) - return; - - var result = Results.SelectedItem?.Result; - - if (result is null || string.IsNullOrEmpty(result.Preview.FilePath)) - return; - - bool success = await QuickLookHelper.OpenQuickLookAsync(result.Preview.FilePath); + bool success = await QuickLookHelper.OpenQuickLookAsync(path); if (success) { ExternalPreviewOpen = true; @@ -679,6 +674,12 @@ private async Task CloseQuickLookPreviewAsync() } } + private bool CanExternalPreviewSelectedResult(out string path) + { + path = Results.SelectedItem?.Result?.Preview.FilePath; + return string.IsNullOrEmpty(path); + } + #endregion #region Query From cf10a4a17689a4631ffd03d49925373352827504 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 16:55:27 +0800 Subject: [PATCH 13/44] Rename method for clarity --- Flow.Launcher/ViewModel/MainViewModel.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3f749f02457..2a08f3994e2 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -579,7 +579,7 @@ private void TogglePreview() { if (Settings.AlwaysPreview == true && PreviewVisible) { - HidePreview(); // When Always preview, toggle off rather than open external + HideInternalPreview(); // When Always preview, toggle off rather than open external } else if (!ExternalPreviewOpen) { @@ -592,22 +592,22 @@ private void TogglePreview() } else if (!PreviewVisible) { - ShowPreview(); + ShowInternalPreview(); } else { - HidePreview(); + HideInternalPreview(); } } - private void ShowPreview() + private void ShowInternalPreview() { ResultAreaColumn = 1; PreviewVisible = true; Results.SelectedItem?.LoadPreviewImage(); } - private void HidePreview() + private void HideInternalPreview() { ResultAreaColumn = 3; PreviewVisible = false; @@ -617,11 +617,11 @@ public void ResetPreview() { if (Settings.AlwaysPreview == true) { - ShowPreview(); + ShowInternalPreview(); } else { - HidePreview(); + HideInternalPreview(); } } @@ -632,7 +632,7 @@ private void UpdatePreview() _ = ToggleQuickLookPreviewAsync(path, ExternalPreviewOpen); if (PreviewVisible) { - HidePreview(); + HideInternalPreview(); } } else if (PreviewVisible) @@ -643,7 +643,7 @@ private void UpdatePreview() { // When external is open and select a result that can't be previewed by external program _ = CloseQuickLookPreviewAsync(); - ShowPreview(); + ShowInternalPreview(); } } From 10d1410c5e9b8bf057df7010271d6eafcb639163 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 17:08:11 +0800 Subject: [PATCH 14/44] Reduce unnecessary image load --- 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 2a08f3994e2..871dd723c93 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -615,7 +615,7 @@ private void HideInternalPreview() public void ResetPreview() { - if (Settings.AlwaysPreview == true) + if (Settings.AlwaysPreview == true && !PreviewVisible) { ShowInternalPreview(); } From ba8bb3a3b89ad9d81cf568011c3c71658d385ff7 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 17:06:38 +0800 Subject: [PATCH 15/44] Fix switching preview logic --- Flow.Launcher/ViewModel/MainViewModel.cs | 59 +++++++++++++++++------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 871dd723c93..59696aaa617 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -579,18 +579,24 @@ private void TogglePreview() { if (Settings.AlwaysPreview == true && PreviewVisible) { + // Only hit this line at first preview toggle after toggle on Flow HideInternalPreview(); // When Always preview, toggle off rather than open external } - else if (!ExternalPreviewOpen) - { - _ = OpenQuickLookPreviewAsync(path); - } else { - _ = CloseQuickLookPreviewAsync(); + ToggleExternalPreview(path); } } - else if (!PreviewVisible) + else + { + // Fallback + ToggleInternalPreview(); + } + } + + private void ToggleInternalPreview() + { + if (!PreviewVisible) { ShowInternalPreview(); } @@ -600,6 +606,18 @@ private void TogglePreview() } } + private void ToggleExternalPreview(string path) + { + if (!ExternalPreviewOpen) + { + _ = OpenQuickLookPreviewAsync(path); + } + else + { + _ = CloseQuickLookPreviewAsync(); + } + } + private void ShowInternalPreview() { ResultAreaColumn = 1; @@ -629,21 +647,30 @@ private void UpdatePreview() { if (Settings.UseQuickLook && CanExternalPreviewSelectedResult(out var path)) { - _ = ToggleQuickLookPreviewAsync(path, ExternalPreviewOpen); - if (PreviewVisible) + // Should use external preview for selected result + if (ExternalPreviewOpen) + { + _ = ToggleQuickLookPreviewAsync(path, true); + } + else if(PreviewVisible) { + // When internal is open and select a result that should use external preview + _ = OpenQuickLookPreviewAsync(path); HideInternalPreview(); } } - else if (PreviewVisible) - { - Results.SelectedItem?.LoadPreviewImage(); - } else { - // When external is open and select a result that can't be previewed by external program - _ = CloseQuickLookPreviewAsync(); - ShowInternalPreview(); + if (PreviewVisible) + { + Results.SelectedItem?.LoadPreviewImage(); + } + else if (ExternalPreviewOpen) + { + // When external is open and select a result that can't use external preview + _ = CloseQuickLookPreviewAsync(); + ShowInternalPreview(); + } } } @@ -677,7 +704,7 @@ private async Task CloseQuickLookPreviewAsync() private bool CanExternalPreviewSelectedResult(out string path) { path = Results.SelectedItem?.Result?.Preview.FilePath; - return string.IsNullOrEmpty(path); + return !string.IsNullOrEmpty(path); } #endregion From 098e90b864775a2cd00cf9b014ea353c29de23cd Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 17:16:02 +0800 Subject: [PATCH 16/44] Hide external preview when hiding flow --- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 59696aaa617..b3d11fc7c3a 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1110,6 +1110,11 @@ public async void Hide() // Trick for no delay MainWindowOpacity = 0; + if (ExternalPreviewOpen) + { + _ = CloseQuickLookPreviewAsync(); + } + if (!SelectedIsFromQueryResults()) { SelectedResults = Results; From 26ff98e3b9af6b57f5c7126979d7527beb643de0 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 22 Apr 2023 17:18:40 +0800 Subject: [PATCH 17/44] comment --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index b3d11fc7c3a..3096009266f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -647,6 +647,8 @@ private void UpdatePreview() { if (Settings.UseQuickLook && CanExternalPreviewSelectedResult(out var path)) { + // TODO: When always preview (internal is open) and select another result can use external preview + // then switched to external, looks bad // Should use external preview for selected result if (ExternalPreviewOpen) { From 9061da9efa021bb5ec2ad16c0092d0a1942c1078 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 23 Apr 2023 19:09:58 +0800 Subject: [PATCH 18/44] Formatting --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3096009266f..9198759dcd5 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -293,6 +293,8 @@ private async Task OpenResultAsync(string index) } } + #endregion + #region BasicCommands [RelayCommand] @@ -358,8 +360,6 @@ public void ToggleGameMode() #endregion - #endregion - #region ViewModel Properties public Settings Settings { get; } From 441d4cb17387091a3d22caeb6b9cf1048490bc79 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 23 Apr 2023 19:50:44 +0800 Subject: [PATCH 19/44] Configure await for QuickLook calls --- Flow.Launcher/ViewModel/MainViewModel.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 9198759dcd5..b70d15d4537 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -610,11 +610,11 @@ private void ToggleExternalPreview(string path) { if (!ExternalPreviewOpen) { - _ = OpenQuickLookPreviewAsync(path); + _ = OpenQuickLookPreviewAsync(path).ConfigureAwait(false); } else { - _ = CloseQuickLookPreviewAsync(); + _ = CloseQuickLookPreviewAsync().ConfigureAwait(false); } } @@ -652,12 +652,12 @@ private void UpdatePreview() // Should use external preview for selected result if (ExternalPreviewOpen) { - _ = ToggleQuickLookPreviewAsync(path, true); + _ = ToggleQuickLookPreviewAsync(path, true).ConfigureAwait(false); } else if(PreviewVisible) { // When internal is open and select a result that should use external preview - _ = OpenQuickLookPreviewAsync(path); + _ = OpenQuickLookPreviewAsync(path).ConfigureAwait(false); HideInternalPreview(); } } @@ -670,7 +670,7 @@ private void UpdatePreview() else if (ExternalPreviewOpen) { // When external is open and select a result that can't use external preview - _ = CloseQuickLookPreviewAsync(); + _ = CloseQuickLookPreviewAsync().ConfigureAwait(false); ShowInternalPreview(); } } @@ -1114,7 +1114,7 @@ public async void Hide() if (ExternalPreviewOpen) { - _ = CloseQuickLookPreviewAsync(); + _ = CloseQuickLookPreviewAsync().ConfigureAwait(false); } if (!SelectedIsFromQueryResults()) From 45a42a5e549733379ef1925f5af65c5f3bd1fe24 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 23 Apr 2023 20:00:06 +0800 Subject: [PATCH 20/44] Log exception when QL timeout --- Flow.Launcher/Helper/QuickLookHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs index 1003e731f9e..136f86cdb09 100644 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -96,9 +96,10 @@ static async Task QuickLookServerAvailable() return serverInstances; } - catch (TimeoutException) + catch (TimeoutException e) { client.Close(); + Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook connection timeout", e); return 0; } } From cda587fb585b4fe89c5db9e338c0bb00a6bcfd83 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 23 Apr 2023 20:52:10 +0800 Subject: [PATCH 21/44] Show toast when QL unavailable --- Flow.Launcher/Helper/QuickLookHelper.cs | 46 ++++++++++++++++--------- Flow.Launcher/Languages/en.xaml | 2 ++ 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs index 136f86cdb09..0d8251f1fb7 100644 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -6,6 +6,7 @@ using System.Security.Principal; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Core.Resource; namespace Flow.Launcher.Helper { @@ -17,7 +18,7 @@ internal static class QuickLookHelper private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; - + /// /// Toggle QuickLook /// @@ -25,34 +26,39 @@ internal static class QuickLookHelper /// Is swtiching file /// public static async Task ToggleQuickLookAsync(string path, bool switchPreview = false) - { - //bool isQuickLookAvailable = await DetectQuickLookAvailabilityAsync(); - - //if (!isQuickLookAvailable) - //{ - // if (!switchPreview) - // { - // Log.Warn($"{nameof(QuickLookHelper)}", "QuickLook not detected"); - // } - // return; - //} - + { if (string.IsNullOrEmpty(path)) return false; - return await SendQuickLookPipeMsgAsync(switchPreview ? pipeMessageSwitch : pipeMessageToggle, path); + bool success = await SendQuickLookPipeMsgAsync(switchPreview ? pipeMessageSwitch : pipeMessageToggle, path); + if (!success) + { + ShowQuickLookUnavailableToast(); + } + return success; } public static async Task CloseQuickLookAsync() { - return await SendQuickLookPipeMsgAsync(pipeMessageClose); + bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); + if (!success) + { + ShowQuickLookUnavailableToast(); + } + return success; } public static async Task OpenQuickLookAsync(string path) { if (string.IsNullOrEmpty(path)) return false; - return await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); + if (!success) + { + ShowQuickLookUnavailableToast(); + } + return success; } private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") @@ -80,7 +86,7 @@ private static async Task SendQuickLookPipeMsgAsync(string message, string return true; } - private static async Task DetectQuickLookAvailabilityAsync() + public static async Task DetectQuickLookAvailabilityAsync() { static async Task QuickLookServerAvailable() { @@ -115,5 +121,11 @@ static async Task QuickLookServerAvailable() return false; } } + + private static void ShowQuickLookUnavailableToast() + { + Notification.Show(InternationalizationManager.Instance.GetTranslation("QuickLookFail"), + InternationalizationManager.Instance.GetTranslation("QuickLookFailTips")); + } } } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index e3433b98f3e..2f2c3c8bf4e 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -309,6 +309,8 @@ Please wait... + Failed to launch QuickLook + Please check if QuickLook is running. Checking for new update From 2ed33a232d06770593f9f7eabe2ede5922ba2b33 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 14 May 2023 01:40:45 +0800 Subject: [PATCH 22/44] Refactor QuickLook toggle and update logic - Forcibly open/close/switch content when needed - Remove unused functions --- Flow.Launcher/Helper/QuickLookHelper.cs | 25 +++++- Flow.Launcher/ViewModel/MainViewModel.cs | 109 ++++++++++------------- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs index 0d8251f1fb7..59c94ae2f0a 100644 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -23,14 +23,13 @@ internal static class QuickLookHelper /// Toggle QuickLook /// /// File path to preview - /// Is swtiching file /// - public static async Task ToggleQuickLookAsync(string path, bool switchPreview = false) + public static async Task ToggleQuickLookAsync(string path) { if (string.IsNullOrEmpty(path)) return false; - - bool success = await SendQuickLookPipeMsgAsync(switchPreview ? pipeMessageSwitch : pipeMessageToggle, path); + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); if (!success) { ShowQuickLookUnavailableToast(); @@ -61,6 +60,24 @@ public static async Task OpenQuickLookAsync(string path) return success; } + /// + /// Switch QuickLook to preview another file if it's on + /// + /// File path to preview + /// + public static async Task SwitchQuickLookAsync(string path) + { + if (string.IsNullOrEmpty(path)) + return false; + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); + if (!success) + { + ShowQuickLookUnavailableToast(); + } + return success; + } + private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") { await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index b70d15d4537..06ac4620911 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -564,8 +564,6 @@ public string PreviewHotkey public bool PreviewVisible { get; set; } = false; - public bool ExternalPreviewOpen { get; set; } = false; - public int ResultAreaColumn { get; set; } = 1; #endregion @@ -608,14 +606,24 @@ private void ToggleInternalPreview() private void ToggleExternalPreview(string path) { - if (!ExternalPreviewOpen) - { - _ = OpenQuickLookPreviewAsync(path).ConfigureAwait(false); - } - else - { - _ = CloseQuickLookPreviewAsync().ConfigureAwait(false); - } + _ = QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); + } + + private void OpenExternalPreview(string path) + { + _ = QuickLookHelper.OpenQuickLookAsync(path).ConfigureAwait(false); + } + + private void CloseExternalPreview() + { + _ = QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); + } + + private void SwitchExternalPreview(string path) + { + // Switches preview content + // When external is off, do nothing + _ = QuickLookHelper.SwitchQuickLookAsync(path).ConfigureAwait(false); } private void ShowInternalPreview() @@ -645,61 +653,41 @@ public void ResetPreview() private void UpdatePreview() { - if (Settings.UseQuickLook && CanExternalPreviewSelectedResult(out var path)) - { - // TODO: When always preview (internal is open) and select another result can use external preview - // then switched to external, looks bad - // Should use external preview for selected result - if (ExternalPreviewOpen) - { - _ = ToggleQuickLookPreviewAsync(path, true).ConfigureAwait(false); - } - else if(PreviewVisible) - { - // When internal is open and select a result that should use external preview - _ = OpenQuickLookPreviewAsync(path).ConfigureAwait(false); - HideInternalPreview(); - } - } - else + if (Settings.UseQuickLook) { - if (PreviewVisible) + if (CanExternalPreviewSelectedResult(out var path)) { - Results.SelectedItem?.LoadPreviewImage(); + // Should use external preview for selected result + if (PreviewVisible) + { + // Previewing + // When internal is open and select a result that should use external preview + // External must be off when PreviewVisible + HideInternalPreview(); + OpenExternalPreview(path); + } + else + { + // Internal is off, try to switch preview content + SwitchExternalPreview(path); + } } - else if (ExternalPreviewOpen) + else { - // When external is open and select a result that can't use external preview - _ = CloseQuickLookPreviewAsync().ConfigureAwait(false); - ShowInternalPreview(); + // Should use internal preview for selected result + if (PreviewVisible) + { + Results.SelectedItem?.LoadPreviewImage(); + } + else + { + CloseExternalPreview(); // Forcibly close, ideally should only close when it's on + } } } - } - - private async Task ToggleQuickLookPreviewAsync(string path, bool switchFile = false) - { - bool success = await QuickLookHelper.ToggleQuickLookAsync(path, switchFile); - if (success) - { - ExternalPreviewOpen = switchFile || !ExternalPreviewOpen; - } - } - - private async Task OpenQuickLookPreviewAsync(string path) - { - bool success = await QuickLookHelper.OpenQuickLookAsync(path); - if (success) - { - ExternalPreviewOpen = true; - } - } - - private async Task CloseQuickLookPreviewAsync() - { - bool success = await QuickLookHelper.CloseQuickLookAsync(); - if (success) + else if(PreviewVisible) { - ExternalPreviewOpen = false; + Results.SelectedItem?.LoadPreviewImage(); } } @@ -1112,10 +1100,7 @@ public async void Hide() // Trick for no delay MainWindowOpacity = 0; - if (ExternalPreviewOpen) - { - _ = CloseQuickLookPreviewAsync().ConfigureAwait(false); - } + CloseExternalPreview(); if (!SelectedIsFromQueryResults()) { From 64afeedfc5667521b34463ce627d641c96663798 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 14 May 2023 01:51:20 +0800 Subject: [PATCH 23/44] Rename variable --- Flow.Launcher.Infrastructure/UserSettings/Settings.cs | 2 +- Flow.Launcher/SettingWindow.xaml | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 17abec8043e..3e63c2e6510 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -160,7 +160,7 @@ public CustomBrowserViewModel CustomBrowser /// public bool ShouldUsePinyin { get; set; } = false; - public bool UseQuickLook { get; set; } = false; + public bool UseExternalPreview { get; set; } = false; public bool AlwaysPreview { get; set; } = false; diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 2b39b3b9443..49877cb6635 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -868,7 +868,7 @@ diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 06ac4620911..ad5f810bc2b 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -573,7 +573,7 @@ public string PreviewHotkey [RelayCommand] private void TogglePreview() { - if (Settings.UseQuickLook && CanExternalPreviewSelectedResult(out var path)) + if (Settings.UseExternalPreview && CanExternalPreviewSelectedResult(out var path)) { if (Settings.AlwaysPreview == true && PreviewVisible) { @@ -653,7 +653,7 @@ public void ResetPreview() private void UpdatePreview() { - if (Settings.UseQuickLook) + if (Settings.UseExternalPreview) { if (CanExternalPreviewSelectedResult(out var path)) { From d266e9eb273e6c0bae40e47c36c7f59715aac99f Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 21 May 2023 00:29:56 +0800 Subject: [PATCH 24/44] Simplify QuickLook logic - Only try to use QuickLook when built-in preview is hidden --- Flow.Launcher/ViewModel/MainViewModel.cs | 89 +++++++++++------------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ad5f810bc2b..34007f5df05 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -570,25 +570,23 @@ public string PreviewHotkey #region Preview + // Not accurate + public bool ExternalPreviewOpen { get; set; } = false; + [RelayCommand] private void TogglePreview() { - if (Settings.UseExternalPreview && CanExternalPreviewSelectedResult(out var path)) + if (PreviewVisible) { - if (Settings.AlwaysPreview == true && PreviewVisible) - { - // Only hit this line at first preview toggle after toggle on Flow - HideInternalPreview(); // When Always preview, toggle off rather than open external - } - else - { - ToggleExternalPreview(path); - } + HideInternalPreview(); + } + else if(Settings.UseExternalPreview && CanExternalPreviewSelectedResult(out var path)) + { + _ = ToggleExternalPreviewAsync(path); } else { - // Fallback - ToggleInternalPreview(); + ShowInternalPreview(); } } @@ -604,22 +602,34 @@ private void ToggleInternalPreview() } } - private void ToggleExternalPreview(string path) + private async Task ToggleExternalPreviewAsync(string path) { - _ = QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); + bool success = await QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); + if (success) + { + ExternalPreviewOpen = !ExternalPreviewOpen; + } } - private void OpenExternalPreview(string path) + private async Task OpenExternalPreviewAsync(string path) { - _ = QuickLookHelper.OpenQuickLookAsync(path).ConfigureAwait(false); + bool success = await QuickLookHelper.OpenQuickLookAsync(path).ConfigureAwait(false); + if (success) + { + ExternalPreviewOpen = true; + } } - - private void CloseExternalPreview() + + private async Task CloseExternalPreviewAsync() { - _ = QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); + bool success = await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); + if (success) + { + ExternalPreviewOpen = false; + } } - - private void SwitchExternalPreview(string path) + + private async Task SwitchExternalPreviewAsync(string path) { // Switches preview content // When external is off, do nothing @@ -653,42 +663,21 @@ public void ResetPreview() private void UpdatePreview() { - if (Settings.UseExternalPreview) + if (PreviewVisible) + { + Results.SelectedItem?.LoadPreviewImage(); + } + else if (Settings.UseExternalPreview) { if (CanExternalPreviewSelectedResult(out var path)) { - // Should use external preview for selected result - if (PreviewVisible) - { - // Previewing - // When internal is open and select a result that should use external preview - // External must be off when PreviewVisible - HideInternalPreview(); - OpenExternalPreview(path); - } - else - { - // Internal is off, try to switch preview content - SwitchExternalPreview(path); - } + _ = SwitchExternalPreviewAsync(path); } else { - // Should use internal preview for selected result - if (PreviewVisible) - { - Results.SelectedItem?.LoadPreviewImage(); - } - else - { - CloseExternalPreview(); // Forcibly close, ideally should only close when it's on - } + _ = CloseExternalPreviewAsync(); } } - else if(PreviewVisible) - { - Results.SelectedItem?.LoadPreviewImage(); - } } private bool CanExternalPreviewSelectedResult(out string path) @@ -1100,7 +1089,7 @@ public async void Hide() // Trick for no delay MainWindowOpacity = 0; - CloseExternalPreview(); + _ = CloseExternalPreviewAsync(); if (!SelectedIsFromQueryResults()) { From ff7457dc0f17a9914ae536857faa4104bcb420a6 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 21 May 2023 00:31:16 +0800 Subject: [PATCH 25/44] Keep FL open when focus lost if QL open --- Flow.Launcher/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 96b114dac94..70f4d817c4b 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -494,7 +494,7 @@ private async void OnDeactivated(object sender, EventArgs e) if (_settings.UseAnimation) await Task.Delay(100); - if (_settings.HideWhenDeactivated) + if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewOpen) { _viewModel.Hide(); } From cb33756a03a6c35db1dc1c7d3f752ae041ba632c Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 21 May 2023 00:32:45 +0800 Subject: [PATCH 26/44] Remove QL support for programs --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 3a59601edbf..6da6006ae5b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -168,10 +168,6 @@ public Result Result(string query, IPublicAPI api) Score = matchResult.Score, TitleHighlightData = matchResult.MatchData, ContextData = this, - Preview = new Result.PreviewInfo - { - FilePath = FullPath, - }, Action = c => { // Ctrl + Enter to open containing folder From abfbef41aeeceac68f48c1210d3a2377b258ec6d Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 21 May 2023 00:38:59 +0800 Subject: [PATCH 27/44] update --- .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 78ae8476bc6..419ccef386d 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -95,3 +95,4 @@ keyevent KListener requery vkcode +quicklook \ No newline at end of file From c705b013264a9e2f3b76828e4de711299dfbae52 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 22 May 2023 20:29:44 +0800 Subject: [PATCH 28/44] Change glyph --- Flow.Launcher/SettingWindow.xaml | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 49877cb6635..d6b64de2fb5 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -872,7 +872,7 @@ Style="{DynamicResource SideToggleSwitch}" ToolTip="{DynamicResource UseQuickLookToolTip}" /> -  +  diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 34007f5df05..69cbd33d490 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -578,6 +578,7 @@ private void TogglePreview() { if (PreviewVisible) { + // To deal with always preview HideInternalPreview(); } else if(Settings.UseExternalPreview && CanExternalPreviewSelectedResult(out var path)) From 7033bce433fd6eb41e5a2c6e1c3327bf496e43ae Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 22 May 2023 20:36:34 +0800 Subject: [PATCH 29/44] Add minimum gap time between fail toasts --- Flow.Launcher/Helper/QuickLookHelper.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs index 59c94ae2f0a..f5023cfc7fd 100644 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -13,12 +13,15 @@ namespace Flow.Launcher.Helper internal static class QuickLookHelper { private const int TIMEOUT = 500; + private static DateTime lastNotificationTime = DateTime.MinValue; + private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; - + + /// /// Toggle QuickLook /// @@ -64,14 +67,15 @@ public static async Task OpenQuickLookAsync(string path) /// Switch QuickLook to preview another file if it's on /// /// File path to preview + /// Send notification if fail /// - public static async Task SwitchQuickLookAsync(string path) + public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = false) { if (string.IsNullOrEmpty(path)) return false; bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); - if (!success) + if (sendFailToast && !success) { ShowQuickLookUnavailableToast(); } @@ -141,8 +145,12 @@ static async Task QuickLookServerAvailable() private static void ShowQuickLookUnavailableToast() { - Notification.Show(InternationalizationManager.Instance.GetTranslation("QuickLookFail"), + if (lastNotificationTime.AddSeconds(10) < DateTime.Now) + { + Notification.Show(InternationalizationManager.Instance.GetTranslation("QuickLookFail"), InternationalizationManager.Instance.GetTranslation("QuickLookFailTips")); + lastNotificationTime = DateTime.Now; + } } } } From 2bb398d95ef87b1c1eef777be368601b1a319b39 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:08:22 +0800 Subject: [PATCH 30/44] Add sendFailToast arg --- Flow.Launcher/Helper/QuickLookHelper.cs | 15 ++++++++------- Flow.Launcher/ViewModel/MainViewModel.cs | 21 +++++++++++---------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs index f5023cfc7fd..85682b1d677 100644 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -26,37 +26,38 @@ internal static class QuickLookHelper /// Toggle QuickLook /// /// File path to preview + /// Send toast when fails. /// - public static async Task ToggleQuickLookAsync(string path) + public static async Task ToggleQuickLookAsync(string path, bool sendFailToast = true) { if (string.IsNullOrEmpty(path)) return false; bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); - if (!success) + if (sendFailToast && !success) { ShowQuickLookUnavailableToast(); } return success; } - public static async Task CloseQuickLookAsync() + public static async Task CloseQuickLookAsync(bool sendFailToast = true) { bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); - if (!success) + if (sendFailToast && !success) { ShowQuickLookUnavailableToast(); } return success; } - public static async Task OpenQuickLookAsync(string path) + public static async Task OpenQuickLookAsync(string path, bool sendFailToast = true) { if (string.IsNullOrEmpty(path)) return false; bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); - if (!success) + if (sendFailToast && !success) { ShowQuickLookUnavailableToast(); } @@ -69,7 +70,7 @@ public static async Task OpenQuickLookAsync(string path) /// File path to preview /// Send notification if fail /// - public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = false) + public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = true) { if (string.IsNullOrEmpty(path)) return false; diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 69cbd33d490..7df2ff038a1 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -612,29 +612,29 @@ private async Task ToggleExternalPreviewAsync(string path) } } - private async Task OpenExternalPreviewAsync(string path) + private async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true) { - bool success = await QuickLookHelper.OpenQuickLookAsync(path).ConfigureAwait(false); + bool success = await QuickLookHelper.OpenQuickLookAsync(path, sendFailToast).ConfigureAwait(false); if (success) { - ExternalPreviewOpen = true; + ExternalPreviewOpen = false; } } - private async Task CloseExternalPreviewAsync() + private async Task CloseExternalPreviewAsync(bool sendFailToast = true) { - bool success = await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); + bool success = await QuickLookHelper.CloseQuickLookAsync(sendFailToast).ConfigureAwait(false); if (success) { ExternalPreviewOpen = false; } } - - private async Task SwitchExternalPreviewAsync(string path) + + private async Task SwitchExternalPreviewAsync(string path, bool sendFailToast = true) { // Switches preview content // When external is off, do nothing - _ = QuickLookHelper.SwitchQuickLookAsync(path).ConfigureAwait(false); + _ = QuickLookHelper.SwitchQuickLookAsync(path, sendFailToast).ConfigureAwait(false); } private void ShowInternalPreview() @@ -672,7 +672,7 @@ private void UpdatePreview() { if (CanExternalPreviewSelectedResult(out var path)) { - _ = SwitchExternalPreviewAsync(path); + _ = SwitchExternalPreviewAsync(path, false); } else { @@ -1090,7 +1090,8 @@ public async void Hide() // Trick for no delay MainWindowOpacity = 0; - _ = CloseExternalPreviewAsync(); + if (Settings.UseExternalPreview) + _ = CloseExternalPreviewAsync(false); if (!SelectedIsFromQueryResults()) { From e248fcbca58e3282518a67b4b90bbc743c243a75 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:26:37 +0800 Subject: [PATCH 31/44] Fix build error introduced in 1eae69304f3f961b70fe224a58d870d85bcb08ad Recover FilePath in 71376b8b --- Flow.Launcher.Plugin/Result.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 588bf9c7bc6..a231a0ed649 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -265,6 +265,11 @@ public record PreviewInfo /// public IconDelegate PreviewDelegate { get; set; } = null; + /// + /// File path of the result. For third-party preview programs such as QuickLook. + /// + public string FilePath { get; set; } = null; + /// /// Default instance of /// From 8da88bd22a41f42a23e03f9d1fa91dcad971e378 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 5 Aug 2023 22:29:19 +0800 Subject: [PATCH 32/44] Hide fail toast for closing --- Flow.Launcher/Helper/QuickLookHelper.cs | 6 +----- Flow.Launcher/ViewModel/MainViewModel.cs | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs index 85682b1d677..cc1e8d63fa8 100644 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ b/Flow.Launcher/Helper/QuickLookHelper.cs @@ -41,13 +41,9 @@ public static async Task ToggleQuickLookAsync(string path, bool sendFailTo return success; } - public static async Task CloseQuickLookAsync(bool sendFailToast = true) + public static async Task CloseQuickLookAsync() { bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); - if (sendFailToast && !success) - { - ShowQuickLookUnavailableToast(); - } return success; } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index af50954a621..050b9ff099c 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -632,9 +632,9 @@ private async Task OpenExternalPreviewAsync(string path, bool sendFailToast = tr } } - private async Task CloseExternalPreviewAsync(bool sendFailToast = true) + private async Task CloseExternalPreviewAsync() { - bool success = await QuickLookHelper.CloseQuickLookAsync(sendFailToast).ConfigureAwait(false); + bool success = await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); if (success) { ExternalPreviewOpen = false; @@ -1105,7 +1105,7 @@ public async void Hide() MainWindowOpacity = 0; if (Settings.UseExternalPreview) - _ = CloseExternalPreviewAsync(false); + _ = CloseExternalPreviewAsync(); if (!SelectedIsFromQueryResults()) { From 666211dd4cecae901f0a5509447fc07e2a72feaa Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 28 May 2024 21:27:48 +1000 Subject: [PATCH 33/44] add plugin support for external preview --- Flow.Launcher.Core/Plugin/PluginManager.cs | 32 +++++++++++++++++++ .../Interfaces/IAsyncExternalPreview.cs | 15 +++++++++ 2 files changed, 47 insertions(+) create mode 100644 Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index f8c9a3f1746..5d675150734 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -88,6 +88,38 @@ public static async Task ReloadDataAsync() }).ToArray()); } + public static async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true) + { + await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch + { + IAsyncExternalPreview p => p.OpenPreviewAsync(path, sendFailToast), + _ => Task.CompletedTask, + }).ToArray()); + } + + public static async Task CloseExternalPreviewAsync() + { + await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch + { + IAsyncExternalPreview p => p.ClosePreviewAsync(), + _ => Task.CompletedTask, + }).ToArray()); + } + + public static async Task SwitchExternalPreviewAsync(string path, bool sendFailToast = true) + { + await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch + { + IAsyncExternalPreview p => p.SwitchPreviewAsync(path, sendFailToast), + _ => Task.CompletedTask, + }).ToArray()); + } + + public static bool UseExternalPreview() + { + return GetPluginsForInterface().Any(x => !x.Metadata.Disabled); + } + static PluginManager() { // validate user directory diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs new file mode 100644 index 00000000000..9ee05b46c0c --- /dev/null +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace Flow.Launcher.Plugin +{ + public interface IAsyncExternalPreview: IFeatures + { + public Task TogglePreviewAsync(string path); + + public Task OpenPreviewAsync(string path, bool sendFailToast = true); + + public Task ClosePreviewAsync(); + + public Task SwitchPreviewAsync(string path, bool sendFailToast = true); + } +} From 8da1313e5ff4e1a6cbe9ecb334f5262c672f71c8 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 28 May 2024 21:31:29 +1000 Subject: [PATCH 34/44] update external preview methods to use the external preview interface --- Flow.Launcher/ViewModel/MainViewModel.cs | 35 ++++++------------------ 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 050b9ff099c..c408a5176ab 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -614,38 +614,21 @@ private void ToggleInternalPreview() } } - private async Task ToggleExternalPreviewAsync(string path) + private void OpenExternalPreview(string path, bool sendFailToast = true) { - bool success = await QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); - if (success) - { - ExternalPreviewOpen = !ExternalPreviewOpen; - } + _ = PluginManager.OpenExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false); + ExternalPreviewVisible = true; } - private async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true) + private void CloseExternalPreview() { - bool success = await QuickLookHelper.OpenQuickLookAsync(path, sendFailToast).ConfigureAwait(false); - if (success) - { - ExternalPreviewOpen = false; - } - } - - private async Task CloseExternalPreviewAsync() - { - bool success = await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); - if (success) - { - ExternalPreviewOpen = false; - } + _ = PluginManager.CloseExternalPreviewAsync().ConfigureAwait(false); + ExternalPreviewVisible = false; } - private async Task SwitchExternalPreviewAsync(string path, bool sendFailToast = true) + private void SwitchExternalPreview(string path, bool sendFailToast = true) { - // Switches preview content - // When external is off, do nothing - _ = QuickLookHelper.SwitchQuickLookAsync(path, sendFailToast).ConfigureAwait(false); + _ = PluginManager.SwitchExternalPreviewAsync(path,sendFailToast).ConfigureAwait(false); } private void ShowInternalPreview() From 587536f0f5edb5a222e66839f6818bdc71779459 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 28 May 2024 21:38:02 +1000 Subject: [PATCH 35/44] updated PreviewVisible logic to base on ResultAreaColumn size --- Flow.Launcher/ViewModel/MainViewModel.cs | 36 +++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index c408a5176ab..c58aa9c72e4 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -572,25 +572,35 @@ public string PreviewHotkey public string Image => Constant.QueryTextBoxIconImagePath; public bool StartWithEnglishMode => Settings.AlwaysStartEn; - - public bool PreviewVisible { get; set; } = false; - - public int ResultAreaColumn { get; set; } = 1; - + #endregion #region Preview - // Not accurate - public bool ExternalPreviewOpen { get; set; } = false; - - [RelayCommand] - private void TogglePreview() + public bool InternalPreviewVisible { - if (PreviewVisible) + get { - // To deal with always preview - HideInternalPreview(); + if (ResultAreaColumn == ResultAreaColumnPreviewShown) + return true; + + if (ResultAreaColumn == ResultAreaColumnPreviewHidden) + return false; +#if DEBUG + throw new NotImplementedException("ResultAreaColumn should match ResultAreaColumnPreviewShown/ResultAreaColumnPreviewHidden value"); +#else + Log.Error("MainViewModel", "ResultAreaColumnPreviewHidden/ResultAreaColumnPreviewShown int value not implemented", "InternalPreviewVisible"); +#endif + return false; + } + } + + private static readonly int ResultAreaColumnPreviewShown = 1; + + private static readonly int ResultAreaColumnPreviewHidden = 3; + + public int ResultAreaColumn { get; set; } = ResultAreaColumnPreviewShown; + } else if(Settings.UseExternalPreview && CanExternalPreviewSelectedResult(out var path)) { From 53e4bbb22b327535e147d1a08be784c9aadb1ada Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 28 May 2024 21:41:59 +1000 Subject: [PATCH 36/44] updated internal and external preview's show and hide logic --- Flow.Launcher/MainWindow.xaml | 4 +- Flow.Launcher/MainWindow.xaml.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 103 ++++++++++++++++------- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 88e95aa692f..7be2f667d14 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -399,7 +399,7 @@ + Visibility="{Binding InternalPreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}"> Date: Wed, 29 May 2024 14:05:19 +1000 Subject: [PATCH 37/44] add QuickLook plugin for testing --- Flow.Launcher.sln | 55 ++++--- .../Helper/QuickLookHelper.cs | 153 +++++++++++++++++ Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 23 ++- .../Flow.Launcher.Plugin.QuickLook.csproj | 51 ++++++ .../Helpers/QuickLookHelper.cs | 154 ++++++++++++++++++ .../Images/app.png | Bin 0 -> 40421 bytes .../Languages/en.xaml | 14 ++ .../Flow.Launcher.Plugin.QuickLook/Main.cs | 52 ++++++ .../plugin.json | 12 ++ 9 files changed, 493 insertions(+), 21 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs create mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj create mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs create mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Images/app.png create mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml create mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs create mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 13e98833a9a..9b376ca1406 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -5,17 +5,18 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launcher\Flow.Launcher.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}" ProjectSection(ProjectDependencies) = postProject {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} + {403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E} {4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217} - {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4} - {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} + {5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A} + {588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E} - {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} + {758F3331-8D38-49F9-913C-60A18A8AEF3B} = {758F3331-8D38-49F9-913C-60A18A8AEF3B} {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37} - {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A} {A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26} - {5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A} - {403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E} - {588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3} + {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} + {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4} + {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Test", "Flow.Launcher.Test\Flow.Launcher.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}" @@ -70,6 +71,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Plugin EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.WindowsSettings", "Plugins\Flow.Launcher.Plugin.WindowsSettings\Flow.Launcher.Plugin.WindowsSettings.csproj", "{5043CECE-E6A7-4867-9CBE-02D27D83747A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flow.Launcher.Plugin.QuickLook", "plugins\Flow.Launcher.Plugin.QuickLook\Flow.Launcher.Plugin.QuickLook.csproj", "{758F3331-8D38-49F9-913C-60A18A8AEF3B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -80,8 +83,19 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -104,18 +118,6 @@ Global {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.Build.0 = Release|Any CPU {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.ActiveCfg = Release|Any CPU {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.Build.0 = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -285,6 +287,18 @@ Global {5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.Build.0 = Release|Any CPU {5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.ActiveCfg = Release|Any CPU {5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.Build.0 = Release|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x64.ActiveCfg = Debug|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x64.Build.0 = Debug|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x86.ActiveCfg = Debug|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x86.Build.0 = Debug|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|Any CPU.Build.0 = Release|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x64.ActiveCfg = Release|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x64.Build.0 = Release|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x86.ActiveCfg = Release|Any CPU + {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -302,6 +316,7 @@ Global {588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {5043CECE-E6A7-4867-9CBE-02D27D83747A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} + {758F3331-8D38-49F9-913C-60A18A8AEF3B} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs new file mode 100644 index 00000000000..f3f25122f41 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs @@ -0,0 +1,153 @@ +// Adapted from Files +// https://github.com/files-community/Files/blob/ad33c75c53382fcb9b16fa9cd66ae5399f3dff0b/src/Files.App/Helpers/QuickLookHelpers.cs +using System; +using System.IO.Pipes; +using System.IO; +using System.Security.Principal; +using System.Threading.Tasks; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Core.Resource; + +namespace Flow.Launcher.Plugin.Explorer.Helper +{ + internal static class QuickLookHelper + { + private const int TIMEOUT = 500; + private static DateTime lastNotificationTime = DateTime.MinValue; + + private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; + private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; + private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; + private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; + private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; + + + /// + /// Toggle QuickLook + /// + /// File path to preview + /// Send toast when fails. + /// + public static async Task ToggleQuickLookAsync(string path, bool sendFailToast = true) + { + if (string.IsNullOrEmpty(path)) + return false; + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); + if (sendFailToast && !success) + { + //ShowQuickLookUnavailableToast(); + } + return success; + } + + public static async Task CloseQuickLookAsync() + { + bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); + return success; + } + + public static async Task OpenQuickLookAsync(string path, bool sendFailToast = true) + { + if (string.IsNullOrEmpty(path)) + return false; + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); + if (sendFailToast && !success) + { + //ShowQuickLookUnavailableToast(); + } + return success; + } + + /// + /// Switch QuickLook to preview another file if it's on + /// + /// File path to preview + /// Send notification if fail + /// + public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = true) + { + if (string.IsNullOrEmpty(path)) + return false; + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); + if (sendFailToast && !success) + { + //ShowQuickLookUnavailableToast(); + } + return success; + } + + private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") + { + await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); + try + { + await client.ConnectAsync(TIMEOUT); + + await using var writer = new StreamWriter(client); + await writer.WriteLineAsync($"{message}|{arg}"); + await writer.FlushAsync(); + } + catch (TimeoutException) + { + client.Close(); + Log.Error($"{nameof(QuickLookHelper)}", "QuickLook timeout"); + return false; + } + catch (Exception e) + { + Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook error", e); + return false; + } + return true; + } + + public static async Task DetectQuickLookAvailabilityAsync() + { + static async Task QuickLookServerAvailable() + { + await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); + try + { + await client.ConnectAsync(TIMEOUT); + var serverInstances = client.NumberOfServerInstances; + + await using var writer = new StreamWriter(client); + await writer.WriteLineAsync($"{pipeMessageSwitch}|"); + await writer.FlushAsync(); + + return serverInstances; + } + catch (TimeoutException e) + { + client.Close(); + Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook connection timeout", e); + return 0; + } + } + + try + { + var result = await QuickLookServerAvailable(); + return result != 0; + } + catch (Exception e) + { + Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook unavailable", e); + return false; + } + } + + private static void ShowQuickLookUnavailableToast() + { + if (lastNotificationTime.AddSeconds(10) < DateTime.Now) + { + //Notification.Show(InternationalizationManager.Instance.GetTranslation("QuickLookFail"), + // InternationalizationManager.Instance.GetTranslation("QuickLookFailTips")); + lastNotificationTime = DateTime.Now; + } + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index e4056131d4b..2ed32a13b05 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -14,7 +14,7 @@ namespace Flow.Launcher.Plugin.Explorer { - public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n + public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IAsyncExternalPreview { internal static PluginInitContext Context { get; set; } @@ -87,6 +87,27 @@ public async Task> QueryAsync(Query query, CancellationToken token) } } + public async Task TogglePreviewAsync(string path) + { + bool success = await QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); + } + public async Task ClosePreviewAsync() + { + bool success = await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); + } + + public async Task SwitchPreviewAsync(string path, bool sendFailToast = true) + { + // Switches preview content + // When external is off, do nothing + _ = QuickLookHelper.SwitchQuickLookAsync(path, sendFailToast).ConfigureAwait(false); + } + + public async Task OpenPreviewAsync(string path, bool sendFailToast = true) + { + bool success = await QuickLookHelper.OpenQuickLookAsync(path, sendFailToast).ConfigureAwait(false); + } + public string GetTranslatedPluginTitle() { return Context.API.GetTranslation("plugin_explorer_plugin_name"); diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj b/Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj new file mode 100644 index 00000000000..b7e79f0a3c8 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj @@ -0,0 +1,51 @@ + + + + Library + net7.0-windows + true + false + warnings + + + + + ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.QuickLook + + + + + ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.QuickLook + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + PreserveNewest + Designer + MSBuild:Compile + + + + + + + + diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs b/Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs new file mode 100644 index 00000000000..dfdf04871e5 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs @@ -0,0 +1,154 @@ +using System; +using System.IO.Pipes; +using System.IO; +using System.Security.Principal; +using System.Threading.Tasks; + +namespace Flow.Launcher.Plugin.QuickLook.Helpers +{ + /// + /// Adapted from Files + /// https://github.com/files-community/Files/blob/ad33c75c53382fcb9b16fa9cd66ae5399f3dff0b/src/Files.App/Helpers/QuickLookHelpers.cs + /// + internal static class QuickLookHelper + { + private static readonly IPublicAPI api = Main.Context.API; + + private const int TIMEOUT = 500; + private static DateTime lastNotificationTime = DateTime.MinValue; + + private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; + private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; + private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; + private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; + private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; + + + /// + /// Toggle QuickLook + /// + /// File path to preview + /// Send toast when fails. + /// + public static async Task ToggleQuickLookAsync(string path, bool sendFailToast = true) + { + if (string.IsNullOrEmpty(path)) + return false; + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); + if (sendFailToast && !success) + { + ShowQuickLookUnavailableToast(); + } + return success; + } + + public static async Task CloseQuickLookAsync() + { + bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); + return success; + } + + public static async Task OpenQuickLookAsync(string path, bool sendFailToast = true) + { + if (string.IsNullOrEmpty(path)) + return false; + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); + if (sendFailToast && !success) + { + ShowQuickLookUnavailableToast(); + } + return success; + } + + /// + /// Switch QuickLook to preview another file if it's on + /// + /// File path to preview + /// Send notification if fail + /// + public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = true) + { + if (string.IsNullOrEmpty(path)) + return false; + + bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); + if (sendFailToast && !success) + { + ShowQuickLookUnavailableToast(); + } + return success; + } + + private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") + { + await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); + try + { + await client.ConnectAsync(TIMEOUT); + + await using var writer = new StreamWriter(client); + await writer.WriteLineAsync($"{message}|{arg}"); + await writer.FlushAsync(); + } + catch (TimeoutException e) + { + client.Close(); + api.LogException($"{nameof(QuickLookHelper)}", "QuickLook timeout", e); + return false; + } + catch (Exception e) + { + api.LogException($"{nameof(QuickLookHelper)}", "QuickLook error", e); + return false; + } + return true; + } + + public static async Task DetectQuickLookAvailabilityAsync() + { + static async Task QuickLookServerAvailable() + { + await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); + try + { + await client.ConnectAsync(TIMEOUT); + var serverInstances = client.NumberOfServerInstances; + + await using var writer = new StreamWriter(client); + await writer.WriteLineAsync($"{pipeMessageSwitch}|"); + await writer.FlushAsync(); + + return serverInstances; + } + catch (TimeoutException e) + { + client.Close(); + api.LogException($"{nameof(QuickLookHelper)}", "QuickLook connection timeout", e); + return 0; + } + } + + try + { + var result = await QuickLookServerAvailable(); + return result != 0; + } + catch (Exception e) + { + api.LogException($"{nameof(QuickLookHelper)}", "QuickLook unavailable", e); + return false; + } + } + + private static void ShowQuickLookUnavailableToast() + { + if (lastNotificationTime.AddSeconds(10) < DateTime.Now) + { + api.ShowMsgError(api.GetTranslation("quicklook_failed_to_launch"), api.GetTranslation("quicklook_fail_tips")); + lastNotificationTime = DateTime.Now; + } + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Images/app.png b/Plugins/Flow.Launcher.Plugin.QuickLook/Images/app.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8238c03c81329bf48c5ff3842309b3ee8a9d51 GIT binary patch literal 40421 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 literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml new file mode 100644 index 00000000000..283e1d5ec17 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml @@ -0,0 +1,14 @@ + + + + Explorer + Find and manage files and folders via Windows Search or Everything + + + Failed to launch QuickLook + Please check if QuickLook is running. + + diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs b/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs new file mode 100644 index 00000000000..3696ca434c1 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Flow.Launcher.Plugin.QuickLook.Helpers; + +namespace Flow.Launcher.Plugin.QuickLook +{ + public class Main : IAsyncPlugin, IAsyncExternalPreview, IPluginI18n + { + internal static PluginInitContext Context { get; set; } + + public Task InitAsync(PluginInitContext context) + { + Context = context; + + // prompt quicklook install if not found? + + return Task.CompletedTask; + } + + public async Task TogglePreviewAsync(string path) + { + await QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); + } + public async Task ClosePreviewAsync() + { + await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); + } + + public async Task SwitchPreviewAsync(string path, bool sendFailToast = true) + { + await QuickLookHelper.SwitchQuickLookAsync(path, sendFailToast).ConfigureAwait(false); + } + + public async Task OpenPreviewAsync(string path, bool sendFailToast = true) + { + await QuickLookHelper.OpenQuickLookAsync(path, sendFailToast).ConfigureAwait(false); + } + + public async Task> QueryAsync(Query query, CancellationToken token) => new List(); + + public string GetTranslatedPluginTitle() + { + return Context.API.GetTranslation("plugin_name"); + } + + public string GetTranslatedPluginDescription() + { + return Context.API.GetTranslation("plugin_description"); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json b/Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json new file mode 100644 index 00000000000..0a078e8be14 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json @@ -0,0 +1,12 @@ +{ + "ID": "e6a13bf1-5op9-2b96-a7fd-130b7vdt3d14", + "ActionKeywords": [ "*" ], + "Name": "QuickLook", + "Description": "Use QuickLook to preview files", + "Author": "Flow Launcher", + "Version": "1.0.0", + "Language": "csharp", + "Website": "https://github.com/Flow-Launcher/Flow.Launcher.Plugin.QuickLook", + "ExecuteFileName": "Flow.Launcher.Plugin.QuickLook.dll", + "IcoPath": "Images\\app.png" +} From 493a30e6c100fdf5462375742d43e2804e9ddbd5 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 29 May 2024 16:12:04 +1000 Subject: [PATCH 38/44] remove QuickLook related code from flow --- Flow.Launcher/Languages/en.xaml | 4 - Flow.Launcher/SettingWindow.xaml | 18 --- .../Helper/QuickLookHelper.cs | 153 ------------------ Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 23 +-- .../Languages/en.xaml | 4 +- 5 files changed, 3 insertions(+), 199 deletions(-) delete mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 1072c2e0287..d36a49538ee 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -81,8 +81,6 @@ 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. - Use QuickLook - Use QuickLook to preview file results. Shadow effect is not allowed while current theme has blur effect enabled @@ -318,8 +316,6 @@ Please wait... - Failed to launch QuickLook - Please check if QuickLook is running. Checking for new update diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index e98a6cfc803..1e886c022b2 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -859,24 +859,6 @@ - - - - - - - - -  - - - - diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs deleted file mode 100644 index f3f25122f41..00000000000 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/QuickLookHelper.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Adapted from Files -// https://github.com/files-community/Files/blob/ad33c75c53382fcb9b16fa9cd66ae5399f3dff0b/src/Files.App/Helpers/QuickLookHelpers.cs -using System; -using System.IO.Pipes; -using System.IO; -using System.Security.Principal; -using System.Threading.Tasks; -using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Core.Resource; - -namespace Flow.Launcher.Plugin.Explorer.Helper -{ - internal static class QuickLookHelper - { - private const int TIMEOUT = 500; - private static DateTime lastNotificationTime = DateTime.MinValue; - - private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; - private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; - private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; - private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; - private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; - - - /// - /// Toggle QuickLook - /// - /// File path to preview - /// Send toast when fails. - /// - public static async Task ToggleQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); - if (sendFailToast && !success) - { - //ShowQuickLookUnavailableToast(); - } - return success; - } - - public static async Task CloseQuickLookAsync() - { - bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); - return success; - } - - public static async Task OpenQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); - if (sendFailToast && !success) - { - //ShowQuickLookUnavailableToast(); - } - return success; - } - - /// - /// Switch QuickLook to preview another file if it's on - /// - /// File path to preview - /// Send notification if fail - /// - public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); - if (sendFailToast && !success) - { - //ShowQuickLookUnavailableToast(); - } - return success; - } - - private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") - { - await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); - try - { - await client.ConnectAsync(TIMEOUT); - - await using var writer = new StreamWriter(client); - await writer.WriteLineAsync($"{message}|{arg}"); - await writer.FlushAsync(); - } - catch (TimeoutException) - { - client.Close(); - Log.Error($"{nameof(QuickLookHelper)}", "QuickLook timeout"); - return false; - } - catch (Exception e) - { - Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook error", e); - return false; - } - return true; - } - - public static async Task DetectQuickLookAvailabilityAsync() - { - static async Task QuickLookServerAvailable() - { - await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); - try - { - await client.ConnectAsync(TIMEOUT); - var serverInstances = client.NumberOfServerInstances; - - await using var writer = new StreamWriter(client); - await writer.WriteLineAsync($"{pipeMessageSwitch}|"); - await writer.FlushAsync(); - - return serverInstances; - } - catch (TimeoutException e) - { - client.Close(); - Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook connection timeout", e); - return 0; - } - } - - try - { - var result = await QuickLookServerAvailable(); - return result != 0; - } - catch (Exception e) - { - Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook unavailable", e); - return false; - } - } - - private static void ShowQuickLookUnavailableToast() - { - if (lastNotificationTime.AddSeconds(10) < DateTime.Now) - { - //Notification.Show(InternationalizationManager.Instance.GetTranslation("QuickLookFail"), - // InternationalizationManager.Instance.GetTranslation("QuickLookFailTips")); - lastNotificationTime = DateTime.Now; - } - } - } -} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index 2ed32a13b05..e4056131d4b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -14,7 +14,7 @@ namespace Flow.Launcher.Plugin.Explorer { - public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n, IAsyncExternalPreview + public class Main : ISettingProvider, IAsyncPlugin, IContextMenu, IPluginI18n { internal static PluginInitContext Context { get; set; } @@ -87,27 +87,6 @@ public async Task> QueryAsync(Query query, CancellationToken token) } } - public async Task TogglePreviewAsync(string path) - { - bool success = await QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); - } - public async Task ClosePreviewAsync() - { - bool success = await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); - } - - public async Task SwitchPreviewAsync(string path, bool sendFailToast = true) - { - // Switches preview content - // When external is off, do nothing - _ = QuickLookHelper.SwitchQuickLookAsync(path, sendFailToast).ConfigureAwait(false); - } - - public async Task OpenPreviewAsync(string path, bool sendFailToast = true) - { - bool success = await QuickLookHelper.OpenQuickLookAsync(path, sendFailToast).ConfigureAwait(false); - } - public string GetTranslatedPluginTitle() { return Context.API.GetTranslation("plugin_explorer_plugin_name"); diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml index 283e1d5ec17..d6a760f27b9 100644 --- a/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml @@ -4,8 +4,8 @@ xmlns:system="clr-namespace:System;assembly=mscorlib"> - Explorer - Find and manage files and folders via Windows Search or Everything + QuickLook + Use QuickLook to preview files Failed to launch QuickLook From 9e39e48bf80d4c32745e0a498142e10a55418193 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 30 May 2024 21:49:44 +1000 Subject: [PATCH 39/44] minor clean up and update --- .../UserSettings/Settings.cs | 2 -- .../Interfaces/IAsyncExternalPreview.cs | 22 ++++++++++++++++--- Flow.Launcher.Plugin/Result.cs | 4 ++-- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 892cb38760e..67b2ae4061f 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -186,8 +186,6 @@ public CustomBrowserViewModel CustomBrowser /// public bool ShouldUsePinyin { get; set; } = false; - public bool UseExternalPreview { get; set; } = false; - public bool AlwaysPreview { get; set; } = false; public bool AlwaysStartEn { get; set; } = false; diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs index 9ee05b46c0c..575a422d9d9 100644 --- a/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs @@ -2,14 +2,30 @@ namespace Flow.Launcher.Plugin { - public interface IAsyncExternalPreview: IFeatures + /// + /// This interface is for plugins that wish to provide file preview (external preview) + /// via a third party app instead of the default preview. + /// + public interface IAsyncExternalPreview : IFeatures { - public Task TogglePreviewAsync(string path); - + /// + /// Method for opening/showing the preview. + /// + /// The file path to open the preview for + /// Whether to send a toast message notification on failure for the user public Task OpenPreviewAsync(string path, bool sendFailToast = true); + /// + /// Method for closing/hiding the preview. + /// public Task ClosePreviewAsync(); + /// + /// Method for switching the preview to the next file result. + /// This requires the external preview be already open/showing + /// + /// The file path to switch the preview for + /// Whether to send a toast message notification on failure for the user public Task SwitchPreviewAsync(string path, bool sendFailToast = true); } } diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 179fc5ef8c2..9b42b102176 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -261,7 +261,7 @@ public ValueTask ExecuteAsync(ActionContext context) /// Contains data used to populate the preview section of this result. /// public PreviewInfo Preview { get; set; } = PreviewInfo.Default; - + /// /// Info of the preview section of a /// @@ -291,7 +291,7 @@ public record PreviewInfo public IconDelegate PreviewDelegate { get; set; } = null; /// - /// File path of the result. For third-party preview programs such as QuickLook. + /// File path of the result. For third-party programs providing external preview. /// public string FilePath { get; set; } = null; diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 7f3ec1ff311..94a3a9695bf 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -768,7 +768,7 @@ public bool InternalPreviewVisible public int ResultAreaColumn { get; set; } = ResultAreaColumnPreviewShown; // This is not a reliable indicator of whether external preview is visible due to the - // ability of manually closing/exiting the external preview program, and this does not inform flow that + // ability of manually closing/exiting the external preview program which, does not inform flow that // preview is no longer available. public bool ExternalPreviewVisible { get; set; } = false; From f4f519b5ae0400c795c74b39ab63f2ee2c69e6f9 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 31 May 2024 17:02:54 +1000 Subject: [PATCH 40/44] minor clean up --- Flow.Launcher/ViewModel/MainViewModel.cs | 30 ++++++++++-------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 94a3a9695bf..71a390522a0 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -803,15 +803,13 @@ private void HidePreview() [RelayCommand] private void TogglePreview() { - switch (InternalPreviewVisible || ExternalPreviewVisible) + if (InternalPreviewVisible || ExternalPreviewVisible) { - case true: - HidePreview(); - break; - - case false: - ShowPreview(); - break; + HidePreview(); + } + else + { + ShowPreview(); } } @@ -857,15 +855,13 @@ private void HideInternalPreview() public void ResetPreview() { - switch (Settings.AlwaysPreview) + if (Settings.AlwaysPreview) { - case true: - ShowPreview(); - break; - - case false: - HidePreview(); - break; + ShowPreview(); + } + else + { + HidePreview(); } } @@ -885,7 +881,7 @@ private void UpdatePreview() break; case true - when !ExternalPreviewVisible && Settings.AlwaysPreview && CanExternalPreviewSelectedResult(out var path): + when !ExternalPreviewVisible && Settings.AlwaysPreview && CanExternalPreviewSelectedResult(out var _): ShowPreview(); break; From 2e47da4ad98ed520e61357960e619f3ebe06ea51 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 6 Jun 2024 12:36:26 +1000 Subject: [PATCH 41/44] allow internal preview when external not available --- Flow.Launcher/ViewModel/MainViewModel.cs | 62 +++++++++++++++--------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 7097239e19a..6df44e3f10e 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -782,19 +782,27 @@ private void ShowPreview() { var useExternalPreview = PluginManager.UseExternalPreview(); - if (!useExternalPreview) - ShowInternalPreview(); - - if (useExternalPreview) + switch (useExternalPreview) { - // Internal preview may still be on when user switches to external - if (InternalPreviewVisible) - HideInternalPreview(); - - if (CanExternalPreviewSelectedResult(out var path)) + case true + when CanExternalPreviewSelectedResult(out var path): + // Internal preview may still be on when user switches to external + if (InternalPreviewVisible) + HideInternalPreview(); OpenExternalPreview(path); - } + break; + case true + when !CanExternalPreviewSelectedResult(out var _): + if (ExternalPreviewVisible) + CloseExternalPreview(); + ShowInternalPreview(); + break; + + case false: + ShowInternalPreview(); + break; + } } private void HidePreview() @@ -873,27 +881,33 @@ public void ResetPreview() private void UpdatePreview() { - if (InternalPreviewVisible) - { - Results.SelectedItem?.LoadPreviewImage(); - return; - } - switch (PluginManager.UseExternalPreview()) { case true - when ExternalPreviewVisible && CanExternalPreviewSelectedResult(out var path): - SwitchExternalPreview(path, false); + when CanExternalPreviewSelectedResult(out var path): + if (ExternalPreviewVisible) + { + SwitchExternalPreview(path, false); + } + else if (InternalPreviewVisible) + { + HideInternalPreview(); + OpenExternalPreview(path); + } break; case true - when !ExternalPreviewVisible && Settings.AlwaysPreview && CanExternalPreviewSelectedResult(out var _): - ShowPreview(); + when !CanExternalPreviewSelectedResult(out var _): + if (ExternalPreviewVisible) + { + CloseExternalPreview(); + ShowInternalPreview(); + } break; - case true - when !CanExternalPreviewSelectedResult(out var _): - HidePreview(); + case false + when InternalPreviewVisible: + Results.SelectedItem?.LoadPreviewImage(); break; } } From e6f0f28ca560f460ff4b75e7a899e7bd3be6c499 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 9 Jun 2024 17:25:58 +0800 Subject: [PATCH 42/44] Don't show external preview when always preview enabled --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6df44e3f10e..58946fc4ce0 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -871,7 +871,7 @@ public void ResetPreview() { if (Settings.AlwaysPreview) { - ShowPreview(); + ShowInternalPreview(); } else { From 4dbecb1b5a720a0b2d2c2322043ec9ec32fd60c1 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 12 Jun 2024 14:14:25 +1000 Subject: [PATCH 43/44] allow preview plugin to override the AlwaysPreview setting --- Flow.Launcher.Core/Plugin/PluginManager.cs | 12 ++++++++++- .../Interfaces/IAsyncExternalPreview.cs | 9 ++++++++ Flow.Launcher/ViewModel/MainViewModel.cs | 21 ++++++++++++------- .../Flow.Launcher.Plugin.QuickLook/Main.cs | 2 ++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 837de90f12b..a827e9e7ade 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Core.ExternalPlugins; +using Flow.Launcher.Core.ExternalPlugins; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -122,6 +122,16 @@ public static bool UseExternalPreview() return GetPluginsForInterface().Any(x => !x.Metadata.Disabled); } + public static bool AllowAlwaysPreview() + { + var plugin = GetPluginsForInterface().FirstOrDefault(x => !x.Metadata.Disabled); + + if (plugin is null) + return false; + + return ((IAsyncExternalPreview)plugin.Plugin).AllowAlwaysPreview(); + } + static PluginManager() { // validate user directory diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs index 575a422d9d9..cc4f94c569c 100644 --- a/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncExternalPreview.cs @@ -27,5 +27,14 @@ public interface IAsyncExternalPreview : IFeatures /// The file path to switch the preview for /// Whether to send a toast message notification on failure for the user public Task SwitchPreviewAsync(string path, bool sendFailToast = true); + + /// + /// Allows the preview plugin to override the AlwaysPreview setting. Typically useful if plugin's preview does not + /// fully work well with being shown together when the query window appears with results. + /// When AlwaysPreview setting is on and this is set to false, the preview will not be shown when query + /// window appears with results, instead the internal preview will be shown. + /// + /// + public bool AllowAlwaysPreview(); } } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 58946fc4ce0..83ff6f064c4 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -869,13 +869,20 @@ private void HideInternalPreview() public void ResetPreview() { - if (Settings.AlwaysPreview) + switch (Settings.AlwaysPreview) { - ShowInternalPreview(); - } - else - { - HidePreview(); + case true + when PluginManager.AllowAlwaysPreview() && CanExternalPreviewSelectedResult(out var path): + OpenExternalPreview(path); + break; + + case true: + ShowInternalPreview(); + break; + + case false: + HidePreview(); + break; } } diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs b/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs index 3696ca434c1..4388ad3d9a0 100644 --- a/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs @@ -39,6 +39,8 @@ public async Task OpenPreviewAsync(string path, bool sendFailToast = true) public async Task> QueryAsync(Query query, CancellationToken token) => new List(); + public bool AllowAlwaysPreview() => false; + public string GetTranslatedPluginTitle() { return Context.API.GetTranslation("plugin_name"); From 7f31fefe98722725b89f4ab7d6c1e179fec29e8a Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 16 Jun 2024 12:59:52 +1000 Subject: [PATCH 44/44] remove QuickLook plugin test project --- Flow.Launcher.sln | 55 +++---- Flow.Launcher/Helper/QuickLookHelper.cs | 153 ----------------- .../Flow.Launcher.Plugin.QuickLook.csproj | 51 ------ .../Helpers/QuickLookHelper.cs | 154 ------------------ .../Images/app.png | Bin 40421 -> 0 bytes .../Languages/en.xaml | 14 -- .../Flow.Launcher.Plugin.QuickLook/Main.cs | 54 ------ .../plugin.json | 12 -- 8 files changed, 20 insertions(+), 473 deletions(-) delete mode 100644 Flow.Launcher/Helper/QuickLookHelper.cs delete mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj delete mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs delete mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Images/app.png delete mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml delete mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs delete mode 100644 Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 0f195ff0e6d..e44b23232fb 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -5,18 +5,17 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launcher\Flow.Launcher.csproj", "{DB90F671-D861-46BB-93A3-F1304F5BA1C5}" ProjectSection(ProjectDependencies) = postProject {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} - {403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E} {4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217} - {5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A} - {588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3} + {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4} + {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E} - {758F3331-8D38-49F9-913C-60A18A8AEF3B} = {758F3331-8D38-49F9-913C-60A18A8AEF3B} - {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37} - {A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} - {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F} - {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4} + {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37} {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A} + {A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26} + {5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A} + {403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E} + {588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Test", "Flow.Launcher.Test\Flow.Launcher.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}" @@ -72,8 +71,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Plugin EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.WindowsSettings", "Plugins\Flow.Launcher.Plugin.WindowsSettings\Flow.Launcher.Plugin.WindowsSettings.csproj", "{5043CECE-E6A7-4867-9CBE-02D27D83747A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flow.Launcher.Plugin.QuickLook", "plugins\Flow.Launcher.Plugin.QuickLook\Flow.Launcher.Plugin.QuickLook.csproj", "{758F3331-8D38-49F9-913C-60A18A8AEF3B}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -84,19 +81,8 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU - {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -119,6 +105,18 @@ Global {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.Build.0 = Release|Any CPU {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.ActiveCfg = Release|Any CPU {8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.Build.0 = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.ActiveCfg = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x64.Build.0 = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.ActiveCfg = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Debug|x86.Build.0 = Debug|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|Any CPU.Build.0 = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.ActiveCfg = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x64.Build.0 = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.ActiveCfg = Release|Any CPU + {DB90F671-D861-46BB-93A3-F1304F5BA1C5}.Release|x86.Build.0 = Release|Any CPU {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -288,18 +286,6 @@ Global {5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.Build.0 = Release|Any CPU {5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.ActiveCfg = Release|Any CPU {5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.Build.0 = Release|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x64.ActiveCfg = Debug|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x64.Build.0 = Debug|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x86.ActiveCfg = Debug|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Debug|x86.Build.0 = Debug|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|Any CPU.Build.0 = Release|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x64.ActiveCfg = Release|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x64.Build.0 = Release|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x86.ActiveCfg = Release|Any CPU - {758F3331-8D38-49F9-913C-60A18A8AEF3B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -317,7 +303,6 @@ Global {588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {5043CECE-E6A7-4867-9CBE-02D27D83747A} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} - {758F3331-8D38-49F9-913C-60A18A8AEF3B} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED} diff --git a/Flow.Launcher/Helper/QuickLookHelper.cs b/Flow.Launcher/Helper/QuickLookHelper.cs deleted file mode 100644 index cc1e8d63fa8..00000000000 --- a/Flow.Launcher/Helper/QuickLookHelper.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Adapted from Files -// https://github.com/files-community/Files/blob/ad33c75c53382fcb9b16fa9cd66ae5399f3dff0b/src/Files.App/Helpers/QuickLookHelpers.cs -using System; -using System.IO.Pipes; -using System.IO; -using System.Security.Principal; -using System.Threading.Tasks; -using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Core.Resource; - -namespace Flow.Launcher.Helper -{ - internal static class QuickLookHelper - { - private const int TIMEOUT = 500; - private static DateTime lastNotificationTime = DateTime.MinValue; - - private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; - private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; - private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; - private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; - private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; - - - /// - /// Toggle QuickLook - /// - /// File path to preview - /// Send toast when fails. - /// - public static async Task ToggleQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); - if (sendFailToast && !success) - { - ShowQuickLookUnavailableToast(); - } - return success; - } - - public static async Task CloseQuickLookAsync() - { - bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); - return success; - } - - public static async Task OpenQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); - if (sendFailToast && !success) - { - ShowQuickLookUnavailableToast(); - } - return success; - } - - /// - /// Switch QuickLook to preview another file if it's on - /// - /// File path to preview - /// Send notification if fail - /// - public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); - if (sendFailToast && !success) - { - ShowQuickLookUnavailableToast(); - } - return success; - } - - private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") - { - await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); - try - { - await client.ConnectAsync(TIMEOUT); - - await using var writer = new StreamWriter(client); - await writer.WriteLineAsync($"{message}|{arg}"); - await writer.FlushAsync(); - } - catch (TimeoutException) - { - client.Close(); - Log.Error($"{nameof(QuickLookHelper)}", "QuickLook timeout"); - return false; - } - catch (Exception e) - { - Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook error", e); - return false; - } - return true; - } - - public static async Task DetectQuickLookAvailabilityAsync() - { - static async Task QuickLookServerAvailable() - { - await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); - try - { - await client.ConnectAsync(TIMEOUT); - var serverInstances = client.NumberOfServerInstances; - - await using var writer = new StreamWriter(client); - await writer.WriteLineAsync($"{pipeMessageSwitch}|"); - await writer.FlushAsync(); - - return serverInstances; - } - catch (TimeoutException e) - { - client.Close(); - Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook connection timeout", e); - return 0; - } - } - - try - { - var result = await QuickLookServerAvailable(); - return result != 0; - } - catch (Exception e) - { - Log.Exception($"{nameof(QuickLookHelper)}", "QuickLook unavailable", e); - return false; - } - } - - private static void ShowQuickLookUnavailableToast() - { - if (lastNotificationTime.AddSeconds(10) < DateTime.Now) - { - Notification.Show(InternationalizationManager.Instance.GetTranslation("QuickLookFail"), - InternationalizationManager.Instance.GetTranslation("QuickLookFailTips")); - lastNotificationTime = DateTime.Now; - } - } - } -} diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj b/Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj deleted file mode 100644 index b7e79f0a3c8..00000000000 --- a/Plugins/Flow.Launcher.Plugin.QuickLook/Flow.Launcher.Plugin.QuickLook.csproj +++ /dev/null @@ -1,51 +0,0 @@ - - - - Library - net7.0-windows - true - false - warnings - - - - - ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.QuickLook - - - - - ..\..\Output\Release\Plugins\Flow.Launcher.Plugin.QuickLook - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - - - - - PreserveNewest - Designer - MSBuild:Compile - - - - - - - - diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs b/Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs deleted file mode 100644 index dfdf04871e5..00000000000 --- a/Plugins/Flow.Launcher.Plugin.QuickLook/Helpers/QuickLookHelper.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.IO.Pipes; -using System.IO; -using System.Security.Principal; -using System.Threading.Tasks; - -namespace Flow.Launcher.Plugin.QuickLook.Helpers -{ - /// - /// Adapted from Files - /// https://github.com/files-community/Files/blob/ad33c75c53382fcb9b16fa9cd66ae5399f3dff0b/src/Files.App/Helpers/QuickLookHelpers.cs - /// - internal static class QuickLookHelper - { - private static readonly IPublicAPI api = Main.Context.API; - - private const int TIMEOUT = 500; - private static DateTime lastNotificationTime = DateTime.MinValue; - - private static readonly string pipeName = $"QuickLook.App.Pipe.{WindowsIdentity.GetCurrent().User?.Value}"; - private static readonly string pipeMessageSwitch = "QuickLook.App.PipeMessages.Switch"; - private static readonly string pipeMessageToggle = "QuickLook.App.PipeMessages.Toggle"; - private static readonly string pipeMessageClose = "QuickLook.App.PipeMessages.Close"; - private static readonly string pipeMessageInvoke = "QuickLook.App.PipeMessages.Invoke"; - - - /// - /// Toggle QuickLook - /// - /// File path to preview - /// Send toast when fails. - /// - public static async Task ToggleQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageToggle, path); - if (sendFailToast && !success) - { - ShowQuickLookUnavailableToast(); - } - return success; - } - - public static async Task CloseQuickLookAsync() - { - bool success = await SendQuickLookPipeMsgAsync(pipeMessageClose); - return success; - } - - public static async Task OpenQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageInvoke, path); - if (sendFailToast && !success) - { - ShowQuickLookUnavailableToast(); - } - return success; - } - - /// - /// Switch QuickLook to preview another file if it's on - /// - /// File path to preview - /// Send notification if fail - /// - public static async Task SwitchQuickLookAsync(string path, bool sendFailToast = true) - { - if (string.IsNullOrEmpty(path)) - return false; - - bool success = await SendQuickLookPipeMsgAsync(pipeMessageSwitch, path); - if (sendFailToast && !success) - { - ShowQuickLookUnavailableToast(); - } - return success; - } - - private static async Task SendQuickLookPipeMsgAsync(string message, string arg = "") - { - await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); - try - { - await client.ConnectAsync(TIMEOUT); - - await using var writer = new StreamWriter(client); - await writer.WriteLineAsync($"{message}|{arg}"); - await writer.FlushAsync(); - } - catch (TimeoutException e) - { - client.Close(); - api.LogException($"{nameof(QuickLookHelper)}", "QuickLook timeout", e); - return false; - } - catch (Exception e) - { - api.LogException($"{nameof(QuickLookHelper)}", "QuickLook error", e); - return false; - } - return true; - } - - public static async Task DetectQuickLookAvailabilityAsync() - { - static async Task QuickLookServerAvailable() - { - await using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); - try - { - await client.ConnectAsync(TIMEOUT); - var serverInstances = client.NumberOfServerInstances; - - await using var writer = new StreamWriter(client); - await writer.WriteLineAsync($"{pipeMessageSwitch}|"); - await writer.FlushAsync(); - - return serverInstances; - } - catch (TimeoutException e) - { - client.Close(); - api.LogException($"{nameof(QuickLookHelper)}", "QuickLook connection timeout", e); - return 0; - } - } - - try - { - var result = await QuickLookServerAvailable(); - return result != 0; - } - catch (Exception e) - { - api.LogException($"{nameof(QuickLookHelper)}", "QuickLook unavailable", e); - return false; - } - } - - private static void ShowQuickLookUnavailableToast() - { - if (lastNotificationTime.AddSeconds(10) < DateTime.Now) - { - api.ShowMsgError(api.GetTranslation("quicklook_failed_to_launch"), api.GetTranslation("quicklook_fail_tips")); - lastNotificationTime = DateTime.Now; - } - } - } -} diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Images/app.png b/Plugins/Flow.Launcher.Plugin.QuickLook/Images/app.png deleted file mode 100644 index 5a8238c03c81329bf48c5ff3842309b3ee8a9d51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40421 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 diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml deleted file mode 100644 index d6a760f27b9..00000000000 --- a/Plugins/Flow.Launcher.Plugin.QuickLook/Languages/en.xaml +++ /dev/null @@ -1,14 +0,0 @@ - - - - QuickLook - Use QuickLook to preview files - - - Failed to launch QuickLook - Please check if QuickLook is running. - - diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs b/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs deleted file mode 100644 index 4388ad3d9a0..00000000000 --- a/Plugins/Flow.Launcher.Plugin.QuickLook/Main.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Flow.Launcher.Plugin.QuickLook.Helpers; - -namespace Flow.Launcher.Plugin.QuickLook -{ - public class Main : IAsyncPlugin, IAsyncExternalPreview, IPluginI18n - { - internal static PluginInitContext Context { get; set; } - - public Task InitAsync(PluginInitContext context) - { - Context = context; - - // prompt quicklook install if not found? - - return Task.CompletedTask; - } - - public async Task TogglePreviewAsync(string path) - { - await QuickLookHelper.ToggleQuickLookAsync(path).ConfigureAwait(false); - } - public async Task ClosePreviewAsync() - { - await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); - } - - public async Task SwitchPreviewAsync(string path, bool sendFailToast = true) - { - await QuickLookHelper.SwitchQuickLookAsync(path, sendFailToast).ConfigureAwait(false); - } - - public async Task OpenPreviewAsync(string path, bool sendFailToast = true) - { - await QuickLookHelper.OpenQuickLookAsync(path, sendFailToast).ConfigureAwait(false); - } - - public async Task> QueryAsync(Query query, CancellationToken token) => new List(); - - public bool AllowAlwaysPreview() => false; - - public string GetTranslatedPluginTitle() - { - return Context.API.GetTranslation("plugin_name"); - } - - public string GetTranslatedPluginDescription() - { - return Context.API.GetTranslation("plugin_description"); - } - } -} diff --git a/Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json b/Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json deleted file mode 100644 index 0a078e8be14..00000000000 --- a/Plugins/Flow.Launcher.Plugin.QuickLook/plugin.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "ID": "e6a13bf1-5op9-2b96-a7fd-130b7vdt3d14", - "ActionKeywords": [ "*" ], - "Name": "QuickLook", - "Description": "Use QuickLook to preview files", - "Author": "Flow Launcher", - "Version": "1.0.0", - "Language": "csharp", - "Website": "https://github.com/Flow-Launcher/Flow.Launcher.Plugin.QuickLook", - "ExecuteFileName": "Flow.Launcher.Plugin.QuickLook.dll", - "IcoPath": "Images\\app.png" -}