diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index f8c9a3f17..5d6751507 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -88,6 +88,38 @@ namespace Flow.Launcher.Core.Plugin }).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 000000000..9ee05b46c --- /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); + } +} diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 88e95aa69..7be2f667d 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -399,7 +399,7 @@ + Visibility="{Binding InternalPreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}"> 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; + public bool InternalPreviewVisible + { + get + { + 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; + + // 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 + // preview is no longer available. + public bool ExternalPreviewVisible { get; set; } = false; + + private void ShowPreview() + { + var useExternalPreview = PluginManager.UseExternalPreview(); + + if (!useExternalPreview) + ShowInternalPreview(); + + if (useExternalPreview) + { + // Internal preview may still be on when user switches to external + if (InternalPreviewVisible) + HideInternalPreview(); + + if (CanExternalPreviewSelectedResult(out var path)) + OpenExternalPreview(path); + } + + } + + private void HidePreview() + { + if (PluginManager.UseExternalPreview()) + CloseExternalPreview(); + + if (InternalPreviewVisible) + HideInternalPreview(); + } [RelayCommand] private void TogglePreview() { - if (PreviewVisible) + switch (InternalPreviewVisible || ExternalPreviewVisible) { - // To deal with always preview - HideInternalPreview(); - } - else if(Settings.UseExternalPreview && CanExternalPreviewSelectedResult(out var path)) - { - _ = ToggleExternalPreviewAsync(path); - } - else - { - ShowInternalPreview(); + case true: + HidePreview(); + break; + + case false: + ShowPreview(); + break; } } private void ToggleInternalPreview() { - if (!PreviewVisible) + if (!InternalPreviewVisible) { ShowInternalPreview(); } @@ -614,81 +661,72 @@ namespace Flow.Launcher.ViewModel } } - 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; - } + _ = PluginManager.CloseExternalPreviewAsync().ConfigureAwait(false); + ExternalPreviewVisible = false; } - private async Task CloseExternalPreviewAsync() + private void SwitchExternalPreview(string path, bool sendFailToast = true) { - bool success = await QuickLookHelper.CloseQuickLookAsync().ConfigureAwait(false); - if (success) - { - ExternalPreviewOpen = false; - } - } - - private async Task SwitchExternalPreviewAsync(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() { - ResultAreaColumn = 1; - PreviewVisible = true; + ResultAreaColumn = ResultAreaColumnPreviewShown; Results.SelectedItem?.LoadPreviewImage(); } private void HideInternalPreview() { - ResultAreaColumn = 3; - PreviewVisible = false; + ResultAreaColumn = ResultAreaColumnPreviewHidden; } public void ResetPreview() { - if (Settings.AlwaysPreview == true && !PreviewVisible) + switch (Settings.AlwaysPreview) { - ShowInternalPreview(); - } - else - { - HideInternalPreview(); + case true: + ShowPreview(); + break; + + case false: + HidePreview(); + break; } } private void UpdatePreview() { - if (PreviewVisible) + if (InternalPreviewVisible) { Results.SelectedItem?.LoadPreviewImage(); + return; } - else if (Settings.UseExternalPreview) + + switch (PluginManager.UseExternalPreview()) { - if (CanExternalPreviewSelectedResult(out var path)) - { - _ = SwitchExternalPreviewAsync(path, false); - } - else - { - _ = CloseExternalPreviewAsync(); - } + case true + when ExternalPreviewVisible && CanExternalPreviewSelectedResult(out var path): + SwitchExternalPreview(path, false); + break; + + case true + when !ExternalPreviewVisible && Settings.AlwaysPreview && CanExternalPreviewSelectedResult(out var path): + ShowPreview(); + break; + + case true + when !CanExternalPreviewSelectedResult(out var _): + HidePreview(); + break; } } @@ -1104,8 +1142,8 @@ namespace Flow.Launcher.ViewModel // Trick for no delay MainWindowOpacity = 0; - if (Settings.UseExternalPreview) - _ = CloseExternalPreviewAsync(); + if (ExternalPreviewVisible) + CloseExternalPreview(); if (!SelectedIsFromQueryResults()) {