diff options
author | Marco Bubke <marco.bubke@qt.io> | 2020-07-27 18:14:33 +0200 |
---|---|---|
committer | Marco Bubke <marco.bubke@qt.io> | 2020-08-10 12:53:30 +0000 |
commit | e43c7fdb1de786a2d5b90d68830c5308319a7dcf (patch) | |
tree | baffab654aebea5569ae3ff408d18eb793765980 | |
parent | b5d59c75a7dee830483631cd9045d5338fc25c2d (diff) | |
download | qt-creator-e43c7fdb1de786a2d5b90d68830c5308319a7dcf.tar.gz |
QmlDesigner: Split messaging and process for puppets
This will make it easier to implement custom puppets. The new connection
manager will restucture the code and it add a mechanism to capture data
too.
Task-number: QDS-2529
Change-Id: I5d15c3303ef1c9a3e25ba197d350e0d561ce813a
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
46 files changed, 1736 insertions, 833 deletions
diff --git a/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h b/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h new file mode 100644 index 0000000000..c7950e278b --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QMetaType> + +#include "imagecontainer.h" + +namespace QmlDesigner { + +class CapturedDataCommand +{ +public: + struct NodeData + { + friend QDataStream &operator<<(QDataStream &out, const NodeData &data) + { + out << data.nodeId; + out << data.contentRect; + out << data.sceneTransform; + out << data.text; + + return out; + } + + friend QDataStream &operator>>(QDataStream &in, NodeData &data) + { + in >> data.nodeId; + in >> data.contentRect; + in >> data.sceneTransform; + in >> data.text; + + return in; + } + + qint32 nodeId = -1; + QRectF contentRect; + QTransform sceneTransform; + QString text; + }; + + struct StateData + { + friend QDataStream &operator<<(QDataStream &out, const StateData &data) + { + out << data.image; + out << data.nodeData; + + return out; + } + + friend QDataStream &operator>>(QDataStream &in, StateData &data) + { + in >> data.image; + in >> data.nodeData; + + return in; + } + + ImageContainer image; + QVector<NodeData> nodeData; + }; + + friend QDataStream &operator<<(QDataStream &out, const CapturedDataCommand &command) + { + out << command.stateData; + + return out; + } + + friend QDataStream &operator>>(QDataStream &in, CapturedDataCommand &command) + { + in >> command.stateData; + + return in; + } + +public: + QVector<StateData> stateData; +}; + +} // namespace QmlDesigner + +Q_DECLARE_METATYPE(QmlDesigner::CapturedDataCommand) diff --git a/share/qtcreator/qml/qmlpuppet/commands/commands.pri b/share/qtcreator/qml/qmlpuppet/commands/commands.pri index 03a44ae750..f343cbbd47 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/commands.pri +++ b/share/qtcreator/qml/qmlpuppet/commands/commands.pri @@ -1,69 +1,70 @@ -INCLUDEPATH += $$PWD/ +INCLUDEPATH += $$PWD -HEADERS += $$PWD/synchronizecommand.h -HEADERS += $$PWD/changepreviewimagesizecommand.h -HEADERS += $$PWD/changelanguagecommand.h -HEADERS += $$PWD//debugoutputcommand.h -HEADERS += $$PWD/endpuppetcommand.h -HEADERS += $$PWD/tokencommand.h -HEADERS += $$PWD/componentcompletedcommand.h -HEADERS += $$PWD/completecomponentcommand.h -HEADERS += $$PWD/statepreviewimagechangedcommand.h -HEADERS += $$PWD/childrenchangedcommand.h -HEADERS += $$PWD/changebindingscommand.h -HEADERS += $$PWD/changefileurlcommand.h -HEADERS += $$PWD/changeidscommand.h -HEADERS += $$PWD/changenodesourcecommand.h -HEADERS += $$PWD/changestatecommand.h -HEADERS += $$PWD/changevaluescommand.h -HEADERS += $$PWD/createscenecommand.h -HEADERS += $$PWD/clearscenecommand.h -HEADERS += $$PWD/createinstancescommand.h -HEADERS += $$PWD/informationchangedcommand.h -HEADERS += $$PWD/pixmapchangedcommand.h -HEADERS += $$PWD/removeinstancescommand.h -HEADERS += $$PWD/removepropertiescommand.h -HEADERS += $$PWD/reparentinstancescommand.h -HEADERS += $$PWD/valueschangedcommand.h -HEADERS += $$PWD/changeauxiliarycommand.h -HEADERS += $$PWD/removesharedmemorycommand.h -HEADERS += $$PWD/puppetalivecommand.h -HEADERS += $$PWD/changeselectioncommand.h -HEADERS += $$PWD/update3dviewstatecommand.h -HEADERS += $$PWD/puppettocreatorcommand.h -HEADERS += $$PWD/inputeventcommand.h -HEADERS += $$PWD/view3dactioncommand.h +HEADERS += $$PWD/synchronizecommand.h \ \ + $$PWD/captureddatacommand.h \ + $$PWD/changepreviewimagesizecommand.h \ + $$PWD/changelanguagecommand.h \ + $$PWD//debugoutputcommand.h \ + $$PWD/endpuppetcommand.h \ + $$PWD/tokencommand.h \ + $$PWD/componentcompletedcommand.h \ + $$PWD/completecomponentcommand.h \ + $$PWD/statepreviewimagechangedcommand.h \ + $$PWD/childrenchangedcommand.h \ + $$PWD/changebindingscommand.h \ + $$PWD/changefileurlcommand.h \ + $$PWD/changeidscommand.h \ + $$PWD/changenodesourcecommand.h \ + $$PWD/changestatecommand.h \ + $$PWD/changevaluescommand.h \ + $$PWD/createscenecommand.h \ + $$PWD/clearscenecommand.h \ + $$PWD/createinstancescommand.h \ + $$PWD/informationchangedcommand.h \ + $$PWD/pixmapchangedcommand.h \ + $$PWD/removeinstancescommand.h \ + $$PWD/removepropertiescommand.h \ + $$PWD/reparentinstancescommand.h \ + $$PWD/valueschangedcommand.h \ + $$PWD/changeauxiliarycommand.h \ + $$PWD/removesharedmemorycommand.h \ + $$PWD/puppetalivecommand.h \ + $$PWD/changeselectioncommand.h \ + $$PWD/update3dviewstatecommand.h \ + $$PWD/puppettocreatorcommand.h \ + $$PWD/inputeventcommand.h \ + $$PWD/view3dactioncommand.h -SOURCES += $$PWD/synchronizecommand.cpp -SOURCES += $$PWD/changepreviewimagesizecommand.cpp -SOURCES += $$PWD/changelanguagecommand.cpp -SOURCES += $$PWD/debugoutputcommand.cpp -SOURCES += $$PWD/endpuppetcommand.cpp -SOURCES += $$PWD/tokencommand.cpp -SOURCES += $$PWD/componentcompletedcommand.cpp -SOURCES += $$PWD/completecomponentcommand.cpp -SOURCES += $$PWD/statepreviewimagechangedcommand.cpp -SOURCES += $$PWD/childrenchangedcommand.cpp -SOURCES += $$PWD/changebindingscommand.cpp -SOURCES += $$PWD/changefileurlcommand.cpp -SOURCES += $$PWD/changeidscommand.cpp -SOURCES += $$PWD/changenodesourcecommand.cpp -SOURCES += $$PWD/changestatecommand.cpp -SOURCES += $$PWD/changevaluescommand.cpp -SOURCES += $$PWD/informationchangedcommand.cpp -SOURCES += $$PWD/removeinstancescommand.cpp -SOURCES += $$PWD/removepropertiescommand.cpp -SOURCES += $$PWD/reparentinstancescommand.cpp -SOURCES += $$PWD/valueschangedcommand.cpp -SOURCES += $$PWD/clearscenecommand.cpp -SOURCES += $$PWD/createinstancescommand.cpp -SOURCES += $$PWD/createscenecommand.cpp -SOURCES += $$PWD/pixmapchangedcommand.cpp -SOURCES += $$PWD/changeauxiliarycommand.cpp -SOURCES += $$PWD/removesharedmemorycommand.cpp -SOURCES += $$PWD/puppetalivecommand.cpp -SOURCES += $$PWD/changeselectioncommand.cpp -SOURCES += $$PWD/update3dviewstatecommand.cpp -SOURCES += $$PWD/puppettocreatorcommand.cpp -SOURCES += $$PWD/inputeventcommand.cpp -SOURCES += $$PWD/view3dactioncommand.cpp +SOURCES += $$PWD/synchronizecommand.cpp \ + $$PWD/changepreviewimagesizecommand.cpp \ + $$PWD/changelanguagecommand.cpp \ + $$PWD/debugoutputcommand.cpp \ + $$PWD/endpuppetcommand.cpp \ + $$PWD/tokencommand.cpp \ + $$PWD/componentcompletedcommand.cpp \ + $$PWD/completecomponentcommand.cpp \ + $$PWD/statepreviewimagechangedcommand.cpp \ + $$PWD/childrenchangedcommand.cpp \ + $$PWD/changebindingscommand.cpp \ + $$PWD/changefileurlcommand.cpp \ + $$PWD/changeidscommand.cpp \ + $$PWD/changenodesourcecommand.cpp \ + $$PWD/changestatecommand.cpp \ + $$PWD/changevaluescommand.cpp \ + $$PWD/informationchangedcommand.cpp \ + $$PWD/removeinstancescommand.cpp \ + $$PWD/removepropertiescommand.cpp \ + $$PWD/reparentinstancescommand.cpp \ + $$PWD/valueschangedcommand.cpp \ + $$PWD/clearscenecommand.cpp \ + $$PWD/createinstancescommand.cpp \ + $$PWD/createscenecommand.cpp \ + $$PWD/pixmapchangedcommand.cpp \ + $$PWD/changeauxiliarycommand.cpp \ + $$PWD/removesharedmemorycommand.cpp \ + $$PWD/puppetalivecommand.cpp \ + $$PWD/changeselectioncommand.cpp \ + $$PWD/update3dviewstatecommand.cpp \ + $$PWD/puppettocreatorcommand.cpp \ + $$PWD/inputeventcommand.cpp \ + $$PWD/view3dactioncommand.cpp diff --git a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp index 8a338df93d..24dad65090 100644 --- a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp +++ b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp @@ -35,6 +35,7 @@ #include "nodeinstanceserverinterface.h" +#include "captureddatacommand.h" #include "changeauxiliarycommand.h" #include "changebindingscommand.h" #include "changefileurlcommand.h" @@ -84,12 +85,11 @@ constexpr void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSock #endif NodeInstanceClientProxy::NodeInstanceClientProxy(QObject *parent) - : QObject(parent), - m_inputIoDevice(nullptr), - m_outputIoDevice(nullptr), - m_nodeInstanceServer(nullptr), - m_writeCommandCounter(0), - m_synchronizeId(-1) + : QObject(parent) + , m_inputIoDevice(nullptr) + , m_outputIoDevice(nullptr) + , m_writeCommandCounter(0) + , m_synchronizeId(-1) { connect(&m_puppetAliveTimer, &QTimer::timeout, this, &NodeInstanceClientProxy::sendPuppetAliveCommand); m_puppetAliveTimer.setInterval(2000); @@ -174,7 +174,8 @@ bool compareCommands(const QVariant &command, const QVariant &controlCommand) else if (command.userType() == debugOutputCommandType) return command.value<DebugOutputCommand>() == controlCommand.value<DebugOutputCommand>(); else if (command.userType() == changeSelectionCommandType) - return command.value<ChangeSelectionCommand>() == controlCommand.value<ChangeSelectionCommand>(); + return command.value<ChangeSelectionCommand>() + == controlCommand.value<ChangeSelectionCommand>(); } return false; @@ -267,6 +268,11 @@ void NodeInstanceClientProxy::handlePuppetToCreatorCommand(const PuppetToCreator writeCommand(QVariant::fromValue(command)); } +void NodeInstanceClientProxy::capturedData(const CapturedDataCommand &command) +{ + writeCommand(QVariant::fromValue(command)); +} + void NodeInstanceClientProxy::flush() { } @@ -365,12 +371,13 @@ void NodeInstanceClientProxy::sendPuppetAliveCommand() NodeInstanceServerInterface *NodeInstanceClientProxy::nodeInstanceServer() const { - return m_nodeInstanceServer; + return m_nodeInstanceServer.get(); } -void NodeInstanceClientProxy::setNodeInstanceServer(NodeInstanceServerInterface *nodeInstanceServer) +void NodeInstanceClientProxy::setNodeInstanceServer( + std::unique_ptr<NodeInstanceServerInterface> nodeInstanceServer) { - m_nodeInstanceServer = nodeInstanceServer; + m_nodeInstanceServer = std::move(nodeInstanceServer); } void NodeInstanceClientProxy::createInstances(const CreateInstancesCommand &command) diff --git a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h index e6f4b58df4..fd681b6990 100644 --- a/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h +++ b/share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h @@ -69,7 +69,7 @@ class NodeInstanceClientProxy : public QObject, public NodeInstanceClientInterfa Q_OBJECT public: - NodeInstanceClientProxy(QObject *parent = nullptr); + NodeInstanceClientProxy(QObject *parent); void informationChanged(const InformationChangedCommand &command) override; void valuesChanged(const ValuesChangedCommand &command) override; @@ -83,6 +83,7 @@ public: void puppetAlive(const PuppetAliveCommand &command); void selectionChanged(const ChangeSelectionCommand &command) override; void handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) override; + void capturedData(const CapturedDataCommand &capturedData) override; void flush() override; void synchronizeWithClientProcess() override; @@ -94,7 +95,7 @@ protected: void writeCommand(const QVariant &command); void dispatchCommand(const QVariant &command); NodeInstanceServerInterface *nodeInstanceServer() const; - void setNodeInstanceServer(NodeInstanceServerInterface *nodeInstanceServer); + void setNodeInstanceServer(std::unique_ptr<NodeInstanceServerInterface> nodeInstanceServer); void createInstances(const CreateInstancesCommand &command); void changeFileUrl(const ChangeFileUrlCommand &command); @@ -130,7 +131,7 @@ private: QTimer m_puppetAliveTimer; QIODevice *m_inputIoDevice; QIODevice *m_outputIoDevice; - NodeInstanceServerInterface *m_nodeInstanceServer; + std::unique_ptr<NodeInstanceServerInterface> m_nodeInstanceServer; quint32 m_writeCommandCounter; int m_synchronizeId; }; diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceclientinterface.h b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceclientinterface.h index 9498cfb1f7..d60e0d7ff0 100644 --- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceclientinterface.h +++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceclientinterface.h @@ -42,6 +42,7 @@ class DebugOutputCommand; class PuppetAliveCommand; class ChangeSelectionCommand; class PuppetToCreatorCommand; +class CapturedDataCommand; class NodeInstanceClientInterface { @@ -57,6 +58,7 @@ public: virtual void debugOutput(const DebugOutputCommand &command) = 0; virtual void selectionChanged(const ChangeSelectionCommand &command) = 0; virtual void handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) = 0; + virtual void capturedData(const CapturedDataCommand &command) = 0; virtual void flush() {} virtual void synchronizeWithClientProcess() {} diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp index 62669d3158..e3a05376e2 100644 --- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp +++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp @@ -27,16 +27,17 @@ #include <qmetatype.h> #include "addimportcontainer.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 "changepreviewimagesizecommand.h" #include "childrenchangedcommand.h" #include "clearscenecommand.h" #include "completecomponentcommand.h" @@ -219,6 +220,9 @@ void NodeInstanceServerInterface::registerCommands() qRegisterMetaType<ChangePreviewImageSizeCommand>("ChangePreviewImageSizeCommand"); qRegisterMetaTypeStreamOperators<ChangePreviewImageSizeCommand>("ChangePreviewImageSizeCommand"); + + qRegisterMetaType<CapturedDataCommand>("CapturedDataCommand"); + qRegisterMetaTypeStreamOperators<CapturedDataCommand>("CapturedDataCommand"); } } diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h index 10c2d1fdbb..39eb2618d6 100644 --- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h +++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h @@ -60,11 +60,6 @@ class NodeInstanceServerInterface : public QObject { Q_OBJECT public: - enum RunModus { - NormalModus, - TestModus // No preview images and synchronized - }; - explicit NodeInstanceServerInterface(QObject *parent = nullptr); virtual void createInstances(const CreateInstancesCommand &command) = 0; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri index 4fe46e29d8..ce9bb2376e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri @@ -5,52 +5,54 @@ versionAtLeast(QT_VERSION, 5.15.0):qtHaveModule(quick3d) { DEFINES *= QUICK3D_MODULE } -HEADERS += $$PWD/qt5nodeinstanceserver.h -HEADERS += $$PWD/qt5testnodeinstanceserver.h -HEADERS += $$PWD/qt5informationnodeinstanceserver.h -HEADERS += $$PWD/qt5rendernodeinstanceserver.h -HEADERS += $$PWD/qt5previewnodeinstanceserver.h -HEADERS += $$PWD/qt5nodeinstanceclientproxy.h -HEADERS += $$PWD/quickitemnodeinstance.h -HEADERS += $$PWD/behaviornodeinstance.h -HEADERS += $$PWD/dummycontextobject.h -HEADERS += $$PWD/childrenchangeeventfilter.h -HEADERS += $$PWD/componentnodeinstance.h -HEADERS += $$PWD/dummynodeinstance.h -HEADERS += $$PWD/nodeinstanceserver.h -HEADERS += $$PWD/nodeinstancesignalspy.h -HEADERS += $$PWD/objectnodeinstance.h -HEADERS += $$PWD/qmlpropertychangesnodeinstance.h -HEADERS += $$PWD/qmlstatenodeinstance.h -HEADERS += $$PWD/qmltransitionnodeinstance.h -HEADERS += $$PWD/servernodeinstance.h -HEADERS += $$PWD/anchorchangesnodeinstance.h -HEADERS += $$PWD/positionernodeinstance.h -HEADERS += $$PWD/layoutnodeinstance.h -HEADERS += $$PWD/qt3dpresentationnodeinstance.h -HEADERS += $$PWD/quick3dnodeinstance.h +HEADERS += $$PWD/qt5nodeinstanceserver.h \ + $$PWD/qt5capturenodeinstanceserver.h \ + $$PWD/qt5testnodeinstanceserver.h \ + $$PWD/qt5informationnodeinstanceserver.h \ + $$PWD/qt5rendernodeinstanceserver.h \ + $$PWD/qt5previewnodeinstanceserver.h \ + $$PWD/qt5nodeinstanceclientproxy.h \ + $$PWD/quickitemnodeinstance.h \ + $$PWD/behaviornodeinstance.h \ + $$PWD/dummycontextobject.h \ + $$PWD/childrenchangeeventfilter.h \ + $$PWD/componentnodeinstance.h \ + $$PWD/dummynodeinstance.h \ + $$PWD/nodeinstanceserver.h \ + $$PWD/nodeinstancesignalspy.h \ + $$PWD/objectnodeinstance.h \ + $$PWD/qmlpropertychangesnodeinstance.h \ + $$PWD/qmlstatenodeinstance.h \ + $$PWD/qmltransitionnodeinstance.h \ + $$PWD/servernodeinstance.h \ + $$PWD/anchorchangesnodeinstance.h \ + $$PWD/positionernodeinstance.h \ + $$PWD/layoutnodeinstance.h \ + $$PWD/qt3dpresentationnodeinstance.h \ + $$PWD/quick3dnodeinstance.h -SOURCES += $$PWD/qt5nodeinstanceserver.cpp -SOURCES += $$PWD/qt5testnodeinstanceserver.cpp -SOURCES += $$PWD/qt5informationnodeinstanceserver.cpp -SOURCES += $$PWD/qt5rendernodeinstanceserver.cpp -SOURCES += $$PWD/qt5previewnodeinstanceserver.cpp -SOURCES += $$PWD/qt5nodeinstanceclientproxy.cpp -SOURCES += $$PWD/quickitemnodeinstance.cpp -SOURCES += $$PWD/behaviornodeinstance.cpp -SOURCES += $$PWD/dummycontextobject.cpp -SOURCES += $$PWD/childrenchangeeventfilter.cpp -SOURCES += $$PWD/componentnodeinstance.cpp -SOURCES += $$PWD/dummynodeinstance.cpp -SOURCES += $$PWD/nodeinstanceserver.cpp -SOURCES += $$PWD/nodeinstancesignalspy.cpp -SOURCES += $$PWD/objectnodeinstance.cpp -SOURCES += $$PWD/qmlpropertychangesnodeinstance.cpp -SOURCES += $$PWD/qmlstatenodeinstance.cpp -SOURCES += $$PWD/qmltransitionnodeinstance.cpp -SOURCES += $$PWD/servernodeinstance.cpp -SOURCES += $$PWD/anchorchangesnodeinstance.cpp -SOURCES += $$PWD/positionernodeinstance.cpp -SOURCES += $$PWD/layoutnodeinstance.cpp -SOURCES += $$PWD/qt3dpresentationnodeinstance.cpp -SOURCES += $$PWD/quick3dnodeinstance.cpp +SOURCES += $$PWD/qt5nodeinstanceserver.cpp \ + $$PWD/qt5capturenodeinstanceserver.cpp \ + $$PWD/qt5testnodeinstanceserver.cpp \ + $$PWD/qt5informationnodeinstanceserver.cpp \ + $$PWD/qt5rendernodeinstanceserver.cpp \ + $$PWD/qt5previewnodeinstanceserver.cpp \ + $$PWD/qt5nodeinstanceclientproxy.cpp \ + $$PWD/quickitemnodeinstance.cpp \ + $$PWD/behaviornodeinstance.cpp \ + $$PWD/dummycontextobject.cpp \ + $$PWD/childrenchangeeventfilter.cpp \ + $$PWD/componentnodeinstance.cpp \ + $$PWD/dummynodeinstance.cpp \ + $$PWD/nodeinstanceserver.cpp \ + $$PWD/nodeinstancesignalspy.cpp \ + $$PWD/objectnodeinstance.cpp \ + $$PWD/qmlpropertychangesnodeinstance.cpp \ + $$PWD/qmlstatenodeinstance.cpp \ + $$PWD/qmltransitionnodeinstance.cpp \ + $$PWD/servernodeinstance.cpp \ + $$PWD/anchorchangesnodeinstance.cpp \ + $$PWD/positionernodeinstance.cpp \ + $$PWD/layoutnodeinstance.cpp \ + $$PWD/qt3dpresentationnodeinstance.cpp \ + $$PWD/quick3dnodeinstance.cpp diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index 9b0b59d58b..1abfc634bd 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -179,6 +179,8 @@ NodeInstanceServer::NodeInstanceServer(NodeInstanceClientInterface *nodeInstance m_childrenChangeEventFilter(new Internal::ChildrenChangeEventFilter(this)), m_nodeInstanceClient(nodeInstanceClient) { + m_idInstances.reserve(1000); + qmlRegisterType<DummyContextObject>("QmlDesigner", 1, 0, "DummyContextObject"); connect(m_childrenChangeEventFilter.data(), &Internal::ChildrenChangeEventFilter::childrenChanged, this, &NodeInstanceServer::emitParentChanged); @@ -226,8 +228,8 @@ ServerNodeInstance NodeInstanceServer::instanceForId(qint32 id) const if (id < 0) return ServerNodeInstance(); - Q_ASSERT(m_idInstanceHash.contains(id)); - return m_idInstanceHash.value(id); + Q_ASSERT(m_idInstances.size() > id); + return m_idInstances[id]; } bool NodeInstanceServer::hasInstanceForId(qint32 id) const @@ -235,7 +237,7 @@ bool NodeInstanceServer::hasInstanceForId(qint32 id) const if (id < 0) return false; - return m_idInstanceHash.contains(id) && m_idInstanceHash.value(id).isValid(); + return m_idInstances.size() > id && m_idInstances[id].isValid(); } ServerNodeInstance NodeInstanceServer::instanceForObject(QObject *object) const @@ -790,7 +792,7 @@ void NodeInstanceServer::removeAllInstanceRelationships() instance.makeInvalid(); } - m_idInstanceHash.clear(); + m_idInstances.clear(); m_objectInstanceHash.clear(); } @@ -1243,10 +1245,11 @@ void NodeInstanceServer::notifyPropertyChange(qint32 instanceid, const PropertyN void NodeInstanceServer::insertInstanceRelationship(const ServerNodeInstance &instance) { Q_ASSERT(instance.isValid()); - Q_ASSERT(!m_idInstanceHash.contains(instance.instanceId())); Q_ASSERT(!m_objectInstanceHash.contains(instance.internalObject())); m_objectInstanceHash.insert(instance.internalObject(), instance); - m_idInstanceHash.insert(instance.instanceId(), instance); + if (instance.instanceId() >= m_idInstances.size()) + m_idInstances.resize(instance.instanceId() + 1); + m_idInstances[instance.instanceId()] = instance; } void NodeInstanceServer::removeInstanceRelationsip(qint32 instanceId) @@ -1255,7 +1258,7 @@ void NodeInstanceServer::removeInstanceRelationsip(qint32 instanceId) ServerNodeInstance instance = instanceForId(instanceId); if (instance.isValid()) instance.setId(QString()); - m_idInstanceHash.remove(instanceId); + m_idInstances[instanceId] = ServerNodeInstance{}; m_objectInstanceHash.remove(instance.internalObject()); instance.makeInvalid(); } @@ -1383,8 +1386,8 @@ void NodeInstanceServer::removeInstanceRelationsipForDeletedObject(QObject *obje ServerNodeInstance instance = instanceForObject(object); m_objectInstanceHash.remove(object); - if (m_idInstanceHash.contains(instance.instanceId())) - m_idInstanceHash.remove(instance.instanceId()); + if (instance.instanceId() >= 0 && m_idInstances.size() > instance.instanceId()) + m_idInstances[instance.instanceId()] = ServerNodeInstance{}; } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index 87eb5a1b7e..2c628f1411 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -160,6 +160,8 @@ public: ServerNodeInstance instanceForObject(QObject *object) const; bool hasInstanceForObject(QObject *object) const; + const QVector<ServerNodeInstance> &nodeInstances() const { return m_idInstances; } + virtual QQmlEngine *engine() const = 0; QQmlContext *context() const; @@ -272,7 +274,7 @@ private: void setupOnlyWorkingImports(const QStringList &workingImportStatementList); ServerNodeInstance m_rootNodeInstance; ServerNodeInstance m_activeStateInstance; - QHash<qint32, ServerNodeInstance> m_idInstanceHash; + QVector<ServerNodeInstance> m_idInstances; QHash<QObject*, ServerNodeInstance> m_objectInstanceHash; QMultiHash<QString, ObjectPropertyPair> m_fileSystemWatcherHash; QList<QPair<QString, QPointer<QObject> > > m_dummyObjectList; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.cpp new file mode 100644 index 0000000000..aea75a76c7 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "qt5capturenodeinstanceserver.h" +#include "servernodeinstance.h" + +#include <captureddatacommand.h> +#include <createscenecommand.h> +#include <nodeinstanceclientinterface.h> + +#include <QImage> +#include <QQuickView> + +namespace QmlDesigner { + +namespace { + +QImage renderPreviewImage(ServerNodeInstance rootNodeInstance) +{ + rootNodeInstance.updateDirtyNodeRecursive(); + + QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize(); + + QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize); + + return previewImage; +} + +CapturedDataCommand::StateData collectStateData(ServerNodeInstance rootNodeInstance, + const QVector<ServerNodeInstance> &nodeInstances, + qint32 stateInstanceId) +{ + CapturedDataCommand::StateData stateData; + stateData.image = ImageContainer(stateInstanceId, + QmlDesigner::renderPreviewImage(rootNodeInstance), + stateInstanceId); + + for (const ServerNodeInstance &instance : nodeInstances) { + auto textProperty = instance.property("text"); + if (!textProperty.isNull() && instance.holdsGraphical()) { + CapturedDataCommand::NodeData nodeData; + nodeData.nodeId = instance.instanceId(); + nodeData.contentRect = instance.contentItemBoundingRect(); + nodeData.sceneTransform = instance.sceneTransform(); + nodeData.text = textProperty.toString(); + stateData.nodeData.push_back(std::move(nodeData)); + } + } + + return stateData; +} +} // namespace + +void Qt5CaptureNodeInstanceServer::collectItemChangesAndSendChangeCommands() +{ + static bool inFunction = false; + + if (!rootNodeInstance().holdsGraphical()) + return; + + if (!inFunction) { + inFunction = true; + + DesignerSupport::polishItems(quickView()); + + QVector<CapturedDataCommand::StateData> stateDatas; + stateDatas.push_back(collectStateData(rootNodeInstance(), nodeInstances(), 0)); + + for (ServerNodeInstance stateInstance : rootNodeInstance().stateInstances()) { + stateInstance.activateState(); + stateDatas.push_back( + collectStateData(rootNodeInstance(), nodeInstances(), stateInstance.instanceId())); + stateInstance.deactivateState(); + } + + nodeInstanceClient()->capturedData(CapturedDataCommand{stateDatas}); + + slowDownRenderTimer(); + inFunction = false; + } +} + +} // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.h new file mode 100644 index 0000000000..cd0208e563 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <qt5previewnodeinstanceserver.h> + +namespace QmlDesigner { + +class Qt5CaptureNodeInstanceServer : public Qt5PreviewNodeInstanceServer +{ +public: + explicit Qt5CaptureNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) + : Qt5PreviewNodeInstanceServer(nodeInstanceClient) + {} + +protected: + void collectItemChangesAndSendChangeCommands() override; + +private: +}; + +} // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index 449e4ff188..1cdfc91074 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -27,6 +27,7 @@ #include <QCoreApplication> +#include "qt5capturenodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" #include "qt5previewnodeinstanceserver.h" #include "qt5rendernodeinstanceserver.h" @@ -37,7 +38,7 @@ #if defined(Q_OS_UNIX) #include <unistd.h> #elif defined(Q_OS_WIN) -#include <windows.h> +#include <Windows.h> #endif namespace QmlDesigner { @@ -57,18 +58,21 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : DesignerSupport::activateDesignerWindowManager(); if (QCoreApplication::arguments().at(1) == QLatin1String("--readcapturedstream")) { qputenv("DESIGNER_DONT_USE_SHARED_MEMORY", "1"); - setNodeInstanceServer(new Qt5TestNodeInstanceServer(this)); + setNodeInstanceServer(std::make_unique<Qt5TestNodeInstanceServer>(this)); initializeCapturedStream(QCoreApplication::arguments().at(2)); readDataStream(); QCoreApplication::exit(); } else if (QCoreApplication::arguments().at(2) == QLatin1String("previewmode")) { - setNodeInstanceServer(new Qt5PreviewNodeInstanceServer(this)); + setNodeInstanceServer(std::make_unique<Qt5PreviewNodeInstanceServer>(this)); initializeSocket(); } else if (QCoreApplication::arguments().at(2) == QLatin1String("editormode")) { - setNodeInstanceServer(new Qt5InformationNodeInstanceServer(this)); + setNodeInstanceServer(std::make_unique<Qt5InformationNodeInstanceServer>(this)); initializeSocket(); } else if (QCoreApplication::arguments().at(2) == QLatin1String("rendermode")) { - setNodeInstanceServer(new Qt5RenderNodeInstanceServer(this)); + setNodeInstanceServer(std::make_unique<Qt5RenderNodeInstanceServer>(this)); + initializeSocket(); + } else if (QCoreApplication::arguments().at(2) == QLatin1String("capturemode")) { + setNodeInstanceServer(std::make_unique<Qt5CaptureNodeInstanceServer>(this)); initializeSocket(); } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp index 50db9b9971..452f2ddd2f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp @@ -84,7 +84,8 @@ void Qt5PreviewNodeInstanceServer::collectItemChangesAndSendChangeCommands() instance.deactivateState(); } - nodeInstanceClient()->statePreviewImagesChanged(StatePreviewImageChangedCommand(imageContainerVector)); + nodeInstanceClient()->statePreviewImagesChanged( + StatePreviewImageChangedCommand(imageContainerVector)); slowDownRenderTimer(); inFunction = false; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h index ceb3b1e5b1..0881b738d9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h @@ -71,6 +71,7 @@ class ServerNodeInstance friend class Qt5InformationNodeInstanceServer; friend class Qt5NodeInstanceServer; friend class Qt5PreviewNodeInstanceServer; + friend class Qt5CaptureNodeInstanceServer; friend class Qt5TestNodeInstanceServer; friend class QHash<qint32, ServerNodeInstance>; friend uint qHash(const ServerNodeInstance &instance); @@ -169,6 +170,8 @@ public: static bool isSubclassOf(QObject *object, const QByteArray &superTypeName); void setModifiedFlag(bool b); + void updateDirtyNodeRecursive(); + bool holdsGraphical() const; private: // functions ServerNodeInstance(const QSharedPointer<Internal::ObjectNodeInstance> &abstractInstance); @@ -195,7 +198,6 @@ private: // functions void setDeleteHeldInstance(bool deleteInstance); void reparent(const ServerNodeInstance &oldParentInstance, const PropertyName &oldParentProperty, const ServerNodeInstance &newParentInstance, const PropertyName &newParentProperty); - void setId(const QString &id); static QSharedPointer<Internal::ObjectNodeInstance> createInstance(QObject *objectToBeWrapped); @@ -204,10 +206,6 @@ private: // functions void setNodeSource(const QString &source); - bool holdsGraphical() const; - - void updateDirtyNodeRecursive(); - QObject *internalObject() const; // should be not used outside of the nodeinstances!!!! private: // variables diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index f571497016..ecec481f90 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -505,6 +505,12 @@ extend_qtc_plugin(QmlDesigner puppetbuildprogressdialog.cpp puppetbuildprogressdialog.h puppetbuildprogressdialog.ui puppetcreator.cpp puppetcreator.h puppetdialog.cpp puppetdialog.h puppetdialog.ui + connectionmanagerinterface.cpp connectionmanagerinterface.h + baseconnectionmanager.cpp baseconnectionmanager.h + connectionmanager.cpp connectionmanager.h + capturingconnectionmanager.cpp capturingconnectionmanager.h + interactiveconnectionmanager.cpp interactiveconnectionmanager.h + qprocessuniqueptr.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 6a5edb18a3..1fc817cd01 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -86,7 +86,7 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, if (!isCancelled()) { // Wait for icon generation processes to finish - if (m_qmlPuppetProcesses.isEmpty()) { + if (m_qmlPuppetProcesses.empty()) { finalizeQuick3DImport(); } else { m_qmlPuppetCount = m_qmlPuppetProcesses.size(); @@ -186,10 +186,12 @@ void ItemLibraryAssetImporter::processFinished(int exitCode, QProcess::ExitStatu auto process = qobject_cast<QProcess *>(sender()); if (process) { - m_qmlPuppetProcesses.remove(process); - process->deleteLater(); + m_qmlPuppetProcesses.erase( + std::remove_if(m_qmlPuppetProcesses.begin(), + m_qmlPuppetProcesses.end(), + [&](const auto &entry) { return entry.get() == process; })); const QString progressTitle = tr("Generating icons."); - if (m_qmlPuppetProcesses.isEmpty()) { + if (m_qmlPuppetProcesses.empty()) { notifyProgress(100, progressTitle); finalizeQuick3DImport(); } else { @@ -215,7 +217,6 @@ void ItemLibraryAssetImporter::reset() m_tempDir = new QTemporaryDir; m_importFiles.clear(); m_overwrittenImports.clear(); - qDeleteAll(m_qmlPuppetProcesses); m_qmlPuppetProcesses.clear(); m_qmlPuppetCount = 0; #endif @@ -498,16 +499,21 @@ bool ItemLibraryAssetImporter::generateComponentIcon(int size, const QString &ic puppetCreator.createQml2PuppetExecutableIfMissing(); QStringList puppetArgs; puppetArgs << "--rendericon" << QString::number(size) << iconFile << iconSource; - QProcess *process = puppetCreator.createPuppetProcess( - "custom", {}, this, "", SLOT(processFinished(int, QProcess::ExitStatus)), puppetArgs); + QProcessUniquePointer process = puppetCreator.createPuppetProcess( + "custom", + {}, + this, + std::function<void()>(), + [&](int exitCode, QProcess::ExitStatus exitStatus) { + processFinished(exitCode, exitStatus); + }, + puppetArgs); if (process->waitForStarted(5000)) { - connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), - process, &QProcess::deleteLater); - m_qmlPuppetProcesses << process; + m_qmlPuppetProcesses.push_back(std::move(process)); return true; } else { - delete process; + process.reset(); } } return false; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 4bdccad6af..5921144151 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -24,14 +24,16 @@ ****************************************************************************/ #pragma once +#include "import.h" + +#include <qprocessuniqueptr.h> + #include <QSet> -#include <QtCore/qobject.h> -#include <QtCore/qstringlist.h> #include <QtCore/qhash.h> #include <QtCore/qjsonobject.h> +#include <QtCore/qobject.h> #include <QtCore/qprocess.h> - -#include "import.h" +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE class QSSGAssetImportManager; @@ -99,7 +101,7 @@ private: bool m_cancelled = false; QString m_importPath; QTemporaryDir *m_tempDir = nullptr; - QSet<QProcess *> m_qmlPuppetProcesses; + std::vector<QProcessUniquePointer> m_qmlPuppetProcesses; int m_qmlPuppetCount = 0; }; } // QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 63b73cbdff..78e56d46cd 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -62,6 +62,7 @@ class RemovePropertiesCommand; class CompleteComponentCommand; class InformationContainer; class TokenCommand; +class ConnectionManagerInterface; class QMLDESIGNERCORE_EXPORT NodeInstanceView : public AbstractView, public NodeInstanceClientInterface { @@ -72,7 +73,7 @@ class QMLDESIGNERCORE_EXPORT NodeInstanceView : public AbstractView, public Node public: using Pointer = QWeakPointer<NodeInstanceView>; - explicit NodeInstanceView(QObject *parent = nullptr, NodeInstanceServerInterface::RunModus runModus = NodeInstanceServerInterface::NormalModus); + explicit NodeInstanceView(ConnectionManagerInterface &connectionManager); ~NodeInstanceView() override; void modelAttached(Model *model) override; @@ -94,7 +95,7 @@ public: void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override; void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; void nodeSourceChanged(const ModelNode &modelNode, const QString &newNodeSource) override; - + void capturedData(const CapturedDataCommand &capturedData) override; void currentStateChanged(const ModelNode &node) override; QList<NodeInstance> instances() const; @@ -142,6 +143,7 @@ protected: void timerEvent(QTimerEvent *event) override; private: // functions + std::unique_ptr<NodeInstanceServerProxy> createNodeInstanceServerProxy(); void activateState(const NodeInstance &instance); void activateBaseState(); @@ -161,9 +163,8 @@ private: // functions void setStateInstance(const NodeInstance &stateInstance); void clearStateInstance(); - NodeInstanceServerInterface *nodeInstanceServer() const; - QMultiHash<ModelNode, InformationName> informationChanged(const QVector<InformationContainer> &containerVector); - + QMultiHash<ModelNode, InformationName> informationChanged( + const QVector<InformationContainer> &containerVector); CreateSceneCommand createCreateSceneCommand(); ClearSceneCommand createClearSceneCommand() const; @@ -196,16 +197,15 @@ private: // functions // puppet to creator command handlers void handlePuppetKeyPress(int key, Qt::KeyboardModifiers modifiers); +private: NodeInstance m_rootNodeInstance; NodeInstance m_activeStateInstance; - QHash<ModelNode, NodeInstance> m_nodeInstanceHash; QHash<ModelNode, QImage> m_statePreviewImage; - - QPointer<NodeInstanceServerProxy> m_nodeInstanceServer; + ConnectionManagerInterface &m_connectionManager; + std::unique_ptr<NodeInstanceServerProxy> m_nodeInstanceServer; QImage m_baseStatePreviewImage; QElapsedTimer m_lastCrashTime; - NodeInstanceServerInterface::RunModus m_runModus; ProjectExplorer::Target *m_currentTarget = nullptr; int m_restartProcessTimerId; RewriterTransaction m_puppetTransaction; diff --git a/src/plugins/qmldesigner/designercore/include/viewmanager.h b/src/plugins/qmldesigner/designercore/include/viewmanager.h index 3862d54187..cc6f5d15d0 100644 --- a/src/plugins/qmldesigner/designercore/include/viewmanager.h +++ b/src/plugins/qmldesigner/designercore/include/viewmanager.h @@ -122,7 +122,7 @@ private: // functions QList<QPointer<AbstractView>> standardViews() const; private: // variables - ViewManagerData *d; + std::unique_ptr<ViewManagerData> d; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp new file mode 100644 index 0000000000..0d4540b37f --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "baseconnectionmanager.h" +#include "endpuppetcommand.h" +#include "nodeinstanceserverproxy.h" +#include "nodeinstanceview.h" + +#include <QLocalSocket> + +namespace QmlDesigner { + +void BaseConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &, + ProjectExplorer::Target *) +{ + m_nodeInstanceServerProxy = nodeInstanceServerProxy; + m_isActive = true; +} + +void BaseConnectionManager::shutDown() +{ + m_isActive = false; + + writeCommand(QVariant::fromValue(EndPuppetCommand())); + + m_nodeInstanceServerProxy = nullptr; +} + +bool BaseConnectionManager::isActive() const +{ + return m_isActive; +} + +void BaseConnectionManager::showCannotConnectToPuppetWarningAndSwitchToEditMode() {} + +void BaseConnectionManager::processFinished() +{ + processFinished(-1, QProcess::CrashExit); +} + +void BaseConnectionManager::writeCommandToIODevice(const QVariant &command, + QIODevice *ioDevice, + unsigned int commandCounter) +{ + if (ioDevice) { + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_8); + out << quint32(0); + out << quint32(commandCounter); + out << command; + out.device()->seek(0); + out << quint32(static_cast<unsigned long long>(block.size()) - sizeof(quint32)); + + ioDevice->write(block); + } +} + +void BaseConnectionManager::dispatchCommand(const QVariant &command, Connection &) +{ + if (!isActive()) + return; + + m_nodeInstanceServerProxy->dispatchCommand(command); +} + +void BaseConnectionManager::readDataStream(Connection &connection) +{ + QList<QVariant> commandList; + + while (!connection.socket->atEnd()) { + if (connection.socket->bytesAvailable() < int(sizeof(quint32))) + break; + + QDataStream in(connection.socket.get()); + in.setVersion(QDataStream::Qt_4_8); + + if (connection.blockSize == 0) + in >> connection.blockSize; + + if (connection.socket->bytesAvailable() < connection.blockSize) + break; + + quint32 commandCounter = 0; + in >> commandCounter; + bool commandLost = !((connection.lastReadCommandCounter == 0 && commandCounter == 0) + || (connection.lastReadCommandCounter + 1 == commandCounter)); + if (commandLost) + qDebug() << "server command lost: " << connection.lastReadCommandCounter << commandCounter; + connection.lastReadCommandCounter = commandCounter; + + QVariant command; + in >> command; + connection.blockSize = 0; + + commandList.append(command); + } + + for (const QVariant &command : commandList) + dispatchCommand(command, connection); +} +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h new file mode 100644 index 0000000000..83a41a2bd8 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "connectionmanagerinterface.h" + +#include <QProcess> + +QT_BEGIN_NAMESPACE +class QLocalSocket; +QT_END_NAMESPACE + +namespace ProjectExplorer { +class Target; +} + +namespace QmlDesigner { + +class AbstractView; +class NodeInstanceServerProxy; + +class QMLDESIGNERCORE_EXPORT BaseConnectionManager : public QObject, public ConnectionManagerInterface +{ + Q_OBJECT + +public: + BaseConnectionManager() = default; + + void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) override; + void shutDown() override; + + bool isActive() const; + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + virtual void showCannotConnectToPuppetWarningAndSwitchToEditMode(); + using ConnectionManagerInterface::processFinished; + void processFinished(); + void writeCommandToIODevice(const QVariant &command, + QIODevice *ioDevice, + unsigned int commandCounter); + void readDataStream(Connection &connection); + + NodeInstanceServerProxy *nodeInstanceServerProxy() const { return m_nodeInstanceServerProxy; } + +private: + NodeInstanceServerProxy *m_nodeInstanceServerProxy{}; + bool m_isActive = false; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp new file mode 100644 index 0000000000..b6e81f1ffe --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "capturingconnectionmanager.h" + +#include <coreplugin/messagebox.h> + +namespace QmlDesigner { + +void CapturingConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) +{ + InteractiveConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target); + + int indexOfCapturePuppetStream = QCoreApplication::arguments().indexOf( + "-capture-puppet-stream"); + if (indexOfCapturePuppetStream > 0) { + m_captureFileForTest.setFileName( + QCoreApplication::arguments().at(indexOfCapturePuppetStream + 1)); + bool isOpen = m_captureFileForTest.open(QIODevice::WriteOnly); + qDebug() << "file is open: " << isOpen; + } +} + +void CapturingConnectionManager::processFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (m_captureFileForTest.isOpen()) { + m_captureFileForTest.close(); + Core::AsynchronousMessageBox::warning( + tr("QML Emulation Layer (QML Puppet) Crashed"), + tr("You are recording a puppet stream and the emulations layer crashed. " + "It is recommended to reopen the Qt Quick Designer and start again.")); + } + + InteractiveConnectionManager::processFinished(exitCode, exitStatus); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h new file mode 100644 index 0000000000..de63da87fc --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <interactiveconnectionmanager.h> + +namespace QmlDesigner { + +class CapturingConnectionManager : public InteractiveConnectionManager +{ +public: + void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) override; + + void processFinished(int exitCode, QProcess::ExitStatus exitStatus) override; + +private: + QFile m_captureFileForTest; +}; + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp new file mode 100644 index 0000000000..edf7f20b9c --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "connectionmanager.h" +#include "endpuppetcommand.h" +#include "nodeinstanceserverproxy.h" +#include "nodeinstanceview.h" +#include "puppetcreator.h" + +#ifndef QMLDESIGNER_TEST +#include <qmldesignerplugin.h> +#endif + +#include <projectexplorer/target.h> + +#include <QLocalServer> +#include <QLocalSocket> +#include <QUuid> + +namespace QmlDesigner { + +ConnectionManager::ConnectionManager() = default; + +ConnectionManager::~ConnectionManager() = default; + +void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) +{ + BaseConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target); + + m_localServer = std::make_unique<QLocalServer>(); + QString socketToken(QUuid::createUuid().toString()); + m_localServer->listen(socketToken); + m_localServer->setMaxPendingConnections(3); + + NodeInstanceView *nodeInstanceView = nodeInstanceServerProxy->nodeInstanceView(); + PuppetCreator puppetCreator(target, nodeInstanceView->model()); + puppetCreator.setQrcMappingString(qrcMappingString); + + puppetCreator.createQml2PuppetExecutableIfMissing(); + + for (Connection &connection : m_connections) { + connection.qmlPuppetProcess = puppetCreator.createPuppetProcess( + connection.mode, + socketToken, + nodeInstanceView, + [&] { printProcessOutput(connection.qmlPuppetProcess.get(), connection.name); }, + [&](int exitCode, QProcess::ExitStatus exitStatus) { + processFinished(exitCode, exitStatus); + }); + + const int second = 1000; + int waitConstant = 8 * second; + + if (!connection.qmlPuppetProcess->waitForStarted(waitConstant)) { + closeSocketsAndKillProcesses(); + showCannotConnectToPuppetWarningAndSwitchToEditMode(); + return; + } + + waitConstant /= 2; + + bool connectedToPuppet = true; + if (!m_localServer->hasPendingConnections()) + connectedToPuppet = m_localServer->waitForNewConnection(waitConstant); + + if (connectedToPuppet) { + connection.socket.reset(m_localServer->nextPendingConnection()); + QObject::connect(connection.socket.get(), &QIODevice::readyRead, [&] { + readDataStream(connection); + }); + } else { + closeSocketsAndKillProcesses(); + showCannotConnectToPuppetWarningAndSwitchToEditMode(); + return; + } + } + + m_localServer->close(); + + connect(this, + &ConnectionManager::processCrashed, + nodeInstanceServerProxy, + &NodeInstanceServerProxy::processCrashed); +} + +void ConnectionManager::shutDown() +{ + BaseConnectionManager::shutDown(); + + closeSocketsAndKillProcesses(); + + m_localServer.reset(); + + for (Connection &connection : m_connections) + connection.clear(); +} + +void ConnectionManager::writeCommand(const QVariant &command) +{ + for (Connection &connection : m_connections) + writeCommandToIODevice(command, connection.socket.get(), m_writeCommandCounter); + + m_writeCommandCounter++; +} + +void ConnectionManager::processFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + auto finishedProcess = qobject_cast<QProcess *>(sender()); + if (finishedProcess) + qWarning() << "Process" << (exitStatus == QProcess::CrashExit ? "crashed:" : "finished:") + << finishedProcess->arguments() << "exitCode:" << exitCode; + else + qWarning() << "Process" << (exitStatus == QProcess::CrashExit ? "crashed:" : "finished:") + << sender() << "exitCode:" << exitCode; + + writeCommand(QVariant::fromValue(EndPuppetCommand())); + + closeSocketsAndKillProcesses(); + + if (exitStatus == QProcess::CrashExit) + emit processCrashed(); +} + +void ConnectionManager::closeSocketsAndKillProcesses() +{ + for (Connection &connection : m_connections) { + if (connection.socket) { + disconnect(connection.socket.get()); + disconnect(connection.qmlPuppetProcess.get()); + connection.socket->waitForBytesWritten(1000); + connection.socket->abort(); + } + + if (connection.qmlPuppetProcess) { + QTimer::singleShot(3000, connection.qmlPuppetProcess.get(), &QProcess::terminate); + QTimer::singleShot(6000, connection.qmlPuppetProcess.get(), &QProcess::kill); + } + + connection.clear(); + } +} + +void ConnectionManager::printProcessOutput(QProcess *process, const QString &connectionName) +{ + while (process && process->canReadLine()) { + QByteArray line = process->readLine(); + line.chop(1); + qDebug().nospace() << connectionName << " Puppet: " << line; + } + qDebug() << "\n"; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.h b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h new file mode 100644 index 0000000000..4bb85b5ff2 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "baseconnectionmanager.h" +#include "nodeinstanceserverinterface.h" + +#include <QElapsedTimer> +#include <QFile> +#include <QProcess> + +QT_BEGIN_NAMESPACE +class QLocalServer; +class QLocalSocket; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT ConnectionManager : public BaseConnectionManager +{ + Q_OBJECT + +public: + ConnectionManager(); + ~ConnectionManager() override; + enum PuppetStreamType { FirstPuppetStream, SecondPuppetStream, ThirdPuppetStream }; + + void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) override; + void shutDown() override; + + void writeCommand(const QVariant &command) override; + +signals: + void processCrashed(); + +protected: + using BaseConnectionManager::processFinished; + void processFinished(int exitCode, QProcess::ExitStatus exitStatus) override; + +private: + void printProcessOutput(QProcess *process, const QString &connectionName); + void closeSocketsAndKillProcesses(); + +protected: + std::vector<Connection> m_connections; + quint32 m_writeCommandCounter = 0; + +private: + std::unique_ptr<QLocalServer> m_localServer; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.cpp b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.cpp new file mode 100644 index 0000000000..2aa7df6be6 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "connectionmanagerinterface.h" + +#include <QLocalSocket> +#include <QProcess> + +namespace QmlDesigner { + +ConnectionManagerInterface::~ConnectionManagerInterface() = default; + +ConnectionManagerInterface::Connection::~Connection() = default; + +ConnectionManagerInterface::Connection::Connection(const QString &name, const QString &mode) + : name{name} + , mode{mode} + , timer{std::make_unique<QTimer>()} +{} + +ConnectionManagerInterface::Connection::Connection(Connection &&connection) = default; + +void ConnectionManagerInterface::Connection::clear() +{ + qmlPuppetProcess.reset(); + socket.reset(); + blockSize = 0; + lastReadCommandCounter = 0; + timer->stop(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h new file mode 100644 index 0000000000..f3d8e6293e --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "qprocessuniqueptr.h" +#include <qmldesignercorelib_global.h> + + +QT_BEGIN_NAMESPACE +class QLocalSocket; +QT_END_NAMESPACE + +namespace ProjectExplorer { +class Target; +} + +namespace QmlDesigner { + +class NodeInstanceServerProxy; + +class QMLDESIGNERCORE_EXPORT ConnectionManagerInterface +{ +public: + class QMLDESIGNERCORE_EXPORT Connection final + { + public: + Connection(const QString &name, const QString &mode); + Connection(Connection &&connection); + + ~Connection(); + + void clear(); + + public: + QString name; + QString mode; + QProcessUniquePointer qmlPuppetProcess; + std::unique_ptr<QLocalSocket> socket; + quint32 blockSize = 0; + quint32 lastReadCommandCounter = 0; + std::unique_ptr<QTimer> timer; + }; + + virtual ~ConnectionManagerInterface(); + + virtual void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) + = 0; + virtual void shutDown() = 0; + + virtual void writeCommand(const QVariant &command) = 0; + +protected: + virtual void dispatchCommand(const QVariant &command, Connection &connection) = 0; + virtual void processFinished(int exitCode, QProcess::ExitStatus exitStatus) = 0; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/instances.pri b/src/plugins/qmldesigner/designercore/instances/instances.pri index 9856e1243f..7d6b71a080 100644 --- a/src/plugins/qmldesigner/designercore/instances/instances.pri +++ b/src/plugins/qmldesigner/designercore/instances/instances.pri @@ -1,14 +1,25 @@ INCLUDEPATH += $$PWD/ -HEADERS += $$PWD/../include/nodeinstance.h -HEADERS += $$PWD/nodeinstanceserverproxy.h -HEADERS += $$PWD/puppetcreator.h -HEADERS += $$PWD/puppetbuildprogressdialog.h +HEADERS += $$PWD/../include/nodeinstance.h \ + $$PWD/baseconnectionmanager.h \ + $$PWD/capturingconnectionmanager.h \ + $$PWD/connectionmanager.h \ + $$PWD/connectionmanagerinterface.h \ + $$PWD/interactiveconnectionmanager.h \ + $$PWD/nodeinstanceserverproxy.h \ + $$PWD/puppetcreator.h \ + $$PWD/puppetbuildprogressdialog.h \ + $$PWD/qprocessuniqueptr.h -SOURCES += $$PWD/nodeinstanceserverproxy.cpp -SOURCES += $$PWD/nodeinstance.cpp -SOURCES += $$PWD/nodeinstanceview.cpp -SOURCES += $$PWD/puppetcreator.cpp -SOURCES += $$PWD/puppetbuildprogressdialog.cpp +SOURCES += $$PWD/nodeinstanceserverproxy.cpp \ + $$PWD/baseconnectionmanager.cpp \ + $$PWD/capturingconnectionmanager.cpp \ + $$PWD/connectionmanager.cpp \ + $$PWD/connectionmanagerinterface.cpp \ + $$PWD/interactiveconnectionmanager.cpp \ + $$PWD/nodeinstance.cpp \ + $$PWD/nodeinstanceview.cpp \ + $$PWD/puppetcreator.cpp \ + $$PWD/puppetbuildprogressdialog.cpp FORMS += $$PWD/puppetbuildprogressdialog.ui diff --git a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp new file mode 100644 index 0000000000..4b6c73f19e --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "interactiveconnectionmanager.h" +#include "nodeinstanceserverproxy.h" +#include "nodeinstanceview.h" + +#include <qmldesignerplugin.h> + +#include <coreplugin/messagebox.h> + +#include <QLocalSocket> + +namespace QmlDesigner { + +InteractiveConnectionManager::InteractiveConnectionManager() +{ + m_connections.emplace_back("Editor", "editormode"); + m_connections.emplace_back("Render", "rendermode"); + m_connections.emplace_back("Preview", "previewmode"); +} + +void InteractiveConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) +{ + ConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target); + + DesignerSettings settings = QmlDesignerPlugin::instance()->settings(); + int timeOutTime = settings.value(DesignerSettingsKey::PUPPET_KILL_TIMEOUT).toInt(); + for (Connection &connection : m_connections) + connection.timer->setInterval(timeOutTime); + + if (QmlDesignerPlugin::instance() + ->settings() + .value(DesignerSettingsKey::DEBUG_PUPPET) + .toString() + .isEmpty()) { + for (Connection &connection : m_connections) { + QObject::connect(connection.timer.get(), &QTimer::timeout, [&]() { + puppetTimeout(connection); + }); + } + } +} + +void InteractiveConnectionManager::showCannotConnectToPuppetWarningAndSwitchToEditMode() +{ + Core::AsynchronousMessageBox::warning( + tr("Cannot Connect to QML Emulation Layer (QML Puppet)"), + tr("The executable of the QML emulation layer (QML Puppet) may not be responding. " + "Switching to another kit might help.")); + + QmlDesignerPlugin::instance()->switchToTextModeDeferred(); + nodeInstanceServerProxy()->nodeInstanceView()->emitDocumentMessage( + tr("Cannot Connect to QML Emulation Layer (QML Puppet)")); +} + +void InteractiveConnectionManager::dispatchCommand(const QVariant &command, Connection &connection) +{ + static const int puppetAliveCommandType = QMetaType::type("PuppetAliveCommand"); + + if (command.userType() == puppetAliveCommandType) { + puppetAlive(connection); + } else { + BaseConnectionManager::dispatchCommand(command, connection); + } +} + +void InteractiveConnectionManager::puppetTimeout(Connection &connection) +{ + if (connection.socket && connection.socket->waitForReadyRead(10)) { + connection.timer->stop(); + connection.timer->start(); + return; + } + + processFinished(); +} + +void InteractiveConnectionManager::puppetAlive(Connection &connection) +{ + connection.timer->stop(); + connection.timer->start(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h new file mode 100644 index 0000000000..1946620a43 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "connectionmanager.h" + +namespace QmlDesigner { + +class InteractiveConnectionManager : public ConnectionManager +{ +public: + InteractiveConnectionManager(); + + void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy, + const QString &qrcMappingString, + ProjectExplorer::Target *target) override; + + void showCannotConnectToPuppetWarningAndSwitchToEditMode() override; + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + void puppetTimeout(Connection &connection); + void puppetAlive(Connection &connection); +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index 8db9aa3a98..026d587233 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp @@ -25,6 +25,7 @@ #include "nodeinstanceserverproxy.h" +#include "connectionmanagerinterface.h" #include "puppetcreator.h" #include <changeauxiliarycommand.h> @@ -60,218 +61,55 @@ #include <valueschangedcommand.h> #include <view3dactioncommand.h> -#include <nodeinstanceview.h> #include <import.h> +#include <nodeinstanceview.h> #include <rewriterview.h> -#ifndef QMLDESIGNER_TEST -#include <qmldesignerplugin.h> -#endif - -#include <coreplugin/icore.h> -#include <utils/hostosinfo.h> -#include <coreplugin/messagebox.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> #include <projectexplorer/kit.h> -#include <qtsupport/qtkitinformation.h> +#include <utils/hostosinfo.h> #include <qtsupport/baseqtversion.h> +#include <qtsupport/qtkitinformation.h> #include <qtsupport/qtsupportconstants.h> +#include <QCoreApplication> +#include <QDir> +#include <QFileInfo> #include <QLocalServer> #include <QLocalSocket> #include <QLoggingCategory> +#include <QMessageBox> #include <QProcess> -#include <QCoreApplication> -#include <QUuid> -#include <QFileInfo> -#include <QDir> -#include <QTimer> #include <QTextStream> -#include <QMessageBox> +#include <QTimer> +#include <QUuid> namespace QmlDesigner { -static Q_LOGGING_CATEGORY(instanceViewBenchmark, "qtc.nodeinstances.init", QtWarningMsg) - -void NodeInstanceServerProxy::showCannotConnectToPuppetWarningAndSwitchToEditMode() -{ -#ifndef QMLDESIGNER_TEST - Core::AsynchronousMessageBox::warning(tr("Cannot Connect to QML Emulation Layer (QML Puppet)"), - tr("The executable of the QML emulation layer (QML Puppet) may not be responding. " - "Switching to another kit might help.")); - - QmlDesignerPlugin::instance()->switchToTextModeDeferred(); - m_nodeInstanceView->emitDocumentMessage(tr("Cannot Connect to QML Emulation Layer (QML Puppet)")); -#endif - -} +static Q_LOGGING_CATEGORY(instanceViewBenchmark, "qtc.nodeinstances.init", QtWarningMsg); NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceView, - RunModus runModus, - ProjectExplorer::Target *target) - : NodeInstanceServerInterface(nodeInstanceView), - m_localServer(new QLocalServer(this)), - m_nodeInstanceView(nodeInstanceView), - m_runModus(runModus) + ProjectExplorer::Target *target, + ConnectionManagerInterface &connectionManager) + : m_nodeInstanceView(nodeInstanceView) + , m_connectionManager{connectionManager} + { if (instanceViewBenchmark().isInfoEnabled()) m_benchmarkTimer.start(); - QString socketToken(QUuid::createUuid().toString()); - m_localServer->listen(socketToken); - m_localServer->setMaxPendingConnections(3); - - PuppetCreator puppetCreator(target, nodeInstanceView->model()); - puppetCreator.setQrcMappingString(qrcMappingString()); - - puppetCreator.createQml2PuppetExecutableIfMissing(); - - m_qmlPuppetEditorProcess = puppetCreator.createPuppetProcess("editormode", - socketToken, - this, - SLOT(printEditorProcessOutput()), - SLOT(processFinished(int,QProcess::ExitStatus))); - - if (runModus == NormalModus) { - m_qmlPuppetRenderProcess = puppetCreator.createPuppetProcess("rendermode", - socketToken, - this, - SLOT(printRenderProcessOutput()), - SLOT(processFinished(int,QProcess::ExitStatus))); - m_qmlPuppetPreviewProcess = puppetCreator.createPuppetProcess("previewmode", - socketToken, - this, - SLOT(printPreviewProcessOutput()), - SLOT(processFinished(int,QProcess::ExitStatus))); - } - - const int second = 1000; - const int waitConstant = 8 * second; - if (m_qmlPuppetEditorProcess->waitForStarted(waitConstant)) { - connect(m_qmlPuppetEditorProcess.data(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), - m_qmlPuppetEditorProcess.data(), &QProcess::deleteLater); - qCInfo(instanceViewBenchmark) << "puppets started:" << m_benchmarkTimer.elapsed(); - - if (runModus == NormalModus) { - m_qmlPuppetPreviewProcess->waitForStarted(waitConstant / 2); - connect(m_qmlPuppetPreviewProcess.data(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), - m_qmlPuppetPreviewProcess.data(), &QProcess::deleteLater); - - m_qmlPuppetRenderProcess->waitForStarted(waitConstant / 2); - connect(m_qmlPuppetRenderProcess.data(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), - m_qmlPuppetRenderProcess.data(), &QProcess::deleteLater); - } - - bool connectedToPuppet = true; - - if (!m_localServer->hasPendingConnections()) - connectedToPuppet = m_localServer->waitForNewConnection(waitConstant / 4); - - if (connectedToPuppet) { - m_firstSocket = m_localServer->nextPendingConnection(); - connect(m_firstSocket.data(), &QIODevice::readyRead, this, - &NodeInstanceServerProxy::readFirstDataStream); - - if (runModus == NormalModus) { - if (!m_localServer->hasPendingConnections()) - connectedToPuppet = m_localServer->waitForNewConnection(waitConstant / 4); - - if (connectedToPuppet) { - m_secondSocket = m_localServer->nextPendingConnection(); - connect(m_secondSocket.data(), &QIODevice::readyRead, this, &NodeInstanceServerProxy::readSecondDataStream); - - if (!m_localServer->hasPendingConnections()) - connectedToPuppet = m_localServer->waitForNewConnection(waitConstant / 4); - - qCInfo(instanceViewBenchmark) << "puppets connected:" << m_benchmarkTimer.elapsed(); - if (connectedToPuppet) { - m_thirdSocket = m_localServer->nextPendingConnection(); - connect(m_thirdSocket.data(), &QIODevice::readyRead, this, &NodeInstanceServerProxy::readThirdDataStream); - } else { - showCannotConnectToPuppetWarningAndSwitchToEditMode(); - } - } else { - showCannotConnectToPuppetWarningAndSwitchToEditMode(); - } - } - } else { - showCannotConnectToPuppetWarningAndSwitchToEditMode(); - } - - } else { - showCannotConnectToPuppetWarningAndSwitchToEditMode(); - } - - m_localServer->close(); - - - int indexOfCapturePuppetStream = QCoreApplication::arguments().indexOf("-capture-puppet-stream"); - if (indexOfCapturePuppetStream > 0) { - m_captureFileForTest.setFileName(QCoreApplication::arguments().at(indexOfCapturePuppetStream + 1)); - bool isOpen = m_captureFileForTest.open(QIODevice::WriteOnly); - qDebug() << "file is open: " << isOpen; - } - -#ifndef QMLDESIGNER_TEST - DesignerSettings settings = QmlDesignerPlugin::instance()->settings(); - int timeOutTime = settings.value(DesignerSettingsKey::PUPPET_KILL_TIMEOUT).toInt(); - m_firstTimer.setInterval(timeOutTime); - m_secondTimer.setInterval(timeOutTime); - m_thirdTimer.setInterval(timeOutTime); - - if (QmlDesignerPlugin::instance()->settings().value(DesignerSettingsKey:: - DEBUG_PUPPET).toString().isEmpty()) { - - connect(&m_firstTimer, &QTimer::timeout, this, - [this](){ NodeInstanceServerProxy::puppetTimeout(FirstPuppetStream); }); - connect(&m_secondTimer, &QTimer::timeout, this, - [this](){ NodeInstanceServerProxy::puppetTimeout(SecondPuppetStream); }); - connect(&m_thirdTimer, &QTimer::timeout, this, - [this](){ NodeInstanceServerProxy::puppetTimeout(ThirdPuppetStream); }); - } -#endif + m_connectionManager.setUp(this, qrcMappingString(), target); + + qCInfo(instanceViewBenchmark) << "puppets setup:" << m_benchmarkTimer.elapsed(); } NodeInstanceServerProxy::~NodeInstanceServerProxy() { - m_destructing = true; - - disconnect(this, SLOT(processFinished(int,QProcess::ExitStatus))); - - writeCommand(QVariant::fromValue(EndPuppetCommand())); - - if (m_firstSocket) { - m_firstSocket->waitForBytesWritten(1000); - m_firstSocket->abort(); - } - - if (m_secondSocket) { - m_secondSocket->waitForBytesWritten(1000); - m_secondSocket->abort(); - } - - if (m_thirdSocket) { - m_thirdSocket->waitForBytesWritten(1000); - m_thirdSocket->abort(); - } - - if (m_qmlPuppetEditorProcess) { - QTimer::singleShot(3000, m_qmlPuppetEditorProcess.data(), &QProcess::terminate); - QTimer::singleShot(6000, m_qmlPuppetEditorProcess.data(), &QProcess::kill); - } - - if (m_qmlPuppetPreviewProcess) { - QTimer::singleShot(3000, m_qmlPuppetPreviewProcess.data(), &QProcess::terminate); - QTimer::singleShot(6000, m_qmlPuppetPreviewProcess.data(), &QProcess::kill); - } - - if (m_qmlPuppetRenderProcess) { - QTimer::singleShot(3000, m_qmlPuppetRenderProcess.data(), &QProcess::terminate); - QTimer::singleShot(6000, m_qmlPuppetRenderProcess.data(), &QProcess::kill); - } + m_connectionManager.shutDown(); } -void NodeInstanceServerProxy::dispatchCommand(const QVariant &command, PuppetStreamType puppetStreamType) +void NodeInstanceServerProxy::dispatchCommand(const QVariant &command) { static const int informationChangedCommandType = QMetaType::type("InformationChangedCommand"); static const int valuesChangedCommandType = QMetaType::type("ValuesChangedCommand"); @@ -280,15 +118,10 @@ void NodeInstanceServerProxy::dispatchCommand(const QVariant &command, PuppetStr static const int childrenChangedCommandType = QMetaType::type("ChildrenChangedCommand"); static const int statePreviewImageChangedCommandType = QMetaType::type("StatePreviewImageChangedCommand"); static const int componentCompletedCommandType = QMetaType::type("ComponentCompletedCommand"); - static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand"); static const int tokenCommandType = QMetaType::type("TokenCommand"); static const int debugOutputCommandType = QMetaType::type("DebugOutputCommand"); - static const int puppetAliveCommandType = QMetaType::type("PuppetAliveCommand"); static const int changeSelectionCommandType = QMetaType::type("ChangeSelectionCommand"); - static const int puppetToCreatorCommand = QMetaType::type("PuppetToCreatorCommand"); - - if (m_destructing) - return; + static const int puppetToCreatorCommandType = QMetaType::type("PuppetToCreatorCommand"); qCInfo(instanceViewBenchmark) << "dispatching command" << command.userType() << command.typeName(); if (command.userType() == informationChangedCommandType) { @@ -311,13 +144,8 @@ void NodeInstanceServerProxy::dispatchCommand(const QVariant &command, PuppetStr nodeInstanceClient()->debugOutput(command.value<DebugOutputCommand>()); } else if (command.userType() == changeSelectionCommandType) { nodeInstanceClient()->selectionChanged(command.value<ChangeSelectionCommand>()); - } else if (command.userType() == puppetToCreatorCommand) { + } else if (command.userType() == puppetToCreatorCommandType) { nodeInstanceClient()->handlePuppetToCreatorCommand(command.value<PuppetToCreatorCommand>()); - } else if (command.userType() == puppetAliveCommandType) { - puppetAlive(puppetStreamType); - } else if (command.userType() == synchronizeCommandType) { - SynchronizeCommand synchronizeCommand = command.value<SynchronizeCommand>(); - m_synchronizeId = synchronizeCommand.synchronizeId(); } else { Q_ASSERT(false); } @@ -327,33 +155,13 @@ void NodeInstanceServerProxy::dispatchCommand(const QVariant &command, PuppetStr NodeInstanceClientInterface *NodeInstanceServerProxy::nodeInstanceClient() const { - return m_nodeInstanceView.data(); -} - -void NodeInstanceServerProxy::puppetAlive(NodeInstanceServerProxy::PuppetStreamType puppetStreamType) -{ - switch (puppetStreamType) { - case FirstPuppetStream: - m_firstTimer.stop(); - m_firstTimer.start(); - break; - case SecondPuppetStream: - m_secondTimer.stop(); - m_secondTimer.start(); - break; - case ThirdPuppetStream: - m_thirdTimer.stop(); - m_thirdTimer.start(); - break; - default: - break; - } + return m_nodeInstanceView; } QString NodeInstanceServerProxy::qrcMappingString() const { - if (m_nodeInstanceView && m_nodeInstanceView.data()->model()) { - RewriterView *rewriterView = m_nodeInstanceView.data()->model()->rewriterView(); + if (m_nodeInstanceView && m_nodeInstanceView->model()) { + RewriterView *rewriterView = m_nodeInstanceView->model()->rewriterView(); if (rewriterView) { QString mappingString; @@ -374,265 +182,9 @@ QString NodeInstanceServerProxy::qrcMappingString() const return QString(); } -void NodeInstanceServerProxy::processFinished() -{ - processFinished(-1, QProcess::CrashExit); -} - -void NodeInstanceServerProxy::puppetTimeout(PuppetStreamType puppetStreamType) -{ - switch (puppetStreamType) { - case FirstPuppetStream: - if (m_firstSocket->waitForReadyRead(10)) { - m_firstTimer.stop(); - m_firstTimer.start(); - return; - } - break; - case SecondPuppetStream: - if (m_secondSocket->waitForReadyRead(10)) { - m_secondTimer.stop(); - m_secondTimer.start(); - return; - } - break; - case ThirdPuppetStream: - if (m_thirdSocket->waitForReadyRead(10)) { - m_thirdTimer.stop(); - m_thirdTimer.start(); - return; - } - break; - default: - break; - } - - processFinished(); -} - -static void writeCommandToIODecive(const QVariant &command, QIODevice *ioDevice, unsigned int commandCounter) -{ - if (ioDevice) { - QByteArray block; - QDataStream out(&block, QIODevice::WriteOnly); - out.setVersion(QDataStream::Qt_4_8); - out << quint32(0); - out << quint32(commandCounter); - out << command; - out.device()->seek(0); - out << quint32(block.size() - sizeof(quint32)); - - ioDevice->write(block); - } -} - void NodeInstanceServerProxy::writeCommand(const QVariant &command) { - writeCommandToIODecive(command, m_firstSocket.data(), m_writeCommandCounter); - writeCommandToIODecive(command, m_secondSocket.data(), m_writeCommandCounter); - writeCommandToIODecive(command, m_thirdSocket.data(), m_writeCommandCounter); - - if (m_captureFileForTest.isWritable()) { - qDebug() << "Write stream to file: " << m_captureFileForTest.fileName(); - writeCommandToIODecive(command, &m_captureFileForTest, m_writeCommandCounter); - qDebug() << "\twrite file: " << m_captureFileForTest.pos(); - } - - m_writeCommandCounter++; - if (m_runModus == TestModus) { - static int synchronizeId = 0; - synchronizeId++; - SynchronizeCommand synchronizeCommand(synchronizeId); - - writeCommandToIODecive(QVariant::fromValue(synchronizeCommand), m_firstSocket.data(), m_writeCommandCounter); - m_writeCommandCounter++; - - while (m_firstSocket->waitForReadyRead(100)) { - readFirstDataStream(); - if (m_synchronizeId == synchronizeId) - return; - } - } -} - -void NodeInstanceServerProxy::processFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - auto finishedProcess = qobject_cast<QProcess*>(sender()); - if (finishedProcess) - qWarning() << "Process" << (exitStatus == QProcess::CrashExit ? "crashed:" : "finished:") << finishedProcess->arguments() << "exitCode:" << exitCode; - else - qWarning() << "Process" << (exitStatus == QProcess::CrashExit ? "crashed:" : "finished:") << sender() << "exitCode:" << exitCode; - - if (m_captureFileForTest.isOpen()) { - m_captureFileForTest.close(); - Core::AsynchronousMessageBox::warning(tr("QML Emulation Layer (QML Puppet) Crashed"), - tr("You are recording a puppet stream and the emulations layer crashed. " - "It is recommended to reopen the Qt Quick Designer and start again.")); - } - - - writeCommand(QVariant::fromValue(EndPuppetCommand())); - - if (m_firstSocket) { - m_firstSocket->waitForBytesWritten(1000); - m_firstSocket->abort(); - } - - if (m_secondSocket) { - m_secondSocket->waitForBytesWritten(1000); - m_secondSocket->abort(); - } - - if (m_thirdSocket) { - m_thirdSocket->waitForBytesWritten(1000); - m_thirdSocket->abort(); - } - - if (exitStatus == QProcess::CrashExit) - emit processCrashed(); -} - - -void NodeInstanceServerProxy::readFirstDataStream() -{ - QList<QVariant> commandList; - - while (!m_firstSocket->atEnd()) { - if (m_firstSocket->bytesAvailable() < int(sizeof(quint32))) - break; - - QDataStream in(m_firstSocket.data()); - in.setVersion(QDataStream::Qt_4_8); - - if (m_firstBlockSize == 0) - in >> m_firstBlockSize; - - if (m_firstSocket->bytesAvailable() < m_firstBlockSize) - break; - - quint32 commandCounter; - in >> commandCounter; - bool commandLost = !((m_firstLastReadCommandCounter == 0 && commandCounter == 0) || (m_firstLastReadCommandCounter + 1 == commandCounter)); - if (commandLost) - qDebug() << "server command lost: " << m_firstLastReadCommandCounter << commandCounter; - m_firstLastReadCommandCounter = commandCounter; - - - QVariant command; - in >> command; - m_firstBlockSize = 0; - - commandList.append(command); - } - - foreach (const QVariant &command, commandList) { - dispatchCommand(command, FirstPuppetStream); - } -} - -void NodeInstanceServerProxy::readSecondDataStream() -{ - QList<QVariant> commandList; - - while (!m_secondSocket->atEnd()) { - if (m_secondSocket->bytesAvailable() < int(sizeof(quint32))) - break; - - QDataStream in(m_secondSocket.data()); - in.setVersion(QDataStream::Qt_4_8); - - if (m_secondBlockSize == 0) - in >> m_secondBlockSize; - - if (m_secondSocket->bytesAvailable() < m_secondBlockSize) - break; - - quint32 commandCounter; - in >> commandCounter; - bool commandLost = !((m_secondLastReadCommandCounter == 0 && commandCounter == 0) || (m_secondLastReadCommandCounter + 1 == commandCounter)); - if (commandLost) - qDebug() << "server command lost: " << m_secondLastReadCommandCounter << commandCounter; - m_secondLastReadCommandCounter = commandCounter; - - - QVariant command; - in >> command; - m_secondBlockSize = 0; - - commandList.append(command); - } - - foreach (const QVariant &command, commandList) { - dispatchCommand(command, SecondPuppetStream); - } -} - -void NodeInstanceServerProxy::readThirdDataStream() -{ - QList<QVariant> commandList; - - while (!m_thirdSocket->atEnd()) { - if (m_thirdSocket->bytesAvailable() < int(sizeof(quint32))) - break; - - QDataStream in(m_thirdSocket.data()); - in.setVersion(QDataStream::Qt_4_8); - - if (m_thirdBlockSize == 0) - in >> m_thirdBlockSize; - - if (m_thirdSocket->bytesAvailable() < m_thirdBlockSize) - break; - - quint32 commandCounter; - in >> commandCounter; - bool commandLost = !((m_thirdLastReadCommandCounter == 0 && commandCounter == 0) || (m_thirdLastReadCommandCounter + 1 == commandCounter)); - if (commandLost) - qDebug() << "server command lost: " << m_thirdLastReadCommandCounter << commandCounter; - m_thirdLastReadCommandCounter = commandCounter; - - - QVariant command; - in >> command; - m_thirdBlockSize = 0; - - commandList.append(command); - } - - foreach (const QVariant &command, commandList) { - dispatchCommand(command, ThirdPuppetStream); - } -} - -void NodeInstanceServerProxy::printEditorProcessOutput() -{ - while (m_qmlPuppetEditorProcess && m_qmlPuppetEditorProcess->canReadLine()) { - QByteArray line = m_qmlPuppetEditorProcess->readLine(); - line.chop(1); - qDebug().nospace() << "Editor Puppet: " << line; - } - qDebug() << "\n"; -} - -void NodeInstanceServerProxy::printPreviewProcessOutput() -{ - while (m_qmlPuppetPreviewProcess && m_qmlPuppetPreviewProcess->canReadLine()) { - QByteArray line = m_qmlPuppetPreviewProcess->readLine(); - line.chop(1); - qDebug().nospace() << "Preview Puppet: " << line; - } - qDebug() << "\n"; -} - -void NodeInstanceServerProxy::printRenderProcessOutput() -{ - while (m_qmlPuppetRenderProcess && m_qmlPuppetRenderProcess->canReadLine()) { - QByteArray line = m_qmlPuppetRenderProcess->readLine(); - line.chop(1); - qDebug().nospace() << "Render Puppet: " << line; - } - - qDebug() << "\n"; + m_connectionManager.writeCommand(command); } void NodeInstanceServerProxy::createInstances(const CreateInstancesCommand &command) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h index 4b0df0fd9a..2efc7ea8a4 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h @@ -48,21 +48,17 @@ namespace QmlDesigner { class NodeInstanceClientInterface; class NodeInstanceView; class NodeInstanceClientProxy; +class ConnectionManagerInterface; class NodeInstanceServerProxy : public NodeInstanceServerInterface { + friend class BaseConnectionManager; Q_OBJECT public: - enum PuppetStreamType { - FirstPuppetStream, - SecondPuppetStream, - ThirdPuppetStream, - }; - explicit NodeInstanceServerProxy(NodeInstanceView *nodeInstanceView, - RunModus runModus, - ProjectExplorer::Target *target); + ProjectExplorer::Target *target, + ConnectionManagerInterface &connectionManager); ~NodeInstanceServerProxy() override; void createInstances(const CreateInstancesCommand &command) override; void changeFileUrl(const ChangeFileUrlCommand &command) override; @@ -88,52 +84,22 @@ public: void changeLanguage(const ChangeLanguageCommand &command) override; void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override; + NodeInstanceView *nodeInstanceView() const { return m_nodeInstanceView; } + + QString qrcMappingString() const; + protected: void writeCommand(const QVariant &command); - void dispatchCommand(const QVariant &command, PuppetStreamType puppetStreamType); + void dispatchCommand(const QVariant &command); NodeInstanceClientInterface *nodeInstanceClient() const; - void puppetAlive(PuppetStreamType puppetStreamType); - QString qrcMappingString() const; signals: void processCrashed(); -private slots: - void processFinished(); - void puppetTimeout(PuppetStreamType puppetStreamType); - void processFinished(int exitCode, QProcess::ExitStatus exitStatus); - void readFirstDataStream(); - void readSecondDataStream(); - void readThirdDataStream(); - - void printEditorProcessOutput(); - void printPreviewProcessOutput(); - void printRenderProcessOutput(); - void showCannotConnectToPuppetWarningAndSwitchToEditMode(); private: - QFile m_captureFileForTest; - QTimer m_firstTimer; - QTimer m_secondTimer; - QTimer m_thirdTimer; - QPointer<QLocalServer> m_localServer; - QPointer<QLocalSocket> m_firstSocket; - QPointer<QLocalSocket> m_secondSocket; - QPointer<QLocalSocket> m_thirdSocket; - QPointer<NodeInstanceView> m_nodeInstanceView; - QPointer<QProcess> m_qmlPuppetEditorProcess; - QPointer<QProcess> m_qmlPuppetPreviewProcess; - QPointer<QProcess> m_qmlPuppetRenderProcess; - quint32 m_firstBlockSize = 0; - quint32 m_secondBlockSize = 0; - quint32 m_thirdBlockSize = 0; - quint32 m_writeCommandCounter = 0; - quint32 m_firstLastReadCommandCounter = 0; - quint32 m_secondLastReadCommandCounter = 0; - quint32 m_thirdLastReadCommandCounter = 0; - RunModus m_runModus; - int m_synchronizeId = -1; + NodeInstanceView *m_nodeInstanceView{}; QElapsedTimer m_benchmarkTimer; - bool m_destructing = false; + ConnectionManagerInterface &m_connectionManager; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index ce6412962d..8c078e29aa 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -27,6 +27,7 @@ #include "abstractproperty.h" #include "bindingproperty.h" +#include "captureddatacommand.h" #include "changeauxiliarycommand.h" #include "changebindingscommand.h" #include "changefileurlcommand.h" @@ -41,6 +42,7 @@ #include "clearscenecommand.h" #include "completecomponentcommand.h" #include "componentcompletedcommand.h" +#include "connectionmanagerinterface.h" #include "createinstancescommand.h" #include "createscenecommand.h" #include "debugoutputcommand.h" @@ -124,11 +126,10 @@ namespace QmlDesigner { \sa ~NodeInstanceView, setRenderOffScreen() */ -NodeInstanceView::NodeInstanceView(QObject *parent, NodeInstanceServerInterface::RunModus runModus) - : AbstractView(parent), - m_baseStatePreviewImage(QSize(100, 100), QImage::Format_ARGB32), - m_runModus(runModus), - m_restartProcessTimerId(0) +NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager) + : m_connectionManager(connectionManager) + , m_baseStatePreviewImage(QSize(100, 100), QImage::Format_ARGB32) + , m_restartProcessTimerId(0) { m_baseStatePreviewImage.fill(0xFFFFFF); } @@ -140,7 +141,6 @@ NodeInstanceView::NodeInstanceView(QObject *parent, NodeInstanceServerInterface: NodeInstanceView::~NodeInstanceView() { removeAllInstanceNodeRelationships(); - delete nodeInstanceServer(); m_currentTarget = nullptr; } @@ -191,14 +191,16 @@ bool static parentTakesOverRendering(const ModelNode &modelNode) void NodeInstanceView::modelAttached(Model *model) { AbstractView::modelAttached(model); - auto server = new NodeInstanceServerProxy(this, m_runModus, m_currentTarget); - m_nodeInstanceServer = server; + m_nodeInstanceServer = createNodeInstanceServerProxy(); m_lastCrashTime.start(); - connect(server, &NodeInstanceServerProxy::processCrashed, this, &NodeInstanceView::handleCrash); + connect(m_nodeInstanceServer.get(), + &NodeInstanceServerProxy::processCrashed, + this, + &NodeInstanceView::handleCrash); if (!isSkippedRootNode(rootModelNode())) { - nodeInstanceServer()->createScene(createCreateSceneCommand()); - nodeInstanceServer()->changeSelection(createChangeSelectionCommand(model->selectedNodes(this))); + m_nodeInstanceServer->createScene(createCreateSceneCommand()); + m_nodeInstanceServer->changeSelection(createChangeSelectionCommand(model->selectedNodes(this))); } ModelNode stateNode = currentStateNode(); @@ -206,15 +208,14 @@ void NodeInstanceView::modelAttached(Model *model) NodeInstance newStateInstance = instanceForModelNode(stateNode); activateState(newStateInstance); } - } void NodeInstanceView::modelAboutToBeDetached(Model * model) { removeAllInstanceNodeRelationships(); - if (nodeInstanceServer()) { - nodeInstanceServer()->clearScene(createClearSceneCommand()); - delete nodeInstanceServer(); + if (m_nodeInstanceServer) { + m_nodeInstanceServer->clearScene(createClearSceneCommand()); + m_nodeInstanceServer.reset(); } m_statePreviewImage.clear(); m_baseStatePreviewImage = QImage(); @@ -274,15 +275,18 @@ void NodeInstanceView::restartProcess() killTimer(m_restartProcessTimerId); if (model()) { - delete nodeInstanceServer(); + m_nodeInstanceServer.reset(); + m_nodeInstanceServer = createNodeInstanceServerProxy(); - auto server = new NodeInstanceServerProxy(this, m_runModus, m_currentTarget); - m_nodeInstanceServer = server; - connect(server, &NodeInstanceServerProxy::processCrashed, this, &NodeInstanceView::handleCrash); + connect(m_nodeInstanceServer.get(), + &NodeInstanceServerProxy::processCrashed, + this, + &NodeInstanceView::handleCrash); if (!isSkippedRootNode(rootModelNode())) { - nodeInstanceServer()->createScene(createCreateSceneCommand()); - nodeInstanceServer()->changeSelection(createChangeSelectionCommand(model()->selectedNodes(this))); + m_nodeInstanceServer->createScene(createCreateSceneCommand()); + m_nodeInstanceServer->changeSelection( + createChangeSelectionCommand(model()->selectedNodes(this))); } ModelNode stateNode = currentStateNode(); @@ -313,17 +317,19 @@ void NodeInstanceView::nodeCreated(const ModelNode &createdNode) propertyList.append(createdNode.variantProperty("y")); updatePosition(propertyList); - nodeInstanceServer()->createInstances(createCreateInstancesCommand({instance})); - nodeInstanceServer()->changePropertyValues(createChangeValueCommand(createdNode.variantProperties())); - nodeInstanceServer()->completeComponent(createComponentCompleteCommand({instance})); + 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) { - nodeInstanceServer()->removeInstances(createRemoveInstancesCommand(removedNode)); - nodeInstanceServer()->removeSharedMemory(createRemoveSharedMemoryCommand("Image", removedNode.internalId())); + m_nodeInstanceServer->removeInstances(createRemoveInstancesCommand(removedNode)); + m_nodeInstanceServer->removeSharedMemory( + createRemoveSharedMemoryCommand("Image", removedNode.internalId())); removeInstanceAndSubInstances(removedNode); } @@ -343,11 +349,10 @@ void NodeInstanceView::resetHorizontalAnchors(const ModelNode &modelNode) valueList.append(modelNode.variantProperty("width")); if (!valueList.isEmpty()) - nodeInstanceServer()->changePropertyValues(createChangeValueCommand(valueList)); + m_nodeInstanceServer->changePropertyValues(createChangeValueCommand(valueList)); if (!bindingList.isEmpty()) - nodeInstanceServer()->changePropertyBindings(createChangeBindingCommand(bindingList)); - + m_nodeInstanceServer->changePropertyBindings(createChangeBindingCommand(bindingList)); } void NodeInstanceView::resetVerticalAnchors(const ModelNode &modelNode) @@ -366,10 +371,10 @@ void NodeInstanceView::resetVerticalAnchors(const ModelNode &modelNode) valueList.append(modelNode.variantProperty("height")); if (!valueList.isEmpty()) - nodeInstanceServer()->changePropertyValues(createChangeValueCommand(valueList)); + m_nodeInstanceServer->changePropertyValues(createChangeValueCommand(valueList)); if (!bindingList.isEmpty()) - nodeInstanceServer()->changePropertyBindings(createChangeBindingCommand(bindingList)); + m_nodeInstanceServer->changePropertyBindings(createChangeBindingCommand(bindingList)); } void NodeInstanceView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& propertyList) @@ -388,10 +393,10 @@ void NodeInstanceView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& RemoveInstancesCommand removeInstancesCommand = createRemoveInstancesCommand(nodeList); if (!removeInstancesCommand.instanceIds().isEmpty()) - nodeInstanceServer()->removeInstances(removeInstancesCommand); + m_nodeInstanceServer->removeInstances(removeInstancesCommand); - nodeInstanceServer()->removeSharedMemory(createRemoveSharedMemoryCommand("Image", nodeList)); - nodeInstanceServer()->removeProperties(createRemovePropertiesCommand(nonNodePropertyList)); + m_nodeInstanceServer->removeSharedMemory(createRemoveSharedMemoryCommand("Image", nodeList)); + m_nodeInstanceServer->removeProperties(createRemovePropertiesCommand(nonNodePropertyList)); foreach (const AbstractProperty &property, propertyList) { const PropertyName &name = property.name(); @@ -445,7 +450,7 @@ void NodeInstanceView::nodeTypeChanged(const ModelNode &, const TypeName &, int, void NodeInstanceView::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags /*propertyChange*/) { - nodeInstanceServer()->changePropertyBindings(createChangeBindingCommand(propertyList)); + m_nodeInstanceServer->changePropertyBindings(createChangeBindingCommand(propertyList)); } /*! @@ -460,7 +465,7 @@ void NodeInstanceView::bindingPropertiesChanged(const QList<BindingProperty>& pr void NodeInstanceView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags /*propertyChange*/) { updatePosition(propertyList); - nodeInstanceServer()->changePropertyValues(createChangeValueCommand(propertyList)); + m_nodeInstanceServer->changePropertyValues(createChangeValueCommand(propertyList)); } /*! Notifies the view that the property parent of the model node \a node has @@ -476,20 +481,21 @@ void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractP { if (!isSkippedNode(node)) { updateChildren(newPropertyParent); - nodeInstanceServer()->reparentInstances(createReparentInstancesCommand(node, newPropertyParent, oldPropertyParent)); + m_nodeInstanceServer->reparentInstances( + createReparentInstancesCommand(node, newPropertyParent, oldPropertyParent)); } } void NodeInstanceView::fileUrlChanged(const QUrl &/*oldUrl*/, const QUrl &newUrl) { - nodeInstanceServer()->changeFileUrl(createChangeFileUrlCommand(newUrl)); + m_nodeInstanceServer->changeFileUrl(createChangeFileUrlCommand(newUrl)); } void NodeInstanceView::nodeIdChanged(const ModelNode& node, const QString& /*newId*/, const QString& /*oldId*/) { if (hasInstanceForModelNode(node)) { NodeInstance instance = instanceForModelNode(node); - nodeInstanceServer()->changeIds(createChangeIdsCommand({instance})); + m_nodeInstanceServer->changeIds(createChangeIdsCommand({instance})); } } @@ -512,7 +518,7 @@ void NodeInstanceView::nodeOrderChanged(const NodeListProperty & listProperty, } } - nodeInstanceServer()->reparentInstances(ReparentInstancesCommand(containerList)); + m_nodeInstanceServer->reparentInstances(ReparentInstancesCommand(containerList)); } void NodeInstanceView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) @@ -531,16 +537,16 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, if (value.isValid() || name == "invisible") { PropertyValueContainer container(instance.instanceId(), name, value, TypeName()); ChangeAuxiliaryCommand changeAuxiliaryCommand({container}); - nodeInstanceServer()->changeAuxiliaryValues(changeAuxiliaryCommand); + m_nodeInstanceServer->changeAuxiliaryValues(changeAuxiliaryCommand); } else { if (node.hasVariantProperty(name)) { PropertyValueContainer container(instance.instanceId(), name, node.variantProperty(name).value(), TypeName()); ChangeValuesCommand changeValueCommand({container}); - nodeInstanceServer()->changePropertyValues(changeValueCommand); + m_nodeInstanceServer->changePropertyValues(changeValueCommand); } else if (node.hasBindingProperty(name)) { PropertyBindingContainer container(instance.instanceId(), name, node.bindingProperty(name).expression(), TypeName()); ChangeBindingsCommand changeValueCommand({container}); - nodeInstanceServer()->changePropertyBindings(changeValueCommand); + m_nodeInstanceServer->changePropertyBindings(changeValueCommand); } } } @@ -548,9 +554,9 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, const QString languageAsString = value.toString(); if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(m_currentTarget)) multiLanguageAspect->setCurrentLocale(languageAsString); - nodeInstanceServer()->changeLanguage({languageAsString}); + m_nodeInstanceServer->changeLanguage({languageAsString}); } else if (node.isRootNode() && name == "previewSize@Internal") { - nodeInstanceServer()->changePreviewImageSize(value.toSize()); + m_nodeInstanceServer->changePreviewImageSize(value.toSize()); } } @@ -565,10 +571,12 @@ void NodeInstanceView::nodeSourceChanged(const ModelNode &node, const QString & if (hasInstanceForModelNode(node)) { NodeInstance instance = instanceForModelNode(node); ChangeNodeSourceCommand changeNodeSourceCommand(instance.instanceId(), newNodeSource); - nodeInstanceServer()->changeNodeSource(changeNodeSourceCommand); + m_nodeInstanceServer->changeNodeSource(changeNodeSourceCommand); } } +void NodeInstanceView::capturedData(const CapturedDataCommand &) {} + void NodeInstanceView::currentStateChanged(const ModelNode &node) { NodeInstance newStateInstance = instanceForModelNode(node); @@ -790,12 +798,6 @@ void NodeInstanceView::updatePosition(const QList<VariantProperty> &propertyList emitInstanceInformationsChange(informationChangeHash); } -NodeInstanceServerInterface *NodeInstanceView::nodeInstanceServer() const -{ - return m_nodeInstanceServer.data(); -} - - NodeInstance NodeInstanceView::loadNode(const ModelNode &node) { NodeInstance instance(NodeInstance::create(node)); @@ -810,12 +812,12 @@ NodeInstance NodeInstanceView::loadNode(const ModelNode &node) void NodeInstanceView::activateState(const NodeInstance &instance) { - nodeInstanceServer()->changeState(ChangeStateCommand(instance.instanceId())); + m_nodeInstanceServer->changeState(ChangeStateCommand(instance.instanceId())); } void NodeInstanceView::activateBaseState() { - nodeInstanceServer()->changeState(ChangeStateCommand(-1)); + m_nodeInstanceServer->changeState(ChangeStateCommand(-1)); } void NodeInstanceView::removeRecursiveChildRelationship(const ModelNode &removedNode) @@ -1248,7 +1250,8 @@ void NodeInstanceView::valuesChanged(const ValuesChangedCommand &command) } } - nodeInstanceServer()->removeSharedMemory(createRemoveSharedMemoryCommand(QStringLiteral("Values"), command.keyNumber())); + m_nodeInstanceServer->removeSharedMemory( + createRemoveSharedMemoryCommand(QStringLiteral("Values"), command.keyNumber())); if (!valuePropertyChangeList.isEmpty()) emitInstancePropertyChange(valuePropertyChangeList); @@ -1456,7 +1459,7 @@ void NodeInstanceView::sendToken(const QString &token, int number, const QVector foreach (const ModelNode &node, nodeVector) instanceIdVector.append(node.internalId()); - nodeInstanceServer()->token(TokenCommand(token, number, instanceIdVector)); + m_nodeInstanceServer->token(TokenCommand(token, number, instanceIdVector)); } void NodeInstanceView::selectionChanged(const ChangeSelectionCommand &command) @@ -1471,7 +1474,7 @@ void NodeInstanceView::selectionChanged(const ChangeSelectionCommand &command) void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand &command) { if (command.type() == PuppetToCreatorCommand::Edit3DToolState) { - if (!m_nodeInstanceServer.isNull()) { + if (m_nodeInstanceServer) { auto data = qvariant_cast<QVariantList>(command.data()); if (data.size() == 3) { QString qmlId = data[0].toString(); @@ -1488,25 +1491,30 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand } } +std::unique_ptr<NodeInstanceServerProxy> NodeInstanceView::createNodeInstanceServerProxy() +{ + return std::make_unique<NodeInstanceServerProxy>(this, m_currentTarget, m_connectionManager); +} + void NodeInstanceView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> & /*lastSelectedNodeList*/) { - nodeInstanceServer()->changeSelection(createChangeSelectionCommand(selectedNodeList)); + m_nodeInstanceServer->changeSelection(createChangeSelectionCommand(selectedNodeList)); } void NodeInstanceView::sendInputEvent(QInputEvent *e) const { - nodeInstanceServer()->inputEvent(InputEventCommand(e)); + m_nodeInstanceServer->inputEvent(InputEventCommand(e)); } void NodeInstanceView::view3DAction(const View3DActionCommand &command) { - nodeInstanceServer()->view3DAction(command); + m_nodeInstanceServer->view3DAction(command); } void NodeInstanceView::edit3DViewResized(const QSize &size) const { - nodeInstanceServer()->update3DViewState(Update3dViewStateCommand(size)); + m_nodeInstanceServer->update3DViewState(Update3dViewStateCommand(size)); } void NodeInstanceView::timerEvent(QTimerEvent *event) diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp index 7eefa7f1bc..a26c59d792 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp @@ -176,39 +176,46 @@ PuppetCreator::PuppetCreator(ProjectExplorer::Target *target, const Model *model { } -QProcess *PuppetCreator::createPuppetProcess(const QString &puppetMode, - const QString &socketToken, - QObject *handlerObject, - const char *outputSlot, - const char *finishSlot, - const QStringList &customOptions) const +QProcessUniquePointer PuppetCreator::createPuppetProcess( + const QString &puppetMode, + const QString &socketToken, + QObject *handlerObject, + std::function<void()> processOutputCallback, + std::function<void(int, QProcess::ExitStatus)> processFinishCallback, + const QStringList &customOptions) const { return puppetProcess(qml2PuppetPath(m_availablePuppetType), qmlPuppetDirectory(m_availablePuppetType), puppetMode, socketToken, handlerObject, - outputSlot, - finishSlot, + processOutputCallback, + processFinishCallback, customOptions); } - -QProcess *PuppetCreator::puppetProcess(const QString &puppetPath, - const QString &workingDirectory, - const QString &puppetMode, - const QString &socketToken, - QObject *handlerObject, - const char *outputSlot, - const char *finishSlot, - const QStringList &customOptions) const +QProcessUniquePointer PuppetCreator::puppetProcess( + const QString &puppetPath, + const QString &workingDirectory, + const QString &puppetMode, + const QString &socketToken, + QObject *handlerObject, + std::function<void()> processOutputCallback, + std::function<void(int, QProcess::ExitStatus)> processFinishCallback, + const QStringList &customOptions) const { - auto puppetProcess = new QProcess; + QProcessUniquePointer puppetProcess{new QProcess}; puppetProcess->setObjectName(puppetMode); puppetProcess->setProcessEnvironment(processEnvironment()); - QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, puppetProcess, &QProcess::kill); - QObject::connect(puppetProcess, SIGNAL(finished(int,QProcess::ExitStatus)), handlerObject, finishSlot); + QObject::connect(QCoreApplication::instance(), + &QCoreApplication::aboutToQuit, + puppetProcess.get(), + &QProcess::kill); + QObject::connect(puppetProcess.get(), + static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), + handlerObject, + processFinishCallback); #ifndef QMLDESIGNER_TEST QString forwardOutput = m_designerSettings.value(DesignerSettingsKey:: @@ -218,7 +225,7 @@ QProcess *PuppetCreator::puppetProcess(const QString &puppetPath, #endif if (forwardOutput == puppetMode || forwardOutput == "all") { puppetProcess->setProcessChannelMode(QProcess::MergedChannels); - QObject::connect(puppetProcess, SIGNAL(readyRead()), handlerObject, outputSlot); + QObject::connect(puppetProcess.get(), &QProcess::readyRead, handlerObject, processOutputCallback); } puppetProcess->setWorkingDirectory(workingDirectory); diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h index bafea8fa3e..e6083d7014 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h @@ -25,6 +25,8 @@ #pragma once +#include "qprocessuniqueptr.h" + #include <QString> #include <QProcessEnvironment> @@ -53,12 +55,13 @@ public: void createQml2PuppetExecutableIfMissing(); - QProcess *createPuppetProcess(const QString &puppetMode, - const QString &socketToken, - QObject *handlerObject, - const char *outputSlot, - const char *finishSlot, - const QStringList &customOptions = {}) const; + QProcessUniquePointer createPuppetProcess( + const QString &puppetMode, + const QString &socketToken, + QObject *handlerObject, + std::function<void()> processOutputCallback, + std::function<void(int, QProcess::ExitStatus)> processFinishCallback, + const QStringList &customOptions = {}) const; void setQrcMappingString(const QString qrcMapping); @@ -82,14 +85,14 @@ protected: bool checkPuppetIsReady(const QString &puppetPath) const; bool qtIsSupported() const; - QProcess *puppetProcess(const QString &puppetPath, - const QString &workingDirectory, - const QString &puppetMode, - const QString &socketToken, - QObject *handlerObject, - const char *outputSlot, - const char *finishSlot, - const QStringList &customOptions) const; + QProcessUniquePointer puppetProcess(const QString &puppetPath, + const QString &workingDirectory, + const QString &puppetMode, + const QString &socketToken, + QObject *handlerObject, + std::function<void()> processOutputCallback, + std::function<void(int, QProcess::ExitStatus)> processFinishCallback, + const QStringList &customOptions) const; QProcessEnvironment processEnvironment() const; diff --git a/src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h b/src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h new file mode 100644 index 0000000000..b03c86f772 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QProcess> + +#include <memory> + +namespace QmlDesigner { + +class QProcessUniquePointerDeleter +{ +public: + void operator()(QProcess *process) + { + process->disconnect(); + QObject::connect(process, + QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), + process, + &QProcess::deleteLater); + + process->terminate(); + + process->deleteLater(); + } +}; + +using QProcessUniquePointer = std::unique_ptr<QProcess, QProcessUniquePointerDeleter>; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp index 3173a68abd..f5dc99bf35 100644 --- a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp +++ b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp @@ -27,23 +27,24 @@ #ifndef QMLDESIGNER_TEST +#include <abstractview.h> #include <componentaction.h> -#include <designmodewidget.h> +#include <componentview.h> #include <crumblebar.h> -#include <abstractview.h> -#include <rewriterview.h> -#include <nodeinstanceview.h> +#include <debugview.h> +#include <designeractionmanagerview.h> +#include <designmodewidget.h> +#include <edit3dview.h> +#include <formeditorview.h> +#include <importmanagerview.h> +#include <interactiveconnectionmanager.h> #include <itemlibraryview.h> #include <navigatorview.h> +#include <nodeinstanceview.h> +#include <propertyeditorview.h> +#include <rewriterview.h> #include <stateseditorview.h> -#include <edit3dview.h> -#include <formeditorview.h> #include <texteditorview.h> -#include <propertyeditorview.h> -#include <componentview.h> -#include <debugview.h> -#include <importmanagerview.h> -#include <designeractionmanagerview.h> #include <qmldesignerplugin.h> #include <utils/algorithm.h> @@ -59,10 +60,11 @@ static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData { public: + InteractiveConnectionManager connectionManager; QmlModelState savedState; Internal::DebugView debugView; DesignerActionManagerView designerActionManagerView; - NodeInstanceView nodeInstanceView; + NodeInstanceView nodeInstanceView{connectionManager}; ComponentView componentView; Edit3DView edit3DView; FormEditorView formEditorView; @@ -81,7 +83,7 @@ static CrumbleBar *crumbleBar() { } ViewManager::ViewManager() - : d(new ViewManagerData) + : d(std::make_unique<ViewManagerData>()) { d->formEditorView.setGotoErrorCallback([this](int line, int column) { d->textEditorView.gotoCursorPosition(line, column); @@ -92,10 +94,9 @@ ViewManager::ViewManager() ViewManager::~ViewManager() { - foreach (const QPointer<AbstractView> &view, d->additionalViews) + for (const QPointer<AbstractView> &view : d->additionalViews) delete view.data(); - delete d; } DesignDocument *ViewManager::currentDesignDocument() const diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index dceaf8656d..971bcffb47 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -321,6 +321,17 @@ Project { "instances/puppetdialog.cpp", "instances/puppetdialog.h", "instances/puppetdialog.ui", + "instances/connectionmanagerinterface.cpp", + "instances/connectionmanagerinterface.h", + "instances/baseconnectionmanager.cpp ", + "instances/baseconnectionmanager.h", + "instances/connectionmanager.cpp", + "instances/connectionmanager.h", + "instances/capturingconnectionmanager.cpp", + "instances/capturingconnectionmanager.h", + "instances/interactiveconnectionmanager.cpp", + "instances/interactiveconnectionmanager.h", + "instances/qprocessuniqueptr.h", "metainfo/itemlibraryinfo.cpp", "metainfo/metainfo.cpp", "metainfo/metainforeader.cpp", diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 9d90fd16c9..d5a6b39e06 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -52,6 +52,7 @@ extend_qtc_executable(qml2puppet inputeventcommand.cpp inputeventcommand.h view3dactioncommand.cpp view3dactioncommand.h valueschangedcommand.cpp + captureddatacommand.cpp captureddatacommand.h ) extend_qtc_executable(qml2puppet @@ -156,6 +157,7 @@ extend_qtc_executable(qml2puppet quick3dnodeinstance.cpp quick3dnodeinstance.h quickitemnodeinstance.cpp quickitemnodeinstance.h servernodeinstance.cpp servernodeinstance.h + qt5capturenodeinstanceserver.cpp qt5capturenodeinstanceserver.h ) extend_qtc_executable(qml2puppet diff --git a/src/tools/qml2puppet/qml2puppet.qbs b/src/tools/qml2puppet/qml2puppet.qbs index 485534008c..b30ce11dc8 100644 --- a/src/tools/qml2puppet/qml2puppet.qbs +++ b/src/tools/qml2puppet/qml2puppet.qbs @@ -107,6 +107,8 @@ QtcTool { "commands/inputeventcommand.h", "commands/view3dactioncommand.cpp", "commands/view3dactioncommand.h", + "commands/captureddatacommand.cpp", + "commands/captureddatacommand.h", "container/addimportcontainer.cpp", "container/addimportcontainer.h", "container/idcontainer.cpp", @@ -207,6 +209,8 @@ QtcTool { "instances/qt5testnodeinstanceserver.h", "instances/servernodeinstance.cpp", "instances/servernodeinstance.h", + "instances/qt5capturenodeinstanceserver.cpp", + "instances/qt5capturenodeinstanceserver.h", "editor3d/generalhelper.cpp", "editor3d/generalhelper.h", "editor3d/mousearea3d.cpp", diff --git a/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt b/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt index 83f9da98a0..fb22fbf06b 100644 --- a/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt +++ b/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt @@ -17,4 +17,5 @@ add_qtc_test(tst_qml_testcore ../testview.cpp ../testview.h testrewriterview.cpp testrewriterview.h tst_testcore.cpp tst_testcore.h + ../testconnectionmanager.cpp ../testconnectionmanager.h ) diff --git a/tests/auto/qml/qmldesigner/coretests/coretests.pro b/tests/auto/qml/qmldesigner/coretests/coretests.pro index dd0f071b0e..fb0437b69f 100644 --- a/tests/auto/qml/qmldesigner/coretests/coretests.pro +++ b/tests/auto/qml/qmldesigner/coretests/coretests.pro @@ -60,11 +60,13 @@ TEMPLATE = app SOURCES += \ ../testview.cpp \ testrewriterview.cpp \ - tst_testcore.cpp + tst_testcore.cpp \ + ../testconnectionmanager.cpp HEADERS += \ ../testview.h \ testrewriterview.h \ - tst_testcore.h + tst_testcore.h \ + ../testconnectionmanager.h RESOURCES += ../data/testfiles.qrc diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 52fa6f519f..e843ad7a3d 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -46,11 +46,12 @@ #include <stylesheetmerger.h> #include <QDebug> +#include "../testconnectionmanager.h" #include "../testview.h" -#include <variantproperty.h> #include <abstractproperty.h> #include <bindingproperty.h> #include <nodeproperty.h> +#include <variantproperty.h> #include <nodelistproperty.h> #include <nodeabstractproperty.h> @@ -1999,8 +2000,9 @@ void tst_TestCore::testModelRemoveNode() QVERIFY(view.data()); model->attachView(view.data()); - NodeInstanceView *nodeInstanceView = new NodeInstanceView(model.data(), NodeInstanceServerInterface::TestModus); - model->attachView(nodeInstanceView); + TestConnectionManager connectionManager; + NodeInstanceView nodeInstanceView{connectionManager}; + model->attachView(&nodeInstanceView); QCOMPARE(view->rootModelNode().directSubModelNodes().count(), 0); @@ -2051,7 +2053,7 @@ void tst_TestCore::testModelRemoveNode() childNode = view->createModelNode("QtQuick.Item", 1, 1); childNode.destroy(); - model->detachView(nodeInstanceView); + model->detachView(&nodeInstanceView); } void tst_TestCore::reparentingNode() @@ -6140,17 +6142,21 @@ void tst_TestCore::testInstancesAttachToExistingModel() // Attach NodeInstanceView - QScopedPointer<NodeInstanceView> instanceView(new NodeInstanceView(0, NodeInstanceServerInterface::TestModus)); - QVERIFY(instanceView.data()); - model->attachView(instanceView.data()); + TestConnectionManager connectionManager; + + NodeInstanceView instanceView{connectionManager}; - NodeInstance rootInstance = instanceView->instanceForModelNode(rootNode); - NodeInstance rectangleInstance = instanceView->instanceForModelNode(rectangleNode); + model->attachView(&instanceView); + + NodeInstance rootInstance = instanceView.instanceForModelNode(rootNode); + NodeInstance rectangleInstance = instanceView.instanceForModelNode(rectangleNode); QVERIFY(rootInstance.isValid()); QVERIFY(rectangleInstance.isValid()); QCOMPARE(QVariant(100), rectangleInstance.property("width")); QVERIFY(rootInstance.instanceId() >= 0); QVERIFY(rectangleInstance.instanceId() >= 0); + + model->detachView(&instanceView); } void tst_TestCore::testQmlModelAddMultipleStates() diff --git a/tests/auto/qml/qmldesigner/testconnectionmanager.cpp b/tests/auto/qml/qmldesigner/testconnectionmanager.cpp new file mode 100644 index 0000000000..f637fb1f1a --- /dev/null +++ b/tests/auto/qml/qmldesigner/testconnectionmanager.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "testconnectionmanager.h" +#include "synchronizecommand.h" + +#include <QLocalSocket> + +namespace QmlDesigner { + +TestConnectionManager::TestConnectionManager() +{ + m_connections.emplace_back("Editor", "editormode"); +} + +void TestConnectionManager::writeCommand(const QVariant &command) +{ + TestConnectionManager::writeCommand(command); + + m_writeCommandCounter++; + + static int synchronizeId = 0; + synchronizeId++; + SynchronizeCommand synchronizeCommand(synchronizeId); + + QLocalSocket *socket = m_connections.front().socket.get(); + + writeCommandToIODevice(QVariant::fromValue(synchronizeCommand), socket, m_writeCommandCounter); + m_writeCommandCounter++; + + while (socket->waitForReadyRead(100)) { + readDataStream(m_connections.front()); + if (m_synchronizeId == synchronizeId) + return; + } +} + +void TestConnectionManager::dispatchCommand(const QVariant &command, Connection &connection) +{ + static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand"); + + if (command.userType() == synchronizeCommandType) { + SynchronizeCommand synchronizeCommand = command.value<SynchronizeCommand>(); + m_synchronizeId = synchronizeCommand.synchronizeId(); + } else { + ConnectionManager::dispatchCommand(command, connection); + } +} + +} // namespace QmlDesigner diff --git a/tests/auto/qml/qmldesigner/testconnectionmanager.h b/tests/auto/qml/qmldesigner/testconnectionmanager.h new file mode 100644 index 0000000000..6ea44c1c1d --- /dev/null +++ b/tests/auto/qml/qmldesigner/testconnectionmanager.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <connectionmanager.h> + +#include <QFile> +#include <QPointer> +#include <QProcess> +#include <QTimer> + +QT_BEGIN_NAMESPACE +class QLocalServer; +class QLocalSocket; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class TestConnectionManager final : public ConnectionManager +{ +public: + TestConnectionManager(); + + void writeCommand(const QVariant &command) override; + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + int m_synchronizeId = -1; +}; + +} // namespace QmlDesigner |