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 
48 QT_BEGIN_NAMESPACE
49 
50 namespace Qt3DCore {
51 namespace Quick {
52 
53 namespace {
54 struct Quick3DQmlOwner
55 {
Quick3DQmlOwnerQt3DCore::Quick::__anonaa93cd9c0111::Quick3DQmlOwner56     Quick3DQmlOwner(QQmlEngine *e, QObject *o)
57         : engine(e)
58         , object(o)
59     {}
60 
61     QQmlEngine *engine;
62     QObject *object;
63 
contextQt3DCore::Quick::__anonaa93cd9c0111::Quick3DQmlOwner64     QQmlContext *context() const
65     {
66         return engine->contextForObject(object);
67     }
68 };
69 
_q_findQmlOwner(QObject * object)70 Quick3DQmlOwner _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 
79 class Quick3DEntityLoaderIncubator : public QQmlIncubator
80 {
81 public:
Quick3DEntityLoaderIncubator(Quick3DEntityLoader * loader)82     Quick3DEntityLoaderIncubator(Quick3DEntityLoader *loader)
83         : QQmlIncubator(AsynchronousIfNested),
84           m_loader(loader)
85     {
86     }
87 
88 protected:
statusChanged(Status status)89     void statusChanged(Status status) final
90     {
91         Quick3DEntityLoaderPrivate *priv = Quick3DEntityLoaderPrivate::get(m_loader);
92 
93         switch (status) {
94         case Ready: {
95             Q_ASSERT(priv->m_entity == nullptr);
96             priv->m_entity = qobject_cast<QEntity *>(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(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 
122 private:
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 
Quick3DEntityLoader(QNode * parent)146 Quick3DEntityLoader::Quick3DEntityLoader(QNode *parent)
147     : QEntity(*new Quick3DEntityLoaderPrivate, parent)
148 {
149 }
150 
~Quick3DEntityLoader()151 Quick3DEntityLoader::~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 */
entity() const166 QObject *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 */
source() const176 QUrl Quick3DEntityLoader::source() const
177 {
178     Q_D(const Quick3DEntityLoader);
179     return d->m_source;
180 }
181 
setSource(const QUrl & url)182 void 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 
sourceComponent() const195 QQmlComponent *Quick3DEntityLoader::sourceComponent() const
196 {
197     Q_D(const Quick3DEntityLoader);
198     return d->m_sourceComponent;
199 }
200 
setSourceComponent(QQmlComponent * component)201 void 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(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  */
status() const224 Quick3DEntityLoader::Status Quick3DEntityLoader::status() const
225 {
226     Q_D(const Quick3DEntityLoader);
227     return d->m_status;
228 }
229 
Quick3DEntityLoaderPrivate()230 Quick3DEntityLoaderPrivate::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 
clear()241 void 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 
loadFromSource()267 void Quick3DEntityLoaderPrivate::loadFromSource()
268 {
269     Q_Q(Quick3DEntityLoader);
270 
271     if (m_source.isEmpty()) {
272         emit q->entityChanged();
273         return;
274     }
275 
276     loadComponent(m_source);
277 }
278 
loadComponent(const QUrl & source)279 void 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(q);
288     m_component = new QQmlComponent(owner.engine, owner.object);
289     QObject::connect(m_component, SIGNAL(statusChanged(QQmlComponent::Status)),
290                      q, SLOT(_q_componentStatusChanged(QQmlComponent::Status)));
291     m_component->loadUrl(source, QQmlComponent::Asynchronous);
292 }
293 
loadComponent(QQmlComponent * component)294 void 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(m_component ? m_component->status() : QQmlComponent::Null);
302 }
303 
_q_componentStatusChanged(QQmlComponent::Status status)304 void 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(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, m_context);
335 }
336 
setStatus(Quick3DEntityLoader::Status status)337 void 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(true);
343         emit q->statusChanged(m_status);
344         q->blockNotifications(blocked);
345     }
346 }
347 
348 } // namespace Quick
349 } // namespace Qt3DCore
350 
351 QT_END_NAMESPACE
352 
353 #include "moc_quick3dentityloader_p.cpp"
354