1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativeloader_p_p.h"
44 #include <qdeclarativeinfo.h>
45 #include <qdeclarativeengine_p.h>
46 #include <qdeclarativeglobal_p.h>
50 QDeclarativeLoaderPrivate::QDeclarativeLoaderPrivate()
51 : item(0), component(0), ownComponent(false), updatingSize(false),
52 itemWidthValid(false), itemHeightValid(false)
56 QDeclarativeLoaderPrivate::~QDeclarativeLoaderPrivate()
60 void QDeclarativeLoaderPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
62 if (resizeItem == item) {
63 if (!updatingSize && newGeometry.width() != oldGeometry.width())
64 itemWidthValid = true;
65 if (!updatingSize && newGeometry.height() != oldGeometry.height())
66 itemHeightValid = true;
69 QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
72 void QDeclarativeLoaderPrivate::clear()
75 component->deleteLater();
82 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
83 QDeclarativeItemPrivate *p =
84 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
85 p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
88 // We can't delete immediately because our item may have triggered
89 // the Loader to load a different item.
91 item->scene()->removeItem(item);
93 item->setParentItem(0);
94 item->setVisible(false);
101 void QDeclarativeLoaderPrivate::initResize()
103 Q_Q(QDeclarativeLoader);
104 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
105 QDeclarativeItemPrivate *p =
106 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
107 p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
108 // We may override the item's size, so we need to remember
109 // whether the item provided its own valid size.
110 itemWidthValid = p->widthValid;
111 itemHeightValid = p->heightValid;
112 } else if (item && item->isWidget()) {
113 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
114 widget->installEventFilter(q);
120 \qmlclass Loader QDeclarativeLoader
121 \ingroup qml-utility-elements
125 \brief The Loader item allows dynamically loading an Item-based
126 subtree from a URL or Component.
128 Loader is used to dynamically load visual QML components. It can load a
129 QML file (using the \l source property) or a \l Component object (using
130 the \l sourceComponent property). It is useful for delaying the creation
131 of a component until it is required: for example, when a component should
132 be created on demand, or when a component should not be created
133 unnecessarily for performance reasons.
135 Here is a Loader that loads "Page1.qml" as a component when the
136 \l MouseArea is clicked:
138 \snippet doc/src/snippets/declarative/loader/simple.qml 0
140 The loaded item can be accessed using the \l item property.
142 If the \l source or \l sourceComponent changes, any previously instantiated
143 items are destroyed. Setting \l source to an empty string or setting
144 \l sourceComponent to \c undefined destroys the currently loaded item,
145 freeing resources and leaving the Loader empty.
147 \section2 Loader sizing behavior
149 Loader is like any other visual item and must be positioned and sized
150 accordingly to become visible.
153 \o If an explicit size is not specified for the Loader, the Loader
154 is automatically resized to the size of the loaded item once the
156 \o If the size of the Loader is specified explicitly by setting
157 the width, height or by anchoring, the loaded item will be resized
158 to the size of the Loader.
161 In both scenarios the size of the item and the Loader are identical.
162 This ensures that anchoring to the Loader is equivalent to anchoring
170 \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0
171 \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0
173 \o The red rectangle will be sized to the size of the root item.
174 \o The red rectangle will be 50x50, centered in the root item.
178 \section2 Receiving signals from loaded items
180 Any signals emitted from the loaded item can be received using the
181 \l Connections element. For example, the following \c application.qml
182 loads \c MyItem.qml, and is able to receive the \c message signal from
183 the loaded item through a \l Connections object:
190 \o \snippet doc/src/snippets/declarative/loader/connections.qml 0
191 \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0
194 Alternatively, since \c MyItem.qml is loaded within the scope of the
195 Loader, it could also directly call any function defined in the Loader or
199 \section2 Focus and key events
201 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
202 \c true for any of its children to get the \e {active focus}. (See
203 \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
204 for more details.) Any key events received in the loaded item should likely
205 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
207 For example, the following \c application.qml loads \c KeyReader.qml when
208 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
209 set to \c true for the Loader as well as the \l Item in the dynamically
217 \o \snippet doc/src/snippets/declarative/loader/focus.qml 0
218 \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0
221 Once \c KeyReader.qml is loaded, it accepts key events and sets
222 \c event.accepted to \c true so that the event is not propagated to the
225 \sa {dynamic-object-creation}{Dynamic Object Creation}
228 QDeclarativeLoader::QDeclarativeLoader(QDeclarativeItem *parent)
229 : QDeclarativeImplicitSizeItem(*(new QDeclarativeLoaderPrivate), parent)
231 Q_D(QDeclarativeLoader);
232 d->flags |= QGraphicsItem::ItemIsFocusScope;
235 QDeclarativeLoader::~QDeclarativeLoader()
237 Q_D(QDeclarativeLoader);
239 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(d->item)) {
240 QDeclarativeItemPrivate *p =
241 static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem));
242 p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
248 \qmlproperty url Loader::source
249 This property holds the URL of the QML component to instantiate.
251 Note the QML component must be an \l{Item}-based component. The loader
252 cannot load non-visual components.
254 To unload the currently loaded item, set this property to an empty string,
255 or set \l sourceComponent to \c undefined. Setting \c source to a
256 new URL will also cause the item created by the previous URL to be unloaded.
258 \sa sourceComponent, status, progress
260 QUrl QDeclarativeLoader::source() const
262 Q_D(const QDeclarativeLoader);
266 void QDeclarativeLoader::setSource(const QUrl &url)
268 Q_D(QDeclarativeLoader);
269 if (d->source == url)
276 if (d->source.isEmpty()) {
277 emit sourceChanged();
278 emit statusChanged();
279 emit progressChanged();
284 d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
285 d->ownComponent = true;
287 if (isComponentComplete())
292 \qmlproperty Component Loader::sourceComponent
293 This property holds the \l{Component} to instantiate.
299 Rectangle { color: "red"; width: 10; height: 10 }
302 Loader { sourceComponent: redSquare }
303 Loader { sourceComponent: redSquare; x: 10 }
307 To unload the currently loaded item, set this property to an empty string
313 QDeclarativeComponent *QDeclarativeLoader::sourceComponent() const
315 Q_D(const QDeclarativeLoader);
319 void QDeclarativeLoader::setSourceComponent(QDeclarativeComponent *comp)
321 Q_D(QDeclarativeLoader);
322 if (comp == d->component)
328 d->ownComponent = false;
331 emit sourceChanged();
332 emit statusChanged();
333 emit progressChanged();
338 if (isComponentComplete())
342 void QDeclarativeLoader::resetSourceComponent()
344 setSourceComponent(0);
347 void QDeclarativeLoaderPrivate::load()
349 Q_Q(QDeclarativeLoader);
351 if (!q->isComponentComplete() || !component)
354 if (!component->isLoading()) {
357 QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
358 q, SLOT(_q_sourceLoaded()));
359 QObject::connect(component, SIGNAL(progressChanged(qreal)),
360 q, SIGNAL(progressChanged()));
361 emit q->statusChanged();
362 emit q->progressChanged();
363 emit q->sourceChanged();
364 emit q->itemChanged();
368 void QDeclarativeLoaderPrivate::_q_sourceLoaded()
370 Q_Q(QDeclarativeLoader);
373 if (!component->errors().isEmpty()) {
374 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
375 emit q->sourceChanged();
376 emit q->statusChanged();
377 emit q->progressChanged();
381 QDeclarativeContext *creationContext = component->creationContext();
382 if (!creationContext) creationContext = qmlContext(q);
383 QDeclarativeContext *ctxt = new QDeclarativeContext(creationContext);
384 ctxt->setContextObject(q);
386 QDeclarativeGuard<QDeclarativeComponent> c = component;
387 QObject *obj = component->beginCreate(ctxt);
388 if (component != c) {
389 // component->create could trigger a change in source that causes
390 // component to be set to something else. In that case we just
399 item = qobject_cast<QGraphicsObject *>(obj);
401 QDeclarative_setParent_noEvent(ctxt, obj);
402 QDeclarative_setParent_noEvent(item, q);
403 item->setParentItem(q);
404 // item->setFocus(true);
407 qmlInfo(q) << QDeclarativeLoader::tr("Loader does not support loading non-visual elements.");
412 if (!component->errors().isEmpty())
413 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
418 component->completeCreate();
419 emit q->sourceChanged();
420 emit q->statusChanged();
421 emit q->progressChanged();
422 emit q->itemChanged();
428 \qmlproperty enumeration Loader::status
430 This property holds the status of QML loading. It can be one of:
432 \o Loader.Null - no QML source has been set
433 \o Loader.Ready - the QML source has been loaded
434 \o Loader.Loading - the QML source is currently being loaded
435 \o Loader.Error - an error occurred while loading the QML source
438 Use this status to provide an update or respond to the status change in some way.
439 For example, you could:
442 \o Trigger a state change:
444 State { name: 'loaded'; when: loader.status == Loader.Ready }
447 \o Implement an \c onStatusChanged signal handler:
451 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
455 \o Bind to the status value:
457 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
461 Note that if the source is a local file, the status will initially be Ready (or Error). While
462 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
467 QDeclarativeLoader::Status QDeclarativeLoader::status() const
469 Q_D(const QDeclarativeLoader);
472 return static_cast<QDeclarativeLoader::Status>(d->component->status());
477 return d->source.isEmpty() ? Null : Error;
480 void QDeclarativeLoader::componentComplete()
482 Q_D(QDeclarativeLoader);
484 QDeclarativeItem::componentComplete();
490 \qmlsignal Loader::onLoaded()
492 This handler is called when the \l status becomes \c Loader.Ready, or on successful
498 \qmlproperty real Loader::progress
500 This property holds the progress of loading QML data from the network, from
501 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
502 this value will rapidly change from 0 to 1.
506 qreal QDeclarativeLoader::progress() const
508 Q_D(const QDeclarativeLoader);
514 return d->component->progress();
519 void QDeclarativeLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
521 Q_Q(QDeclarativeLoader);
522 if (!item || updatingSize)
526 if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) {
528 q->setImplicitWidth(qmlItem->implicitWidth());
530 q->setImplicitWidth(qmlItem->width());
531 if (loaderGeometryChanged && q->widthValid())
532 qmlItem->setWidth(q->width());
533 if (!itemHeightValid)
534 q->setImplicitHeight(qmlItem->implicitHeight());
536 q->setImplicitHeight(qmlItem->height());
537 if (loaderGeometryChanged && q->heightValid())
538 qmlItem->setHeight(q->height());
539 } else if (item && item->isWidget()) {
540 QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item);
541 QSizeF widgetSize = widget->size();
542 q->setImplicitWidth(widgetSize.width());
543 if (loaderGeometryChanged && q->widthValid())
544 widgetSize.setWidth(q->width());
545 q->setImplicitHeight(widgetSize.height());
546 if (loaderGeometryChanged && q->heightValid())
547 widgetSize.setHeight(q->height());
548 if (widget->size() != widgetSize)
549 widget->resize(widgetSize);
551 updatingSize = false;
555 \qmlproperty Item Loader::item
556 This property holds the top-level item that is currently loaded.
558 QGraphicsObject *QDeclarativeLoader::item() const
560 Q_D(const QDeclarativeLoader);
564 void QDeclarativeLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
566 Q_D(QDeclarativeLoader);
567 if (newGeometry != oldGeometry) {
570 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
573 QVariant QDeclarativeLoader::itemChange(GraphicsItemChange change, const QVariant &value)
575 Q_D(QDeclarativeLoader);
576 if (change == ItemSceneHasChanged) {
577 if (d->item && d->item->isWidget()) {
578 d->item->removeEventFilter(this);
579 d->item->installEventFilter(this);
582 return QDeclarativeItem::itemChange(change, value);
585 bool QDeclarativeLoader::eventFilter(QObject *watched, QEvent *e)
587 Q_D(QDeclarativeLoader);
588 if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) {
589 if (d->item && d->item->isWidget())
590 d->_q_updateSize(false);
592 return QDeclarativeItem::eventFilter(watched, e);
595 #include <moc_qdeclarativeloader_p.cpp>