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 static const QQuickItemPrivate::ChangeTypes watchedChanges
56 = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
58 QQuickLoaderPrivate::QQuickLoaderPrivate()
59 : item(0), object(0), component(0), itemContext(0), incubator(0), updatingSize(false),
60 active(true), loadingFromSource(false), asynchronous(false)
64 QQuickLoaderPrivate::~QQuickLoaderPrivate()
69 disposeInitialPropertyValues();
72 void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
74 if (resizeItem == item)
76 QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
79 void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *)
82 q->setImplicitWidth(getImplicitWidth());
85 void QQuickLoaderPrivate::itemImplicitHeightChanged(QQuickItem *)
88 q->setImplicitHeight(getImplicitHeight());
91 void QQuickLoaderPrivate::clear()
94 disposeInitialPropertyValues();
102 if (loadingFromSource && component) {
103 // disconnect since we deleteLater
104 QObject::disconnect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
105 q, SLOT(_q_sourceLoaded()));
106 QObject::disconnect(component, SIGNAL(progressChanged(qreal)),
107 q, SIGNAL(progressChanged()));
108 component->deleteLater();
114 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
115 p->removeItemChangeListener(this, watchedChanges);
117 // We can't delete immediately because our item may have triggered
118 // the Loader to load a different item.
119 item->setParentItem(0);
120 item->setVisible(false);
124 object->deleteLater();
129 void QQuickLoaderPrivate::initResize()
133 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
134 p->addItemChangeListener(this, watchedChanges);
138 qreal QQuickLoaderPrivate::getImplicitWidth() const
140 Q_Q(const QQuickLoader);
141 // If the Loader has a valid width then Loader has set an explicit width on the
142 // item, and we want the item's implicitWidth. If the Loader's width has
143 // not been set then its implicitWidth is the width of the item.
145 return q->widthValid() ? item->implicitWidth() : item->width();
146 return QQuickImplicitSizeItemPrivate::getImplicitWidth();
149 qreal QQuickLoaderPrivate::getImplicitHeight() const
151 Q_Q(const QQuickLoader);
152 // If the Loader has a valid height then Loader has set an explicit height on the
153 // item, and we want the item's implicitHeight. If the Loader's height has
154 // not been set then its implicitHeight is the height of the item.
156 return q->heightValid() ? item->implicitHeight() : item->height();
157 return QQuickImplicitSizeItemPrivate::getImplicitHeight();
162 \instantiates QQuickLoader
163 \inqmlmodule QtQuick 2
164 \ingroup qtquick-dynamic
167 \brief Allows dynamic loading of a subtree from a URL or Component
169 Loader is used to dynamically load QML components.
172 QML file (using the \l source property) or a \l Component object (using
173 the \l sourceComponent property). It is useful for delaying the creation
174 of a component until it is required: for example, when a component should
175 be created on demand, or when a component should not be created
176 unnecessarily for performance reasons.
178 Here is a Loader that loads "Page1.qml" as a component when the
179 \l MouseArea is clicked:
181 \snippet qml/loader/simple.qml 0
183 The loaded object can be accessed using the \l item property.
185 If the \l source or \l sourceComponent changes, any previously instantiated
186 items are destroyed. Setting \l source to an empty string or setting
187 \l sourceComponent to \c undefined destroys the currently loaded object,
188 freeing resources and leaving the Loader empty.
190 \section2 Loader sizing behavior
192 If the source component is not an Item type, Loader does not
193 apply any special sizing rules. When used to load visual types,
194 Loader applies the following sizing rules:
197 \li If an explicit size is not specified for the Loader, the Loader
198 is automatically resized to the size of the loaded item once the
200 \li If the size of the Loader is specified explicitly by setting
201 the width, height or by anchoring, the loaded item will be resized
202 to the size of the Loader.
205 In both scenarios the size of the item and the Loader are identical.
206 This ensures that anchoring to the Loader is equivalent to anchoring
214 \li \snippet qml/loader/sizeloader.qml 0
215 \li \snippet qml/loader/sizeitem.qml 0
217 \li The red rectangle will be sized to the size of the root item.
218 \li The red rectangle will be 50x50, centered in the root item.
222 \section2 Receiving signals from loaded objects
224 Any signals emitted from the loaded object can be received using the
225 \l Connections type. For example, the following \c application.qml
226 loads \c MyItem.qml, and is able to receive the \c message signal from
227 the loaded item through a \l Connections object:
234 \li \snippet qml/loader/connections.qml 0
235 \li \snippet qml/loader/MyItem.qml 0
238 Alternatively, since \c MyItem.qml is loaded within the scope of the
239 Loader, it could also directly call any function defined in the Loader or
243 \section2 Focus and key events
245 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
246 \c true for any of its children to get the \e {active focus}. (See
247 \l{Keyboard Focus in Qt Quick}
248 for more details.) Any key events received in the loaded item should likely
249 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
251 For example, the following \c application.qml loads \c KeyReader.qml when
252 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
253 set to \c true for the Loader as well as the \l Item in the dynamically
261 \li \snippet qml/loader/focus.qml 0
262 \li \snippet qml/loader/KeyReader.qml 0
265 Once \c KeyReader.qml is loaded, it accepts key events and sets
266 \c event.accepted to \c true so that the event is not propagated to the
269 Since QtQuick 2.0 Loader can also load non-visual components.
271 \section2 Using a Loader within a view delegate
273 In some cases you may wish to use a Loader within a view delegate to improve delegate
274 loading performance. This works well in most cases, but there is one important issue to
275 be aware of related to the \l{QtQuick2::Component#creation-context}{creation context} of a Component.
277 In the following example, the \c index context property inserted by the ListView into \c delegateComponent's
278 context will be inaccessible to Text, as the Loader will use the creation context of \c myComponent as the parent
279 context when instantiating it, and \c index does not refer to anything within that context chain.
281 \snippet qml/loader/creationContext1.qml 0
283 In this situation we can either move the component inline,
285 \snippet qml/loader/creationContext2.qml 0
287 into a separate file,
289 \snippet qml/loader/creationContext3.qml 0
291 or explicitly set the required information as a property of the Loader (this works because the
292 Loader sets itself as the context object for the component it is loading).
294 \snippet qml/loader/creationContext4.qml 0
296 \sa {dynamic-object-creation}{Dynamic Object Creation}
299 QQuickLoader::QQuickLoader(QQuickItem *parent)
300 : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
302 setFlag(ItemIsFocusScope);
305 QQuickLoader::~QQuickLoader()
309 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
310 p->removeItemChangeListener(d, watchedChanges);
315 \qmlproperty bool QtQuick2::Loader::active
316 This property is \c true if the Loader is currently active.
317 The default value for this property is \c true.
319 If the Loader is inactive, changing the \l source or \l sourceComponent
320 will not cause the item to be instantiated until the Loader is made active.
322 Setting the value to inactive will cause any \l item loaded by the loader
323 to be released, but will not affect the \l source or \l sourceComponent.
325 The \l status of an inactive loader is always \c Null.
327 \sa source, sourceComponent
329 bool QQuickLoader::active() const
331 Q_D(const QQuickLoader);
335 void QQuickLoader::setActive(bool newVal)
338 if (d->active != newVal) {
340 if (newVal == true) {
341 if (d->loadingFromSource) {
344 loadFromSourceComponent();
347 // cancel any current incubation
349 d->incubator->clear();
350 delete d->itemContext;
355 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
356 p->removeItemChangeListener(d, watchedChanges);
358 // We can't delete immediately because our item may have triggered
359 // the Loader to load a different item.
360 d->item->setParentItem(0);
361 d->item->setVisible(false);
365 d->object->deleteLater();
369 emit statusChanged();
371 emit activeChanged();
377 \qmlproperty url QtQuick2::Loader::source
378 This property holds the URL of the QML component to instantiate.
380 Since QtQuick 2.0 Loader is able to load any type of object; it
381 is not restricted to Item types.
383 To unload the currently loaded object, set this property to an empty string,
384 or set \l sourceComponent to \c undefined. Setting \c source to a
385 new URL will also cause the item created by the previous URL to be unloaded.
387 \sa sourceComponent, status, progress
389 QUrl QQuickLoader::source() const
391 Q_D(const QQuickLoader);
395 void QQuickLoader::setSource(const QUrl &url)
397 setSource(url, true); // clear previous values
400 void QQuickLoader::setSource(const QUrl &url, bool needsClear)
403 if (d->source == url)
410 d->loadingFromSource = true;
415 emit sourceChanged();
418 void QQuickLoader::loadFromSource()
421 if (d->source.isEmpty()) {
422 emit sourceChanged();
423 emit statusChanged();
424 emit progressChanged();
429 if (isComponentComplete()) {
430 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
431 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
437 \qmlproperty Component QtQuick2::Loader::sourceComponent
438 This property holds the \l{Component} to instantiate.
444 Rectangle { color: "red"; width: 10; height: 10 }
447 Loader { sourceComponent: redSquare }
448 Loader { sourceComponent: redSquare; x: 10 }
452 To unload the currently loaded object, set this property to an empty string
455 Since QtQuick 2.0 Loader is able to load any type of object; it
456 is not restricted to Item types.
461 QQmlComponent *QQuickLoader::sourceComponent() const
463 Q_D(const QQuickLoader);
467 void QQuickLoader::setSourceComponent(QQmlComponent *comp)
470 if (comp == d->component)
476 d->loadingFromSource = false;
479 loadFromSourceComponent();
481 emit sourceComponentChanged();
484 void QQuickLoader::resetSourceComponent()
486 setSourceComponent(0);
489 void QQuickLoader::loadFromSourceComponent()
493 emit sourceComponentChanged();
494 emit statusChanged();
495 emit progressChanged();
500 if (isComponentComplete())
505 \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
507 Creates an object instance of the given \a source component that will have
508 the given \a properties. The \a properties argument is optional. The instance
509 will be accessible via the \l item property once loading and instantiation
512 If the \l active property is \c false at the time when this function is called,
513 the given \a source component will not be loaded but the \a source and initial
514 \a properties will be cached. When the loader is made \l active, an instance of
515 the \a source component will be created with the initial \a properties set.
517 Setting the initial property values of an instance of a component in this manner
518 will \b{not} trigger any associated \l{Behavior}s.
520 Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
521 is changed after calling this function but prior to setting the loader \l active.
528 // ExampleComponent.qml
540 to: (rect.width + 20)
553 onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
556 Component.onCompleted: {
557 squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
558 // will trigger the onLoaded code when complete.
566 void QQuickLoader::setSource(QQmlV8Function *args)
571 bool ipvError = false;
572 args->returnValue(v8::Undefined());
573 v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
578 QUrl sourceUrl = d->resolveSourceUrl(args);
579 if (!ipv.IsEmpty()) {
580 d->disposeInitialPropertyValues();
581 d->initialPropertyValues = qPersistentNew(ipv);
582 d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
585 setSource(sourceUrl, false); // already cleared and set ipv above.
588 void QQuickLoaderPrivate::disposeInitialPropertyValues()
590 if (!initialPropertyValues.IsEmpty())
591 qPersistentDispose(initialPropertyValues);
592 if (!qmlGlobalForIpv.IsEmpty())
593 qPersistentDispose(qmlGlobalForIpv);
596 void QQuickLoaderPrivate::load()
600 if (!q->isComponentComplete() || !component)
603 if (!component->isLoading()) {
606 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
607 q, SLOT(_q_sourceLoaded()));
608 QObject::connect(component, SIGNAL(progressChanged(qreal)),
609 q, SIGNAL(progressChanged()));
610 emit q->statusChanged();
611 emit q->progressChanged();
612 if (loadingFromSource)
613 emit q->sourceChanged();
615 emit q->sourceComponentChanged();
616 emit q->itemChanged();
620 void QQuickLoaderIncubator::setInitialState(QObject *o)
622 loader->setInitialState(o);
625 void QQuickLoaderPrivate::setInitialState(QObject *obj)
629 QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
631 // If the item doesn't have an explicit size, but the Loader
632 // does, then set the item's size now before bindings are
633 // evaluated, otherwise we will end up resizing the item
634 // later and triggering any affected bindings/anchors.
635 if (widthValid && !QQuickItemPrivate::get(item)->widthValid)
636 item->setWidth(q->width());
637 if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
638 item->setHeight(q->height());
639 item->setParentItem(q);
642 QQml_setParent_noEvent(itemContext, obj);
643 QQml_setParent_noEvent(obj, q);
647 if (initialPropertyValues.IsEmpty())
650 QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
651 Q_ASSERT(d && d->engine);
652 d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj);
655 void QQuickLoaderIncubator::statusChanged(Status status)
657 loader->incubatorStateChanged(status);
660 void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
663 if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
666 if (status == QQmlIncubator::Ready) {
667 object = incubator->object();
668 item = qmlobject_cast<QQuickItem*>(object);
669 emit q->itemChanged();
672 } else if (status == QQmlIncubator::Error) {
673 if (!incubator->errors().isEmpty())
674 QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
677 delete incubator->object();
679 emit q->itemChanged();
681 if (loadingFromSource)
682 emit q->sourceChanged();
684 emit q->sourceComponentChanged();
685 emit q->statusChanged();
686 emit q->progressChanged();
687 if (status == QQmlIncubator::Ready)
689 disposeInitialPropertyValues(); // cleanup
692 void QQuickLoaderPrivate::_q_sourceLoaded()
695 if (!component || !component->errors().isEmpty()) {
697 QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
698 if (loadingFromSource)
699 emit q->sourceChanged();
701 emit q->sourceComponentChanged();
702 emit q->statusChanged();
703 emit q->progressChanged();
704 emit q->itemChanged(); //Like clearing source, emit itemChanged even if previous item was also null
705 disposeInitialPropertyValues(); // cleanup
709 QQmlContext *creationContext = component->creationContext();
710 if (!creationContext) creationContext = qmlContext(q);
711 itemContext = new QQmlContext(creationContext);
712 itemContext->setContextObject(q);
715 incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
717 component->create(*incubator, itemContext);
719 if (incubator && incubator->status() == QQmlIncubator::Loading)
720 emit q->statusChanged();
724 \qmlproperty enumeration QtQuick2::Loader::status
726 This property holds the status of QML loading. It can be one of:
728 \li Loader.Null - the loader is inactive or no QML source has been set
729 \li Loader.Ready - the QML source has been loaded
730 \li Loader.Loading - the QML source is currently being loaded
731 \li Loader.Error - an error occurred while loading the QML source
734 Use this status to provide an update or respond to the status change in some way.
735 For example, you could:
738 \li Trigger a state change:
740 State { name: 'loaded'; when: loader.status == Loader.Ready }
743 \li Implement an \c onStatusChanged signal handler:
747 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
751 \li Bind to the status value:
753 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
757 Note that if the source is a local file, the status will initially be Ready (or Error). While
758 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
763 QQuickLoader::Status QQuickLoader::status() const
765 Q_D(const QQuickLoader);
771 switch (d->component->status()) {
772 case QQmlComponent::Loading:
774 case QQmlComponent::Error:
776 case QQmlComponent::Null:
784 switch (d->incubator->status()) {
785 case QQmlIncubator::Loading:
787 case QQmlIncubator::Error:
797 return d->source.isEmpty() ? Null : Error;
800 void QQuickLoader::componentComplete()
803 QQuickItem::componentComplete();
805 if (d->loadingFromSource) {
806 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
807 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
814 \qmlsignal QtQuick2::Loader::onLoaded()
816 This handler is called when the \l status becomes \c Loader.Ready, or on successful
822 \qmlproperty real QtQuick2::Loader::progress
824 This property holds the progress of loading QML data from the network, from
825 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
826 this value will rapidly change from 0 to 1.
830 qreal QQuickLoader::progress() const
832 Q_D(const QQuickLoader);
838 return d->component->progress();
844 \qmlproperty bool QtQuick2::Loader::asynchronous
846 This property holds whether the component will be instantiated asynchronously.
848 When used in conjunction with the \l source property, loading and compilation
849 will also be performed in a background thread.
851 Loading asynchronously creates the objects declared by the component
852 across multiple frames, and reduces the
853 likelihood of glitches in animation. When loading asynchronously the status
854 will change to Loader.Loading. Once the entire component has been created, the
855 \l item will be available and the status will change to Loader.Ready.
857 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
861 source: "mycomponent.qml"
863 visible: status == Loader.Ready
867 Note that this property affects object instantiation only; it is unrelated to
868 loading a component asynchronously via a network.
870 bool QQuickLoader::asynchronous() const
872 Q_D(const QQuickLoader);
873 return d->asynchronous;
876 void QQuickLoader::setAsynchronous(bool a)
879 if (d->asynchronous == a)
883 emit asynchronousChanged();
886 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
889 if (!item || updatingSize)
894 if (loaderGeometryChanged && q->widthValid())
895 item->setWidth(q->width());
896 if (loaderGeometryChanged && q->heightValid())
897 item->setHeight(q->height());
899 q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
901 updatingSize = false;
905 \qmlproperty object QtQuick2::Loader::item
906 This property holds the top-level object that is currently loaded.
908 Since QtQuick 2.0 Loader can load any object type.
910 QObject *QQuickLoader::item() const
912 Q_D(const QQuickLoader);
916 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
919 if (newGeometry != oldGeometry) {
922 QQuickItem::geometryChanged(newGeometry, oldGeometry);
925 QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV8Function *args)
927 QV8Engine *v8engine = args->engine();
928 QString arg = v8engine->toString((*args)[0]->ToString());
932 QQmlContextData *context = args->context();
934 return context->resolvedUrl(QUrl(arg));
937 v8::Handle<v8::Object> QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error)
939 v8::Local<v8::Object> valuemap;
940 if (args->Length() >= 2) {
941 v8::Local<v8::Value> v = (*args)[1];
942 if (!v->IsObject() || v->IsArray()) {
944 qmlInfo(loader) << loader->tr("setSource: value is not an object");
947 valuemap = v8::Local<v8::Object>::Cast(v);
954 #include <moc_qquickloader_p.cpp>