QmlDesigner: Reset puppet if shader code changes

If a shader file inside the project is modified, reset the puppets to
update custom materials and effects that use the modified shader.

Change-Id: Ie81accf5f3029c31b49b65b5e1b53e1259a9b4eb
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2020-12-08 16:48:19 +02:00
parent 276c0ae482
commit 757d3200aa
2 changed files with 115 additions and 0 deletions

View File

@@ -39,10 +39,13 @@
#include <QPointer> #include <QPointer>
#include <QRectF> #include <QRectF>
#include <QTime> #include <QTime>
#include <QTimer>
#include <QtGui/qevent.h> #include <QtGui/qevent.h>
#include <memory> #include <memory>
QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher)
namespace ProjectExplorer { namespace ProjectExplorer {
class Target; class Target;
} }
@@ -219,6 +222,8 @@ private: // functions
QVariant modelNodePreviewImageDataToVariant(const ModelNodePreviewImageData &imageData); QVariant modelNodePreviewImageDataToVariant(const ModelNodePreviewImageData &imageData);
void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image); void updatePreviewImageForNode(const ModelNode &modelNode, const QImage &image);
void updateWatcher(const QString &path);
private: private:
QHash<QString, ModelNodePreviewImageData> m_imageDataMap; QHash<QString, ModelNodePreviewImageData> m_imageDataMap;
@@ -236,7 +241,16 @@ private:
// key: fileUrl value: (key: instance qml id, value: related tool states) // key: fileUrl value: (key: instance qml id, value: related tool states)
QHash<QUrl, QHash<QString, QVariantMap>> m_edit3DToolStates; QHash<QUrl, QHash<QString, QVariantMap>> m_edit3DToolStates;
std::function<void()> m_crashCallback{[this] { handleCrash(); }}; std::function<void()> m_crashCallback{[this] { handleCrash(); }};
// We use QFileSystemWatcher directly instead of Utils::FileSystemWatcher as we want
// shader changes to be applied immediately rather than requiring reactivation of
// the creator application.
QFileSystemWatcher *m_fileSystemWatcher;
QTimer m_resetTimer;
QTimer m_updateWatcherTimer;
QSet<QString> m_pendingUpdateDirs;
}; };
} // namespace ProxyNodeInstanceView } // namespace ProxyNodeInstanceView

View File

@@ -100,6 +100,7 @@
#include <QTimerEvent> #include <QTimerEvent>
#include <QPicture> #include <QPicture>
#include <QPainter> #include <QPainter>
#include <QDirIterator>
enum { enum {
debug = false debug = false
@@ -135,8 +136,44 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
: m_connectionManager(connectionManager) : m_connectionManager(connectionManager)
, m_baseStatePreviewImage(QSize(100, 100), QImage::Format_ARGB32) , m_baseStatePreviewImage(QSize(100, 100), QImage::Format_ARGB32)
, m_restartProcessTimerId(0) , m_restartProcessTimerId(0)
, m_fileSystemWatcher(new QFileSystemWatcher(this))
{ {
m_baseStatePreviewImage.fill(0xFFFFFF); m_baseStatePreviewImage.fill(0xFFFFFF);
// Interval > 0 is used for QFileSystemWatcher related timers to allow all notifications
// related to a single event to be received before we act.
m_resetTimer.setSingleShot(true);
m_resetTimer.setInterval(100);
QObject::connect(&m_resetTimer, &QTimer::timeout, [this] {
resetPuppet();
});
m_updateWatcherTimer.setSingleShot(true);
m_updateWatcherTimer.setInterval(100);
QObject::connect(&m_updateWatcherTimer, &QTimer::timeout, [this] {
for (const auto &path : qAsConst(m_pendingUpdateDirs))
updateWatcher(path);
m_pendingUpdateDirs.clear();
});
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged,
[this](const QString &path) {
const QSet<QString> pendingDirs = m_pendingUpdateDirs;
for (const auto &pendingPath : pendingDirs) {
if (path.startsWith(pendingPath)) {
// no need to add path, already handled by a pending parent path
return;
} else if (pendingPath.startsWith(path)) {
// Parent path to a pending path added, remove the pending path
m_pendingUpdateDirs.remove(pendingPath);
}
}
m_pendingUpdateDirs.insert(path);
m_updateWatcherTimer.start();
});
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this] {
m_resetTimer.start();
});
} }
@@ -210,6 +247,8 @@ void NodeInstanceView::modelAttached(Model *model)
NodeInstance newStateInstance = instanceForModelNode(stateNode); NodeInstance newStateInstance = instanceForModelNode(stateNode);
activateState(newStateInstance); activateState(newStateInstance);
} }
updateWatcher({});
} }
void NodeInstanceView::modelAboutToBeDetached(Model * model) void NodeInstanceView::modelAboutToBeDetached(Model * model)
@@ -227,6 +266,11 @@ void NodeInstanceView::modelAboutToBeDetached(Model * model)
m_activeStateInstance = NodeInstance(); m_activeStateInstance = NodeInstance();
m_rootNodeInstance = NodeInstance(); m_rootNodeInstance = NodeInstance();
AbstractView::modelAboutToBeDetached(model); AbstractView::modelAboutToBeDetached(model);
m_resetTimer.stop();
m_updateWatcherTimer.stop();
m_pendingUpdateDirs.clear();
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->directories());
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
} }
void NodeInstanceView::handleCrash() void NodeInstanceView::handleCrash()
@@ -1704,4 +1748,61 @@ void NodeInstanceView::updatePreviewImageForNode(const ModelNode &modelNode, con
emitModelNodelPreviewPixmapChanged(modelNode, pixmap); emitModelNodelPreviewPixmapChanged(modelNode, pixmap);
} }
void NodeInstanceView::updateWatcher(const QString &path)
{
QString rootPath;
QStringList oldFiles;
QStringList oldDirs;
QStringList newFiles;
QStringList newDirs;
if (path.isEmpty()) {
// Do full update
rootPath = QFileInfo(model()->fileUrl().toLocalFile()).absolutePath();
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->directories());
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
} else {
rootPath = path;
const QStringList files = m_fileSystemWatcher->files();
const QStringList dirs = m_fileSystemWatcher->directories();
for (const auto &file : files) {
if (file.startsWith(path))
oldFiles.append(file);
}
for (const auto &dir : dirs) {
if (dir.startsWith(path))
oldDirs.append(dir);
}
}
newDirs.append(rootPath);
QDirIterator dirIterator(rootPath, {}, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (dirIterator.hasNext())
newDirs.append(dirIterator.next());
// Common shader suffixes
static const QStringList filterList {"*.frag", "*.vert",
"*.glsl", "*.glslv", "*.glslf",
"*.vsh","*.fsh"};
QDirIterator fileIterator(rootPath, filterList, QDir::Files, QDirIterator::Subdirectories);
while (fileIterator.hasNext())
newFiles.append(fileIterator.next());
if (oldDirs != newDirs) {
if (!oldDirs.isEmpty())
m_fileSystemWatcher->removePaths(oldDirs);
if (!newDirs.isEmpty())
m_fileSystemWatcher->addPaths(newDirs);
}
if (newFiles != oldFiles) {
if (!oldFiles.isEmpty())
m_fileSystemWatcher->removePaths(oldFiles);
if (!newFiles.isEmpty())
m_fileSystemWatcher->addPaths(newFiles);
}
}
} }