summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/nodes/qnode.cpp18
-rw-r--r--src/quick3d/quick3d/qt3dquick_global.cpp27
-rw-r--r--tests/auto/quick3d/dynamicnodecreation/data/createDynamicChild.qml19
-rw-r--r--tests/auto/quick3d/dynamicnodecreation/data/createMultiple.qml13
-rw-r--r--tests/auto/quick3d/dynamicnodecreation/data/createSingle.qml7
-rw-r--r--tests/auto/quick3d/dynamicnodecreation/data/dynamicEntity.qml8
-rw-r--r--tests/auto/quick3d/dynamicnodecreation/dynamicnodecreation.pro20
-rw-r--r--tests/auto/quick3d/dynamicnodecreation/tst_dynamicnodecreation.cpp195
-rw-r--r--tests/auto/quick3d/quick3d.pro3
9 files changed, 307 insertions, 3 deletions
diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp
index 4cdd9ca7b..871eaed39 100644
--- a/src/core/nodes/qnode.cpp
+++ b/src/core/nodes/qnode.cpp
@@ -115,6 +115,10 @@ void QNodePrivate::init(QNode *parent)
void QNodePrivate::notifyCreationChange()
{
Q_Q(QNode);
+ // Do nothing if we already have already sent a node creation change
+ // and not a subsequent node destroyed change.
+ if (m_hasBackendNode)
+ return;
QNodeCreatedChangeGenerator generator(q);
const auto creationChanges = generator.creationChanges();
for (const auto &change : creationChanges)
@@ -265,6 +269,13 @@ void QNodePrivate::_q_setParentHelper(QNode *parent)
Q_Q(QNode);
QNode *oldParentNode = q->parentNode();
+ // We may get the situation where the QML engine has set the QObject
+ // parent but we have not yet set up the QNode parent requirements.
+ // This check handles this and means we propagate the scene and arbiter
+ // from the parent in the code below.
+ const bool needsSceneInit = !oldParentNode
+ || (oldParentNode && m_parentId != oldParentNode->id());
+
// If we had a parent, we let him know that we are about to change
// parent
if (oldParentNode) {
@@ -283,7 +294,7 @@ void QNodePrivate::_q_setParentHelper(QNode *parent)
if (newParentNode) {
// If we had no parent but are about to set one,
// we need to send a QNodeCreatedChange
- if (!oldParentNode) {
+ if (needsSceneInit) {
QNodePrivate *newParentPrivate = QNodePrivate::get(newParentNode);
// Set the scene helper / arbiter
@@ -696,7 +707,10 @@ bool QNode::blockNotifications(bool block)
void QNode::setParent(QNode *parent)
{
Q_D(QNode);
- if (parentNode() == parent)
+
+ // If we already have a parent don't do anything. Be careful to ensure
+ // that QNode knows about the parent, not just QObject (by checking the ids)
+ if (parentNode() == parent && d->m_parentId == parentNode()->id())
return;
d->_q_setParentHelper(parent);
diff --git a/src/quick3d/quick3d/qt3dquick_global.cpp b/src/quick3d/quick3d/qt3dquick_global.cpp
index 78523aa4a..f25aae268 100644
--- a/src/quick3d/quick3d/qt3dquick_global.cpp
+++ b/src/quick3d/quick3d/qt3dquick_global.cpp
@@ -669,12 +669,39 @@ static Quick3DColorProvider *getColorProvider()
return &colorProvider;
}
+static QQmlPrivate::AutoParentResult qquick3ditem_autoParent(QObject *obj, QObject *parent)
+{
+ // When setting a parent (especially during dynamic object creation) in QML,
+ // also try to set up the analogous item/window relationship.
+ auto parentNode = qmlobject_cast<Qt3DCore::QNode *>(parent);
+ if (parentNode) {
+ auto node = qmlobject_cast<Qt3DCore::QNode *>(obj);
+ if (node) {
+ // A QNode has another QNode child
+ node->setParent(parentNode);
+ return QQmlPrivate::Parented;
+ }
+ } else {
+ return QQmlPrivate::IncompatibleParent;
+ }
+ return QQmlPrivate::IncompatibleObject;
+}
+
void Quick3D_initialize()
{
Qt3DCore::Quick::Quick3DValueTypes::registerValueTypes();
QQml_addValueTypeProvider(getValueTypeProvider());
QQml_setColorProvider(getColorProvider());
QAbstractNodeFactory::registerNodeFactory(QuickNodeFactory::instance());
+
+ // Register a hook called when we do component.create() that sets the
+ // parent. We need this as QObject::setParent() is insufficient to propagate
+ // the arbiter and scene to the children (see QNode::setParent(QNode *).
+ // TODO: Replace this with virtual void QObjectPrivate::setParent(QObject *)
+ // that can be called from QObject ctor and QObject::setParent(). That would
+ // allow removal of this hook here and in QtQuick.
+ QQmlPrivate::RegisterAutoParent autoparent = { 0, &qquick3ditem_autoParent };
+ QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
}
void Quick3D_registerType(const char *className, const char *quickName, int major, int minor)
diff --git a/tests/auto/quick3d/dynamicnodecreation/data/createDynamicChild.qml b/tests/auto/quick3d/dynamicnodecreation/data/createDynamicChild.qml
new file mode 100644
index 000000000..bdbea280e
--- /dev/null
+++ b/tests/auto/quick3d/dynamicnodecreation/data/createDynamicChild.qml
@@ -0,0 +1,19 @@
+import QtQml 2.1
+import Qt3D.Core 2.0
+
+Entity {
+ id: root
+ property bool success: true
+ property int value: 12345
+
+ Timer {
+ interval: 0
+ repeat: false
+ running: true
+
+ onTriggered: {
+ var factory = Qt.createComponent("dynamicEntity.qml");
+ var dynamicChild = factory.createObject(root, { "value": 27182818 });
+ }
+ }
+}
diff --git a/tests/auto/quick3d/dynamicnodecreation/data/createMultiple.qml b/tests/auto/quick3d/dynamicnodecreation/data/createMultiple.qml
new file mode 100644
index 000000000..c42f2d796
--- /dev/null
+++ b/tests/auto/quick3d/dynamicnodecreation/data/createMultiple.qml
@@ -0,0 +1,13 @@
+import QtQml 2.1
+import Qt3D.Core 2.0
+
+Entity {
+ property bool success: true
+ property int value: 12345
+
+ Entity {
+ objectName: "childEntity"
+ property bool success: false
+ property int value: 54321
+ }
+}
diff --git a/tests/auto/quick3d/dynamicnodecreation/data/createSingle.qml b/tests/auto/quick3d/dynamicnodecreation/data/createSingle.qml
new file mode 100644
index 000000000..c28aa08eb
--- /dev/null
+++ b/tests/auto/quick3d/dynamicnodecreation/data/createSingle.qml
@@ -0,0 +1,7 @@
+import QtQml 2.1
+import Qt3D.Core 2.0
+
+Entity {
+ property bool success: true
+ property int value: 12345
+}
diff --git a/tests/auto/quick3d/dynamicnodecreation/data/dynamicEntity.qml b/tests/auto/quick3d/dynamicnodecreation/data/dynamicEntity.qml
new file mode 100644
index 000000000..300e03369
--- /dev/null
+++ b/tests/auto/quick3d/dynamicnodecreation/data/dynamicEntity.qml
@@ -0,0 +1,8 @@
+import QtQml 2.1
+import Qt3D.Core 2.0
+
+Entity {
+ objectName: "dynamicChildEntity"
+ property bool success: false
+ property int value: 54321
+}
diff --git a/tests/auto/quick3d/dynamicnodecreation/dynamicnodecreation.pro b/tests/auto/quick3d/dynamicnodecreation/dynamicnodecreation.pro
new file mode 100644
index 000000000..9e1292da8
--- /dev/null
+++ b/tests/auto/quick3d/dynamicnodecreation/dynamicnodecreation.pro
@@ -0,0 +1,20 @@
+CONFIG += testcase
+TARGET = tst_dynamicnodecreation
+osx:CONFIG -= app_bundle
+
+INCLUDEPATH += ../../shared/
+SOURCES += \
+ tst_dynamicnodecreation.cpp
+
+OTHER_FILES = \
+ data/createDynamicChild.qml \
+ data/createMultiple.qml \
+ data/createSingle.qml \
+ data/inactive.qml \
+ data/dynamicEntity.qml
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core-private gui-private 3dcore 3dcore-private 3dquick 3dquick-private testlib
diff --git a/tests/auto/quick3d/dynamicnodecreation/tst_dynamicnodecreation.cpp b/tests/auto/quick3d/dynamicnodecreation/tst_dynamicnodecreation.cpp
new file mode 100644
index 000000000..8050451bb
--- /dev/null
+++ b/tests/auto/quick3d/dynamicnodecreation/tst_dynamicnodecreation.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../../shared/util.h"
+
+#include <qtest.h>
+#include <QSignalSpy>
+#include <QDebug>
+
+#include <Qt3DQuick/qqmlaspectengine.h>
+
+#include <Qt3DCore/qentity.h>
+#include <Qt3DCore/qnode.h>
+#include <Qt3DCore/private/qnode_p.h>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlcontext.h>
+
+using namespace Qt3DCore::Quick;
+using namespace Qt3DCore;
+
+class tst_dynamicnodecreation : public QQmlDataTest
+{
+ Q_OBJECT
+
+private slots:
+ void createSingleEntityViaQmlEngine();
+ void createSingleEntityViaAspectEngine();
+ void createMultipleEntitiesViaAspectEngine();
+ void createEntityAndDynamicChild();
+};
+
+void tst_dynamicnodecreation::createSingleEntityViaQmlEngine()
+{
+ // GIVEN
+ QQmlAspectEngine engine;
+
+ // WHEN
+ QQmlEngine *qmlEngine = engine.qmlEngine();
+
+ // THEN
+ QVERIFY(qmlEngine != nullptr);
+
+ // WHEN
+ QQmlComponent component(qmlEngine, testFileUrl("createSingle.qml"));
+ auto entity = qobject_cast<QEntity *>(component.create());
+
+ // THEN
+ QVERIFY(entity != nullptr);
+ QVERIFY(entity->parent() == nullptr);
+ QCOMPARE(entity->property("success").toBool(), true);
+ QCOMPARE(entity->property("value").toInt(), 12345);
+
+ // WHEN
+ auto nodeD = Qt3DCore::QNodePrivate::get(entity);
+
+ // THEN
+ QVERIFY(nodeD->m_scene == nullptr);
+ QVERIFY(nodeD->m_changeArbiter == nullptr);
+ QCOMPARE(nodeD->m_id.isNull(), false);
+}
+
+void tst_dynamicnodecreation::createSingleEntityViaAspectEngine()
+{
+ // GIVEN
+ QQmlAspectEngine engine;
+
+ // WHEN
+ engine.setSource(testFileUrl("createSingle.qml"));
+ auto entity = engine.aspectEngine()->rootEntity().data();
+
+ // THEN
+ QVERIFY(entity != nullptr);
+ QVERIFY(entity->parent() == engine.aspectEngine());
+ QCOMPARE(entity->property("success").toBool(), true);
+ QCOMPARE(entity->property("value").toInt(), 12345);
+
+ // WHEN
+ auto nodeD = QNodePrivate::get(entity);
+
+ // THEN
+ QVERIFY(nodeD->m_scene != nullptr);
+ QVERIFY(nodeD->m_changeArbiter != nullptr);
+ QCOMPARE(nodeD->m_id.isNull(), false);
+}
+
+void tst_dynamicnodecreation::createMultipleEntitiesViaAspectEngine()
+{
+ // GIVEN
+ QQmlAspectEngine engine;
+
+ // WHEN
+ engine.setSource(testFileUrl("createMultiple.qml"));
+ auto entity = engine.aspectEngine()->rootEntity().data();
+
+ // THEN
+ QVERIFY(entity != nullptr);
+ QVERIFY(entity->parent() == engine.aspectEngine());
+ QCOMPARE(entity->property("success").toBool(), true);
+ QCOMPARE(entity->property("value").toInt(), 12345);
+ QCOMPARE(entity->childNodes().size(), 1);
+
+ // WHEN
+ auto nodeD = Qt3DCore::QNodePrivate::get(entity);
+
+ // THEN
+ QVERIFY(nodeD->m_scene != nullptr);
+ QVERIFY(nodeD->m_changeArbiter != nullptr);
+ QCOMPARE(nodeD->m_id.isNull(), false);
+
+ // WHEN
+ auto child = qobject_cast<QEntity *>(entity->childNodes().first());
+
+ // THEN
+ QCOMPARE(child->objectName(), QLatin1String("childEntity"));
+ QCOMPARE(child->property("success").toBool(), false);
+ QCOMPARE(child->property("value").toInt(), 54321);
+ QCOMPARE(child->childNodes().size(), 0);
+ QCOMPARE(child->parentEntity(), entity);
+}
+
+void tst_dynamicnodecreation::createEntityAndDynamicChild()
+{
+ // GIVEN
+ QQmlAspectEngine engine;
+
+ // WHEN
+ engine.setSource(testFileUrl("createDynamicChild.qml"));
+ auto entity = engine.aspectEngine()->rootEntity().data();
+
+ // THEN
+ QVERIFY(entity != nullptr);
+ QVERIFY(entity->parent() == engine.aspectEngine());
+ QCOMPARE(entity->property("success").toBool(), true);
+ QCOMPARE(entity->property("value").toInt(), 12345);
+ QCOMPARE(entity->childNodes().size(), 0);
+
+ // WHEN
+ auto *nodeD = Qt3DCore::QNodePrivate::get(entity);
+
+ // THEN
+ QVERIFY(nodeD->m_scene != nullptr);
+ QVERIFY(nodeD->m_changeArbiter != nullptr);
+ QCOMPARE(nodeD->m_id.isNull(), false);
+
+ // WHEN
+ //QGuiApplication::processEvents(QEventLoop::AllEvents, 10);
+ QGuiApplication::processEvents();
+ auto child = qobject_cast<QEntity *>(entity->childNodes().first());
+
+ // THEN
+ QCOMPARE(child->objectName(), QLatin1String("dynamicChildEntity"));
+ QCOMPARE(child->property("success").toBool(), false);
+ QCOMPARE(child->property("value").toInt(), 27182818);
+ QCOMPARE(child->childNodes().size(), 0);
+ QCOMPARE(child->parentEntity(), entity);
+
+ // WHEN
+ auto *childD = Qt3DCore::QNodePrivate::get(child);
+
+ // THEN
+ QCOMPARE(childD->m_scene, nodeD->m_scene);
+ QCOMPARE(childD->m_changeArbiter, nodeD->m_changeArbiter);
+ QCOMPARE(childD->m_id.isNull(), false);
+}
+
+QTEST_MAIN(tst_dynamicnodecreation)
+
+#include "tst_dynamicnodecreation.moc"
diff --git a/tests/auto/quick3d/quick3d.pro b/tests/auto/quick3d/quick3d.pro
index 898265ab6..782e68500 100644
--- a/tests/auto/quick3d/quick3d.pro
+++ b/tests/auto/quick3d/quick3d.pro
@@ -2,5 +2,6 @@ TEMPLATE = subdirs
contains(QT_CONFIG, private_tests) {
SUBDIRS += \
- quick3dnodeinstantiator
+ quick3dnodeinstantiator \
+ dynamicnodecreation
}