reuse viewer window

This commit is contained in:
Paddy Xu
2017-05-16 18:32:36 +03:00
parent f6843eef93
commit a672e441d0
8 changed files with 126 additions and 150 deletions

View File

@@ -56,6 +56,8 @@ namespace QuickLook.Plugin.VideoViewer
mediaElement.Stop(); mediaElement.Stop();
_context.ShowNotification("", "An error occurred while loading the video."); _context.ShowNotification("", "An error occurred while loading the video.");
throw new Exception();
} }
public void LoadAndPlay(string path) public void LoadAndPlay(string path)

View File

@@ -20,22 +20,27 @@
</Window.Background> </Window.Background>
<Border x:Name="windowBorder" BorderThickness="1" BorderBrush="#FF7B7B7B"> <Border x:Name="windowBorder" BorderThickness="1" BorderBrush="#FF7B7B7B">
<Grid> <Grid>
<DockPanel x:Name="windowPanel" Opacity="1"> <DockPanel x:Name="windowPanel">
<DockPanel.Style> <DockPanel.Style>
<Style TargetType="{x:Type DockPanel}"> <Style TargetType="{x:Type DockPanel}">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding ContextObject.IsBusy, ElementName=mainWindow}" Value="False"> <DataTrigger Binding="{Binding ContextObject.IsBusy, ElementName=mainWindow, Mode=OneWay}" Value="False">
<DataTrigger.EnterActions> <DataTrigger.EnterActions>
<BeginStoryboard> <BeginStoryboard>
<Storyboard> <Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1"
From="0" BeginTime="0:0:0" Duration="0:0:0.05" />
To="1"
BeginTime="0:0:0"
Duration="0:0:0.1" />
</Storyboard> </Storyboard>
</BeginStoryboard> </BeginStoryboard>
</DataTrigger.EnterActions> </DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0"
BeginTime="0:0:0" Duration="0:0:0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
@@ -52,37 +57,52 @@
</Grid> </Grid>
</DockPanel> </DockPanel>
<Grid> <Grid>
<local:ViewContentContainer x:Name="viewContentContainer" /> <ContentControl x:Name="container" />
</Grid> </Grid>
</DockPanel> </DockPanel>
<Grid x:Name="busyIndicatorLayer" Visibility="Hidden"> <Grid x:Name="busyIndicatorLayer">
<Grid.Style> <Grid.Style>
<Style TargetType="{x:Type Grid}"> <Style TargetType="{x:Type Grid}">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding ContextObject.IsBusy, ElementName=mainWindow}" Value="False"> <DataTrigger Binding="{Binding ContextObject.IsBusy, ElementName=mainWindow, Mode=OneWay}" Value="False">
<DataTrigger.EnterActions> <DataTrigger.EnterActions>
<BeginStoryboard> <BeginStoryboard>
<Storyboard> <Storyboard>
<ParallelTimeline> <ParallelTimeline>
<DoubleAnimation Storyboard.TargetProperty="Opacity" <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0"
From="1" BeginTime="0:0:0" Duration="0:0:0.05" />
To="0"
BeginTime="0:0:0"
Duration="0:0:0.1" />
<ObjectAnimationUsingKeyFrames <ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.Visibility)"> Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.1" <DiscreteObjectKeyFrame KeyTime="0:0:0.05"
Value="{x:Static Visibility.Collapsed}" /> Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
</ParallelTimeline> </ParallelTimeline>
</Storyboard> </Storyboard>
</BeginStoryboard> </BeginStoryboard>
</DataTrigger.EnterActions> </DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ParallelTimeline>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1"
BeginTime="0:0:0" Duration="0:0:0" />
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</Grid.Style> </Grid.Style>
<control:BusyDecorator x:Name="busyDecorator" IsBusyIndicatorShowing="True" VerticalAlignment="Center" <!--IsBusyIndicatorShowing="{Binding IsVisible, ElementName=busyIndicatorLayer}"-->
<control:BusyDecorator x:Name="busyDecorator"
IsBusyIndicatorShowing="True"
VerticalAlignment="Center"
HorizontalAlignment="Center" /> HorizontalAlignment="Center" />
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -2,7 +2,8 @@
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Interop; using System.Windows.Threading;
using QuickLook.ExtensionMethods;
using QuickLook.Helpers; using QuickLook.Helpers;
using QuickLook.Plugin; using QuickLook.Plugin;
@@ -11,7 +12,7 @@ namespace QuickLook
/// <summary> /// <summary>
/// Interaction logic for MainWindow.xaml /// Interaction logic for MainWindow.xaml
/// </summary> /// </summary>
internal partial class MainWindow : Window, IDisposable internal partial class MainWindow : Window
{ {
internal MainWindow() internal MainWindow()
{ {
@@ -20,31 +21,23 @@ namespace QuickLook
InitializeComponent(); InitializeComponent();
// revert designer changes
windowPanel.Opacity = 0d;
busyIndicatorLayer.Visibility = Visibility.Visible;
busyIndicatorLayer.Opacity = 1d;
// do not set TopMost property if we are now debugging. it makes debugging painful... // do not set TopMost property if we are now debugging. it makes debugging painful...
if (!Debugger.IsAttached) if (!Debugger.IsAttached)
Topmost = true; Topmost = true;
// restore changes by Designer
windowPanel.Opacity = 0d;
busyIndicatorLayer.Visibility = Visibility.Visible;
Loaded += (sender, e) => AeroGlassHelper.EnableBlur(this); Loaded += (sender, e) => AeroGlassHelper.EnableBlur(this);
buttonCloseWindow.MouseLeftButtonUp += (sender, e) => Close(); buttonCloseWindow.MouseLeftButtonUp += (sender, e) => { Hide(); };
titleBarArea.PreviewMouseLeftButtonDown += DragMoveCurrentWindow; titleBarArea.PreviewMouseLeftButtonDown += DragMoveCurrentWindow;
} }
public ContextObject ContextObject { get; } public ContextObject ContextObject { get; private set; }
public void Dispose()
{
GC.SuppressFinalize(this);
ContextObject?.Dispose();
// stop the background thread
busyDecorator?.Dispose();
}
private void DragMoveCurrentWindow(object sender, MouseButtonEventArgs e) private void DragMoveCurrentWindow(object sender, MouseButtonEventArgs e)
{ {
@@ -71,17 +64,37 @@ namespace QuickLook
Width = ContextObject.PreferredSize.Width + windowBorder.BorderThickness.Left + Width = ContextObject.PreferredSize.Width + windowBorder.BorderThickness.Left +
windowBorder.BorderThickness.Right; windowBorder.BorderThickness.Right;
Left = (SystemParameters.VirtualScreenWidth - Width) / 2;
Top = (SystemParameters.VirtualScreenHeight - Height) / 2;
ResizeMode = ContextObject.CanResize ? ResizeMode.CanResizeWithGrip : ResizeMode.NoResize; ResizeMode = ContextObject.CanResize ? ResizeMode.CanResizeWithGrip : ResizeMode.NoResize;
base.Show(); base.Show();
if (!ContextObject.Focusable) //if (!ContextObject.Focusable)
WindowHelper.SetNoactivate(new WindowInteropHelper(this)); // WindowHelper.SetNoactivate(new WindowInteropHelper(this));
}
private new void Hide()
{
container.Content = null;
// clean up plugin and refresh ContextObject for next use
ContextObject.ViewerPlugin?.Dispose();
ContextObject.Reset();
GC.Collect();
// revert UI changes
ContextObject.IsBusy = true;
Left -= 10000;
Dispatcher.Delay(100, _ => base.Hide());
} }
internal void BeginShow(IViewer matchedPlugin, string path) internal void BeginShow(IViewer matchedPlugin, string path)
{ {
ContextObject.CurrentContentContainer = viewContentContainer; ContextObject.CurrentContentContainer = container;
ContextObject.ViewerPlugin = matchedPlugin; ContextObject.ViewerPlugin = matchedPlugin;
// get window size before showing it // get window size before showing it
@@ -89,12 +102,19 @@ namespace QuickLook
Show(); Show();
matchedPlugin.View(path, ContextObject); // load plugin, do not block UI
Dispatcher.BeginInvoke(new Action(() => matchedPlugin.View(path, ContextObject)),
DispatcherPriority.Render);
} }
~MainWindow() internal bool BeginHide()
{ {
Dispose(); if (Visibility != Visibility.Visible)
return false;
Hide();
return true;
} }
} }
} }

View File

@@ -2,6 +2,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using QuickLook.Annotations; using QuickLook.Annotations;
namespace QuickLook.Plugin namespace QuickLook.Plugin
@@ -9,12 +10,12 @@ namespace QuickLook.Plugin
/// <summary> /// <summary>
/// A runtime object which allows interaction between this plugin and QuickLook. /// A runtime object which allows interaction between this plugin and QuickLook.
/// </summary> /// </summary>
public class ContextObject : INotifyPropertyChanged, IDisposable public class ContextObject : INotifyPropertyChanged
{ {
private bool _isBusy = true; private bool _isBusy = true;
private string _title = ""; private string _title = "";
internal ViewContentContainer CurrentContentContainer; internal ContentControl CurrentContentContainer;
internal IViewer ViewerPlugin; internal IViewer ViewerPlugin;
/// <summary> /// <summary>
@@ -35,8 +36,8 @@ namespace QuickLook.Plugin
/// </summary> /// </summary>
public object ViewerContent public object ViewerContent
{ {
get => CurrentContentContainer.container.Content; get => CurrentContentContainer.Content;
set => CurrentContentContainer.container.Content = value; set => CurrentContentContainer.Content = value;
} }
/// <summary> /// <summary>
@@ -67,10 +68,8 @@ namespace QuickLook.Plugin
/// </summary> /// </summary>
public bool Focusable { get; set; } = false; public bool Focusable { get; set; } = false;
public void Dispose() public void DisposePlugin()
{ {
GC.SuppressFinalize(this);
ViewerPlugin?.Dispose(); ViewerPlugin?.Dispose();
ViewerPlugin = null; ViewerPlugin = null;
} }
@@ -105,7 +104,6 @@ namespace QuickLook.Plugin
var heightRatio = max.Height * maxRatio / size.Height; var heightRatio = max.Height * maxRatio / size.Height;
var ratio = Math.Min(widthRatio, heightRatio); var ratio = Math.Min(widthRatio, heightRatio);
if (ratio > 1) ratio = 1; if (ratio > 1) ratio = 1;
PreferredSize = new Size {Width = size.Width * ratio, Height = size.Height * ratio}; PreferredSize = new Size {Width = size.Width * ratio, Height = size.Height * ratio};
@@ -121,15 +119,20 @@ namespace QuickLook.Plugin
return new Size(SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight); return new Size(SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight);
} }
internal void Reset()
{
Title = "";
ViewerContent = null;
IsBusy = true;
PreferredSize = new Size();
CanResize = true;
Focusable = false;
}
[NotifyPropertyChangedInvocator] [NotifyPropertyChangedInvocator]
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
~ContextObject()
{
Dispose();
}
} }
} }

View File

@@ -101,9 +101,6 @@
<Compile Include="Plugin\IViewer.cs" /> <Compile Include="Plugin\IViewer.cs" />
<Compile Include="Helpers\DpiHelpers.cs" /> <Compile Include="Helpers\DpiHelpers.cs" />
<Compile Include="TrayIcon.cs" /> <Compile Include="TrayIcon.cs" />
<Compile Include="ViewContentContainer.xaml.cs">
<DependentUpon>ViewContentContainer.xaml</DependentUpon>
</Compile>
<Compile Include="Plugin\ContextObject.cs" /> <Compile Include="Plugin\ContextObject.cs" />
<Compile Include="Helpers\WindowHelper.cs" /> <Compile Include="Helpers\WindowHelper.cs" />
<Compile Include="ViewWindowManager.cs" /> <Compile Include="ViewWindowManager.cs" />
@@ -134,10 +131,6 @@
<DependentUpon>MainWindow.xaml</DependentUpon> <DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Page Include="ViewContentContainer.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\BusyDecorator.xaml"> <Page Include="Styles\BusyDecorator.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>

View File

@@ -1,15 +0,0 @@
<UserControl x:Class="QuickLook.ViewContentContainer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:QuickLook"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ContentControl x:Name="container">
<Label Content="ContentControl Placeholder" VerticalContentAlignment="Center"
HorizontalContentAlignment="Center" Background="LightGray" />
</ContentControl>
</Grid>
</UserControl>

View File

@@ -1,15 +0,0 @@
using System.Windows.Controls;
namespace QuickLook
{
/// <summary>
/// Interaction logic for ViewContentContainer.xaml
/// </summary>
public partial class ViewContentContainer : UserControl
{
public ViewContentContainer()
{
InitializeComponent();
}
}
}

View File

@@ -15,7 +15,31 @@ namespace QuickLook
{ {
private static ViewWindowManager _instance; private static ViewWindowManager _instance;
private MainWindow _viewWindow; private readonly MainWindow _viewWindow;
internal ViewWindowManager()
{
_viewWindow = new MainWindow();
_viewWindow.Closed += (sender, e) =>
{
if (App.RunningAsViewer)
Application.Current.Shutdown();
};
}
internal void InvokeRoutine()
{
if (!WindowHelper.IsFocusedControlExplorerItem())
if (!WindowHelper.IsFocusedWindowSelf())
return;
if (_viewWindow.BeginHide())
return;
var path = GetCurrentSelection();
InvokeViewer(path);
}
internal void InvokeViewer(string path) internal void InvokeViewer(string path)
{ {
@@ -29,43 +53,15 @@ namespace QuickLook
BeginShowNewWindow(matchedPlugin, path); BeginShowNewWindow(matchedPlugin, path);
} }
internal void InvokeRoutine()
{
if (!WindowHelper.IsFocusedControlExplorerItem())
if (!WindowHelper.IsFocusedWindowSelf())
return;
if (CloseCurrentWindow())
return;
var path = GetCurrentSelection();
InvokeViewer(path);
}
private void BeginShowNewWindow(IViewer matchedPlugin, string path) private void BeginShowNewWindow(IViewer matchedPlugin, string path)
{ {
_viewWindow = new MainWindow();
_viewWindow.Closed += (sender2, e2) =>
{
if (App.RunningAsViewer)
{
Application.Current.Shutdown();
return;
}
_viewWindow.Dispose();
_viewWindow = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
};
try try
{ {
_viewWindow.BeginShow(matchedPlugin, path); _viewWindow.BeginShow(matchedPlugin, path);
} }
catch (Exception e) // if current plugin failed, switch to default one. catch (Exception e) // if current plugin failed, switch to default one.
{ {
_viewWindow.Close(); _viewWindow.BeginHide();
Debug.WriteLine(e.ToString()); Debug.WriteLine(e.ToString());
Debug.WriteLine(e.StackTrace); Debug.WriteLine(e.StackTrace);
@@ -81,34 +77,6 @@ namespace QuickLook
throw; throw;
} }
} }
#pragma warning disable 1058
catch // Catch SEH exceptions here.
#pragma warning restore 1058
{
_viewWindow.Close();
if (matchedPlugin.GetType() != PluginManager.GetInstance().DefaultPlugin)
{
matchedPlugin.Dispose();
matchedPlugin = PluginManager.GetInstance().DefaultPlugin.CreateInstance<IViewer>();
BeginShowNewWindow(matchedPlugin, path);
}
else
{
throw;
}
}
}
private bool CloseCurrentWindow()
{
if (_viewWindow != null)
{
_viewWindow.Close();
return true;
}
return false;
} }
private string GetCurrentSelection() private string GetCurrentSelection()