diff options
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 } |