aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Jenssen <tim.jenssen@qt.io>2020-08-13 08:34:24 +0200
committerTim Jenssen <tim.jenssen@qt.io>2020-08-13 06:39:40 +0000
commit4fe5b923cddc9110094a883b5b7dd3051d8c8d90 (patch)
tree34e3984ab983882423bc563542a041b99b35f065
parent24ca17f714ec133acc2bf0fd93c051ef730d69dc (diff)
parentdc870f538dd3e11464a3ece179edf024161e14ba (diff)
Merge remote-tracking branch 'origin/qds-1.59' into 4.13
-rwxr-xr-xscripts/createDevPackage.py1
-rw-r--r--share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h107
-rw-r--r--share/qtcreator/qml/qmlpuppet/commands/commands.pri135
-rw-r--r--share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.cpp27
-rw-r--r--share/qtcreator/qml/qmlpuppet/instances/nodeinstanceclientproxy.h7
-rw-r--r--share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceclientinterface.h2
-rw-r--r--share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.cpp6
-rw-r--r--share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri102
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp21
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h4
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.cpp105
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturenodeinstanceserver.h45
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp14
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp8
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h8
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml63
-rw-r--r--src/libs/advanceddockingsystem/elidinglabel.cpp2
-rw-r--r--src/libs/sqlite/sqlite-lib.pri2
-rw-r--r--src/libs/utils/smallstringio.h7
-rw-r--r--src/plugins/projectexplorer/project.cpp15
-rw-r--r--src/plugins/projectexplorer/project.h2
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt6
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h7
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp7
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp5
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp32
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp34
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp14
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.h3
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.cpp5
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp28
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h12
-rw-r--r--src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp51
-rw-r--r--src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h4
-rw-r--r--src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp132
-rw-r--r--src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h30
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp2
-rw-r--r--src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri6
-rw-r--r--src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.cpp88
-rw-r--r--src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.h65
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodeinstanceview.h18
-rw-r--r--src/plugins/qmldesigner/designercore/include/viewmanager.h2
-rw-r--r--src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp127
-rw-r--r--src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h76
-rw-r--r--src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp65
-rw-r--r--src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h46
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp178
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.h77
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.cpp55
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h81
-rw-r--r--src/plugins/qmldesigner/designercore/instances/instances.pri29
-rw-r--r--src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp110
-rw-r--r--src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h51
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp504
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h56
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp150
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp57
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetcreator.h31
-rw-r--r--src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h53
-rw-r--r--src/plugins/qmldesigner/designercore/model/viewmanager.cpp31
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.qbs12
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp38
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h3
-rw-r--r--src/plugins/qmlpreview/CMakeLists.txt2
-rw-r--r--src/plugins/qmlpreview/projectfileselectionswidget.cpp159
-rw-r--r--src/plugins/qmlpreview/projectfileselectionswidget.h50
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationclient.cpp8
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationclient.h1
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationwidget.cpp452
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationwidget.h95
-rw-r--r--src/plugins/qmlpreview/qmlpreview.pro8
-rw-r--r--src/plugins/qmlpreview/qmlpreview.qbs4
-rw-r--r--src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp64
-rw-r--r--src/plugins/qmlpreview/qmlpreviewconnectionmanager.h2
-rw-r--r--src/plugins/qmlpreview/qmlpreviewplugin.cpp93
-rw-r--r--src/plugins/qmlpreview/qmlpreviewplugin.h5
-rw-r--r--src/plugins/qmlpreview/qmlpreviewruncontrol.cpp16
-rw-r--r--src/plugins/qmlpreview/qmlpreviewruncontrol.h4
-rw-r--r--src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp51
-rw-r--r--src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h13
-rw-r--r--src/plugins/qmlprojectmanager/qmlproject.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp5
-rw-r--r--src/tools/qml2puppet/CMakeLists.txt2
-rw-r--r--src/tools/qml2puppet/qml2puppet.qbs3
-rw-r--r--tests/auto/qml/qmldesigner/coretests/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmldesigner/coretests/coretests.pro6
-rw-r--r--tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp24
-rw-r--r--tests/auto/qml/qmldesigner/testconnectionmanager.cpp72
-rw-r--r--tests/auto/qml/qmldesigner/testconnectionmanager.h56
-rw-r--r--tests/unit/unittest/listmodeleditor-test.cpp461
93 files changed, 3686 insertions, 1048 deletions
diff --git a/scripts/createDevPackage.py b/scripts/createDevPackage.py
index 1c8b85d143..f6613b3830 100755
--- a/scripts/createDevPackage.py
+++ b/scripts/createDevPackage.py
@@ -59,6 +59,7 @@ source_include_patterns = [
# directories
r"^(?!(share|tests)/.*$)(.*/)?$", # look into all directories except under share/ and tests/
r"^share/(qtcreator/(qml/(qmlpuppet/(.*/)?)?)?)?$", # for shared headers for qt quick designer plugins
+ r"^src/plugins/help/qlitehtml/.*\.(h|pri|cpp|c|txt|md)$", # litehtml is used by extra plugins
# files
r"^HACKING$",
r"^LICENSE.*$",
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 7072196437..b57e3aab8f 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
@@ -5,54 +5,56 @@ 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/quick3dtexturenodeinstance.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 \
+ $$PWD/quick3dtexturenodeinstance.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/quick3dtexturenodeinstance.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 \
+ $$PWD/quick3dtexturenodeinstance.cpp
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
index 1b48a9ae14..ce5cf29953 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 b0dc39a2a2..140e216049 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;
@@ -277,7 +279,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 852ef16bad..810c8f0dcf 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();
handleExtraRender();
@@ -105,7 +106,7 @@ QImage Qt5PreviewNodeInstanceServer::renderPreviewImage()
QSize previewImageSize = boundingRect.size().toSize();
- if (!m_previewSize.isNull())
+ if (m_previewSize.isValid() && !m_previewSize.isNull())
previewImageSize.scale(m_previewSize, Qt::KeepAspectRatio);
QImage previewImage = rootNodeInstance().renderPreviewImage(previewImageSize);
@@ -124,9 +125,6 @@ void Qt5PreviewNodeInstanceServer::changePreviewImageSize(
{
m_previewSize = command.size;
- if (!command.size.isValid())
- m_previewSize = {160, 160};
-
collectItemChangesAndSendChangeCommands();
}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
index 10943a676a..ea27429c66 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
@@ -72,6 +72,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);
@@ -171,6 +172,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);
@@ -197,7 +200,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);
@@ -206,10 +208,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/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml
index 6c5e6fde86..6132cfd261 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml
@@ -26,6 +26,8 @@
import QtQuick 2.1
import HelperWidgets 2.0
import QtQuick.Layouts 1.0
+import StudioControls 1.0 as StudioControls
+import StudioTheme 1.0 as StudioTheme
Section {
anchors.left: parent.left
@@ -46,9 +48,29 @@ Section {
Label {
text: qsTr("Text")
}
- LineEdit {
- backendValue: backendValues.text
- Layout.fillWidth: true
+
+ RowLayout {
+ LineEdit {
+ backendValue: backendValues.text
+ Layout.fillWidth: true
+ }
+
+ StudioControls.AbstractButton {
+ id: richTextEditorButton
+ buttonIcon: StudioTheme.Constants.textAlignTop
+ onClicked: {
+ richTextDialogLoader.show()
+ }
+ }
+
+ RichTextEditor{
+ onRejected: {
+ hideWidget()
+ }
+ onAccepted: {
+ hideWidget()
+ }
+ }
}
Label {
@@ -219,4 +241,39 @@ Section {
Layout.fillWidth: true
}
}
+
+ Loader {
+ id: richTextDialogLoader
+
+ visible: false
+ active: visible
+
+ function show() {
+ richTextDialogLoader.visible = true
+ }
+
+ sourceComponent: Item {
+ id: richTextEditorParent
+
+ Component.onCompleted: {
+ richTextEditor.showWidget()
+ richTextEditor.richText = backendValues.text.value
+ }
+
+ RichTextEditor {
+ id: richTextEditor
+
+ onRejected: {
+ hideWidget()
+ richTextDialogLoader.visible = false
+ }
+ onAccepted: {
+ backendValues.text.value = richTextEditor.richText
+ backendValues.textFormat.setEnumeration("Text", "RichText")
+ hideWidget()
+ richTextDialogLoader.visible = false
+ }
+ }
+ }
+ }
}
diff --git a/src/libs/advanceddockingsystem/elidinglabel.cpp b/src/libs/advanceddockingsystem/elidinglabel.cpp
index 75808ff9c1..d58494b7e1 100644
--- a/src/libs/advanceddockingsystem/elidinglabel.cpp
+++ b/src/libs/advanceddockingsystem/elidinglabel.cpp
@@ -1,4 +1,4 @@
-/****************************************************************************
+/****************************************************************************
**
** Copyright (C) 2020 Uwe Kindler
** Contact: https://www.qt.io/licensing/
diff --git a/src/libs/sqlite/sqlite-lib.pri b/src/libs/sqlite/sqlite-lib.pri
index 1ab7631387..8ded8ca106 100644
--- a/src/libs/sqlite/sqlite-lib.pri
+++ b/src/libs/sqlite/sqlite-lib.pri
@@ -67,3 +67,5 @@ CONFIG(debug, debug|release): DEFINES += SQLITE_ENABLE_API_ARMOR
OTHER_FILES += README.md
contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols
+
+CONFIG += exceptions
diff --git a/src/libs/utils/smallstringio.h b/src/libs/utils/smallstringio.h
index 233b9b5d97..308b44f155 100644
--- a/src/libs/utils/smallstringio.h
+++ b/src/libs/utils/smallstringio.h
@@ -78,12 +78,7 @@ QDebug &operator<<(QDebug &debug, const String &string)
template <uint Size>
std::ostream &operator<<(std::ostream &out, const BasicSmallString<Size> &string)
{
- BasicSmallString<Size> formatedString = string.clone();
-
- formatedString.replace("\n", "\\n");
- formatedString.replace("\t", "\\t");
-
- out.write(formatedString.data(), std::streamsize(formatedString.size()));
+ out.write(string.data(), std::streamsize(string.size()));
return out;
}
diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp
index d808ca8d7f..03e24ee3af 100644
--- a/src/plugins/projectexplorer/project.cpp
+++ b/src/plugins/projectexplorer/project.cpp
@@ -1000,6 +1000,21 @@ QVariant Project::extraData(const QString &key) const
return d->m_extraData.value(key);
}
+QStringList Project::availableQmlPreviewTranslations(QString *errorMessage)
+{
+ const auto projectDirectory = rootProjectDirectory().toFileInfo().absoluteFilePath();
+ const QDir languageDirectory(projectDirectory + "/i18n");
+ const auto qmFiles = languageDirectory.entryList({"qml_*.qm"});
+ if (qmFiles.isEmpty() && errorMessage)
+ errorMessage->append(tr("Could not find any qml_*.qm file at '%1'").arg(languageDirectory.absolutePath()));
+ return Utils::transform(qmFiles, [](const QString &qmFile) {
+ const int localeStartPosition = qmFile.lastIndexOf("_") + 1;
+ const int localeEndPosition = qmFile.size() - QString(".qm").size();
+ const QString locale = qmFile.left(localeEndPosition).mid(localeStartPosition);
+ return locale;
+ });
+}
+
#if defined(WITH_TESTS)
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h
index f03fe6c7c9..8d12e4c15c 100644
--- a/src/plugins/projectexplorer/project.h
+++ b/src/plugins/projectexplorer/project.h
@@ -170,6 +170,8 @@ public:
void setExtraData(const QString &key, const QVariant &data);
QVariant extraData(const QString &key) const;
+ QStringList availableQmlPreviewTranslations(QString *errorMessage);
+
signals:
void projectFileIsDirty(const Utils::FilePath &path);
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 6f7cd737cf..5c6479653d 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/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
index a1c0e2181c..5f09b74860 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
@@ -45,14 +45,19 @@ const char DocumentNameTag[] = "name";
// Layer data tags
const char ArtboardListTag[] = "artboards";
+const char NameTag[] = "name";
+
const char XPosTag[] = "x";
const char YPosTag[] = "y";
const char WidthTag[] = "width";
const char HeightTag[] = "height";
-
+const char MetadataTag[] = "metadata";
+const char ChildrenTag[] = "children";
const char QmlIdTag[] = "qmlId";
const char ExportTypeTag[] = "exportType";
+const char ExportTypeComponent[] = "component";
+const char ExportTypeChild[] = "child";
const char QmlPropertiesTag[] = "qmlProperties";
const char ImportsTag[] = "extraImports";
const char UuidTag[] = "uuid";
diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp
index 059b6ecb16..973cb6e013 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp
@@ -53,6 +53,7 @@ static void populateLineage(const QmlDesigner::ModelNode &node, QByteArrayList &
}
namespace QmlDesigner {
+using namespace Constants;
std::vector<std::unique_ptr<Internal::NodeParserCreatorBase>> Component::m_readers;
Component::Component(AssetExporter &exporter, const ModelNode &rootNode):
@@ -76,6 +77,10 @@ void Component::exportComponent()
{
QTC_ASSERT(m_rootNode.isValid(), return);
m_json = nodeToJson(m_rootNode);
+ // Change the export type to component
+ QJsonObject metadata = m_json.value(MetadataTag).toObject();
+ metadata.insert(ExportTypeTag, ExportTypeComponent);
+ m_json.insert(MetadataTag, metadata);
addImports();
}
@@ -124,7 +129,7 @@ QJsonObject Component::nodeToJson(const ModelNode &node)
children.append(nodeToJson(childnode));
if (!children.isEmpty())
- jsonObject.insert("children", children);
+ jsonObject.insert(ChildrenTag, children);
return jsonObject;
}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp
index a42b730062..adc46678ae 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp
@@ -58,7 +58,10 @@ QJsonObject AssetNodeParser::json(Component &component) const
Utils::FilePath assetPath = component.exporter().exportAsset(objectNode(), uuid());
QJsonObject assetData;
assetData.insert(AssetPathTag, assetPath.toString());
- jsonObject.insert(AssetDataTag, assetData);
+
+ QJsonObject metadata = jsonObject.value(MetadataTag).toObject();
+ metadata.insert(AssetDataTag, assetData);
+ jsonObject.insert(MetadataTag, metadata);
return jsonObject;
}
}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp
index 5104732e1c..43963aa8b2 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp
@@ -28,6 +28,17 @@
#include "qmlitemnode.h"
+namespace {
+static QString capitalize(const QString &str)
+{
+ if (str.isEmpty())
+ return {};
+ QString tmp = str;
+ tmp[0] = QChar(str[0]).toUpper().toLatin1();
+ return tmp;
+}
+}
+
namespace QmlDesigner {
using namespace Constants;
ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage,
@@ -47,10 +58,16 @@ QJsonObject QmlDesigner::ItemNodeParser::json(QmlDesigner::Component &component)
Q_UNUSED(component);
const QmlObjectNode &qmlObjectNode = objectNode();
QJsonObject jsonObject;
- jsonObject.insert(QmlIdTag, qmlObjectNode.id());
- QmlItemNode itemNode = qmlObjectNode.toQmlItemNode();
+
+ const QString qmlId = qmlObjectNode.id();
+ QString name = m_node.simplifiedTypeName();
+ if (!qmlId.isEmpty())
+ name.append("_" + capitalize(qmlId));
+
+ jsonObject.insert(NameTag, name);
// Position relative to parent
+ QmlItemNode itemNode = qmlObjectNode.toQmlItemNode();
QPointF pos = itemNode.instancePosition();
jsonObject.insert(XPosTag, pos.x());
jsonObject.insert(YPosTag, pos.y());
@@ -60,10 +77,13 @@ QJsonObject QmlDesigner::ItemNodeParser::json(QmlDesigner::Component &component)
jsonObject.insert(WidthTag, size.width());
jsonObject.insert(HeightTag, size.height());
- jsonObject.insert(UuidTag, uuid());
- jsonObject.insert(ExportTypeTag, "child");
- jsonObject.insert(TypeNameTag, QString::fromLatin1(m_node.type()));
+ QJsonObject metadata;
+ metadata.insert(QmlIdTag, qmlId);
+ metadata.insert(UuidTag, uuid());
+ metadata.insert(ExportTypeTag, ExportTypeChild);
+ metadata.insert(TypeNameTag, QString::fromLatin1(m_node.type()));
- return jsonObject;
+ jsonObject.insert(MetadataTag, metadata);
+ return jsonObject;
}
}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp
index bffe5ed8d5..9b797ad77d 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp
@@ -81,7 +81,9 @@ QJsonObject TextNodeParser::json(Component &component) const
textDetails.insert(IsMultilineTag, propertyValue("wrapMode").toString().compare("NoWrap") != 0);
- jsonObject.insert(TextDetailsTag, textDetails);
+ QJsonObject metadata = jsonObject.value(MetadataTag).toObject();
+ metadata.insert(TextDetailsTag, textDetails);
+ jsonObject.insert(MetadataTag, metadata);
return jsonObject;
}
}
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index cbc987651a..feff075582 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -363,32 +363,30 @@ public:
bool isEnabled(const SelectionContext &) const override { return true; }
- static ModelNode listModelNode(const ModelNode &listViewNode)
- {
- if (listViewNode.hasProperty("model")) {
- if (listViewNode.hasBindingProperty("model"))
- return listViewNode.bindingProperty("model").resolveToModelNode();
- else if (listViewNode.hasNodeProperty("model"))
- return listViewNode.nodeProperty("model").modelNode();
- }
-
- ModelNode newModel = listViewNode.view()->createModelNode("QtQml.Models.ListModel", 2, 15);
- listViewNode.nodeProperty("mode").reparentHere(newModel);
-
- return newModel;
- }
-
static void openDialog(const SelectionContext &selectionState)
{
- ListModelEditorModel model;
-
ModelNode targetNode = selectionState.targetNode();
if (!targetNode.isValid())
targetNode = selectionState.currentSingleSelectedNode();
if (!targetNode.isValid())
return;
- model.setListModel(listModelNode(targetNode));
+ AbstractView *view = targetNode.view();
+ NodeMetaInfo modelMetaInfo = view->model()->metaInfo("ListModel");
+ NodeMetaInfo elementMetaInfo = view->model()->metaInfo("ListElement");
+
+ ListModelEditorModel model{[&] {
+ return view->createModelNode(modelMetaInfo.typeName(),
+ modelMetaInfo.majorVersion(),
+ modelMetaInfo.minorVersion());
+ },
+ [&] {
+ return view->createModelNode(elementMetaInfo.typeName(),
+ elementMetaInfo.majorVersion(),
+ elementMetaInfo.minorVersion());
+ }};
+
+ model.setListView(targetNode);
ListModelEditorDialog dialog{Core::ICore::mainWindow()};
dialog.setModel(&model);
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
index 6a4acacb97..b621dacb4a 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
@@ -283,6 +283,11 @@ bool FormEditorItem::flowHitTest(const QPointF & ) const
return false;
}
+void FormEditorItem::setFrameColor(const QColor &color)
+{
+ m_frameColor = color;
+}
+
FormEditorItem::~FormEditorItem()
{
scene()->removeItemFromHash(this);
@@ -313,14 +318,21 @@ void FormEditorItem::paintBoundingRect(QPainter *painter) const
pen.setJoinStyle(Qt::MiterJoin);
const QColor frameColor(0xaa, 0xaa, 0xaa);
- static const QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static const QColor selectionColor = Utils::creatorTheme()->color(
+ Utils::Theme::QmlDesigner_FormEditorSelectionColor);
if (scene()->showBoundingRects()) {
pen.setColor(frameColor.darker(150));
pen.setStyle(Qt::DotLine);
painter->setPen(pen);
painter->drawRect(m_boundingRect.adjusted(0., 0., -1., -1.));
+ }
+ if (m_frameColor.isValid()) {
+ pen.setColor(m_frameColor);
+ pen.setStyle(Qt::SolidLine);
+ painter->setPen(pen);
+ painter->drawRect(m_boundingRect.adjusted(0., 0., -1., -1.));
}
if (m_highlightBoundingRect) {
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
index 61ade49916..37e34fc4f7 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
@@ -117,6 +117,8 @@ public:
virtual bool flowHitTest(const QPointF &point) const;
+ void setFrameColor(const QColor &color);
+
protected:
AbstractFormEditorTool* tool() const;
void paintBoundingRect(QPainter *painter) const;
@@ -129,6 +131,7 @@ protected:
QRectF m_boundingRect;
QRectF m_paintedBoundingRect;
QRectF m_selectionBoundingRect;
+ QColor m_frameColor{0xaa, 0xaa, 0xaa};
private: // functions
void setup();
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
index 6eec3f933c..96dda07c51 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
@@ -633,6 +633,11 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node, const PropertyN
editorItem->update();
}
}
+
+ if (name == "FrameColor@Internal") {
+ if (FormEditorItem *editorItem = scene()->itemForQmlItemNode(item))
+ editorItem->setFrameColor(data.value<QColor>());
+ }
}
void FormEditorView::instancesCompleted(const QVector<ModelNode> &completedNodeList)
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/components/listmodeleditor/listmodeleditordialog.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp
index 283acab78b..0cf73976a8 100644
--- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp
+++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp
@@ -27,6 +27,7 @@
#include "listmodeleditormodel.h"
#include <theme.h>
+#include <qmldesignericons.h>
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
@@ -71,6 +72,10 @@ ListModelEditorDialog::ListModelEditorDialog(QWidget *parent)
m_addColumnAction = toolBar->addAction(getIcon(Theme::Icon::addColumnAfter), tr("Add Column"));
m_removeColumnsAction = toolBar->addAction(getIcon(Theme::Icon::deleteColumn),
tr("Remove Columns"));
+ m_moveDownAction = toolBar->addAction(Icons::ARROW_DOWN.icon(), tr("Move down (CTRL + Down)."));
+ m_moveDownAction->setShortcut(QKeySequence(Qt::Key_Down | Qt::CTRL));
+ m_moveUpAction = toolBar->addAction(Icons::ARROW_UP.icon(), tr("Move up (CTRL + Up)."));
+ m_moveDownAction->setShortcut(QKeySequence(Qt::Key_Up | Qt::CTRL));
}
ListModelEditorDialog::~ListModelEditorDialog() = default;
@@ -83,6 +88,8 @@ void ListModelEditorDialog::setModel(ListModelEditorModel *model)
connect(m_addColumnAction, &QAction::triggered, this, &ListModelEditorDialog::openColumnDialog);
connect(m_removeRowsAction, &QAction::triggered, this, &ListModelEditorDialog::removeRows);
connect(m_removeColumnsAction, &QAction::triggered, this, &ListModelEditorDialog::removeColumns);
+ connect(m_moveDownAction, &QAction::triggered, this, &ListModelEditorDialog::moveRowsDown);
+ connect(m_moveUpAction, &QAction::triggered, this, &ListModelEditorDialog::moveRowsUp);
connect(m_tableView->horizontalHeader(),
&QHeaderView::sectionDoubleClicked,
this,
@@ -114,40 +121,12 @@ void ListModelEditorDialog::openColumnDialog()
void ListModelEditorDialog::removeRows()
{
- const QList<QModelIndex> indices = m_tableView->selectionModel()->selectedRows();
- std::vector<int> rows;
- rows.reserve(indices.size());
-
- for (QModelIndex index : indices)
- rows.push_back(index.row());
-
- std::sort(rows.begin(), rows.end());
-
- rows.erase(std::unique(rows.begin(), rows.end()), rows.end());
-
- std::reverse(rows.begin(), rows.end());
-
- for (int row : rows)
- m_model->removeRow(row);
+ m_model->removeRows(m_tableView->selectionModel()->selectedRows());
}
void ListModelEditorDialog::removeColumns()
{
- const QList<QModelIndex> indices = m_tableView->selectionModel()->selectedColumns();
- std::vector<int> columns;
- columns.reserve(indices.size());
-
- for (QModelIndex index : indices)
- columns.push_back(index.column());
-
- std::sort(columns.begin(), columns.end());
-
- columns.erase(std::unique(columns.begin(), columns.end()), columns.end());
-
- std::reverse(columns.begin(), columns.end());
-
- for (int row : columns)
- m_model->removeColumn(row);
+ m_model->removeColumns(m_tableView->selectionModel()->selectedColumns());
}
void ListModelEditorDialog::changeHeader(int column)
@@ -162,4 +141,16 @@ void ListModelEditorDialog::changeHeader(int column)
m_model->renameColumn(column, newPropertyName);
}
+void ListModelEditorDialog::moveRowsDown()
+{
+ QItemSelection selection = m_model->moveRowsDown(m_tableView->selectionModel()->selectedRows());
+ m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select);
+}
+
+void ListModelEditorDialog::moveRowsUp()
+{
+ QItemSelection selection = m_model->moveRowsUp(m_tableView->selectionModel()->selectedRows());
+ m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select);
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h
index 519d0869fa..24e19c8ff9 100644
--- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h
+++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h
@@ -59,6 +59,8 @@ private:
void removeRows();
void removeColumns();
void changeHeader(int column);
+ void moveRowsDown();
+ void moveRowsUp();
private:
ListModelEditorModel *m_model{};
@@ -66,6 +68,8 @@ private:
QAction *m_removeRowsAction{};
QAction *m_addColumnAction{};
QAction *m_removeColumnsAction{};
+ QAction *m_moveUpAction{};
+ QAction *m_moveDownAction{};
QTableView *m_tableView{};
};
diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
index 98722c3e8f..b73a53f76c 100644
--- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
+++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
@@ -26,7 +26,9 @@
#include "listmodeleditormodel.h"
#include <abstractview.h>
+#include <bindingproperty.h>
#include <nodelistproperty.h>
+#include <nodeproperty.h>
#include <variantproperty.h>
#include <QVariant>
@@ -49,6 +51,17 @@ public:
QVariant maybeConvertToNumber(const QVariant &value)
{
+ if (value.type() == QVariant::Bool)
+ return value;
+
+ if (value.type() == QVariant::String) {
+ const QString text = value.toString();
+ if (text == "true")
+ return QVariant(true);
+ if (text == "false")
+ return QVariant(false);
+ }
+
bool canConvert = false;
double convertedValue = value.toDouble(&canConvert);
if (canConvert) {
@@ -185,6 +198,22 @@ void renameProperties(const QStandardItemModel *model,
static_cast<ListModelItem *>(model->item(rowIndex, columnIndex))->renameProperty(newPropertyName);
}
+ModelNode listModelNode(const ModelNode &listViewNode,
+ const std::function<ModelNode()> &createModelCallback)
+{
+ if (listViewNode.hasProperty("model")) {
+ if (listViewNode.hasBindingProperty("model"))
+ return listViewNode.bindingProperty("model").resolveToModelNode();
+ else if (listViewNode.hasNodeProperty("model"))
+ return listViewNode.nodeProperty("model").modelNode();
+ }
+
+ ModelNode newModel = createModelCallback();
+ listViewNode.nodeProperty("model").reparentHere(newModel);
+
+ return newModel;
+}
+
} // namespace
void ListModelEditorModel::populateModel()
@@ -214,9 +243,20 @@ void ListModelEditorModel::appendItems(const ModelNode &listElementNode)
appendRow(row);
}
+void ListModelEditorModel::setListModel(ModelNode node)
+{
+ m_listModelNode = node;
+ populateModel();
+}
+
+void ListModelEditorModel::setListView(ModelNode listView)
+{
+ setListModel(listModelNode(listView, m_createModelCallback));
+}
+
void ListModelEditorModel::addRow()
{
- auto newElement = m_listModelNode.view()->createModelNode("QtQml.Models.ListElement", 2, 15);
+ auto newElement = m_createElementCallback();
m_listModelNode.defaultNodeListProperty().reparentHere(newElement);
appendItems(newElement);
@@ -260,6 +300,26 @@ void ListModelEditorModel::removeColumn(int column)
}
}
+void ListModelEditorModel::removeColumns(const QList<QModelIndex> &indices)
+{
+ std::vector<int> columns = filterColumns(indices);
+
+ std::reverse(columns.begin(), columns.end());
+
+ for (int column : columns)
+ removeColumn(column);
+}
+
+void ListModelEditorModel::removeRows(const QList<QModelIndex> &indices)
+{
+ std::vector<int> rows = filterRows(indices);
+
+ std::reverse(rows.begin(), rows.end());
+
+ for (int row : rows)
+ removeRow(row);
+}
+
void ListModelEditorModel::removeRow(int row)
{
QList<QStandardItem *> rowItems = QStandardItemModel::takeRow(row);
@@ -299,4 +359,74 @@ void ListModelEditorModel::renameColumn(int oldColumn, const QString &newColumnN
setHorizontalHeaderLabels(convertToStringList(m_propertyNames));
}
+QItemSelection ListModelEditorModel::moveRowsUp(const QList<QModelIndex> &indices)
+{
+ std::vector<int> rows = filterRows(indices);
+
+ if (rows.empty() || rows.front() < 1)
+ return {};
+
+ auto nodeListProperty = m_listModelNode.defaultNodeListProperty();
+
+ for (int row : rows) {
+ insertRow(row - 1, takeRow(row));
+ nodeListProperty.slide(row, row - 1);
+ }
+
+ return {index(rows.front() - 1, 0), index(rows.back() - 1, columnCount() - 1)};
+}
+
+QItemSelection ListModelEditorModel::moveRowsDown(const QList<QModelIndex> &indices)
+{
+ std::vector<int> rows = filterRows(indices);
+
+ if (rows.empty() || rows.back() >= (rowCount() - 1))
+ return {};
+
+ auto nodeListProperty = m_listModelNode.defaultNodeListProperty();
+
+ std::reverse(rows.begin(), rows.end());
+
+ for (int row : rows) {
+ insertRow(row + 1, takeRow(row));
+ nodeListProperty.slide(row, row + 1);
+ }
+
+ return {index(rows.front() + 1, 0), index(rows.back() + 1, columnCount() - 1)};
+}
+
+std::vector<int> ListModelEditorModel::filterColumns(const QList<QModelIndex> &indices)
+{
+ std::vector<int> columns;
+ columns.reserve(indices.size());
+
+ for (QModelIndex index : indices) {
+ if (index.column() >= 0)
+ columns.push_back(index.column());
+ }
+
+ std::sort(columns.begin(), columns.end());
+
+ columns.erase(std::unique(columns.begin(), columns.end()), columns.end());
+
+ return columns;
+}
+
+std::vector<int> ListModelEditorModel::filterRows(const QList<QModelIndex> &indices)
+{
+ std::vector<int> rows;
+ rows.reserve(indices.size());
+
+ for (QModelIndex index : indices) {
+ if (index.row() >= 0)
+ rows.push_back(index.row());
+ }
+
+ std::sort(rows.begin(), rows.end());
+
+ rows.erase(std::unique(rows.begin(), rows.end()), rows.end());
+
+ return rows;
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h
index 35d41bee68..97bd9c18d8 100644
--- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h
+++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h
@@ -27,19 +27,26 @@
#include <modelnode.h>
+#include <QItemSelection>
#include <QStandardItemModel>
namespace QmlDesigner {
class ListModelEditorModel : public QStandardItemModel
{
+ using QStandardItemModel::removeColumns;
+ using QStandardItemModel::removeRows;
public:
- void setListModel(ModelNode node)
- {
- m_listModelNode = node;
- populateModel();
- }
+ ListModelEditorModel(std::function<ModelNode()> createModelCallback,
+ std::function<ModelNode()> createElementCallback)
+ : m_createModelCallback(std::move(createModelCallback))
+ , m_createElementCallback(std::move(createElementCallback))
+ {}
+
+ void setListModel(ModelNode node);
+
+ void setListView(ModelNode listView);
void addRow();
void addColumn(const QString &columnName);
@@ -48,11 +55,18 @@ public:
bool setValue(int row, int column, QVariant value, Qt::ItemDataRole role = Qt::EditRole);
- void removeColumn(int column);
- void removeRow(int row);
+ void removeColumns(const QList<QModelIndex> &indices);
+ void removeRows(const QList<QModelIndex> &indices);
void renameColumn(int column, const QString &newColumnName);
+ QItemSelection moveRowsUp(const QList<QModelIndex> &indices);
+ QItemSelection moveRowsDown(const QList<QModelIndex> &indices);
+
+ static std::vector<int> filterColumns(const QList<QModelIndex> &indices);
+ static std::vector<int> filterRows(const QList<QModelIndex> &indices);
private:
+ void removeRow(int row);
+ void removeColumn(int column);
void populateModel();
void createItems(const QList<ModelNode> &listElementNodes);
void appendItems(const ModelNode &listElementNode);
@@ -60,6 +74,8 @@ private:
private:
ModelNode m_listModelNode;
QList<QmlDesigner::PropertyName> m_propertyNames;
+ std::function<ModelNode()> m_createModelCallback;
+ std::function<ModelNode()> m_createElementCallback;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp
index e8c85bc1b6..7032a26ef7 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp
@@ -40,6 +40,7 @@
#include "aligndistribute.h"
#include "propertyeditorcontextobject.h"
#include "tooltip.h"
+#include "richtexteditor/richtexteditorproxy.h"
namespace QmlDesigner {
@@ -69,6 +70,7 @@ void Quick2PropertyEditorView::registerQmlTypes()
AlignDistribute::registerDeclarativeType();
Tooltip::registerDeclarativeType();
EasingCurveEditor::registerDeclarativeType();
+ RichTextEditorProxy::registerDeclarativeType();
}
}
diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri
index 68b6dbe026..b71be2268d 100644
--- a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri
+++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.pri
@@ -1,7 +1,9 @@
-HEADERS += $$PWD/richtexteditor.h
+HEADERS += $$PWD/richtexteditor.h \
+ $$PWD/richtexteditorproxy.h
HEADERS += $$PWD/hyperlinkdialog.h
-SOURCES += $$PWD/richtexteditor.cpp
+SOURCES += $$PWD/richtexteditor.cpp \
+ $$PWD/richtexteditorproxy.cpp
SOURCES += $$PWD/hyperlinkdialog.cpp
FORMS += $$PWD/richtexteditor.ui
diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.cpp b/src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.cpp
new file mode 100644
index 0000000000..ba914724db
--- /dev/null
+++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** 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 "richtexteditorproxy.h"
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+
+#include "richtexteditor.h"
+
+namespace QmlDesigner {
+
+RichTextEditorProxy::RichTextEditorProxy(QObject *parent)
+ : QObject(parent)
+ , m_dialog(new QDialog{})
+ , m_widget(new RichTextEditor{})
+{
+ QGridLayout *layout = new QGridLayout{};
+
+ layout->addWidget(m_widget);
+ QDialogButtonBox *standardButtons = new QDialogButtonBox{QDialogButtonBox::Ok
+ | QDialogButtonBox::Cancel};
+
+ connect(standardButtons, &QDialogButtonBox::accepted, m_dialog, &QDialog::accept);
+ connect(standardButtons, &QDialogButtonBox::rejected, m_dialog, &QDialog::reject);
+
+ layout->addWidget(standardButtons);
+
+ m_dialog->setLayout(layout);
+
+ connect(m_dialog, &QDialog::accepted, [this]() { emit accepted(); });
+ connect(m_dialog, &QDialog::rejected, [this]() { emit rejected(); });
+}
+
+RichTextEditorProxy::~RichTextEditorProxy()
+{
+ delete m_dialog;
+}
+
+void RichTextEditorProxy::registerDeclarativeType()
+{
+ qmlRegisterType<RichTextEditorProxy>("HelperWidgets", 2, 0, "RichTextEditor");
+}
+
+void RichTextEditorProxy::showWidget()
+{
+ m_dialog->show();
+}
+
+void RichTextEditorProxy::hideWidget()
+{
+ m_dialog->hide();
+}
+
+QString RichTextEditorProxy::richText() const
+{
+ return m_widget->richText();
+}
+
+void RichTextEditorProxy::setRichText(const QString &text)
+{
+ m_widget->setRichText(text);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.h b/src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.h
new file mode 100644
index 0000000000..7e62a0e388
--- /dev/null
+++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditorproxy.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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 <QObject>
+#include <QQuickItem>
+
+QT_BEGIN_NAMESPACE
+class QDialog;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+class RichTextEditor;
+
+class RichTextEditorProxy : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString richText READ richText WRITE setRichText)
+public:
+ explicit RichTextEditorProxy(QObject *parent = nullptr);
+ ~RichTextEditorProxy();
+
+ static void registerDeclarativeType();
+
+ Q_INVOKABLE void showWidget();
+ Q_INVOKABLE void hideWidget();
+
+ QString richText() const;
+ void setRichText(const QString &text);
+
+signals:
+ void accepted();
+ void rejected();
+
+private:
+ QDialog *m_dialog;
+ RichTextEditor *m_widget;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
index 39874f51b0..7d11f57cb3 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp
@@ -103,7 +103,7 @@ static QAction *createAction(const Core::Id &id,
TimelineToolBar::TimelineToolBar(QWidget *parent)
: QToolBar(parent)
, m_grp()
- , m_dialog(new AnimationCurveDialog(Core::ICore::dialogParent()))
+ , m_dialog(new AnimationCurveDialog(this))
, m_curveModel(new AnimationCurveEditorModel(0., 500.))
{
m_dialog->setModel(m_curveModel);
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 ef0f29c2c1..cad7d55221 100644
--- a/src/plugins/qmldesigner/designercore/include/viewmanager.h
+++ b/src/plugins/qmldesigner/designercore/include/viewmanager.h
@@ -122,7 +122,7 @@ private: // functions
const 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..b7a2cc282e
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** 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>
+#include <QTimer>
+
+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..4be52a327c
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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>
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QTimer>
+
+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..fa8528579d
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** 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 <QTimer>
+#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..b4fdb70794
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** 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>
+#include <QTimer>
+
+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..92d7449bc0
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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>
+
+#include <QTimer>
+
+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..6da44603df
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** 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>
+#include <QTimer>
+
+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 7cf2784a7d..21623f865c 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)
@@ -355,8 +360,8 @@ void NodeInstanceView::resetVerticalAnchors(const ModelNode &modelNode)
QList<BindingProperty> bindingList;
QList<VariantProperty> valueList;
- if (modelNode.hasBindingProperty("yx"))
- bindingList.append(modelNode.bindingProperty("yx"));
+ if (modelNode.hasBindingProperty("x"))
+ bindingList.append(modelNode.bindingProperty("x"));
else if (modelNode.hasVariantProperty("y"))
valueList.append(modelNode.variantProperty("y"));
@@ -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,30 +537,26 @@ 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);
}
}
}
} else if (node.isRootNode() && name == "language@Internal") {
const QString languageAsString = value.toString();
- if (m_currentTarget) {
- if (auto rc = m_currentTarget->activeRunConfiguration()) {
- if (auto multiLanguageAspect = rc->aspect<QmlProjectManager::QmlMultiLanguageAspect>())
- multiLanguageAspect->setLastUsedLanguage(languageAsString);
- }
- }
- nodeInstanceServer()->changeLanguage({languageAsString});
+ if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(m_currentTarget))
+ multiLanguageAspect->setCurrentLocale(languageAsString);
+ m_nodeInstanceServer->changeLanguage({languageAsString});
} else if (node.isRootNode() && name == "previewSize@Internal") {
- nodeInstanceServer()->changePreviewImageSize(value.toSize());
+ m_nodeInstanceServer->changePreviewImageSize(value.toSize());
}
}
@@ -569,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);
@@ -794,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));
@@ -814,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)
@@ -994,12 +992,8 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
}
QString lastUsedLanguage;
- if (m_currentTarget) {
- if (auto rc = m_currentTarget->activeRunConfiguration()) {
- if (auto multiLanguageAspect = rc->aspect<QmlProjectManager::QmlMultiLanguageAspect>())
- lastUsedLanguage = multiLanguageAspect->lastUsedLanguage();
- }
- }
+ if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(m_currentTarget))
+ lastUsedLanguage = multiLanguageAspect->currentLocale();
return CreateSceneCommand(
instanceContainerList,
@@ -1256,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);
@@ -1464,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)
@@ -1479,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();
@@ -1496,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 c74c41b323..bdae76c0d3 100644
--- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
@@ -178,39 +178,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::
@@ -220,7 +227,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);
@@ -514,11 +521,9 @@ QProcessEnvironment PuppetCreator::processEnvironment() const
customFileSelectors = m_target->additionalData("CustomFileSelectorsData").toStringList();
- if (auto *rc = m_target->activeRunConfiguration()) {
- if (auto multiLanguageAspect = rc->aspect<QmlProjectManager::QmlMultiLanguageAspect>()) {
- if (!multiLanguageAspect->databaseFilePath().isEmpty())
- environment.set("QT_MULTILANGUAGE_DATABASE", multiLanguageAspect->databaseFilePath().toString());
- }
+ if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(m_target)) {
+ if (!multiLanguageAspect->databaseFilePath().isEmpty())
+ environment.set("QT_MULTILANGUAGE_DATABASE", multiLanguageAspect->databaseFilePath().toString());
}
}
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h
index 00b5a0e921..c19a0e1381 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 80c23acad7..c3946a5378 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 0e9ee73191..b08766d46b 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.qbs
+++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs
@@ -134,6 +134,7 @@ Project {
"commands/changestatecommand.h",
"commands/changevaluescommand.cpp",
"commands/changevaluescommand.h",
+ "commands/captureddatacommand.h",
"commands/childrenchangedcommand.cpp",
"commands/childrenchangedcommand.h",
"commands/clearscenecommand.cpp",
@@ -321,6 +322,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/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
index b94f87bc40..616678d855 100644
--- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
@@ -212,24 +212,26 @@ void FpsAction::currentContextChanged(const SelectionContext &)
SwitchLanguageComboboxAction::SwitchLanguageComboboxAction(QObject *parent)
: QWidgetAction(parent)
{
- connect(ProjectExplorer::SessionManager::instance(),
- &ProjectExplorer::SessionManager::startupProjectChanged,
- this,
- &SwitchLanguageComboboxAction::updateProjectLocales);
}
QWidget *SwitchLanguageComboboxAction::createWidget(QWidget *parent)
{
QPointer<QComboBox> comboBox = new QComboBox(parent);
- comboBox->setToolTip(tr("Switch the language used by preview."));
+ const QString toolTip(tr("Switch the language used by preview."));
+ comboBox->setToolTip(toolTip);
comboBox->addItem(tr("Default"));
- auto refreshComboBoxFunction = [this, comboBox] (ProjectExplorer::Project *project) {
- if (comboBox) {
- if (updateProjectLocales(project)) {
+ auto refreshComboBoxFunction = [this, comboBox, toolTip] (ProjectExplorer::Project *project) {
+ if (comboBox && project) {
+ QString errorMessage;
+ auto locales = project->availableQmlPreviewTranslations(&errorMessage);
+ if (!errorMessage.isEmpty())
+ comboBox->setToolTip(QString("%1<br/>(%2)").arg(toolTip, errorMessage));
+ if (m_previousLocales != locales) {
comboBox->clear();
comboBox->addItem(tr("Default"));
- comboBox->addItems(m_localeStrings);
+ comboBox->addItems(locales);
+ m_previousLocales = locales;
}
}
};
@@ -250,24 +252,6 @@ QWidget *SwitchLanguageComboboxAction::createWidget(QWidget *parent)
return comboBox;
}
-bool SwitchLanguageComboboxAction::updateProjectLocales(Project *project)
-{
- if (!project)
- return false;
- auto previousLocales = m_localeStrings;
- m_localeStrings.clear();
- const auto projectDirectory = project->rootProjectDirectory().toFileInfo().absoluteFilePath();
- const QDir languageDirectory(projectDirectory + "/i18n");
- const auto qmFiles = languageDirectory.entryList({"qml_*.qm"});
- m_localeStrings = Utils::transform(qmFiles, [](const QString &qmFile) {
- const int localeStartPosition = qmFile.lastIndexOf("_") + 1;
- const int localeEndPosition = qmFile.size() - QString(".qm").size();
- const QString locale = qmFile.left(localeEndPosition).mid(localeStartPosition);
- return locale;
- });
- return previousLocales != m_localeStrings;
-}
-
SwitchLanguageAction::SwitchLanguageAction()
: m_switchLanguageAction(new SwitchLanguageComboboxAction(nullptr))
{
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h
index a23f125ca1..8bbbc453d6 100644
--- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h
@@ -113,8 +113,7 @@ signals:
protected:
QWidget *createWidget(QWidget *parent) override;
private:
- bool updateProjectLocales(ProjectExplorer::Project *project);
- QStringList m_localeStrings;
+ QStringList m_previousLocales;
};
class SwitchLanguageAction : public ActionInterface
diff --git a/src/plugins/qmlpreview/CMakeLists.txt b/src/plugins/qmlpreview/CMakeLists.txt
index 345f429e81..00bda33ab0 100644
--- a/src/plugins/qmlpreview/CMakeLists.txt
+++ b/src/plugins/qmlpreview/CMakeLists.txt
@@ -9,6 +9,8 @@ add_qtc_plugin(QmlPreview
qmlpreviewruncontrol.cpp qmlpreviewruncontrol.h
qmldebugtranslationclient.cpp qmldebugtranslationclient.h
qmlpreview_global.h
+ projectfileselectionswidget.cpp projectfileselectionswidget.h
+ qmldebugtranslationwidget.cpp qmldebugtranslationwidget.h
)
extend_qtc_plugin(QmlPreview
diff --git a/src/plugins/qmlpreview/projectfileselectionswidget.cpp b/src/plugins/qmlpreview/projectfileselectionswidget.cpp
new file mode 100644
index 0000000000..f6f20fc2d4
--- /dev/null
+++ b/src/plugins/qmlpreview/projectfileselectionswidget.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** 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 "projectfileselectionswidget.h"
+
+#include <projectexplorer/target.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/session.h>
+
+#include <utils/treemodel.h>
+
+#include <QAbstractTableModel>
+#include <QBoxLayout>
+#include <QHeaderView>
+#include <QTreeView>
+
+namespace QmlPreview {
+
+class ProjectFileItem : public Utils::TreeItem
+{
+public:
+ ProjectFileItem() = default;
+ ProjectFileItem(const Utils::FilePath &f, bool d)
+ : filePath(f)
+ , disabled(d)
+ {}
+
+ Qt::ItemFlags flags(int) const override
+ {
+ return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
+ }
+
+ QVariant data(int , int role) const override
+ {
+ if (role == Qt::DisplayRole)
+ return filePath.toUserOutput();
+ if (role == Qt::CheckStateRole) {
+ if (disabled)
+ return Qt::Unchecked;
+ else
+ return Qt::Checked;
+ }
+ return QVariant();
+ }
+
+ bool setData(int , const QVariant &data, int role) override
+ {
+ if (role != Qt::CheckStateRole)
+ return false;
+ disabled = (data == Qt::Unchecked);
+ return true;
+ }
+
+ Utils::FilePath filePath;
+ bool disabled = false;
+};
+
+
+ProjectFileSelectionsWidget::ProjectFileSelectionsWidget(const QString &projectSettingsKey, ProjectExplorer::FileType fileType, QWidget *parent)
+ : QWidget(parent)
+ , m_projectSettingsKey(projectSettingsKey)
+ , m_fileType(fileType)
+{
+ auto model = new Utils::TreeModel<ProjectFileItem>(this);
+ model->setHeader({tr("Files to test:")});
+ auto updateCheckedFiles = [this, model] () {
+ m_checkedFiles.clear();
+ QStringList uncheckedFiles;
+ model->forAllItems([&, this](ProjectFileItem *item) {
+ if (item->disabled)
+ uncheckedFiles.append(item->filePath.toString());
+ else
+ m_checkedFiles.append(item->filePath);
+ });
+ if (auto project = ProjectExplorer::SessionManager::startupProject())
+ project->setNamedSettings(m_projectSettingsKey, uncheckedFiles);
+ emit selectionChanged(m_checkedFiles);
+ };
+
+ connect(model, &QAbstractItemModel::dataChanged, updateCheckedFiles);
+
+ auto view = new QTreeView(this);
+ view->setMinimumSize(QSize(100, 100));
+ view->setTextElideMode(Qt::ElideMiddle);
+ view->setWordWrap(false);
+ view->setUniformRowHeights(true);
+ view->setModel(model);
+
+ const auto viewLayout = new QHBoxLayout;
+ viewLayout->addWidget(view);
+
+ auto layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addLayout(viewLayout);
+
+ auto initModel = [this, model, updateCheckedFiles](ProjectExplorer::Project *project) {
+ if (!project)
+ return;
+
+ auto refreshModel = [this, model, updateCheckedFiles] () {
+ model->clear();
+ if (auto project = ProjectExplorer::SessionManager::startupProject()) {
+ const auto settingsDisabledFiles = project->namedSettings(m_projectSettingsKey).toStringList();
+
+ if (auto rootProjectNode = project->rootProjectNode()) {
+ rootProjectNode->forEachNode([this, settingsDisabledFiles, model](ProjectExplorer::FileNode *fileNode) {
+ if (fileNode->fileType() == m_fileType) {
+ bool isDisabled = settingsDisabledFiles.contains(fileNode->filePath().toString());
+ model->rootItem()->appendChild(new ProjectFileItem(fileNode->filePath(), isDisabled));
+ }
+ });
+ }
+ updateCheckedFiles();
+ }
+ };
+ // deploymentDataChanged is only triggered if the active project changed, so it is not a
+ // problem that maybe many different targets are connected to refreshModel
+ this->connect(project->activeTarget(), &ProjectExplorer::Target::deploymentDataChanged,
+ model, refreshModel, Qt::UniqueConnection);
+ refreshModel();
+ };
+
+ if (auto project = ProjectExplorer::SessionManager::startupProject()) {
+ initModel(project);
+ }
+
+ connect(ProjectExplorer::SessionManager::instance(),
+ &ProjectExplorer::SessionManager::startupProjectChanged,
+ initModel);
+}
+
+Utils::FilePaths ProjectFileSelectionsWidget::checkedFiles()
+{
+ return m_checkedFiles;
+}
+
+} // QmlPreview
diff --git a/src/plugins/qmlpreview/projectfileselectionswidget.h b/src/plugins/qmlpreview/projectfileselectionswidget.h
new file mode 100644
index 0000000000..4bacb41d49
--- /dev/null
+++ b/src/plugins/qmlpreview/projectfileselectionswidget.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 <projectexplorer/projectnodes.h>
+#include <utils/fileutils.h>
+
+#include <QWidget>
+
+namespace QmlPreview {
+
+class ProjectFileSelectionsWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ProjectFileSelectionsWidget(const QString &projectSettingsKey, ProjectExplorer::FileType fileType, QWidget *parent = nullptr);
+ Utils::FilePaths checkedFiles();
+signals:
+ void selectionChanged(const Utils::FilePaths &selectedFiles);
+private:
+ const QString m_projectSettingsKey;
+ ProjectExplorer::FileType m_fileType;
+
+ Utils::FilePaths m_checkedFiles;
+};
+
+} // QmlPreview
diff --git a/src/plugins/qmlpreview/qmldebugtranslationclient.cpp b/src/plugins/qmlpreview/qmldebugtranslationclient.cpp
index 0ccea79300..d62d3d0d20 100644
--- a/src/plugins/qmlpreview/qmldebugtranslationclient.cpp
+++ b/src/plugins/qmlpreview/qmldebugtranslationclient.cpp
@@ -57,6 +57,14 @@ void QmlDebugTranslationClient::changeElidedTextWarningString(const QString &war
sendMessage(packet.data());
}
+void QmlDebugTranslationClient::changeElideWarning(bool elideWarning)
+{
+ if (elideWarning)
+ enableElidedTextWarning();
+ else
+ disableElidedTextWarning();
+}
+
void QmlDebugTranslationClient::setDebugTranslationServiceLogFile(const QString &logFilePath)
{
QmlDebug::QPacket packet(dataStreamVersion());
diff --git a/src/plugins/qmlpreview/qmldebugtranslationclient.h b/src/plugins/qmlpreview/qmldebugtranslationclient.h
index c27726a7eb..2b69e99b0a 100644
--- a/src/plugins/qmlpreview/qmldebugtranslationclient.h
+++ b/src/plugins/qmlpreview/qmldebugtranslationclient.h
@@ -50,6 +50,7 @@ public:
void changeLanguage(const QUrl &url, const QString &locale);
void changeWarningColor(const QColor &warningColor);
void changeElidedTextWarningString(const QString &warningString); //is QByteArray better here?
+ void changeElideWarning(bool elideWarning);
void setDebugTranslationServiceLogFile(const QString &logFilePath);
void enableElidedTextWarning();
void disableElidedTextWarning();
diff --git a/src/plugins/qmlpreview/qmldebugtranslationwidget.cpp b/src/plugins/qmlpreview/qmldebugtranslationwidget.cpp
new file mode 100644
index 0000000000..3de3eff2c2
--- /dev/null
+++ b/src/plugins/qmlpreview/qmldebugtranslationwidget.cpp
@@ -0,0 +1,452 @@
+/****************************************************************************
+**
+** 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 "qmldebugtranslationwidget.h"
+#include "qmlpreviewruncontrol.h"
+#include "qmlpreviewplugin.h"
+#include "projectfileselectionswidget.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/editormanager/ieditor.h>
+#include <coreplugin/outputwindow.h>
+
+#include <projectexplorer/runcontrol.h>
+#include <projectexplorer/projecttree.h>
+
+#include <utils/outputformatter.h>
+#include <utils/utilsicons.h>
+#include <utils/fileutils.h>
+
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
+#include <extensionsystem/iplugin.h>
+
+#include <projectexplorer/projectnodes.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/task.h>
+#include <projectexplorer/taskhub.h>
+
+#include <qmlprojectmanager/qmlmultilanguageaspect.h>
+
+#include <qtsupport/qtoutputformatter.h>
+
+#include <QIcon>
+#include <QRegularExpression>
+
+#include <QCheckBox>
+#include <QLabel>
+#include <QVBoxLayout>
+#include <QAction>
+#include <QPushButton>
+#include <QCheckBox>
+#include <QLineEdit>
+#include <QButtonGroup>
+#include <QRadioButton>
+#include <QSpacerItem>
+#include <QToolButton>
+#include <QTextBlock>
+#include <QFileDialog>
+
+namespace {
+QObject *getPreviewPlugin()
+{
+ auto pluginIt = std::find_if(ExtensionSystem::PluginManager::plugins().begin(),
+ ExtensionSystem::PluginManager::plugins().end(),
+ [](const ExtensionSystem::PluginSpec *p) {
+ return p->name() == "QmlPreview";
+ });
+
+ if (pluginIt != ExtensionSystem::PluginManager::plugins().constEnd())
+ return (*pluginIt)->plugin();
+
+ return nullptr;
+}
+
+}
+
+namespace QmlPreview {
+
+QmlDebugTranslationWidget::QmlDebugTranslationWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ auto mainLayout = new QVBoxLayout(this);
+
+ auto buttonGroup = new QButtonGroup(this);
+ // it gets the text from updateCurrentEditor method
+ m_singleFileButton = new QRadioButton();
+ m_singleFileButton->setChecked(true);
+ buttonGroup->addButton(m_singleFileButton);
+
+ const QString projectSettingsKey = "QmlPreview.DisabledDebugTranslationFiles";
+ const ProjectExplorer::FileType filterFileType = ProjectExplorer::FileType::QML;
+ auto checkableProjectFileView = new ProjectFileSelectionsWidget(projectSettingsKey, filterFileType);
+ checkableProjectFileView->setVisible(false);
+ connect(checkableProjectFileView, &ProjectFileSelectionsWidget::selectionChanged, this, &QmlDebugTranslationWidget::setFiles);
+ m_multipleFileButton = new QRadioButton(tr("multiple files"));
+ // TODO: fix multiple files issues, because it have some issues disable it for now
+ m_multipleFileButton->setDisabled(true);
+ buttonGroup->addButton(m_multipleFileButton);
+ connect(m_multipleFileButton, &QAbstractButton::toggled, [checkableProjectFileView, this](bool checked) {
+ checkableProjectFileView->setVisible(checked);
+ setFiles(checkableProjectFileView->checkedFiles());
+ });
+
+ mainLayout->addWidget(m_singleFileButton);
+ mainLayout->addWidget(m_multipleFileButton);
+ mainLayout->addWidget(checkableProjectFileView);
+
+ // language checkboxes are add in updateAvailableTranslations method
+ m_selectLanguageLayout = new QHBoxLayout;
+ mainLayout->addLayout(m_selectLanguageLayout);
+
+ auto elideWarningCheckBox = new QCheckBox(tr("Enable elide warning"));
+ layout()->addWidget(elideWarningCheckBox);
+ connect(elideWarningCheckBox, &QCheckBox::stateChanged, [this] (int state) {
+ m_elideWarning = (state == Qt::Checked);
+ });
+
+ auto controlLayout = new QHBoxLayout;
+ mainLayout->addLayout(controlLayout);
+
+ auto showLogButton = new QToolButton;
+ showLogButton->setText(tr("Show log"));
+ showLogButton->setCheckable(true);
+ controlLayout->addWidget(showLogButton);
+
+ // TODO: do we still need this buttons?
+// auto pauseButton = new QToolButton;
+// pauseButton->setText(tr("Pause"));
+// pauseButton->setCheckable(true);
+// controlLayout->addWidget(pauseButton);
+
+// auto onTheFlyButton = new QToolButton;
+// onTheFlyButton->setText(tr("On the fly"));
+// controlLayout->addWidget(onTheFlyButton);
+
+ m_runTestButton = new QPushButton();
+ m_runTestButton->setCheckable(true);
+ m_runTestButton->setText(runButtonText());
+ connect(m_runTestButton, &QPushButton::toggled, [this](bool checked) {
+ m_runTestButton->setText(runButtonText(checked));
+ });
+
+ connect(m_runTestButton, &QPushButton::clicked, [this](bool checked) {
+ if (checked)
+ runTest();
+ else {
+ if (m_currentRunControl)
+ m_currentRunControl->initiateStop();
+ // TODO: what happens if we already have a preview running?
+// QmlPreviewPlugin::stopAllRunControls();
+// qWarning() << "not implemented"; // TODO: stop still running tests
+ }
+ });
+ controlLayout->addWidget(m_runTestButton);
+
+ m_runOutputWindow = new Core::OutputWindow(Core::Context("QmlPreview.DebugTranslation"),
+ "QmlPreview/OutputWindow/Zoom");
+
+ m_runOutputWindow->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_runOutputWindow->setReadOnly(true);
+ m_runOutputWindow->setVisible(false);
+ mainLayout->addWidget(m_runOutputWindow);
+
+ QSpacerItem *endSpacerItem = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
+ mainLayout->addItem(endSpacerItem);
+
+ connect(showLogButton, &QToolButton::toggled, m_runOutputWindow, [this, mainLayout, endSpacerItem](bool checked) {
+ m_runOutputWindow->setVisible(checked);
+ if (m_runOutputWindow->isVisible())
+ mainLayout->takeAt(mainLayout->count() - 1);
+ else
+ mainLayout->addItem(endSpacerItem);
+ });
+
+ auto loadLogButton = new QToolButton;
+ loadLogButton->setText(tr("Load"));
+ controlLayout->addWidget(loadLogButton);
+ connect(loadLogButton, &QToolButton::clicked, this, &QmlDebugTranslationWidget::loadLogFile);
+
+ auto saveLogButton = new QToolButton;
+ saveLogButton->setText(tr("Save"));
+ controlLayout->addWidget(saveLogButton);
+ connect(saveLogButton, &QToolButton::clicked, this, &QmlDebugTranslationWidget::saveLogToFile);
+
+ auto clearButton = new QToolButton;
+ clearButton->setText(tr("Clear"));
+ controlLayout->addWidget(clearButton);
+ connect(clearButton, &QToolButton::clicked, this, &QmlDebugTranslationWidget::clear);
+
+ Core::EditorManager *editorManager = Core::EditorManager::instance();
+ connect(editorManager, &Core::EditorManager::currentEditorChanged, this, &QmlDebugTranslationWidget::updateCurrentEditor);
+ updateCurrentEditor(Core::EditorManager::currentEditor());
+
+ connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::startupProjectChanged,
+ this, &QmlDebugTranslationWidget::updateCurrentTranslations);
+
+ updateStartupProjectTranslations();
+
+ ProjectExplorer::TaskHub::addCategory("QmlPreview.Translation", tr("Translation issues"));
+}
+
+QmlDebugTranslationWidget::~QmlDebugTranslationWidget()
+{
+
+}
+
+void QmlDebugTranslationWidget::updateCurrentEditor(const Core::IEditor *editor)
+{
+ if (editor && editor->document())
+ m_currentFilePath = editor->document()->filePath();
+ else
+ m_currentFilePath.clear();
+ m_singleFileButton->setText(singleFileButtonText(m_currentFilePath.toString()));
+
+}
+
+void QmlDebugTranslationWidget::updateStartupProjectTranslations()
+{
+ updateCurrentTranslations(ProjectExplorer::SessionManager::startupProject());
+}
+
+void QmlDebugTranslationWidget::updateCurrentTranslations(ProjectExplorer::Project *project)
+{
+ for (int i = m_selectLanguageLayout->count()-1; i >= 0; --i) {
+ auto layoutItem = m_selectLanguageLayout->takeAt(i);
+ delete layoutItem->widget();
+ delete layoutItem;
+ }
+ if (!project)
+ return;
+
+ if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(project)) {
+ connect(multiLanguageAspect, &QmlProjectManager::QmlMultiLanguageAspect::changed,
+ this, &QmlDebugTranslationWidget::updateStartupProjectTranslations,
+ Qt::UniqueConnection);
+ if (multiLanguageAspect->value()) {
+ m_selectLanguageLayout->addWidget(new QLabel(
+ tr("Current language is \'<b>%1</b>\' can be changed in the 'Translation' tab.")
+ .arg(multiLanguageAspect->currentLocale())));
+ m_testLanguages.clear();
+ } else {
+ m_selectLanguageLayout->addWidget(new QLabel(tr("Select which language should be tested:")));
+ QString errorMessage;
+ for (auto language : project->availableQmlPreviewTranslations(&errorMessage)) {
+ auto languageCheckBox = new QCheckBox(language);
+ m_selectLanguageLayout->addWidget(languageCheckBox);
+ connect(languageCheckBox, &QCheckBox::stateChanged, [this, language] (int state) {
+ if (state == Qt::Checked)
+ m_testLanguages.append(language);
+ else
+ m_testLanguages.removeAll(language);
+ });
+ languageCheckBox->setChecked(true);
+ }
+ m_selectLanguageLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
+ }
+ }
+}
+
+void QmlDebugTranslationWidget::setFiles(const Utils::FilePaths &filePathes)
+{
+ m_selectedFilePaths = filePathes;
+}
+
+void QmlDebugTranslationWidget::runTest()
+{
+ m_runOutputWindow->grayOutOldContent();
+
+ auto runControl = new ProjectExplorer::RunControl(ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE);
+ QTC_ASSERT(runControl, qWarning("Can not create a QmlPreviewRunner"); return;);
+ auto previewPlugin = qobject_cast<Internal::QmlPreviewPlugin*>(getPreviewPlugin());
+
+ connect(runControl, &ProjectExplorer::RunControl::started, [this, runControl, previewPlugin]() {
+ //Q_ASSERT(m_currentRunControl == nullptr); //TODO: who deletes the runcontrol
+ m_currentRunControl = runControl;
+ m_runOutputWindow->setLineParsers(
+ ProjectExplorer::OutputFormatterFactory::createFormatters(runControl->target()));
+ int timerCounter = 1;
+ const auto testLanguageList = m_testLanguages;
+
+ auto testLanguages = [previewPlugin, runControl, testLanguageList](int timerCounter, const QString &previewedFile) {
+ qDebug() << "testLanguages" << previewedFile;
+ for (auto language : testLanguageList) {
+ QTimer::singleShot(timerCounter * 1000, previewPlugin, [previewPlugin, runControl, language, previewedFile]() {
+ if (runControl && runControl->isRunning()) {
+ if (!previewedFile.isEmpty())
+ previewPlugin->setPreviewedFile(previewedFile);
+ previewPlugin->setLocale(language);
+ }
+ });
+ }
+ };
+ if (m_multipleFileButton->isChecked()) {
+ for (auto filePath : m_selectedFilePaths) {
+ testLanguages(timerCounter++, filePath.toString());
+ }
+ } else {
+ testLanguages(timerCounter, QString());
+ }
+
+ });
+ connect(runControl, &ProjectExplorer::RunControl::stopped, [this]() {
+ m_runTestButton->setChecked(false);
+ //delete m_currentRunControl; // who deletes the runcontrol?
+ m_currentRunControl = nullptr;
+ if (auto previewPlugin = qobject_cast<Internal::QmlPreviewPlugin*>(getPreviewPlugin()))
+ previewPlugin->setLocale(m_lastUsedLanguageBeforeTest);
+ });
+
+ connect(runControl, &ProjectExplorer::RunControl::appendMessage,
+ this, &QmlDebugTranslationWidget::appendMessage);
+
+ if (auto project = ProjectExplorer::SessionManager::startupProject()) {
+ if (auto target = project->activeTarget()) {
+ if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current(target))
+ m_lastUsedLanguageBeforeTest = multiLanguageAspect->currentLocale();
+ if (auto runConfiguration = target->activeRunConfiguration()) {
+ runControl->setRunConfiguration(runConfiguration);
+ if (runControl->createMainWorker()) {
+ previewPlugin->setLocale(QString());
+ runControl->initiateStart();
+ }
+ }
+ }
+ }
+}
+
+void QmlDebugTranslationWidget::clear()
+{
+ m_runOutputWindow->clear();
+ ProjectExplorer::TaskHub::clearTasks("QmlPreview.Translation");
+}
+
+QString QmlDebugTranslationWidget::currentDir() const
+{
+ return m_lastDir.isEmpty() ?
+ ProjectExplorer::ProjectTree::currentFilePath().parentDir().toString() : m_lastDir;
+}
+
+void QmlDebugTranslationWidget::setCurrentDir(const QString &path)
+{
+ m_lastDir = path;
+ const QString currentDir = m_lastDir.isEmpty() ?
+ ProjectExplorer::ProjectTree::currentFilePath().parentDir().toString() : m_lastDir;
+}
+
+void QmlDebugTranslationWidget::loadLogFile()
+{
+ const auto fileName = QFileDialog::getOpenFileName(this, QStringLiteral("Open File"), currentDir());
+ if (!fileName.isEmpty()) {
+ setCurrentDir(QFileInfo(fileName).absolutePath());
+ QFile f(fileName);
+ if (f.open(QFile::ReadOnly)) {
+ clear();
+ while (!f.atEnd())
+ appendMessage(QString::fromUtf8(f.readLine()), Utils::DebugFormat);
+ } else {
+ // TODO: maybe add this message to log and tasks
+ qWarning() << "Failed to open" << fileName << ":" << f.errorString();
+ }
+ }
+}
+
+void QmlDebugTranslationWidget::saveLogToFile()
+{
+ const QString fileName = QFileDialog::getSaveFileName(
+ this, tr("Choose file to save logged issues."), currentDir());
+ if (!fileName.isEmpty()) {
+ setCurrentDir(QFileInfo(fileName).absolutePath());
+ QFile f(fileName);
+ if (f.open(QFile::WriteOnly | QFile::Text))
+ f.write(m_runOutputWindow->toPlainText().toUtf8());
+ }
+}
+
+void QmlDebugTranslationWidget::appendMessage(const QString &message, Utils::OutputFormat format)
+{
+ const auto newLine = QRegularExpression("[\r\n]");
+ const auto messages = message.split(newLine, QString::SkipEmptyParts);
+
+ if (messages.count() > 1) {
+ for (auto m : messages)
+ appendMessage(m + "\n", format);
+ return;
+ }
+ const QString serviceSeperator = ": QQmlDebugTranslationService: ";
+ if (!message.contains(serviceSeperator) || message.contains("DebugTranslation service - language changed"))
+ return;
+ QString locationString = message;
+ locationString = locationString.split(serviceSeperator).first();
+ static const QRegularExpression qmlLineColumnLink("^(" QT_QML_URL_REGEXP ")" // url
+ ":(\\d+)" // line
+ ":(\\d+)$"); // column
+ const QRegularExpressionMatch qmlLineColumnMatch = qmlLineColumnLink.match(locationString);
+
+ auto fileLine = -1;
+ QUrl fileUrl;
+ if (qmlLineColumnMatch.hasMatch()) {
+ fileUrl = QUrl(qmlLineColumnMatch.captured(1));
+ fileLine = qmlLineColumnMatch.captured(2).toInt();
+ }
+
+ m_runOutputWindow->appendMessage(message, format);
+
+
+ auto type = ProjectExplorer::Task::TaskType::Warning;
+ auto description = message.split(serviceSeperator).at(1);
+ auto filePath = Utils::FilePath::fromString(fileUrl.toLocalFile());
+ auto category = "QmlPreview.Translation";
+ auto icon = Utils::Icons::WARNING.icon();
+
+ ProjectExplorer::TaskHub::addTask(ProjectExplorer::Task(type,
+ description,
+ filePath,
+ fileLine,
+ category,
+ icon,
+ ProjectExplorer::Task::NoOptions));
+}
+
+QString QmlDebugTranslationWidget::singleFileButtonText(const QString &filePath)
+{
+ auto buttonText = tr("current file: %1");
+ if (filePath.isEmpty())
+ return buttonText.arg(tr("empty"));
+ return buttonText.arg(filePath);
+}
+
+QString QmlDebugTranslationWidget::runButtonText(bool isRunning)
+{
+ if (isRunning) {
+ return tr("Stop");
+ }
+ return tr("Run language tests");
+}
+
+} // namespace QmlPreview
diff --git a/src/plugins/qmlpreview/qmldebugtranslationwidget.h b/src/plugins/qmlpreview/qmldebugtranslationwidget.h
new file mode 100644
index 0000000000..ef9deb14e9
--- /dev/null
+++ b/src/plugins/qmlpreview/qmldebugtranslationwidget.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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 "qmlpreview_global.h"
+
+#include <utils/fileutils.h>
+#include <utils/outputformat.h>
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QRadioButton;
+class QPushButton;
+class QHBoxLayout;
+QT_END_NAMESPACE
+
+namespace Core {
+class IEditor;
+class OutputWindow;
+}
+namespace ProjectExplorer {
+class Project;
+class RunControl;
+}
+
+namespace QmlPreview {
+
+class QMLPREVIEW_EXPORT QmlDebugTranslationWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QmlDebugTranslationWidget(QWidget *parent = nullptr);
+ ~QmlDebugTranslationWidget() override;
+
+ void setCurrentFile(const Utils::FilePath &filepath);
+ void setFiles(const Utils::FilePaths &filePathes);
+ void updateStartupProjectTranslations();
+private:
+ void updateCurrentEditor(const Core::IEditor *editor);
+ void updateCurrentTranslations(ProjectExplorer::Project *project);
+ void runTest();
+ void appendMessage(const QString &message, Utils::OutputFormat format);
+ void clear();
+ void loadLogFile();
+ void saveLogToFile();
+ QString currentDir() const;
+ void setCurrentDir(const QString &path);
+
+ QString singleFileButtonText(const QString &filePath);
+ QString runButtonText(bool isRunning = false);
+
+ QStringList m_testLanguages;
+ QString m_lastUsedLanguageBeforeTest;
+ bool m_elideWarning = false;
+
+ Core::OutputWindow *m_runOutputWindow = nullptr;
+
+ QRadioButton *m_singleFileButton = nullptr;
+ QRadioButton *m_multipleFileButton = nullptr;
+ QPushButton *m_runTestButton = nullptr;
+
+ Utils::FilePath m_currentFilePath;
+ Utils::FilePaths m_selectedFilePaths;
+ ProjectExplorer::RunControl *m_currentRunControl = nullptr;
+
+ QString m_lastDir;
+
+ QHBoxLayout *m_selectLanguageLayout;
+};
+
+} // namespace QmlPreview
diff --git a/src/plugins/qmlpreview/qmlpreview.pro b/src/plugins/qmlpreview/qmlpreview.pro
index cfc571521f..ea53ac2f4e 100644
--- a/src/plugins/qmlpreview/qmlpreview.pro
+++ b/src/plugins/qmlpreview/qmlpreview.pro
@@ -10,19 +10,23 @@ include(tests/tests.pri)
HEADERS += \
qmlpreview_global.h \
qmldebugtranslationclient.h \
+ qmldebugtranslationwidget.h \
qmlpreviewclient.h \
qmlpreviewplugin.h \
qmlpreviewruncontrol.h \
qmlpreviewconnectionmanager.h \
- qmlpreviewfileontargetfinder.h
+ qmlpreviewfileontargetfinder.h \
+ projectfileselectionswidget.h
SOURCES += \
qmlpreviewplugin.cpp \
qmldebugtranslationclient.cpp \
+ qmldebugtranslationwidget.cpp \
qmlpreviewclient.cpp \
qmlpreviewruncontrol.cpp \
qmlpreviewconnectionmanager.cpp \
- qmlpreviewfileontargetfinder.cpp
+ qmlpreviewfileontargetfinder.cpp \
+ projectfileselectionswidget.cpp
OTHER_FILES += \
QmlPreview.json.in
diff --git a/src/plugins/qmlpreview/qmlpreview.qbs b/src/plugins/qmlpreview/qmlpreview.qbs
index 08e6472578..6773487962 100644
--- a/src/plugins/qmlpreview/qmlpreview.qbs
+++ b/src/plugins/qmlpreview/qmlpreview.qbs
@@ -26,6 +26,8 @@ QtcPlugin {
"qmlpreviewclient.h",
"qmldebugtranslationclient.cpp",
"qmldebugtranslationclient.h",
+ "qmldebugtranslationwidget.cpp",
+ "qmldebugtranslationwidget.h",
"qmlpreviewconnectionmanager.cpp",
"qmlpreviewconnectionmanager.h",
"qmlpreviewfileontargetfinder.cpp",
@@ -35,6 +37,8 @@ QtcPlugin {
"qmlpreviewplugin.h",
"qmlpreviewruncontrol.cpp",
"qmlpreviewruncontrol.h",
+ "projectfileselectionswidget.cpp",
+ "projectfileselectionswidget.h"
]
}
diff --git a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp
index 0fcf6a36b8..7da4bdea13 100644
--- a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.cpp
@@ -76,17 +76,22 @@ void QmlPreviewConnectionManager::createClients()
QUrl QmlPreviewConnectionManager::findValidI18nDirectoryAsUrl(const QString &locale)
{
+ QTC_ASSERT(!m_lastLoadedUrl.isEmpty(), return {};);
+
const QString shortLocale = locale.left(locale.indexOf("_"));
QString path = m_lastLoadedUrl.path();
+ QString foundPath;
while (!path.isEmpty()) {
path = path.left(qMax(0, path.lastIndexOf("/")));
QUrl url = m_lastLoadedUrl;
+
auto tryPath = [&](const QString &postfix) {
url.setPath(path + "/i18n/qml_" + postfix);
bool success = false;
- m_projectFileFinder.findFile(url, &success);
+ foundPath = m_projectFileFinder.findFile(url, &success).first().toString();
+ foundPath = foundPath.left(qMax(0, foundPath.lastIndexOf("/i18n")));
return success;
};
@@ -101,21 +106,32 @@ QUrl QmlPreviewConnectionManager::findValidI18nDirectoryAsUrl(const QString &loc
}
QUrl url = m_lastLoadedUrl;
- url.setPath(path);
+ if (foundPath.isEmpty())
+ url.setPath(path);
+ else
+ url.setPath(foundPath);
return url;
}
void QmlPreviewConnectionManager::createDebugTranslationClient()
{
m_qmlDebugTranslationClient = new QmlDebugTranslationClient(connection());
- QObject::connect(this, &QmlPreviewConnectionManager::language,
+ connect(this, &QmlPreviewConnectionManager::language,
m_qmlDebugTranslationClient.data(), [this](const QString &locale) {
- // service expects a context URL.
- // Search the parent directories of the last loaded URL for i18n files.
- m_qmlDebugTranslationClient->changeLanguage(findValidI18nDirectoryAsUrl(locale), locale);
+ if (m_lastLoadedUrl.isEmpty()) {
+ // findValidI18nDirectoryAsUrl does not work if we didn't load any file
+ m_initLocale = locale;
+ } else {
+ // service expects a context URL.
+ // Search the parent directories of the last loaded URL for i18n files.
+ m_qmlDebugTranslationClient->changeLanguage(findValidI18nDirectoryAsUrl(locale), locale);
+ }
});
- QObject::connect(m_qmlDebugTranslationClient.data(), &QmlDebugTranslationClient::debugServiceUnavailable,
+ connect(this, &QmlPreviewConnectionManager::changeElideWarning,
+ m_qmlDebugTranslationClient, &QmlDebugTranslationClient::changeElideWarning);
+
+ connect(m_qmlDebugTranslationClient.data(), &QmlDebugTranslationClient::debugServiceUnavailable,
this, []() {
QMessageBox::warning(Core::ICore::dialogParent(), "Error connect to QML DebugTranslation service",
"QML DebugTranslation feature is not available for this version of Qt.");
@@ -126,8 +142,7 @@ void QmlPreviewConnectionManager::createPreviewClient()
{
m_qmlPreviewClient = new QmlPreviewClient(connection());
- QObject::connect(
- this, &QmlPreviewConnectionManager::loadFile, m_qmlPreviewClient.data(),
+ connect(this, &QmlPreviewConnectionManager::loadFile, m_qmlPreviewClient.data(),
[this](const QString &filename, const QString &changedFile,
const QByteArray &contents) {
if (!m_fileClassifier(changedFile)) {
@@ -144,23 +159,32 @@ void QmlPreviewConnectionManager::createPreviewClient()
m_lastLoadedUrl = m_targetFileFinder.findUrl(filename);
m_qmlPreviewClient->loadUrl(m_lastLoadedUrl);
+ if (!m_initLocale.isEmpty()) {
+ emit language(m_initLocale);
+ m_initLocale.clear();
+ }
});
- QObject::connect(this, &QmlPreviewConnectionManager::rerun,
+ connect(this, &QmlPreviewConnectionManager::rerun,
m_qmlPreviewClient.data(), &QmlPreviewClient::rerun);
- QObject::connect(this, &QmlPreviewConnectionManager::zoom,
+ connect(this, &QmlPreviewConnectionManager::zoom,
m_qmlPreviewClient.data(), &QmlPreviewClient::zoom);
- QObject::connect(this, &QmlPreviewConnectionManager::language,
+ connect(this, &QmlPreviewConnectionManager::language,
m_qmlPreviewClient.data(), [this](const QString &locale) {
- // service expects a context URL.
- // Search the parent directories of the last loaded URL for i18n files.
- m_qmlPreviewClient->language(findValidI18nDirectoryAsUrl(locale), locale);
+ if (m_lastLoadedUrl.isEmpty()) {
+ // findValidI18nDirectoryAsUrl does not work if we didn't load any file
+ m_initLocale = locale;
+ } else {
+ // service expects a context URL.
+ // Search the parent directories of the last loaded URL for i18n files.
+ m_qmlPreviewClient->language(findValidI18nDirectoryAsUrl(locale), locale);
+ }
});
- QObject::connect(m_qmlPreviewClient.data(), &QmlPreviewClient::pathRequested,
+ connect(m_qmlPreviewClient.data(), &QmlPreviewClient::pathRequested,
this, [this](const QString &path) {
const bool found = m_projectFileFinder.findFileOrDirectory(
path, [&](const QString &filename, int confidence) {
@@ -190,13 +214,13 @@ void QmlPreviewConnectionManager::createPreviewClient()
m_qmlPreviewClient->announceError(path);
});
- QObject::connect(m_qmlPreviewClient.data(), &QmlPreviewClient::errorReported,
+ connect(m_qmlPreviewClient.data(), &QmlPreviewClient::errorReported,
this, [](const QString &error) {
Core::MessageManager::write("Error loading QML Live Preview:");
Core::MessageManager::write(error);
});
- QObject::connect(m_qmlPreviewClient.data(), &QmlPreviewClient::fpsReported,
+ connect(m_qmlPreviewClient.data(), &QmlPreviewClient::fpsReported,
this, [this](const QmlPreviewClient::FpsInfo &frames) {
if (m_fpsHandler) {
quint16 stats[] = {
@@ -207,13 +231,13 @@ void QmlPreviewConnectionManager::createPreviewClient()
}
});
- QObject::connect(m_qmlPreviewClient.data(), &QmlPreviewClient::debugServiceUnavailable,
+ connect(m_qmlPreviewClient.data(), &QmlPreviewClient::debugServiceUnavailable,
this, []() {
QMessageBox::warning(Core::ICore::dialogParent(), "Error loading QML Live Preview",
"QML Live Preview is not available for this version of Qt.");
}, Qt::QueuedConnection); // Queue it, so that it interfere with the connection timer
- QObject::connect(&m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged,
+ connect(&m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged,
m_qmlPreviewClient.data(), [this](const QString &changedFile) {
if (!m_fileLoader || !m_lastLoadedUrl.isValid())
return;
diff --git a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h
index 7d87ca79f3..7693eda695 100644
--- a/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h
+++ b/src/plugins/qmlpreview/qmlpreviewconnectionmanager.h
@@ -57,6 +57,7 @@ signals:
void loadFile(const QString &filename, const QString &changedFile, const QByteArray &contents);
void zoom(float zoomFactor);
void language(const QString &locale);
+ void changeElideWarning(bool elideWarning);
void rerun();
void restart();
@@ -78,6 +79,7 @@ private:
QmlPreviewFileLoader m_fileLoader = nullptr;
QmlPreviewFileClassifier m_fileClassifier = nullptr;
QmlPreviewFpsHandler m_fpsHandler = nullptr;
+ QString m_initLocale;
};
} // namespace Internal
diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
index 6c92c12b0a..ffae9a1815 100644
--- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp
@@ -26,11 +26,14 @@
#include "qmlpreviewplugin.h"
#include "qmlpreviewruncontrol.h"
+#include "qmldebugtranslationwidget.h"
+
#ifdef WITH_TESTS
#include "tests/qmlpreviewclient_test.h"
#include "tests/qmlpreviewplugin_test.h"
#endif
+#include <coreplugin/icore.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -53,6 +56,12 @@
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljstools/qmljstoolsconstants.h>
+#include <qmlprojectmanager/qmlmultilanguageaspect.h>
+
+#include <qtsupport/qtkitinformation.h>
+#include <qtsupport/qtversionmanager.h>
+#include <qtsupport/baseqtversion.h>
+
#include <QAction>
using namespace ProjectExplorer;
@@ -142,6 +151,8 @@ public:
float m_zoomFactor = -1.0;
QmlPreview::QmlPreviewFpsHandler m_fpsHandler = nullptr;
QString m_locale;
+ bool elideWarning = false;
+ QPointer<QmlDebugTranslationWidget> m_qmlDebugTranslationWidget;
RunWorkerFactory localRunWorkerFactory{
RunWorkerFactory::make<LocalQmlPreviewSupport>(),
@@ -153,7 +164,7 @@ public:
RunWorkerFactory runWorkerFactory{
[this](RunControl *runControl) {
QmlPreviewRunner *runner = new QmlPreviewRunner(runControl, m_fileLoader, m_fileClassifer,
- m_fpsHandler, m_zoomFactor, m_locale);
+ m_fpsHandler, m_zoomFactor);
connect(q, &QmlPreviewPlugin::updatePreviews,
runner, &QmlPreviewRunner::loadFile);
connect(q, &QmlPreviewPlugin::rerunPreviews,
@@ -164,6 +175,8 @@ public:
runner, &QmlPreviewRunner::zoom);
connect(q, &QmlPreviewPlugin::localeChanged,
runner, &QmlPreviewRunner::language);
+ connect(q, &QmlPreviewPlugin::elideWarningChanged,
+ runner, &QmlPreviewRunner::changeElideWarning);
connect(runner, &RunWorker::started, this, [this, runControl] {
addPreview(runControl);
@@ -192,13 +205,60 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent)
action->setEnabled(SessionManager::startupProject() != nullptr);
connect(SessionManager::instance(), &SessionManager::startupProjectChanged, action,
&QAction::setEnabled);
- connect(action, &QAction::triggered, this, []() {
+ connect(action, &QAction::triggered, this, [this]() {
+ if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current())
+ m_locale = multiLanguageAspect->currentLocale();
+
ProjectExplorerPlugin::runStartupProject(Constants::QML_PREVIEW_RUN_MODE);
});
- menu->addAction(Core::ActionManager::registerAction(action, "QmlPreview.Internal"),
- Constants::G_BUILD_RUN);
+ menu->addAction(
+ Core::ActionManager::registerAction(action, "QmlPreview.RunPreview"),
+ Constants::G_BUILD_RUN);
+
+ action = new QAction(QmlPreviewPlugin::tr("Test translations"), this);
+ action->setToolTip(QLatin1String("Runs the preview with all available translations and collects all issues."));
+ action->setEnabled(SessionManager::startupProject() != nullptr);
+ connect(SessionManager::instance(), &SessionManager::startupProjectChanged, action,
+ &QAction::setEnabled);
+ connect(action, &QAction::triggered, this, [this]() {
+ if (SessionManager::startupProject()) {
+ // Deletion for this widget is taken care of in aboutToShutdown() and registerWindow()
+ m_qmlDebugTranslationWidget = new QmlDebugTranslationWidget();
+ Core::ICore::registerWindow(m_qmlDebugTranslationWidget, Core::Context("Core.DebugTranslation"));
+ m_qmlDebugTranslationWidget->show();
+ }
+ });
+ menu->addAction(
+ Core::ActionManager::registerAction(action, "QmlPreview.TestTranslations"),
+ Constants::G_BUILD_RUN);
+ auto updateTestTranslationAction = [action]() {
+ bool showTestTranslationAction = false;
+ bool enableTestTranslationAction = false;
+ QtSupport::BaseQtVersion *activeQt{};
+ if (auto project = SessionManager::startupProject()) {
+ if (auto target = project->activeTarget()) {
+ if (auto activeKit = target->kit())
+ activeQt = QtSupport::QtKitAspect::qtVersion(activeKit);
+ }
+ }
+ for (auto qtVersion : QtSupport::QtVersionManager::versions()) {
+ if (qtVersion->features().contains("QtStudio")) {
+ showTestTranslationAction = true;
+ if (qtVersion == activeQt)
+ enableTestTranslationAction = true;
+ }
+ }
+ action->setVisible(showTestTranslationAction);
+ action->setEnabled(enableTestTranslationAction);
+ };
+ connect(ProjectExplorer::SessionManager::instance(),
+ &ProjectExplorer::SessionManager::startupProjectChanged,
+ updateTestTranslationAction);
+
+ connect(QtSupport::QtVersionManager::instance(),
+ &QtSupport::QtVersionManager::qtVersionsChanged,
+ updateTestTranslationAction);
- Core::Context projectTreeContext(Constants::C_PROJECT_TREE);
menu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT);
action = new QAction(QmlPreviewPlugin::tr("Preview File"), this);
action->setEnabled(false);
@@ -207,9 +267,9 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent)
action->setEnabled(!previews.isEmpty());
});
connect(action, &QAction::triggered, this, &QmlPreviewPluginPrivate::previewCurrentFile);
- menu->addAction(Core::ActionManager::registerAction(action, "QmlPreview.Preview",
- projectTreeContext),
- Constants::G_FILE_OTHER);
+ menu->addAction(
+ Core::ActionManager::registerAction(action, "QmlPreview.PreviewFile", Core::Context(Constants::C_PROJECT_TREE)),
+ Constants::G_FILE_OTHER);
action->setVisible(false);
connect(ProjectTree::instance(), &ProjectTree::currentNodeChanged, action, [action]() {
const Node *node = ProjectTree::currentNode();
@@ -247,6 +307,7 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlPreviewPlugin::aboutToShutdown()
{
d->m_parseThread.quit();
d->m_parseThread.wait();
+ delete d->m_qmlDebugTranslationWidget;
return SynchronousShutdown;
}
@@ -333,6 +394,8 @@ QString QmlPreviewPlugin::locale() const
void QmlPreviewPlugin::setLocale(const QString &locale)
{
+ if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current())
+ multiLanguageAspect->setCurrentLocale(locale);
if (d->m_locale == locale)
return;
@@ -340,6 +403,16 @@ void QmlPreviewPlugin::setLocale(const QString &locale)
emit localeChanged(d->m_locale);
}
+bool QmlPreviewPlugin::elideWarning() const
+{
+ return d->elideWarning;
+}
+
+void QmlPreviewPlugin::changeElideWarning(bool elideWarning)
+{
+ d->elideWarning = elideWarning;
+}
+
void QmlPreviewPlugin::setFileLoader(QmlPreviewFileLoader fileLoader)
{
if (d->m_fileLoader == fileLoader)
@@ -412,6 +485,10 @@ void QmlPreviewPluginPrivate::setDirty()
void QmlPreviewPluginPrivate::addPreview(ProjectExplorer::RunControl *preview)
{
m_runningPreviews.append(preview);
+ if (auto multiLanguageAspect = preview->aspect<QmlProjectManager::QmlMultiLanguageAspect>()) {
+ connect(multiLanguageAspect, &QmlProjectManager::QmlMultiLanguageAspect::changed,
+ preview, &ProjectExplorer::RunControl::initiateStop);
+ }
emit q->runningPreviewsChanged(m_runningPreviews);
}
diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.h b/src/plugins/qmlpreview/qmlpreviewplugin.h
index df0a59f2b8..13146105ac 100644
--- a/src/plugins/qmlpreview/qmlpreviewplugin.h
+++ b/src/plugins/qmlpreview/qmlpreviewplugin.h
@@ -59,6 +59,7 @@ class QmlPreviewPlugin : public ExtensionSystem::IPlugin
WRITE setFpsHandler NOTIFY fpsHandlerChanged)
Q_PROPERTY(float zoomFactor READ zoomFactor WRITE setZoomFactor NOTIFY zoomFactorChanged)
Q_PROPERTY(QString locale READ locale WRITE setLocale NOTIFY localeChanged)
+ Q_PROPERTY(bool elideWarning READ elideWarning WRITE changeElideWarning NOTIFY elideWarningChanged)
public:
~QmlPreviewPlugin() override;
@@ -86,6 +87,9 @@ public:
QString locale() const;
void setLocale(const QString &locale);
+ bool elideWarning() const;
+ void changeElideWarning(bool elideWarning);
+
signals:
void checkDocument(const QString &name, const QByteArray &contents,
QmlJS::Dialect::Enum dialect);
@@ -100,6 +104,7 @@ signals:
void zoomFactorChanged(float zoomFactor);
void localeChanged(const QString &locale);
+ void elideWarningChanged(bool elideWarning);
private:
class QmlPreviewPluginPrivate *d = nullptr;
diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
index f1caffa1a7..518e33794f 100644
--- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
+++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp
@@ -27,7 +27,6 @@
#include <qmlprojectmanager/qmlproject.h>
#include <qmlprojectmanager/qmlmainfileaspect.h>
-#include <qmlprojectmanager/qmlmultilanguageaspect.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
@@ -50,8 +49,7 @@ QmlPreviewRunner::QmlPreviewRunner(ProjectExplorer::RunControl *runControl,
QmlPreviewFileLoader fileLoader,
QmlPreviewFileClassifier fileClassifier,
QmlPreviewFpsHandler fpsHandler,
- float initialZoom,
- const QString &initialLocale)
+ float initialZoom)
: RunWorker(runControl)
{
setId("QmlPreviewRunner");
@@ -68,12 +66,13 @@ QmlPreviewRunner::QmlPreviewRunner(ProjectExplorer::RunControl *runControl,
&m_connectionManager, &Internal::QmlPreviewConnectionManager::zoom);
connect(this, &QmlPreviewRunner::language,
&m_connectionManager, &Internal::QmlPreviewConnectionManager::language);
+ connect(this, &QmlPreviewRunner::changeElideWarning,
+ &m_connectionManager, &Internal::QmlPreviewConnectionManager::changeElideWarning);
+
connect(&m_connectionManager, &Internal::QmlPreviewConnectionManager::connectionOpened,
- this, [this, initialZoom, initialLocale]() {
+ this, [this, initialZoom]() {
if (initialZoom > 0)
emit zoom(initialZoom);
- if (!initialLocale.isEmpty())
- emit language(initialLocale);
emit ready();
});
@@ -153,11 +152,6 @@ LocalQmlPreviewSupport::LocalQmlPreviewSupport(ProjectExplorer::RunControl *runC
}
}
- if (auto multiLanguageAspect = runControl->aspect<QmlProjectManager::QmlMultiLanguageAspect>()) {
- if (!multiLanguageAspect->databaseFilePath().isEmpty())
- runnable.environment.set("QT_MULTILANGUAGE_DATABASE", multiLanguageAspect->databaseFilePath().toString());
- }
-
Utils::QtcProcess::addArg(&runnable.commandLineArguments,
QmlDebug::qmlDebugLocalArguments(QmlDebug::QmlPreviewServices,
serverUrl.path()));
diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.h b/src/plugins/qmlpreview/qmlpreviewruncontrol.h
index 7a25a62c10..38740b31a3 100644
--- a/src/plugins/qmlpreview/qmlpreviewruncontrol.h
+++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.h
@@ -39,7 +39,7 @@ class QmlPreviewRunner : public ProjectExplorer::RunWorker
public:
QmlPreviewRunner(ProjectExplorer::RunControl *runControl, QmlPreviewFileLoader fileLoader,
QmlPreviewFileClassifier fileClassifier, QmlPreviewFpsHandler fpsHandler,
- float initialZoom, const QString &initialLocale);
+ float initialZoom);
void setServerUrl(const QUrl &serverUrl);
QUrl serverUrl() const;
@@ -51,7 +51,7 @@ signals:
void zoom(float zoomFactor);
void rerun();
void ready();
-
+ void changeElideWarning(bool elideWarning);
private:
void start() override;
void stop() override;
diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
index 64dd8a93d6..1e5e88712e 100644
--- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
+++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp
@@ -30,6 +30,7 @@
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
static bool isMultilanguagePresent()
@@ -46,7 +47,8 @@ static bool isMultilanguagePresent()
static Utils::FilePath getMultilanguageDatabaseFilePath(ProjectExplorer::Target *target)
{
if (target) {
- auto filePath = target->project()->projectDirectory().pathAppended("/multilanguage-experimental-v1.db");
+ auto filePath = target->project()->projectDirectory().pathAppended(
+ "multilanguage-experimental-v2.db");
if (filePath.exists())
return filePath;
}
@@ -81,28 +83,24 @@ QmlMultiLanguageAspect::QmlMultiLanguageAspect(ProjectExplorer::Target *target)
setDefaultValue(!databaseFilePath().isEmpty());
QVariantMap getDefaultValues;
fromMap(getDefaultValues);
-
- if (auto previewPlugin = getPreviewPlugin())
- connect(previewPlugin, SIGNAL(localeChanged(QString)), this, SLOT(setLastUsedLanguage(QString)));
}
QmlMultiLanguageAspect::~QmlMultiLanguageAspect()
{
}
-void QmlMultiLanguageAspect::setLastUsedLanguage(const QString &language)
+void QmlMultiLanguageAspect::setCurrentLocale(const QString &locale)
{
+ if (m_currentLocale == locale)
+ return;
+ m_currentLocale = locale;
if (auto previewPlugin = getPreviewPlugin())
- previewPlugin->setProperty("locale", language);
- if (m_lastUsedLanguage != language) {
- m_lastUsedLanguage = language;
- emit changed();
- }
+ previewPlugin->setProperty("locale", locale);
}
-QString QmlMultiLanguageAspect::lastUsedLanguage() const
+QString QmlMultiLanguageAspect::currentLocale() const
{
- return m_lastUsedLanguage;
+ return m_currentLocale;
}
Utils::FilePath QmlMultiLanguageAspect::databaseFilePath() const
@@ -115,14 +113,37 @@ Utils::FilePath QmlMultiLanguageAspect::databaseFilePath() const
void QmlMultiLanguageAspect::toMap(QVariantMap &map) const
{
BaseBoolAspect::toMap(map);
- if (!m_lastUsedLanguage.isEmpty())
- map.insert(Constants::LAST_USED_LANGUAGE, m_lastUsedLanguage);
+ if (!m_currentLocale.isEmpty())
+ map.insert(Constants::LAST_USED_LANGUAGE, m_currentLocale);
}
void QmlMultiLanguageAspect::fromMap(const QVariantMap &map)
{
BaseBoolAspect::fromMap(map);
- setLastUsedLanguage(map.value(Constants::LAST_USED_LANGUAGE, "en").toString());
+ setCurrentLocale(map.value(Constants::LAST_USED_LANGUAGE, "en").toString());
+}
+
+QmlMultiLanguageAspect *QmlMultiLanguageAspect::current()
+{
+ if (auto project = ProjectExplorer::SessionManager::startupProject())
+ return current(project);
+ return {};
+}
+
+QmlMultiLanguageAspect *QmlMultiLanguageAspect::current(ProjectExplorer::Project *project)
+{
+ if (auto target = project->activeTarget())
+ return current(target);
+ return {};
+}
+
+QmlMultiLanguageAspect *QmlMultiLanguageAspect::current(ProjectExplorer::Target *target)
+{
+ if (auto runConfiguration = target->activeRunConfiguration()) {
+ if (auto multiLanguageAspect = runConfiguration->aspect<QmlProjectManager::QmlMultiLanguageAspect>())
+ return multiLanguageAspect;
+ }
+ return {};
}
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h
index 163552caf0..c98c5e2aec 100644
--- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h
+++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h
@@ -40,18 +40,23 @@ public:
explicit QmlMultiLanguageAspect(ProjectExplorer::Target *target);
~QmlMultiLanguageAspect() override;
- QString lastUsedLanguage() const;
+ QString currentLocale() const;
+ void setCurrentLocale(const QString &locale);
Utils::FilePath databaseFilePath() const;
void toMap(QVariantMap &map) const final;
void fromMap(const QVariantMap &map) final;
-public slots:
- void setLastUsedLanguage(const QString &language);
+ static QmlMultiLanguageAspect *current();
+ static QmlMultiLanguageAspect *current(ProjectExplorer::Project *project);
+ static QmlMultiLanguageAspect *current(ProjectExplorer::Target *target);
+
+signals:
+ void currentLocaleChanged(const QString &locale);
private:
ProjectExplorer::Target *m_target = nullptr;
mutable Utils::FilePath m_databaseFilePath;
- QString m_lastUsedLanguage;
+ QString m_currentLocale;
};
} // namespace QmlProjectManager
diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp
index 7d6297db94..9b4820586e 100644
--- a/src/plugins/qmlprojectmanager/qmlproject.cpp
+++ b/src/plugins/qmlprojectmanager/qmlproject.cpp
@@ -372,7 +372,7 @@ void QmlBuildSystem::generateProjectTree()
auto newRoot = std::make_unique<QmlProjectNode>(project());
- for (const QString &f : m_projectItem.data()->files()) {
+ for (const QString &f : m_projectItem->files()) {
const Utils::FilePath fileName = Utils::FilePath::fromString(f);
const FileType fileType = (fileName == projectFilePath())
? FileType::Project : FileNode::fileTypeForFileName(fileName);
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index a0b77fffb7..7c30dfc156 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -117,7 +117,10 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id)
if (m_multiLanguageAspect && m_multiLanguageAspect->value() && !m_multiLanguageAspect->databaseFilePath().isEmpty()) {
env.set("QT_MULTILANGUAGE_DATABASE", m_multiLanguageAspect->databaseFilePath().toString());
- env.set("QT_MULTILANGUAGE_LANGUAGE", m_multiLanguageAspect->lastUsedLanguage());
+ env.set("QT_MULTILANGUAGE_LANGUAGE", m_multiLanguageAspect->currentLocale());
+ } else {
+ env.unset("QT_MULTILANGUAGE_DATABASE");
+ env.unset("QT_MULTILANGUAGE_LANGUAGE");
}
return env;
};
diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt
index 26285896ec..2b80c8ca29 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.h
)
extend_qtc_executable(qml2puppet
@@ -155,6 +156,7 @@ extend_qtc_executable(qml2puppet
quick3dtexturenodeinstance.cpp quick3dtexturenodeinstance.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 a09f106d76..3aab61eb9c 100644
--- a/src/tools/qml2puppet/qml2puppet.qbs
+++ b/src/tools/qml2puppet/qml2puppet.qbs
@@ -116,6 +116,7 @@ QtcTool {
"commands/inputeventcommand.h",
"commands/view3dactioncommand.cpp",
"commands/view3dactioncommand.h",
+ "commands/captureddatacommand.h",
"container/addimportcontainer.cpp",
"container/addimportcontainer.h",
"container/idcontainer.cpp",
@@ -218,6 +219,8 @@ QtcTool {
"instances/qt5testnodeinstanceserver.h",
"instances/servernodeinstance.cpp",
"instances/servernodeinstance.h",
+ "instances/qt5capturenodeinstanceserver.cpp",
+ "instances/qt5capturenodeinstanceserver.h",
"editor3d/generalhelper.cpp",
"editor3d/mousearea3d.cpp",
"editor3d/camerageometry.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 7afb0fda2e..c179025da5 100644
--- a/tests/auto/qml/qmldesigner/coretests/coretests.pro
+++ b/tests/auto/qml/qmldesigner/coretests/coretests.pro
@@ -61,11 +61,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 fc5d9dcfa9..965d9b7aee 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
diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp
index ca0913f865..bdd5e9c070 100644
--- a/tests/unit/unittest/listmodeleditor-test.cpp
+++ b/tests/unit/unittest/listmodeleditor-test.cpp
@@ -31,14 +31,17 @@
#include <qmldesigner/components/listmodeleditor/listmodeleditormodel.h>
#include <qmldesigner/designercore/include/abstractview.h>
+#include <qmldesigner/designercore/include/bindingproperty.h>
#include <qmldesigner/designercore/include/model.h>
#include <qmldesigner/designercore/include/nodelistproperty.h>
+#include <qmldesigner/designercore/include/nodeproperty.h>
#include <qmldesigner/designercore/include/variantproperty.h>
namespace {
using QmlDesigner::AbstractProperty;
using QmlDesigner::AbstractView;
+using QmlDesigner::ListModelEditorModel;
using QmlDesigner::ModelNode;
MATCHER_P2(HasItem,
@@ -93,6 +96,7 @@ public:
emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
+ listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15);
listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15);
mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode);
element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}});
@@ -172,10 +176,20 @@ public:
return properties;
}
+ QModelIndex index(int row, int column) const { return model.index(row, column); }
+
+ QList<ModelNode> elements(const ModelNode &node) const
+ {
+ return node.defaultNodeListProperty().toModelNodeList();
+ }
+
protected:
std::unique_ptr<QmlDesigner::Model> designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)};
NiceMock<MockListModelEditorView> mockView;
- QmlDesigner::ListModelEditorModel model;
+ QmlDesigner::ListModelEditorModel model{
+ [&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); },
+ [&] { return mockView.createModelNode("QtQml.Models.ListElement", 2, 15); }};
+ ModelNode listViewNode;
ModelNode listModelNode;
ModelNode emptyListModelNode;
ModelNode element1;
@@ -427,7 +441,7 @@ TEST_F(ListModelEditor, RemoveColumnRemovesDisplayValues)
{
model.setListModel(listModelNode);
- model.removeColumn(2);
+ model.removeColumns({index(0, 2)});
ASSERT_THAT(displayValues(),
ElementsAre(ElementsAre(IsInvalid(), "foo", 42),
@@ -442,14 +456,14 @@ TEST_F(ListModelEditor, RemoveColumnRemovesProperties)
EXPECT_CALL(mockView, propertiesRemoved(ElementsAre(IsAbstractProperty(element2, "image"))));
EXPECT_CALL(mockView, propertiesRemoved(ElementsAre(IsAbstractProperty(element3, "image"))));
- model.removeColumn(0);
+ model.removeColumns({index(0, 0)});
}
TEST_F(ListModelEditor, RemoveColumnRemovesPropertyName)
{
model.setListModel(listModelNode);
- model.removeColumn(1);
+ model.removeColumns({index(0, 1)});
ASSERT_THAT(model.propertyNames(), ElementsAre("image", "value", "value2"));
}
@@ -458,7 +472,7 @@ TEST_F(ListModelEditor, RemoveRowRemovesDisplayValues)
{
model.setListModel(listModelNode);
- model.removeRow(1);
+ model.removeRows({index(1, 0)});
ASSERT_THAT(displayValues(),
ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
@@ -471,7 +485,7 @@ TEST_F(ListModelEditor, RemoveRowRemovesElementInListModel)
EXPECT_CALL(mockView, nodeRemoved(Eq(element2), _, _));
- model.removeRow(1);
+ model.removeRows({index(1, 0)});
}
TEST_F(ListModelEditor, ConvertStringFloatToFloat)
@@ -721,7 +735,7 @@ TEST_F(ListModelEditor, RemoveColumnAfterRenameColumn)
model.setListModel(listModelNode);
model.renameColumn(1, "mood");
- model.removeColumn(1);
+ model.removeColumns({index(0, 1)});
ASSERT_THAT(properties(),
ElementsAre(UnorderedElementsAre(IsVariantProperty("value", 1),
@@ -909,44 +923,457 @@ TEST_F(ListModelEditor, RemoveLastRow)
model.addColumn("mood");
model.addRow();
- model.removeRow(0);
+ model.removeRows({index(0, 0)});
ASSERT_THAT(displayValues(), IsEmpty());
}
-TEST_F(ListModelEditor, RemoveLastColumn)
+TEST_F(ListModelEditor, RemoveLastEmptyRow)
{
model.setListModel(emptyListModelNode);
model.addColumn("mood");
model.addRow();
+ model.removeColumns({index(0, 0)});
- model.removeColumn(0);
+ model.removeRows({index(0, 0)});
ASSERT_THAT(displayValues(), ElementsAre(IsEmpty()));
}
-TEST_F(ListModelEditor, RemoveLastEmptyColumn)
+TEST_F(ListModelEditor, RemoveLastColumn)
{
model.setListModel(emptyListModelNode);
model.addColumn("mood");
model.addRow();
- model.removeRow(0);
- model.removeColumn(0);
+ model.removeColumns({index(0, 0)});
- ASSERT_THAT(displayValues(), IsEmpty());
+ ASSERT_THAT(displayValues(), ElementsAre(IsEmpty()));
}
-TEST_F(ListModelEditor, RemoveLastEmptyRow)
+TEST_F(ListModelEditor, RemoveLastEmptyColumn)
{
model.setListModel(emptyListModelNode);
model.addColumn("mood");
model.addRow();
- model.removeColumn(0);
+ model.removeRows({index(0, 0)});
- model.removeRow(0);
+ model.removeColumns({index(0, 0)});
ASSERT_THAT(displayValues(), IsEmpty());
}
+TEST_F(ListModelEditor, RemoveColumns)
+{
+ model.setListModel(listModelNode);
+ model.removeColumns({index(0, 1), index(0, 3), index(1, 1), index(0, 4)});
+
+ ASSERT_THAT(properties(),
+ ElementsAre(UnorderedElementsAre(IsVariantProperty("value", 1)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("value", 4)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("value", 111))));
+}
+
+TEST_F(ListModelEditor, RemoveRows)
+{
+ model.setListModel(listModelNode);
+
+ model.removeRows({index(1, 0), index(2, 0), index(3, 0), index(2, 0)});
+
+ ASSERT_THAT(properties(),
+ ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"),
+ IsVariantProperty("value", 1),
+ IsVariantProperty("value2", 42))));
+}
+
+TEST_F(ListModelEditor, FilterColumns)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(0, 0), index(1, 1), index(0, 2), index(0, 1)};
+
+ auto columns = ListModelEditorModel::filterColumns(indices);
+
+ ASSERT_THAT(columns, ElementsAre(0, 1, 2));
+}
+
+TEST_F(ListModelEditor, FilterColumnsInvalidColumns)
+{
+ QList<QModelIndex> indices = {index(0, 0), index(1, 1), index(0, 2), index(0, 1)};
+
+ auto columns = ListModelEditorModel::filterColumns(indices);
+
+ ASSERT_THAT(columns, IsEmpty());
+}
+
+TEST_F(ListModelEditor, FilterColumnsEmptyInput)
+{
+ QList<QModelIndex> indices;
+
+ auto columns = ListModelEditorModel::filterColumns(indices);
+
+ ASSERT_THAT(columns, IsEmpty());
+}
+
+TEST_F(ListModelEditor, FilterRows)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(0, 0), index(1, 1), index(2, 2), index(0, 1)};
+
+ auto rows = ListModelEditorModel::filterRows(indices);
+
+ ASSERT_THAT(rows, ElementsAre(0, 1, 2));
+}
+
+TEST_F(ListModelEditor, FilterRowsInvalidColumns)
+{
+ QList<QModelIndex> indices = {index(0, 0), index(1, 1), index(2, 2), index(0, 1)};
+
+ auto rows = ListModelEditorModel::filterRows(indices);
+
+ ASSERT_THAT(rows, IsEmpty());
+}
+
+TEST_F(ListModelEditor, FilterRowsEmptyInput)
+{
+ QList<QModelIndex> indices;
+
+ auto rows = ListModelEditorModel::filterRows(indices);
+
+ ASSERT_THAT(rows, IsEmpty());
+}
+
+TEST_F(ListModelEditor, CannotMoveEmptyRowsUp)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(-1, 1)};
+
+ model.moveRowsUp(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3));
+}
+
+TEST_F(ListModelEditor, MoveRowUp)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(1, 2), index(1, 0)};
+
+ model.moveRowsUp(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element2, element1, element3));
+}
+
+TEST_F(ListModelEditor, MoveRowsUp)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(2, 2), index(1, 0)};
+
+ model.moveRowsUp(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element2, element3, element1));
+}
+
+TEST_F(ListModelEditor, CannotMoveFirstRowsUp)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(0, 1), index(1, 2), index(0, 0)};
+
+ model.moveRowsUp(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3));
+}
+
+TEST_F(ListModelEditor, CannotMoveEmptyRowsUpDisplayValues)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(-1, 1)};
+
+ model.moveRowsUp(indices);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, CannotMoveFirstRowUpDisplayValues)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(0, 1), index(1, 2), index(0, 0)};
+
+ model.moveRowsUp(indices);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, MoveRowsUpDisplayValues)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(2, 2), index(1, 0)};
+
+ model.moveRowsUp(indices);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid()),
+ ElementsAre(IsInvalid(), "foo", 1, 42)));
+}
+
+TEST_F(ListModelEditor, NoSelectionAfterCannotMoveLastRowsDown)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(0, 1), index(1, 2), index(0, 0)};
+
+ auto selection = model.moveRowsUp(indices);
+
+ ASSERT_THAT(selection.indexes(), IsEmpty());
+}
+
+TEST_F(ListModelEditor, NoSelectionAfterMoveEmptyRowsDown)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(-1, 1)};
+
+ auto selection = model.moveRowsUp(indices);
+
+ ASSERT_THAT(selection.indexes(), IsEmpty());
+}
+
+TEST_F(ListModelEditor, SelectionAfterMoveRowsDown)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(2, 2), index(1, 0)};
+
+ auto selection = model.moveRowsUp(indices);
+
+ ASSERT_THAT(selection.indexes(),
+ ElementsAre(index(0, 0),
+ index(0, 1),
+ index(0, 2),
+ index(0, 3),
+ index(1, 0),
+ index(1, 1),
+ index(1, 2),
+ index(1, 3)));
+}
+
+TEST_F(ListModelEditor, CannotMoveEmptyRowsDown)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(-1, 1)};
+
+ model.moveRowsDown(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3));
+}
+
+TEST_F(ListModelEditor, MoveRowDown)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(1, 2), index(1, 0)};
+
+ model.moveRowsDown(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element3, element2));
+}
+
+TEST_F(ListModelEditor, MoveRowsDown)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(0, 2), index(1, 0)};
+
+ model.moveRowsDown(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element3, element1, element2));
+}
+
+TEST_F(ListModelEditor, CannotMoveLastRowsDown)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(2, 1), index(1, 2), index(2, 0)};
+
+ model.moveRowsDown(indices);
+
+ ASSERT_THAT(elements(listModelNode), ElementsAre(element1, element2, element3));
+}
+
+TEST_F(ListModelEditor, CannotMoveEmptyRowsDownDisplayValues)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(-1, 1)};
+
+ model.moveRowsDown(indices);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, CannotMoveLastRowDownDisplayValues)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(2, 1), index(1, 2), index(2, 0)};
+
+ model.moveRowsDown(indices);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, MoveRowsDownDisplayValues)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(0, 2), index(1, 0)};
+
+ model.moveRowsDown(indices);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre("pic.png", "poo", 111, IsInvalid()),
+ ElementsAre(IsInvalid(), "foo", 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, NoSelectionAfterCannotMoveLastRowsUp)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(2, 1), index(1, 2), index(2, 0)};
+
+ auto selection = model.moveRowsDown(indices);
+
+ ASSERT_THAT(selection.indexes(), IsEmpty());
+}
+
+TEST_F(ListModelEditor, NoSelectionAfterMoveEmptyRowsUp)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(-1, 1)};
+
+ auto selection = model.moveRowsDown(indices);
+
+ ASSERT_THAT(selection.indexes(), IsEmpty());
+}
+
+TEST_F(ListModelEditor, SelectionAfterMoveRowsUp)
+{
+ model.setListModel(listModelNode);
+ QList<QModelIndex> indices = {index(1, 1), index(0, 2), index(1, 0)};
+
+ auto selection = model.moveRowsDown(indices);
+
+ ASSERT_THAT(selection.indexes(),
+ ElementsAre(index(1, 0),
+ index(1, 1),
+ index(1, 2),
+ index(1, 3),
+ index(2, 0),
+ index(2, 1),
+ index(2, 2),
+ index(2, 3)));
+}
+
+TEST_F(ListModelEditor, ListViewHasNoModel)
+{
+ model.setListView(listViewNode);
+
+ ASSERT_THAT(listViewNode.nodeProperty("model").modelNode().type(), Eq("QtQml.Models.ListModel"));
+}
+
+TEST_F(ListModelEditor, ListViewHasModelInside)
+{
+ listViewNode.nodeProperty("model").reparentHere(listModelNode);
+
+ model.setListView(listViewNode);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, ListViewHasModelBinding)
+{
+ listModelNode.setIdWithoutRefactoring("listModel");
+ listViewNode.bindingProperty("model").setExpression("listModel");
+
+ model.setListView(listViewNode);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, AddBooleanDisplayValues)
+{
+ model.setListModel(listModelNode);
+
+ model.setValue(0, 1, true);
+
+ ASSERT_THAT(displayValues(),
+ ElementsAre(ElementsAre(IsInvalid(), true, 1, 42),
+ ElementsAre("pic.png", "bar", 4, IsInvalid()),
+ ElementsAre("pic.png", "poo", 111, IsInvalid())));
+}
+
+TEST_F(ListModelEditor, AddBooleanProperties)
+{
+ model.setListModel(listModelNode);
+
+ model.setValue(0, 1, true);
+
+ ASSERT_THAT(properties(),
+ ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"),
+ IsVariantProperty("value", true),
+ IsVariantProperty("value2", 42)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("name", "bar"),
+ IsVariantProperty("value", 4)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("name", "poo"),
+ IsVariantProperty("value", 111))));
+}
+
+TEST_F(ListModelEditor, AddTrueAsStringProperties)
+{
+ model.setListModel(listModelNode);
+
+ model.setValue(0, 1, "true");
+
+ ASSERT_THAT(properties(),
+ ElementsAre(UnorderedElementsAre(IsVariantProperty("name", true),
+ IsVariantProperty("value", 1),
+ IsVariantProperty("value2", 42)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("name", "bar"),
+ IsVariantProperty("value", 4)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("name", "poo"),
+ IsVariantProperty("value", 111))));
+}
+
+TEST_F(ListModelEditor, AddFalseAsStringProperties)
+{
+ model.setListModel(listModelNode);
+
+ model.setValue(0, 1, "false");
+
+ ASSERT_THAT(properties(),
+ ElementsAre(UnorderedElementsAre(IsVariantProperty("name", false),
+ IsVariantProperty("value", 1),
+ IsVariantProperty("value2", 42)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("name", "bar"),
+ IsVariantProperty("value", 4)),
+ UnorderedElementsAre(IsVariantProperty("image", "pic.png"),
+ IsVariantProperty("name", "poo"),
+ IsVariantProperty("value", 111))));
+}
+
} // namespace