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 QtQml 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 <QtQml/qqmlinfo.h>
46 #include <private/qqmlengine_p.h>
47 #include <private/qqmlglobal_p.h>
49 #include <private/qqmlcomponent_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()
67 disposeInitialPropertyValues();
70 void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
72 if (resizeItem == item) {
73 if (!updatingSize && newGeometry.width() != oldGeometry.width())
74 itemWidthValid = true;
75 if (!updatingSize && newGeometry.height() != oldGeometry.height())
76 itemHeightValid = true;
79 QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
82 void QQuickLoaderPrivate::clear()
85 disposeInitialPropertyValues();
93 if (loadingFromSource && component) {
94 // disconnect since we deleteLater
95 QObject::disconnect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
96 q, SLOT(_q_sourceLoaded()));
97 QObject::disconnect(component, SIGNAL(progressChanged(qreal)),
98 q, SIGNAL(progressChanged()));
99 component->deleteLater();
105 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
106 p->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
108 // We can't delete immediately because our item may have triggered
109 // the Loader to load a different item.
110 item->setParentItem(0);
111 item->setVisible(false);
117 void QQuickLoaderPrivate::initResize()
119 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
120 p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
121 // We may override the item's size, so we need to remember
122 // whether the item provided its own valid size.
123 itemWidthValid = p->widthValid;
124 itemHeightValid = p->heightValid;
129 \qmlclass Loader QQuickLoader
130 \inqmlmodule QtQuick 2
131 \ingroup qml-utility-elements
134 \brief The Loader item allows dynamically loading an Item-based
135 subtree from a URL or Component.
137 Loader is used to dynamically load visual QML components. It can load a
138 QML file (using the \l source property) or a \l Component object (using
139 the \l sourceComponent property). It is useful for delaying the creation
140 of a component until it is required: for example, when a component should
141 be created on demand, or when a component should not be created
142 unnecessarily for performance reasons.
144 Here is a Loader that loads "Page1.qml" as a component when the
145 \l MouseArea is clicked:
147 \snippet doc/src/snippets/qml/loader/simple.qml 0
149 The loaded item can be accessed using the \l item property.
151 If the \l source or \l sourceComponent changes, any previously instantiated
152 items are destroyed. Setting \l source to an empty string or setting
153 \l sourceComponent to \c undefined destroys the currently loaded item,
154 freeing resources and leaving the Loader empty.
156 \section2 Loader sizing behavior
158 Loader is like any other visual item and must be positioned and sized
159 accordingly to become visible.
162 \li If an explicit size is not specified for the Loader, the Loader
163 is automatically resized to the size of the loaded item once the
165 \li If the size of the Loader is specified explicitly by setting
166 the width, height or by anchoring, the loaded item will be resized
167 to the size of the Loader.
170 In both scenarios the size of the item and the Loader are identical.
171 This ensures that anchoring to the Loader is equivalent to anchoring
179 \li \snippet doc/src/snippets/qml/loader/sizeloader.qml 0
180 \li \snippet doc/src/snippets/qml/loader/sizeitem.qml 0
182 \li The red rectangle will be sized to the size of the root item.
183 \li The red rectangle will be 50x50, centered in the root item.
187 \section2 Receiving signals from loaded items
189 Any signals emitted from the loaded item can be received using the
190 \l Connections element. For example, the following \c application.qml
191 loads \c MyItem.qml, and is able to receive the \c message signal from
192 the loaded item through a \l Connections object:
199 \li \snippet doc/src/snippets/qml/loader/connections.qml 0
200 \li \snippet doc/src/snippets/qml/loader/MyItem.qml 0
203 Alternatively, since \c MyItem.qml is loaded within the scope of the
204 Loader, it could also directly call any function defined in the Loader or
208 \section2 Focus and key events
210 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
211 \c true for any of its children to get the \e {active focus}. (See
212 \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
213 for more details.) Any key events received in the loaded item should likely
214 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
216 For example, the following \c application.qml loads \c KeyReader.qml when
217 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
218 set to \c true for the Loader as well as the \l Item in the dynamically
226 \li \snippet doc/src/snippets/qml/loader/focus.qml 0
227 \li \snippet doc/src/snippets/qml/loader/KeyReader.qml 0
230 Once \c KeyReader.qml is loaded, it accepts key events and sets
231 \c event.accepted to \c true so that the event is not propagated to the
234 \sa {dynamic-object-creation}{Dynamic Object Creation}
237 QQuickLoader::QQuickLoader(QQuickItem *parent)
238 : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
240 setFlag(ItemIsFocusScope);
243 QQuickLoader::~QQuickLoader()
247 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
248 p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
253 \qmlproperty bool QtQuick2::Loader::active
254 This property is \c true if the Loader is currently active.
255 The default value for the \l active property is \c true.
257 If the Loader is inactive, changing the \l source or \l sourceComponent
258 will not cause the item to be instantiated until the Loader is made active.
260 Setting the value to inactive will cause any \l item loaded by the loader
261 to be released, but will not affect the \l source or \l sourceComponent.
263 The \l status of an inactive loader is always \c Null.
265 \sa source, sourceComponent
267 bool QQuickLoader::active() const
269 Q_D(const QQuickLoader);
273 void QQuickLoader::setActive(bool newVal)
276 if (d->active != newVal) {
278 if (newVal == true) {
279 if (d->loadingFromSource) {
282 loadFromSourceComponent();
286 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
287 p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
289 // We can't delete immediately because our item may have triggered
290 // the Loader to load a different item.
291 d->item->setParentItem(0);
292 d->item->setVisible(false);
293 d->item->deleteLater();
297 emit statusChanged();
299 emit activeChanged();
305 \qmlproperty url QtQuick2::Loader::source
306 This property holds the URL of the QML component to instantiate.
308 Note the QML component must be an \l{Item}-based component. The loader
309 cannot load non-visual components.
311 To unload the currently loaded item, set this property to an empty string,
312 or set \l sourceComponent to \c undefined. Setting \c source to a
313 new URL will also cause the item created by the previous URL to be unloaded.
315 \sa sourceComponent, status, progress
317 QUrl QQuickLoader::source() const
319 Q_D(const QQuickLoader);
323 void QQuickLoader::setSource(const QUrl &url)
325 setSource(url, true); // clear previous values
328 void QQuickLoader::setSource(const QUrl &url, bool needsClear)
331 if (d->source == url)
338 d->loadingFromSource = true;
343 emit sourceChanged();
346 void QQuickLoader::loadFromSource()
349 if (d->source.isEmpty()) {
350 emit sourceChanged();
351 emit statusChanged();
352 emit progressChanged();
357 if (isComponentComplete()) {
358 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
359 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
365 \qmlproperty Component QtQuick2::Loader::sourceComponent
366 This property holds the \l{Component} to instantiate.
372 Rectangle { color: "red"; width: 10; height: 10 }
375 Loader { sourceComponent: redSquare }
376 Loader { sourceComponent: redSquare; x: 10 }
380 To unload the currently loaded item, set this property to an empty string
386 QQmlComponent *QQuickLoader::sourceComponent() const
388 Q_D(const QQuickLoader);
392 void QQuickLoader::setSourceComponent(QQmlComponent *comp)
395 if (comp == d->component)
401 d->loadingFromSource = false;
404 loadFromSourceComponent();
406 emit sourceComponentChanged();
409 void QQuickLoader::resetSourceComponent()
411 setSourceComponent(0);
414 void QQuickLoader::loadFromSourceComponent()
418 emit sourceComponentChanged();
419 emit statusChanged();
420 emit progressChanged();
425 if (isComponentComplete())
430 \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
432 Creates an object instance of the given \a source component that will have
433 the given \a properties. The \a properties argument is optional. The instance
434 will be accessible via the \l item property once loading and instantiation
437 If the \l active property is \c false at the time when this function is called,
438 the given \a source component will not be loaded but the \a source and initial
439 \a properties will be cached. When the loader is made \l active, an instance of
440 the \a source component will be created with the initial \a properties set.
442 Setting the initial property values of an instance of a component in this manner
443 will \b{not} trigger any associated \l{Behavior}s.
445 Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
446 is changed after calling this function but prior to setting the loader \l active.
453 // ExampleComponent.qml
465 to: (rect.width + 20)
478 onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
481 Component.onCompleted: {
482 squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
483 // will trigger the onLoaded code when complete.
491 void QQuickLoader::setSource(QQmlV8Function *args)
496 bool ipvError = false;
497 args->returnValue(v8::Undefined());
498 v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
503 QUrl sourceUrl = d->resolveSourceUrl(args);
504 if (!ipv.IsEmpty()) {
505 d->disposeInitialPropertyValues();
506 d->initialPropertyValues = qPersistentNew(ipv);
507 d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
510 setSource(sourceUrl, false); // already cleared and set ipv above.
513 void QQuickLoaderPrivate::disposeInitialPropertyValues()
515 if (!initialPropertyValues.IsEmpty())
516 qPersistentDispose(initialPropertyValues);
517 if (!qmlGlobalForIpv.IsEmpty())
518 qPersistentDispose(qmlGlobalForIpv);
521 void QQuickLoaderPrivate::load()
525 if (!q->isComponentComplete() || !component)
528 if (!component->isLoading()) {
531 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
532 q, SLOT(_q_sourceLoaded()));
533 QObject::connect(component, SIGNAL(progressChanged(qreal)),
534 q, SIGNAL(progressChanged()));
535 emit q->statusChanged();
536 emit q->progressChanged();
537 if (loadingFromSource)
538 emit q->sourceChanged();
540 emit q->sourceComponentChanged();
541 emit q->itemChanged();
545 void QQuickLoaderIncubator::setInitialState(QObject *o)
547 loader->setInitialState(o);
550 void QQuickLoaderPrivate::setInitialState(QObject *obj)
554 QQuickItem *item = qobject_cast<QQuickItem*>(obj);
556 QQml_setParent_noEvent(itemContext, obj);
557 QQml_setParent_noEvent(item, q);
558 item->setParentItem(q);
562 if (initialPropertyValues.IsEmpty())
565 QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
566 Q_ASSERT(d && d->engine);
567 d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj);
570 void QQuickLoaderIncubator::statusChanged(Status status)
572 loader->incubatorStateChanged(status);
575 void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
578 if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
581 if (status == QQmlIncubator::Ready) {
582 QObject *obj = incubator->object();
583 item = qobject_cast<QQuickItem*>(obj);
585 emit q->itemChanged();
588 qmlInfo(q) << QQuickLoader::tr("Loader does not support loading non-visual elements.");
592 emit q->itemChanged();
595 } else if (status == QQmlIncubator::Error) {
596 if (!incubator->errors().isEmpty())
597 QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
600 delete incubator->object();
602 emit q->itemChanged();
604 if (loadingFromSource)
605 emit q->sourceChanged();
607 emit q->sourceComponentChanged();
608 emit q->statusChanged();
609 emit q->progressChanged();
611 disposeInitialPropertyValues(); // cleanup
614 void QQuickLoaderPrivate::_q_sourceLoaded()
617 if (!component || !component->errors().isEmpty()) {
619 QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
620 if (loadingFromSource)
621 emit q->sourceChanged();
623 emit q->sourceComponentChanged();
624 emit q->statusChanged();
625 emit q->progressChanged();
626 disposeInitialPropertyValues(); // cleanup
630 QQmlContext *creationContext = component->creationContext();
631 if (!creationContext) creationContext = qmlContext(q);
632 itemContext = new QQmlContext(creationContext);
633 itemContext->setContextObject(q);
636 incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
638 component->create(*incubator, itemContext);
640 if (incubator && incubator->status() == QQmlIncubator::Loading)
641 emit q->statusChanged();
645 \qmlproperty enumeration QtQuick2::Loader::status
647 This property holds the status of QML loading. It can be one of:
649 \li Loader.Null - the loader is inactive or no QML source has been set
650 \li Loader.Ready - the QML source has been loaded
651 \li Loader.Loading - the QML source is currently being loaded
652 \li Loader.Error - an error occurred while loading the QML source
655 Use this status to provide an update or respond to the status change in some way.
656 For example, you could:
659 \li Trigger a state change:
661 State { name: 'loaded'; when: loader.status == Loader.Ready }
664 \li Implement an \c onStatusChanged signal handler:
668 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
672 \li Bind to the status value:
674 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
678 Note that if the source is a local file, the status will initially be Ready (or Error). While
679 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
684 QQuickLoader::Status QQuickLoader::status() const
686 Q_D(const QQuickLoader);
692 switch (d->component->status()) {
693 case QQmlComponent::Loading:
695 case QQmlComponent::Error:
697 case QQmlComponent::Null:
705 switch (d->incubator->status()) {
706 case QQmlIncubator::Loading:
708 case QQmlIncubator::Error:
718 return d->source.isEmpty() ? Null : Error;
721 void QQuickLoader::componentComplete()
724 QQuickItem::componentComplete();
726 if (d->loadingFromSource) {
727 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
728 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
735 \qmlsignal QtQuick2::Loader::onLoaded()
737 This handler is called when the \l status becomes \c Loader.Ready, or on successful
743 \qmlproperty real QtQuick2::Loader::progress
745 This property holds the progress of loading QML data from the network, from
746 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
747 this value will rapidly change from 0 to 1.
751 qreal QQuickLoader::progress() const
753 Q_D(const QQuickLoader);
759 return d->component->progress();
765 \qmlproperty bool QtQuick2::Loader::asynchronous
767 This property holds whether the component will be instantiated asynchronously.
769 When used in conjunction with the \l source property, loading and compilation
770 will also be performed in a background thread.
772 Loading asynchronously creates the objects declared by the component
773 across multiple frames, and reduces the
774 likelihood of glitches in animation. When loading asynchronously the status
775 will change to Loader.Loading. Once the entire component has been created, the
776 \l item will be available and the status will change to Loader.Ready.
778 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
782 source: "mycomponent.qml"
784 visible: status == Loader.Ready
788 Note that this property affects object instantiation only; it is unrelated to
789 loading a component asynchronously via a network.
791 bool QQuickLoader::asynchronous() const
793 Q_D(const QQuickLoader);
794 return d->asynchronous;
797 void QQuickLoader::setAsynchronous(bool a)
800 if (d->asynchronous == a)
804 emit asynchronousChanged();
807 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
810 if (!item || updatingSize)
815 qreal iWidth = !itemWidthValid ? item->implicitWidth() : item->width();
816 qreal iHeight = !itemHeightValid ? item->implicitHeight() : item->height();
817 q->setImplicitSize(iWidth, iHeight);
819 if (loaderGeometryChanged && q->widthValid())
820 item->setWidth(q->width());
821 if (loaderGeometryChanged && q->heightValid())
822 item->setHeight(q->height());
824 updatingSize = false;
828 \qmlproperty Item QtQuick2::Loader::item
829 This property holds the top-level item that is currently loaded.
831 QQuickItem *QQuickLoader::item() const
833 Q_D(const QQuickLoader);
837 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
840 if (newGeometry != oldGeometry) {
843 QQuickItem::geometryChanged(newGeometry, oldGeometry);
846 QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV8Function *args)
848 QV8Engine *v8engine = args->engine();
849 QString arg = v8engine->toString((*args)[0]->ToString());
853 QQmlContextData *context = args->context();
855 return context->resolvedUrl(QUrl(arg));
858 v8::Handle<v8::Object> QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error)
860 v8::Local<v8::Object> valuemap;
861 if (args->Length() >= 2) {
862 v8::Local<v8::Value> v = (*args)[1];
863 if (!v->IsObject() || v->IsArray()) {
865 qmlInfo(loader) << loader->tr("setSource: value is not an object");
868 valuemap = v8::Local<v8::Object>::Cast(v);
875 #include <moc_qquickloader_p.cpp>