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 | { |
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 | |
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: |
82 | Quick3DEntityLoaderIncubator(Quick3DEntityLoader *loader) |
83 | : QQmlIncubator(AsynchronousIfNested), |
84 | m_loader(loader) |
85 | { |
86 | } |
87 | |
88 | protected: |
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 | |
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 | |
146 | Quick3DEntityLoader::Quick3DEntityLoader(QNode *parent) |
147 | : QEntity(*new Quick3DEntityLoaderPrivate, parent) |
148 | { |
149 | } |
150 | |
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 | */ |
166 | 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 | */ |
176 | QUrl Quick3DEntityLoader::source() const |
177 | { |
178 | Q_D(const Quick3DEntityLoader); |
179 | return d->m_source; |
180 | } |
181 | |
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 | |
195 | QQmlComponent *Quick3DEntityLoader::sourceComponent() const |
196 | { |
197 | Q_D(const Quick3DEntityLoader); |
198 | return d->m_sourceComponent; |
199 | } |
200 | |
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(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 | */ |
224 | Quick3DEntityLoader::Status Quick3DEntityLoader::status() const |
225 | { |
226 | Q_D(const Quick3DEntityLoader); |
227 | return d->m_status; |
228 | } |
229 | |
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 | |
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 | |
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(source: m_source); |
277 | } |
278 | |
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(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 | |
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(status: m_component ? m_component->status() : QQmlComponent::Null); |
302 | } |
303 | |
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(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 | |
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(block: true); |
343 | emit q->statusChanged(status: m_status); |
344 | q->blockNotifications(block: blocked); |
345 | } |
346 | } |
347 | |
348 | } // namespace Quick |
349 | } // namespace Qt3DCore |
350 | |
351 | QT_END_NAMESPACE |
352 | |
353 | #include "moc_quick3dentityloader_p.cpp" |
354 | |