1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qabstractaspect.h" |
41 | #include "qabstractaspect_p.h" |
42 | |
43 | #include <QMetaObject> |
44 | #include <QMetaProperty> |
45 | |
46 | #include <Qt3DCore/qcomponent.h> |
47 | #include <Qt3DCore/qentity.h> |
48 | #include <Qt3DCore/qpropertyupdatedchange.h> |
49 | #include <Qt3DCore/qpropertyvalueaddedchange.h> |
50 | #include <Qt3DCore/qpropertyvalueremovedchange.h> |
51 | #include <Qt3DCore/qcomponentaddedchange.h> |
52 | #include <Qt3DCore/qcomponentremovedchange.h> |
53 | |
54 | #include <Qt3DCore/private/corelogging_p.h> |
55 | #include <Qt3DCore/private/qaspectjobmanager_p.h> |
56 | #include <Qt3DCore/private/qaspectmanager_p.h> |
57 | #include <Qt3DCore/private/qchangearbiter_p.h> |
58 | #include <Qt3DCore/private/qnodevisitor_p.h> |
59 | #include <Qt3DCore/private/qnode_p.h> |
60 | #include <Qt3DCore/private/qscene_p.h> |
61 | #include <Qt3DCore/private/qnode_p.h> |
62 | |
63 | QT_BEGIN_NAMESPACE |
64 | |
65 | namespace Qt3DCore { |
66 | |
67 | QAbstractAspectPrivate::QAbstractAspectPrivate() |
68 | : QObjectPrivate() |
69 | , m_root(nullptr) |
70 | , m_rootId() |
71 | , m_aspectManager(nullptr) |
72 | , m_jobManager(nullptr) |
73 | , m_arbiter(nullptr) |
74 | { |
75 | } |
76 | |
77 | QAbstractAspectPrivate::~QAbstractAspectPrivate() |
78 | { |
79 | } |
80 | |
81 | QAbstractAspectPrivate *QAbstractAspectPrivate::get(QAbstractAspect *aspect) |
82 | { |
83 | return aspect->d_func(); |
84 | } |
85 | |
86 | /*! |
87 | * \internal |
88 | * Called in the context of the main thread |
89 | */ |
90 | void QAbstractAspectPrivate::onEngineAboutToShutdown() |
91 | { |
92 | } |
93 | |
94 | /*! \internal */ |
95 | void QAbstractAspectPrivate::unregisterBackendType(const QMetaObject &mo) |
96 | { |
97 | m_backendCreatorFunctors.remove(akey: &mo); |
98 | } |
99 | |
100 | /*! |
101 | * \class Qt3DCore::QAbstractAspect |
102 | * \inheaderfile Qt3DCore/QAbstractAspect |
103 | * \inherits QObject |
104 | * \inmodule Qt3DCore |
105 | * \brief QAbstractAspect is the base class for aspects that provide a vertical slice of behavior. |
106 | */ |
107 | |
108 | /*! |
109 | * \fn void Qt3DCore::QAbstractAspect::registerBackendType(const Qt3DCore::QBackendNodeMapperPtr &functor) |
110 | * Registers backend with \a functor. |
111 | */ |
112 | |
113 | /*! |
114 | * \macro QT3D_REGISTER_ASPECT(name, AspectType) |
115 | * \relates Qt3DCore::QAbstractAspect |
116 | * |
117 | * Convenience macro for registering \a AspectType for instantiation by the |
118 | * currently set Qt3DCore::QAspectFactory. This makes it possible to create an |
119 | * instance of \a AspectType in the aspect thread by later passing \a name to |
120 | * Qt3DCore::QAspectEngine::registerAspect(const QString &name). |
121 | * |
122 | * \note It is also possible to register a new aspect without using this macro |
123 | * by instead using Qt3DCore::QAspectEngine::registerAspect(QAbstractAspect *aspect) |
124 | * which will handle moving a previously created aspect instance to the aspect |
125 | * thread context. |
126 | * |
127 | * KDAB has published a few articles about writing custom Qt3D aspects |
128 | * \l {https://www.kdab.com/writing-custom-qt-3d-aspect/}{on their blog}. These |
129 | * provide an excellent starting point if you wish to learn more about it. |
130 | */ |
131 | |
132 | |
133 | /*! |
134 | * Constructs a new QAbstractAspect with \a parent |
135 | */ |
136 | QAbstractAspect::QAbstractAspect(QObject *parent) |
137 | : QAbstractAspect(*new QAbstractAspectPrivate, parent) {} |
138 | |
139 | /*! |
140 | * \typedef Qt3DCore::QAspectJobPtr |
141 | * \relates Qt3DCore::QAbstractAspect |
142 | * |
143 | * A shared pointer for QAspectJob. |
144 | */ |
145 | |
146 | /*! |
147 | * \typedef Qt3DCore::QBackendNodeMapperPtr |
148 | * \relates Qt3DCore::QAbstractAspect |
149 | * |
150 | * A shared pointer for QBackendNodeMapper. |
151 | */ |
152 | |
153 | /*! |
154 | * \internal |
155 | */ |
156 | QAbstractAspect::QAbstractAspect(QAbstractAspectPrivate &dd, QObject *parent) |
157 | : QObject(dd, parent) |
158 | { |
159 | } |
160 | |
161 | /*! |
162 | \internal |
163 | */ |
164 | QAbstractAspect::~QAbstractAspect() |
165 | { |
166 | } |
167 | |
168 | /*! |
169 | * \return root entity node id. |
170 | */ |
171 | QNodeId QAbstractAspect::rootEntityId() const Q_DECL_NOEXCEPT |
172 | { |
173 | Q_D(const QAbstractAspect); |
174 | return d->m_rootId; |
175 | } |
176 | |
177 | /*! |
178 | * Registers backend with \a obj and \a functor. |
179 | */ |
180 | void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor) |
181 | { |
182 | Q_D(QAbstractAspect); |
183 | d->m_backendCreatorFunctors.insert(akey: &obj, avalue: {functor, QAbstractAspectPrivate::DefaultMapper}); |
184 | } |
185 | |
186 | void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor, bool supportsSyncing) |
187 | { |
188 | Q_D(QAbstractAspect); |
189 | const auto f = supportsSyncing ? QAbstractAspectPrivate::SupportsSyncing : QAbstractAspectPrivate::DefaultMapper; |
190 | d->m_backendCreatorFunctors.insert(akey: &obj, avalue: {functor, f}); |
191 | } |
192 | |
193 | void QAbstractAspect::unregisterBackendType(const QMetaObject &obj) |
194 | { |
195 | Q_D(QAbstractAspect); |
196 | d->m_backendCreatorFunctors.remove(akey: &obj); |
197 | } |
198 | |
199 | QVariant QAbstractAspect::executeCommand(const QStringList &args) |
200 | { |
201 | Q_UNUSED(args) |
202 | return QVariant(); |
203 | } |
204 | |
205 | QVector<QAspectJobPtr> QAbstractAspect::jobsToExecute(qint64 time) |
206 | { |
207 | Q_UNUSED(time) |
208 | return QVector<QAspectJobPtr>(); |
209 | } |
210 | |
211 | QAbstractAspectPrivate::BackendNodeMapperAndInfo QAbstractAspectPrivate::mapperForNode(const QMetaObject *metaObj) const |
212 | { |
213 | Q_ASSERT(metaObj); |
214 | BackendNodeMapperAndInfo info; |
215 | |
216 | while (metaObj != nullptr && info.first.isNull()) { |
217 | info = m_backendCreatorFunctors.value(akey: metaObj); |
218 | metaObj = metaObj->superClass(); |
219 | } |
220 | return info; |
221 | } |
222 | |
223 | void QAbstractAspectPrivate::syncDirtyFrontEndNodes(const QVector<QNode *> &nodes) |
224 | { |
225 | for (auto node: qAsConst(t: nodes)) { |
226 | const QMetaObject *metaObj = QNodePrivate::get(q: node)->m_typeInfo; |
227 | const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj); |
228 | const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first; |
229 | |
230 | if (!backendNodeMapper) |
231 | continue; |
232 | |
233 | QBackendNode *backend = backendNodeMapper->get(id: node->id()); |
234 | if (!backend) |
235 | continue; |
236 | |
237 | const bool supportsSyncing = backendNodeMapperInfo.second & SupportsSyncing; |
238 | if (supportsSyncing) |
239 | syncDirtyFrontEndNode(node, backend, firstTime: false); |
240 | else |
241 | sendPropertyMessages(node, backend); |
242 | } |
243 | } |
244 | |
245 | void QAbstractAspectPrivate::syncDirtyFrontEndSubNodes(const QVector<NodeRelationshipChange> &nodes) |
246 | { |
247 | for (const auto &nodeChange: qAsConst(t: nodes)) { |
248 | auto getBackend = [this](QNode *node) -> std::tuple<QBackendNode *, bool> { |
249 | const QMetaObject *metaObj = QNodePrivate::get(q: node)->m_typeInfo; |
250 | if (!metaObj) |
251 | return {}; |
252 | const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj); |
253 | const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first; |
254 | |
255 | if (!backendNodeMapper) |
256 | return {}; |
257 | |
258 | QBackendNode *backend = backendNodeMapper->get(id: node->id()); |
259 | if (!backend) |
260 | return {}; |
261 | |
262 | const bool supportsSyncing = backendNodeMapperInfo.second & SupportsSyncing; |
263 | |
264 | return std::tuple<QBackendNode *, bool>(backend, supportsSyncing); |
265 | }; |
266 | |
267 | auto nodeInfo = getBackend(nodeChange.node); |
268 | if (!std::get<0>(t&: nodeInfo)) |
269 | continue; |
270 | |
271 | auto subNodeInfo = getBackend(nodeChange.subNode); |
272 | if (!std::get<0>(t&: subNodeInfo)) |
273 | continue; |
274 | |
275 | switch (nodeChange.change) { |
276 | case PropertyValueAdded: { |
277 | if (std::get<1>(t&: nodeInfo)) |
278 | break; // do nothing as the node will be dirty anyway |
279 | |
280 | QPropertyValueAddedChange change(nodeChange.node->id()); |
281 | change.setPropertyName(nodeChange.property); |
282 | change.setAddedValue(QVariant::fromValue(value: nodeChange.subNode->id())); |
283 | QPropertyValueAddedChangePtr pChange(&change, [](QPropertyValueAddedChange *) { }); |
284 | std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange); |
285 | } |
286 | break; |
287 | case PropertyValueRemoved: { |
288 | if (std::get<1>(t&: nodeInfo)) |
289 | break; // do nothing as the node will be dirty anyway |
290 | |
291 | QPropertyValueRemovedChange change(nodeChange.node->id()); |
292 | change.setPropertyName(nodeChange.property); |
293 | change.setRemovedValue(QVariant::fromValue(value: nodeChange.subNode->id())); |
294 | QPropertyValueRemovedChangePtr pChange(&change, [](QPropertyValueRemovedChange *) { }); |
295 | std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange); |
296 | } |
297 | break; |
298 | case ComponentAdded: { |
299 | // let the entity know it has a new component |
300 | if (std::get<1>(t&: nodeInfo)) { |
301 | QBackendNodePrivate::get(n: std::get<0>(t&: nodeInfo))->componentAdded(frontend: nodeChange.subNode); |
302 | } else { |
303 | QComponentAddedChange change(qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode), qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node)); |
304 | QComponentAddedChangePtr pChange(&change, [](QComponentAddedChange *) { }); |
305 | std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange); |
306 | } |
307 | |
308 | // let the component know it was added to an entity |
309 | if (std::get<1>(t&: subNodeInfo)) { |
310 | QBackendNodePrivate::get(n: std::get<0>(t&: subNodeInfo))->addedToEntity(frontend: nodeChange.node); |
311 | } else { |
312 | QComponentAddedChange change(qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode), qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node)); |
313 | QComponentAddedChangePtr pChange(&change, [](QComponentAddedChange *) { }); |
314 | std::get<0>(t&: subNodeInfo)->sceneChangeEvent(e: pChange); |
315 | } |
316 | } |
317 | break; |
318 | case ComponentRemoved: { |
319 | // let the entity know a component was removed |
320 | if (std::get<1>(t&: nodeInfo)) { |
321 | QBackendNodePrivate::get(n: std::get<0>(t&: nodeInfo))->componentRemoved(frontend: nodeChange.subNode); |
322 | } else { |
323 | QComponentRemovedChange change(qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode), qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node)); |
324 | QComponentRemovedChangePtr pChange(&change, [](QComponentRemovedChange *) { }); |
325 | std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange); |
326 | } |
327 | |
328 | // let the component know it was removed from an entity |
329 | if (std::get<1>(t&: subNodeInfo)) { |
330 | QBackendNodePrivate::get(n: std::get<0>(t&: subNodeInfo))->removedFromEntity(frontend: nodeChange.node); |
331 | } else { |
332 | QComponentRemovedChange change(qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node), qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode)); |
333 | QComponentRemovedChangePtr pChange(&change, [](QComponentRemovedChange *) { }); |
334 | std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange); |
335 | } |
336 | } |
337 | break; |
338 | default: |
339 | break; |
340 | } |
341 | } |
342 | } |
343 | |
344 | void QAbstractAspectPrivate::syncDirtyFrontEndNode(QNode *node, QBackendNode *backend, bool firstTime) const |
345 | { |
346 | Q_ASSERT(false); // overload in derived class |
347 | if (!firstTime) |
348 | sendPropertyMessages(node, backend); |
349 | } |
350 | |
351 | void QAbstractAspectPrivate::sendPropertyMessages(QNode *node, QBackendNode *backend) const |
352 | { |
353 | const int offset = QNode::staticMetaObject.propertyOffset(); |
354 | const auto metaObj = node->metaObject(); |
355 | const int count = metaObj->propertyCount(); |
356 | |
357 | const auto toBackendValue = [](const QVariant &data) -> QVariant |
358 | { |
359 | if (data.canConvert<QNode*>()) { |
360 | QNode *node = data.value<QNode*>(); |
361 | |
362 | // Ensure the node and all ancestors have issued their node creation changes. |
363 | // We can end up here if a newly created node with a parent is immediately set |
364 | // as a property on another node. In this case the deferred call to |
365 | // _q_postConstructorInit() will not have happened yet as the event |
366 | // loop will still be blocked. We need to do this for all ancestors, |
367 | // since the subtree of this node otherwise can end up on the backend |
368 | // with a reference to a non-existent parent. |
369 | if (node) |
370 | QNodePrivate::get(q: node)->_q_ensureBackendNodeCreated(); |
371 | |
372 | const QNodeId id = node ? node->id() : QNodeId(); |
373 | return QVariant::fromValue(value: id); |
374 | } |
375 | |
376 | return data; |
377 | }; |
378 | |
379 | QPropertyUpdatedChange change(node->id()); |
380 | QPropertyUpdatedChangePtr pchange(&change, [](QPropertyUpdatedChange *) { }); |
381 | for (int index = offset; index < count; index++) { |
382 | const QMetaProperty pro = metaObj->property(index); |
383 | change.setPropertyName(pro.name()); |
384 | change.setValue(toBackendValue(pro.read(obj: node))); |
385 | backend->sceneChangeEvent(e: pchange); |
386 | } |
387 | |
388 | auto const dynamicProperties = node->dynamicPropertyNames(); |
389 | for (const QByteArray &name: dynamicProperties) { |
390 | change.setPropertyName(name.data()); |
391 | change.setValue(toBackendValue(node->property(name: name.data()))); |
392 | backend->sceneChangeEvent(e: pchange); |
393 | } |
394 | } |
395 | |
396 | QBackendNode *QAbstractAspectPrivate::createBackendNode(const NodeTreeChange &change) const |
397 | { |
398 | const QMetaObject *metaObj = change.metaObj; |
399 | const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj); |
400 | const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first; |
401 | |
402 | if (!backendNodeMapper) |
403 | return nullptr; |
404 | |
405 | QBackendNode *backend = backendNodeMapper->get(id: change.id); |
406 | if (backend != nullptr) |
407 | return backend; |
408 | |
409 | QNode *node = change.node; |
410 | QNodeCreatedChangeBasePtr creationChange; |
411 | const bool supportsSyncing = backendNodeMapperInfo.second & SupportsSyncing; |
412 | if (supportsSyncing) { |
413 | // All objects modified to use syncing should only use the id in the creation functor |
414 | QNodeCreatedChangeBase changeObj(node); |
415 | creationChange = QNodeCreatedChangeBasePtr(&changeObj, [](QNodeCreatedChangeBase *) {}); |
416 | backend = backendNodeMapper->create(change: creationChange); |
417 | } else { |
418 | creationChange = node->createNodeCreationChange(); |
419 | backend = backendNodeMapper->create(change: creationChange); |
420 | } |
421 | |
422 | if (!backend) |
423 | return nullptr; |
424 | |
425 | // TODO: Find some place else to do all of this function from the arbiter |
426 | backend->setPeerId(change.id); |
427 | |
428 | // Backend could be null if the user decides that his functor should only |
429 | // perform some action when encountering a given type of item but doesn't need to |
430 | // return a QBackendNode pointer. |
431 | |
432 | QBackendNodePrivate *backendPriv = QBackendNodePrivate::get(n: backend); |
433 | backendPriv->setEnabled(node->isEnabled()); |
434 | |
435 | // TO DO: Find a way to specify the changes to observe |
436 | // Register backendNode with QChangeArbiter |
437 | if (m_arbiter != nullptr) { // Unit tests may not have the arbiter registered |
438 | qCDebug(Nodes) << q_func()->objectName() << "Creating backend node for node id" |
439 | << node->id() << "of type" << QNodePrivate::get(q: node)->m_typeInfo->className(); |
440 | m_arbiter->registerObserver(observer: backendPriv, nodeId: backend->peerId(), changeFlags: AllChanges); |
441 | if (backend->mode() == QBackendNode::ReadWrite) |
442 | m_arbiter->scene()->addObservable(observable: backendPriv, id: backend->peerId()); |
443 | } |
444 | |
445 | if (supportsSyncing) |
446 | syncDirtyFrontEndNode(node, backend, firstTime: true); |
447 | else |
448 | backend->initializeFromPeer(change: creationChange); |
449 | |
450 | return backend; |
451 | } |
452 | |
453 | void QAbstractAspectPrivate::clearBackendNode(const NodeTreeChange &change) const |
454 | { |
455 | const QMetaObject *metaObj = change.metaObj; |
456 | const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj); |
457 | const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first; |
458 | |
459 | if (!backendNodeMapper) |
460 | return; |
461 | |
462 | // Request the mapper to destroy the corresponding backend node |
463 | QBackendNode *backend = backendNodeMapper->get(id: change.id); |
464 | if (backend) { |
465 | qCDebug(Nodes) << "Deleting backend node for node id" |
466 | << change.id << "of type" << metaObj->className(); |
467 | QBackendNodePrivate *backendPriv = QBackendNodePrivate::get(n: backend); |
468 | m_arbiter->unregisterObserver(observer: backendPriv, nodeId: backend->peerId()); |
469 | if (backend->mode() == QBackendNode::ReadWrite) |
470 | m_arbiter->scene()->removeObservable(observable: backendPriv, id: backend->peerId()); |
471 | backendNodeMapper->destroy(id: change.id); |
472 | } |
473 | } |
474 | |
475 | void QAbstractAspectPrivate::setRootAndCreateNodes(QEntity *rootObject, const QVector<NodeTreeChange> &nodesChanges) |
476 | { |
477 | qCDebug(Aspects) << Q_FUNC_INFO << "rootObject =" << rootObject; |
478 | if (rootObject == m_root) |
479 | return; |
480 | |
481 | m_root = rootObject; |
482 | m_rootId = rootObject->id(); |
483 | |
484 | for (const NodeTreeChange &change : nodesChanges) |
485 | createBackendNode(change); |
486 | } |
487 | |
488 | |
489 | QServiceLocator *QAbstractAspectPrivate::services() const |
490 | { |
491 | return m_aspectManager ? m_aspectManager->serviceLocator() : nullptr; |
492 | } |
493 | |
494 | QAbstractAspectJobManager *QAbstractAspectPrivate::jobManager() const |
495 | { |
496 | return m_jobManager; |
497 | } |
498 | |
499 | QVector<QAspectJobPtr> QAbstractAspectPrivate::jobsToExecute(qint64 time) |
500 | { |
501 | Q_Q(QAbstractAspect); |
502 | auto res = q->jobsToExecute(time); |
503 | |
504 | { |
505 | QMutexLocker lock(&m_singleShotMutex); |
506 | res << m_singleShotJobs; |
507 | m_singleShotJobs.clear(); |
508 | } |
509 | |
510 | return res; |
511 | } |
512 | |
513 | void QAbstractAspectPrivate::jobsDone() |
514 | { |
515 | } |
516 | |
517 | void QAbstractAspectPrivate::frameDone() |
518 | { |
519 | } |
520 | |
521 | /*! |
522 | * Called in the context of the aspect thread once the aspect has been registered. |
523 | * This provides an opportunity for the aspect to do any initialization tasks that |
524 | * require to be in the aspect thread context such as creating QObject subclasses that |
525 | * must have affinity with this thread. |
526 | * |
527 | * \sa onUnregistered |
528 | */ |
529 | void QAbstractAspect::onRegistered() |
530 | { |
531 | } |
532 | |
533 | /*! |
534 | * Called in the context of the aspect thread during unregistration |
535 | * of the aspect. This gives the aspect a chance to do any final pieces of |
536 | * cleanup that it would not do when just changing to a new scene. |
537 | * |
538 | * \sa onRegistered |
539 | */ |
540 | void QAbstractAspect::onUnregistered() |
541 | { |
542 | } |
543 | |
544 | /*! |
545 | * |
546 | * Called in the QAspectThread context |
547 | */ |
548 | void QAbstractAspect::onEngineStartup() |
549 | { |
550 | } |
551 | |
552 | /*! |
553 | * |
554 | * Called in the QAspectThread context |
555 | */ |
556 | void QAbstractAspect::onEngineShutdown() |
557 | { |
558 | } |
559 | |
560 | void QAbstractAspect::scheduleSingleShotJob(const Qt3DCore::QAspectJobPtr &job) |
561 | { |
562 | Q_D(QAbstractAspect); |
563 | QMutexLocker lock(&d->m_singleShotMutex); |
564 | d->m_singleShotJobs.push_back(t: job); |
565 | } |
566 | |
567 | namespace Debug { |
568 | |
569 | AsynchronousCommandReply::AsynchronousCommandReply(const QString &commandName, QObject *parent) |
570 | : QObject(parent) |
571 | , m_commandName(commandName) |
572 | , m_finished(false) |
573 | { |
574 | } |
575 | |
576 | void AsynchronousCommandReply::setFinished(bool replyFinished) |
577 | { |
578 | m_finished = replyFinished; |
579 | if (m_finished) |
580 | emit finished(reply: this); |
581 | } |
582 | |
583 | void AsynchronousCommandReply::setData(const QByteArray &data) |
584 | { |
585 | m_data = data; |
586 | } |
587 | |
588 | } // Debug |
589 | |
590 | |
591 | } // of namespace Qt3DCore |
592 | |
593 | QT_END_NAMESPACE |
594 | |