mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-05 04:39:08 +08:00
[Peek] Add cancellation token OnFilePropertyChanged (#22643)
* Cancel file loading before opening another file * Add omitted cancellation checks * Catch task cancelled exception; Add more cancellation checkpoints * Add cancellation checkpoint beofre GetBitmapFromHBitmapAsync * Correct typo * Update to pass cancellation token individually to each async methods * Add lost cancellationToken source * Add cancellation token to PngPreviewer Co-authored-by: Yawen Hou <yawenhou@microsoft.com>
This commit is contained in:
parent
496220f77e
commit
b075c00f2b
@ -6,6 +6,7 @@ namespace Peek.FilePreviewer
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
@ -42,6 +43,8 @@ namespace Peek.FilePreviewer
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string imageInfoTooltip = ResourceLoader.GetForViewIndependentUse().GetString("PreviewTooltip_Blank");
|
private string imageInfoTooltip = ResourceLoader.GetForViewIndependentUse().GetString("PreviewTooltip_Blank");
|
||||||
|
|
||||||
|
private CancellationTokenSource _cancellationTokenSource = new ();
|
||||||
|
|
||||||
public FilePreview()
|
public FilePreview()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -54,8 +57,12 @@ namespace Peek.FilePreviewer
|
|||||||
{
|
{
|
||||||
if (Previewer?.State == PreviewState.Error)
|
if (Previewer?.State == PreviewState.Error)
|
||||||
{
|
{
|
||||||
|
// Cancel previous loading task
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
_cancellationTokenSource = new ();
|
||||||
|
|
||||||
Previewer = previewerFactory.CreateDefaultPreviewer(File);
|
Previewer = previewerFactory.CreateDefaultPreviewer(File);
|
||||||
await UpdatePreviewAsync();
|
await UpdatePreviewAsync(_cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,6 +96,10 @@ namespace Peek.FilePreviewer
|
|||||||
|
|
||||||
private async Task OnFilePropertyChanged()
|
private async Task OnFilePropertyChanged()
|
||||||
{
|
{
|
||||||
|
// Cancel previous loading task
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
_cancellationTokenSource = new ();
|
||||||
|
|
||||||
// TODO: track and cancel existing async preview tasks
|
// TODO: track and cancel existing async preview tasks
|
||||||
// https://github.com/microsoft/PowerToys/issues/22480
|
// https://github.com/microsoft/PowerToys/issues/22480
|
||||||
if (File == null)
|
if (File == null)
|
||||||
@ -101,20 +112,31 @@ namespace Peek.FilePreviewer
|
|||||||
}
|
}
|
||||||
|
|
||||||
Previewer = previewerFactory.Create(File);
|
Previewer = previewerFactory.Create(File);
|
||||||
await UpdatePreviewAsync();
|
|
||||||
|
await UpdatePreviewAsync(_cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdatePreviewAsync()
|
private async Task UpdatePreviewAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (Previewer != null)
|
if (Previewer != null)
|
||||||
{
|
{
|
||||||
var size = await Previewer.GetPreviewSizeAsync();
|
try
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
var size = await Previewer.GetPreviewSizeAsync(cancellationToken);
|
||||||
SizeFormat windowSizeFormat = UnsupportedFilePreviewer != null ? SizeFormat.Percentage : SizeFormat.Pixels;
|
SizeFormat windowSizeFormat = UnsupportedFilePreviewer != null ? SizeFormat.Percentage : SizeFormat.Pixels;
|
||||||
PreviewSizeChanged?.Invoke(this, new PreviewSizeChangedArgs(size, windowSizeFormat));
|
PreviewSizeChanged?.Invoke(this, new PreviewSizeChangedArgs(size, windowSizeFormat));
|
||||||
await Previewer.LoadPreviewAsync();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
}
|
await Previewer.LoadPreviewAsync(cancellationToken);
|
||||||
|
|
||||||
await UpdateImageTooltipAsync();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
await UpdateImageTooltipAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// TODO: Log task cancelled exception?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnPreviewerChanging(IPreviewer? value)
|
partial void OnPreviewerChanging(IPreviewer? value)
|
||||||
@ -139,7 +161,7 @@ namespace Peek.FilePreviewer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateImageTooltipAsync()
|
private async Task UpdateImageTooltipAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (File == null)
|
if (File == null)
|
||||||
{
|
{
|
||||||
@ -152,6 +174,7 @@ namespace Peek.FilePreviewer
|
|||||||
string fileNameFormatted = ReadableStringHelper.FormatResourceString("PreviewTooltip_FileName", File.FileName);
|
string fileNameFormatted = ReadableStringHelper.FormatResourceString("PreviewTooltip_FileName", File.FileName);
|
||||||
sb.Append(fileNameFormatted);
|
sb.Append(fileNameFormatted);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
string fileType = await PropertyHelper.GetFileType(File.Path);
|
string fileType = await PropertyHelper.GetFileType(File.Path);
|
||||||
string fileTypeFormatted = string.IsNullOrEmpty(fileType) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_FileType", fileType);
|
string fileTypeFormatted = string.IsNullOrEmpty(fileType) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_FileType", fileType);
|
||||||
sb.Append(fileTypeFormatted);
|
sb.Append(fileTypeFormatted);
|
||||||
@ -160,10 +183,12 @@ namespace Peek.FilePreviewer
|
|||||||
string dateModifiedFormatted = string.IsNullOrEmpty(dateModified) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_DateModified", dateModified);
|
string dateModifiedFormatted = string.IsNullOrEmpty(dateModified) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_DateModified", dateModified);
|
||||||
sb.Append(dateModifiedFormatted);
|
sb.Append(dateModifiedFormatted);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
Size dimensions = await PropertyHelper.GetImageSize(File.Path);
|
Size dimensions = await PropertyHelper.GetImageSize(File.Path);
|
||||||
string dimensionsFormatted = dimensions.IsEmpty ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_Dimensions", dimensions.Width, dimensions.Height);
|
string dimensionsFormatted = dimensions.IsEmpty ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_Dimensions", dimensions.Width, dimensions.Height);
|
||||||
sb.Append(dimensionsFormatted);
|
sb.Append(dimensionsFormatted);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
ulong bytes = await PropertyHelper.GetFileSizeInBytes(File.Path);
|
ulong bytes = await PropertyHelper.GetFileSizeInBytes(File.Path);
|
||||||
string fileSize = ReadableStringHelper.BytesToReadableString(bytes);
|
string fileSize = ReadableStringHelper.BytesToReadableString(bytes);
|
||||||
string fileSizeFormatted = string.IsNullOrEmpty(fileSize) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_FileSize", fileSize);
|
string fileSizeFormatted = string.IsNullOrEmpty(fileSize) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_FileSize", fileSize);
|
||||||
|
@ -6,6 +6,7 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
|
|
||||||
@ -15,9 +16,9 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
|
|
||||||
public static bool IsFileTypeSupported(string fileExt) => throw new NotImplementedException();
|
public static bool IsFileTypeSupported(string fileExt) => throw new NotImplementedException();
|
||||||
|
|
||||||
public Task<Size> GetPreviewSizeAsync();
|
public Task<Size> GetPreviewSizeAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task LoadPreviewAsync();
|
Task LoadPreviewAsync(CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PreviewState
|
public enum PreviewState
|
||||||
|
@ -49,18 +49,14 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
|
|
||||||
private bool IsFullImageLoaded => FullQualityImageTask?.Status == TaskStatus.RanToCompletion;
|
private bool IsFullImageLoaded => FullQualityImageTask?.Status == TaskStatus.RanToCompletion;
|
||||||
|
|
||||||
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
private CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Size> GetPreviewSizeAsync()
|
public async Task<Size> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var propertyImageSize = await PropertyHelper.GetImageSize(File.Path);
|
var propertyImageSize = await PropertyHelper.GetImageSize(File.Path);
|
||||||
if (propertyImageSize != Size.Empty)
|
if (propertyImageSize != Size.Empty)
|
||||||
{
|
{
|
||||||
@ -70,13 +66,14 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
return await WICHelper.GetImageSize(File.Path);
|
return await WICHelper.GetImageSize(File.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadPreviewAsync()
|
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
State = PreviewState.Loading;
|
State = PreviewState.Loading;
|
||||||
|
|
||||||
LowQualityThumbnailTask = LoadLowQualityThumbnailAsync();
|
LowQualityThumbnailTask = LoadLowQualityThumbnailAsync(cancellationToken);
|
||||||
HighQualityThumbnailTask = LoadHighQualityThumbnailAsync();
|
HighQualityThumbnailTask = LoadHighQualityThumbnailAsync(cancellationToken);
|
||||||
FullQualityImageTask = LoadFullQualityImageAsync();
|
FullQualityImageTask = LoadFullQualityImageAsync(cancellationToken);
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
await Task.WhenAll(LowQualityThumbnailTask, HighQualityThumbnailTask, FullQualityImageTask);
|
await Task.WhenAll(LowQualityThumbnailTask, HighQualityThumbnailTask, FullQualityImageTask);
|
||||||
|
|
||||||
@ -97,15 +94,11 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<bool> LoadLowQualityThumbnailAsync()
|
private Task<bool> LoadLowQualityThumbnailAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return TaskExtension.RunSafe(async () =>
|
return TaskExtension.RunSafe(async () =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsFullImageLoaded && !IsHighQualityThumbnailLoaded)
|
if (!IsFullImageLoaded && !IsHighQualityThumbnailLoaded)
|
||||||
{
|
{
|
||||||
@ -117,24 +110,23 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
throw new ArgumentNullException(nameof(hbitmap));
|
throw new ArgumentNullException(nameof(hbitmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
await Dispatcher.RunOnUiThread(async () =>
|
await Dispatcher.RunOnUiThread(async () =>
|
||||||
{
|
{
|
||||||
var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap, cancellationToken);
|
||||||
Preview = thumbnailBitmap;
|
Preview = thumbnailBitmap;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<bool> LoadHighQualityThumbnailAsync()
|
private Task<bool> LoadHighQualityThumbnailAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return TaskExtension.RunSafe(async () =>
|
return TaskExtension.RunSafe(async () =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsFullImageLoaded)
|
if (!IsFullImageLoaded)
|
||||||
{
|
{
|
||||||
@ -146,29 +138,29 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
throw new ArgumentNullException(nameof(hbitmap));
|
throw new ArgumentNullException(nameof(hbitmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
await Dispatcher.RunOnUiThread(async () =>
|
await Dispatcher.RunOnUiThread(async () =>
|
||||||
{
|
{
|
||||||
var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap, cancellationToken);
|
||||||
Preview = thumbnailBitmap;
|
Preview = thumbnailBitmap;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<bool> LoadFullQualityImageAsync()
|
private Task<bool> LoadFullQualityImageAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return TaskExtension.RunSafe(async () =>
|
return TaskExtension.RunSafe(async () =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Check if this is performant
|
// TODO: Check if this is performant
|
||||||
await Dispatcher.RunOnUiThread(async () =>
|
await Dispatcher.RunOnUiThread(async () =>
|
||||||
{
|
{
|
||||||
var bitmap = await GetFullBitmapFromPathAsync(File.Path);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
var bitmap = await GetFullBitmapFromPathAsync(File.Path, cancellationToken);
|
||||||
Preview = bitmap;
|
Preview = bitmap;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -183,27 +175,34 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
return hasFailedLoadingLowQualityThumbnail && hasFailedLoadingHighQualityThumbnail && hasFailedLoadingFullQualityImage;
|
return hasFailedLoadingLowQualityThumbnail && hasFailedLoadingHighQualityThumbnail && hasFailedLoadingFullQualityImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<BitmapImage> GetFullBitmapFromPathAsync(string path)
|
private static async Task<BitmapImage> GetFullBitmapFromPathAsync(string path, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var bitmap = new BitmapImage();
|
var bitmap = new BitmapImage();
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
using (FileStream stream = System.IO.File.OpenRead(path))
|
using (FileStream stream = System.IO.File.OpenRead(path))
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
await bitmap.SetSourceAsync(stream.AsRandomAccessStream());
|
await bitmap.SetSourceAsync(stream.AsRandomAccessStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<BitmapSource> GetBitmapFromHBitmapAsync(IntPtr hbitmap)
|
private static async Task<BitmapSource> GetBitmapFromHBitmapAsync(IntPtr hbitmap, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var bitmap = System.Drawing.Image.FromHbitmap(hbitmap);
|
var bitmap = System.Drawing.Image.FromHbitmap(hbitmap);
|
||||||
var bitmapImage = new BitmapImage();
|
var bitmapImage = new BitmapImage();
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
using (var stream = new MemoryStream())
|
using (var stream = new MemoryStream())
|
||||||
{
|
{
|
||||||
bitmap.Save(stream, ImageFormat.Bmp);
|
bitmap.Save(stream, ImageFormat.Bmp);
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
|
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,19 +46,14 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
|
|
||||||
private Task<bool>? FullQualityImageTask { get; set; }
|
private Task<bool>? FullQualityImageTask { get; set; }
|
||||||
|
|
||||||
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
private CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
|
||||||
|
|
||||||
private bool IsFullImageLoaded => FullQualityImageTask?.Status == TaskStatus.RanToCompletion;
|
private bool IsFullImageLoaded => FullQualityImageTask?.Status == TaskStatus.RanToCompletion;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Size> GetPreviewSizeAsync()
|
public async Task<Size> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var propertyImageSize = await PropertyHelper.GetImageSize(File.Path);
|
var propertyImageSize = await PropertyHelper.GetImageSize(File.Path);
|
||||||
if (propertyImageSize != Size.Empty)
|
if (propertyImageSize != Size.Empty)
|
||||||
@ -66,16 +61,18 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
return propertyImageSize;
|
return propertyImageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
return await WICHelper.GetImageSize(File.Path);
|
return await WICHelper.GetImageSize(File.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadPreviewAsync()
|
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
State = PreviewState.Loading;
|
State = PreviewState.Loading;
|
||||||
|
|
||||||
PreviewQualityThumbnailTask = LoadPreviewImageAsync();
|
PreviewQualityThumbnailTask = LoadPreviewImageAsync(cancellationToken);
|
||||||
FullQualityImageTask = LoadFullImageAsync();
|
FullQualityImageTask = LoadFullImageAsync(cancellationToken);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
await Task.WhenAll(PreviewQualityThumbnailTask, FullQualityImageTask);
|
await Task.WhenAll(PreviewQualityThumbnailTask, FullQualityImageTask);
|
||||||
|
|
||||||
if (Preview == null)
|
if (Preview == null)
|
||||||
@ -100,56 +97,54 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<bool> LoadPreviewImageAsync()
|
private Task<bool> LoadPreviewImageAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var thumbnailTCS = new TaskCompletionSource();
|
var thumbnailTCS = new TaskCompletionSource();
|
||||||
return TaskExtension.RunSafe(async () =>
|
return TaskExtension.RunSafe(async () =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsFullImageLoaded)
|
if (!IsFullImageLoaded)
|
||||||
{
|
{
|
||||||
await Dispatcher.RunOnUiThread(async () =>
|
await Dispatcher.RunOnUiThread(async () =>
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
Preview = await ThumbnailHelper.GetThumbnailAsync(File.Path, _png_image_size);
|
Preview = await ThumbnailHelper.GetThumbnailAsync(File.Path, _png_image_size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<bool> LoadFullImageAsync()
|
private Task<bool> LoadFullImageAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var thumbnailTCS = new TaskCompletionSource();
|
var thumbnailTCS = new TaskCompletionSource();
|
||||||
return TaskExtension.RunSafe(async () =>
|
return TaskExtension.RunSafe(async () =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Dispatcher.RunOnUiThread(async () =>
|
await Dispatcher.RunOnUiThread(async () =>
|
||||||
{
|
{
|
||||||
WriteableBitmap? bitmap = null;
|
WriteableBitmap? bitmap = null;
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var sFile = await StorageFile.GetFileFromPathAsync(File.Path);
|
var sFile = await StorageFile.GetFileFromPathAsync(File.Path);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
using (var randomAccessStream = await sFile.OpenStreamForReadAsync())
|
using (var randomAccessStream = await sFile.OpenStreamForReadAsync())
|
||||||
{
|
{
|
||||||
// Create an encoder with the desired format
|
// Create an encoder with the desired format
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var decoder = await BitmapDecoder.CreateAsync(
|
var decoder = await BitmapDecoder.CreateAsync(
|
||||||
BitmapDecoder.PngDecoderId,
|
BitmapDecoder.PngDecoderId,
|
||||||
randomAccessStream.AsRandomAccessStream());
|
randomAccessStream.AsRandomAccessStream());
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var softwareBitmap = await decoder.GetSoftwareBitmapAsync(
|
var softwareBitmap = await decoder.GetSoftwareBitmapAsync(
|
||||||
BitmapPixelFormat.Bgra8,
|
BitmapPixelFormat.Bgra8,
|
||||||
BitmapAlphaMode.Premultiplied);
|
BitmapAlphaMode.Premultiplied);
|
||||||
|
|
||||||
// full quality image
|
// full quality image
|
||||||
bitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
|
bitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
softwareBitmap?.CopyToBuffer(bitmap.PixelBuffer);
|
softwareBitmap?.CopyToBuffer(bitmap.PixelBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
namespace Peek.FilePreviewer.Previewers
|
namespace Peek.FilePreviewer.Previewers
|
||||||
{
|
{
|
||||||
|
using System.Threading;
|
||||||
using Peek.Common.Models;
|
using Peek.Common.Models;
|
||||||
|
|
||||||
public class PreviewerFactory
|
public class PreviewerFactory
|
||||||
|
@ -70,21 +70,16 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
|
|
||||||
private DispatcherQueue Dispatcher { get; }
|
private DispatcherQueue Dispatcher { get; }
|
||||||
|
|
||||||
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
private CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
|
||||||
|
|
||||||
private Task<bool>? IconPreviewTask { get; set; }
|
private Task<bool>? IconPreviewTask { get; set; }
|
||||||
|
|
||||||
private Task<bool>? DisplayInfoTask { get; set; }
|
private Task<bool>? DisplayInfoTask { get; set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Size> GetPreviewSizeAsync()
|
public Task<Size> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.Run(() =>
|
return Task.Run(() =>
|
||||||
{
|
{
|
||||||
@ -92,12 +87,14 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadPreviewAsync()
|
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
State = PreviewState.Loading;
|
State = PreviewState.Loading;
|
||||||
|
|
||||||
IconPreviewTask = LoadIconPreviewAsync();
|
IconPreviewTask = LoadIconPreviewAsync(cancellationToken);
|
||||||
DisplayInfoTask = LoadDisplayInfoAsync();
|
DisplayInfoTask = LoadDisplayInfoAsync(cancellationToken);
|
||||||
|
|
||||||
await Task.WhenAll(IconPreviewTask, DisplayInfoTask);
|
await Task.WhenAll(IconPreviewTask, DisplayInfoTask);
|
||||||
|
|
||||||
@ -107,38 +104,34 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> LoadIconPreviewAsync()
|
public Task<bool> LoadIconPreviewAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return TaskExtension.RunSafe(async () =>
|
return TaskExtension.RunSafe(async () =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Get icon with transparency
|
// TODO: Get icon with transparency
|
||||||
IconHelper.GetIcon(Path.GetFullPath(File.Path), out IntPtr hbitmap);
|
IconHelper.GetIcon(Path.GetFullPath(File.Path), out IntPtr hbitmap);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
await Dispatcher.RunOnUiThread(async () =>
|
await Dispatcher.RunOnUiThread(async () =>
|
||||||
{
|
{
|
||||||
var iconBitmap = await GetBitmapFromHBitmapAsync(hbitmap);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
var iconBitmap = await GetBitmapFromHBitmapAsync(hbitmap, cancellationToken);
|
||||||
IconPreview = iconBitmap;
|
IconPreview = iconBitmap;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> LoadDisplayInfoAsync()
|
public Task<bool> LoadDisplayInfoAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return TaskExtension.RunSafe(async () =>
|
return TaskExtension.RunSafe(async () =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// File Properties
|
// File Properties
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var bytes = await PropertyHelper.GetFileSizeInBytes(File.Path);
|
var bytes = await PropertyHelper.GetFileSizeInBytes(File.Path);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var type = await PropertyHelper.GetFileType(File.Path);
|
var type = await PropertyHelper.GetFileType(File.Path);
|
||||||
|
|
||||||
await Dispatcher.RunOnUiThread(() =>
|
await Dispatcher.RunOnUiThread(() =>
|
||||||
@ -169,17 +162,22 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
return hasFailedLoadingIconPreview && hasFailedLoadingDisplayInfo;
|
return hasFailedLoadingIconPreview && hasFailedLoadingDisplayInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move this to a helper file (ImagePrevier uses the same code)
|
// TODO: Move this to a helper file (ImagePreviewer uses the same code)
|
||||||
private static async Task<BitmapSource> GetBitmapFromHBitmapAsync(IntPtr hbitmap)
|
private static async Task<BitmapSource> GetBitmapFromHBitmapAsync(IntPtr hbitmap, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
var bitmap = System.Drawing.Image.FromHbitmap(hbitmap);
|
var bitmap = System.Drawing.Image.FromHbitmap(hbitmap);
|
||||||
var bitmapImage = new BitmapImage();
|
var bitmapImage = new BitmapImage();
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
using (var stream = new MemoryStream())
|
using (var stream = new MemoryStream())
|
||||||
{
|
{
|
||||||
bitmap.Save(stream, ImageFormat.Bmp);
|
bitmap.Save(stream, ImageFormat.Bmp);
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
|
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,6 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Microsoft.UI.Dispatching;
|
|
||||||
using Peek.FilePreviewer.Controls;
|
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using File = Peek.Common.Models.File;
|
using File = Peek.Common.Models.File;
|
||||||
|
|
||||||
@ -39,14 +37,14 @@ namespace Peek.FilePreviewer.Previewers
|
|||||||
|
|
||||||
private File File { get; }
|
private File File { get; }
|
||||||
|
|
||||||
public Task<Size> GetPreviewSizeAsync()
|
public Task<Size> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// TODO: define how to proper window size on HTML content.
|
// TODO: define how to proper window size on HTML content.
|
||||||
var size = new Size(1280, 720);
|
var size = new Size(1280, 720);
|
||||||
return Task.FromResult(size);
|
return Task.FromResult(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task LoadPreviewAsync()
|
public Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
State = PreviewState.Loading;
|
State = PreviewState.Loading;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user