|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtDeclarative module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "private/qdeclarativeloader_p_p.h" |
|
43 |
|
44 #include <qdeclarativeinfo.h> |
|
45 #include <qdeclarativeengine_p.h> |
|
46 #include <qdeclarativeglobal_p.h> |
|
47 |
|
48 QT_BEGIN_NAMESPACE |
|
49 |
|
50 QDeclarativeLoaderPrivate::QDeclarativeLoaderPrivate() |
|
51 : item(0), component(0), ownComponent(false) |
|
52 { |
|
53 } |
|
54 |
|
55 QDeclarativeLoaderPrivate::~QDeclarativeLoaderPrivate() |
|
56 { |
|
57 } |
|
58 |
|
59 void QDeclarativeLoaderPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) |
|
60 { |
|
61 if (resizeItem == item) |
|
62 _q_updateSize(false); |
|
63 QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); |
|
64 } |
|
65 |
|
66 void QDeclarativeLoaderPrivate::clear() |
|
67 { |
|
68 if (ownComponent) { |
|
69 component->deleteLater(); |
|
70 component = 0; |
|
71 ownComponent = false; |
|
72 } |
|
73 source = QUrl(); |
|
74 |
|
75 if (item) { |
|
76 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) { |
|
77 QDeclarativeItemPrivate *p = |
|
78 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem)); |
|
79 p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); |
|
80 } |
|
81 |
|
82 // We can't delete immediately because our item may have triggered |
|
83 // the Loader to load a different item. |
|
84 if (item->scene()) { |
|
85 item->scene()->removeItem(item); |
|
86 } else { |
|
87 item->setParentItem(0); |
|
88 item->setVisible(false); |
|
89 } |
|
90 item->deleteLater(); |
|
91 item = 0; |
|
92 } |
|
93 } |
|
94 |
|
95 void QDeclarativeLoaderPrivate::initResize() |
|
96 { |
|
97 Q_Q(QDeclarativeLoader); |
|
98 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) { |
|
99 QDeclarativeItemPrivate *p = |
|
100 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem)); |
|
101 p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); |
|
102 } else if (item && item->isWidget()) { |
|
103 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item); |
|
104 widget->installEventFilter(q); |
|
105 } |
|
106 _q_updateSize(); |
|
107 } |
|
108 |
|
109 /*! |
|
110 \qmlclass Loader QDeclarativeLoader |
|
111 \since 4.7 |
|
112 \inherits Item |
|
113 |
|
114 \brief The Loader item allows dynamically loading an Item-based |
|
115 subtree from a QML URL or Component. |
|
116 |
|
117 Loader instantiates an item from a component. The component to |
|
118 instantiate may be specified directly by the \c sourceComponent |
|
119 property, or loaded from a URL via the \c source property. |
|
120 |
|
121 It is also an effective means of delaying the creation of a component |
|
122 until it is required: |
|
123 \code |
|
124 Loader { id: pageLoader } |
|
125 Rectangle { |
|
126 MouseArea { anchors.fill: parent; onClicked: pageLoader.source = "Page1.qml" } |
|
127 } |
|
128 \endcode |
|
129 |
|
130 If the Loader source is changed, any previous items instantiated |
|
131 will be destroyed. Setting \c source to an empty string, or setting |
|
132 sourceComponent to \e undefined |
|
133 will destroy the currently instantiated items, freeing resources |
|
134 and leaving the Loader empty. For example: |
|
135 |
|
136 \code |
|
137 pageLoader.source = "" |
|
138 or |
|
139 pageLoader.sourceComponent = undefined |
|
140 \endcode |
|
141 |
|
142 unloads "Page1.qml" and frees resources consumed by it. |
|
143 |
|
144 \sa {dynamic-object-creation}{Dynamic Object Creation} |
|
145 */ |
|
146 |
|
147 /*! |
|
148 \internal |
|
149 \class QDeclarativeLoader |
|
150 \qmlclass Loader |
|
151 */ |
|
152 |
|
153 /*! |
|
154 Create a new QDeclarativeLoader instance. |
|
155 */ |
|
156 QDeclarativeLoader::QDeclarativeLoader(QDeclarativeItem *parent) |
|
157 : QDeclarativeItem(*(new QDeclarativeLoaderPrivate), parent) |
|
158 { |
|
159 Q_D(QDeclarativeItem); |
|
160 d->flags |= QGraphicsItem::ItemIsFocusScope; |
|
161 } |
|
162 |
|
163 /*! |
|
164 Destroy the loader instance. |
|
165 */ |
|
166 QDeclarativeLoader::~QDeclarativeLoader() |
|
167 { |
|
168 } |
|
169 |
|
170 /*! |
|
171 \qmlproperty url Loader::source |
|
172 This property holds the URL of the QML component to |
|
173 instantiate. |
|
174 |
|
175 \sa sourceComponent, status, progress |
|
176 */ |
|
177 QUrl QDeclarativeLoader::source() const |
|
178 { |
|
179 Q_D(const QDeclarativeLoader); |
|
180 return d->source; |
|
181 } |
|
182 |
|
183 void QDeclarativeLoader::setSource(const QUrl &url) |
|
184 { |
|
185 Q_D(QDeclarativeLoader); |
|
186 if (d->source == url) |
|
187 return; |
|
188 |
|
189 d->clear(); |
|
190 |
|
191 d->source = url; |
|
192 if (d->source.isEmpty()) { |
|
193 emit sourceChanged(); |
|
194 emit statusChanged(); |
|
195 emit progressChanged(); |
|
196 emit itemChanged(); |
|
197 return; |
|
198 } |
|
199 |
|
200 d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this); |
|
201 d->ownComponent = true; |
|
202 if (!d->component->isLoading()) { |
|
203 d->_q_sourceLoaded(); |
|
204 } else { |
|
205 connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), |
|
206 this, SLOT(_q_sourceLoaded())); |
|
207 connect(d->component, SIGNAL(progressChanged(qreal)), |
|
208 this, SIGNAL(progressChanged())); |
|
209 emit statusChanged(); |
|
210 emit progressChanged(); |
|
211 emit sourceChanged(); |
|
212 emit itemChanged(); |
|
213 } |
|
214 } |
|
215 |
|
216 /*! |
|
217 \qmlproperty Component Loader::sourceComponent |
|
218 The sourceComponent property holds the \l{Component} to instantiate. |
|
219 |
|
220 \qml |
|
221 Item { |
|
222 Component { |
|
223 id: redSquare |
|
224 Rectangle { color: "red"; width: 10; height: 10 } |
|
225 } |
|
226 |
|
227 Loader { sourceComponent: redSquare } |
|
228 Loader { sourceComponent: redSquare; x: 10 } |
|
229 } |
|
230 \endqml |
|
231 |
|
232 \sa source, progress |
|
233 */ |
|
234 |
|
235 QDeclarativeComponent *QDeclarativeLoader::sourceComponent() const |
|
236 { |
|
237 Q_D(const QDeclarativeLoader); |
|
238 return d->component; |
|
239 } |
|
240 |
|
241 void QDeclarativeLoader::setSourceComponent(QDeclarativeComponent *comp) |
|
242 { |
|
243 Q_D(QDeclarativeLoader); |
|
244 if (comp == d->component) |
|
245 return; |
|
246 |
|
247 d->clear(); |
|
248 |
|
249 d->component = comp; |
|
250 d->ownComponent = false; |
|
251 if (!d->component) { |
|
252 emit sourceChanged(); |
|
253 emit statusChanged(); |
|
254 emit progressChanged(); |
|
255 emit itemChanged(); |
|
256 return; |
|
257 } |
|
258 |
|
259 if (!d->component->isLoading()) { |
|
260 d->_q_sourceLoaded(); |
|
261 } else { |
|
262 connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), |
|
263 this, SLOT(_q_sourceLoaded())); |
|
264 connect(d->component, SIGNAL(progressChanged(qreal)), |
|
265 this, SIGNAL(progressChanged())); |
|
266 emit progressChanged(); |
|
267 emit sourceChanged(); |
|
268 emit statusChanged(); |
|
269 emit itemChanged(); |
|
270 } |
|
271 } |
|
272 |
|
273 void QDeclarativeLoader::resetSourceComponent() |
|
274 { |
|
275 setSourceComponent(0); |
|
276 } |
|
277 |
|
278 void QDeclarativeLoaderPrivate::_q_sourceLoaded() |
|
279 { |
|
280 Q_Q(QDeclarativeLoader); |
|
281 |
|
282 if (component) { |
|
283 if (!component->errors().isEmpty()) { |
|
284 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors()); |
|
285 emit q->sourceChanged(); |
|
286 emit q->statusChanged(); |
|
287 emit q->progressChanged(); |
|
288 return; |
|
289 } |
|
290 |
|
291 QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(q)); |
|
292 ctxt->setContextObject(q); |
|
293 |
|
294 QDeclarativeComponent *c = component; |
|
295 QObject *obj = component->create(ctxt); |
|
296 if (component != c) { |
|
297 // component->create could trigger a change in source that causes |
|
298 // component to be set to something else. In that case we just |
|
299 // need to cleanup. |
|
300 delete obj; |
|
301 delete ctxt; |
|
302 return; |
|
303 } |
|
304 if (obj) { |
|
305 item = qobject_cast<QGraphicsObject *>(obj); |
|
306 if (item) { |
|
307 QDeclarative_setParent_noEvent(ctxt, obj); |
|
308 QDeclarative_setParent_noEvent(item, q); |
|
309 item->setParentItem(q); |
|
310 // item->setFocus(true); |
|
311 initResize(); |
|
312 } else { |
|
313 qmlInfo(q) << QDeclarativeLoader::tr("Loader does not support loading non-visual elements."); |
|
314 delete obj; |
|
315 delete ctxt; |
|
316 } |
|
317 } else { |
|
318 if (!component->errors().isEmpty()) |
|
319 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors()); |
|
320 delete obj; |
|
321 delete ctxt; |
|
322 source = QUrl(); |
|
323 } |
|
324 emit q->sourceChanged(); |
|
325 emit q->statusChanged(); |
|
326 emit q->progressChanged(); |
|
327 emit q->itemChanged(); |
|
328 emit q->loaded(); |
|
329 } |
|
330 } |
|
331 |
|
332 /*! |
|
333 \qmlproperty enumeration Loader::status |
|
334 |
|
335 This property holds the status of QML loading. It can be one of: |
|
336 \list |
|
337 \o Loader.Null - no QML source has been set |
|
338 \o Loader.Ready - the QML source has been loaded |
|
339 \o Loader.Loading - the QML source is currently being loaded |
|
340 \o Loader.Error - an error occurred while loading the QML source |
|
341 \endlist |
|
342 |
|
343 Note that a change in the status property does not cause anything to happen |
|
344 (although it reflects what has happened to the loader internally). If you wish |
|
345 to react to the change in status you need to do it yourself, for example in one |
|
346 of the following ways: |
|
347 \list |
|
348 \o Create a state, so that a state change occurs, e.g. State{name: 'loaded'; when: loader.status = Loader.Ready;} |
|
349 \o Do something inside the onLoaded signal handler, e.g. Loader{id: loader; onLoaded: console.log('Loaded');} |
|
350 \o Bind to the status variable somewhere, e.g. Text{text: if(loader.status!=Loader.Ready){'Not Loaded';}else{'Loaded';}} |
|
351 \endlist |
|
352 \sa progress |
|
353 |
|
354 Note that if the source is a local file, the status will initially be Ready (or Error). While |
|
355 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked. |
|
356 */ |
|
357 |
|
358 QDeclarativeLoader::Status QDeclarativeLoader::status() const |
|
359 { |
|
360 Q_D(const QDeclarativeLoader); |
|
361 |
|
362 if (d->component) |
|
363 return static_cast<QDeclarativeLoader::Status>(d->component->status()); |
|
364 |
|
365 if (d->item) |
|
366 return Ready; |
|
367 |
|
368 return d->source.isEmpty() ? Null : Error; |
|
369 } |
|
370 |
|
371 void QDeclarativeLoader::componentComplete() |
|
372 { |
|
373 QDeclarativeItem::componentComplete(); |
|
374 if (status() == Ready) |
|
375 emit loaded(); |
|
376 } |
|
377 |
|
378 |
|
379 /*! |
|
380 \qmlsignal Loader::onLoaded() |
|
381 |
|
382 This handler is called when the \l status becomes Loader.Ready, or on successful |
|
383 initial load. |
|
384 */ |
|
385 |
|
386 |
|
387 /*! |
|
388 \qmlproperty real Loader::progress |
|
389 |
|
390 This property holds the progress of loading QML data from the network, from |
|
391 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so |
|
392 this value will rapidly change from 0 to 1. |
|
393 |
|
394 \sa status |
|
395 */ |
|
396 qreal QDeclarativeLoader::progress() const |
|
397 { |
|
398 Q_D(const QDeclarativeLoader); |
|
399 |
|
400 if (d->item) |
|
401 return 1.0; |
|
402 |
|
403 if (d->component) |
|
404 return d->component->progress(); |
|
405 |
|
406 return 0.0; |
|
407 } |
|
408 |
|
409 void QDeclarativeLoaderPrivate::_q_updateSize(bool loaderGeometryChanged) |
|
410 { |
|
411 Q_Q(QDeclarativeLoader); |
|
412 if (!item) |
|
413 return; |
|
414 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) { |
|
415 q->setImplicitWidth(qmlItem->width()); |
|
416 if (loaderGeometryChanged && q->widthValid()) |
|
417 qmlItem->setWidth(q->width()); |
|
418 q->setImplicitHeight(qmlItem->height()); |
|
419 if (loaderGeometryChanged && q->heightValid()) |
|
420 qmlItem->setHeight(q->height()); |
|
421 } else if (item && item->isWidget()) { |
|
422 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item); |
|
423 QSizeF widgetSize = widget->size(); |
|
424 q->setImplicitWidth(widgetSize.width()); |
|
425 if (loaderGeometryChanged && q->widthValid()) |
|
426 widgetSize.setWidth(q->width()); |
|
427 q->setImplicitHeight(widgetSize.height()); |
|
428 if (loaderGeometryChanged && q->heightValid()) |
|
429 widgetSize.setHeight(q->height()); |
|
430 if (widget->size() != widgetSize) |
|
431 widget->resize(widgetSize); |
|
432 } |
|
433 } |
|
434 |
|
435 /*! |
|
436 \qmlproperty Item Loader::item |
|
437 This property holds the top-level item created from source. |
|
438 */ |
|
439 QGraphicsObject *QDeclarativeLoader::item() const |
|
440 { |
|
441 Q_D(const QDeclarativeLoader); |
|
442 return d->item; |
|
443 } |
|
444 |
|
445 void QDeclarativeLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
|
446 { |
|
447 Q_D(QDeclarativeLoader); |
|
448 if (newGeometry != oldGeometry) { |
|
449 d->_q_updateSize(); |
|
450 } |
|
451 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); |
|
452 } |
|
453 |
|
454 QVariant QDeclarativeLoader::itemChange(GraphicsItemChange change, const QVariant &value) |
|
455 { |
|
456 Q_D(QDeclarativeLoader); |
|
457 if (change == ItemSceneHasChanged) { |
|
458 if (d->item && d->item->isWidget()) { |
|
459 d->item->removeEventFilter(this); |
|
460 d->item->installEventFilter(this); |
|
461 } |
|
462 } |
|
463 return QDeclarativeItem::itemChange(change, value); |
|
464 } |
|
465 |
|
466 bool QDeclarativeLoader::eventFilter(QObject *watched, QEvent *e) |
|
467 { |
|
468 Q_D(QDeclarativeLoader); |
|
469 if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) { |
|
470 if (d->item && d->item->isWidget()) |
|
471 d->_q_updateSize(false); |
|
472 } |
|
473 return QDeclarativeItem::eventFilter(watched, e); |
|
474 } |
|
475 |
|
476 #include <moc_qdeclarativeloader_p.cpp> |
|
477 |
|
478 QT_END_NAMESPACE |