mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-05-16 18:32:40 +12:00
Improve icon loading performance
Revert always loading common file icon extensions in sync. (It slows down load time of sub menu significantly, so keep using async) Fix preferring persistent icons for watcher updates. Simplified icon loading routines.
This commit is contained in:
parent
425bd91160
commit
5314af937f
|
@ -726,7 +726,7 @@ namespace SystemTrayMenu.Business
|
|||
|
||||
IconReader.RemoveIconFromCache(rowData.Path);
|
||||
rowDataRenamed.HiddenEntry = hasHiddenFlag;
|
||||
rowDataRenamed.LoadIcon(false);
|
||||
rowDataRenamed.LoadIcon(true);
|
||||
rowDatas.Add(rowDataRenamed);
|
||||
}
|
||||
else
|
||||
|
@ -806,7 +806,7 @@ namespace SystemTrayMenu.Business
|
|||
}
|
||||
|
||||
rowData.HiddenEntry = hasHiddenFlag;
|
||||
rowData.LoadIcon(false);
|
||||
rowData.LoadIcon(true);
|
||||
|
||||
var items = (List<RowData>)menu.GetDataGridView().Items.SourceCollection;
|
||||
List<RowData> rowDatas = new(items.Count + 1) { rowData };
|
||||
|
|
|
@ -229,20 +229,9 @@ namespace SystemTrayMenu.DataClasses
|
|||
/// <param name="propertyName">Name of the changing property.</param>
|
||||
public void CallPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
internal void LoadIcon(bool synchronousLoading)
|
||||
internal void LoadIcon(bool isMainMenu)
|
||||
{
|
||||
bool cacheHit;
|
||||
|
||||
if (IsPointingToFolder)
|
||||
{
|
||||
cacheHit = IconReader.GetFolderIconWithCache(Path, ShowOverlay, Level == 0, UpdateFinalIcon, synchronousLoading);
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheHit = IconReader.GetFileIconWithCache(Path, ResolvedPath, ShowOverlay, Level == 0, UpdateFinalIcon, synchronousLoading);
|
||||
}
|
||||
|
||||
if (!cacheHit)
|
||||
if (!IconReader.GetIconAsync(IsPointingToFolder, Path, ResolvedPath, ShowOverlay, isMainMenu, UpdateFinalIcon, isMainMenu))
|
||||
{
|
||||
IconLoading = true;
|
||||
ColumnIcon = IconReader.LoadingImage; // TODO: Maybe add rotation animation like for the loading Menu icon? (See: pictureBoxLoading, LoadingRotation)
|
||||
|
@ -385,16 +374,12 @@ namespace SystemTrayMenu.DataClasses
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateFinalIcon(BitmapSource? icon)
|
||||
private void UpdateFinalIcon(BitmapSource icon)
|
||||
{
|
||||
if (icon == null)
|
||||
{
|
||||
icon = IconReader.NotFoundImage;
|
||||
}
|
||||
else if (HiddenEntry)
|
||||
if (HiddenEntry)
|
||||
{
|
||||
icon = ImagingHelper.ApplyOpactiy(icon, 0.5d);
|
||||
icon?.Freeze(); // Make it accessible for any thread
|
||||
icon.Freeze(); // Make it accessible for any thread
|
||||
}
|
||||
|
||||
IconLoading = false;
|
||||
|
|
|
@ -29,27 +29,29 @@ namespace SystemTrayMenu.Utilities
|
|||
|
||||
private static readonly ConcurrentDictionary<string, BitmapSource> IconDictPersistent = new();
|
||||
private static readonly ConcurrentDictionary<string, BitmapSource> IconDictCache = new();
|
||||
private static readonly BlockingCollection<Action> IconFactoryQueue = new();
|
||||
private static readonly BlockingCollection<Action> IconFactoryQueueSTA = new();
|
||||
private static readonly List<Thread> IconFactoryThreadPoolSTA = new(16);
|
||||
|
||||
internal static void Startup()
|
||||
{
|
||||
for (int i = 0; i < IconFactoryThreadPoolSTA.Capacity; i++)
|
||||
{
|
||||
Thread thread = new(IconFactoryWorkerSTA);
|
||||
thread.Name = "IconFactory STA #" + i.ToString();
|
||||
Thread thread = new(IconFactoryWorkerSTA)
|
||||
{
|
||||
Name = "IconFactory STA #" + i.ToString(),
|
||||
};
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
IconFactoryThreadPoolSTA.Add(thread);
|
||||
}
|
||||
|
||||
void IconFactoryWorkerSTA()
|
||||
static void IconFactoryWorkerSTA()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
IconFactoryQueue.Take()();
|
||||
IconFactoryQueueSTA.Take()();
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
|
@ -89,48 +91,64 @@ namespace SystemTrayMenu.Utilities
|
|||
|
||||
internal static void RemoveIconFromCache(string path) => IconDictPersistent.Remove(path, out _);
|
||||
|
||||
internal static bool GetFileIconWithCache(
|
||||
/// <summary>
|
||||
/// Loads an Icon requested by parameters.
|
||||
/// </summary>
|
||||
/// <param name="isFolder">Icon is a folder or file icon.</param>
|
||||
/// <param name="path">Path to the file or directory entry.</param>
|
||||
/// <param name="resolvedPath">Path to the file or directory entry which 'path' is pointing at.</param>
|
||||
/// <param name="linkOverlay">Apply the link overlay to the icon.</param>
|
||||
/// <param name="persistentEntry">Load from or into persistent cache.</param>
|
||||
/// <param name="onIconLoaded">Callback called when icon got loaded.</param>
|
||||
/// <param name="synchronousLoading">Force synchronous loading. e.g. during preloading on startup.</param>
|
||||
/// <returns>True = Icon was loaded synchronously, False = Icon will be loaded in the background.</returns>
|
||||
internal static bool GetIconAsync(
|
||||
bool isFolder,
|
||||
string path,
|
||||
string resolvedPath,
|
||||
bool linkOverlay,
|
||||
bool checkPersistentFirst,
|
||||
Action<BitmapSource?> onIconLoaded,
|
||||
bool persistentEntry,
|
||||
Action<BitmapSource> onIconLoaded,
|
||||
bool synchronousLoading)
|
||||
{
|
||||
string key;
|
||||
string extension = Path.GetExtension(path);
|
||||
if (IsExtensionWithSameIcon(extension))
|
||||
string key = path;
|
||||
|
||||
if (!isFolder)
|
||||
{
|
||||
// Generic file extension
|
||||
key = extension + ":" + linkOverlay;
|
||||
checkPersistentFirst = true; // Always store them in persistent cache
|
||||
synchronousLoading = true; // Always load them after another
|
||||
}
|
||||
else
|
||||
{
|
||||
key = path;
|
||||
string extension = Path.GetExtension(path);
|
||||
if (IsExtensionWithSameIcon(extension))
|
||||
{
|
||||
// Generic file extension
|
||||
key = extension + ":" + linkOverlay;
|
||||
persistentEntry = true; // Always store them in persistent cache
|
||||
}
|
||||
}
|
||||
|
||||
return CacheGetOrAddIcon(path, checkPersistentFirst, onIconLoaded, synchronousLoading, FactoryIconFileSTA);
|
||||
|
||||
BitmapSource FactoryIconFileSTA(string keyExtension)
|
||||
if (!DictIconCache(persistentEntry).TryGetValue(key, out BitmapSource? icon) &&
|
||||
!DictIconCache(!persistentEntry).TryGetValue(key, out icon))
|
||||
{
|
||||
return GetIconAsBitmapSourceSTA(path, resolvedPath, linkOverlay, false);
|
||||
if (synchronousLoading)
|
||||
{
|
||||
icon = DictIconCache(persistentEntry).GetOrAdd(key, FactoryIconSTA);
|
||||
}
|
||||
else
|
||||
{
|
||||
IconFactoryQueueSTA.Add(() =>
|
||||
{
|
||||
BitmapSource icon = DictIconCache(persistentEntry).GetOrAdd(key, FactoryIconSTA);
|
||||
onIconLoaded(icon);
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool GetFolderIconWithCache(
|
||||
string path,
|
||||
bool linkOverlay,
|
||||
bool checkPersistentFirst,
|
||||
Action<BitmapSource?> onIconLoaded,
|
||||
bool synchronousLoading)
|
||||
{
|
||||
return CacheGetOrAddIcon(path, checkPersistentFirst, onIconLoaded, synchronousLoading, FactoryIconFolderSTA);
|
||||
onIconLoaded(icon);
|
||||
return true;
|
||||
|
||||
BitmapSource FactoryIconFolderSTA(string keyExtension)
|
||||
BitmapSource FactoryIconSTA(string keyExtension)
|
||||
{
|
||||
return GetIconAsBitmapSourceSTA(path, path, linkOverlay, true);
|
||||
return GetIconAsBitmapSourceSTA(path, resolvedPath, linkOverlay, isFolder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,23 +165,23 @@ namespace SystemTrayMenu.Utilities
|
|||
|
||||
private static bool CacheGetOrAddIcon(
|
||||
string key,
|
||||
bool checkPersistentFirst,
|
||||
bool persistentEntry,
|
||||
Action<BitmapSource?> onIconLoaded,
|
||||
bool synchronousLoading,
|
||||
Func<string, BitmapSource> factory)
|
||||
{
|
||||
if (!DictIconCache(checkPersistentFirst).TryGetValue(key, out BitmapSource? icon) &&
|
||||
!DictIconCache(!checkPersistentFirst).TryGetValue(key, out icon))
|
||||
if (!DictIconCache(persistentEntry).TryGetValue(key, out BitmapSource? icon) &&
|
||||
!DictIconCache(!persistentEntry).TryGetValue(key, out icon))
|
||||
{
|
||||
if (synchronousLoading)
|
||||
{
|
||||
icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, factory);
|
||||
icon = DictIconCache(persistentEntry).GetOrAdd(key, factory);
|
||||
}
|
||||
else
|
||||
{
|
||||
IconFactoryQueue.Add(() =>
|
||||
IconFactoryQueueSTA.Add(() =>
|
||||
{
|
||||
BitmapSource icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, factory);
|
||||
BitmapSource icon = DictIconCache(persistentEntry).GetOrAdd(key, factory);
|
||||
onIconLoaded(icon);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue