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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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