[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:
Yawen Hou 2022-12-09 11:25:51 -05:00 committed by GitHub
parent 496220f77e
commit b075c00f2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 96 deletions

View File

@ -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
SizeFormat windowSizeFormat = UnsupportedFilePreviewer != null ? SizeFormat.Percentage : SizeFormat.Pixels; {
PreviewSizeChanged?.Invoke(this, new PreviewSizeChangedArgs(size, windowSizeFormat)); cancellationToken.ThrowIfCancellationRequested();
await Previewer.LoadPreviewAsync(); var size = await Previewer.GetPreviewSizeAsync(cancellationToken);
} SizeFormat windowSizeFormat = UnsupportedFilePreviewer != null ? SizeFormat.Percentage : SizeFormat.Pixels;
PreviewSizeChanged?.Invoke(this, new PreviewSizeChangedArgs(size, windowSizeFormat));
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);

View File

@ -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

View File

@ -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());
} }

View File

@ -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);
} }

View File

@ -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

View File

@ -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());
} }

View File

@ -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;