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 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | namespace Qt3DCore { |
27 | |
28 | QNodePrivate::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 | |
43 | QNodePrivate::~QNodePrivate() |
44 | { |
45 | } |
46 | |
47 | void 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 | */ |
72 | void 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 | */ |
97 | void 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 | */ |
125 | void 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 | */ |
166 | void 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 | */ |
211 | void 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 | */ |
240 | void 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 | |
300 | void 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 | |
315 | void 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 | |
330 | void 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 | */ |
346 | void 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 | */ |
372 | void 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 | */ |
393 | void 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 |
408 | void 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 | */ |
429 | void 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 | */ |
475 | void QNodePrivate::setScene(QScene *scene) |
476 | { |
477 | if (m_scene != scene) { |
478 | m_scene = scene; |
479 | } |
480 | } |
481 | |
482 | /*! |
483 | \internal |
484 | */ |
485 | QScene *QNodePrivate::scene() const |
486 | { |
487 | return m_scene; |
488 | } |
489 | |
490 | /*! |
491 | \internal |
492 | */ |
493 | void 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 | |
505 | void 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 | */ |
527 | void 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 | |
544 | void QNodePrivate::update() |
545 | { |
546 | if (m_changeArbiter) { |
547 | Q_Q(QNode); |
548 | m_changeArbiter->addDirtyFrontEndNode(node: q); |
549 | } |
550 | } |
551 | |
552 | void QNodePrivate::markDirty(QScene::DirtyNodeSet changes) |
553 | { |
554 | if (m_scene) |
555 | m_scene->markDirty(changes); |
556 | } |
557 | |
558 | /*! |
559 | \internal |
560 | */ |
561 | QNodePrivate *QNodePrivate::get(QNode *q) |
562 | { |
563 | return q->d_func(); |
564 | } |
565 | |
566 | /*! |
567 | \internal |
568 | */ |
569 | const QNodePrivate *QNodePrivate::get(const QNode *q) |
570 | { |
571 | return q->d_func(); |
572 | } |
573 | |
574 | /*! |
575 | \internal |
576 | */ |
577 | void 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 | */ |
606 | QNode::QNode(QNode *parent) |
607 | : QNode(*new QNodePrivate, parent) {} |
608 | |
609 | /*! \internal */ |
610 | QNode::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 */ |
623 | QNode::~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 | */ |
640 | QNodeId 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 | */ |
657 | QNode *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 | */ |
668 | bool 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 | */ |
685 | bool 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 |
695 | void 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 | */ |
736 | QNodeVector 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 | } |
749 | void 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 | */ |
770 | bool QNode::isEnabled() const |
771 | { |
772 | Q_D(const QNode); |
773 | return d->m_enabled; |
774 | } |
775 | |
776 | namespace { |
777 | |
778 | /*! \internal */ |
779 | inline const QMetaObjectPrivate *priv(const uint* data) |
780 | { |
781 | return reinterpret_cast<const QMetaObjectPrivate*>(data); |
782 | } |
783 | |
784 | /*! \internal */ |
785 | inline 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 | */ |
800 | const 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 | */ |
826 | NodePostConstructorInit::NodePostConstructorInit(QObject *parent) |
827 | : QObject(parent) |
828 | , m_requestedProcessing(false) |
829 | { |
830 | } |
831 | |
832 | NodePostConstructorInit::~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 | */ |
844 | void 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 | */ |
866 | void 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 | */ |
878 | void 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 | |
889 | QT_END_NAMESPACE |
890 | |
891 | #include "moc_qnode_p.cpp" |
892 | |
893 | #include "moc_qnode.cpp" |
894 | |