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 "quick3dentityloader_p_p.h"
41
42#include <QtQml/QQmlContext>
43#include <QtQml/QQmlEngine>
44#include <QtQml/QQmlIncubator>
45
46#include <QtQml/private/qqmlengine_p.h>
47
48QT_BEGIN_NAMESPACE
49
50namespace Qt3DCore {
51namespace Quick {
52
53namespace {
54struct Quick3DQmlOwner
55{
56 Quick3DQmlOwner(QQmlEngine *e, QObject *o)
57 : engine(e)
58 , object(o)
59 {}
60
61 QQmlEngine *engine;
62 QObject *object;
63
64 QQmlContext *context() const
65 {
66 return engine->contextForObject(object);
67 }
68};
69
70Quick3DQmlOwner _q_findQmlOwner(QObject *object)
71{
72 auto o = object;
73 while (!qmlEngine(o) && o->parent())
74 o = o->parent();
75 return Quick3DQmlOwner(qmlEngine(o), o);
76}
77}
78
79class Quick3DEntityLoaderIncubator : public QQmlIncubator
80{
81public:
82 Quick3DEntityLoaderIncubator(Quick3DEntityLoader *loader)
83 : QQmlIncubator(AsynchronousIfNested),
84 m_loader(loader)
85 {
86 }
87
88protected:
89 void statusChanged(Status status) final
90 {
91 Quick3DEntityLoaderPrivate *priv = Quick3DEntityLoaderPrivate::get(q: m_loader);
92
93 switch (status) {
94 case Ready: {
95 Q_ASSERT(priv->m_entity == nullptr);
96 priv->m_entity = qobject_cast<QEntity *>(object: object());
97 Q_ASSERT(priv->m_entity != nullptr);
98 priv->m_entity->setParent(m_loader);
99 emit m_loader->entityChanged();
100 priv->setStatus(Quick3DEntityLoader::Ready);
101 break;
102 }
103
104 case Loading: {
105 priv->setStatus(Quick3DEntityLoader::Loading);
106 break;
107 }
108
109 case Error: {
110 QQmlEnginePrivate::warning(_q_findQmlOwner(object: m_loader).engine, errors());
111 priv->clear();
112 emit m_loader->entityChanged();
113 priv->setStatus(Quick3DEntityLoader::Error);
114 break;
115 }
116
117 default:
118 break;
119 }
120 }
121
122private:
123 Quick3DEntityLoader *m_loader;
124};
125
126/*!
127 \qmltype EntityLoader
128 \inqmlmodule Qt3D.Core
129 \inherits Entity
130 \since 5.5
131 \brief Provides a way to dynamically load an Entity subtree.
132
133 An EntityLoader provides the facitily to load predefined set of entities
134 from qml source file. EntityLoader itself is an entity and the loaded entity
135 tree is set as a child of the loader. The loaded entity tree root can be
136 accessed with EntityLoader::entity property.
137
138 \badcode
139 EntityLoader {
140 id: loader
141 source: "./SphereEntity.qml"
142 }
143 \endcode
144*/
145
146Quick3DEntityLoader::Quick3DEntityLoader(QNode *parent)
147 : QEntity(*new Quick3DEntityLoaderPrivate, parent)
148{
149}
150
151Quick3DEntityLoader::~Quick3DEntityLoader()
152{
153 Q_D(Quick3DEntityLoader);
154 d->clear();
155}
156
157/*!
158 \qmlproperty QtQml::QtObject EntityLoader::entity
159 Holds the loaded entity tree root.
160 \readonly
161
162 This property allows access to the content of the loader. It references
163 either a valid Entity object if the status property equals
164 EntityLoader.Ready, it is equal to null otherwise.
165*/
166QObject *Quick3DEntityLoader::entity() const
167{
168 Q_D(const Quick3DEntityLoader);
169 return d->m_entity;
170}
171
172/*!
173 \qmlproperty url Qt3DCore::EntityLoader::source
174 Holds the source url.
175*/
176QUrl Quick3DEntityLoader::source() const
177{
178 Q_D(const Quick3DEntityLoader);
179 return d->m_source;
180}
181
182void Quick3DEntityLoader::setSource(const QUrl &url)
183{
184 Q_D(Quick3DEntityLoader);
185
186 if (url == d->m_source)
187 return;
188
189 d->clear();
190 d->m_source = url;
191 emit sourceChanged();
192 d->loadFromSource();
193}
194
195QQmlComponent *Quick3DEntityLoader::sourceComponent() const
196{
197 Q_D(const Quick3DEntityLoader);
198 return d->m_sourceComponent;
199}
200
201void Quick3DEntityLoader::setSourceComponent(QQmlComponent *component)
202{
203 Q_D(Quick3DEntityLoader);
204 if (d->m_sourceComponent == component)
205 return;
206
207 d->clear();
208 d->m_sourceComponent = component;
209 emit sourceComponentChanged();
210 d->loadComponent(component: d->m_sourceComponent);
211}
212
213/*!
214 \qmlproperty Status Qt3DCore::EntityLoader::status
215
216 Holds the status of the entity loader.
217 \list
218 \li EntityLoader.Null
219 \li EntityLoader.Loading
220 \li EntityLoader.Ready
221 \li EntityLoader.Error
222 \endlist
223 */
224Quick3DEntityLoader::Status Quick3DEntityLoader::status() const
225{
226 Q_D(const Quick3DEntityLoader);
227 return d->m_status;
228}
229
230Quick3DEntityLoaderPrivate::Quick3DEntityLoaderPrivate()
231 : QEntityPrivate(),
232 m_incubator(nullptr),
233 m_context(nullptr),
234 m_component(nullptr),
235 m_sourceComponent(nullptr),
236 m_entity(nullptr),
237 m_status(Quick3DEntityLoader::Null)
238{
239}
240
241void Quick3DEntityLoaderPrivate::clear()
242{
243 if (m_incubator) {
244 m_incubator->clear();
245 delete m_incubator;
246 m_incubator = nullptr;
247 }
248
249 if (m_entity) {
250 m_entity->setParent(Q_NODE_NULLPTR);
251 delete m_entity;
252 m_entity = nullptr;
253 }
254
255 // Only delete m_component if we were loading from a URL otherwise it means
256 // m_component = m_sourceComponent which we don't own.
257 if (m_component && m_component != m_sourceComponent)
258 delete m_component;
259 m_component = nullptr;
260
261 if (m_context) {
262 delete m_context;
263 m_context = nullptr;
264 }
265}
266
267void Quick3DEntityLoaderPrivate::loadFromSource()
268{
269 Q_Q(Quick3DEntityLoader);
270
271 if (m_source.isEmpty()) {
272 emit q->entityChanged();
273 return;
274 }
275
276 loadComponent(source: m_source);
277}
278
279void Quick3DEntityLoaderPrivate::loadComponent(const QUrl &source)
280{
281 Q_Q(Quick3DEntityLoader);
282
283 Q_ASSERT(m_entity == nullptr);
284 Q_ASSERT(m_component == nullptr);
285 Q_ASSERT(m_context == nullptr);
286
287 auto owner = _q_findQmlOwner(object: q);
288 m_component = new QQmlComponent(owner.engine, owner.object);
289 QObject::connect(sender: m_component, SIGNAL(statusChanged(QQmlComponent::Status)),
290 receiver: q, SLOT(_q_componentStatusChanged(QQmlComponent::Status)));
291 m_component->loadUrl(url: source, mode: QQmlComponent::Asynchronous);
292}
293
294void Quick3DEntityLoaderPrivate::loadComponent(QQmlComponent *component)
295{
296 Q_ASSERT(m_entity == nullptr);
297 Q_ASSERT(m_component == nullptr);
298 Q_ASSERT(m_context == nullptr);
299
300 m_component = component;
301 _q_componentStatusChanged(status: m_component ? m_component->status() : QQmlComponent::Null);
302}
303
304void Quick3DEntityLoaderPrivate::_q_componentStatusChanged(QQmlComponent::Status status)
305{
306 Q_Q(Quick3DEntityLoader);
307
308 Q_ASSERT(m_entity == nullptr);
309 Q_ASSERT(m_context == nullptr);
310 Q_ASSERT(m_incubator == nullptr);
311
312 if (!m_component) {
313 clear();
314 emit q->entityChanged();
315 return;
316 }
317
318 auto owner = _q_findQmlOwner(object: q);
319 if (!m_component->errors().isEmpty()) {
320 QQmlEnginePrivate::warning(owner.engine, m_component->errors());
321 clear();
322 emit q->entityChanged();
323 return;
324 }
325
326 // Still loading
327 if (status != QQmlComponent::Ready)
328 return;
329
330 m_context = new QQmlContext(owner.context());
331 m_context->setContextObject(owner.object);
332
333 m_incubator = new Quick3DEntityLoaderIncubator(q);
334 m_component->create(*m_incubator, context: m_context);
335}
336
337void Quick3DEntityLoaderPrivate::setStatus(Quick3DEntityLoader::Status status)
338{
339 Q_Q(Quick3DEntityLoader);
340 if (status != m_status) {
341 m_status = status;
342 const bool blocked = q->blockNotifications(block: true);
343 emit q->statusChanged(status: m_status);
344 q->blockNotifications(block: blocked);
345 }
346}
347
348} // namespace Quick
349} // namespace Qt3DCore
350
351QT_END_NAMESPACE
352
353#include "moc_quick3dentityloader_p.cpp"
354

source code of qt3d/src/quick3d/quick3d/items/quick3dentityloader.cpp