Skip to content
203 changes: 118 additions & 85 deletions src/Files.App/Data/Models/ItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -937,117 +937,142 @@ private async Task<BitmapImage> GetShieldIcon()
return shieldIcon;
}

private async Task LoadItemThumbnailAsync(ListedItem item)
/// <summary>
/// Loads basic icon using ReturnIconOnly flag
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private async Task LoadBasicIconAsync(ListedItem item)
{
var thumbnailSize = folderSettings.GetRoundedIconSize();

if (item.IsLibrary || item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive)
{
var getIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48;
var getThumbnailOnly = !item.IsExecutable && !getIconOnly;
var iconInfo = await FileThumbnailHelper.GetIconAsync(
// Get icon
var icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
folderSettings.GetRoundedIconSize(),
item.IsFolder,
false,
getThumbnailOnly,
getIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None);
IconOptions.ReturnIconOnly);

if (!iconInfo.isIconCached)
{
// Assign a placeholder icon while trying to get a cached thumbnail
if (iconInfo.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

// Loop until cached thumbnail is loaded or timeout is reached
var cancellationTokenSource = new CancellationTokenSource(3000);
while (!iconInfo.isIconCached)
{
iconInfo = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
false,
getThumbnailOnly,
getIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None);

if (cancellationTokenSource.Token.IsCancellationRequested)
break;

await Task.Delay(500);
}
}

if (iconInfo.IconData is not null)
if (icon.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
var image = await icon.IconData.ToBitmapAsync();
if (image is not null)
{
// Assign the thumbnail/icon to the listed item
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
// Assign FileImage property
item.FileImage = image;

// Add the file icon to the DefaultIcons list
// Add file icon to the DefaultIcons list
if
(
!item.IsFolder &&
!DefaultIcons.ContainsKey(item.FileExtension.ToLowerInvariant()) &&
!string.IsNullOrEmpty(item.FileExtension) &&
!item.IsShortcut &&
!item.IsExecutable
)
{
var fileIcon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
false,
false,
IconOptions.ReturnIconOnly);

var bitmapImage = await fileIcon.IconData.ToBitmapAsync();
DefaultIcons.TryAdd(item.FileExtension.ToLowerInvariant(), bitmapImage);
DefaultIcons.TryAdd(item.FileExtension.ToLowerInvariant(), image);
}
}
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
// Get icon overlay
var iconOverlay = await FileThumbnailHelper.GetIconOverlayAsync(item.ItemPath, true);
if (iconOverlay is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconOverlay.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}

/// <summary>
/// Loads thumbnail without any flags
/// Returns early if thumbnails aren't needed for this item (eg. if thumbnails are disabled or size is too small)
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private async Task LoadThumbnailAsync(ListedItem item)
{
// Cancel if thumbnails aren't enabled
var thumbnailSize = folderSettings.GetRoundedIconSize();
if (UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48)
return;

// Get thumbnail
var icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
item.IsFolder,
false,
IconOptions.None);

var iconOverlay = await FileThumbnailHelper.GetIconOverlayAsync(item.ItemPath, false);
if (iconOverlay is not null)
if (icon.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
// Assign the icon overlay to the listed item
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconOverlay.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
// Assign FileImage property
var image = await icon.IconData.ToBitmapAsync();
if (image is not null)
item.FileImage = image;
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
else
}

/// <summary>
/// Tries getting a cached thumbnail from the system. This is usually only needed for cloud locations, otherwise LoadThumbnailAsync does the job.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private async Task LoadCachedThumbnailAsync(ListedItem item)
{
// Cancel for exe files which don't need thumbnails
if (item.IsExecutable)
return;

// Cancel if thumbnails aren't enabled
var thumbnailSize = folderSettings.GetRoundedIconSize();
if (UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48)
return;

// Get icon
var icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
item.IsFolder,
true,
IconOptions.None);

// Loop until cached thumbnail is loaded or timeout is reached
var cancellationTokenSource = new CancellationTokenSource(3000);
while (true)
{
var getIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48;
var iconInfo = await FileThumbnailHelper.GetIconAsync(
icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
item.IsFolder,
true,
false, getIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None);
IconOptions.None);

if (iconInfo.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
if (icon.isIconCached || cancellationTokenSource.Token.IsCancellationRequested)
break;

await Task.Delay(500);
}

var iconOverlay = await FileThumbnailHelper.GetIconOverlayAsync(item.ItemPath, true);
if (iconOverlay is not null)
// Update FileImage property if icon is cached
if (icon.isIconCached)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconOverlay.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
var image = await icon.IconData.ToBitmapAsync();
if (image is not null)
item.FileImage = image;
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}

Expand Down Expand Up @@ -1092,7 +1117,7 @@ await Task.Run(async () =>
}

cts.Token.ThrowIfCancellationRequested();
_ = LoadItemThumbnailAsync(item);
await LoadBasicIconAsync(item);

if (item.IsLibrary || item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive)
{
Expand Down Expand Up @@ -1198,6 +1223,14 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() =>
SetFileTag(item);
});
}
else
{
// Load cached thumbnail for cloud files
if (item.SyncStatusUI.SyncStatus != CloudDriveSyncStatus.NotSynced && item.SyncStatusUI.SyncStatus != CloudDriveSyncStatus.Unknown)
_ = LoadCachedThumbnailAsync(item);
else
_ = LoadThumbnailAsync(item);
}

if (loadGroupHeaderInfo)
{
Expand Down