Projects: Create QIcons in the UI thread

Creating QIcons elsewhere is not safe because of image reader plugin
loading and the pixmap cache.

Fixes: QTCREATORBUG-25301
Change-Id: Ia22a0cd571f808d7f5c639353fdf2e548743f8ca
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
Eike Ziller
2021-04-08 16:31:31 +02:00
parent f4ab1279fd
commit bce81fd992
9 changed files with 116 additions and 41 deletions

View File

@@ -46,16 +46,19 @@
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <QFileInfo>
#include <QDir>
#include <QFileInfo>
#include <QIcon>
#include <QStyle>
#include <QThread>
#include <QTimer>
#include <memory>
namespace ProjectExplorer {
QHash<QString, QIcon> DirectoryIcon::m_cache;
static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder,
const Utils::FilePath &directory,
const Utils::FilePath &overrideBaseDir,
@@ -481,16 +484,28 @@ QString FolderNode::displayName() const
}
/*!
Contains the icon that should be used in a view. Default is the directory icon
(QStyle::S_PDirIcon).
s\a setIcon()
Contains the icon that should be used in a view. Default is the directory icon
(QStyle::S_PDirIcon). Calling this method is only safe in the UI thread.
\sa setIcon()
*/
QIcon FolderNode::icon() const
{
QTC_CHECK(QThread::currentThread() == QCoreApplication::instance()->thread());
// Instantiating the Icon provider is expensive.
if (m_icon.isNull())
m_icon = Core::FileIconProvider::icon(QFileIconProvider::Folder);
return m_icon;
if (auto strPtr = Utils::get_if<QString>(&m_icon)) {
m_icon = QIcon(*strPtr);
} else if (auto directoryIconPtr = Utils::get_if<DirectoryIcon>(&m_icon)) {
m_icon = directoryIconPtr->icon();
} else if (auto creatorPtr = Utils::get_if<IconCreator>(&m_icon)) {
m_icon = (*creatorPtr)();
} else {
auto iconPtr = Utils::get_if<QIcon>(&m_icon);
if (!iconPtr || iconPtr->isNull())
m_icon = Core::FileIconProvider::icon(QFileIconProvider::Folder);
}
return Utils::get<QIcon>(m_icon);
}
Node *FolderNode::findNode(const std::function<bool(Node *)> &filter)
@@ -724,11 +739,38 @@ void FolderNode::setDisplayName(const QString &name)
m_displayName = name;
}
/*!
Sets the \a icon for this node. Note that creating QIcon instances is only safe in the UI thread.
*/
void FolderNode::setIcon(const QIcon &icon)
{
m_icon = icon;
}
/*!
Sets the \a directoryIcon that is used to create the icon for this node on demand.
*/
void FolderNode::setIcon(const DirectoryIcon &directoryIcon)
{
m_icon = directoryIcon;
}
/*!
Sets the \a path that is used to create the icon for this node on demand.
*/
void FolderNode::setIcon(const QString &path)
{
m_icon = path;
}
/*!
Sets the \a iconCreator function that is used to create the icon for this node on demand.
*/
void FolderNode::setIcon(const IconCreator &iconCreator)
{
m_icon = iconCreator;
}
void FolderNode::setLocationInfo(const QVector<FolderNode::LocationInfo> &info)
{
m_locations = info;
@@ -1048,4 +1090,34 @@ void ContainerNode::handleSubTreeChanged(FolderNode *node)
m_project->handleSubTreeChanged(node);
}
/*!
\class ProjectExplorer::DirectoryIcon
The DirectoryIcon class represents a directory icon with an overlay.
The QIcon is created on demand and globally cached, so other DirectoryIcon
instances with the same overlay share the same QIcon instance.
*/
/*!
Creates a DirectoryIcon for the specified \a overlay.
*/
DirectoryIcon::DirectoryIcon(const QString &overlay)
: m_overlay(overlay)
{}
/*!
Returns the icon for this DirectoryIcon. Calling this method is only safe in the UI thread.
*/
QIcon DirectoryIcon::icon() const
{
QTC_CHECK(QThread::currentThread() == QCoreApplication::instance()->thread());
const auto it = m_cache.find(m_overlay);
if (it != m_cache.end())
return it.value();
const QIcon icon = Core::FileIconProvider::directoryIcon(m_overlay);
m_cache.insert(m_overlay, icon);
return icon;
}
} // namespace ProjectExplorer