forked from qt-creator/qt-creator
Fixes: QDS-14683 Change-Id: Ifdb103b9b31240a290a3d847cc1bfada06b38161 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
2489 lines
88 KiB
C++
2489 lines
88 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "nodeinstanceview.h"
|
|
|
|
#include <abstractproperty.h>
|
|
#include <bindingproperty.h>
|
|
#include <captureddatacommand.h>
|
|
#include <changeauxiliarycommand.h>
|
|
#include <changebindingscommand.h>
|
|
#include <changefileurlcommand.h>
|
|
#include <changeidscommand.h>
|
|
#include <changelanguagecommand.h>
|
|
#include <changenodesourcecommand.h>
|
|
#include <changepreviewimagesizecommand.h>
|
|
#include <changeselectioncommand.h>
|
|
#include <changestatecommand.h>
|
|
#include <changevaluescommand.h>
|
|
#include <childrenchangedcommand.h>
|
|
#include <clearscenecommand.h>
|
|
#include <completecomponentcommand.h>
|
|
#include <componentcompletedcommand.h>
|
|
#include <connectionmanagerinterface.h>
|
|
#include <createinstancescommand.h>
|
|
#include <createscenecommand.h>
|
|
#include <debugoutputcommand.h>
|
|
#include <imageutils.h>
|
|
#include <informationchangedcommand.h>
|
|
#include <inputeventcommand.h>
|
|
#include <nanotrace/nanotrace.h>
|
|
#include <nanotracecommand.h>
|
|
#include <nodeabstractproperty.h>
|
|
#include <nodeinstanceserverproxy.h>
|
|
#include <nodelistproperty.h>
|
|
#include <pixmapchangedcommand.h>
|
|
#include <puppettocreatorcommand.h>
|
|
#include <qml3dnode.h>
|
|
#include <qmlchangeset.h>
|
|
#include <qmldesignerconstants.h>
|
|
#include <qmldesignertr.h>
|
|
#include <qmlstate.h>
|
|
#include <qmltimeline.h>
|
|
#include <qmltimelinekeyframegroup.h>
|
|
#include <qmlvisualnode.h>
|
|
#include <removeinstancescommand.h>
|
|
#include <removepropertiescommand.h>
|
|
#include <removesharedmemorycommand.h>
|
|
#include <reparentinstancescommand.h>
|
|
#include <requestmodelnodepreviewimagecommand.h>
|
|
#include <scenecreatedcommand.h>
|
|
#include <statepreviewimagechangedcommand.h>
|
|
#include <tokencommand.h>
|
|
#include <update3dviewstatecommand.h>
|
|
#include <valueschangedcommand.h>
|
|
#include <variantproperty.h>
|
|
#include <view3dactioncommand.h>
|
|
|
|
#include <auxiliarydataproperties.h>
|
|
#include <designersettings.h>
|
|
#include <externaldependenciesinterface.h>
|
|
#include <model.h>
|
|
#include <modelutils.h>
|
|
#include <modelnode.h>
|
|
#include <nodehints.h>
|
|
#include <qmlitemnode.h>
|
|
#include <rewriterview.h>
|
|
|
|
#include <projectstorage/projectstorage.h>
|
|
|
|
#include <hdrimage.h>
|
|
#include <utils/smallstringview.h>
|
|
|
|
#include <coreplugin/messagemanager.h>
|
|
|
|
#include <projectexplorer/kit.h>
|
|
#include <projectexplorer/target.h>
|
|
|
|
#include <qmlprojectmanager/qmlmultilanguageaspect.h>
|
|
#include <qmlprojectmanager/qmlproject.h>
|
|
|
|
#include <utils/algorithm.h>
|
|
#include <utils/qtcprocess.h>
|
|
#include <utils/qtcassert.h>
|
|
#include <utils/theme/theme.h>
|
|
#include <utils/threadutils.h>
|
|
|
|
#include <qtsupport/qtkitaspect.h>
|
|
|
|
#include <QDirIterator>
|
|
#include <QFileSystemWatcher>
|
|
#include <QImageReader>
|
|
#include <QLocale>
|
|
#include <QMultiHash>
|
|
#include <QPainter>
|
|
#include <QPicture>
|
|
#include <QTimerEvent>
|
|
#include <QUrl>
|
|
#include <QByteArray>
|
|
|
|
enum {
|
|
debug = false
|
|
};
|
|
|
|
/*!
|
|
\defgroup CoreInstance
|
|
*/
|
|
/*!
|
|
\class QmlDesigner::NodeInstanceView
|
|
\ingroup CoreInstance
|
|
\brief The NodeInstanceView class is the central class to create and manage
|
|
instances of the ModelNode class.
|
|
|
|
This view is used to instantiate the model nodes. Many abstract views hold a
|
|
node instance view to get values from the node instances.
|
|
For this purpose, this view can be rendered offscreen.
|
|
|
|
\sa NodeInstance, ModelNode
|
|
*/
|
|
|
|
namespace QmlDesigner {
|
|
|
|
/*!
|
|
Constructs a node instance view object as a child of \a parent. If \a parent
|
|
is destructed, this instance is destructed, too.
|
|
|
|
The class will be rendered offscreen if not set otherwise.
|
|
|
|
\sa ~NodeInstanceView, setRenderOffScreen()
|
|
*/
|
|
NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager,
|
|
ExternalDependenciesInterface &externalDependencies,
|
|
bool qsbEnabled)
|
|
: AbstractView{externalDependencies}
|
|
, m_connectionManager(connectionManager)
|
|
, m_externalDependencies(externalDependencies)
|
|
, m_baseStatePreviewImage(QSize(100, 100), QImage::Format_ARGB32)
|
|
, m_restartProcessTimerId(0)
|
|
, m_fileSystemWatcher(new QFileSystemWatcher(this))
|
|
, m_qsbEnabled(qsbEnabled)
|
|
{
|
|
setKind(Kind::NodeInstance);
|
|
|
|
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, [this] {
|
|
if (isAttached())
|
|
resetPuppet();
|
|
});
|
|
m_updateWatcherTimer.setSingleShot(true);
|
|
m_updateWatcherTimer.setInterval(100);
|
|
QObject::connect(&m_updateWatcherTimer, &QTimer::timeout, this, [this] {
|
|
for (const auto &path : std::as_const(m_pendingUpdateDirs))
|
|
updateWatcher(path);
|
|
m_pendingUpdateDirs.clear();
|
|
});
|
|
|
|
// Since generating qsb files is asynchronous and can trigger directory changes, which in turn
|
|
// can trigger qsb generation, compressing qsb generation is necessary to avoid a lot of
|
|
// unnecessary generation when project with multiple shaders is opened.
|
|
m_generateQsbFilesTimer.setSingleShot(true);
|
|
m_generateQsbFilesTimer.setInterval(100);
|
|
QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, this, [this] {
|
|
handleShaderChanges();
|
|
});
|
|
|
|
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this,
|
|
[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, [this](const QString &path) {
|
|
if (m_qsbTargets.contains(path)) {
|
|
m_qsbTargets.insert(path, true);
|
|
m_generateQsbFilesTimer.start();
|
|
} else if (m_remainingQsbTargets <= 0) {
|
|
m_resetTimer.start();
|
|
}
|
|
});
|
|
|
|
m_rotBlockTimer.setSingleShot(true);
|
|
m_rotBlockTimer.setInterval(0);
|
|
QObject::connect(&m_rotBlockTimer, &QTimer::timeout, this, &NodeInstanceView::updateRotationBlocks);
|
|
}
|
|
|
|
|
|
/*!
|
|
Destructs a node instance view object.
|
|
*/
|
|
NodeInstanceView::~NodeInstanceView()
|
|
{
|
|
removeAllInstanceNodeRelationships();
|
|
m_currentTarget = nullptr;
|
|
}
|
|
|
|
//\{
|
|
|
|
static bool isSkippedRootNode(const ModelNode &node)
|
|
{
|
|
return node.metaInfo().isQtQuickListModel();
|
|
}
|
|
|
|
static bool isSkippedNode(const ModelNode &node)
|
|
{
|
|
auto model = node.model();
|
|
|
|
auto listElement = model->qtQmlModelsListElementMetaInfo();
|
|
auto xmlRole = model->qtQmlXmlListModelXmlListModelRoleMetaInfo();
|
|
|
|
return node.metaInfo().isBasedOn(listElement, xmlRole);
|
|
}
|
|
|
|
static bool parentTakesOverRendering(const ModelNode &modelNode)
|
|
{
|
|
ModelNode currentNode = modelNode;
|
|
|
|
while ((currentNode = currentNode.parentProperty().parentModelNode())) {
|
|
if (NodeHints::fromModelNode(currentNode).takesOverRenderingOfChildren())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static QString crashErrorMessage()
|
|
{
|
|
return Tr::tr("Internal process (QML Puppet) crashed.");
|
|
}
|
|
|
|
/*!
|
|
Notifies the view that it was attached to \a model. For every model node in
|
|
the model, a NodeInstance will be created.
|
|
*/
|
|
|
|
void NodeInstanceView::modelAttached(Model *model)
|
|
{
|
|
AbstractView::modelAttached(model);
|
|
m_nodeInstanceServer = createNodeInstanceServerProxy();
|
|
m_lastCrashTime.start();
|
|
m_connectionManager.setCrashCallback(m_crashCallback);
|
|
|
|
if (!isSkippedRootNode(rootModelNode())) {
|
|
m_nodeInstanceServer->createScene(createCreateSceneCommand());
|
|
m_nodeInstanceServer->changeSelection(createChangeSelectionCommand(model->selectedNodes(this)));
|
|
}
|
|
|
|
ModelNode stateNode = currentStateNode();
|
|
if (stateNode.metaInfo().isQtQuickState()) {
|
|
NodeInstance newStateInstance = instanceForModelNode(stateNode);
|
|
activateState(newStateInstance);
|
|
}
|
|
|
|
if (m_qsbEnabled) {
|
|
m_generateQsbFilesTimer.stop();
|
|
m_qsbTargets.clear();
|
|
updateQsbPathToFilterMap();
|
|
updateWatcher({});
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::modelAboutToBeDetached(Model * model)
|
|
{
|
|
m_connectionManager.setCrashCallback({});
|
|
|
|
m_nodeInstanceCache.insert(model,
|
|
NodeInstanceCacheData(m_nodeInstanceHash, m_statePreviewImage));
|
|
|
|
removeAllInstanceNodeRelationships();
|
|
if (m_nodeInstanceServer) {
|
|
m_nodeInstanceServer->clearScene(createClearSceneCommand());
|
|
m_nodeInstanceServer.reset();
|
|
}
|
|
m_statePreviewImage.clear();
|
|
m_baseStatePreviewImage = QImage();
|
|
removeAllInstanceNodeRelationships();
|
|
m_activeStateInstance = NodeInstance();
|
|
m_rootNodeInstance = NodeInstance();
|
|
AbstractView::modelAboutToBeDetached(model);
|
|
m_resetTimer.stop();
|
|
m_updateWatcherTimer.stop();
|
|
m_pendingUpdateDirs.clear();
|
|
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->directories());
|
|
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
|
|
|
|
m_generateQsbFilesTimer.stop();
|
|
m_qsbTargets.clear();
|
|
}
|
|
|
|
void NodeInstanceView::handleCrash()
|
|
{
|
|
qint64 elaspsedTimeSinceLastCrash = m_lastCrashTime.restart();
|
|
qint64 forceRestartTime = 5000;
|
|
#ifdef QT_DEBUG
|
|
forceRestartTime = 10000;
|
|
#endif
|
|
if (elaspsedTimeSinceLastCrash > forceRestartTime) {
|
|
restartProcess();
|
|
} else {
|
|
if (isAttached()) {
|
|
model()->emitDocumentMessage(
|
|
crashErrorMessage());
|
|
}
|
|
}
|
|
|
|
emitCustomNotification(QStringLiteral("puppet crashed"));
|
|
}
|
|
|
|
void NodeInstanceView::startPuppetTransaction()
|
|
{
|
|
/* We assume no transaction is active. */
|
|
QTC_ASSERT(!m_puppetTransaction.isValid(), return);
|
|
m_puppetTransaction = beginRewriterTransaction("NodeInstanceView::PuppetTransaction");
|
|
}
|
|
|
|
void NodeInstanceView::endPuppetTransaction()
|
|
{
|
|
/* We assume a transaction is active. */
|
|
QTC_ASSERT(m_puppetTransaction.isValid(), return);
|
|
|
|
/* Committing a transaction should not throw, but if there is
|
|
* an issue with rewriting we should show an error message, instead
|
|
* of simply crashing.
|
|
*/
|
|
|
|
try {
|
|
m_puppetTransaction.commit();
|
|
} catch (Exception &e) {
|
|
e.showException();
|
|
}
|
|
}
|
|
|
|
|
|
void NodeInstanceView::clearErrors()
|
|
{
|
|
for (NodeInstance &instance : instances()) {
|
|
instance.setError({});
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::restartProcess()
|
|
{
|
|
clearErrors();
|
|
emitInstanceErrorChange({});
|
|
if (isAttached())
|
|
model()->emitDocumentMessage({}, {});
|
|
|
|
if (m_restartProcessTimerId)
|
|
killTimer(m_restartProcessTimerId);
|
|
|
|
if (model()) {
|
|
m_nodeInstanceServer.reset();
|
|
m_nodeInstanceServer = createNodeInstanceServerProxy();
|
|
|
|
if (!isSkippedRootNode(rootModelNode())) {
|
|
m_nodeInstanceServer->createScene(createCreateSceneCommand());
|
|
m_nodeInstanceServer->changeSelection(
|
|
createChangeSelectionCommand(model()->selectedNodes(this)));
|
|
}
|
|
|
|
ModelNode stateNode = currentStateNode();
|
|
if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState()) {
|
|
NodeInstance newStateInstance = instanceForModelNode(stateNode);
|
|
activateState(newStateInstance);
|
|
}
|
|
}
|
|
|
|
m_restartProcessTimerId = 0;
|
|
}
|
|
|
|
void NodeInstanceView::delayedRestartProcess()
|
|
{
|
|
if (0 == m_restartProcessTimerId)
|
|
m_restartProcessTimerId = startTimer(100);
|
|
}
|
|
|
|
void NodeInstanceView::nodeCreated(const ModelNode &createdNode)
|
|
{
|
|
NodeInstance instance = loadNode(createdNode);
|
|
|
|
if (isSkippedNode(createdNode))
|
|
return;
|
|
|
|
QList<VariantProperty> propertyList;
|
|
propertyList.append(createdNode.variantProperty("x"));
|
|
propertyList.append(createdNode.variantProperty("y"));
|
|
updatePosition(propertyList);
|
|
|
|
m_nodeInstanceServer->createInstances(createCreateInstancesCommand({instance}));
|
|
m_nodeInstanceServer->changePropertyValues(
|
|
createChangeValueCommand(createdNode.variantProperties()));
|
|
m_nodeInstanceServer->completeComponent(createComponentCompleteCommand({instance}));
|
|
}
|
|
|
|
/*! Notifies the view that \a removedNode will be removed.
|
|
*/
|
|
void NodeInstanceView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
|
{
|
|
m_nodeInstanceServer->removeInstances(createRemoveInstancesCommand(removedNode));
|
|
m_nodeInstanceServer->removeSharedMemory(
|
|
createRemoveSharedMemoryCommand("Image", removedNode.internalId()));
|
|
removeInstanceAndSubInstances(removedNode);
|
|
}
|
|
|
|
void NodeInstanceView::resetHorizontalAnchors(const ModelNode &modelNode)
|
|
{
|
|
QList<BindingProperty> bindingList;
|
|
QList<VariantProperty> valueList;
|
|
|
|
if (modelNode.hasBindingProperty("x"))
|
|
bindingList.append(modelNode.bindingProperty("x"));
|
|
else if (modelNode.hasVariantProperty("x"))
|
|
valueList.append(modelNode.variantProperty("x"));
|
|
|
|
if (modelNode.hasBindingProperty("width"))
|
|
bindingList.append(modelNode.bindingProperty("width"));
|
|
else if (modelNode.hasVariantProperty("width"))
|
|
valueList.append(modelNode.variantProperty("width"));
|
|
|
|
if (!valueList.isEmpty())
|
|
m_nodeInstanceServer->changePropertyValues(createChangeValueCommand(valueList));
|
|
|
|
if (!bindingList.isEmpty())
|
|
m_nodeInstanceServer->changePropertyBindings(createChangeBindingCommand(bindingList));
|
|
}
|
|
|
|
void NodeInstanceView::resetVerticalAnchors(const ModelNode &modelNode)
|
|
{
|
|
QList<BindingProperty> bindingList;
|
|
QList<VariantProperty> valueList;
|
|
|
|
if (modelNode.hasBindingProperty("x"))
|
|
bindingList.append(modelNode.bindingProperty("x"));
|
|
else if (modelNode.hasVariantProperty("y"))
|
|
valueList.append(modelNode.variantProperty("y"));
|
|
|
|
if (modelNode.hasBindingProperty("height"))
|
|
bindingList.append(modelNode.bindingProperty("height"));
|
|
else if (modelNode.hasVariantProperty("height"))
|
|
valueList.append(modelNode.variantProperty("height"));
|
|
|
|
if (!valueList.isEmpty())
|
|
m_nodeInstanceServer->changePropertyValues(createChangeValueCommand(valueList));
|
|
|
|
if (!bindingList.isEmpty())
|
|
m_nodeInstanceServer->changePropertyBindings(createChangeBindingCommand(bindingList));
|
|
}
|
|
|
|
void NodeInstanceView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
|
|
QList<ModelNode> nodeList;
|
|
QList<AbstractProperty> nonNodePropertyList;
|
|
|
|
for (const AbstractProperty &property : propertyList) {
|
|
if (property.isNodeAbstractProperty())
|
|
nodeList.append(property.toNodeAbstractProperty().allSubNodes());
|
|
else
|
|
nonNodePropertyList.append(property);
|
|
}
|
|
|
|
RemoveInstancesCommand removeInstancesCommand = createRemoveInstancesCommand(nodeList);
|
|
|
|
if (!removeInstancesCommand.instanceIds().isEmpty())
|
|
m_nodeInstanceServer->removeInstances(removeInstancesCommand);
|
|
|
|
m_nodeInstanceServer->removeSharedMemory(createRemoveSharedMemoryCommand("Image", nodeList));
|
|
m_nodeInstanceServer->removeProperties(createRemovePropertiesCommand(nonNodePropertyList));
|
|
|
|
for (const AbstractProperty &property : propertyList) {
|
|
const PropertyNameView name = property.name();
|
|
if (name == "anchors.fill") {
|
|
resetHorizontalAnchors(property.parentModelNode());
|
|
resetVerticalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.centerIn") {
|
|
resetHorizontalAnchors(property.parentModelNode());
|
|
resetVerticalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.top") {
|
|
resetVerticalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.left") {
|
|
resetHorizontalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.right") {
|
|
resetHorizontalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.bottom") {
|
|
resetVerticalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.horizontalCenter") {
|
|
resetHorizontalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.verticalCenter") {
|
|
resetVerticalAnchors(property.parentModelNode());
|
|
} else if (name == "anchors.baseline") {
|
|
resetVerticalAnchors(property.parentModelNode());
|
|
} else {
|
|
maybeResetOnPropertyChange(name, property.parentModelNode(),
|
|
AbstractView::EmptyPropertiesRemoved);
|
|
}
|
|
}
|
|
|
|
for (const ModelNode &node : std::as_const(nodeList))
|
|
removeInstanceNodeRelationship(node);
|
|
}
|
|
|
|
void NodeInstanceView::removeInstanceAndSubInstances(const ModelNode &node)
|
|
{
|
|
const QList<ModelNode> subNodes = node.allSubModelNodes();
|
|
for (const ModelNode &subNode : subNodes) {
|
|
if (hasInstanceForModelNode(subNode))
|
|
removeInstanceNodeRelationship(subNode);
|
|
}
|
|
|
|
if (hasInstanceForModelNode(node))
|
|
removeInstanceNodeRelationship(node);
|
|
}
|
|
|
|
void NodeInstanceView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/)
|
|
{
|
|
restartProcess();
|
|
}
|
|
|
|
void NodeInstanceView::nodeTypeChanged(const ModelNode &, const TypeName &, int, int)
|
|
{
|
|
restartProcess();
|
|
}
|
|
|
|
void NodeInstanceView::bindingPropertiesChanged(const QList<BindingProperty>& propertyList,
|
|
PropertyChangeFlags propertyChange)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
m_nodeInstanceServer->changePropertyBindings(createChangeBindingCommand(propertyList));
|
|
|
|
for (const auto &property : propertyList)
|
|
maybeResetOnPropertyChange(property.name(), property.parentModelNode(), propertyChange);
|
|
}
|
|
|
|
/*!
|
|
Notifies the view that abstract property values specified by \a propertyList
|
|
were changed for a model node.
|
|
|
|
The property will be set for the node instance.
|
|
|
|
\sa AbstractProperty, NodeInstance, ModelNode
|
|
*/
|
|
|
|
void NodeInstanceView::variantPropertiesChanged(const QList<VariantProperty>& propertyList,
|
|
PropertyChangeFlags propertyChange)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
updatePosition(propertyList);
|
|
m_nodeInstanceServer->changePropertyValues(createChangeValueCommand(propertyList));
|
|
|
|
for (const auto &property : propertyList)
|
|
maybeResetOnPropertyChange(property.name(), property.parentModelNode(), propertyChange);
|
|
}
|
|
/*!
|
|
Notifies the view that the property parent of the model node \a node has
|
|
changed from \a oldPropertyParent to \a newPropertyParent.
|
|
|
|
\note Also the \c {ModelNode::childNodes()} list was changed. The
|
|
Node instance tree will be changed to reflect the model node tree change.
|
|
|
|
\sa NodeInstance, ModelNode
|
|
*/
|
|
|
|
void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags /*propertyChange*/)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
if (!isSkippedNode(node)) {
|
|
updateChildren(newPropertyParent);
|
|
m_nodeInstanceServer->reparentInstances(
|
|
createReparentInstancesCommand(node, newPropertyParent, oldPropertyParent));
|
|
|
|
// Reset QML Puppet when particle emitter/affector is reparented to work around issue in
|
|
// autodetecting the particle system it belongs to. QTBUG-101157
|
|
if (auto metaInfo = node.metaInfo();
|
|
(metaInfo.isQtQuick3DParticles3DParticleEmitter3D()
|
|
|| metaInfo.isQtQuick3DParticles3DAffector3D())
|
|
&& node.property("system").toBindingProperty().expression().isEmpty()) {
|
|
resetPuppet();
|
|
}
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::fileUrlChanged(const QUrl &/*oldUrl*/, const QUrl &newUrl)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
m_nodeInstanceServer->changeFileUrl(createChangeFileUrlCommand(newUrl));
|
|
}
|
|
|
|
void NodeInstanceView::nodeIdChanged(const ModelNode& node, const QString& /*newId*/, const QString &oldId)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
|
|
if (hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
m_nodeInstanceServer->changeIds(createChangeIdsCommand({instance}));
|
|
m_imageDataMap.remove(oldId);
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::nodeOrderChanged(const NodeListProperty &listProperty)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
QVector<ReparentContainer> containerList;
|
|
PropertyNameView propertyName = listProperty.name();
|
|
qint32 containerInstanceId = -1;
|
|
ModelNode containerNode = listProperty.parentModelNode();
|
|
if (hasInstanceForModelNode(containerNode))
|
|
containerInstanceId = instanceForModelNode(containerNode).instanceId();
|
|
|
|
const QList<ModelNode> nodes = listProperty.toModelNodeList();
|
|
for (const ModelNode &node : nodes) {
|
|
qint32 instanceId = -1;
|
|
if (hasInstanceForModelNode(node)) {
|
|
instanceId = instanceForModelNode(node).instanceId();
|
|
ReparentContainer container(instanceId,
|
|
containerInstanceId,
|
|
propertyName.toByteArray(),
|
|
containerInstanceId,
|
|
propertyName.toByteArray());
|
|
containerList.append(container);
|
|
}
|
|
}
|
|
|
|
m_nodeInstanceServer->reparentInstances(ReparentInstancesCommand(containerList));
|
|
}
|
|
|
|
void NodeInstanceView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/)
|
|
{
|
|
delayedRestartProcess();
|
|
}
|
|
|
|
void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
|
|
AuxiliaryDataKeyView key,
|
|
const QVariant &value)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return );
|
|
|
|
switch (key.type) {
|
|
case AuxiliaryDataType::Document:
|
|
if ((key == lockedProperty || key == invisibleProperty) && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
|
|
PropertyValueContainer container{instance.instanceId(),
|
|
key.name,
|
|
value,
|
|
{},
|
|
key.type};
|
|
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
|
|
}
|
|
break;
|
|
|
|
case AuxiliaryDataType::NodeInstanceAuxiliary:
|
|
if (hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
PropertyValueContainer container{instance.instanceId(),
|
|
key.name,
|
|
value,
|
|
TypeName(),
|
|
key.type};
|
|
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
|
|
}
|
|
break;
|
|
|
|
case AuxiliaryDataType::NodeInstancePropertyOverwrite:
|
|
if (hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
PropertyValueContainer container{instance.instanceId(), key.name, value, TypeName(), key.type};
|
|
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
|
|
}
|
|
break;
|
|
|
|
case AuxiliaryDataType::Temporary:
|
|
if (node.isRootNode()) {
|
|
if (key == languageProperty) {
|
|
const QString languageAsString = value.toString();
|
|
if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(
|
|
m_currentTarget))
|
|
multiLanguageAspect->setCurrentLocale(languageAsString);
|
|
m_nodeInstanceServer->changeLanguage({languageAsString});
|
|
} else if (key.name == "previewSize") {
|
|
m_nodeInstanceServer->changePreviewImageSize(value.toSize());
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::customNotification(const AbstractView *view,
|
|
const QString &identifier,
|
|
const QList<ModelNode> &,
|
|
const QList<QVariant> &)
|
|
{
|
|
if (view && identifier == QStringLiteral("reset QmlPuppet"))
|
|
delayedRestartProcess();
|
|
}
|
|
|
|
void NodeInstanceView::customNotification(const CustomNotificationPackage &package)
|
|
{
|
|
if (auto inputEvent = std::get_if<InputEvent>(&package))
|
|
sendInputEvent(inputEvent->event);
|
|
else if (auto resize3DCanvas = std::get_if<Resize3DCanvas>(&package))
|
|
edit3DViewResized(resize3DCanvas->size);
|
|
}
|
|
|
|
void NodeInstanceView::nodeSourceChanged(const ModelNode &node, const QString & newNodeSource)
|
|
{
|
|
QTC_ASSERT(m_nodeInstanceServer, return);
|
|
if (hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
ChangeNodeSourceCommand changeNodeSourceCommand(instance.instanceId(), newNodeSource);
|
|
m_nodeInstanceServer->changeNodeSource(changeNodeSourceCommand);
|
|
|
|
// QML Puppet doesn't deal with node source changes properly, so just reset the QML Puppet for now
|
|
resetPuppet(); // TODO: Remove this once the issue is properly fixed (QDS-4955)
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::capturedData(const CapturedDataCommand &) {}
|
|
|
|
void NodeInstanceView::currentStateChanged(const ModelNode &node)
|
|
{
|
|
NodeInstance newStateInstance = instanceForModelNode(node);
|
|
|
|
if (newStateInstance.isValid() && node.metaInfo().isQtQuickState())
|
|
activateState(newStateInstance);
|
|
else
|
|
activateBaseState();
|
|
}
|
|
|
|
void NodeInstanceView::sceneCreated(const SceneCreatedCommand &) {}
|
|
|
|
//\}
|
|
|
|
void NodeInstanceView::removeAllInstanceNodeRelationships()
|
|
{
|
|
m_nodeInstanceHash.clear();
|
|
}
|
|
|
|
/*!
|
|
Returns a list of all node instances.
|
|
|
|
\sa NodeInstance
|
|
*/
|
|
|
|
QList<NodeInstance> NodeInstanceView::instances() const
|
|
{
|
|
return m_nodeInstanceHash.values();
|
|
}
|
|
|
|
/*!
|
|
Returns the node instance for \a node, which must be valid.
|
|
|
|
Returns an invalid node instance if no node instance for this model node
|
|
exists.
|
|
|
|
\sa NodeInstance
|
|
*/
|
|
NodeInstance NodeInstanceView::instanceForModelNode(const ModelNode &node) const
|
|
{
|
|
Q_ASSERT(node.isValid());
|
|
Q_ASSERT(m_nodeInstanceHash.contains(node));
|
|
Q_ASSERT(m_nodeInstanceHash.value(node).modelNode() == node);
|
|
return m_nodeInstanceHash.value(node);
|
|
}
|
|
|
|
bool NodeInstanceView::hasInstanceForModelNode(const ModelNode &node) const
|
|
{
|
|
return m_nodeInstanceHash.contains(node);
|
|
}
|
|
|
|
NodeInstance NodeInstanceView::instanceForId(qint32 id) const
|
|
{
|
|
if (id < 0 || !hasModelNodeForInternalId(id))
|
|
return NodeInstance();
|
|
|
|
return m_nodeInstanceHash.value(modelNodeForInternalId(id));
|
|
}
|
|
|
|
bool NodeInstanceView::hasInstanceForId(qint32 id) const
|
|
{
|
|
if (id < 0 || !hasModelNodeForInternalId(id))
|
|
return false;
|
|
|
|
return m_nodeInstanceHash.contains(modelNodeForInternalId(id));
|
|
}
|
|
|
|
/*!
|
|
Returns the root node instance of this view.
|
|
|
|
\sa NodeInstance
|
|
*/
|
|
NodeInstance NodeInstanceView::rootNodeInstance() const
|
|
{
|
|
return m_rootNodeInstance;
|
|
}
|
|
|
|
/*!
|
|
Returns the \a instance of this view.
|
|
|
|
This can be the root node instance if it is specified in the QML file.
|
|
\code
|
|
QGraphicsView {
|
|
QGraphicsScene {
|
|
Item {}
|
|
}
|
|
}
|
|
\endcode
|
|
|
|
If there is node view in the QML file:
|
|
\code
|
|
|
|
Item {}
|
|
|
|
\endcode
|
|
Then a new node instance for this QGraphicsView is
|
|
generated which is not the root instance of this node instance view.
|
|
|
|
This is the way to get this QGraphicsView node instance.
|
|
|
|
\sa NodeInstance
|
|
*/
|
|
|
|
|
|
|
|
void NodeInstanceView::insertInstanceRelationships(const NodeInstance &instance)
|
|
{
|
|
Q_ASSERT(instance.instanceId() >=0);
|
|
if (m_nodeInstanceHash.contains(instance.modelNode()))
|
|
return;
|
|
|
|
m_nodeInstanceHash.insert(instance.modelNode(), instance);
|
|
}
|
|
|
|
void NodeInstanceView::removeInstanceNodeRelationship(const ModelNode &node)
|
|
{
|
|
Q_ASSERT(m_nodeInstanceHash.contains(node));
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
m_nodeInstanceHash.remove(node);
|
|
m_statePreviewImage.remove(node);
|
|
instance.makeInvalid();
|
|
}
|
|
|
|
void NodeInstanceView::setStateInstance(const NodeInstance &stateInstance)
|
|
{
|
|
m_activeStateInstance = stateInstance;
|
|
}
|
|
|
|
void NodeInstanceView::clearStateInstance()
|
|
{
|
|
m_activeStateInstance = NodeInstance();
|
|
}
|
|
|
|
NodeInstance NodeInstanceView::activeStateInstance() const
|
|
{
|
|
return m_activeStateInstance;
|
|
}
|
|
|
|
void NodeInstanceView::updateChildren(const NodeAbstractProperty &newPropertyParent)
|
|
{
|
|
const QList<ModelNode> childNodeVector = newPropertyParent.directSubNodes();
|
|
|
|
qint32 parentInstanceId = newPropertyParent.parentModelNode().internalId();
|
|
|
|
for (const ModelNode &childNode : childNodeVector) {
|
|
qint32 instanceId = childNode.internalId();
|
|
if (hasInstanceForId(instanceId)) {
|
|
NodeInstance instance = instanceForId(instanceId);
|
|
if (instance.directUpdates())
|
|
instance.setParentId(parentInstanceId);
|
|
}
|
|
}
|
|
|
|
if (!childNodeVector.isEmpty())
|
|
emitInstancesChildrenChanged(childNodeVector);
|
|
}
|
|
|
|
void setXValue(NodeInstance &instance, const VariantProperty &variantProperty, QMultiHash<ModelNode, InformationName> &informationChangeHash)
|
|
{
|
|
instance.setX(variantProperty.value().toDouble());
|
|
informationChangeHash.insert(instance.modelNode(), Transform);
|
|
}
|
|
|
|
void setYValue(NodeInstance &instance, const VariantProperty &variantProperty, QMultiHash<ModelNode, InformationName> &informationChangeHash)
|
|
{
|
|
instance.setY(variantProperty.value().toDouble());
|
|
informationChangeHash.insert(instance.modelNode(), Transform);
|
|
}
|
|
|
|
|
|
void NodeInstanceView::updatePosition(const QList<VariantProperty> &propertyList)
|
|
{
|
|
QMultiHash<ModelNode, InformationName> informationChangeHash;
|
|
|
|
QmlModelState currentState = currentStateNode();
|
|
bool isBaseState = currentState.isBaseState();
|
|
|
|
for (const VariantProperty &variantProperty : propertyList) {
|
|
if (variantProperty.name() == "x") {
|
|
const ModelNode modelNode = variantProperty.parentModelNode();
|
|
if (!isBaseState && QmlPropertyChanges::isValidQmlPropertyChanges(modelNode)) {
|
|
ModelNode targetModelNode = QmlPropertyChanges(modelNode).target();
|
|
if (targetModelNode.isValid()) {
|
|
NodeInstance instance = instanceForModelNode(targetModelNode);
|
|
setXValue(instance, variantProperty, informationChangeHash);
|
|
}
|
|
} else {
|
|
NodeInstance instance = instanceForModelNode(modelNode);
|
|
setXValue(instance, variantProperty, informationChangeHash);
|
|
}
|
|
} else if (variantProperty.name() == "y") {
|
|
const ModelNode modelNode = variantProperty.parentModelNode();
|
|
if (!isBaseState && QmlPropertyChanges::isValidQmlPropertyChanges(modelNode)) {
|
|
ModelNode targetModelNode = QmlPropertyChanges(modelNode).target();
|
|
if (targetModelNode.isValid()) {
|
|
NodeInstance instance = instanceForModelNode(targetModelNode);
|
|
setYValue(instance, variantProperty, informationChangeHash);
|
|
}
|
|
} else {
|
|
NodeInstance instance = instanceForModelNode(modelNode);
|
|
setYValue(instance, variantProperty, informationChangeHash);
|
|
}
|
|
} else if (QmlTimeline::isValidQmlTimeline(currentTimelineNode())
|
|
&& variantProperty.name() == "value"
|
|
&& QmlTimelineKeyframeGroup::isValidKeyframe(variantProperty.parentModelNode())) {
|
|
QmlTimelineKeyframeGroup frames = QmlTimelineKeyframeGroup::keyframeGroupForKeyframe(
|
|
variantProperty.parentModelNode());
|
|
|
|
if (frames.propertyName() == "x" && frames.target().isValid()) {
|
|
NodeInstance instance = instanceForModelNode(frames.target());
|
|
setXValue(instance, variantProperty, informationChangeHash);
|
|
} else if (frames.propertyName() == "y" && frames.target().isValid()) {
|
|
NodeInstance instance = instanceForModelNode(frames.target());
|
|
setYValue(instance, variantProperty, informationChangeHash);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!informationChangeHash.isEmpty())
|
|
emitInstanceInformationsChange(informationChangeHash);
|
|
}
|
|
|
|
NodeInstance NodeInstanceView::loadNode(const ModelNode &node)
|
|
{
|
|
NodeInstance instance(NodeInstance::create(node));
|
|
|
|
insertInstanceRelationships(instance);
|
|
|
|
if (node.isRootNode())
|
|
m_rootNodeInstance = instance;
|
|
|
|
return instance;
|
|
}
|
|
|
|
void NodeInstanceView::activateState(const NodeInstance &instance)
|
|
{
|
|
m_nodeInstanceServer->changeState(ChangeStateCommand(instance.instanceId()));
|
|
}
|
|
|
|
void NodeInstanceView::activateBaseState()
|
|
{
|
|
m_nodeInstanceServer->changeState(ChangeStateCommand(-1));
|
|
}
|
|
|
|
void NodeInstanceView::removeRecursiveChildRelationship(const ModelNode &removedNode)
|
|
{
|
|
// if (hasInstanceForNode(removedNode)) {
|
|
// instanceForNode(removedNode).setId(QString());
|
|
// }
|
|
|
|
const QList<ModelNode> nodes = removedNode.directSubModelNodes();
|
|
for (const ModelNode &childNode : nodes)
|
|
removeRecursiveChildRelationship(childNode);
|
|
|
|
removeInstanceNodeRelationship(removedNode);
|
|
}
|
|
|
|
QRectF NodeInstanceView::sceneRect() const
|
|
{
|
|
if (rootNodeInstance().isValid())
|
|
return rootNodeInstance().boundingRect();
|
|
|
|
return {};
|
|
}
|
|
|
|
namespace {
|
|
|
|
QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList)
|
|
{
|
|
QList<ModelNode> filteredNodeList;
|
|
for (const ModelNode &node : nodeList) {
|
|
if (isSkippedNode(node))
|
|
continue;
|
|
|
|
filteredNodeList.append(node);
|
|
}
|
|
|
|
return filteredNodeList;
|
|
}
|
|
bool shouldSendAuxiliary(const AuxiliaryDataKey &key)
|
|
{
|
|
return key.type == AuxiliaryDataType::NodeInstancePropertyOverwrite
|
|
|| key.type == AuxiliaryDataType::NodeInstanceAuxiliary || key == invisibleProperty
|
|
|| key == lockedProperty;
|
|
}
|
|
|
|
bool parentIsBehavior(ModelNode node)
|
|
{
|
|
while (node.isValid() && !node.isRootNode()) {
|
|
if (!node.behaviorPropertyName().isEmpty())
|
|
return true;
|
|
|
|
node = node.parentProperty().parentModelNode();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
TypeName createQualifiedTypeName(const ModelNode &node)
|
|
{
|
|
if (!node)
|
|
return {};
|
|
|
|
#ifdef QDS_USE_PROJECTSTORAGE
|
|
auto model = node.model();
|
|
auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId());
|
|
if (exportedTypes.size()) {
|
|
const auto &exportedType = exportedTypes.front();
|
|
using Storage::ModuleKind;
|
|
auto module = model->projectStorage()->module(exportedType.moduleId);
|
|
Utils::PathString typeName;
|
|
switch (module.kind) {
|
|
case ModuleKind::QmlLibrary:
|
|
typeName += module.name;
|
|
typeName += '/';
|
|
break;
|
|
case ModuleKind::PathLibrary:
|
|
break;
|
|
case ModuleKind::CppLibrary:
|
|
break;
|
|
}
|
|
|
|
typeName += exportedType.name;
|
|
|
|
return typeName.toQByteArray();
|
|
}
|
|
|
|
return {};
|
|
#else
|
|
return node.type();
|
|
#endif
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
|
|
{
|
|
QList<ModelNode> nodeList = allModelNodes();
|
|
QList<NodeInstance> instanceList;
|
|
|
|
std::optional oldNodeInstanceHash = m_nodeInstanceCache.take(model());
|
|
if (oldNodeInstanceHash && oldNodeInstanceHash->instances.value(rootModelNode()).isValid()) {
|
|
instanceList = loadInstancesFromCache(nodeList, oldNodeInstanceHash.value());
|
|
} else {
|
|
for (const ModelNode &node : std::as_const(nodeList)) {
|
|
NodeInstance instance = loadNode(node);
|
|
if (!isSkippedNode(node))
|
|
instanceList.append(instance);
|
|
}
|
|
}
|
|
|
|
clearErrors();
|
|
|
|
nodeList = filterNodesForSkipItems(nodeList);
|
|
|
|
QList<VariantProperty> variantPropertyList;
|
|
QList<BindingProperty> bindingPropertyList;
|
|
|
|
QVector<PropertyValueContainer> auxiliaryContainerVector;
|
|
for (const ModelNode &node : std::as_const(nodeList)) {
|
|
variantPropertyList.append(node.variantProperties());
|
|
bindingPropertyList.append(node.bindingProperties());
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
for (const auto &element : node.auxiliaryData()) {
|
|
if (shouldSendAuxiliary(element.first)) {
|
|
auxiliaryContainerVector.emplace_back(instance.instanceId(),
|
|
element.first.name.toQByteArray(),
|
|
element.second,
|
|
TypeName(),
|
|
element.first.type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QVector<InstanceContainer> instanceContainerList;
|
|
for (const NodeInstance &instance : std::as_const(instanceList)) {
|
|
InstanceContainer::NodeSourceType nodeSourceType = static_cast<InstanceContainer::NodeSourceType>(instance.modelNode().nodeSourceType());
|
|
|
|
InstanceContainer::NodeMetaType nodeMetaType = InstanceContainer::ObjectMetaType;
|
|
if (instance.modelNode().metaInfo().isQtQuickItem())
|
|
nodeMetaType = InstanceContainer::ItemMetaType;
|
|
|
|
InstanceContainer::NodeFlags nodeFlags;
|
|
|
|
if (parentTakesOverRendering(instance.modelNode()))
|
|
nodeFlags |= InstanceContainer::ParentTakesOverRendering;
|
|
|
|
const auto modelNode = instance.modelNode();
|
|
|
|
InstanceContainer container(instance.instanceId(),
|
|
createQualifiedTypeName(modelNode),
|
|
modelNode.majorVersion(),
|
|
modelNode.minorVersion(),
|
|
ModelUtils::componentFilePath(modelNode),
|
|
modelNode.nodeSource(),
|
|
nodeSourceType,
|
|
nodeMetaType,
|
|
nodeFlags);
|
|
|
|
if (!parentIsBehavior(modelNode)) {
|
|
instanceContainerList.append(container);
|
|
}
|
|
}
|
|
|
|
QVector<ReparentContainer> reparentContainerList;
|
|
for (const NodeInstance &instance : std::as_const(instanceList)) {
|
|
if (instance.modelNode().hasParentProperty()) {
|
|
NodeAbstractProperty parentProperty = instance.modelNode().parentProperty();
|
|
ReparentContainer container(instance.instanceId(),
|
|
-1,
|
|
PropertyName(),
|
|
instanceForModelNode(parentProperty.parentModelNode()).instanceId(),
|
|
parentProperty.name().toByteArray());
|
|
reparentContainerList.append(container);
|
|
}
|
|
}
|
|
|
|
QVector<IdContainer> idContainerList;
|
|
for (const NodeInstance &instance : std::as_const(instanceList)) {
|
|
QString id = instance.modelNode().id();
|
|
if (!id.isEmpty()) {
|
|
IdContainer container(instance.instanceId(), id);
|
|
idContainerList.append(container);
|
|
}
|
|
}
|
|
|
|
QVector<PropertyValueContainer> valueContainerList;
|
|
for (const VariantProperty &property : std::as_const(variantPropertyList)) {
|
|
ModelNode node = property.parentModelNode();
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
PropertyValueContainer container(instance.instanceId(), property.name(), property.value(), property.dynamicTypeName());
|
|
valueContainerList.append(container);
|
|
}
|
|
}
|
|
|
|
QVector<PropertyBindingContainer> bindingContainerList;
|
|
for (const BindingProperty &property : std::as_const(bindingPropertyList)) {
|
|
ModelNode node = property.parentModelNode();
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
const QString expression = fullyQualifyPropertyIfApplies(property);
|
|
|
|
PropertyBindingContainer container(instance.instanceId(),
|
|
property.name().toByteArray(),
|
|
expression,
|
|
property.dynamicTypeName());
|
|
bindingContainerList.append(container);
|
|
}
|
|
}
|
|
|
|
QVector<AddImportContainer> importVector;
|
|
for (const Import &import : model()->imports())
|
|
importVector.append(AddImportContainer(import.url(), import.file(), import.version(), import.alias(), import.importPaths()));
|
|
|
|
QVector<MockupTypeContainer> mockupTypesVector;
|
|
|
|
#ifndef QDS_USE_PROJECTSTORAGE
|
|
for (const QmlTypeData &cppTypeData : model()->rewriterView()->getQMLTypes()) {
|
|
const QString versionString = cppTypeData.versionString;
|
|
int majorVersion = -1;
|
|
int minorVersion = -1;
|
|
|
|
if (versionString.contains(QStringLiteral("."))) {
|
|
const QStringList splittedString = versionString.split(QStringLiteral("."));
|
|
majorVersion = splittedString.constFirst().toInt();
|
|
minorVersion = splittedString.constLast().toInt();
|
|
}
|
|
|
|
bool isItem = false;
|
|
|
|
if (!cppTypeData.isSingleton) { /* Singletons only appear on the right hand sides of bindings and create just warnings. */
|
|
const TypeName typeName = cppTypeData.typeName.toUtf8();
|
|
const QString uri = cppTypeData.importUrl;
|
|
|
|
NodeMetaInfo metaInfo = model()->metaInfo(uri.toUtf8() + "." + typeName);
|
|
|
|
if (metaInfo.isValid())
|
|
isItem = metaInfo.isGraphicalItem();
|
|
|
|
MockupTypeContainer mockupType(typeName, uri, majorVersion, minorVersion, isItem);
|
|
|
|
mockupTypesVector.append(mockupType);
|
|
} else { /* We need a type for the signleton import */
|
|
const TypeName typeName = cppTypeData.typeName.toUtf8() + "Mockup";
|
|
const QString uri = cppTypeData.importUrl;
|
|
|
|
MockupTypeContainer mockupType(typeName, uri, majorVersion, minorVersion, isItem);
|
|
|
|
mockupTypesVector.append(mockupType);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
QString lastUsedLanguage;
|
|
if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(m_currentTarget))
|
|
lastUsedLanguage = multiLanguageAspect->currentLocale();
|
|
|
|
ModelNode stateNode = currentStateNode();
|
|
qint32 stateInstanceId = 0;
|
|
if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState())
|
|
stateInstanceId = stateNode.internalId();
|
|
|
|
QHash<QString, QVariantMap> sceneStates = m_edit3DToolStates[model()->fileUrl()];
|
|
QHash<QString, QVariantMap> projectStates = m_edit3DToolStates[
|
|
QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath())];
|
|
const QString ptsId = "@PTS";
|
|
if (projectStates.contains(ptsId))
|
|
sceneStates.insert(ptsId, projectStates[ptsId]);
|
|
|
|
return CreateSceneCommand(instanceContainerList,
|
|
reparentContainerList,
|
|
idContainerList,
|
|
valueContainerList,
|
|
bindingContainerList,
|
|
auxiliaryContainerVector,
|
|
importVector,
|
|
mockupTypesVector,
|
|
model()->fileUrl(),
|
|
m_externalDependencies.currentResourcePath(),
|
|
sceneStates,
|
|
lastUsedLanguage,
|
|
stateInstanceId);
|
|
}
|
|
|
|
ClearSceneCommand NodeInstanceView::createClearSceneCommand() const
|
|
{
|
|
return {};
|
|
}
|
|
|
|
CompleteComponentCommand NodeInstanceView::createComponentCompleteCommand(const QList<NodeInstance> &instanceList) const
|
|
{
|
|
QVector<qint32> containerList;
|
|
for (const NodeInstance &instance : instanceList) {
|
|
if (instance.instanceId() >= 0)
|
|
containerList.append(instance.instanceId());
|
|
}
|
|
|
|
return CompleteComponentCommand(containerList);
|
|
}
|
|
|
|
ComponentCompletedCommand NodeInstanceView::createComponentCompletedCommand(const QList<NodeInstance> &instanceList) const
|
|
{
|
|
QVector<qint32> containerList;
|
|
for (const NodeInstance &instance : instanceList) {
|
|
if (instance.instanceId() >= 0)
|
|
containerList.append(instance.instanceId());
|
|
}
|
|
|
|
return ComponentCompletedCommand(containerList);
|
|
}
|
|
|
|
CreateInstancesCommand NodeInstanceView::createCreateInstancesCommand(const QList<NodeInstance> &instanceList) const
|
|
{
|
|
QVector<InstanceContainer> containerList;
|
|
for (const NodeInstance &instance : instanceList) {
|
|
InstanceContainer::NodeSourceType nodeSourceType = static_cast<InstanceContainer::NodeSourceType>(instance.modelNode().nodeSourceType());
|
|
|
|
InstanceContainer::NodeMetaType nodeMetaType = InstanceContainer::ObjectMetaType;
|
|
if (instance.modelNode().metaInfo().isQtQuickItem())
|
|
nodeMetaType = InstanceContainer::ItemMetaType;
|
|
|
|
InstanceContainer::NodeFlags nodeFlags;
|
|
|
|
if (parentTakesOverRendering(instance.modelNode()))
|
|
nodeFlags |= InstanceContainer::ParentTakesOverRendering;
|
|
|
|
const auto modelNode = instance.modelNode();
|
|
InstanceContainer container(instance.instanceId(),
|
|
createQualifiedTypeName(modelNode),
|
|
modelNode.majorVersion(),
|
|
modelNode.minorVersion(),
|
|
ModelUtils::componentFilePath(modelNode),
|
|
modelNode.nodeSource(),
|
|
nodeSourceType,
|
|
nodeMetaType,
|
|
nodeFlags);
|
|
containerList.append(container);
|
|
}
|
|
|
|
return CreateInstancesCommand(containerList);
|
|
}
|
|
|
|
ReparentInstancesCommand NodeInstanceView::createReparentInstancesCommand(const QList<NodeInstance> &instanceList) const
|
|
{
|
|
QVector<ReparentContainer> containerList;
|
|
for (const NodeInstance &instance : instanceList) {
|
|
if (instance.modelNode().hasParentProperty()) {
|
|
NodeAbstractProperty parentProperty = instance.modelNode().parentProperty();
|
|
ReparentContainer container(instance.instanceId(),
|
|
-1,
|
|
PropertyName(),
|
|
instanceForModelNode(parentProperty.parentModelNode()).instanceId(),
|
|
parentProperty.name().toByteArray());
|
|
containerList.append(container);
|
|
}
|
|
}
|
|
|
|
return ReparentInstancesCommand(containerList);
|
|
}
|
|
|
|
ReparentInstancesCommand NodeInstanceView::createReparentInstancesCommand(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent) const
|
|
{
|
|
QVector<ReparentContainer> containerList;
|
|
|
|
qint32 newParentInstanceId = -1;
|
|
qint32 oldParentInstanceId = -1;
|
|
|
|
if (newPropertyParent.isValid() && hasInstanceForModelNode(newPropertyParent.parentModelNode()))
|
|
newParentInstanceId = instanceForModelNode(newPropertyParent.parentModelNode()).instanceId();
|
|
|
|
|
|
if (oldPropertyParent.isValid() && hasInstanceForModelNode(oldPropertyParent.parentModelNode()))
|
|
oldParentInstanceId = instanceForModelNode(oldPropertyParent.parentModelNode()).instanceId();
|
|
|
|
ReparentContainer container(instanceForModelNode(node).instanceId(),
|
|
oldParentInstanceId,
|
|
oldPropertyParent.name().toByteArray(),
|
|
newParentInstanceId,
|
|
newPropertyParent.name().toByteArray());
|
|
|
|
containerList.append(container);
|
|
|
|
return ReparentInstancesCommand(containerList);
|
|
}
|
|
|
|
ChangeFileUrlCommand NodeInstanceView::createChangeFileUrlCommand(const QUrl &fileUrl) const
|
|
{
|
|
return {fileUrl};
|
|
}
|
|
|
|
ChangeValuesCommand NodeInstanceView::createChangeValueCommand(const QList<VariantProperty>& propertyList) const
|
|
{
|
|
QVector<PropertyValueContainer> containerList;
|
|
|
|
QmlTimeline timeline = currentTimelineNode();
|
|
|
|
bool reflectionFlag = m_puppetTransaction.isValid() && (!timeline || !timeline.isRecording());
|
|
|
|
for (const VariantProperty &property : propertyList) {
|
|
ModelNode node = property.parentModelNode();
|
|
if (QmlPropertyChanges::isValidQmlPropertyChanges(node))
|
|
reflectionFlag = false;
|
|
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
PropertyValueContainer container(instance.instanceId(), property.name(), property.value(), property.dynamicTypeName());
|
|
container.setReflectionFlag(reflectionFlag);
|
|
containerList.append(container);
|
|
}
|
|
}
|
|
|
|
return ChangeValuesCommand(containerList);
|
|
}
|
|
|
|
ChangeBindingsCommand NodeInstanceView::createChangeBindingCommand(const QList<BindingProperty> &propertyList) const
|
|
{
|
|
QVector<PropertyBindingContainer> containerList;
|
|
|
|
for (const BindingProperty &property : propertyList) {
|
|
ModelNode node = property.parentModelNode();
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
const QString expression = fullyQualifyPropertyIfApplies(property);
|
|
PropertyBindingContainer container(instance.instanceId(),
|
|
property.name().toByteArray(),
|
|
expression,
|
|
property.dynamicTypeName());
|
|
containerList.append(container);
|
|
}
|
|
|
|
}
|
|
|
|
return {containerList};
|
|
}
|
|
|
|
ChangeIdsCommand NodeInstanceView::createChangeIdsCommand(const QList<NodeInstance> &instanceList) const
|
|
{
|
|
QVector<IdContainer> containerList;
|
|
for (const NodeInstance &instance : instanceList) {
|
|
QString id = instance.modelNode().id();
|
|
if (!id.isEmpty()) {
|
|
IdContainer container(instance.instanceId(), id);
|
|
containerList.append(container);
|
|
}
|
|
}
|
|
|
|
return {containerList};
|
|
}
|
|
|
|
|
|
|
|
RemoveInstancesCommand NodeInstanceView::createRemoveInstancesCommand(const QList<ModelNode> &nodeList) const
|
|
{
|
|
QVector<qint32> idList;
|
|
for (const ModelNode &node : nodeList) {
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
|
|
if (instance.instanceId() >= 0)
|
|
idList.append(instance.instanceId());
|
|
}
|
|
}
|
|
|
|
return RemoveInstancesCommand(idList);
|
|
}
|
|
|
|
ChangeSelectionCommand NodeInstanceView::createChangeSelectionCommand(const QList<ModelNode> &nodeList) const
|
|
{
|
|
QVector<qint32> idList;
|
|
for (const ModelNode &node : nodeList) {
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
|
|
if (instance.instanceId() >= 0)
|
|
idList.append(instance.instanceId());
|
|
}
|
|
}
|
|
|
|
return ChangeSelectionCommand(idList);
|
|
}
|
|
|
|
RemoveInstancesCommand NodeInstanceView::createRemoveInstancesCommand(const ModelNode &node) const
|
|
{
|
|
QVector<qint32> idList;
|
|
|
|
if (node.isValid() && hasInstanceForModelNode(node))
|
|
idList.append(instanceForModelNode(node).instanceId());
|
|
|
|
return RemoveInstancesCommand(idList);
|
|
}
|
|
|
|
RemovePropertiesCommand NodeInstanceView::createRemovePropertiesCommand(const QList<AbstractProperty> &propertyList) const
|
|
{
|
|
QVector<PropertyAbstractContainer> containerList;
|
|
|
|
for (const AbstractProperty &property : propertyList) {
|
|
ModelNode node = property.parentModelNode();
|
|
if (node.isValid() && hasInstanceForModelNode(node)) {
|
|
NodeInstance instance = instanceForModelNode(node);
|
|
PropertyAbstractContainer container(instance.instanceId(),
|
|
property.name().toByteArray(),
|
|
property.dynamicTypeName());
|
|
containerList.append(container);
|
|
}
|
|
|
|
}
|
|
|
|
return RemovePropertiesCommand(containerList);
|
|
}
|
|
|
|
RemoveSharedMemoryCommand NodeInstanceView::createRemoveSharedMemoryCommand(const QString &sharedMemoryTypeName, quint32 keyNumber)
|
|
{
|
|
return RemoveSharedMemoryCommand(sharedMemoryTypeName, {static_cast<qint32>(keyNumber)});
|
|
}
|
|
|
|
RemoveSharedMemoryCommand NodeInstanceView::createRemoveSharedMemoryCommand(const QString &sharedMemoryTypeName, const QList<ModelNode> &nodeList)
|
|
{
|
|
QVector<qint32> keyNumberVector;
|
|
|
|
for (const ModelNode &modelNode : nodeList)
|
|
keyNumberVector.append(modelNode.internalId());
|
|
|
|
return RemoveSharedMemoryCommand(sharedMemoryTypeName, keyNumberVector);
|
|
}
|
|
|
|
void NodeInstanceView::valuesChanged(const ValuesChangedCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QList<QPair<ModelNode, PropertyName> > valuePropertyChangeList;
|
|
|
|
const QVector<PropertyValueContainer> containers = command.valueChanges();
|
|
for (const PropertyValueContainer &container : containers) {
|
|
if (hasInstanceForId(container.instanceId())) {
|
|
NodeInstance instance = instanceForId(container.instanceId());
|
|
if (instance.isValid()) {
|
|
instance.setProperty(container.name(), container.value());
|
|
valuePropertyChangeList.append({instance.modelNode(), container.name()});
|
|
}
|
|
}
|
|
}
|
|
|
|
m_nodeInstanceServer->removeSharedMemory(
|
|
createRemoveSharedMemoryCommand(QStringLiteral("Values"), command.keyNumber()));
|
|
|
|
if (!valuePropertyChangeList.isEmpty())
|
|
emitInstancePropertyChange(valuePropertyChangeList);
|
|
}
|
|
|
|
void NodeInstanceView::valuesModified(const ValuesModifiedCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
if (command.transactionOption == ValuesModifiedCommand::TransactionOption::Start)
|
|
startPuppetTransaction();
|
|
|
|
for (const PropertyValueContainer &container : command.valueChanges()) {
|
|
if (hasInstanceForId(container.instanceId())) {
|
|
NodeInstance instance = instanceForId(container.instanceId());
|
|
if (instance.isValid()) {
|
|
if (auto qmlObjectNode = QmlObjectNode(instance.modelNode())) {
|
|
if (qmlObjectNode.modelValue(container.name()) != container.value())
|
|
qmlObjectNode.setVariantProperty(container.name(), container.value());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (command.transactionOption == ValuesModifiedCommand::TransactionOption::End)
|
|
endPuppetTransaction();
|
|
}
|
|
|
|
void NodeInstanceView::pixmapChanged(const PixmapChangedCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QSet<ModelNode> renderImageChangeSet;
|
|
|
|
QVector<InformationContainer> containerVector;
|
|
|
|
const QVector<ImageContainer> containers = command.images();
|
|
for (const ImageContainer &container : containers) {
|
|
if (hasInstanceForId(container.instanceId())) {
|
|
NodeInstance instance = instanceForId(container.instanceId());
|
|
if (instance.isValid()) {
|
|
if (container.rect().isValid()) {
|
|
InformationContainer rectContainer = InformationContainer(container.instanceId(),
|
|
BoundingRectPixmap,
|
|
container.rect(),
|
|
{},
|
|
{});
|
|
containerVector.append(rectContainer);
|
|
}
|
|
instance.setRenderPixmap(container.image());
|
|
renderImageChangeSet.insert(instance.modelNode());
|
|
}
|
|
}
|
|
}
|
|
|
|
m_nodeInstanceServer->benchmark(Q_FUNC_INFO + QString::number(renderImageChangeSet.size()));
|
|
|
|
if (!renderImageChangeSet.isEmpty())
|
|
emitInstancesRenderImageChanged(Utils::toList(renderImageChangeSet));
|
|
|
|
if (!containerVector.isEmpty()) {
|
|
QMultiHash<ModelNode, InformationName> informationChangeHash = informationChanged(
|
|
containerVector);
|
|
|
|
if (!informationChangeHash.isEmpty())
|
|
emitInstanceInformationsChange(informationChangeHash);
|
|
}
|
|
}
|
|
|
|
QMultiHash<ModelNode, InformationName> NodeInstanceView::informationChanged(const QVector<InformationContainer> &containerVector)
|
|
{
|
|
QMultiHash<ModelNode, InformationName> informationChangeHash;
|
|
|
|
for (const InformationContainer &container : containerVector) {
|
|
if (hasInstanceForId(container.instanceId())) {
|
|
NodeInstance instance = instanceForId(container.instanceId());
|
|
if (instance.isValid()) {
|
|
InformationName informationChange = instance.setInformation(container.name(), container.information(), container.secondInformation(), container.thirdInformation());
|
|
if (informationChange != NoInformationChange)
|
|
informationChangeHash.insert(instance.modelNode(), informationChange);
|
|
}
|
|
}
|
|
}
|
|
|
|
return informationChangeHash;
|
|
}
|
|
|
|
void NodeInstanceView::informationChanged(const InformationChangedCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QMultiHash<ModelNode, InformationName> informationChangeHash = informationChanged(command.informations());
|
|
|
|
m_nodeInstanceServer->benchmark(Q_FUNC_INFO + QString::number(informationChangeHash.size()));
|
|
|
|
if (!informationChangeHash.isEmpty())
|
|
emitInstanceInformationsChange(informationChangeHash);
|
|
}
|
|
|
|
QImage NodeInstanceView::statePreviewImage(const ModelNode &stateNode) const
|
|
{
|
|
if (!isAttached())
|
|
return {};
|
|
|
|
if (stateNode == rootModelNode())
|
|
return m_baseStatePreviewImage;
|
|
|
|
return m_statePreviewImage.value(stateNode);
|
|
}
|
|
|
|
void NodeInstanceView::setTarget(ProjectExplorer::Target *newTarget)
|
|
{
|
|
if (m_currentTarget != newTarget) {
|
|
m_currentTarget = newTarget;
|
|
if (m_currentTarget && m_currentTarget->kit()) {
|
|
if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(m_currentTarget->kit())) {
|
|
m_qsbPath = qtVer->binPath().pathAppended("qsb").withExecutableSuffix();
|
|
if (!m_qsbPath.exists())
|
|
m_qsbPath.clear();
|
|
}
|
|
}
|
|
|
|
restartProcess();
|
|
}
|
|
}
|
|
|
|
ProjectExplorer::Target *NodeInstanceView::target() const
|
|
{
|
|
return m_currentTarget;
|
|
}
|
|
|
|
void NodeInstanceView::statePreviewImagesChanged(const StatePreviewImageChangedCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QVector<ModelNode> previewImageChangeVector;
|
|
|
|
const QVector<ImageContainer> containers = command.previews();
|
|
for (const ImageContainer &container : containers) {
|
|
if (container.keyNumber() == -1) {
|
|
m_baseStatePreviewImage = container.image();
|
|
if (!container.image().isNull())
|
|
previewImageChangeVector.append(rootModelNode());
|
|
} else if (hasInstanceForId(container.instanceId())) {
|
|
ModelNode node = modelNodeForInternalId(container.instanceId());
|
|
m_statePreviewImage.insert(node, container.image());
|
|
if (!container.image().isNull())
|
|
previewImageChangeVector.append(node);
|
|
}
|
|
}
|
|
|
|
if (!previewImageChangeVector.isEmpty())
|
|
emitInstancesPreviewImageChanged(previewImageChangeVector);
|
|
}
|
|
|
|
void NodeInstanceView::componentCompleted(const ComponentCompletedCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QVector<ModelNode> nodeVector;
|
|
|
|
const QVector<qint32> instances = command.instances();
|
|
for (const qint32 &instanceId : instances) {
|
|
if (hasModelNodeForInternalId(instanceId))
|
|
nodeVector.append(modelNodeForInternalId(instanceId));
|
|
}
|
|
|
|
m_nodeInstanceServer->benchmark(Q_FUNC_INFO + QString::number(nodeVector.size()));
|
|
|
|
if (!nodeVector.isEmpty())
|
|
emitInstancesCompleted(nodeVector);
|
|
}
|
|
|
|
void NodeInstanceView::childrenChanged(const ChildrenChangedCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QVector<ModelNode> childNodeVector;
|
|
|
|
const QVector<qint32> instances = command.childrenInstances();
|
|
for (const qint32 &instanceId : instances) {
|
|
if (hasInstanceForId(instanceId)) {
|
|
NodeInstance instance = instanceForId(instanceId);
|
|
if (instance.parentId() == -1 || !instance.directUpdates())
|
|
instance.setParentId(command.parentInstanceId());
|
|
childNodeVector.append(instance.modelNode());
|
|
}
|
|
}
|
|
|
|
QMultiHash<ModelNode, InformationName> informationChangeHash = informationChanged(command.informations());
|
|
|
|
if (!informationChangeHash.isEmpty())
|
|
emitInstanceInformationsChange(informationChangeHash);
|
|
|
|
if (!childNodeVector.isEmpty())
|
|
emitInstancesChildrenChanged(childNodeVector);
|
|
}
|
|
|
|
void NodeInstanceView::token(const TokenCommand &command)
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QVector<ModelNode> nodeVector;
|
|
|
|
const QVector<qint32> instances = command.instances();
|
|
for (const qint32 &instanceId : instances) {
|
|
if (hasModelNodeForInternalId(instanceId))
|
|
nodeVector.append(modelNodeForInternalId(instanceId));
|
|
}
|
|
|
|
if (isAttached())
|
|
model()->emitInstanceToken(this, command.tokenName(), command.tokenNumber(), nodeVector);
|
|
}
|
|
|
|
void NodeInstanceView::debugOutput(const DebugOutputCommand & command)
|
|
{
|
|
DocumentMessage error(crashErrorMessage());
|
|
if (command.instanceIds().isEmpty() && isAttached()) {
|
|
model()->emitDocumentMessage(command.text());
|
|
} else {
|
|
QVector<qint32> instanceIdsWithChangedErrors;
|
|
const QVector<qint32> instanceIds = command.instanceIds();
|
|
for (const qint32 &instanceId : instanceIds) {
|
|
NodeInstance instance = instanceForId(instanceId);
|
|
if (instance.isValid()) {
|
|
if (instance.setError(command.text()))
|
|
instanceIdsWithChangedErrors.append(instanceId);
|
|
} else if (isAttached()) {
|
|
model()->emitDocumentMessage(command.text());
|
|
}
|
|
}
|
|
emitInstanceErrorChange(instanceIdsWithChangedErrors);
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::sendToken(const QString &token, int number, const QVector<ModelNode> &nodeVector)
|
|
{
|
|
QVector<qint32> instanceIdVector;
|
|
for (const ModelNode &node : nodeVector)
|
|
instanceIdVector.append(node.internalId());
|
|
|
|
m_nodeInstanceServer->token(TokenCommand(token, number, instanceIdVector));
|
|
}
|
|
|
|
void NodeInstanceView::selectionChanged(const ChangeSelectionCommand &command)
|
|
{
|
|
clearSelectedModelNodes();
|
|
const QVector<qint32> instanceIds = command.instanceIds();
|
|
for (const qint32 &instanceId : instanceIds) {
|
|
if (hasModelNodeForInternalId(instanceId))
|
|
selectModelNode(modelNodeForInternalId(instanceId));
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command)
|
|
{
|
|
if (command.type() == PuppetToCreatorCommand::Edit3DToolState) {
|
|
if (m_nodeInstanceServer) {
|
|
auto data = qvariant_cast<QVariantList>(command.data());
|
|
if (data.size() == 3) {
|
|
QString qmlId = data[0].toString();
|
|
QUrl mainKey;
|
|
if (qmlId == "@PTS") // Project tool state
|
|
mainKey = QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath());
|
|
else
|
|
mainKey = model()->fileUrl();
|
|
m_edit3DToolStates[mainKey][qmlId].insert(data[1].toString(), data[2]);
|
|
}
|
|
}
|
|
} else if (command.type() == PuppetToCreatorCommand::Render3DView) {
|
|
ImageContainer container = qvariant_cast<ImageContainer>(command.data());
|
|
if (!container.image().isNull() && isAttached())
|
|
model()->emitRenderImage3DChanged(this, container.image());
|
|
} else if (command.type() == PuppetToCreatorCommand::ActiveSceneChanged) {
|
|
const auto sceneState = qvariant_cast<QVariantMap>(command.data());
|
|
if (isAttached())
|
|
model()->emitUpdateActiveScene3D(this, sceneState);
|
|
} else if (command.type() == PuppetToCreatorCommand::ActiveSplitChanged) {
|
|
// Active split change is a special case of active scene change
|
|
QVariantMap splitState;
|
|
splitState.insert("activeSplit", command.data());
|
|
if (isAttached())
|
|
model()->emitUpdateActiveScene3D(this, splitState);
|
|
} else if (command.type() == PuppetToCreatorCommand::RenderModelNodePreviewImage) {
|
|
ImageContainer container = qvariant_cast<ImageContainer>(command.data());
|
|
QImage image = container.image();
|
|
if (hasModelNodeForInternalId(container.instanceId()) && !image.isNull()) {
|
|
auto node = modelNodeForInternalId(container.instanceId());
|
|
if (node.isValid()) {
|
|
const double ratio = m_externalDependencies.formEditorDevicePixelRatio();
|
|
image.setDevicePixelRatio(ratio);
|
|
updatePreviewImageForNode(node, image, container.requestId());
|
|
}
|
|
}
|
|
} else if (command.type() == PuppetToCreatorCommand::Import3DSupport) {
|
|
QVariantMap supportMap;
|
|
if (externalDependencies().isQt6Project())
|
|
supportMap = qvariant_cast<QVariantMap>(command.data());
|
|
if (isAttached())
|
|
model()->emitImport3DSupportChanged(this, supportMap);
|
|
} else if (command.type() == PuppetToCreatorCommand::NodeAtPos) {
|
|
auto data = qvariant_cast<QVariantList>(command.data());
|
|
if (data.size() == 2) {
|
|
ModelNode modelNode = modelNodeForInternalId(data[0].toInt());
|
|
QVector3D pos3d = data[1].value<QVector3D>();
|
|
if (isAttached())
|
|
model()->emitNodeAtPosResult(this, modelNode, pos3d);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<NodeInstanceServerProxy> NodeInstanceView::createNodeInstanceServerProxy()
|
|
{
|
|
return std::make_unique<NodeInstanceServerProxy>(this,
|
|
m_currentTarget,
|
|
m_connectionManager,
|
|
m_externalDependencies);
|
|
}
|
|
|
|
void NodeInstanceView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
|
|
const QList<ModelNode> & /*lastSelectedNodeList*/)
|
|
{
|
|
m_nodeInstanceServer->changeSelection(createChangeSelectionCommand(selectedNodeList));
|
|
m_rotBlockTimer.start();
|
|
}
|
|
|
|
void NodeInstanceView::sendInputEvent(QEvent *event)
|
|
{
|
|
m_nodeInstanceServer->inputEvent(InputEventCommand(event));
|
|
}
|
|
|
|
void NodeInstanceView::view3DAction(View3DActionType type, const QVariant &value)
|
|
{
|
|
m_nodeInstanceServer->view3DAction({type, value});
|
|
}
|
|
|
|
constexpr double previewImageDimensions = 150.;
|
|
|
|
void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node,
|
|
const ModelNode &renderNode,
|
|
const QSize &size,
|
|
const QByteArray &requestId) const
|
|
{
|
|
if (m_nodeInstanceServer && node.isValid() && hasInstanceForModelNode(node)) {
|
|
auto instance = instanceForModelNode(node);
|
|
if (instance.isValid()) {
|
|
qint32 renderItemId = -1;
|
|
QString componentPath;
|
|
QSize imageSize;
|
|
if (renderNode.isValid()) {
|
|
auto renderInstance = instanceForModelNode(renderNode);
|
|
if (renderInstance.isValid())
|
|
renderItemId = renderInstance.instanceId();
|
|
if (renderNode.isComponent())
|
|
componentPath = ModelUtils::componentFilePath(renderNode);
|
|
} else if (node.isComponent()) {
|
|
componentPath = ModelUtils::componentFilePath(node);
|
|
}
|
|
|
|
const double ratio = m_externalDependencies.formEditorDevicePixelRatio();
|
|
|
|
if (size.isValid()) {
|
|
imageSize = size * ratio;
|
|
} else {
|
|
const int dim = std::lround(previewImageDimensions * ratio);
|
|
imageSize = {dim, dim};
|
|
}
|
|
|
|
m_nodeInstanceServer->requestModelNodePreviewImage(RequestModelNodePreviewImageCommand(
|
|
instance.instanceId(), imageSize, componentPath, renderItemId, requestId));
|
|
}
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::edit3DViewResized(const QSize &size)
|
|
{
|
|
m_nodeInstanceServer->update3DViewState(Update3dViewStateCommand(size));
|
|
}
|
|
|
|
void NodeInstanceView::timerEvent(QTimerEvent *event)
|
|
{
|
|
if (m_restartProcessTimerId == event->timerId())
|
|
restartProcess();
|
|
}
|
|
|
|
void NodeInstanceView::emitInstancePropertyChange(const QList<QPair<ModelNode, PropertyName>> &propertyList)
|
|
{
|
|
if (isAttached())
|
|
model()->emitInstancePropertyChange(this, propertyList);
|
|
}
|
|
|
|
void NodeInstanceView::emitInstanceErrorChange(const QVector<qint32> &instanceIds)
|
|
{
|
|
if (isAttached())
|
|
model()->emitInstanceErrorChange(this, instanceIds);
|
|
}
|
|
|
|
void NodeInstanceView::emitInstancesCompleted(const QVector<ModelNode> &nodeVector)
|
|
{
|
|
if (isAttached())
|
|
model()->emitInstancesCompleted(this, nodeVector);
|
|
}
|
|
|
|
void NodeInstanceView::emitInstanceInformationsChange(
|
|
const QMultiHash<ModelNode, InformationName> &informationChangeHash)
|
|
{
|
|
if (isAttached())
|
|
model()->emitInstanceInformationsChange(this, informationChangeHash);
|
|
}
|
|
|
|
void NodeInstanceView::emitInstancesRenderImageChanged(const QVector<ModelNode> &nodeVector)
|
|
{
|
|
if (isAttached())
|
|
model()->emitInstancesRenderImageChanged(this, nodeVector);
|
|
}
|
|
|
|
void NodeInstanceView::emitInstancesPreviewImageChanged(const QVector<ModelNode> &nodeVector)
|
|
{
|
|
if (isAttached())
|
|
model()->emitInstancesPreviewImageChanged(this, nodeVector);
|
|
}
|
|
|
|
void NodeInstanceView::emitInstancesChildrenChanged(const QVector<ModelNode> &nodeVector)
|
|
{
|
|
if (isAttached())
|
|
model()->emitInstancesChildrenChanged(this, nodeVector);
|
|
}
|
|
|
|
QVariant NodeInstanceView::modelNodePreviewImageDataToVariant(const ModelNodePreviewImageData &imageData) const
|
|
{
|
|
static QPixmap placeHolder;
|
|
if (placeHolder.isNull()) {
|
|
QPixmap placeHolderSrc(":/navigator/icon/tooltip_placeholder.png");
|
|
placeHolder = {150, 150};
|
|
// Placeholder has transparency, but we don't want to show the checkerboard, so
|
|
// paint in the correct background color
|
|
placeHolder.fill(Utils::creatorColor(Utils::Theme::BackgroundColorNormal));
|
|
QPainter painter(&placeHolder);
|
|
painter.drawPixmap(0, 0, 150, 150, placeHolderSrc);
|
|
}
|
|
|
|
QVariantMap map;
|
|
map.insert("type", imageData.type);
|
|
if (imageData.pixmap.isNull())
|
|
map.insert("pixmap", placeHolder);
|
|
else
|
|
map.insert("pixmap", QVariant::fromValue<QPixmap>(imageData.pixmap));
|
|
map.insert("id", imageData.id);
|
|
map.insert("info", imageData.info);
|
|
return map;
|
|
}
|
|
|
|
QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNode) const
|
|
{
|
|
if (!modelNode.isValid())
|
|
return {};
|
|
|
|
VariantProperty prop = modelNode.variantProperty("source");
|
|
QString imageSource = prop.value().toString();
|
|
|
|
ModelNodePreviewImageData imageData;
|
|
imageData.id = modelNode.id();
|
|
imageData.type = QString::fromUtf8(createQualifiedTypeName(modelNode));
|
|
const double ratio = m_externalDependencies.formEditorDevicePixelRatio();
|
|
|
|
if (imageSource.isEmpty() && modelNode.metaInfo().isQtQuick3DTexture()) {
|
|
// Texture node may have sourceItem instead
|
|
BindingProperty binding = modelNode.bindingProperty("sourceItem");
|
|
if (binding.isValid()) {
|
|
ModelNode boundNode = binding.resolveToModelNode();
|
|
if (boundNode.isValid()) {
|
|
// If bound node is a component, fall back to component render mechanism, as
|
|
// QmlItemNode::instanceRenderPixmap() often includes unnecessary empty space
|
|
// for those
|
|
if (boundNode.isComponent()) {
|
|
return previewImageDataForGenericNode(modelNode, boundNode);
|
|
} else {
|
|
QmlItemNode itemNode(boundNode);
|
|
const int dim = std::lround(previewImageDimensions * ratio);
|
|
imageData.pixmap = itemNode.instanceRenderPixmap().scaled(dim,
|
|
dim,
|
|
Qt::KeepAspectRatio);
|
|
imageData.pixmap.setDevicePixelRatio(ratio);
|
|
}
|
|
imageData.info = Tr::tr("Source item: %1").arg(boundNode.id());
|
|
}
|
|
}
|
|
} else {
|
|
if (imageSource.isEmpty() && modelNode.isComponent()) {
|
|
// Image component has no custom source set, so fall back to component rendering to get
|
|
// the default component image.
|
|
return previewImageDataForGenericNode(modelNode, {});
|
|
}
|
|
|
|
QFileInfo imageFi(imageSource);
|
|
if (imageFi.isRelative())
|
|
imageSource = QFileInfo(modelNode.model()->fileUrl().toLocalFile())
|
|
.dir()
|
|
.absoluteFilePath(imageSource);
|
|
|
|
imageFi = QFileInfo(imageSource);
|
|
QDateTime modified = imageFi.lastModified();
|
|
|
|
bool reload = true;
|
|
if (m_imageDataMap.contains(imageData.id)) {
|
|
imageData = m_imageDataMap[imageData.id];
|
|
if (modified == imageData.time)
|
|
reload = false;
|
|
}
|
|
|
|
if (reload) {
|
|
QPixmap originalPixmap;
|
|
if (modelNode.metaInfo().isQtSafeRendererSafeRendererPicture()) {
|
|
QPicture picture;
|
|
picture.load(imageSource);
|
|
if (!picture.isNull()) {
|
|
QImage paintImage(picture.width(), picture.height(), QImage::Format_ARGB32);
|
|
paintImage.fill(Qt::transparent);
|
|
QPainter painter(&paintImage);
|
|
painter.drawPicture(0, 0, picture);
|
|
painter.end();
|
|
originalPixmap = QPixmap::fromImage(paintImage);
|
|
}
|
|
} else {
|
|
if (imageFi.suffix() == "hdr")
|
|
originalPixmap = HdrImage{imageSource}.toPixmap();
|
|
else
|
|
originalPixmap.load(imageSource);
|
|
}
|
|
if (!originalPixmap.isNull()) {
|
|
const int dim = std::lround(previewImageDimensions * ratio);
|
|
imageData.pixmap = originalPixmap.scaled(dim, dim, Qt::KeepAspectRatio);
|
|
imageData.pixmap.setDevicePixelRatio(ratio);
|
|
imageData.time = modified;
|
|
imageData.info = ImageUtils::imageInfoString(imageSource);
|
|
m_imageDataMap.insert(imageData.id, imageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return modelNodePreviewImageDataToVariant(imageData);
|
|
}
|
|
|
|
void NodeInstanceView::startNanotrace()
|
|
{
|
|
NANOTRACE_INIT("QmlDesigner", "MainThread", "nanotrace_qmldesigner.json");
|
|
m_connectionManager.writeCommand(QVariant::fromValue(StartNanotraceCommand(QDir::currentPath())));
|
|
}
|
|
|
|
void NodeInstanceView::endNanotrace()
|
|
{
|
|
NANOTRACE_SHUTDOWN();
|
|
m_connectionManager.writeCommand(QVariant::fromValue(EndNanotraceCommand()) );
|
|
}
|
|
|
|
QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &modelNode,
|
|
const ModelNode &renderNode,
|
|
const QSize &size,
|
|
const QByteArray &requestId) const
|
|
{
|
|
if (!modelNode.isValid())
|
|
return {};
|
|
|
|
ModelNodePreviewImageData imageData;
|
|
|
|
// We need QML Puppet to generate the image, which needs to be asynchronous.
|
|
// Until the image is ready, we show a placeholder
|
|
const QString id = modelNode.id();
|
|
if (m_imageDataMap.contains(id)) {
|
|
imageData = m_imageDataMap[id];
|
|
} else {
|
|
imageData.type = QString::fromLatin1(createQualifiedTypeName(modelNode));
|
|
imageData.id = id;
|
|
|
|
// There might be multiple requests for different preview pixmap sizes.
|
|
// Here only the one with no request id is stored.
|
|
if (requestId.isEmpty())
|
|
m_imageDataMap.insert(id, imageData);
|
|
}
|
|
requestModelNodePreviewImage(modelNode, renderNode, size, requestId);
|
|
|
|
return modelNodePreviewImageDataToVariant(imageData);
|
|
}
|
|
|
|
void NodeInstanceView::updatePreviewImageForNode(const ModelNode &modelNode,
|
|
const QImage &image,
|
|
const QByteArray &requestId)
|
|
{
|
|
QPixmap pixmap = QPixmap::fromImage(image);
|
|
if (m_imageDataMap.contains(modelNode.id()))
|
|
m_imageDataMap[modelNode.id()].pixmap = pixmap;
|
|
if (isAttached())
|
|
model()->emitModelNodelPreviewPixmapChanged(this, modelNode, pixmap, requestId);
|
|
}
|
|
|
|
void NodeInstanceView::updateWatcher(const QString &path)
|
|
{
|
|
QString rootPath;
|
|
QStringList oldFiles;
|
|
QStringList oldDirs;
|
|
QStringList newFiles;
|
|
QStringList newDirs;
|
|
|
|
const QString projPath = m_externalDependencies.currentProjectDirPath();
|
|
|
|
if (projPath.isEmpty())
|
|
return;
|
|
|
|
const QStringList files = m_fileSystemWatcher->files();
|
|
const QStringList directories = m_fileSystemWatcher->directories();
|
|
if (path.isEmpty()) {
|
|
// Do full update
|
|
rootPath = projPath;
|
|
if (!directories.isEmpty())
|
|
m_fileSystemWatcher->removePaths(directories);
|
|
if (!files.isEmpty())
|
|
m_fileSystemWatcher->removePaths(files);
|
|
} else {
|
|
rootPath = path;
|
|
for (const auto &file : files) {
|
|
if (file.startsWith(path))
|
|
oldFiles.append(file);
|
|
}
|
|
for (const auto &directory : directories) {
|
|
if (directory.startsWith(path))
|
|
oldDirs.append(directory);
|
|
}
|
|
}
|
|
|
|
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());
|
|
|
|
// Find out which shader files need qsb files generated for them.
|
|
// Go through all configured paths and find files that match the specified filter in that path.
|
|
bool generateQsb = false;
|
|
QHash<QString, QStringList>::const_iterator it = m_qsbPathToFilterMap.constBegin();
|
|
while (it != m_qsbPathToFilterMap.constEnd()) {
|
|
if (!it.key().isEmpty() && !it.key().startsWith(rootPath)) {
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
QDirIterator qsbIterator(it.key().isEmpty() ? rootPath : it.key(),
|
|
it.value(), QDir::Files,
|
|
it.key().isEmpty() ? QDirIterator::Subdirectories
|
|
: QDirIterator::NoIteratorFlags);
|
|
|
|
while (qsbIterator.hasNext()) {
|
|
QString qsbFile = qsbIterator.next();
|
|
|
|
if (qsbFile.endsWith(".qsb"))
|
|
continue; // Skip any generated files that are caught by wildcards
|
|
|
|
// Filters may specify shader files with non-default suffixes, so add them to newFiles
|
|
if (!newFiles.contains(qsbFile))
|
|
newFiles.append(qsbFile);
|
|
|
|
// Only generate qsb files for newly detected files. This avoids immediately regenerating
|
|
// qsb file if it's manually deleted, as directory change triggers calling this method.
|
|
if (!oldFiles.contains(qsbFile)) {
|
|
m_qsbTargets.insert(qsbFile, true);
|
|
generateQsb = true;
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (generateQsb)
|
|
m_generateQsbFilesTimer.start();
|
|
}
|
|
|
|
void NodeInstanceView::handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader)
|
|
{
|
|
--m_remainingQsbTargets;
|
|
|
|
const QString errStr = qsbProcess->errorString();
|
|
const QByteArray stdErrStr = qsbProcess->readAllRawStandardError();
|
|
|
|
if (!errStr.isEmpty() || !stdErrStr.isEmpty()) {
|
|
Core::MessageManager::writeSilently(Tr::tr("Failed to generate QSB file for: %1").arg(shader));
|
|
if (!errStr.isEmpty())
|
|
Core::MessageManager::writeSilently(errStr);
|
|
if (!stdErrStr.isEmpty())
|
|
Core::MessageManager::writeSilently(QString::fromUtf8(stdErrStr));
|
|
}
|
|
|
|
if (m_remainingQsbTargets <= 0)
|
|
m_resetTimer.start();
|
|
|
|
qsbProcess->deleteLater();
|
|
}
|
|
|
|
void NodeInstanceView::updateQsbPathToFilterMap()
|
|
{
|
|
m_qsbPathToFilterMap.clear();
|
|
if (m_currentTarget && !m_qsbPath.isEmpty()) {
|
|
const auto bs = qobject_cast<QmlProjectManager::QmlBuildSystem *>(m_currentTarget->buildSystem());
|
|
if (!bs)
|
|
return;
|
|
const QStringList shaderToolFiles = bs->shaderToolFiles();
|
|
|
|
const QString projPath = m_externalDependencies.currentProjectDirPath();
|
|
|
|
if (projPath.isEmpty())
|
|
return;
|
|
|
|
// Parse ShaderTool files from project configuration.
|
|
// Separate files to path and file name (called filter here as it can contain wildcards)
|
|
// and group filters by paths. Blank path indicates project-wide file wildcard.
|
|
for (const auto &file : shaderToolFiles) {
|
|
auto idx = file.lastIndexOf('/');
|
|
QString key;
|
|
QString filter;
|
|
if (idx >= 0) {
|
|
key = projPath + "/" + file.left(idx);
|
|
filter = file.mid(idx + 1);
|
|
} else {
|
|
filter = file;
|
|
}
|
|
m_qsbPathToFilterMap[key].append(filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::handleShaderChanges()
|
|
{
|
|
if (!m_currentTarget)
|
|
return;
|
|
|
|
const auto bs = qobject_cast<QmlProjectManager::QmlBuildSystem *>(m_currentTarget->buildSystem());
|
|
if (!bs)
|
|
return;
|
|
|
|
QStringList baseArgs = bs->shaderToolArgs();
|
|
if (baseArgs.isEmpty())
|
|
return;
|
|
|
|
QStringList newShaders;
|
|
QHash<QString, bool>::iterator it = m_qsbTargets.begin();
|
|
while (it != m_qsbTargets.end()) {
|
|
if (it.value()) {
|
|
newShaders.append(it.key());
|
|
it.value() = false;
|
|
}
|
|
++it;
|
|
}
|
|
|
|
if (newShaders.isEmpty())
|
|
return;
|
|
|
|
m_remainingQsbTargets += newShaders.size();
|
|
|
|
for (const auto &shader : std::as_const(newShaders)) {
|
|
const Utils::FilePath srcFile = Utils::FilePath::fromString(shader);
|
|
const Utils::FilePath srcPath = srcFile.absolutePath();
|
|
const Utils::FilePath outPath = Utils::FilePath::fromString(shader + ".qsb");
|
|
|
|
if (!srcFile.exists()) {
|
|
m_qsbTargets.remove(shader);
|
|
--m_remainingQsbTargets;
|
|
continue;
|
|
}
|
|
|
|
if ((outPath.exists() && outPath.lastModified() > srcFile.lastModified())) {
|
|
--m_remainingQsbTargets;
|
|
continue;
|
|
}
|
|
|
|
QStringList args = baseArgs;
|
|
args.append("-o");
|
|
args.append(outPath.toUrlishString());
|
|
args.append(shader);
|
|
auto qsbProcess = new Utils::Process(this);
|
|
connect(qsbProcess, &Utils::Process::done, this, [this, qsbProcess, shader] {
|
|
handleQsbProcessExit(qsbProcess, shader);
|
|
});
|
|
qsbProcess->setWorkingDirectory(srcPath);
|
|
qsbProcess->setCommand({m_qsbPath, args});
|
|
qsbProcess->start();
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::updateRotationBlocks()
|
|
{
|
|
if (!model())
|
|
return;
|
|
|
|
QList<ModelNode> qml3DNodes;
|
|
QSet<ModelNode> rotationKeyframeTargets;
|
|
bool groupsResolved = false;
|
|
const PropertyName targetPropName {"target"};
|
|
const PropertyName propertyPropName {"property"};
|
|
const PropertyName rotationPropName {"rotation"};
|
|
const QList<ModelNode> selectedNodes = selectedModelNodes();
|
|
for (const auto &node : selectedNodes) {
|
|
if (Qml3DNode::isValidQml3DNode(node)) {
|
|
if (!groupsResolved) {
|
|
const QList<ModelNode> keyframeGroups = allModelNodesOfType(
|
|
model()->qtQuickTimelineKeyframeGroupMetaInfo());
|
|
for (const auto &kfgNode : keyframeGroups) {
|
|
if (kfgNode.isValid()) {
|
|
VariantProperty varProp = kfgNode.variantProperty(propertyPropName);
|
|
if (varProp.isValid() && varProp.value().value<PropertyName>() == rotationPropName) {
|
|
BindingProperty bindProp = kfgNode.bindingProperty(targetPropName);
|
|
if (bindProp.isValid()) {
|
|
ModelNode targetNode = bindProp.resolveToModelNode();
|
|
if (Qml3DNode::isValidQml3DNode(targetNode))
|
|
rotationKeyframeTargets.insert(targetNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
groupsResolved = true;
|
|
}
|
|
qml3DNodes.append(node);
|
|
}
|
|
}
|
|
if (!qml3DNodes.isEmpty()) {
|
|
for (const auto &node : std::as_const(qml3DNodes)) {
|
|
if (rotationKeyframeTargets.contains(node))
|
|
node.setAuxiliaryData(rotBlockProperty, true);
|
|
else
|
|
node.setAuxiliaryData(rotBlockProperty, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NodeInstanceView::maybeResetOnPropertyChange(PropertyNameView name,
|
|
const ModelNode &node,
|
|
PropertyChangeFlags flags)
|
|
{
|
|
bool reset = false;
|
|
if (flags & AbstractView::PropertiesAdded && name == "model"
|
|
&& node.metaInfo().isQtQuickRepeater()) {
|
|
// TODO: This is a workaround for QTBUG-97583:
|
|
// Reset QML Puppet when repeater model is first added, if there is already a delegate
|
|
if (node.hasProperty("delegate"))
|
|
reset = true;
|
|
} else if (name == "shader" && node.metaInfo().isQtQuick3DShader()) {
|
|
reset = true;
|
|
}
|
|
if (reset)
|
|
resetPuppet();
|
|
}
|
|
|
|
QList<NodeInstance> NodeInstanceView::loadInstancesFromCache(const QList<ModelNode> &nodeList,
|
|
const NodeInstanceCacheData &cache)
|
|
{
|
|
QList<NodeInstance> instanceList;
|
|
|
|
auto previews = cache.previewImages;
|
|
auto iterator = previews.begin();
|
|
while (iterator != previews.end()) {
|
|
if (iterator.key().isValid())
|
|
m_statePreviewImage.insert(iterator.key(), iterator.value());
|
|
iterator++;
|
|
}
|
|
|
|
for (const ModelNode &node : std::as_const(nodeList)) {
|
|
NodeInstance instance = cache.instances.value(node);
|
|
if (instance.isValid())
|
|
insertInstanceRelationships(instance);
|
|
else
|
|
instance = loadNode(node);
|
|
|
|
if (node.isRootNode())
|
|
m_rootNodeInstance = instance;
|
|
if (!isSkippedNode(node))
|
|
instanceList.append(instanceForModelNode(node));
|
|
}
|
|
|
|
return instanceList;
|
|
}
|
|
|
|
static bool isCapitalized(const QString &string)
|
|
{
|
|
if (string.length() == 0)
|
|
return false;
|
|
|
|
return string.at(0).isUpper();
|
|
}
|
|
|
|
QString NodeInstanceView::fullyQualifyPropertyIfApplies(const BindingProperty &property) const
|
|
{
|
|
auto parentModelNode = property.parentModelNode();
|
|
|
|
const QString originalExpression = property.expression();
|
|
|
|
if (!parentModelNode || parentModelNode.isRootNode())
|
|
return originalExpression;
|
|
|
|
const ModelNode rootNode = rootModelNode();
|
|
|
|
if (!rootNode.hasId())
|
|
return originalExpression;
|
|
|
|
if (originalExpression.contains('.'))
|
|
return originalExpression;
|
|
|
|
if (isCapitalized(originalExpression))
|
|
return originalExpression;
|
|
|
|
const NodeMetaInfo metaInfo = parentModelNode.metaInfo();
|
|
|
|
if (!metaInfo.isValid())
|
|
return originalExpression;
|
|
|
|
const auto propertyName = originalExpression.toUtf8();
|
|
|
|
if (metaInfo.hasProperty(propertyName))
|
|
return originalExpression;
|
|
|
|
if (hasId(originalExpression))
|
|
return originalExpression;
|
|
|
|
QString qualifiedExpression = rootNode.id() + "." + originalExpression;
|
|
|
|
if (rootNode.hasProperty(propertyName))
|
|
return qualifiedExpression;
|
|
|
|
const NodeMetaInfo rootMetaInfo = rootNode.metaInfo();
|
|
|
|
if (!rootMetaInfo.isValid())
|
|
return originalExpression;
|
|
|
|
if (rootMetaInfo.hasProperty(propertyName))
|
|
return qualifiedExpression;
|
|
|
|
return originalExpression;
|
|
}
|
|
|
|
} // namespace QmlDesigner
|