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 | |
25 | QT_BEGIN_NAMESPACE |
26 | |
27 | namespace Qt3DCore { |
28 | |
29 | QNodePrivate::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 | |
44 | QNodePrivate::~QNodePrivate() |
45 | { |
46 | } |
47 | |
48 | void 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 | */ |
73 | void 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 | */ |
98 | void 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 | */ |
126 | void 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 | */ |
167 | void 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 | */ |
212 | void 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 | */ |
241 | void 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 | |
301 | void 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 | |
316 | void 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 | |
331 | void 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 | */ |
347 | void 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 | */ |
373 | void 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 | */ |
394 | void 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 |
409 | void 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 | */ |
430 | void 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 | */ |
476 | void QNodePrivate::setScene(QScene *scene) |
477 | { |
478 | if (m_scene != scene) { |
479 | m_scene = scene; |
480 | } |
481 | } |
482 | |
483 | /*! |
484 | \internal |
485 | */ |
486 | QScene *QNodePrivate::scene() const |
487 | { |
488 | return m_scene; |
489 | } |
490 | |
491 | /*! |
492 | \internal |
493 | */ |
494 | void 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 | |
506 | void 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 | */ |
528 | void 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 | |
545 | void QNodePrivate::update() |
546 | { |
547 | if (m_changeArbiter) { |
548 | Q_Q(QNode); |
549 | m_changeArbiter->addDirtyFrontEndNode(node: q); |
550 | } |
551 | } |
552 | |
553 | void QNodePrivate::markDirty(QScene::DirtyNodeSet changes) |
554 | { |
555 | if (m_scene) |
556 | m_scene->markDirty(changes); |
557 | } |
558 | |
559 | /*! |
560 | \internal |
561 | */ |
562 | QNodePrivate *QNodePrivate::get(QNode *q) |
563 | { |
564 | return q->d_func(); |
565 | } |
566 | |
567 | /*! |
568 | \internal |
569 | */ |
570 | const QNodePrivate *QNodePrivate::get(const QNode *q) |
571 | { |
572 | return q->d_func(); |
573 | } |
574 | |
575 | /*! |
576 | \internal |
577 | */ |
578 | void 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 | */ |
607 | QNode::QNode(QNode *parent) |
608 | : QNode(*new QNodePrivate, parent) {} |
609 | |
610 | /*! \internal */ |
611 | QNode::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 */ |
624 | QNode::~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 | */ |
641 | QNodeId 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 | */ |
658 | QNode *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 | */ |
669 | bool 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 | */ |
686 | bool 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 |
696 | void 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 | */ |
737 | QNodeVector 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 | } |
750 | void 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 | */ |
771 | bool QNode::isEnabled() const |
772 | { |
773 | Q_D(const QNode); |
774 | return d->m_enabled; |
775 | } |
776 | |
777 | namespace { |
778 | |
779 | /*! \internal */ |
780 | inline const QMetaObjectPrivate *priv(const uint* data) |
781 | { |
782 | return reinterpret_cast<const QMetaObjectPrivate*>(data); |
783 | } |
784 | |
785 | /*! \internal */ |
786 | inline 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 | */ |
801 | const 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 | */ |
827 | NodePostConstructorInit::NodePostConstructorInit(QObject *parent) |
828 | : QObject(parent) |
829 | , m_requestedProcessing(false) |
830 | { |
831 | } |
832 | |
833 | NodePostConstructorInit::~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 | */ |
845 | void 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 | */ |
870 | void 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 | */ |
882 | void 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 | |
894 | QT_END_NAMESPACE |
895 | |
896 | #include "moc_qnode_p.cpp" |
897 | |
898 | #include "moc_qnode.cpp" |
899 |
Definitions
- QNodePrivate
- ~QNodePrivate
- init
- createBackendNode
- notifyDestructionChangesAndRemoveFromScene
- _q_postConstructorInit
- _q_addChild
- _q_removeChild
- _q_setParentHelper
- registerNotifiedProperties
- unregisterNotifiedProperties
- propertyChanged
- setSceneHelper
- unsetSceneHelper
- addEntityComponentToScene
- setArbiter
- _q_ensureBackendNodeCreated
- setScene
- scene
- notifyPropertyChange
- notifyDynamicPropertyChange
- insertTree
- update
- markDirty
- get
- get
- nodePtrDeleter
- QNode
- QNode
- ~QNode
- id
- parentNode
- notificationsBlocked
- blockNotifications
- setParent
- childNodes
- setEnabled
- isEnabled
- priv
- isDynamicMetaObject
- findStaticMetaObject
- NodePostConstructorInit
- ~NodePostConstructorInit
- addNode
- removeNode
Learn Advanced QML with KDAB
Find out more