mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-12 18:39:45 +00:00
pinch zoom gesture; rewrite zoomviewer
This commit is contained in:
@@ -5,11 +5,13 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:QuickLook.Plugin.ImageViewer"
|
xmlns:local="clr-namespace:QuickLook.Plugin.ImageViewer"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
|
x:Name="imagePanel"
|
||||||
d:DesignHeight="300" d:DesignWidth="300">
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
<Grid>
|
<Grid>
|
||||||
<ScrollViewer x:Name="viewPanel" BorderThickness="0" HorizontalScrollBarVisibility="Auto"
|
<ScrollViewer x:Name="viewPanel" BorderThickness="0" HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Auto" Focusable="False">
|
VerticalScrollBarVisibility="Auto" Focusable="False" IsManipulationEnabled="True">
|
||||||
<Image x:Name="viewPanelImage" Stretch="None" RenderOptions.BitmapScalingMode="HighQuality" />
|
<Image x:Name="viewPanelImage" Stretch="None" Source="{Binding Source, ElementName=imagePanel}"
|
||||||
|
RenderOptions.BitmapScalingMode="{Binding RenderMode, ElementName=imagePanel}" />
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
@@ -16,58 +16,141 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using ImageMagick;
|
using System.Windows.Media.Imaging;
|
||||||
using XamlAnimatedGif;
|
using System.Windows.Threading;
|
||||||
|
using QuickLook.Annotations;
|
||||||
|
|
||||||
namespace QuickLook.Plugin.ImageViewer
|
namespace QuickLook.Plugin.ImageViewer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for ImagePanel.xaml
|
/// Interaction logic for ImagePanel.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ImagePanel : UserControl
|
public partial class ImagePanel : UserControl, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private Point? _dragInitPos;
|
private Point? _dragInitPos;
|
||||||
private double _minZoomFactor = 1d;
|
private DateTime _lastZoomTime = DateTime.MinValue;
|
||||||
|
private double _maxZoomFactor = 3d;
|
||||||
|
private double _minZoomFactor = 0.1d;
|
||||||
|
private BitmapScalingMode _renderMode = BitmapScalingMode.HighQuality;
|
||||||
|
private BitmapSource _source;
|
||||||
private double _zoomFactor = 1d;
|
private double _zoomFactor = 1d;
|
||||||
|
|
||||||
public ImagePanel(string path)
|
private bool _zoomToFit = true;
|
||||||
|
|
||||||
|
public ImagePanel()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
LoadImage(path);
|
SizeChanged += ImagePanel_SizeChanged;
|
||||||
|
|
||||||
Loaded += (sender, e) => { ZoomToFit(); };
|
|
||||||
|
|
||||||
viewPanel.PreviewMouseWheel += ViewPanel_PreviewMouseWheel;
|
viewPanel.PreviewMouseWheel += ViewPanel_PreviewMouseWheel;
|
||||||
|
|
||||||
viewPanel.MouseLeftButtonDown += ViewPanel_MouseLeftButtonDown;
|
viewPanel.MouseLeftButtonDown += ViewPanel_MouseLeftButtonDown;
|
||||||
viewPanel.MouseMove += ViewPanel_MouseMove;
|
viewPanel.MouseMove += ViewPanel_MouseMove;
|
||||||
|
|
||||||
|
viewPanel.ManipulationStarting += ViewPanel_ManipulationStarting;
|
||||||
|
viewPanel.ManipulationDelta += ViewPanel_ManipulationDelta;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadImage(string path)
|
public BitmapScalingMode RenderMode
|
||||||
{
|
{
|
||||||
if (Path.GetExtension(path).ToLower() == ".gif")
|
get => _renderMode;
|
||||||
AnimationBehavior.SetSourceUri(viewPanelImage, new Uri(path));
|
set
|
||||||
|
|
||||||
using (var image = new MagickImage(path))
|
|
||||||
{
|
{
|
||||||
image.Rotate(image.Orientation == OrientationType.RightTop
|
_renderMode = value;
|
||||||
? 90
|
OnPropertyChanged();
|
||||||
: image.Orientation == OrientationType.BottomRight
|
|
||||||
? 180
|
|
||||||
: image.Orientation == OrientationType.LeftBotom
|
|
||||||
? 270
|
|
||||||
: 0);
|
|
||||||
|
|
||||||
viewPanelImage.Source = image.ToBitmapSource();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ZoomToFit
|
||||||
|
{
|
||||||
|
get => _zoomToFit;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_zoomToFit = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double MinZoomFactor
|
||||||
|
{
|
||||||
|
get => _minZoomFactor;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_minZoomFactor = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double MaxZoomFactor
|
||||||
|
{
|
||||||
|
get => _maxZoomFactor;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_maxZoomFactor = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double ZoomFactor
|
||||||
|
{
|
||||||
|
get => _zoomFactor;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_zoomFactor = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmapSource Source
|
||||||
|
{
|
||||||
|
get => _source;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_source = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
public event EventHandler<int> ImageScrolled;
|
||||||
|
public event EventHandler DelayedReRender;
|
||||||
|
|
||||||
|
private void ImagePanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (ZoomToFit)
|
||||||
|
DoZoomToFit(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewPanel_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
|
||||||
|
{
|
||||||
|
e.ManipulationContainer = viewPanel;
|
||||||
|
e.Mode = ManipulationModes.Scale | ManipulationModes.Translate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewPanel_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
|
||||||
|
{
|
||||||
|
var delta = e.DeltaManipulation;
|
||||||
|
|
||||||
|
var newZoom = ZoomFactor + ZoomFactor * (delta.Scale.X - 1);
|
||||||
|
|
||||||
|
Zoom(newZoom, false);
|
||||||
|
|
||||||
|
viewPanel.ScrollToHorizontalOffset(viewPanel.HorizontalOffset - delta.Translation.X);
|
||||||
|
viewPanel.ScrollToVerticalOffset(viewPanel.VerticalOffset - delta.Translation.Y);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void ViewPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void ViewPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
e.MouseDevice.Capture(viewPanel);
|
e.MouseDevice.Capture(viewPanel);
|
||||||
@@ -95,6 +178,8 @@ namespace QuickLook.Plugin.ImageViewer
|
|||||||
|
|
||||||
var delta = _dragInitPos.Value - e.GetPosition(viewPanel);
|
var delta = _dragInitPos.Value - e.GetPosition(viewPanel);
|
||||||
|
|
||||||
|
Debug.WriteLine(_dragInitPos.Value);
|
||||||
|
|
||||||
viewPanel.ScrollToHorizontalOffset(delta.X);
|
viewPanel.ScrollToHorizontalOffset(delta.X);
|
||||||
viewPanel.ScrollToVerticalOffset(delta.Y);
|
viewPanel.ScrollToVerticalOffset(delta.Y);
|
||||||
}
|
}
|
||||||
@@ -108,33 +193,63 @@ namespace QuickLook.Plugin.ImageViewer
|
|||||||
// normal scroll
|
// normal scroll
|
||||||
viewPanel.ScrollToVerticalOffset(viewPanel.VerticalOffset - e.Delta);
|
viewPanel.ScrollToVerticalOffset(viewPanel.VerticalOffset - e.Delta);
|
||||||
|
|
||||||
|
ImageScrolled?.Invoke(this, e.Delta);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// zoom
|
// zoom
|
||||||
var newZoom = _zoomFactor + (double) e.Delta / 120 * 0.1;
|
var newZoom = ZoomFactor + ZoomFactor * e.Delta / 120 * 0.1;
|
||||||
|
|
||||||
newZoom = Math.Max(newZoom, _minZoomFactor);
|
|
||||||
newZoom = Math.Min(newZoom, 3);
|
|
||||||
|
|
||||||
Zoom(newZoom, false);
|
Zoom(newZoom, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ZoomToFit()
|
public Size GetScrollSize()
|
||||||
{
|
{
|
||||||
|
return new Size(viewPanel.ScrollableWidth, viewPanel.ScrollableHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point GetScrollPosition()
|
||||||
|
{
|
||||||
|
return new Point(viewPanel.HorizontalOffset, viewPanel.VerticalOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetScrollPosition(Point point)
|
||||||
|
{
|
||||||
|
viewPanel.ScrollToHorizontalOffset(point.X);
|
||||||
|
viewPanel.ScrollToVerticalOffset(point.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoZoomToFit(bool suppressEvent)
|
||||||
|
{
|
||||||
|
if (viewPanelImage.Source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var factor = Math.Min(viewPanel.ActualWidth / viewPanelImage.Source.Width,
|
var factor = Math.Min(viewPanel.ActualWidth / viewPanelImage.Source.Width,
|
||||||
viewPanel.ActualHeight / viewPanelImage.Source.Height);
|
viewPanel.ActualHeight / viewPanelImage.Source.Height);
|
||||||
|
|
||||||
_minZoomFactor = factor;
|
Zoom(factor, true, suppressEvent);
|
||||||
|
|
||||||
Zoom(factor, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Zoom(double factor, bool fromCenter)
|
public void ResetZoom()
|
||||||
{
|
{
|
||||||
_zoomFactor = factor;
|
Zoom(1d, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
var position = fromCenter
|
public void Zoom(double factor, bool toFit, bool suppressEvent = false)
|
||||||
|
{
|
||||||
|
if (viewPanelImage.Source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
factor = Math.Max(factor, MinZoomFactor);
|
||||||
|
factor = Math.Min(factor, MaxZoomFactor);
|
||||||
|
|
||||||
|
ZoomFactor = factor;
|
||||||
|
|
||||||
|
if (!toFit)
|
||||||
|
ZoomToFit = false;
|
||||||
|
|
||||||
|
var position = toFit
|
||||||
? new Point(viewPanelImage.Source.Width / 2, viewPanelImage.Source.Height / 2)
|
? new Point(viewPanelImage.Source.Width / 2, viewPanelImage.Source.Height / 2)
|
||||||
: Mouse.GetPosition(viewPanelImage);
|
: Mouse.GetPosition(viewPanelImage);
|
||||||
|
|
||||||
@@ -151,6 +266,41 @@ namespace QuickLook.Plugin.ImageViewer
|
|||||||
viewPanel.ScrollToHorizontalOffset(offset.X);
|
viewPanel.ScrollToHorizontalOffset(offset.X);
|
||||||
viewPanel.ScrollToVerticalOffset(offset.Y);
|
viewPanel.ScrollToVerticalOffset(offset.Y);
|
||||||
UpdateLayout();
|
UpdateLayout();
|
||||||
|
|
||||||
|
if (!suppressEvent)
|
||||||
|
ProcessDelayed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessDelayed()
|
||||||
|
{
|
||||||
|
_lastZoomTime = DateTime.Now;
|
||||||
|
|
||||||
|
Task.Delay(500).ContinueWith(t =>
|
||||||
|
{
|
||||||
|
if (DateTime.Now - _lastZoomTime < TimeSpan.FromSeconds(0.5))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug.WriteLine($"ProcessDelayed fired: {Thread.CurrentThread.ManagedThreadId}");
|
||||||
|
|
||||||
|
Dispatcher.BeginInvoke(new Action(() => DelayedReRender?.Invoke(this, new EventArgs())),
|
||||||
|
DispatcherPriority.Background);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[NotifyPropertyChangedInvocator]
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ScrollToTop()
|
||||||
|
{
|
||||||
|
viewPanel.ScrollToTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ScrollToBottom()
|
||||||
|
{
|
||||||
|
viewPanel.ScrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,11 +15,13 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using ImageMagick;
|
using ImageMagick;
|
||||||
|
using XamlAnimatedGif;
|
||||||
|
|
||||||
namespace QuickLook.Plugin.ImageViewer
|
namespace QuickLook.Plugin.ImageViewer
|
||||||
{
|
{
|
||||||
@@ -69,12 +71,15 @@ namespace QuickLook.Plugin.ImageViewer
|
|||||||
|
|
||||||
public void View(string path, ContextObject context)
|
public void View(string path, ContextObject context)
|
||||||
{
|
{
|
||||||
_ip = new ImagePanel(path);
|
_ip = new ImagePanel();
|
||||||
|
|
||||||
context.ViewerContent = _ip;
|
context.ViewerContent = _ip;
|
||||||
context.Title = _imageSize.IsEmpty
|
context.Title = _imageSize.IsEmpty
|
||||||
? $"{Path.GetFileName(path)}"
|
? $"{Path.GetFileName(path)}"
|
||||||
: $"{Path.GetFileName(path)} ({_imageSize.Width}×{_imageSize.Height})";
|
: $"{Path.GetFileName(path)} ({_imageSize.Width}×{_imageSize.Height})";
|
||||||
|
|
||||||
|
LoadImage(_ip, path);
|
||||||
|
|
||||||
context.IsBusy = false;
|
context.IsBusy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,5 +88,24 @@ namespace QuickLook.Plugin.ImageViewer
|
|||||||
Directory.SetCurrentDirectory(App.AppPath);
|
Directory.SetCurrentDirectory(App.AppPath);
|
||||||
_ip = null;
|
_ip = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LoadImage(ImagePanel ui, string path)
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(path).ToLower() == ".gif")
|
||||||
|
AnimationBehavior.SetSourceUri(ui.viewPanelImage, new Uri(path));
|
||||||
|
|
||||||
|
using (var image = new MagickImage(path))
|
||||||
|
{
|
||||||
|
image.Rotate(image.Orientation == OrientationType.RightTop
|
||||||
|
? 90
|
||||||
|
: image.Orientation == OrientationType.BottomRight
|
||||||
|
? 180
|
||||||
|
: image.Orientation == OrientationType.LeftBotom
|
||||||
|
? 270
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
ui.Source = image.ToBitmapSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,130 +0,0 @@
|
|||||||
// Copyright © 2017 Paddy Xu
|
|
||||||
//
|
|
||||||
// This file is part of QuickLook program.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Threading;
|
|
||||||
|
|
||||||
namespace QuickLook.Plugin.PDFViewer
|
|
||||||
{
|
|
||||||
public sealed class PreviewMouseWheelMonitor : IDisposable
|
|
||||||
{
|
|
||||||
private readonly UIElement _canvas;
|
|
||||||
private readonly Dispatcher _dispatcher;
|
|
||||||
private readonly int _sensitivity;
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
private volatile bool _inactive;
|
|
||||||
private AutoResetEvent _resetMonitorEvent;
|
|
||||||
private volatile bool _stopped;
|
|
||||||
|
|
||||||
public PreviewMouseWheelMonitor(UIElement canvas, int sensitivity)
|
|
||||||
{
|
|
||||||
_canvas = canvas;
|
|
||||||
_canvas.PreviewMouseWheel += (s, e) => RaisePreviewMouseWheel(e);
|
|
||||||
|
|
||||||
_sensitivity = sensitivity;
|
|
||||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
|
||||||
_resetMonitorEvent = new AutoResetEvent(false);
|
|
||||||
|
|
||||||
_disposed = false;
|
|
||||||
_inactive = true;
|
|
||||||
_stopped = true;
|
|
||||||
|
|
||||||
var monitor = new Thread(Monitor) {IsBackground = true};
|
|
||||||
monitor.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!_disposed)
|
|
||||||
{
|
|
||||||
_disposed = true;
|
|
||||||
DetachEventHandlers();
|
|
||||||
if (_resetMonitorEvent != null)
|
|
||||||
{
|
|
||||||
_resetMonitorEvent.Close();
|
|
||||||
_resetMonitorEvent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<MouseWheelEventArgs> PreviewMouseWheel;
|
|
||||||
public event EventHandler<EventArgs> PreviewMouseWheelStarted;
|
|
||||||
public event EventHandler<EventArgs> PreviewMouseWheelStopped;
|
|
||||||
|
|
||||||
private void Monitor()
|
|
||||||
{
|
|
||||||
while (!_disposed)
|
|
||||||
{
|
|
||||||
if (_inactive) // if wheel is still inactive...
|
|
||||||
{
|
|
||||||
_resetMonitorEvent.WaitOne(_sensitivity / 10); // ...wait negligibly small quantity of time...
|
|
||||||
continue; // ...and check again
|
|
||||||
}
|
|
||||||
// otherwise, if wheel is active...
|
|
||||||
_inactive = true; // ...purposely change the state to inactive
|
|
||||||
_resetMonitorEvent.WaitOne(_sensitivity); // wait...
|
|
||||||
if (_inactive
|
|
||||||
) // ...and after specified time check if the state is still not re-activated inside mouse wheel event
|
|
||||||
RaiseMouseWheelStopped();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RaisePreviewMouseWheel(MouseWheelEventArgs args)
|
|
||||||
{
|
|
||||||
if (_stopped)
|
|
||||||
RaiseMouseWheelStarted();
|
|
||||||
|
|
||||||
_inactive = false;
|
|
||||||
if (PreviewMouseWheel != null)
|
|
||||||
PreviewMouseWheel(_canvas, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RaiseMouseWheelStarted()
|
|
||||||
{
|
|
||||||
_stopped = false;
|
|
||||||
if (PreviewMouseWheelStarted != null)
|
|
||||||
PreviewMouseWheelStarted(_canvas, new EventArgs());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RaiseMouseWheelStopped()
|
|
||||||
{
|
|
||||||
_stopped = true;
|
|
||||||
if (PreviewMouseWheelStopped != null)
|
|
||||||
_dispatcher.Invoke(() => PreviewMouseWheelStopped(_canvas,
|
|
||||||
new
|
|
||||||
EventArgs())); // invoked on cached dispatcher for convenience (because fired from non-UI thread)
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DetachEventHandlers()
|
|
||||||
{
|
|
||||||
if (PreviewMouseWheel != null)
|
|
||||||
foreach (var handler in PreviewMouseWheel.GetInvocationList().Cast<EventHandler<MouseWheelEventArgs>>())
|
|
||||||
PreviewMouseWheel -= handler;
|
|
||||||
if (PreviewMouseWheelStarted != null)
|
|
||||||
foreach (var handler in PreviewMouseWheelStarted.GetInvocationList().Cast<EventHandler<EventArgs>>())
|
|
||||||
PreviewMouseWheelStarted -= handler;
|
|
||||||
if (PreviewMouseWheelStopped != null)
|
|
||||||
foreach (var handler in PreviewMouseWheelStopped.GetInvocationList().Cast<EventHandler<EventArgs>>())
|
|
||||||
PreviewMouseWheelStopped -= handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -4,6 +4,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:QuickLook.Plugin.PDFViewer"
|
xmlns:local="clr-namespace:QuickLook.Plugin.PDFViewer"
|
||||||
|
xmlns:imageViewer="clr-namespace:QuickLook.Plugin.ImageViewer;assembly=QuickLook.Plugin.ImageViewer"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Name="thisPdfViewer"
|
x:Name="thisPdfViewer"
|
||||||
UseLayoutRounding="True"
|
UseLayoutRounding="True"
|
||||||
@@ -69,10 +70,7 @@
|
|||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
<Grid Grid.Column="1" Background="#00EFEFEF">
|
<Grid Grid.Column="1" Background="#00EFEFEF">
|
||||||
<ScrollViewer x:Name="pageViewPanel" HorizontalScrollBarVisibility="Auto"
|
<imageViewer:ImagePanel x:Name="pagePanel" RenderMode="NearestNeighbor" />
|
||||||
VerticalScrollBarVisibility="Auto" Focusable="False">
|
|
||||||
<Image x:Name="pageViewPanelImage" Stretch="None" RenderOptions.BitmapScalingMode="NearestNeighbor" />
|
|
||||||
</ScrollViewer>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
@@ -18,12 +18,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using QuickLook.ExtensionMethods;
|
||||||
using System.Windows.Media;
|
|
||||||
|
|
||||||
namespace QuickLook.Plugin.PDFViewer
|
namespace QuickLook.Plugin.PDFViewer
|
||||||
{
|
{
|
||||||
@@ -32,30 +32,27 @@ namespace QuickLook.Plugin.PDFViewer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PdfViewerControl : UserControl, INotifyPropertyChanged, IDisposable
|
public partial class PdfViewerControl : UserControl, INotifyPropertyChanged, IDisposable
|
||||||
{
|
{
|
||||||
private Point? _dragInitPos;
|
private const double MinZoomFactor = 0.1d;
|
||||||
private PreviewMouseWheelMonitor _whellMonitor;
|
private const double MaxZoomFactor = 3d;
|
||||||
|
private bool _pdfLoaded;
|
||||||
|
private double _viewRenderFactor = 1d;
|
||||||
|
|
||||||
public PdfViewerControl()
|
public PdfViewerControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
DataContext = this;
|
listThumbnails.SelectionChanged += UpdatePageViewWhenSelectionChanged;
|
||||||
|
|
||||||
PageIds = new ObservableCollection<int>();
|
pagePanel.DelayedReRender += ReRenderCurrentPageDelayed;
|
||||||
|
pagePanel.ImageScrolled += NavigatePage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<int> PageIds { get; set; }
|
public ObservableCollection<int> PageIds { get; set; } = new ObservableCollection<int>();
|
||||||
|
|
||||||
public PdfFile PdfHandleForThumbnails { get; private set; }
|
public PdfFile PdfHandleForThumbnails { get; private set; }
|
||||||
|
|
||||||
public PdfFile PdfHandle { get; private set; }
|
public PdfFile PdfHandle { get; private set; }
|
||||||
|
|
||||||
public bool PdfLoaded { get; private set; }
|
|
||||||
|
|
||||||
public double ZoomFactor { get; set; }
|
|
||||||
|
|
||||||
public double MinZoomFactor { get; set; }
|
|
||||||
|
|
||||||
public int TotalPages => PdfHandle.TotalPages;
|
public int TotalPages => PdfHandle.TotalPages;
|
||||||
|
|
||||||
public int CurrentPage
|
public int CurrentPage
|
||||||
@@ -67,6 +64,7 @@ namespace QuickLook.Plugin.PDFViewer
|
|||||||
listThumbnails.ScrollIntoView(listThumbnails.SelectedItem);
|
listThumbnails.ScrollIntoView(listThumbnails.SelectedItem);
|
||||||
|
|
||||||
CurrentPageChanged?.Invoke(this, new EventArgs());
|
CurrentPageChanged?.Invoke(this, new EventArgs());
|
||||||
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +72,20 @@ namespace QuickLook.Plugin.PDFViewer
|
|||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
_whellMonitor?.Dispose();
|
_pdfLoaded = false;
|
||||||
PdfHandleForThumbnails?.Dispose();
|
PdfHandleForThumbnails?.Dispose();
|
||||||
|
PdfHandleForThumbnails = null;
|
||||||
PdfHandle?.Dispose();
|
PdfHandle?.Dispose();
|
||||||
|
PdfHandle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
private void ReRenderCurrentPageDelayed(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ReRenderCurrentPage();
|
||||||
|
}
|
||||||
|
|
||||||
~PdfViewerControl()
|
~PdfViewerControl()
|
||||||
{
|
{
|
||||||
Dispose();
|
Dispose();
|
||||||
@@ -88,45 +93,27 @@ namespace QuickLook.Plugin.PDFViewer
|
|||||||
|
|
||||||
public event EventHandler CurrentPageChanged;
|
public event EventHandler CurrentPageChanged;
|
||||||
|
|
||||||
private void NavigatePage(object sender, MouseWheelEventArgs e)
|
private void NavigatePage(object sender, int delta)
|
||||||
{
|
{
|
||||||
if (!PdfLoaded)
|
if (!_pdfLoaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Keyboard.Modifiers != ModifierKeys.None)
|
var pos = pagePanel.GetScrollPosition();
|
||||||
return;
|
var size = pagePanel.GetScrollSize();
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
|
|
||||||
if (e.Delta > 0) // up
|
|
||||||
{
|
|
||||||
if (pageViewPanel.VerticalOffset != 0)
|
|
||||||
{
|
|
||||||
pageViewPanel.ScrollToVerticalOffset(pageViewPanel.VerticalOffset - e.Delta); // normal scroll
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
const double tolerance = 0.0001d;
|
||||||
|
if (Math.Abs(pos.Y) < tolerance && delta > 0)
|
||||||
PrevPage();
|
PrevPage();
|
||||||
}
|
else if (Math.Abs(pos.Y - size.Height) < tolerance && delta < 0)
|
||||||
else // down
|
|
||||||
{
|
|
||||||
if (pageViewPanel.VerticalOffset != pageViewPanel.ScrollableHeight)
|
|
||||||
{
|
|
||||||
pageViewPanel.ScrollToVerticalOffset(pageViewPanel.VerticalOffset - e.Delta); // normal scroll
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NextPage();
|
NextPage();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void NextPage()
|
private void NextPage()
|
||||||
{
|
{
|
||||||
if (CurrentPage < PdfHandle.TotalPages - 1)
|
if (CurrentPage < PdfHandle.TotalPages - 1)
|
||||||
{
|
{
|
||||||
CurrentPage++;
|
CurrentPage++;
|
||||||
pageViewPanel.ScrollToTop();
|
pagePanel.ScrollToTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,59 +122,42 @@ namespace QuickLook.Plugin.PDFViewer
|
|||||||
if (CurrentPage > 0)
|
if (CurrentPage > 0)
|
||||||
{
|
{
|
||||||
CurrentPage--;
|
CurrentPage--;
|
||||||
pageViewPanel.ScrollToBottom();
|
pagePanel.ScrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReRenderCurrentPageLowQuality(double viewZoom, bool fromCenter)
|
|
||||||
{
|
|
||||||
if (pageViewPanelImage.Source == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var position = fromCenter
|
|
||||||
? new Point(pageViewPanelImage.Source.Width / 2, pageViewPanelImage.Source.Height / 2)
|
|
||||||
: Mouse.GetPosition(pageViewPanelImage);
|
|
||||||
|
|
||||||
pageViewPanelImage.LayoutTransform = new ScaleTransform(viewZoom, viewZoom);
|
|
||||||
|
|
||||||
pageViewPanel.InvalidateMeasure();
|
|
||||||
|
|
||||||
// critical for calcuating offset
|
|
||||||
pageViewPanel.ScrollToHorizontalOffset(0);
|
|
||||||
pageViewPanel.ScrollToVerticalOffset(0);
|
|
||||||
UpdateLayout();
|
|
||||||
|
|
||||||
var offset = pageViewPanelImage.TranslatePoint(position, pageViewPanel) - Mouse.GetPosition(pageViewPanel);
|
|
||||||
pageViewPanel.ScrollToHorizontalOffset(offset.X);
|
|
||||||
pageViewPanel.ScrollToVerticalOffset(offset.Y);
|
|
||||||
UpdateLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void ReRenderCurrentPage()
|
private void ReRenderCurrentPage()
|
||||||
{
|
{
|
||||||
if (!PdfLoaded)
|
if (!_pdfLoaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var bitmap = PdfHandle.GetPage(CurrentPage, ZoomFactor);
|
Debug.WriteLine($"Renrendering page {CurrentPage}");
|
||||||
|
|
||||||
|
var pos = pagePanel.GetScrollPosition();
|
||||||
|
|
||||||
|
var factor = pagePanel.ZoomFactor * _viewRenderFactor;
|
||||||
|
factor = Math.Max(factor, MinZoomFactor);
|
||||||
|
factor = Math.Min(factor, MaxZoomFactor);
|
||||||
|
pagePanel.MinZoomFactor = MinZoomFactor / factor;
|
||||||
|
pagePanel.MaxZoomFactor = MaxZoomFactor / factor;
|
||||||
|
|
||||||
|
var bitmap = PdfHandle.GetPage(CurrentPage, factor);
|
||||||
var image = bitmap.ToBitmapSource();
|
var image = bitmap.ToBitmapSource();
|
||||||
bitmap.Dispose();
|
bitmap.Dispose();
|
||||||
|
|
||||||
pageViewPanelImage.Source = image;
|
pagePanel.ResetZoom();
|
||||||
pageViewPanelImage.Width = pageViewPanelImage.Source.Width;
|
pagePanel.Source = image;
|
||||||
pageViewPanelImage.Height = pageViewPanelImage.Source.Height;
|
|
||||||
|
|
||||||
// reset view zoom factor
|
_viewRenderFactor = factor;
|
||||||
pageViewPanelImage.LayoutTransform = new ScaleTransform();
|
|
||||||
|
|
||||||
pageViewPanel.InvalidateMeasure();
|
pagePanel.SetScrollPosition(pos);
|
||||||
|
|
||||||
GC.Collect();
|
Dispatcher.Delay(500, t => GC.Collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePageViewWhenSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void UpdatePageViewWhenSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!PdfLoaded)
|
if (!_pdfLoaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (CurrentPage == -1)
|
if (CurrentPage == -1)
|
||||||
@@ -198,21 +168,6 @@ namespace QuickLook.Plugin.PDFViewer
|
|||||||
ReRenderCurrentPage();
|
ReRenderCurrentPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ZoomToFit()
|
|
||||||
{
|
|
||||||
if (!PdfLoaded)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var size = PdfHandle.GetPageSize(CurrentPage, 1d);
|
|
||||||
|
|
||||||
var factor = Math.Min(pageViewPanel.ActualWidth / size.Width, pageViewPanel.ActualHeight / size.Height);
|
|
||||||
|
|
||||||
ZoomFactor = factor;
|
|
||||||
MinZoomFactor = factor;
|
|
||||||
|
|
||||||
ReRenderCurrentPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Size GetDesiredControlSizeByFirstPage(string path)
|
public static Size GetDesiredControlSizeByFirstPage(string path)
|
||||||
{
|
{
|
||||||
var tempHandle = new PdfFile(path);
|
var tempHandle = new PdfFile(path);
|
||||||
@@ -228,102 +183,17 @@ namespace QuickLook.Plugin.PDFViewer
|
|||||||
public void LoadPdf(string path)
|
public void LoadPdf(string path)
|
||||||
{
|
{
|
||||||
PageIds.Clear();
|
PageIds.Clear();
|
||||||
_whellMonitor?.Dispose();
|
|
||||||
|
|
||||||
PdfHandleForThumbnails = new PdfFile(path);
|
PdfHandleForThumbnails = new PdfFile(path);
|
||||||
PdfHandle = new PdfFile(path);
|
PdfHandle = new PdfFile(path);
|
||||||
PdfLoaded = true;
|
_pdfLoaded = true;
|
||||||
|
|
||||||
// fill thumbnails list
|
// fill thumbnails list
|
||||||
Enumerable.Range(0, PdfHandle.TotalPages).ForEach(PageIds.Add);
|
Enumerable.Range(0, PdfHandle.TotalPages).ForEach(PageIds.Add);
|
||||||
OnPropertyChanged(nameof(PageIds));
|
OnPropertyChanged("PageIds");
|
||||||
|
|
||||||
CurrentPage = 0;
|
CurrentPage = 0;
|
||||||
|
pagePanel.DoZoomToFit(true);
|
||||||
// calculate zoom factor for first page
|
|
||||||
ZoomToFit();
|
|
||||||
|
|
||||||
// register events
|
|
||||||
listThumbnails.SelectionChanged += UpdatePageViewWhenSelectionChanged;
|
|
||||||
//pageViewPanel.SizeChanged += ReRenderCurrentPageWhenSizeChanged;
|
|
||||||
|
|
||||||
pageViewPanel.PreviewMouseWheel += NavigatePage;
|
|
||||||
StartMouseWhellDelayedZoomMonitor(pageViewPanel);
|
|
||||||
|
|
||||||
pageViewPanel.MouseLeftButtonDown += DragScrollStart;
|
|
||||||
pageViewPanel.MouseMove += DragScrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DragScrolling(object sender, MouseEventArgs e)
|
|
||||||
{
|
|
||||||
if (!_dragInitPos.HasValue)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (e.LeftButton == MouseButtonState.Released)
|
|
||||||
{
|
|
||||||
e.MouseDevice.Capture(null);
|
|
||||||
|
|
||||||
_dragInitPos = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
|
|
||||||
var delta = _dragInitPos.Value - e.GetPosition(pageViewPanel);
|
|
||||||
|
|
||||||
pageViewPanel.ScrollToHorizontalOffset(delta.X);
|
|
||||||
pageViewPanel.ScrollToVerticalOffset(delta.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DragScrollStart(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
e.MouseDevice.Capture(pageViewPanel);
|
|
||||||
|
|
||||||
_dragInitPos = e.GetPosition(pageViewPanel);
|
|
||||||
var temp = _dragInitPos.Value; // Point is a type value
|
|
||||||
temp.Offset(pageViewPanel.HorizontalOffset, pageViewPanel.VerticalOffset);
|
|
||||||
_dragInitPos = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartMouseWhellDelayedZoomMonitor(UIElement ui)
|
|
||||||
{
|
|
||||||
if (_whellMonitor == null)
|
|
||||||
_whellMonitor = new PreviewMouseWheelMonitor(ui, 100);
|
|
||||||
|
|
||||||
var newZoom = 1d;
|
|
||||||
var scrolling = false;
|
|
||||||
|
|
||||||
_whellMonitor.PreviewMouseWheelStarted += (sender, e) =>
|
|
||||||
{
|
|
||||||
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
newZoom = ZoomFactor;
|
|
||||||
scrolling = true;
|
|
||||||
};
|
|
||||||
_whellMonitor.PreviewMouseWheel += (sender, e) =>
|
|
||||||
{
|
|
||||||
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
|
|
||||||
newZoom = newZoom + (double) e.Delta / 120 * 0.1;
|
|
||||||
|
|
||||||
newZoom = Math.Max(newZoom, MinZoomFactor);
|
|
||||||
newZoom = Math.Min(newZoom, 3);
|
|
||||||
|
|
||||||
ReRenderCurrentPageLowQuality(newZoom / ZoomFactor, false);
|
|
||||||
};
|
|
||||||
_whellMonitor.PreviewMouseWheelStopped += (sender, e) =>
|
|
||||||
{
|
|
||||||
if (!scrolling)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ZoomFactor = newZoom;
|
|
||||||
ReRenderCurrentPage();
|
|
||||||
scrolling = false;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
@@ -73,7 +73,6 @@
|
|||||||
<Compile Include="DpiHelpers.cs" />
|
<Compile Include="DpiHelpers.cs" />
|
||||||
<Compile Include="Extensions.cs" />
|
<Compile Include="Extensions.cs" />
|
||||||
<Compile Include="LibMuPdf.cs" />
|
<Compile Include="LibMuPdf.cs" />
|
||||||
<Compile Include="MouseWheelMonitor.cs" />
|
|
||||||
<Compile Include="PageIdToImageConverter.cs" />
|
<Compile Include="PageIdToImageConverter.cs" />
|
||||||
<Compile Include="PdfFile.cs" />
|
<Compile Include="PdfFile.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
@@ -84,6 +83,11 @@
|
|||||||
<Name>QuickLook</Name>
|
<Name>QuickLook</Name>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\QuickLook.Plugin.ImageViewer\QuickLook.Plugin.ImageViewer.csproj">
|
||||||
|
<Project>{fe5a5111-9607-4721-a7be-422754002ed8}</Project>
|
||||||
|
<Name>QuickLook.Plugin.ImageViewer</Name>
|
||||||
|
<Private>False</Private>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="LibMuPdf32.dll">
|
<Content Include="LibMuPdf32.dll">
|
||||||
|
Reference in New Issue
Block a user