mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
Consolidated IconHelper and ThumbnailHelper. Fixed icon memory leak. Fixed ImagePreviewer thumbnails being created and then not used. Refactored ImagePreviewer.
217 lines
6.8 KiB
C#
217 lines
6.8 KiB
C#
// Copyright (c) Microsoft Corporation
|
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using ManagedCommon;
|
|
using Microsoft.UI.Dispatching;
|
|
using Microsoft.UI.Xaml;
|
|
using Microsoft.UI.Xaml.Media.Imaging;
|
|
using Peek.Common.Extensions;
|
|
using Peek.Common.Helpers;
|
|
using Peek.Common.Models;
|
|
using Peek.FilePreviewer.Models;
|
|
using Peek.FilePreviewer.Previewers.Helpers;
|
|
using Windows.Foundation;
|
|
|
|
namespace Peek.FilePreviewer.Previewers;
|
|
|
|
public partial class SpecialFolderPreviewer : ObservableObject, ISpecialFolderPreviewer, IDisposable
|
|
{
|
|
private readonly DispatcherTimer _syncDetailsDispatcherTimer = new();
|
|
private ulong _folderSize;
|
|
private DateTime? _dateModified;
|
|
|
|
[ObservableProperty]
|
|
private SpecialFolderPreviewData preview = new();
|
|
|
|
[ObservableProperty]
|
|
private PreviewState state;
|
|
|
|
public SpecialFolderPreviewer(IFileSystemItem file)
|
|
{
|
|
_syncDetailsDispatcherTimer.Interval = TimeSpan.FromMilliseconds(500);
|
|
_syncDetailsDispatcherTimer.Tick += DetailsDispatcherTimer_Tick;
|
|
|
|
Item = file;
|
|
Preview.FileName = file.Name;
|
|
Dispatcher = DispatcherQueue.GetForCurrentThread();
|
|
}
|
|
|
|
public static bool IsItemSupported(IFileSystemItem item)
|
|
{
|
|
// Always allow know special folders.
|
|
bool isKnownSpecialFolder = KnownSpecialFolders.FoldersByParsingName.ContainsKey(item.ParsingName);
|
|
|
|
// Allow empty paths unless Unc; icons don't load correctly for Unc paths.
|
|
bool isEmptyNonUncPath = string.IsNullOrEmpty(item.Path) && !PathHelper.IsUncPath(item.ParsingName);
|
|
|
|
return isKnownSpecialFolder || isEmptyNonUncPath;
|
|
}
|
|
|
|
private IFileSystemItem Item { get; }
|
|
|
|
private DispatcherQueue Dispatcher { get; }
|
|
|
|
public void Dispose()
|
|
{
|
|
_syncDetailsDispatcherTimer.Tick -= DetailsDispatcherTimer_Tick;
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public Task<PreviewSize> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
|
{
|
|
Size? size = new(680, 500);
|
|
var previewSize = new PreviewSize { MonitorSize = size, UseEffectivePixels = true };
|
|
return Task.FromResult(previewSize);
|
|
}
|
|
|
|
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
State = PreviewState.Loading;
|
|
|
|
var tasks = await Task.WhenAll(LoadIconPreviewAsync(cancellationToken), LoadDisplayInfoAsync(cancellationToken));
|
|
|
|
State = tasks.All(task => task) ? PreviewState.Loaded : PreviewState.Error;
|
|
}
|
|
|
|
public async Task CopyAsync()
|
|
{
|
|
await Dispatcher.RunOnUiThread(async () =>
|
|
{
|
|
var storageItem = await Item.GetStorageItemAsync();
|
|
ClipboardHelper.SaveToClipboard(storageItem);
|
|
});
|
|
}
|
|
|
|
public async Task<bool> LoadIconPreviewAsync(CancellationToken cancellationToken)
|
|
{
|
|
bool isIconValid = false;
|
|
|
|
var isTaskSuccessful = await TaskExtension.RunSafe(async () =>
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
await Dispatcher.RunOnUiThread(async () =>
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
var iconBitmap = await ThumbnailHelper.GetIconAsync(Item.ParsingName, cancellationToken);
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
isIconValid = iconBitmap != null;
|
|
Preview.IconPreview = iconBitmap ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg"));
|
|
});
|
|
});
|
|
|
|
return isIconValid && isTaskSuccessful;
|
|
}
|
|
|
|
public async Task<bool> LoadDisplayInfoAsync(CancellationToken cancellationToken)
|
|
{
|
|
bool isDisplayValid = false;
|
|
|
|
var isTaskSuccessful = await TaskExtension.RunSafe(async () =>
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
var fileType = await Task.Run(Item.GetContentTypeAsync);
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
isDisplayValid = fileType != null;
|
|
|
|
await Dispatcher.RunOnUiThread(() =>
|
|
{
|
|
Preview.FileType = fileType;
|
|
return Task.CompletedTask;
|
|
});
|
|
|
|
RunUpdateDetailsWorkflow(cancellationToken);
|
|
});
|
|
|
|
return isDisplayValid && isTaskSuccessful;
|
|
}
|
|
|
|
private void RunUpdateDetailsWorkflow(CancellationToken cancellationToken)
|
|
{
|
|
Task.Run(
|
|
async () =>
|
|
{
|
|
try
|
|
{
|
|
await Dispatcher.RunOnUiThread(_syncDetailsDispatcherTimer.Start);
|
|
ComputeDetails(cancellationToken);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError("Failed to update special folder details", ex);
|
|
}
|
|
finally
|
|
{
|
|
await Dispatcher.RunOnUiThread(_syncDetailsDispatcherTimer.Stop);
|
|
}
|
|
|
|
await Dispatcher.RunOnUiThread(SyncDetails);
|
|
},
|
|
cancellationToken);
|
|
}
|
|
|
|
private void ComputeDetails(CancellationToken cancellationToken)
|
|
{
|
|
_dateModified = Item.DateModified;
|
|
|
|
switch (KnownSpecialFolders.FoldersByParsingName.GetValueOrDefault(Item.ParsingName, KnownSpecialFolder.None))
|
|
{
|
|
case KnownSpecialFolder.None:
|
|
break;
|
|
|
|
case KnownSpecialFolder.RecycleBin:
|
|
ThreadHelper.RunOnSTAThread(() => { ComputeRecycleBinDetails(cancellationToken); });
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ComputeRecycleBinDetails(CancellationToken cancellationToken)
|
|
{
|
|
var shell = new Shell32.Shell();
|
|
var recycleBin = shell.NameSpace(10); // CSIDL_BITBUCKET
|
|
|
|
foreach (dynamic item in recycleBin.Items())
|
|
{
|
|
if (cancellationToken.IsCancellationRequested)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_folderSize += Convert.ToUInt64(item.Size);
|
|
}
|
|
}
|
|
|
|
private void SyncDetails()
|
|
{
|
|
Preview.FileSize = _folderSize == 0 ? string.Empty : ReadableStringHelper.BytesToReadableString(_folderSize);
|
|
Preview.DateModified = _dateModified?.ToString(CultureInfo.CurrentCulture) ?? string.Empty;
|
|
}
|
|
|
|
private void DetailsDispatcherTimer_Tick(object? sender, object e)
|
|
{
|
|
SyncDetails();
|
|
}
|
|
}
|