diff --git a/QuickLook.Plugin/QuickLook.Plugin.ArchiveViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.ArchiveViewer/Plugin.cs
index 96f97ea..6f310cb 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.ArchiveViewer/Plugin.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.ArchiveViewer/Plugin.cs
@@ -45,17 +45,19 @@ namespace QuickLook.Plugin.ArchiveViewer
return true;
}
- public void Prepare(string path, ViewContentContainer container)
+ public void BoundViewSize(string path, ViewerObject context)
{
- container.PreferedSize = new Size {Width = 800, Height = 600};
+ context.PreferredSize = new Size {Width = 800, Height = 600};
}
- public void View(string path, ViewContentContainer container)
+ public void View(string path, ViewerObject context)
{
_panel = new ArchiveInfoPanel(path);
- container.SetContent(_panel);
- container.Title = $"{Path.GetFileName(path)}";
+ context.ViewerContent = _panel;
+ context.Title = $"{Path.GetFileName(path)}";
+
+ context.IsBusy = false;
}
public void Dispose()
diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs
index 792d0d1..83a5d23 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs
@@ -33,19 +33,21 @@ namespace QuickLook.Plugin.ImageViewer
}
}
- public void Prepare(string path, ViewContentContainer container)
+ public void BoundViewSize(string path, ViewerObject context)
{
_imageSize = ImageFileHelper.GetImageSize(path);
- container.SetPreferedSizeFit(_imageSize, 0.8);
+ context.SetPreferredSizeFit(_imageSize, 0.8);
}
- public void View(string path, ViewContentContainer container)
+ public void View(string path, ViewerObject context)
{
_ip = new ImagePanel(path);
- container.SetContent(_ip);
- container.Title = $"{Path.GetFileName(path)} ({_imageSize.Width} × {_imageSize.Height})";
+ context.ViewerContent = _ip;
+ context.Title = $"{Path.GetFileName(path)} ({_imageSize.Width} × {_imageSize.Height})";
+
+ context.IsBusy = false;
}
public void Dispose()
diff --git a/QuickLook.Plugin/QuickLook.Plugin.OfficeViewer/PluginInterface.cs b/QuickLook.Plugin/QuickLook.Plugin.OfficeViewer/PluginInterface.cs
index 3e67679..9292728 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.OfficeViewer/PluginInterface.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.OfficeViewer/PluginInterface.cs
@@ -31,12 +31,12 @@ namespace QuickLook.Plugin.OfficeViewer
return false;
}
- public void Prepare(string path, ViewContentContainer container)
+ public void BoundViewSize(string path, ViewerObject context)
{
- container.SetPreferedSizeFit(new Size {Width = 800, Height = 600}, 0.8);
+ context.SetPreferredSizeFit(new Size {Width = 800, Height = 600}, 0.8);
}
- public void View(string path, ViewContentContainer container)
+ public void View(string path, ViewerObject context)
{
using (var officeApp = new OfficeInteropWrapper(path))
{
@@ -59,12 +59,14 @@ namespace QuickLook.Plugin.OfficeViewer
throw ex;
}
- container.Title = $"{Path.GetFileName(path)} (1 / {_pdfViewer.TotalPages})";
+ context.Title = $"{Path.GetFileName(path)} (1 / {_pdfViewer.TotalPages})";
};
- _pdfViewer.CurrentPageChanged += (sender, e) => container.Title =
+ _pdfViewer.CurrentPageChanged += (sender, e) => context.Title =
$"{Path.GetFileName(path)} ({_pdfViewer.CurrectPage + 1} / {_pdfViewer.TotalPages})";
- container.SetContent(_pdfViewer);
+ context.ViewerContent = _pdfViewer;
+
+ context.IsBusy = false;
}
public void Dispose()
diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs
index 6ffddd7..ef7934a 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/Plugin.cs
@@ -7,6 +7,7 @@ namespace QuickLook.Plugin.PDFViewer
public class Plugin : IViewer
{
private PdfViewerControl _pdfControl;
+
public int Priority => int.MaxValue;
public bool CanHandle(string path)
@@ -23,26 +24,28 @@ namespace QuickLook.Plugin.PDFViewer
}
}
- public void Prepare(string path, ViewContentContainer container)
+ public void BoundViewSize(string path, ViewerObject context)
{
_pdfControl = new PdfViewerControl();
var desiredSize = _pdfControl.GetDesiredControlSizeByFirstPage(path);
- container.SetPreferedSizeFit(desiredSize, 0.8);
+ context.SetPreferredSizeFit(desiredSize, 0.8);
}
- public void View(string path, ViewContentContainer container)
+ public void View(string path, ViewerObject context)
{
- container.SetContent(_pdfControl);
+ context.ViewerContent = _pdfControl;
_pdfControl.Loaded += (sender, e) =>
{
_pdfControl.LoadPdf(path);
- container.Title = $"{Path.GetFileName(path)} (1 / {_pdfControl.TotalPages})";
- _pdfControl.CurrentPageChanged += (sender2, e2) => container.Title =
+ context.Title = $"{Path.GetFileName(path)} (1 / {_pdfControl.TotalPages})";
+ _pdfControl.CurrentPageChanged += (sender2, e2) => context.Title =
$"{Path.GetFileName(path)} ({_pdfControl.CurrectPage + 1} / {_pdfControl.TotalPages})";
+
+ context.IsBusy = false;
};
}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Plugin.cs
index 2ba1f69..6c6bd99 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Plugin.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Plugin.cs
@@ -40,17 +40,19 @@ namespace QuickLook.Plugin.TextViewer
}
}
- public void Prepare(string path, ViewContentContainer container)
+ public void BoundViewSize(string path, ViewerObject context)
{
- container.PreferedSize = new Size {Width = 800, Height = 600};
+ context.PreferredSize = new Size {Width = 800, Height = 600};
}
- public void View(string path, ViewContentContainer container)
+ public void View(string path, ViewerObject context)
{
_tvp = new TextViewerPanel(path);
- container.SetContent(_tvp);
- container.Title = $"{Path.GetFileName(path)}";
+ context.ViewerContent = _tvp;
+ context.Title = $"{Path.GetFileName(path)}";
+
+ context.IsBusy = false;
}
public void Dispose()
diff --git a/QuickLook/App.xaml b/QuickLook/App.xaml
index 286264b..f6e5309 100644
--- a/QuickLook/App.xaml
+++ b/QuickLook/App.xaml
@@ -8,6 +8,7 @@
+
diff --git a/QuickLook/Controls/BackgroundVisualHost.cs b/QuickLook/Controls/BackgroundVisualHost.cs
new file mode 100644
index 0000000..79302c4
--- /dev/null
+++ b/QuickLook/Controls/BackgroundVisualHost.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections;
+using System.Threading;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+namespace QuickLook.Controls
+{
+ public delegate Visual CreateContentFunction();
+
+ public class BackgroundVisualHost : FrameworkElement
+ {
+ protected override int VisualChildrenCount => _hostVisual != null ? 1 : 0;
+
+ protected override IEnumerator LogicalChildren
+ {
+ get
+ {
+ if (_hostVisual != null)
+ yield return _hostVisual;
+ }
+ }
+
+ protected override Visual GetVisualChild(int index)
+ {
+ if (_hostVisual != null && index == 0)
+ return _hostVisual;
+
+ throw new IndexOutOfRangeException("index");
+ }
+
+ private void CreateContentHelper()
+ {
+ _threadedHelper = new ThreadedVisualHelper(CreateContent, SafeInvalidateMeasure);
+ _hostVisual = _threadedHelper.HostVisual;
+ }
+
+ private void SafeInvalidateMeasure()
+ {
+ Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Loaded);
+ }
+
+ private void HideContentHelper()
+ {
+ if (_threadedHelper != null)
+ {
+ _threadedHelper.Exit();
+ _threadedHelper = null;
+ InvalidateMeasure();
+ }
+ }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ if (_threadedHelper != null)
+ return _threadedHelper.DesiredSize;
+
+ return base.MeasureOverride(availableSize);
+ }
+
+ private class ThreadedVisualHelper
+ {
+ private readonly CreateContentFunction _createContent;
+ private readonly Action _invalidateMeasure;
+ private readonly AutoResetEvent _sync =
+ new AutoResetEvent(false);
+
+ public ThreadedVisualHelper(
+ CreateContentFunction createContent,
+ Action invalidateMeasure)
+ {
+ HostVisual = new HostVisual();
+ _createContent = createContent;
+ _invalidateMeasure = invalidateMeasure;
+
+ var backgroundUi = new Thread(CreateAndShowContent);
+ backgroundUi.SetApartmentState(ApartmentState.STA);
+ backgroundUi.Name = "BackgroundVisualHostThread";
+ backgroundUi.IsBackground = true;
+ backgroundUi.Start();
+
+ _sync.WaitOne();
+ }
+
+ public HostVisual HostVisual { get; }
+ public Size DesiredSize { get; private set; }
+ private Dispatcher Dispatcher { get; set; }
+
+ public void Exit()
+ {
+ Dispatcher.BeginInvokeShutdown(DispatcherPriority.Send);
+ }
+
+ private void CreateAndShowContent()
+ {
+ Dispatcher = Dispatcher.CurrentDispatcher;
+ var source =
+ new VisualTargetPresentationSource(HostVisual);
+ _sync.Set();
+ source.RootVisual = _createContent();
+ DesiredSize = source.DesiredSize;
+ _invalidateMeasure();
+
+ Dispatcher.Run();
+ source.Dispose();
+ }
+ }
+
+ #region Private Fields
+
+ private ThreadedVisualHelper _threadedHelper;
+ private HostVisual _hostVisual;
+
+ #endregion
+
+ #region IsContentShowingProperty
+
+ ///
+ /// Identifies the IsContentShowing dependency property.
+ ///
+ public static readonly DependencyProperty IsContentShowingProperty = DependencyProperty.Register(
+ "IsContentShowing",
+ typeof(bool),
+ typeof(BackgroundVisualHost),
+ new FrameworkPropertyMetadata(false, OnIsContentShowingChanged));
+
+ ///
+ /// Gets or sets if the content is being displayed.
+ ///
+ public bool IsContentShowing
+ {
+ get => (bool) GetValue(IsContentShowingProperty);
+ set => SetValue(IsContentShowingProperty, value);
+ }
+
+ private static void OnIsContentShowingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var bvh = (BackgroundVisualHost) d;
+
+ if (bvh.CreateContent != null)
+ if ((bool) e.NewValue)
+ bvh.CreateContentHelper();
+ else
+ bvh.HideContentHelper();
+ }
+
+ #endregion
+
+ #region CreateContent Property
+
+ ///
+ /// Identifies the CreateContent dependency property.
+ ///
+ public static readonly DependencyProperty CreateContentProperty = DependencyProperty.Register(
+ "CreateContent",
+ typeof(CreateContentFunction),
+ typeof(BackgroundVisualHost),
+ new FrameworkPropertyMetadata(OnCreateContentChanged));
+
+ ///
+ /// Gets or sets the function used to create the visual to display in a background thread.
+ ///
+ public CreateContentFunction CreateContent
+ {
+ get => (CreateContentFunction) GetValue(CreateContentProperty);
+ set => SetValue(CreateContentProperty, value);
+ }
+
+ private static void OnCreateContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var bvh = (BackgroundVisualHost) d;
+
+ if (bvh.IsContentShowing)
+ {
+ bvh.HideContentHelper();
+ if (e.NewValue != null)
+ bvh.CreateContentHelper();
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/Controls/BusyDecorator.cs b/QuickLook/Controls/BusyDecorator.cs
new file mode 100644
index 0000000..de44137
--- /dev/null
+++ b/QuickLook/Controls/BusyDecorator.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Collections;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Media;
+
+namespace QuickLook.Controls
+{
+ [StyleTypedProperty(Property = "BusyStyle", StyleTargetType = typeof(Control))]
+ public class BusyDecorator : Decorator
+ {
+ private readonly BackgroundVisualHost _busyHost = new BackgroundVisualHost();
+
+ static BusyDecorator()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(
+ typeof(BusyDecorator),
+ new FrameworkPropertyMetadata(typeof(BusyDecorator)));
+ }
+
+ public BusyDecorator()
+ {
+ AddLogicalChild(_busyHost);
+ AddVisualChild(_busyHost);
+
+ SetBinding(_busyHost, IsBusyIndicatorShowingProperty, BackgroundVisualHost.IsContentShowingProperty);
+ SetBinding(_busyHost, BusyHorizontalAlignmentProperty, HorizontalAlignmentProperty);
+ SetBinding(_busyHost, BusyVerticalAlignmentProperty, VerticalAlignmentProperty);
+ }
+
+ protected override int VisualChildrenCount => Child != null ? 2 : 1;
+
+ protected override IEnumerator LogicalChildren
+ {
+ get
+ {
+ if (Child != null)
+ yield return Child;
+
+ yield return _busyHost;
+ }
+ }
+
+ protected override Visual GetVisualChild(int index)
+ {
+ if (Child != null)
+ switch (index)
+ {
+ case 0:
+ return Child;
+
+ case 1:
+ return _busyHost;
+ }
+ else if (index == 0)
+ return _busyHost;
+
+ throw new IndexOutOfRangeException("index");
+ }
+
+ private void SetBinding(DependencyObject obj, DependencyProperty source, DependencyProperty target)
+ {
+ var b = new Binding();
+ b.Source = this;
+ b.Path = new PropertyPath(source);
+ BindingOperations.SetBinding(obj, target, b);
+ }
+
+ protected override Size MeasureOverride(Size constraint)
+ {
+ var ret = new Size(0, 0);
+ if (Child != null)
+ {
+ Child.Measure(constraint);
+ ret = Child.DesiredSize;
+ }
+
+ _busyHost.Measure(constraint);
+
+ return new Size(Math.Max(ret.Width, _busyHost.DesiredSize.Width),
+ Math.Max(ret.Height, _busyHost.DesiredSize.Height));
+ }
+
+ protected override Size ArrangeOverride(Size arrangeSize)
+ {
+ var ret = new Size(0, 0);
+ if (Child != null)
+ {
+ Child.Arrange(new Rect(arrangeSize));
+ ret = Child.RenderSize;
+ }
+
+ _busyHost.Arrange(new Rect(arrangeSize));
+
+ return new Size(Math.Max(ret.Width, _busyHost.RenderSize.Width),
+ Math.Max(ret.Height, _busyHost.RenderSize.Height));
+ }
+
+ #region IsBusyIndicatorShowing Property
+
+ ///
+ /// Identifies the IsBusyIndicatorShowing dependency property.
+ ///
+ public static readonly DependencyProperty IsBusyIndicatorShowingProperty = DependencyProperty.Register(
+ "IsBusyIndicatorShowing",
+ typeof(bool),
+ typeof(BusyDecorator),
+ new FrameworkPropertyMetadata(false,
+ FrameworkPropertyMetadataOptions.AffectsMeasure));
+
+ ///
+ /// Gets or sets if the BusyIndicator is being shown.
+ ///
+ public bool IsBusyIndicatorShowing
+ {
+ get => (bool) GetValue(IsBusyIndicatorShowingProperty);
+ set => SetValue(IsBusyIndicatorShowingProperty, value);
+ }
+
+ #endregion
+
+ #region BusyStyle
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty BusyStyleProperty =
+ DependencyProperty.Register(
+ "BusyStyle",
+ typeof(Style),
+ typeof(BusyDecorator),
+ new FrameworkPropertyMetadata(OnBusyStyleChanged));
+
+ ///
+ /// Gets or sets the Style to apply to the Control that is displayed as the busy indication.
+ ///
+ public Style BusyStyle
+ {
+ get => (Style) GetValue(BusyStyleProperty);
+ set => SetValue(BusyStyleProperty, value);
+ }
+
+ private static void OnBusyStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var bd = (BusyDecorator) d;
+ var nVal = (Style) e.NewValue;
+ bd._busyHost.CreateContent = () => new Control {Style = nVal};
+ }
+
+ #endregion
+
+ #region BusyHorizontalAlignment
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty BusyHorizontalAlignmentProperty = DependencyProperty.Register(
+ "BusyHorizontalAlignment",
+ typeof(HorizontalAlignment),
+ typeof(BusyDecorator),
+ new FrameworkPropertyMetadata(HorizontalAlignment.Center));
+
+ ///
+ /// Gets or sets the HorizontalAlignment to use to layout the control that contains the busy indicator control.
+ ///
+ public HorizontalAlignment BusyHorizontalAlignment
+ {
+ get => (HorizontalAlignment) GetValue(BusyHorizontalAlignmentProperty);
+ set => SetValue(BusyHorizontalAlignmentProperty, value);
+ }
+
+ #endregion
+
+ #region BusyVerticalAlignment
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty BusyVerticalAlignmentProperty = DependencyProperty.Register(
+ "BusyVerticalAlignment",
+ typeof(VerticalAlignment),
+ typeof(BusyDecorator),
+ new FrameworkPropertyMetadata(VerticalAlignment.Center));
+
+ ///
+ /// Gets or sets the the VerticalAlignment to use to layout the control that contains the busy indicator.
+ ///
+ public VerticalAlignment BusyVerticalAlignment
+ {
+ get => (VerticalAlignment) GetValue(BusyVerticalAlignmentProperty);
+ set => SetValue(BusyVerticalAlignmentProperty, value);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/Controls/VisualTargetPresentationSource.cs b/QuickLook/Controls/VisualTargetPresentationSource.cs
new file mode 100644
index 0000000..141984a
--- /dev/null
+++ b/QuickLook/Controls/VisualTargetPresentationSource.cs
@@ -0,0 +1,64 @@
+using System.Windows;
+using System.Windows.Media;
+
+namespace QuickLook.Controls
+{
+ public class VisualTargetPresentationSource : PresentationSource
+ {
+ private readonly VisualTarget _visualTarget;
+ private bool _isDisposed;
+
+ public VisualTargetPresentationSource(HostVisual hostVisual)
+ {
+ _visualTarget = new VisualTarget(hostVisual);
+ AddSource();
+ }
+
+ public Size DesiredSize { get; private set; }
+
+ public override Visual RootVisual
+ {
+ get => _visualTarget.RootVisual;
+ set
+ {
+ var oldRoot = _visualTarget.RootVisual;
+
+ // Set the root visual of the VisualTarget. This visual will
+ // now be used to visually compose the scene.
+ _visualTarget.RootVisual = value;
+
+ // Tell the PresentationSource that the root visual has
+ // changed. This kicks off a bunch of stuff like the
+ // Loaded event.
+ RootChanged(oldRoot, value);
+
+ // Kickoff layout...
+ var rootElement = value as UIElement;
+ if (rootElement != null)
+ {
+ rootElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+ rootElement.Arrange(new Rect(rootElement.DesiredSize));
+
+ DesiredSize = rootElement.DesiredSize;
+ }
+ else
+ {
+ DesiredSize = new Size(0, 0);
+ }
+ }
+ }
+
+ public override bool IsDisposed => _isDisposed;
+
+ protected override CompositionTarget GetCompositionTargetCore()
+ {
+ return _visualTarget;
+ }
+
+ internal void Dispose()
+ {
+ RemoveSource();
+ _isDisposed = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/Utilities/AeroGlass.cs b/QuickLook/Helpers/AeroGlassHelper.cs
similarity index 96%
rename from QuickLook/Utilities/AeroGlass.cs
rename to QuickLook/Helpers/AeroGlassHelper.cs
index d055894..46e2084 100644
--- a/QuickLook/Utilities/AeroGlass.cs
+++ b/QuickLook/Helpers/AeroGlassHelper.cs
@@ -3,9 +3,9 @@ using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
-namespace QuickLook.Utilities
+namespace QuickLook.Helpers
{
- internal static class AeroGlass
+ internal static class AeroGlassHelper
{
internal static void EnableBlur(Window window)
{
diff --git a/QuickLook/Helpers/DpiHelpers.cs b/QuickLook/Helpers/DpiHelpers.cs
new file mode 100644
index 0000000..c8194fc
--- /dev/null
+++ b/QuickLook/Helpers/DpiHelpers.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
+
+namespace QuickLook.Helpers
+{
+ internal static class DpiHelper
+ {
+ public const float DEFAULT_DPI = 96;
+
+ public static Dpi GetCurrentDpi()
+ {
+ var g = Graphics.FromHwnd(IntPtr.Zero);
+ var desktop = g.GetHdc();
+
+ var dpi = new Dpi
+ {
+ HorizontalDpi = GetDeviceCaps(desktop, (int) DeviceCap.LOGPIXELSX),
+ VerticalDpi = GetDeviceCaps(desktop, (int) DeviceCap.LOGPIXELSY)
+ };
+
+ return dpi;
+ }
+
+ [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
+ public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
+
+ private enum DeviceCap
+ {
+ ///
+ /// Logical pixels inch in X
+ ///
+ LOGPIXELSX = 88,
+ ///
+ /// Logical pixels inch in Y
+ ///
+ LOGPIXELSY = 90
+ }
+
+ public struct Dpi
+ {
+ public float HorizontalDpi;
+ public float VerticalDpi;
+ }
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/Utilities/WindowHelper.cs b/QuickLook/Helpers/WindowHelper.cs
similarity index 98%
rename from QuickLook/Utilities/WindowHelper.cs
rename to QuickLook/Helpers/WindowHelper.cs
index 9d901bd..3808b9a 100644
--- a/QuickLook/Utilities/WindowHelper.cs
+++ b/QuickLook/Helpers/WindowHelper.cs
@@ -3,7 +3,7 @@ using System.Text;
using System.Windows.Interop;
using QuickLook.NativeMethods;
-namespace QuickLook.Utilities
+namespace QuickLook.Helpers
{
internal static class WindowHelper
{
diff --git a/QuickLook/MainWindow.xaml b/QuickLook/MainWindow.xaml
index c9ab06d..9f34b87 100644
--- a/QuickLook/MainWindow.xaml
+++ b/QuickLook/MainWindow.xaml
@@ -2,72 +2,84 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fa="http://schemas.fontawesome.io/icons/"
- xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:QuickLook"
+ xmlns:control="clr-namespace:QuickLook.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:plugin="clr-namespace:QuickLook.Plugin"
mc:Ignorable="d" x:Class="QuickLook.MainWindow" x:Name="mainWindow"
UseLayoutRounding="True"
- Topmost="True" d:DesignWidth="624" d:DesignHeight="700"
+ d:DesignWidth="624" d:DesignHeight="700"
MinWidth="275" MinHeight="150"
WindowStartupLocation="CenterScreen"
x:ClassModifier="internal" Focusable="False"
ShowActivated="False" ShowInTaskbar="False" WindowStyle="None"
ResizeMode="CanResizeWithGrip" AllowsTransparency="True">
-
+
-
-
-
-
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/QuickLook/MainWindow.xaml.cs b/QuickLook/MainWindow.xaml.cs
index ee9b328..7d5e9ca 100644
--- a/QuickLook/MainWindow.xaml.cs
+++ b/QuickLook/MainWindow.xaml.cs
@@ -1,54 +1,74 @@
using System;
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
-using System.Windows.Media.Animation;
-using System.Windows.Threading;
-using QuickLook.Annotations;
-using QuickLook.ExtensionMethods;
+using QuickLook.Helpers;
using QuickLook.Plugin;
-using QuickLook.Utilities;
namespace QuickLook
{
///
/// Interaction logic for MainWindow.xaml
///
- internal partial class MainWindow : Window, INotifyPropertyChanged, IDisposable
+ internal partial class MainWindow : Window, IDisposable
{
internal MainWindow()
{
+ // this object should be initialized before loading UI components, because many of which are binding to it.
+ ViewerObject = new ViewerObject();
+
InitializeComponent();
- DataContext = this;
+ // do not set TopMost property if we are now debugging. it makes debugging painful...
+ if (!Debugger.IsAttached)
+ Topmost = true;
- ContentRendered += (sender, e) => AeroGlass.EnableBlur(this);
+ // restore changes by Designer
+ windowPanel.Opacity = 0d;
+ busyIndicatorLayer.Visibility = Visibility.Visible;
- buttonCloseWindow.MouseLeftButtonUp += CloseCurrentWindow;
- titlebarTitleArea.MouseLeftButtonDown += (sender, e) => DragMove();
+ Loaded += (sender, e) => AeroGlassHelper.EnableBlur(this);
+
+ buttonCloseWindow.MouseLeftButtonUp += (sender, e) => Close();
+ titlebarTitleArea.MouseLeftButtonDown += DragMoveCurrentWindow;
}
+ public ViewerObject ViewerObject { get; }
+
public void Dispose()
{
GC.SuppressFinalize(this);
- viewContentContainer?.Dispose();
+ ViewerObject?.Dispose();
}
- public event PropertyChangedEventHandler PropertyChanged;
-
- internal new void Show()
+ private void DragMoveCurrentWindow(object sender, MouseButtonEventArgs e)
{
- loadingIconLayer.Opacity = 1;
+ if (WindowState == WindowState.Maximized)
+ {
+ var dpi = DpiHelper.GetCurrentDpi();
- Height = viewContentContainer.PreferedSize.Height + titlebar.Height + windowBorder.BorderThickness.Top +
+ // MouseDevice.GetPosition() returns device-dependent coordinate, however WPF is not like that
+ var point = PointToScreen(e.MouseDevice.GetPosition(this));
+
+ Left = point.X / (dpi.HorizontalDpi / DpiHelper.DEFAULT_DPI) - RestoreBounds.Width * 0.5;
+ Top = point.Y / (dpi.VerticalDpi / DpiHelper.DEFAULT_DPI);
+
+ WindowState = WindowState.Normal;
+ }
+
+ DragMove();
+ }
+
+ private new void Show()
+ {
+ Height = ViewerObject.PreferredSize.Height + titlebar.Height + windowBorder.BorderThickness.Top +
windowBorder.BorderThickness.Bottom;
- Width = viewContentContainer.PreferedSize.Width + windowBorder.BorderThickness.Left +
+ Width = ViewerObject.PreferredSize.Width + windowBorder.BorderThickness.Left +
windowBorder.BorderThickness.Right;
- ResizeMode = viewContentContainer.CanResize ? ResizeMode.CanResizeWithGrip : ResizeMode.NoResize;
+ ResizeMode = ViewerObject.CanResize ? ResizeMode.CanResizeWithGrip : ResizeMode.NoResize;
base.Show();
@@ -57,54 +77,15 @@ namespace QuickLook
internal void BeginShow(IViewer matchedPlugin, string path)
{
- viewContentContainer.ViewerPlugin = matchedPlugin;
+ ViewerObject.CurrentContentContainer = viewContentContainer;
+ ViewerObject.ViewerPlugin = matchedPlugin;
// get window size before showing it
- matchedPlugin.Prepare(path, viewContentContainer);
+ matchedPlugin.BoundViewSize(path, ViewerObject);
Show();
- matchedPlugin.View(path, viewContentContainer);
-
- ShowFinishLoadingAnimation();
- }
-
- private void ShowFinishLoadingAnimation()
- {
- var speed = 100;
-
- var sb = new Storyboard();
- var ptl = new ParallelTimeline();
-
- var aOpacityR = new DoubleAnimation
- {
- From = 1,
- To = 0,
- Duration = TimeSpan.FromMilliseconds(speed)
- };
-
- Storyboard.SetTarget(aOpacityR, loadingIconLayer);
- Storyboard.SetTargetProperty(aOpacityR, new PropertyPath(OpacityProperty));
-
- ptl.Children.Add(aOpacityR);
-
- sb.Children.Add(ptl);
-
- sb.Begin();
-
- Dispatcher.DelayWithPriority(speed, o => loadingIconLayer.Visibility = Visibility.Hidden, null,
- DispatcherPriority.Render);
- }
-
- private void CloseCurrentWindow(object sender, MouseButtonEventArgs e)
- {
- Close();
- }
-
- [NotifyPropertyChangedInvocator]
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ matchedPlugin.View(path, ViewerObject);
}
~MainWindow()
diff --git a/QuickLook/Plugin/IViewer.cs b/QuickLook/Plugin/IViewer.cs
index 216a397..02869f1 100644
--- a/QuickLook/Plugin/IViewer.cs
+++ b/QuickLook/Plugin/IViewer.cs
@@ -1,11 +1,40 @@
namespace QuickLook.Plugin
{
+ ///
+ /// Interface implemented by every QuickLook.Plugin
+ ///
public interface IViewer
{
+ ///
+ /// Set the priority of this plugin. A plugin with a higher priority may override one with lower priority.
+ /// Set this to int.MaxValue for a maximum priority, int.MinValue for minimum.
+ ///
int Priority { get; }
+
+ ///
+ /// Determine whether this plugin can open this file. Please also check the file header, if applicable.
+ ///
+ /// The full path of the target file.
bool CanHandle(string path);
- void Prepare(string path, ViewContentContainer container);
- void View(string path, ViewContentContainer container);
+
+ ///
+ /// Tell QuickLook the desired window size. Please not do any work that costs a lot of time.
+ ///
+ /// The full path of the target file.
+ /// A runtime object which allows interaction between this plugin and QuickLook.
+ void BoundViewSize(string path, ViewerObject context);
+
+ ///
+ /// Start the loading process. During the process a busy indicator will be shown. Finish by setting context.IsBusy to
+ /// false.
+ ///
+ /// The full path of the target file.
+ /// A runtime object which allows interaction between this plugin and QuickLook.
+ void View(string path, ViewerObject context);
+
+ ///
+ /// Release any unmanaged resource here.
+ ///
void Dispose();
}
}
\ No newline at end of file
diff --git a/QuickLook/Plugin/InfoPanel/PluginInterface.cs b/QuickLook/Plugin/InfoPanel/PluginInterface.cs
index 1fa3cbf..2253d8f 100644
--- a/QuickLook/Plugin/InfoPanel/PluginInterface.cs
+++ b/QuickLook/Plugin/InfoPanel/PluginInterface.cs
@@ -14,19 +14,21 @@ namespace QuickLook.Plugin.InfoPanel
return true;
}
- public void Prepare(string path, ViewContentContainer container)
+ public void BoundViewSize(string path, ViewerObject context)
{
_ip = new InfoPanel();
- container.CanResize = false;
- container.PreferedSize = new Size {Width = _ip.Width, Height = _ip.Height};
+ context.CanResize = false;
+ context.PreferredSize = new Size {Width = _ip.Width, Height = _ip.Height};
}
- public void View(string path, ViewContentContainer container)
+ public void View(string path, ViewerObject context)
{
_ip.DisplayInfo(path);
- container.SetContent(_ip);
+ context.ViewerContent = _ip;
+
+ context.IsBusy = false;
}
public void Dispose()
diff --git a/QuickLook/Plugin/ViewContentContainer.xaml.cs b/QuickLook/Plugin/ViewContentContainer.xaml.cs
deleted file mode 100644
index d260677..0000000
--- a/QuickLook/Plugin/ViewContentContainer.xaml.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using System.Windows;
-using System.Windows.Controls;
-using QuickLook.Annotations;
-
-namespace QuickLook.Plugin
-{
- ///
- /// Interaction logic for ViewContentContainer.xaml
- ///
- public partial class ViewContentContainer : UserControl, INotifyPropertyChanged, IDisposable
- {
- private string _title = string.Empty;
-
- public ViewContentContainer()
- {
- InitializeComponent();
- }
-
- public string Title
- {
- set
- {
- _title = value;
- OnPropertyChanged(nameof(Title));
- }
- get => _title;
- }
-
- public IViewer ViewerPlugin { get; set; }
-
- public Size PreferedSize { get; set; }
-
- public bool CanResize { get; set; } = true;
-
- public void Dispose()
- {
- GC.SuppressFinalize(this);
-
- ViewerPlugin?.Dispose();
- }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- public void SetContent(object content)
- {
- container.Content = content;
- }
-
- public double SetPreferedSizeFit(Size size, double maxRatio)
- {
- if (maxRatio > 1)
- maxRatio = 1;
-
- var max = GetMaximumDisplayBound();
-
- var widthRatio = max.Width * maxRatio / size.Width;
- var heightRatio = max.Height * maxRatio / size.Height;
-
- var ratio = Math.Min(widthRatio, heightRatio);
-
- if (ratio > 1) ratio = 1;
-
- PreferedSize = new Size {Width = size.Width * ratio, Height = size.Height * ratio};
-
- return ratio;
- }
-
- public Size GetMaximumDisplayBound()
- {
- return new Size(SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight);
- }
-
- [NotifyPropertyChangedInvocator]
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- ~ViewContentContainer()
- {
- Dispose();
- }
- }
-}
\ No newline at end of file
diff --git a/QuickLook/Plugin/ViewerObject.cs b/QuickLook/Plugin/ViewerObject.cs
new file mode 100644
index 0000000..211d36f
--- /dev/null
+++ b/QuickLook/Plugin/ViewerObject.cs
@@ -0,0 +1,119 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using QuickLook.Annotations;
+
+namespace QuickLook.Plugin
+{
+ ///
+ /// A runtime object which allows interaction between this plugin and QuickLook.
+ ///
+ public class ViewerObject : INotifyPropertyChanged, IDisposable
+ {
+ private bool _isBusy = true;
+
+ private string _title = "";
+ internal ViewContentContainer CurrentContentContainer;
+ internal IViewer ViewerPlugin;
+
+ ///
+ /// Get or set the title of Viewer window.
+ ///
+ public string Title
+ {
+ get => _title;
+ set
+ {
+ _title = value;
+ OnPropertyChanged();
+ }
+ }
+
+ ///
+ /// Get or set the viewer content control.
+ ///
+ public object ViewerContent
+ {
+ get => CurrentContentContainer.container.Content;
+ set => CurrentContentContainer.container.Content = value;
+ }
+
+ ///
+ /// Show or hide the busy indicator icon.
+ ///
+ public bool IsBusy
+ {
+ get => _isBusy;
+ set
+ {
+ _isBusy = value;
+ OnPropertyChanged();
+ }
+ }
+
+ ///
+ /// Set the exact size you want.
+ ///
+ public Size PreferredSize { get; set; } = new Size {Width = 800, Height = 600};
+
+ ///
+ /// Set whether user are allowed to resize the viewer window.
+ ///
+ public bool CanResize { get; set; } = true;
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+
+ ViewerPlugin?.Dispose();
+ ViewerPlugin = null;
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// Set the size of viewer window and shrink to fit (to screen resolution).
+ /// The window can take maximum (maxRatio*resolution) space.
+ ///
+ /// The desired size.
+ /// The maximum percent (over screen resolution) it can take.
+ public double SetPreferredSizeFit(Size size, double maxRatio)
+ {
+ if (maxRatio > 1)
+ maxRatio = 1;
+
+ var max = GetMaximumDisplayBound();
+
+ var widthRatio = max.Width * maxRatio / size.Width;
+ var heightRatio = max.Height * maxRatio / size.Height;
+
+ var ratio = Math.Min(widthRatio, heightRatio);
+
+ if (ratio > 1) ratio = 1;
+
+ PreferredSize = new Size {Width = size.Width * ratio, Height = size.Height * ratio};
+
+ return ratio;
+ }
+
+ ///
+ /// Get the device-independent resolution.
+ ///
+ public Size GetMaximumDisplayBound()
+ {
+ return new Size(SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight);
+ }
+
+ [NotifyPropertyChangedInvocator]
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ ~ViewerObject()
+ {
+ Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/QuickLook.csproj b/QuickLook/QuickLook.csproj
index 62ccb2e..5cfab23 100644
--- a/QuickLook/QuickLook.csproj
+++ b/QuickLook/QuickLook.csproj
@@ -75,6 +75,9 @@
MSBuild:Compile
Designer
+
+
+
@@ -85,10 +88,12 @@
-
+
+
ViewContentContainer.xaml
-
+
+
MSBuild:Compile
@@ -111,16 +116,20 @@
-
+
MainWindow.xaml
Code
-
+
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
diff --git a/QuickLook/Styles/BusyDecorator.xaml b/QuickLook/Styles/BusyDecorator.xaml
new file mode 100644
index 0000000..d2a1a20
--- /dev/null
+++ b/QuickLook/Styles/BusyDecorator.xaml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QuickLook/Plugin/ViewContentContainer.xaml b/QuickLook/ViewContentContainer.xaml
similarity index 91%
rename from QuickLook/Plugin/ViewContentContainer.xaml
rename to QuickLook/ViewContentContainer.xaml
index c419cd0..83e09e7 100644
--- a/QuickLook/Plugin/ViewContentContainer.xaml
+++ b/QuickLook/ViewContentContainer.xaml
@@ -1,4 +1,4 @@
-
+ /// Interaction logic for ViewContentContainer.xaml
+ ///
+ public partial class ViewContentContainer : UserControl
+ {
+ public ViewContentContainer()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs
index eea6064..9c6f4de 100644
--- a/QuickLook/ViewWindowManager.cs
+++ b/QuickLook/ViewWindowManager.cs
@@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using QuickLook.ExtensionMethods;
+using QuickLook.Helpers;
using QuickLook.Plugin;
-using QuickLook.Utilities;
namespace QuickLook
{