From 3de900a10c6fd051ba54727be2fd1fe47ed10481 Mon Sep 17 00:00:00 2001 From: Svenn-Arne Dragly Date: Fri, 22 Feb 2019 13:50:30 +0100 Subject: Make sure backend node is created when component is added to Entity In addition, the parent of the component and all further ancestors need to be registered on the backend. We do this by calling QNodePrivate::_q_postConstructorInit on all these nodes. Also add autotest that triggers the case referenced in QTBUG-72236 to avoid regressions. Change-Id: Ibf8f43654d145ea8b8082b2f30123ea65e42ff55 Fixes: QTBUG-72236 Reviewed-by: Paul Lemire Reviewed-by: James Turner --- src/core/nodes/qentity.cpp | 2 + src/core/nodes/qnode.cpp | 23 ++++++++++ src/core/nodes/qnode_p.h | 1 + tests/auto/core/nodes/tst_nodes.cpp | 84 +++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp index 64ea65087..2ef78deb6 100644 --- a/src/core/nodes/qentity.cpp +++ b/src/core/nodes/qentity.cpp @@ -152,6 +152,8 @@ void QEntity::addComponent(QComponent *comp) if (!comp->parent()) comp->setParent(this); + QNodePrivate::get(comp)->_q_ensureBackendNodeCreated(); + d->m_components.append(comp); // Ensures proper bookkeeping diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp index 8143ccc75..c2373b805 100644 --- a/src/core/nodes/qnode.cpp +++ b/src/core/nodes/qnode.cpp @@ -505,6 +505,29 @@ void QNodePrivate::setArbiter(QLockableObserverInterface *arbiter) registerNotifiedProperties(); } +/*! + * \internal + * Makes sure this node has a backend by traversing the tree up to the most distant ancestor + * without a backend node and initializing that node. This is done to make sure the parent nodes + * are always created before the child nodes, since child nodes reference parent nodes at creation + * time. + */ +void QNodePrivate::_q_ensureBackendNodeCreated() +{ + if (m_hasBackendNode) + return; + + Q_Q(QNode); + + QNode *nextNode = q; + QNode *topNodeWithoutBackend = nullptr; + while (nextNode != nullptr && !QNodePrivate::get(nextNode)->m_hasBackendNode) { + topNodeWithoutBackend = nextNode; + nextNode = nextNode->parentNode(); + } + QNodePrivate::get(topNodeWithoutBackend)->_q_postConstructorInit(); +} + /*! \class Qt3DCore::QNode \inherits QObject diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h index 73893cc1e..6ffb19ce8 100644 --- a/src/core/nodes/qnode_p.h +++ b/src/core/nodes/qnode_p.h @@ -152,6 +152,7 @@ public: static const QMetaObject *findStaticMetaObject(const QMetaObject *metaObject); void _q_postConstructorInit(); + void _q_ensureBackendNodeCreated(); private: void notifyCreationChange(); diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp index 0106d2dc3..3f7fb4a75 100644 --- a/tests/auto/core/nodes/tst_nodes.cpp +++ b/tests/auto/core/nodes/tst_nodes.cpp @@ -89,6 +89,7 @@ private slots: void appendingComponentToEntity(); void appendingParentlessComponentToEntityWithoutScene(); void appendingParentlessComponentToEntityWithScene(); + void appendingParentlessComponentToNonRootEntity(); void removingComponentFromEntity(); void changeCustomProperty(); @@ -1322,6 +1323,89 @@ void tst_Nodes::appendingParentlessComponentToEntityWithoutScene() } } +void tst_Nodes::appendingParentlessComponentToNonRootEntity() +{ + // GIVEN + ObserverSpy eventSpy; + Qt3DCore::QScene scene; + + { + QScopedPointer root(new MyQEntity()); + root->setArbiterAndScene(&eventSpy, &scene); + root->setSimulateBackendCreated(true); + + QCoreApplication::processEvents(); + + QScopedPointer entity(new MyQEntity(root.data())); + MyQComponent *comp = new MyQComponent(); + + // THEN + QVERIFY(root->parentNode() == nullptr); + QVERIFY(root->children().count() == 1); + QVERIFY(root->components().empty()); + QVERIFY(entity->parentNode() == root.data()); + QVERIFY(entity->children().count() == 0); + QVERIFY(entity->components().empty()); + QVERIFY(comp->parentNode() == nullptr); + + // WHEN + entity->addComponent(comp); + QCoreApplication::processEvents(); + + // THEN + QVERIFY(entity->components().count() == 1); + QVERIFY(entity->components().first() == comp); + QVERIFY(comp->parentNode() == entity.data()); + + QCOMPARE(eventSpy.events.size(), 5); + // - entity created + // - comp created + // - entity added as child to root + // - component added for entity + // - component added for compontent + QVERIFY(eventSpy.events.first().wasLocked()); + + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::NodeCreated); + QCOMPARE(event->subjectId(), entity->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::NodeCreated); + QCOMPARE(event->subjectId(), comp->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::PropertyValueAdded); + QCOMPARE(event->subjectId(), root->id()); + QCOMPARE(event->propertyName(), QByteArrayLiteral("children")); + QCOMPARE(event->addedNodeId(), entity->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), entity->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), comp->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } + } +} + void tst_Nodes::appendingParentlessComponentToEntityWithScene() { // GIVEN -- cgit v1.2.3