1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qnode.h"
5#include "qnode_p.h"
6#include "qscene_p.h"
7
8#include <Qt3DCore/QComponent>
9#include <Qt3DCore/qaspectengine.h>
10#include <Qt3DCore/qentity.h>
11#include <QtCore/QChildEvent>
12#include <QtCore/QEvent>
13#include <QtCore/QMetaObject>
14#include <QtCore/QMetaProperty>
15
16#include <Qt3DCore/private/corelogging_p.h>
17#include <Qt3DCore/private/qdestructionidandtypecollector_p.h>
18#include <Qt3DCore/private/qnodevisitor_p.h>
19#include <Qt3DCore/private/qscene_p.h>
20#include <Qt3DCore/private/qaspectengine_p.h>
21#include <Qt3DCore/private/qaspectmanager_p.h>
22#include <QtCore/private/qmetaobject_p.h>
23
24QT_BEGIN_NAMESPACE
25
26namespace Qt3DCore {
27
28QNodePrivate::QNodePrivate()
29 : QObjectPrivate()
30 , m_changeArbiter(nullptr)
31 , m_typeInfo(nullptr)
32 , m_scene(nullptr)
33 , m_id(QNodeId::createId())
34 , m_blockNotifications(false)
35 , m_hasBackendNode(false)
36 , m_enabled(true)
37 , m_notifiedParent(false)
38 , m_propertyChangesSetup(false)
39 , m_signals(this)
40{
41}
42
43QNodePrivate::~QNodePrivate()
44{
45}
46
47void QNodePrivate::init(QNode *parent)
48{
49 if (!parent)
50 return;
51
52 // If we have a QNode parent that has a scene (and hence change arbiter),
53 // copy these to this QNode. If valid, then also notify the backend
54 // in a deferred way when the object is fully constructed. This is delayed
55 // until the object is fully constructed as it involves calling a virtual
56 // function of QNode.
57 m_parentId = parent->id();
58 const auto parentPrivate = get(q: parent);
59 m_scene = parentPrivate->m_scene;
60 Q_Q(QNode);
61 if (m_scene) {
62 // schedule the backend notification and scene registering -> set observers through scene
63 m_scene->postConstructorInit()->addNode(node: q);
64 }
65}
66
67/*!
68 * \internal
69 *
70 * Sends QNodeCreatedChange events to the aspects.
71 */
72void QNodePrivate::createBackendNode()
73{
74 // Do nothing if we already have already sent a node creation change
75 // and not a subsequent node destroyed change.
76 if (m_hasBackendNode || !m_scene || !m_scene->engine())
77 return;
78
79 Q_Q(QNode);
80 QAspectEnginePrivate::get(engine: m_scene->engine())->addNode(node: q);
81}
82
83/*!
84 * \internal
85 *
86 * Notify the backend that the parent lost this node as a child and
87 * that this node is being destroyed. We only send the node removed
88 * change for the parent's children property iff we have an id for
89 * a parent node. This is set/unset in the _q_addChild()/_q_removeChild()
90 * functions (and initialized in init() if there is a parent at
91 * construction time).
92 *
93 * Likewise, we only send the node destroyed change, iff we have
94 * previously sent a node created change. This is tracked via the
95 * m_hasBackendNode member.
96 */
97void QNodePrivate::notifyDestructionChangesAndRemoveFromScene()
98{
99 Q_Q(QNode);
100
101 // Ensure this node is not queued up for post-construction init
102 // to avoid crashing when the event loop spins.
103 if (m_scene && m_scene->postConstructorInit())
104 m_scene->postConstructorInit()->removeNode(node: q);
105
106 // Tell the backend we are about to be destroyed
107 if (m_hasBackendNode && m_scene && m_scene->engine())
108 QAspectEnginePrivate::get(engine: m_scene->engine())->removeNode(node: q);
109
110 // We unset the scene from the node as its backend node was/is about to be destroyed
111 QNodeVisitor visitor;
112 visitor.traverse(rootNode_: q, instance: this, fN: &QNodePrivate::unsetSceneHelper);
113}
114
115/*!
116 * \internal
117 *
118 * Sends a QNodeCreatedChange event to the aspects and then also notifies the
119 * parent backend node of its new child. This is called in a deferred manner
120 * by NodePostConstructorInit::processNodes to notify the backend of newly created
121 * nodes with a parent that is already part of the scene.
122 *
123 * Also notify the scene of this node, so it may set it's change arbiter.
124 */
125void QNodePrivate::_q_postConstructorInit()
126{
127 Q_Q(QNode);
128
129 // If we've already done the work then bail out. This can happen if the
130 // user creates a QNode subclass with an explicit parent, then immediately
131 // sets the new QNode as a property on another node. In this case, the
132 // property setter will call this function directly, but as we can't
133 // un-schedule a deferred invocation, this function will be called again
134 // the next time the event loop spins. So, catch this case and abort.
135 if (m_hasBackendNode)
136 return;
137
138 // Check that the parent hasn't been unset since this call was enqueued
139 auto parentNode = q->parentNode();
140 if (!parentNode)
141 return;
142
143 // Set the scene on this node and all children it references so that all
144 // children have a scene set since createBackendNode will set
145 // m_hasBackendNode to true for all children, which would prevent them from
146 // ever having their scene set
147 if (m_scene) {
148 QNodeVisitor visitor;
149 visitor.traverse(rootNode_: q, instance: parentNode->d_func(), fN: &QNodePrivate::setSceneHelper);
150 }
151
152 // Let the backend know we have been added to the scene
153 createBackendNode();
154
155 // Let the backend parent know that they have a new child
156 Q_ASSERT(parentNode);
157 QNodePrivate::get(q: parentNode)->_q_addChild(childNode: q);
158}
159
160/*!
161 * \internal
162 *
163 * Called by _q_setParentHelper() or _q_postConstructorInit()
164 * on the main thread.
165 */
166void QNodePrivate::_q_addChild(QNode *childNode)
167{
168 Q_ASSERT(childNode);
169 Q_ASSERT_X(childNode->parent() == q_func(), Q_FUNC_INFO, "not a child of this node");
170
171 // Have we already notified the parent about its new child? If so, bail out
172 // early so that we do not send more than one new child event to the backend
173 QNodePrivate *childD = QNodePrivate::get(q: childNode);
174 if (childD->m_notifiedParent == true)
175 return;
176
177 // Store our id as the parentId in the child so that even if the child gets
178 // removed from the scene as part of the destruction of the parent, when the
179 // parent's children are deleted in the QObject dtor, we still have access to
180 // the parentId. If we didn't store this, we wouldn't have access at that time
181 // because the parent would then only be a QObject, the QNode part would have
182 // been destroyed already.
183 childD->m_parentId = m_id;
184
185 if (!m_scene)
186 return;
187
188 // We need to send a QPropertyNodeAddedChange to the backend
189 // to notify the backend that we have a new child
190 if (m_changeArbiter != nullptr) {
191 // Flag that we have notified the parent. We do this immediately before
192 // creating the change because that recurses back into this function and
193 // we need to catch that to avoid sending more than one new child event
194 // to the backend.
195 childD->m_notifiedParent = true;
196 update();
197 }
198
199 // Update the scene
200 // TODO: Fold this into the QAspectEnginePrivate::addNode so we don't have to
201 // traverse the sub tree three times!
202 QNodeVisitor visitor;
203 visitor.traverse(rootNode_: childNode, instance: this, fN: &QNodePrivate::addEntityComponentToScene);
204}
205
206/*!
207 * \internal
208 *
209 * Called by _q_setParentHelper on the main thread.
210 */
211void QNodePrivate::_q_removeChild(QNode *childNode)
212{
213 Q_ASSERT(childNode);
214 Q_ASSERT_X(childNode->parent() == q_func(), Q_FUNC_INFO, "not a child of this node");
215
216 QNodePrivate::get(q: childNode)->m_parentId = QNodeId();
217 update();
218}
219
220/*!
221 * \internal
222 *
223 * Reparents the public QNode to \a parent. If the new parent is nullptr then this
224 * QNode is no longer part of the scene and so we notify the backend of its removal
225 * from its parent's list of children, and then send a QNodeDestroyedChange to the
226 * aspects so that the corresponding backend node is destroyed.
227 *
228 * If \a parent is not null, then we must tell its new parent about this QNode now
229 * being a child of it on the backend. If this QNode did not have a parent upon
230 * entry to this function, then we must first send a QNodeCreatedChange to the backend
231 * prior to sending the QPropertyNodeAddedChange to its parent.
232 *
233 * Note: This function should never be called from the ctor directly as the type may
234 * not be fully created yet and creating creation changes involves calling a virtual
235 * function on QNode. The function _q_notifyCreationAndChildChanges() is used
236 * for sending initial notification when a parent is passed to the QNode ctor.
237 * That function does a subset of this function with the assumption that the new object
238 * had no parent before (must be true as it is newly constructed).
239 */
240void QNodePrivate::_q_setParentHelper(QNode *parent)
241{
242 Q_Q(QNode);
243 QNode *oldParentNode = q->parentNode();
244
245 // If we had a parent, we let him know that we are about to change
246 // parent
247 if (oldParentNode && m_hasBackendNode) {
248 QNodePrivate::get(q: oldParentNode)->_q_removeChild(childNode: q);
249
250 // If we have an old parent but the new parent is null or if the new
251 // parent hasn't yet been added to the backend the backend node needs
252 // to be destroyed
253 // e.g:
254 // QEntity *child = new QEntity(some_parent);
255 // After some time, in a later event loop
256 // QEntity *newSubTreeRoot = new QEntity(someGlobalExisitingRoot)
257 // child->setParent(newSubTreeRoot)
258 if (!parent || !QNodePrivate::get(q: parent)->m_hasBackendNode)
259 notifyDestructionChangesAndRemoveFromScene();
260 }
261
262 // Flag that we need to notify any new parent
263 m_notifiedParent = false;
264
265 // Basically QObject::setParent but for QObjectPrivate
266 QObjectPrivate::setParent_helper(parent);
267
268 if (parent) {
269 // If we had no parent but are about to set one,
270 // we need to send a QNodeCreatedChange
271 QNodePrivate *newParentPrivate = QNodePrivate::get(q: parent);
272
273 // Set the scene helper / arbiter
274 if (newParentPrivate->m_scene) {
275 QNodeVisitor visitor;
276 visitor.traverse(rootNode_: q, instance: parent->d_func(), fN: &QNodePrivate::setSceneHelper);
277 }
278
279 // We want to make sure that subTreeRoot is always created before
280 // child.
281 // Given a case such as below
282 // QEntity *subTreeRoot = new QEntity(someGlobalExisitingRoot)
283 // QEntity *child = new QEntity();
284 // child->setParent(subTreeRoot)
285 // We need to take into account that subTreeRoot needs to be
286 // created in the backend before the child.
287 // Therefore we only call createBackendNode if the parent
288 // hasn't been created yet as we know that when the parent will be
289 // fully created, it will also send the changes for all of its
290 // children
291
292 if (newParentPrivate->m_hasBackendNode)
293 createBackendNode();
294
295 // If we have a valid new parent, we let him know that we are its child
296 QNodePrivate::get(q: parent)->_q_addChild(childNode: q);
297 }
298}
299
300void QNodePrivate::registerNotifiedProperties()
301{
302 Q_Q(QNode);
303 if (m_propertyChangesSetup)
304 return;
305
306 const int offset = QNode::staticMetaObject.propertyOffset();
307 const int count = q->metaObject()->propertyCount();
308
309 for (int index = offset; index < count; index++)
310 m_signals.connectToPropertyChange(object: q, propertyIndex: index);
311
312 m_propertyChangesSetup = true;
313}
314
315void QNodePrivate::unregisterNotifiedProperties()
316{
317 Q_Q(QNode);
318 if (!m_propertyChangesSetup)
319 return;
320
321 const int offset = QNode::staticMetaObject.propertyOffset();
322 const int count = q->metaObject()->propertyCount();
323
324 for (int index = offset; index < count; index++)
325 m_signals.disconnectFromPropertyChange(object: q, propertyIndex: index);
326
327 m_propertyChangesSetup = false;
328}
329
330void QNodePrivate::propertyChanged(int propertyIndex)
331{
332 Q_UNUSED(propertyIndex);
333
334 // Bail out early if we can to avoid the cost below
335 if (m_blockNotifications)
336 return;
337
338 update();
339}
340
341/*!
342 \internal
343 Recursively sets and adds the nodes in the subtree of base node \a root to the scene.
344 Also takes care of connecting Components and Entities together in the scene.
345 */
346void QNodePrivate::setSceneHelper(QNode *root)
347{
348 // Sets the scene
349 root->d_func()->setScene(m_scene);
350 // addObservable sets the QChangeArbiter
351 m_scene->addObservable(observable: root);
352
353 // We also need to handle QEntity <-> QComponent relationships
354 if (QComponent *c = qobject_cast<QComponent *>(object: root)) {
355 const QList<QEntity *> entities = c->entities();
356 for (QEntity *entity : entities) {
357 if (!m_scene->hasEntityForComponent(componentUuid: c->id(), entityUuid: entity->id())) {
358 if (!c->isShareable() && !m_scene->entitiesForComponent(id: c->id()).isEmpty())
359 qWarning() << "Trying to assign a non shareable component to more than one Entity";
360 m_scene->addEntityForComponent(componentUuid: c->id(), entityUuid: entity->id());
361 }
362 }
363 }
364}
365
366/*!
367 \internal
368
369 Recursively unsets and remove nodes in the subtree of base node \a root from
370 the scene. Also takes care of removing Components and Entities connections.
371 */
372void QNodePrivate::unsetSceneHelper(QNode *node)
373{
374 QNodePrivate *nodePrivate = QNodePrivate::get(q: node);
375
376 // We also need to handle QEntity <-> QComponent relationships removal
377 if (QComponent *c = qobject_cast<QComponent *>(object: node)) {
378 const QList<QEntity *> entities = c->entities();
379 for (QEntity *entity : entities) {
380 if (nodePrivate->m_scene)
381 nodePrivate->m_scene->removeEntityForComponent(componentUuid: c->id(), entityUuid: entity->id());
382 }
383 }
384
385 if (nodePrivate->m_scene != nullptr)
386 nodePrivate->m_scene->removeObservable(observable: node);
387 nodePrivate->setScene(nullptr);
388}
389
390/*!
391 \internal
392 */
393void QNodePrivate::addEntityComponentToScene(QNode *root)
394{
395 if (QEntity *e = qobject_cast<QEntity *>(object: root)) {
396 const auto components = e->components();
397 for (QComponent *c : components) {
398 if (!m_scene->hasEntityForComponent(componentUuid: c->id(), entityUuid: e->id()))
399 m_scene->addEntityForComponent(componentUuid: c->id(), entityUuid: e->id());
400 }
401 }
402}
403
404/*!
405 \internal
406 */
407// Called in the main thread by QScene -> following QEvent::childAdded / addChild
408void QNodePrivate::setArbiter(QChangeArbiter *arbiter)
409{
410 if (m_changeArbiter && m_changeArbiter != arbiter) {
411 unregisterNotifiedProperties();
412
413 // Remove node from dirtyFrontendNodeList on old arbiter
414 Q_Q(QNode);
415 m_changeArbiter->removeDirtyFrontEndNode(node: q);
416 }
417 m_changeArbiter = arbiter;
418 if (m_changeArbiter)
419 registerNotifiedProperties();
420}
421
422/*!
423 * \internal
424 * Makes sure this node has a backend by traversing the tree up to the most distant ancestor
425 * without a backend node and initializing that node. This is done to make sure the parent nodes
426 * are always created before the child nodes, since child nodes reference parent nodes at creation
427 * time.
428 */
429void QNodePrivate::_q_ensureBackendNodeCreated()
430{
431 if (m_hasBackendNode)
432 return;
433
434 Q_Q(QNode);
435
436 QNode *nextNode = q;
437 QNode *topNodeWithoutBackend = nullptr;
438 while (nextNode != nullptr && !QNodePrivate::get(q: nextNode)->m_hasBackendNode) {
439 topNodeWithoutBackend = nextNode;
440 nextNode = nextNode->parentNode();
441 }
442 QNodePrivate::get(q: topNodeWithoutBackend)->_q_postConstructorInit();
443}
444
445/*!
446 \class Qt3DCore::QNode
447 \inherits QObject
448
449 \inmodule Qt3DCore
450 \since 5.5
451
452 \brief QNode is the base class of all Qt3D node classes used to build a
453 Qt3D scene.
454
455 The owernship of QNode is determined by the QObject parent/child
456 relationship between nodes. By itself, a QNode has no visual appearance
457 and no particular meaning, it is there as a way of building a node based tree
458 structure.
459
460 The parent of a QNode instance can only be another QNode instance.
461
462 Each QNode instance has a unique id that allows it to be recognizable
463 from other instances.
464
465 When properties are defined on a QNode subclass, their NOTIFY signal
466 will automatically generate notifications that the Qt3D backend aspects will
467 receive.
468
469 \sa QEntity, QComponent
470*/
471
472/*!
473 \internal
474 */
475void QNodePrivate::setScene(QScene *scene)
476{
477 if (m_scene != scene) {
478 m_scene = scene;
479 }
480}
481
482/*!
483 \internal
484 */
485QScene *QNodePrivate::scene() const
486{
487 return m_scene;
488}
489
490/*!
491 \internal
492 */
493void QNodePrivate::notifyPropertyChange(const char *name, const QVariant &value)
494{
495 Q_UNUSED(name);
496 Q_UNUSED(value);
497
498 // Bail out early if we can to avoid operator new
499 if (m_blockNotifications)
500 return;
501
502 update();
503}
504
505void QNodePrivate::notifyDynamicPropertyChange(const QByteArray &name, const QVariant &value)
506{
507 Q_UNUSED(name);
508 Q_UNUSED(value);
509
510 // Bail out early if we can to avoid operator new
511 if (m_blockNotifications)
512 return;
513
514 update();
515}
516
517// Inserts this tree into the main Scene tree.
518// Needed when SceneLoaders provide a cloned tree from the backend
519// and need to insert it in the main scene tree
520// QNode *root;
521// QNode *subtree;
522// QNodePrivate::get(root)->insertTree(subtree);
523
524/*!
525 \internal
526 */
527void QNodePrivate::insertTree(QNode *treeRoot, int depth)
528{
529 if (m_scene != nullptr) {
530 treeRoot->d_func()->setScene(m_scene);
531 m_scene->addObservable(observable: treeRoot);
532 }
533
534 for (QObject *c : treeRoot->children()) {
535 QNode *n = nullptr;
536 if ((n = qobject_cast<QNode *>(object: c)) != nullptr)
537 insertTree(treeRoot: n, depth: depth + 1);
538 }
539
540 if (depth == 0)
541 treeRoot->setParent(q_func());
542}
543
544void QNodePrivate::update()
545{
546 if (m_changeArbiter) {
547 Q_Q(QNode);
548 m_changeArbiter->addDirtyFrontEndNode(node: q);
549 }
550}
551
552void QNodePrivate::markDirty(QScene::DirtyNodeSet changes)
553{
554 if (m_scene)
555 m_scene->markDirty(changes);
556}
557
558/*!
559 \internal
560 */
561QNodePrivate *QNodePrivate::get(QNode *q)
562{
563 return q->d_func();
564}
565
566/*!
567 \internal
568 */
569const QNodePrivate *QNodePrivate::get(const QNode *q)
570{
571 return q->d_func();
572}
573
574/*!
575 \internal
576 */
577void QNodePrivate::nodePtrDeleter(QNode *q)
578{
579 QObject *p = q->parent();
580 if (p == nullptr)
581 p = q;
582 p->deleteLater();
583}
584
585/*!
586 \fn Qt3DCore::QNodeId Qt3DCore::qIdForNode(Qt3DCore::QNode *node)
587 \relates Qt3DCore::QNode
588 \return node id for \a node.
589*/
590
591/*!
592 \fn template<typename T> Qt3DCore::QNodeIdVector Qt3DCore::qIdsForNodes(const T &nodes)
593 \relates Qt3DCore::QNode
594 \return vector of node ids for \a nodes.
595*/
596
597/*!
598 Creates a new QNode instance with parent \a parent.
599
600 \note The backend aspects will be notified that a QNode instance is
601 part of the scene only if it has a parent; unless this is the root node of
602 the Qt3D scene.
603
604 \sa setParent()
605*/
606QNode::QNode(QNode *parent)
607 : QNode(*new QNodePrivate, parent) {}
608
609/*! \internal */
610QNode::QNode(QNodePrivate &dd, QNode *parent)
611 : QObject(dd, parent)
612{
613 Q_D(QNode);
614 d->init(parent);
615}
616
617/*!
618 \fn Qt3DCore::QNode::nodeDestroyed()
619 Emitted when the node is destroyed.
620*/
621
622/*! \internal */
623QNode::~QNode()
624{
625 Q_D(QNode);
626 // Disconnect each connection that was stored
627 for (const auto &nodeConnectionPair : std::as_const(t&: d->m_destructionConnections))
628 QObject::disconnect(nodeConnectionPair.second);
629 d->m_destructionConnections.clear();
630 Q_EMIT nodeDestroyed();
631
632 // Notify the backend that the parent lost this node as a child and
633 // that this node is being destroyed.
634 d->notifyDestructionChangesAndRemoveFromScene();
635}
636
637/*!
638 Returns the id that uniquely identifies the QNode instance.
639*/
640QNodeId QNode::id() const
641{
642 Q_D(const QNode);
643 return d->m_id;
644}
645
646/*!
647 \property Qt3DCore::QNode::parent
648
649 Holds the immediate QNode parent, or null if the node has no parent.
650
651 Setting the parent will notify the backend aspects about current QNode
652 instance's parent change.
653
654 \note if \a parent happens to be null, this will actually notify that the
655 current QNode instance was removed from the scene.
656*/
657QNode *QNode::parentNode() const
658{
659 return qobject_cast<QNode*>(object: parent());
660}
661
662/*!
663 Returns \c true if aspect notifications are blocked; otherwise returns \c false.
664 By default, notifications are \e not blocked.
665
666 \sa blockNotifications()
667*/
668bool QNode::notificationsBlocked() const
669{
670 Q_D(const QNode);
671 return d->m_blockNotifications;
672}
673
674/*!
675 If \a block is \c true, property change notifications sent by this object
676 to aspects are blocked. If \a block is \c false, no such blocking will occur.
677
678 The return value is the previous value of notificationsBlocked().
679
680 Note that the other notification types will be sent even if the
681 notifications for this object have been blocked.
682
683 \sa notificationsBlocked()
684*/
685bool QNode::blockNotifications(bool block)
686{
687 Q_D(QNode);
688 bool previous = d->m_blockNotifications;
689 d->m_blockNotifications = block;
690 return previous;
691}
692
693// Note: should never be called from the ctor directly as the type may not be fully
694// created yet
695void QNode::setParent(QNode *parent)
696{
697 Q_D(QNode);
698
699 // If we already have a parent don't do anything. Be careful to ensure
700 // that QNode knows about the parent, not just QObject (by checking the ids)
701 if (parentNode() == parent &&
702 ((parent != nullptr && d->m_parentId == parentNode()->id()) || parent == nullptr))
703 return;
704
705 // remove ourself from postConstructorInit queue. The call to _q_setParentHelper
706 // will take care of creating the backend node if necessary depending on new parent.
707 if (d->m_scene)
708 d->m_scene->postConstructorInit()->removeNode(node: this);
709
710 d->_q_setParentHelper(parent);
711
712 // Block notifications as we want to let the _q_setParentHelper
713 // manually handle them
714 const bool blocked = blockNotifications(block: true);
715 emit parentChanged(parent);
716 blockNotifications(block: blocked);
717}
718
719/*!
720 \typedef Qt3DCore::QNodePtr
721 \relates Qt3DCore::QNode
722
723 A shared pointer for QNode.
724*/
725/*!
726 \typedef Qt3DCore::QNodeVector
727 \relates Qt3DCore::QNode
728
729 List of QNode pointers.
730*/
731
732/*!
733 * Returns a list filled with the QNode children of the current
734 * QNode instance.
735 */
736QNodeVector QNode::childNodes() const
737{
738 QNodeVector nodeChildrenList;
739 const QObjectList &objectChildrenList = QObject::children();
740 nodeChildrenList.reserve(asize: objectChildrenList.size());
741
742 for (QObject *c : objectChildrenList) {
743 if (QNode *n = qobject_cast<QNode *>(object: c))
744 nodeChildrenList.push_back(t: n);
745 }
746
747 return nodeChildrenList;
748}
749void QNode::setEnabled(bool isEnabled)
750{
751 Q_D(QNode);
752
753 if (d->m_enabled == isEnabled)
754 return;
755
756 d->m_enabled = isEnabled;
757 emit enabledChanged(enabled: isEnabled);
758}
759
760/*!
761 \property Qt3DCore::QNode::enabled
762
763 Holds the QNode enabled flag.
764 By default a QNode is always enabled.
765
766 \note the interpretation of what enabled means is aspect-dependent. Even if
767 enabled is set to \c false, some aspects may still consider the node in
768 some manner. This is documented on a class by class basis.
769*/
770bool QNode::isEnabled() const
771{
772 Q_D(const QNode);
773 return d->m_enabled;
774}
775
776namespace {
777
778/*! \internal */
779inline const QMetaObjectPrivate *priv(const uint* data)
780{
781 return reinterpret_cast<const QMetaObjectPrivate*>(data);
782}
783
784/*! \internal */
785inline bool isDynamicMetaObject(const QMetaObject *mo)
786{
787 return (priv(data: mo->d.data)->flags & DynamicMetaObject);
788}
789
790} // anonymous
791
792/*!
793 * \internal
794 *
795 * Find the most derived metaobject that doesn't have a dynamic
796 * metaobject farther up the chain.
797 * TODO: Add support to QMetaObject to explicitly say if it's a dynamic
798 * or static metaobject so we don't need this logic
799 */
800const QMetaObject *QNodePrivate::findStaticMetaObject(const QMetaObject *metaObject)
801{
802 const QMetaObject *lastStaticMetaobject = nullptr;
803 auto mo = metaObject;
804 while (mo) {
805 const bool dynamicMetaObject = isDynamicMetaObject(mo);
806 if (dynamicMetaObject)
807 lastStaticMetaobject = nullptr;
808
809 if (!dynamicMetaObject && !lastStaticMetaobject)
810 lastStaticMetaobject = mo;
811
812 mo = mo->superClass();
813 }
814 Q_ASSERT(lastStaticMetaobject);
815 return lastStaticMetaobject;
816}
817
818/*!
819 * \internal
820 *
821 * NodePostConstructorInit handles calling QNode::_q_postConstructorInit for
822 * all nodes. By keeping track of nodes that need initialization we can
823 * create them all together ensuring they get sent to the backend in a single
824 * batch.
825 */
826NodePostConstructorInit::NodePostConstructorInit(QObject *parent)
827 : QObject(parent)
828 , m_requestedProcessing(false)
829{
830}
831
832NodePostConstructorInit::~NodePostConstructorInit() {}
833
834/*!
835 * \internal
836 *
837 * Add a node to the list of nodes needing a call to _q_postConstructorInit
838 * We only add the node if it does not have an ancestor already in the queue
839 * because initializing the ancestor will initialize all it's children.
840 * This ensures that all backend nodes are created from the top-down, with
841 * all parents created before their children
842 *
843 */
844void NodePostConstructorInit::addNode(QNode *node)
845{
846 Q_ASSERT(node);
847 QNode *nextNode = node;
848 while (nextNode != nullptr && !m_nodesToConstruct.contains(t: QNodePrivate::get(q: nextNode)))
849 nextNode = nextNode->parentNode();
850
851 if (!nextNode) {
852 m_nodesToConstruct.append(t: QNodePrivate::get(q: node));
853 if (!m_requestedProcessing){
854 QMetaObject::invokeMethod(obj: this, member: "processNodes", c: Qt::QueuedConnection);
855 m_requestedProcessing = true;
856 }
857 }
858}
859
860/*!
861 * \internal
862 *
863 * Remove a node from the queue. This will ensure none of its
864 * children get initialized
865 */
866void NodePostConstructorInit::removeNode(QNode *node)
867{
868 Q_ASSERT(node);
869 m_nodesToConstruct.removeAll(t: QNodePrivate::get(q: node));
870}
871
872/*!
873 * \internal
874 *
875 * call _q_postConstructorInit for all nodes in the queue
876 * and clear the queue
877 */
878void NodePostConstructorInit::processNodes()
879{
880 m_requestedProcessing = false;
881 while (!m_nodesToConstruct.empty()) {
882 auto node = m_nodesToConstruct.takeFirst();
883 node->_q_postConstructorInit();
884 }
885}
886
887} // namespace Qt3DCore
888
889QT_END_NAMESPACE
890
891#include "moc_qnode_p.cpp"
892
893#include "moc_qnode.cpp"
894

source code of qt3d/src/core/nodes/qnode.cpp