forked from qt-creator/qt-creator
QmlDesigner: Support updating imported assets
Added support for overwriting file-based assets such as images when an asset with the same name is imported to the same location. Assets tab will now also refresh its icons if underlying files are modified. Added local caching of icons to avoid regenerating unchanged icons. Also added bit of a delay before responding to a file system change, which should improve performance when multiple assets are imported at once. Fixes: QDS-3644 Change-Id: Id452d3b1de8f4651a36401878c5067655865b675 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
This commit is contained in:
@@ -82,6 +82,8 @@
|
||||
#include <QPushButton>
|
||||
#include <QGridLayout>
|
||||
#include <QPointer>
|
||||
#include <QMessageBox>
|
||||
#include <QPair>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
@@ -980,9 +982,30 @@ static bool addFilesToProject(const QStringList &fileNames, const QString &defau
|
||||
return true;
|
||||
|
||||
bool allSuccessful = true;
|
||||
QList<QPair<QString, QString>> copyList;
|
||||
QStringList removeList;
|
||||
for (const QString &fileName : fileNames) {
|
||||
const QString targetFile = directory + "/" + QFileInfo(fileName).fileName();
|
||||
const bool success = QFile::copy(fileName, targetFile);
|
||||
if (QFileInfo::exists(targetFile)) {
|
||||
const QString title = QCoreApplication::translate(
|
||||
"ModelNodeOperations", "Overwrite Existing File?");
|
||||
const QString question = QCoreApplication::translate(
|
||||
"ModelNodeOperations", "File already exists. Overwrite?\n\"%1\"").arg(targetFile);
|
||||
if (QMessageBox::question(qobject_cast<QWidget *>(Core::ICore::dialogParent()),
|
||||
title, question, QMessageBox::Yes | QMessageBox::No)
|
||||
!= QMessageBox::Yes) {
|
||||
continue;
|
||||
}
|
||||
removeList.append(targetFile);
|
||||
}
|
||||
copyList.append({fileName, targetFile});
|
||||
}
|
||||
// Defer actual file operations after we have dealt with possible popup dialogs to avoid
|
||||
// unnecessarily refreshing file models multiple times during the operation
|
||||
for (const auto &file : qAsConst(removeList))
|
||||
QFile::remove(file);
|
||||
for (const auto &filePair : qAsConst(copyList)) {
|
||||
const bool success = QFile::copy(filePair.first, filePair.second);
|
||||
|
||||
auto document = QmlDesignerPlugin::instance()->currentDesignDocument();
|
||||
|
||||
@@ -993,7 +1016,7 @@ static bool addFilesToProject(const QStringList &fileNames, const QString &defau
|
||||
if (node) {
|
||||
ProjectExplorer::FolderNode *containingFolder = node->parentFolderNode();
|
||||
if (containingFolder)
|
||||
containingFolder->addFiles(QStringList(targetFile));
|
||||
containingFolder->addFiles(QStringList(filePair.second));
|
||||
}
|
||||
} else {
|
||||
allSuccessful = false;
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <QImageReader>
|
||||
#include <QPainter>
|
||||
#include <QRawFont>
|
||||
#include <QPair>
|
||||
#include <qmath.h>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
@@ -111,17 +110,23 @@ QString fontFamily(const QFileInfo &info)
|
||||
class ItemLibraryFileIconProvider : public QFileIconProvider
|
||||
{
|
||||
public:
|
||||
ItemLibraryFileIconProvider(SynchronousImageCache &fontImageCache)
|
||||
ItemLibraryFileIconProvider(SynchronousImageCache &fontImageCache,
|
||||
QHash<QString, QPair<QDateTime, QIcon>> &iconCache)
|
||||
: QFileIconProvider()
|
||||
, m_fontImageCache(fontImageCache)
|
||||
, m_iconCache(iconCache)
|
||||
{
|
||||
}
|
||||
|
||||
QIcon icon( const QFileInfo & info ) const override
|
||||
{
|
||||
const QString filePath = info.absoluteFilePath();
|
||||
QPair<QDateTime, QIcon> &cachedIcon = m_iconCache[filePath];
|
||||
if (!cachedIcon.second.isNull() && cachedIcon.first == info.lastModified())
|
||||
return cachedIcon.second;
|
||||
|
||||
QIcon icon;
|
||||
const QString suffix = info.suffix().toLower();
|
||||
const QString filePath = info.absoluteFilePath();
|
||||
|
||||
// Provide icon depending on suffix
|
||||
QPixmap origPixmap;
|
||||
@@ -150,6 +155,8 @@ public:
|
||||
icon.addPixmap(pixmap);
|
||||
}
|
||||
|
||||
cachedIcon.first = info.lastModified();
|
||||
cachedIcon.second = icon;
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -163,6 +170,7 @@ public:
|
||||
"Abc"});
|
||||
}
|
||||
|
||||
private:
|
||||
// Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their
|
||||
// x2 versions for HDPI sceens
|
||||
std::vector<QSize> iconSizes = {{384, 384},
|
||||
@@ -175,18 +183,29 @@ public:
|
||||
{32, 32}}; // List
|
||||
|
||||
SynchronousImageCache &m_fontImageCache;
|
||||
QHash<QString, QPair<QDateTime, QIcon>> &m_iconCache;
|
||||
};
|
||||
|
||||
CustomFileSystemModel::CustomFileSystemModel(SynchronousImageCache &fontImageCache, QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_fileSystemModel(new QFileSystemModel(this))
|
||||
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
|
||||
, m_fontImageCache(fontImageCache)
|
||||
{
|
||||
m_fileSystemModel->setIconProvider(new ItemLibraryFileIconProvider(fontImageCache));
|
||||
|
||||
connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this] {
|
||||
m_updatePathTimer.setInterval(200);
|
||||
m_updatePathTimer.setSingleShot(true);
|
||||
m_updatePathTimer.callOnTimeout([this]() {
|
||||
updatePath(m_fileSystemModel->rootPath());
|
||||
});
|
||||
|
||||
// If project directory contents change, or one of the asset files is modified, we must
|
||||
// reconstruct the model to update the icons
|
||||
connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this] {
|
||||
m_updatePathTimer.start();
|
||||
});
|
||||
connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged, [this] {
|
||||
m_updatePathTimer.start();
|
||||
});
|
||||
}
|
||||
|
||||
void CustomFileSystemModel::setFilter(QDir::Filters)
|
||||
@@ -351,9 +370,17 @@ void CustomFileSystemModel::appendIfNotFiltered(const QString &file)
|
||||
QModelIndex CustomFileSystemModel::updatePath(const QString &newPath)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
// We must recreate icon provider to ensure modified icons are recreated
|
||||
auto newProvider = new ItemLibraryFileIconProvider(m_fontImageCache, m_iconCache);
|
||||
m_fileSystemModel->setIconProvider(newProvider);
|
||||
delete m_fileIconProvider;
|
||||
m_fileIconProvider = newProvider;
|
||||
|
||||
m_fileSystemModel->setRootPath(newPath);
|
||||
|
||||
m_fileSystemWatcher->removeDirectories(m_fileSystemWatcher->directories());
|
||||
m_fileSystemWatcher->removeFiles(m_fileSystemWatcher->files());
|
||||
|
||||
m_fileSystemWatcher->addDirectory(newPath, Utils::FileSystemWatcher::WatchAllChanges);
|
||||
|
||||
@@ -385,11 +412,19 @@ QModelIndex CustomFileSystemModel::updatePath(const QString &newPath)
|
||||
while (fileIterator.hasNext())
|
||||
appendIfNotFiltered(fileIterator.next());
|
||||
|
||||
QDirIterator dirIterator(newPath, {}, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
|
||||
while (dirIterator.hasNext())
|
||||
m_fileSystemWatcher->addDirectory(dirIterator.next(), Utils::FileSystemWatcher::WatchAllChanges);
|
||||
QDirIterator dirIterator(newPath, {}, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot,
|
||||
QDirIterator::Subdirectories);
|
||||
while (dirIterator.hasNext()) {
|
||||
const QString entry = dirIterator.next();
|
||||
QFileInfo fi{entry};
|
||||
if (fi.isDir())
|
||||
m_fileSystemWatcher->addDirectory(entry, Utils::FileSystemWatcher::WatchAllChanges);
|
||||
else if (supportedSuffixes().contains(fi.suffix()))
|
||||
m_fileSystemWatcher->addFile(entry, Utils::FileSystemWatcher::WatchAllChanges);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
|
||||
return QAbstractListModel::index(0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
#include <QIcon>
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QFileIconProvider;
|
||||
@@ -40,6 +44,7 @@ namespace Utils { class FileSystemWatcher; }
|
||||
namespace QmlDesigner {
|
||||
|
||||
class SynchronousImageCache;
|
||||
class ItemLibraryFileIconProvider;
|
||||
|
||||
class CustomFileSystemModel : public QAbstractListModel
|
||||
{
|
||||
@@ -78,6 +83,10 @@ private:
|
||||
QStringList m_files;
|
||||
QString m_searchFilter;
|
||||
Utils::FileSystemWatcher *m_fileSystemWatcher;
|
||||
SynchronousImageCache &m_fontImageCache;
|
||||
ItemLibraryFileIconProvider *m_fileIconProvider = nullptr;
|
||||
QHash<QString, QPair<QDateTime, QIcon>> m_iconCache;
|
||||
QTimer m_updatePathTimer;
|
||||
};
|
||||
|
||||
} //QmlDesigner
|
||||
|
||||
Reference in New Issue
Block a user