Skip to content

Commit e597709

Browse files
authored
Feature: Improved thumbnail placeholder performance (#18179)
1 parent 1de5b74 commit e597709

File tree

6 files changed

+76
-0
lines changed

6 files changed

+76
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Data.Contracts
5+
{
6+
internal interface IIconCacheService
7+
{
8+
Task<byte[]?> GetIconAsync(string itemPath, string? extension, bool isFolder);
9+
10+
void Clear();
11+
}
12+
}

src/Files.App/Data/Items/ListedItem.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public partial class ListedItem : ObservableObject, IGroupableItem, IListedItem
3030

3131
public StorageItemTypes PrimaryItemAttribute { get; set; }
3232

33+
public byte[]? PreloadedIconData { get; set; }
34+
3335
private volatile int itemPropertiesInitialized = 0;
3436
public bool ItemPropertiesInitialized
3537
{

src/Files.App/Helpers/Application/AppLifecycleHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ public static IHost ConfigureHost()
260260
.AddSingleton<INetworkService, NetworkService>()
261261
.AddSingleton<IStartMenuService, StartMenuService>()
262262
.AddSingleton<IStorageCacheService, StorageCacheService>()
263+
.AddSingleton<IIconCacheService, IconCacheService>()
263264
.AddSingleton<IStorageArchiveService, StorageArchiveService>()
264265
.AddSingleton<IStorageSecurityService, StorageSecurityService>()
265266
.AddSingleton<IWindowsCompatibilityService, WindowsCompatibilityService>()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Files.Shared.Helpers;
5+
using System.Collections.Concurrent;
6+
using System.IO;
7+
8+
namespace Files.App.Services
9+
{
10+
internal sealed class IconCacheService : IIconCacheService
11+
{
12+
// Dummy path to generate generic icons for folders and executables.
13+
private static readonly string _dummyPath = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory)!, "x46696c6573");
14+
15+
private readonly ConcurrentDictionary<string, byte[]?> _cache = new();
16+
17+
public async Task<byte[]?> GetIconAsync(string itemPath, string? extension, bool isFolder)
18+
{
19+
var key = isFolder ? ":folder:" : (extension?.ToLowerInvariant() ?? ":noext:");
20+
21+
if (_cache.TryGetValue(key, out var cached))
22+
return cached;
23+
24+
string iconPath;
25+
if (isFolder)
26+
iconPath = _dummyPath;
27+
else if (FileExtensionHelpers.IsExecutableFile(extension))
28+
iconPath = _dummyPath + extension;
29+
else
30+
iconPath = itemPath;
31+
32+
var icon = await FileThumbnailHelper.GetIconAsync(
33+
iconPath,
34+
Constants.ShellIconSizes.Jumbo,
35+
isFolder,
36+
IconOptions.ReturnIconOnly);
37+
38+
_cache.TryAdd(key, icon);
39+
return icon;
40+
}
41+
42+
public void Clear()
43+
{
44+
_cache.Clear();
45+
}
46+
}
47+
}

src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public static class Win32StorageEnumerator
1616

1717
private static readonly string folderTypeTextLocalized = Strings.Folder.GetLocalizedResource();
1818

19+
private static readonly IIconCacheService iconCacheService = Ioc.Default.GetRequiredService<IIconCacheService>();
20+
1921
public static async Task<List<ListedItem>> ListEntries(
2022
string path,
2123
IntPtr hFile,
@@ -49,6 +51,7 @@ Func<List<ListedItem>, Task> intermediateAction
4951
var file = await GetFile(findData, path, isGitRepo, cancellationToken);
5052
if (file is not null)
5153
{
54+
file.PreloadedIconData = await iconCacheService.GetIconAsync(file.ItemPath, file.FileExtension, false);
5255
tempList.Add(file);
5356
++count;
5457

@@ -65,6 +68,7 @@ Func<List<ListedItem>, Task> intermediateAction
6568
var folder = await GetFolder(findData, path, isGitRepo, cancellationToken);
6669
if (folder is not null)
6770
{
71+
folder.PreloadedIconData = await iconCacheService.GetIconAsync(folder.ItemPath, null, true);
6872
tempList.Add(folder);
6973
++count;
7074

src/Files.App/Views/Layouts/BaseLayoutPage.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,9 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu
12411241
{
12421242
InitializeDrag(container, listedItem);
12431243

1244+
if (listedItem.PreloadedIconData is not null && listedItem.FileImage is null)
1245+
_ = ApplyPreloadedIconAsync(listedItem);
1246+
12441247
if (!listedItem.ItemPropertiesInitialized)
12451248
{
12461249
uint callbackPhase = 3;
@@ -1254,6 +1257,13 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu
12541257
}
12551258
}
12561259

1260+
private static async Task ApplyPreloadedIconAsync(ListedItem item)
1261+
{
1262+
var image = await item.PreloadedIconData.ToBitmapAsync();
1263+
if (image is not null)
1264+
item.FileImage = image;
1265+
}
1266+
12571267
protected internal void FileListItem_PointerPressed(object sender, PointerRoutedEventArgs e)
12581268
{
12591269
// Set can window to front and bring the window to the front if necessary (#13255)

0 commit comments

Comments
 (0)