mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-11 17:59:17 +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:local="clr-namespace:QuickLook.Plugin.ImageViewer"
|
||||
mc:Ignorable="d"
|
||||
x:Name="imagePanel"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
<Grid>
|
||||
<ScrollViewer x:Name="viewPanel" BorderThickness="0" HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto" Focusable="False">
|
||||
<Image x:Name="viewPanelImage" Stretch="None" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
VerticalScrollBarVisibility="Auto" Focusable="False" IsManipulationEnabled="True">
|
||||
<Image x:Name="viewPanelImage" Stretch="None" Source="{Binding Source, ElementName=imagePanel}"
|
||||
RenderOptions.BitmapScalingMode="{Binding RenderMode, ElementName=imagePanel}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -16,58 +16,141 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using ImageMagick;
|
||||
using XamlAnimatedGif;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using QuickLook.Annotations;
|
||||
|
||||
namespace QuickLook.Plugin.ImageViewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ImagePanel.xaml
|
||||
/// </summary>
|
||||
public partial class ImagePanel : UserControl
|
||||
public partial class ImagePanel : UserControl, INotifyPropertyChanged
|
||||
{
|
||||
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;
|
||||
|
||||
public ImagePanel(string path)
|
||||
private bool _zoomToFit = true;
|
||||
|
||||
public ImagePanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
LoadImage(path);
|
||||
|
||||
Loaded += (sender, e) => { ZoomToFit(); };
|
||||
SizeChanged += ImagePanel_SizeChanged;
|
||||
|
||||
viewPanel.PreviewMouseWheel += ViewPanel_PreviewMouseWheel;
|
||||
|
||||
viewPanel.MouseLeftButtonDown += ViewPanel_MouseLeftButtonDown;
|
||||
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")
|
||||
AnimationBehavior.SetSourceUri(viewPanelImage, new Uri(path));
|
||||
|
||||
using (var image = new MagickImage(path))
|
||||
get => _renderMode;
|
||||
set
|
||||
{
|
||||
image.Rotate(image.Orientation == OrientationType.RightTop
|
||||
? 90
|
||||
: image.Orientation == OrientationType.BottomRight
|
||||
? 180
|
||||
: image.Orientation == OrientationType.LeftBotom
|
||||
? 270
|
||||
: 0);
|
||||
|
||||
viewPanelImage.Source = image.ToBitmapSource();
|
||||
_renderMode = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
e.MouseDevice.Capture(viewPanel);
|
||||
@@ -95,6 +178,8 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
|
||||
var delta = _dragInitPos.Value - e.GetPosition(viewPanel);
|
||||
|
||||
Debug.WriteLine(_dragInitPos.Value);
|
||||
|
||||
viewPanel.ScrollToHorizontalOffset(delta.X);
|
||||
viewPanel.ScrollToVerticalOffset(delta.Y);
|
||||
}
|
||||
@@ -108,33 +193,63 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
// normal scroll
|
||||
viewPanel.ScrollToVerticalOffset(viewPanel.VerticalOffset - e.Delta);
|
||||
|
||||
ImageScrolled?.Invoke(this, e.Delta);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// zoom
|
||||
var newZoom = _zoomFactor + (double) e.Delta / 120 * 0.1;
|
||||
|
||||
newZoom = Math.Max(newZoom, _minZoomFactor);
|
||||
newZoom = Math.Min(newZoom, 3);
|
||||
var newZoom = ZoomFactor + ZoomFactor * e.Delta / 120 * 0.1;
|
||||
|
||||
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,
|
||||
viewPanel.ActualHeight / viewPanelImage.Source.Height);
|
||||
|
||||
_minZoomFactor = factor;
|
||||
|
||||
Zoom(factor, true);
|
||||
Zoom(factor, true, suppressEvent);
|
||||
}
|
||||
|
||||
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)
|
||||
: Mouse.GetPosition(viewPanelImage);
|
||||
|
||||
@@ -151,6 +266,41 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
viewPanel.ScrollToHorizontalOffset(offset.X);
|
||||
viewPanel.ScrollToVerticalOffset(offset.Y);
|
||||
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
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using ImageMagick;
|
||||
using XamlAnimatedGif;
|
||||
|
||||
namespace QuickLook.Plugin.ImageViewer
|
||||
{
|
||||
@@ -69,12 +71,15 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
|
||||
public void View(string path, ContextObject context)
|
||||
{
|
||||
_ip = new ImagePanel(path);
|
||||
_ip = new ImagePanel();
|
||||
|
||||
context.ViewerContent = _ip;
|
||||
context.Title = _imageSize.IsEmpty
|
||||
? $"{Path.GetFileName(path)}"
|
||||
: $"{Path.GetFileName(path)} ({_imageSize.Width}×{_imageSize.Height})";
|
||||
|
||||
LoadImage(_ip, path);
|
||||
|
||||
context.IsBusy = false;
|
||||
}
|
||||
|
||||
@@ -83,5 +88,24 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
Directory.SetCurrentDirectory(App.AppPath);
|
||||
_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:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:QuickLook.Plugin.PDFViewer"
|
||||
xmlns:imageViewer="clr-namespace:QuickLook.Plugin.ImageViewer;assembly=QuickLook.Plugin.ImageViewer"
|
||||
mc:Ignorable="d"
|
||||
x:Name="thisPdfViewer"
|
||||
UseLayoutRounding="True"
|
||||
@@ -69,10 +70,7 @@
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<Grid Grid.Column="1" Background="#00EFEFEF">
|
||||
<ScrollViewer x:Name="pageViewPanel" HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto" Focusable="False">
|
||||
<Image x:Name="pageViewPanelImage" Stretch="None" RenderOptions.BitmapScalingMode="NearestNeighbor" />
|
||||
</ScrollViewer>
|
||||
<imageViewer:ImagePanel x:Name="pagePanel" RenderMode="NearestNeighbor" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -18,12 +18,12 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using QuickLook.ExtensionMethods;
|
||||
|
||||
namespace QuickLook.Plugin.PDFViewer
|
||||
{
|
||||
@@ -32,30 +32,27 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
/// </summary>
|
||||
public partial class PdfViewerControl : UserControl, INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
private Point? _dragInitPos;
|
||||
private PreviewMouseWheelMonitor _whellMonitor;
|
||||
private const double MinZoomFactor = 0.1d;
|
||||
private const double MaxZoomFactor = 3d;
|
||||
private bool _pdfLoaded;
|
||||
private double _viewRenderFactor = 1d;
|
||||
|
||||
public PdfViewerControl()
|
||||
{
|
||||
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 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 CurrentPage
|
||||
@@ -67,6 +64,7 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
listThumbnails.ScrollIntoView(listThumbnails.SelectedItem);
|
||||
|
||||
CurrentPageChanged?.Invoke(this, new EventArgs());
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,13 +72,20 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
_whellMonitor?.Dispose();
|
||||
_pdfLoaded = false;
|
||||
PdfHandleForThumbnails?.Dispose();
|
||||
PdfHandleForThumbnails = null;
|
||||
PdfHandle?.Dispose();
|
||||
PdfHandle = null;
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void ReRenderCurrentPageDelayed(object sender, EventArgs e)
|
||||
{
|
||||
ReRenderCurrentPage();
|
||||
}
|
||||
|
||||
~PdfViewerControl()
|
||||
{
|
||||
Dispose();
|
||||
@@ -88,37 +93,19 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
|
||||
public event EventHandler CurrentPageChanged;
|
||||
|
||||
private void NavigatePage(object sender, MouseWheelEventArgs e)
|
||||
private void NavigatePage(object sender, int delta)
|
||||
{
|
||||
if (!PdfLoaded)
|
||||
if (!_pdfLoaded)
|
||||
return;
|
||||
|
||||
if (Keyboard.Modifiers != ModifierKeys.None)
|
||||
return;
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
if (e.Delta > 0) // up
|
||||
{
|
||||
if (pageViewPanel.VerticalOffset != 0)
|
||||
{
|
||||
pageViewPanel.ScrollToVerticalOffset(pageViewPanel.VerticalOffset - e.Delta); // normal scroll
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = pagePanel.GetScrollPosition();
|
||||
var size = pagePanel.GetScrollSize();
|
||||
|
||||
const double tolerance = 0.0001d;
|
||||
if (Math.Abs(pos.Y) < tolerance && delta > 0)
|
||||
PrevPage();
|
||||
}
|
||||
else // down
|
||||
{
|
||||
if (pageViewPanel.VerticalOffset != pageViewPanel.ScrollableHeight)
|
||||
{
|
||||
pageViewPanel.ScrollToVerticalOffset(pageViewPanel.VerticalOffset - e.Delta); // normal scroll
|
||||
return;
|
||||
}
|
||||
|
||||
else if (Math.Abs(pos.Y - size.Height) < tolerance && delta < 0)
|
||||
NextPage();
|
||||
}
|
||||
}
|
||||
|
||||
private void NextPage()
|
||||
@@ -126,7 +113,7 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
if (CurrentPage < PdfHandle.TotalPages - 1)
|
||||
{
|
||||
CurrentPage++;
|
||||
pageViewPanel.ScrollToTop();
|
||||
pagePanel.ScrollToTop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,59 +122,42 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
if (CurrentPage > 0)
|
||||
{
|
||||
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()
|
||||
{
|
||||
if (!PdfLoaded)
|
||||
if (!_pdfLoaded)
|
||||
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();
|
||||
bitmap.Dispose();
|
||||
|
||||
pageViewPanelImage.Source = image;
|
||||
pageViewPanelImage.Width = pageViewPanelImage.Source.Width;
|
||||
pageViewPanelImage.Height = pageViewPanelImage.Source.Height;
|
||||
pagePanel.ResetZoom();
|
||||
pagePanel.Source = image;
|
||||
|
||||
// reset view zoom factor
|
||||
pageViewPanelImage.LayoutTransform = new ScaleTransform();
|
||||
_viewRenderFactor = factor;
|
||||
|
||||
pageViewPanel.InvalidateMeasure();
|
||||
pagePanel.SetScrollPosition(pos);
|
||||
|
||||
GC.Collect();
|
||||
Dispatcher.Delay(500, t => GC.Collect());
|
||||
}
|
||||
|
||||
private void UpdatePageViewWhenSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!PdfLoaded)
|
||||
if (!_pdfLoaded)
|
||||
return;
|
||||
|
||||
if (CurrentPage == -1)
|
||||
@@ -198,21 +168,6 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
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)
|
||||
{
|
||||
var tempHandle = new PdfFile(path);
|
||||
@@ -228,102 +183,17 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
public void LoadPdf(string path)
|
||||
{
|
||||
PageIds.Clear();
|
||||
_whellMonitor?.Dispose();
|
||||
|
||||
PdfHandleForThumbnails = new PdfFile(path);
|
||||
PdfHandle = new PdfFile(path);
|
||||
PdfLoaded = true;
|
||||
_pdfLoaded = true;
|
||||
|
||||
// fill thumbnails list
|
||||
Enumerable.Range(0, PdfHandle.TotalPages).ForEach(PageIds.Add);
|
||||
OnPropertyChanged(nameof(PageIds));
|
||||
OnPropertyChanged("PageIds");
|
||||
|
||||
CurrentPage = 0;
|
||||
|
||||
// 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;
|
||||
};
|
||||
pagePanel.DoZoomToFit(true);
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
|
@@ -73,7 +73,6 @@
|
||||
<Compile Include="DpiHelpers.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="LibMuPdf.cs" />
|
||||
<Compile Include="MouseWheelMonitor.cs" />
|
||||
<Compile Include="PageIdToImageConverter.cs" />
|
||||
<Compile Include="PdfFile.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
@@ -84,6 +83,11 @@
|
||||
<Name>QuickLook</Name>
|
||||
<Private>False</Private>
|
||||
</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>
|
||||
<Content Include="LibMuPdf32.dll">
|
||||
|
Reference in New Issue
Block a user