| 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 | |