mirror of
https://github.com/reactos/reactos.git
synced 2025-06-27 22:39:43 +00:00
[RAPPS] Load icons on background thread (#6881)
- Load icons on background thread to massively reduce loading time. - Use SM_CXICON sized icons consistently instead of hardcoding 32 in some places.
This commit is contained in:
parent
4321c975c7
commit
d73a838245
3 changed files with 158 additions and 51 deletions
|
@ -13,6 +13,9 @@
|
||||||
|
|
||||||
using namespace Gdiplus;
|
using namespace Gdiplus;
|
||||||
|
|
||||||
|
HICON g_hDefaultPackageIcon = NULL;
|
||||||
|
static int g_DefaultPackageIconILIdx = I_IMAGENONE;
|
||||||
|
|
||||||
// **** CMainToolbar ****
|
// **** CMainToolbar ****
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
@ -970,16 +973,89 @@ CAppInfoDisplay::~CAppInfoDisplay()
|
||||||
|
|
||||||
// **** CAppsListView ****
|
// **** CAppsListView ****
|
||||||
|
|
||||||
|
struct CAsyncLoadIcon {
|
||||||
|
CAsyncLoadIcon *pNext;
|
||||||
|
HWND hAppsList;
|
||||||
|
CAppInfo *AppInfo; // Only used to find the item in the list, do not access on background thread
|
||||||
|
UINT TaskId;
|
||||||
|
bool Parse;
|
||||||
|
WCHAR Location[ANYSIZE_ARRAY];
|
||||||
|
|
||||||
|
void Free() { free(this); }
|
||||||
|
static CAsyncLoadIcon* Queue(HWND hAppsList, CAppInfo &AppInfo, bool Parse);
|
||||||
|
static void StartTasks();
|
||||||
|
} *g_AsyncIconTasks = NULL;
|
||||||
|
static UINT g_AsyncIconTaskId = 0;
|
||||||
|
|
||||||
|
static DWORD CALLBACK
|
||||||
|
AsyncLoadIconProc(LPVOID Param)
|
||||||
|
{
|
||||||
|
for (CAsyncLoadIcon *task = (CAsyncLoadIcon*)Param, *old; task; old->Free())
|
||||||
|
{
|
||||||
|
if (task->TaskId == g_AsyncIconTaskId)
|
||||||
|
{
|
||||||
|
HICON hIcon;
|
||||||
|
if (!task->Parse)
|
||||||
|
hIcon = (HICON)LoadImageW(NULL, task->Location, IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
|
||||||
|
else if (!ExtractIconExW(task->Location, PathParseIconLocationW(task->Location), &hIcon, NULL, 1))
|
||||||
|
hIcon = NULL;
|
||||||
|
|
||||||
|
if (hIcon)
|
||||||
|
{
|
||||||
|
SendMessageW(task->hAppsList, WM_RAPPSLIST_ASYNCICON, (WPARAM)hIcon, (LPARAM)task);
|
||||||
|
DestroyIcon(hIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
old = task;
|
||||||
|
task = task->pNext;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAsyncLoadIcon*
|
||||||
|
CAsyncLoadIcon::Queue(HWND hAppsList, CAppInfo &AppInfo, bool Parse)
|
||||||
|
{
|
||||||
|
ATLASSERT(GetCurrentThreadId() == GetWindowThreadProcessId(hAppsList, NULL));
|
||||||
|
CStringW szIconPath;
|
||||||
|
if (!AppInfo.RetrieveIcon(szIconPath))
|
||||||
|
return NULL;
|
||||||
|
SIZE_T cbstr = (szIconPath.GetLength() + 1) * sizeof(WCHAR);
|
||||||
|
CAsyncLoadIcon *task = (CAsyncLoadIcon*)malloc(sizeof(CAsyncLoadIcon) + cbstr);
|
||||||
|
if (!task)
|
||||||
|
return NULL;
|
||||||
|
task->hAppsList = hAppsList;
|
||||||
|
task->AppInfo = &AppInfo;
|
||||||
|
task->TaskId = g_AsyncIconTaskId;
|
||||||
|
task->Parse = Parse;
|
||||||
|
CopyMemory(task->Location, szIconPath.GetBuffer(), cbstr);
|
||||||
|
szIconPath.ReleaseBuffer();
|
||||||
|
task->pNext = g_AsyncIconTasks;
|
||||||
|
g_AsyncIconTasks = task;
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CAsyncLoadIcon::StartTasks()
|
||||||
|
{
|
||||||
|
CAsyncLoadIcon *tasks = g_AsyncIconTasks;
|
||||||
|
g_AsyncIconTasks = NULL;
|
||||||
|
if (HANDLE hThread = CreateThread(NULL, 0, AsyncLoadIconProc, tasks, 0, NULL))
|
||||||
|
CloseHandle(hThread);
|
||||||
|
else
|
||||||
|
AsyncLoadIconProc(tasks); // Insist so we at least free the tasks
|
||||||
|
}
|
||||||
|
|
||||||
CAppsListView::CAppsListView()
|
CAppsListView::CAppsListView()
|
||||||
{
|
{
|
||||||
|
m_hImageListView = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAppsListView::~CAppsListView()
|
CAppsListView::~CAppsListView()
|
||||||
{
|
{
|
||||||
if (m_hImageListView)
|
if (m_hImageListView)
|
||||||
{
|
|
||||||
ImageList_Destroy(m_hImageListView);
|
ImageList_Destroy(m_hImageListView);
|
||||||
}
|
if (g_hDefaultPackageIcon)
|
||||||
|
DestroyIcon(g_hDefaultPackageIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT
|
LRESULT
|
||||||
|
@ -1000,6 +1076,32 @@ CAppsListView::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &
|
||||||
return lRes;
|
return lRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LRESULT
|
||||||
|
CAppsListView::OnAsyncIcon(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
|
||||||
|
{
|
||||||
|
CAsyncLoadIcon *task = (CAsyncLoadIcon*)lParam;
|
||||||
|
bHandled = TRUE;
|
||||||
|
if (task->TaskId == g_AsyncIconTaskId)
|
||||||
|
{
|
||||||
|
LVITEM lvi;
|
||||||
|
LVFINDINFO lvfi;
|
||||||
|
lvfi.flags = LVFI_PARAM;
|
||||||
|
lvfi.lParam = (LPARAM)task->AppInfo;
|
||||||
|
lvi.iItem = ListView_FindItem(m_hWnd, -1, &lvfi);
|
||||||
|
if (lvi.iItem != -1)
|
||||||
|
{
|
||||||
|
lvi.iImage = ImageList_AddIcon(m_hImageListView, (HICON)wParam);
|
||||||
|
if (lvi.iImage != -1)
|
||||||
|
{
|
||||||
|
lvi.mask = LVIF_IMAGE;
|
||||||
|
lvi.iSubItem = 0;
|
||||||
|
ListView_SetItem(m_hWnd, &lvi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
CAppsListView::SetWatermark(const CStringW &Text)
|
CAppsListView::SetWatermark(const CStringW &Text)
|
||||||
{
|
{
|
||||||
|
@ -1154,11 +1256,6 @@ CAppsListView::Create(HWND hwndParent)
|
||||||
SetCheckboxesVisible(FALSE);
|
SetCheckboxesVisible(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE, LISTVIEW_ICON_SIZE, GetSystemColorDepth() | ILC_MASK, 0, 1);
|
|
||||||
|
|
||||||
SetImageList(m_hImageListView, LVSIL_SMALL);
|
|
||||||
SetImageList(m_hImageListView, LVSIL_NORMAL);
|
|
||||||
|
|
||||||
#pragma push_macro("SubclassWindow")
|
#pragma push_macro("SubclassWindow")
|
||||||
#undef SubclassWindow
|
#undef SubclassWindow
|
||||||
m_hWnd = NULL;
|
m_hWnd = NULL;
|
||||||
|
@ -1215,23 +1312,40 @@ CAppsListView::GetFocusedItemData()
|
||||||
BOOL
|
BOOL
|
||||||
CAppsListView::SetDisplayAppType(APPLICATION_VIEW_TYPE AppType)
|
CAppsListView::SetDisplayAppType(APPLICATION_VIEW_TYPE AppType)
|
||||||
{
|
{
|
||||||
|
++g_AsyncIconTaskId; // Stop loading icons that are now invalid
|
||||||
if (!DeleteAllItems())
|
if (!DeleteAllItems())
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
ApplicationViewType = AppType;
|
ApplicationViewType = AppType;
|
||||||
|
|
||||||
bIsAscending = TRUE;
|
bIsAscending = TRUE;
|
||||||
|
|
||||||
ItemCount = 0;
|
ItemCount = 0;
|
||||||
CheckedItemCount = 0;
|
CheckedItemCount = 0;
|
||||||
|
|
||||||
|
ListView_Scroll(m_hWnd, 0, 0x7fff * -1); // FIXME: a bug in Wine ComCtl32 where VScroll is not reset after deleting items
|
||||||
|
|
||||||
// delete old columns
|
// delete old columns
|
||||||
while (ColumnCount)
|
while (ColumnCount)
|
||||||
{
|
{
|
||||||
DeleteColumn(--ColumnCount);
|
DeleteColumn(--ColumnCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!g_hDefaultPackageIcon)
|
||||||
|
{
|
||||||
|
ImageList_Destroy(m_hImageListView);
|
||||||
|
UINT IconSize = GetSystemMetrics(SM_CXICON);
|
||||||
|
UINT ilc = GetSystemColorDepth() | ILC_MASK;
|
||||||
|
m_hImageListView = ImageList_Create(IconSize, IconSize, ilc, 0, 1);
|
||||||
|
SetImageList(m_hImageListView, LVSIL_SMALL);
|
||||||
|
SetImageList(m_hImageListView, LVSIL_NORMAL);
|
||||||
|
g_hDefaultPackageIcon = (HICON)LoadImageW(hInst, MAKEINTRESOURCEW(IDI_MAIN),
|
||||||
|
IMAGE_ICON, IconSize, IconSize, LR_SHARED);
|
||||||
|
}
|
||||||
ImageList_RemoveAll(m_hImageListView);
|
ImageList_RemoveAll(m_hImageListView);
|
||||||
|
|
||||||
|
g_DefaultPackageIconILIdx = ImageList_AddIcon(m_hImageListView, g_hDefaultPackageIcon);
|
||||||
|
if (g_DefaultPackageIconILIdx == -1)
|
||||||
|
g_DefaultPackageIconILIdx = I_IMAGENONE;
|
||||||
|
|
||||||
// add new columns
|
// add new columns
|
||||||
CStringW szText;
|
CStringW szText;
|
||||||
switch (AppType)
|
switch (AppType)
|
||||||
|
@ -1284,31 +1398,20 @@ CAppsListView::SetViewMode(DWORD ViewMode)
|
||||||
BOOL
|
BOOL
|
||||||
CAppsListView::AddApplication(CAppInfo *AppInfo, BOOL InitialCheckState)
|
CAppsListView::AddApplication(CAppInfo *AppInfo, BOOL InitialCheckState)
|
||||||
{
|
{
|
||||||
|
if (!AppInfo)
|
||||||
|
{
|
||||||
|
CAsyncLoadIcon::StartTasks();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IconIndex = g_DefaultPackageIconILIdx;
|
||||||
if (ApplicationViewType == AppViewTypeInstalledApps)
|
if (ApplicationViewType == AppViewTypeInstalledApps)
|
||||||
{
|
{
|
||||||
/* Load icon from registry */
|
|
||||||
HICON hIcon = NULL;
|
|
||||||
CStringW szIconPath;
|
|
||||||
int IconIndex;
|
|
||||||
if (AppInfo->RetrieveIcon(szIconPath))
|
|
||||||
{
|
|
||||||
IconIndex = PathParseIconLocationW(szIconPath.GetBuffer());
|
|
||||||
szIconPath.ReleaseBuffer();
|
|
||||||
|
|
||||||
ExtractIconExW(szIconPath.GetString(), IconIndex, &hIcon, NULL, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use the default icon if none were found in the file, or if it is not supported (returned 1) */
|
|
||||||
if (!hIcon || (hIcon == (HICON)1))
|
|
||||||
{
|
|
||||||
/* Load default icon */
|
|
||||||
hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
|
|
||||||
}
|
|
||||||
|
|
||||||
IconIndex = ImageList_AddIcon(m_hImageListView, hIcon);
|
|
||||||
DestroyIcon(hIcon);
|
|
||||||
|
|
||||||
int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo);
|
int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo);
|
||||||
|
if (Index == -1)
|
||||||
|
return FALSE;
|
||||||
|
CAsyncLoadIcon::Queue(m_hWnd, *AppInfo, true);
|
||||||
|
|
||||||
SetItemText(Index, 1, AppInfo->szDisplayVersion.IsEmpty() ? L"---" : AppInfo->szDisplayVersion);
|
SetItemText(Index, 1, AppInfo->szDisplayVersion.IsEmpty() ? L"---" : AppInfo->szDisplayVersion);
|
||||||
SetItemText(Index, 2, AppInfo->szComments.IsEmpty() ? L"---" : AppInfo->szComments);
|
SetItemText(Index, 2, AppInfo->szComments.IsEmpty() ? L"---" : AppInfo->szComments);
|
||||||
|
|
||||||
|
@ -1317,25 +1420,10 @@ CAppsListView::AddApplication(CAppInfo *AppInfo, BOOL InitialCheckState)
|
||||||
}
|
}
|
||||||
else if (ApplicationViewType == AppViewTypeAvailableApps)
|
else if (ApplicationViewType == AppViewTypeAvailableApps)
|
||||||
{
|
{
|
||||||
/* Load icon from file */
|
|
||||||
HICON hIcon = NULL;
|
|
||||||
CStringW szIconPath;
|
|
||||||
if (AppInfo->RetrieveIcon(szIconPath))
|
|
||||||
{
|
|
||||||
hIcon = (HICON)LoadImageW(
|
|
||||||
NULL, szIconPath, IMAGE_ICON, LISTVIEW_ICON_SIZE, LISTVIEW_ICON_SIZE, LR_LOADFROMFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hIcon)
|
|
||||||
{
|
|
||||||
/* Load default icon */
|
|
||||||
hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
|
|
||||||
}
|
|
||||||
|
|
||||||
int IconIndex = ImageList_AddIcon(m_hImageListView, hIcon);
|
|
||||||
DestroyIcon(hIcon);
|
|
||||||
|
|
||||||
int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo);
|
int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo);
|
||||||
|
if (Index == -1)
|
||||||
|
return FALSE;
|
||||||
|
CAsyncLoadIcon::Queue(m_hWnd, *AppInfo, false);
|
||||||
|
|
||||||
if (InitialCheckState)
|
if (InitialCheckState)
|
||||||
{
|
{
|
||||||
|
|
|
@ -459,6 +459,14 @@ CMainWindow::ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lPa
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WM_SETTINGCHANGE:
|
||||||
|
if (wParam == SPI_SETNONCLIENTMETRICS || wParam == SPI_SETICONMETRICS)
|
||||||
|
{
|
||||||
|
DestroyIcon(g_hDefaultPackageIcon);
|
||||||
|
g_hDefaultPackageIcon = NULL; // Trigger imagelist recreation on next load
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_TIMER:
|
case WM_TIMER:
|
||||||
if (wParam == SEARCH_TIMER_ID)
|
if (wParam == SEARCH_TIMER_ID)
|
||||||
{
|
{
|
||||||
|
@ -598,6 +606,7 @@ CMainWindow::AddApplicationsToView(CAtlList<CAppInfo *> &List)
|
||||||
m_ApplicationView->AddApplication(Info, bSelected);
|
m_ApplicationView->AddApplication(Info, bSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_ApplicationView->AddApplication(NULL, FALSE); // Tell the list we are done
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
@ -605,6 +614,13 @@ CMainWindow::UpdateApplicationsList(AppsCategories EnumType, BOOL bReload, BOOL
|
||||||
{
|
{
|
||||||
bUpdating = TRUE;
|
bUpdating = TRUE;
|
||||||
|
|
||||||
|
if (HCURSOR hCursor = LoadCursor(NULL, IDC_APPSTARTING))
|
||||||
|
{
|
||||||
|
// The database (.ini files) is parsed on the UI thread, let the user know we are busy
|
||||||
|
SetCursor(hCursor);
|
||||||
|
PostMessage(WM_SETCURSOR, (WPARAM)m_hWnd, MAKELONG(HTCLIENT, WM_MOUSEMOVE));
|
||||||
|
}
|
||||||
|
|
||||||
if (bCheckAvailable)
|
if (bCheckAvailable)
|
||||||
CheckAvailable();
|
CheckAvailable();
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
#include <gdiplus.h>
|
#include <gdiplus.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
extern HICON g_hDefaultPackageIcon;
|
||||||
#define LISTVIEW_ICON_SIZE 32
|
|
||||||
|
|
||||||
// default broken-image icon size
|
// default broken-image icon size
|
||||||
#define BROKENIMG_ICON_SIZE 96
|
#define BROKENIMG_ICON_SIZE 96
|
||||||
|
@ -40,6 +39,7 @@
|
||||||
#define WM_RAPPS_DOWNLOAD_COMPLETE \
|
#define WM_RAPPS_DOWNLOAD_COMPLETE \
|
||||||
(WM_USER + 1) // notify download complete. wParam is error code, and lParam is a pointer to ScrnshotDownloadParam
|
(WM_USER + 1) // notify download complete. wParam is error code, and lParam is a pointer to ScrnshotDownloadParam
|
||||||
#define WM_RAPPS_RESIZE_CHILDREN (WM_USER + 2) // ask parent window to resize children.
|
#define WM_RAPPS_RESIZE_CHILDREN (WM_USER + 2) // ask parent window to resize children.
|
||||||
|
#define WM_RAPPSLIST_ASYNCICON (WM_APP + 0)
|
||||||
|
|
||||||
enum SCRNSHOT_STATUS
|
enum SCRNSHOT_STATUS
|
||||||
{
|
{
|
||||||
|
@ -210,10 +210,13 @@ class CAppsListView : public CUiWindow<CWindowImpl<CAppsListView, CListView>>
|
||||||
|
|
||||||
BEGIN_MSG_MAP(CAppsListView)
|
BEGIN_MSG_MAP(CAppsListView)
|
||||||
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
|
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
|
||||||
|
MESSAGE_HANDLER(WM_RAPPSLIST_ASYNCICON, OnAsyncIcon)
|
||||||
END_MSG_MAP()
|
END_MSG_MAP()
|
||||||
|
|
||||||
LRESULT
|
LRESULT
|
||||||
OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
|
OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
|
||||||
|
LRESULT
|
||||||
|
OnAsyncIcon(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CAppsListView();
|
CAppsListView();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue