1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickloader_p_p.h"
44 #include <QtDeclarative/qdeclarativeinfo.h>
46 #include <private/qdeclarativeengine_p.h>
47 #include <private/qdeclarativeglobal_p.h>
49 #include <private/qdeclarativecomponent_p.h>
51 #include <private/qv8_p.h>
55 QQuickLoaderPrivate::QQuickLoaderPrivate()
56 : item(0), component(0), itemContext(0), incubator(0), updatingSize(false),
57 itemWidthValid(false), itemHeightValid(false),
58 active(true), loadingFromSource(false), asynchronous(false)
62 QQuickLoaderPrivate::~QQuickLoaderPrivate()
65 disposeInitialPropertyValues();
68 void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
70 if (resizeItem == item) {
71 if (!updatingSize && newGeometry.width() != oldGeometry.width())
72 itemWidthValid = true;
73 if (!updatingSize && newGeometry.height() != oldGeometry.height())
74 itemHeightValid = true;
77 QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
80 void QQuickLoaderPrivate::clear()
82 disposeInitialPropertyValues();
87 if (loadingFromSource && component) {
88 component->deleteLater();
94 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
95 p->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
97 // We can't delete immediately because our item may have triggered
98 // the Loader to load a different item.
99 item->setParentItem(0);
100 item->setVisible(false);
106 void QQuickLoaderPrivate::initResize()
108 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
109 p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
110 // We may override the item's size, so we need to remember
111 // whether the item provided its own valid size.
112 itemWidthValid = p->widthValid;
113 itemHeightValid = p->heightValid;
118 \qmlclass Loader QQuickLoader
119 \inqmlmodule QtQuick 2
120 \ingroup qml-utility-elements
123 \brief The Loader item allows dynamically loading an Item-based
124 subtree from a URL or Component.
126 Loader is used to dynamically load visual QML components. It can load a
127 QML file (using the \l source property) or a \l Component object (using
128 the \l sourceComponent property). It is useful for delaying the creation
129 of a component until it is required: for example, when a component should
130 be created on demand, or when a component should not be created
131 unnecessarily for performance reasons.
133 Here is a Loader that loads "Page1.qml" as a component when the
134 \l MouseArea is clicked:
136 \snippet doc/src/snippets/declarative/loader/simple.qml 0
138 The loaded item can be accessed using the \l item property.
140 If the \l source or \l sourceComponent changes, any previously instantiated
141 items are destroyed. Setting \l source to an empty string or setting
142 \l sourceComponent to \c undefined destroys the currently loaded item,
143 freeing resources and leaving the Loader empty.
145 \section2 Loader sizing behavior
147 Loader is like any other visual item and must be positioned and sized
148 accordingly to become visible.
151 \o If an explicit size is not specified for the Loader, the Loader
152 is automatically resized to the size of the loaded item once the
154 \o If the size of the Loader is specified explicitly by setting
155 the width, height or by anchoring, the loaded item will be resized
156 to the size of the Loader.
159 In both scenarios the size of the item and the Loader are identical.
160 This ensures that anchoring to the Loader is equivalent to anchoring
168 \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0
169 \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0
171 \o The red rectangle will be sized to the size of the root item.
172 \o The red rectangle will be 50x50, centered in the root item.
176 \section2 Receiving signals from loaded items
178 Any signals emitted from the loaded item can be received using the
179 \l Connections element. For example, the following \c application.qml
180 loads \c MyItem.qml, and is able to receive the \c message signal from
181 the loaded item through a \l Connections object:
188 \o \snippet doc/src/snippets/declarative/loader/connections.qml 0
189 \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0
192 Alternatively, since \c MyItem.qml is loaded within the scope of the
193 Loader, it could also directly call any function defined in the Loader or
197 \section2 Focus and key events
199 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
200 \c true for any of its children to get the \e {active focus}. (See
201 \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
202 for more details.) Any key events received in the loaded item should likely
203 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
205 For example, the following \c application.qml loads \c KeyReader.qml when
206 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
207 set to \c true for the Loader as well as the \l Item in the dynamically
215 \o \snippet doc/src/snippets/declarative/loader/focus.qml 0
216 \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0
219 Once \c KeyReader.qml is loaded, it accepts key events and sets
220 \c event.accepted to \c true so that the event is not propagated to the
223 \sa {dynamic-object-creation}{Dynamic Object Creation}
226 QQuickLoader::QQuickLoader(QQuickItem *parent)
227 : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
229 setFlag(ItemIsFocusScope);
232 QQuickLoader::~QQuickLoader()
236 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
237 p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
242 \qmlproperty bool QtQuick2::Loader::active
243 This property is \c true if the Loader is currently active.
244 The default value for the \l active property is \c true.
246 If the Loader is inactive, changing the \l source or \l sourceComponent
247 will not cause the item to be instantiated until the Loader is made active.
249 Setting the value to inactive will cause any \l item loaded by the loader
250 to be released, but will not affect the \l source or \l sourceComponent.
252 The \l status of an inactive loader is always \c Null.
254 \sa source, sourceComponent
256 bool QQuickLoader::active() const
258 Q_D(const QQuickLoader);
262 void QQuickLoader::setActive(bool newVal)
265 if (d->active != newVal) {
267 if (newVal == true) {
268 if (d->loadingFromSource) {
271 loadFromSourceComponent();
275 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
276 p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
278 // We can't delete immediately because our item may have triggered
279 // the Loader to load a different item.
280 d->item->setParentItem(0);
281 d->item->setVisible(false);
282 d->item->deleteLater();
286 emit statusChanged();
288 emit activeChanged();
294 \qmlproperty url QtQuick2::Loader::source
295 This property holds the URL of the QML component to instantiate.
297 Note the QML component must be an \l{Item}-based component. The loader
298 cannot load non-visual components.
300 To unload the currently loaded item, set this property to an empty string,
301 or set \l sourceComponent to \c undefined. Setting \c source to a
302 new URL will also cause the item created by the previous URL to be unloaded.
304 \sa sourceComponent, status, progress
306 QUrl QQuickLoader::source() const
308 Q_D(const QQuickLoader);
312 void QQuickLoader::setSource(const QUrl &url)
314 setSource(url, true); // clear previous values
317 void QQuickLoader::setSource(const QUrl &url, bool needsClear)
320 if (d->source == url)
327 d->loadingFromSource = true;
332 emit sourceChanged();
335 void QQuickLoader::loadFromSource()
338 if (d->source.isEmpty()) {
339 emit sourceChanged();
340 emit statusChanged();
341 emit progressChanged();
346 if (isComponentComplete()) {
347 d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
353 \qmlproperty Component QtQuick2::Loader::sourceComponent
354 This property holds the \l{Component} to instantiate.
360 Rectangle { color: "red"; width: 10; height: 10 }
363 Loader { sourceComponent: redSquare }
364 Loader { sourceComponent: redSquare; x: 10 }
368 To unload the currently loaded item, set this property to an empty string
374 QDeclarativeComponent *QQuickLoader::sourceComponent() const
376 Q_D(const QQuickLoader);
380 void QQuickLoader::setSourceComponent(QDeclarativeComponent *comp)
383 if (comp == d->component)
389 d->loadingFromSource = false;
392 loadFromSourceComponent();
394 emit sourceComponentChanged();
397 void QQuickLoader::resetSourceComponent()
399 setSourceComponent(0);
402 void QQuickLoader::loadFromSourceComponent()
406 emit sourceComponentChanged();
407 emit statusChanged();
408 emit progressChanged();
413 if (isComponentComplete())
418 \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
420 Creates an object instance of the given \a source component that will have
421 the given \a properties. The \a properties argument is optional. The instance
422 will be accessible via the \l item property once loading and instantiation
425 If the \l active property is \c false at the time when this function is called,
426 the given \a source component will not be loaded but the \a source and initial
427 \a properties will be cached. When the loader is made \l active, an instance of
428 the \a source component will be created with the initial \a properties set.
430 Setting the initial property values of an instance of a component in this manner
431 will \bold{not} trigger any associated \l{Behavior}s.
433 Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
434 is changed after calling this function but prior to setting the loader \l active.
441 // ExampleComponent.qml
453 to: (rect.width + 20)
466 onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
469 Component.onCompleted: {
470 squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
471 // will trigger the onLoaded code when complete.
479 void QQuickLoader::setSource(QDeclarativeV8Function *args)
484 bool ipvError = false;
485 args->returnValue(v8::Undefined());
486 v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
491 QUrl sourceUrl = d->resolveSourceUrl(args);
492 if (!ipv.IsEmpty()) {
493 d->disposeInitialPropertyValues();
494 d->initialPropertyValues = qPersistentNew(ipv);
495 d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
498 setSource(sourceUrl, false); // already cleared and set ipv above.
501 void QQuickLoaderPrivate::disposeInitialPropertyValues()
503 if (!initialPropertyValues.IsEmpty())
504 qPersistentDispose(initialPropertyValues);
505 if (!qmlGlobalForIpv.IsEmpty())
506 qPersistentDispose(qmlGlobalForIpv);
509 void QQuickLoaderPrivate::load()
513 if (!q->isComponentComplete() || !component)
516 if (!component->isLoading()) {
519 QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
520 q, SLOT(_q_sourceLoaded()));
521 QObject::connect(component, SIGNAL(progressChanged(qreal)),
522 q, SIGNAL(progressChanged()));
523 emit q->statusChanged();
524 emit q->progressChanged();
525 if (loadingFromSource)
526 emit q->sourceChanged();
528 emit q->sourceComponentChanged();
529 emit q->itemChanged();
533 void QQuickLoaderIncubator::setInitialState(QObject *o)
535 loader->setInitialState(o);
538 void QQuickLoaderPrivate::setInitialState(QObject *obj)
542 QQuickItem *item = qobject_cast<QQuickItem*>(obj);
544 QDeclarative_setParent_noEvent(itemContext, obj);
545 QDeclarative_setParent_noEvent(item, q);
546 item->setParentItem(q);
549 if (initialPropertyValues.IsEmpty())
552 QDeclarativeComponentPrivate *d = QDeclarativeComponentPrivate::get(component);
553 Q_ASSERT(d && d->engine);
554 d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj);
557 void QQuickLoaderIncubator::statusChanged(Status status)
559 loader->incubatorStateChanged(status);
562 void QQuickLoaderPrivate::incubatorStateChanged(QDeclarativeIncubator::Status status)
565 if (status == QDeclarativeIncubator::Loading || status == QDeclarativeIncubator::Null)
568 if (status == QDeclarativeIncubator::Ready) {
569 QObject *obj = incubator->object();
570 item = qobject_cast<QQuickItem*>(obj);
574 qmlInfo(q) << QQuickLoader::tr("Loader does not support loading non-visual elements.");
580 } else if (status == QDeclarativeIncubator::Error) {
581 if (!incubator->errors().isEmpty())
582 QDeclarativeEnginePrivate::warning(qmlEngine(q), incubator->errors());
585 delete incubator->object();
588 if (loadingFromSource)
589 emit q->sourceChanged();
591 emit q->sourceComponentChanged();
592 emit q->statusChanged();
593 emit q->progressChanged();
594 emit q->itemChanged();
596 disposeInitialPropertyValues(); // cleanup
599 void QQuickLoaderPrivate::_q_sourceLoaded()
602 if (!component || !component->errors().isEmpty()) {
604 QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
605 if (loadingFromSource)
606 emit q->sourceChanged();
608 emit q->sourceComponentChanged();
609 emit q->statusChanged();
610 emit q->progressChanged();
611 disposeInitialPropertyValues(); // cleanup
615 QDeclarativeContext *creationContext = component->creationContext();
616 if (!creationContext) creationContext = qmlContext(q);
617 itemContext = new QDeclarativeContext(creationContext);
618 itemContext->setContextObject(q);
621 incubator = new QQuickLoaderIncubator(this, asynchronous ? QDeclarativeIncubator::Asynchronous : QDeclarativeIncubator::AsynchronousIfNested);
623 component->create(*incubator, itemContext);
625 if (incubator && incubator->status() == QDeclarativeIncubator::Loading)
626 emit q->statusChanged();
630 \qmlproperty enumeration QtQuick2::Loader::status
632 This property holds the status of QML loading. It can be one of:
634 \o Loader.Null - the loader is inactive or no QML source has been set
635 \o Loader.Ready - the QML source has been loaded
636 \o Loader.Loading - the QML source is currently being loaded
637 \o Loader.Error - an error occurred while loading the QML source
640 Use this status to provide an update or respond to the status change in some way.
641 For example, you could:
644 \o Trigger a state change:
646 State { name: 'loaded'; when: loader.status == Loader.Ready }
649 \o Implement an \c onStatusChanged signal handler:
653 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
657 \o Bind to the status value:
659 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
663 Note that if the source is a local file, the status will initially be Ready (or Error). While
664 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
669 QQuickLoader::Status QQuickLoader::status() const
671 Q_D(const QQuickLoader);
677 switch (d->component->status()) {
678 case QDeclarativeComponent::Loading:
680 case QDeclarativeComponent::Error:
682 case QDeclarativeComponent::Null:
690 switch (d->incubator->status()) {
691 case QDeclarativeIncubator::Loading:
693 case QDeclarativeIncubator::Error:
703 return d->source.isEmpty() ? Null : Error;
706 void QQuickLoader::componentComplete()
709 QQuickItem::componentComplete();
711 if (d->loadingFromSource) {
712 d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
719 \qmlsignal QtQuick2::Loader::onLoaded()
721 This handler is called when the \l status becomes \c Loader.Ready, or on successful
727 \qmlproperty real QtQuick2::Loader::progress
729 This property holds the progress of loading QML data from the network, from
730 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
731 this value will rapidly change from 0 to 1.
735 qreal QQuickLoader::progress() const
737 Q_D(const QQuickLoader);
743 return d->component->progress();
749 \qmlproperty bool QtQuick2::Loader::asynchronous
751 This property holds whether the component will be instantiated asynchronously.
753 Loading asynchronously creates the objects declared by the component
754 across multiple frames, and reduces the
755 likelihood of glitches in animation. When loading asynchronously the status
756 will change to Loader.Loading. Once the entire component has been created, the
757 \l item will be available and the status will change to Loader.Ready.
759 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
763 source: "mycomponent.qml"
765 visible: status == Loader.Ready
769 Note that this property affects object instantiation only; it is unrelated to
770 loading a component asynchronously via a network.
772 bool QQuickLoader::asynchronous() const
774 Q_D(const QQuickLoader);
775 return d->asynchronous;
778 void QQuickLoader::setAsynchronous(bool a)
781 if (d->asynchronous == a)
785 emit asynchronousChanged();
788 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
791 if (!item || updatingSize)
796 qreal iWidth = !itemWidthValid ? item->implicitWidth() : item->width();
797 qreal iHeight = !itemHeightValid ? item->implicitHeight() : item->height();
798 q->setImplicitSize(iWidth, iHeight);
800 if (loaderGeometryChanged && q->widthValid())
801 item->setWidth(q->width());
802 if (loaderGeometryChanged && q->heightValid())
803 item->setHeight(q->height());
805 updatingSize = false;
809 \qmlproperty Item QtQuick2::Loader::item
810 This property holds the top-level item that is currently loaded.
812 QQuickItem *QQuickLoader::item() const
814 Q_D(const QQuickLoader);
818 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
821 if (newGeometry != oldGeometry) {
824 QQuickItem::geometryChanged(newGeometry, oldGeometry);
827 QUrl QQuickLoaderPrivate::resolveSourceUrl(QDeclarativeV8Function *args)
829 QV8Engine *v8engine = args->engine();
830 QString arg = v8engine->toString((*args)[0]->ToString());
834 QDeclarativeContextData *context = args->context();
836 return context->resolvedUrl(QUrl(arg));
839 v8::Handle<v8::Object> QQuickLoaderPrivate::extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error)
841 v8::Local<v8::Object> valuemap;
842 if (args->Length() >= 2) {
843 v8::Local<v8::Value> v = (*args)[1];
844 if (!v->IsObject() || v->IsArray()) {
846 qmlInfo(loader) << loader->tr("setSource: value is not an object");
849 valuemap = v8::Local<v8::Object>::Cast(v);
856 #include <moc_qquickloader_p.cpp>