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();
161 \qmlclass Loader QQuickLoader
162 \inqmlmodule QtQuick 2
163 \ingroup qtquick-utility
166 \brief Allows dynamic loading of a subtree from a URL or Component
168 Loader is used to dynamically load QML components.
171 QML file (using the \l source property) or a \l Component object (using
172 the \l sourceComponent property). It is useful for delaying the creation
173 of a component until it is required: for example, when a component should
174 be created on demand, or when a component should not be created
175 unnecessarily for performance reasons.
177 Here is a Loader that loads "Page1.qml" as a component when the
178 \l MouseArea is clicked:
180 \snippet qml/loader/simple.qml 0
182 The loaded object can be accessed using the \l item property.
184 If the \l source or \l sourceComponent changes, any previously instantiated
185 items are destroyed. Setting \l source to an empty string or setting
186 \l sourceComponent to \c undefined destroys the currently loaded object,
187 freeing resources and leaving the Loader empty.
189 \section2 Loader sizing behavior
191 If the source component is not an Item type, Loader does not
192 apply any special sizing rules. When used to load visual types,
193 Loader applies the following sizing rules:
196 \li If an explicit size is not specified for the Loader, the Loader
197 is automatically resized to the size of the loaded item once the
199 \li If the size of the Loader is specified explicitly by setting
200 the width, height or by anchoring, the loaded item will be resized
201 to the size of the Loader.
204 In both scenarios the size of the item and the Loader are identical.
205 This ensures that anchoring to the Loader is equivalent to anchoring
213 \li \snippet qml/loader/sizeloader.qml 0
214 \li \snippet qml/loader/sizeitem.qml 0
216 \li The red rectangle will be sized to the size of the root item.
217 \li The red rectangle will be 50x50, centered in the root item.
221 \section2 Receiving signals from loaded objects
223 Any signals emitted from the loaded object can be received using the
224 \l Connections element. For example, the following \c application.qml
225 loads \c MyItem.qml, and is able to receive the \c message signal from
226 the loaded item through a \l Connections object:
233 \li \snippet qml/loader/connections.qml 0
234 \li \snippet qml/loader/MyItem.qml 0
237 Alternatively, since \c MyItem.qml is loaded within the scope of the
238 Loader, it could also directly call any function defined in the Loader or
242 \section2 Focus and key events
244 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
245 \c true for any of its children to get the \e {active focus}. (See
246 \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
247 for more details.) Any key events received in the loaded item should likely
248 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
250 For example, the following \c application.qml loads \c KeyReader.qml when
251 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
252 set to \c true for the Loader as well as the \l Item in the dynamically
260 \li \snippet qml/loader/focus.qml 0
261 \li \snippet qml/loader/KeyReader.qml 0
264 Once \c KeyReader.qml is loaded, it accepts key events and sets
265 \c event.accepted to \c true so that the event is not propagated to the
268 Since QtQuick 2.0 Loader can also load non-visual components.
270 \sa {dynamic-object-creation}{Dynamic Object Creation}
273 QQuickLoader::QQuickLoader(QQuickItem *parent)
274 : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
276 setFlag(ItemIsFocusScope);
279 QQuickLoader::~QQuickLoader()
283 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
284 p->removeItemChangeListener(d, watchedChanges);
289 \qmlproperty bool QtQuick2::Loader::active
290 This property is \c true if the Loader is currently active.
291 The default value for the \l active property is \c true.
293 If the Loader is inactive, changing the \l source or \l sourceComponent
294 will not cause the item to be instantiated until the Loader is made active.
296 Setting the value to inactive will cause any \l item loaded by the loader
297 to be released, but will not affect the \l source or \l sourceComponent.
299 The \l status of an inactive loader is always \c Null.
301 \sa source, sourceComponent
303 bool QQuickLoader::active() const
305 Q_D(const QQuickLoader);
309 void QQuickLoader::setActive(bool newVal)
312 if (d->active != newVal) {
314 if (newVal == true) {
315 if (d->loadingFromSource) {
318 loadFromSourceComponent();
321 // cancel any current incubation
323 d->incubator->clear();
324 delete d->itemContext;
329 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
330 p->removeItemChangeListener(d, watchedChanges);
332 // We can't delete immediately because our item may have triggered
333 // the Loader to load a different item.
334 d->item->setParentItem(0);
335 d->item->setVisible(false);
339 d->object->deleteLater();
343 emit statusChanged();
345 emit activeChanged();
351 \qmlproperty url QtQuick2::Loader::source
352 This property holds the URL of the QML component to instantiate.
354 Since QtQuick 2.0 Loader is able to load any type of object; it
355 is not restricted to Item types.
357 To unload the currently loaded object, set this property to an empty string,
358 or set \l sourceComponent to \c undefined. Setting \c source to a
359 new URL will also cause the item created by the previous URL to be unloaded.
361 \sa sourceComponent, status, progress
363 QUrl QQuickLoader::source() const
365 Q_D(const QQuickLoader);
369 void QQuickLoader::setSource(const QUrl &url)
371 setSource(url, true); // clear previous values
374 void QQuickLoader::setSource(const QUrl &url, bool needsClear)
377 if (d->source == url)
384 d->loadingFromSource = true;
389 emit sourceChanged();
392 void QQuickLoader::loadFromSource()
395 if (d->source.isEmpty()) {
396 emit sourceChanged();
397 emit statusChanged();
398 emit progressChanged();
403 if (isComponentComplete()) {
404 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
405 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
411 \qmlproperty Component QtQuick2::Loader::sourceComponent
412 This property holds the \l{Component} to instantiate.
418 Rectangle { color: "red"; width: 10; height: 10 }
421 Loader { sourceComponent: redSquare }
422 Loader { sourceComponent: redSquare; x: 10 }
426 To unload the currently loaded object, set this property to an empty string
429 Since QtQuick 2.0 Loader is able to load any type of object; it
430 is not restricted to Item types.
435 QQmlComponent *QQuickLoader::sourceComponent() const
437 Q_D(const QQuickLoader);
441 void QQuickLoader::setSourceComponent(QQmlComponent *comp)
444 if (comp == d->component)
450 d->loadingFromSource = false;
453 loadFromSourceComponent();
455 emit sourceComponentChanged();
458 void QQuickLoader::resetSourceComponent()
460 setSourceComponent(0);
463 void QQuickLoader::loadFromSourceComponent()
467 emit sourceComponentChanged();
468 emit statusChanged();
469 emit progressChanged();
474 if (isComponentComplete())
479 \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
481 Creates an object instance of the given \a source component that will have
482 the given \a properties. The \a properties argument is optional. The instance
483 will be accessible via the \l item property once loading and instantiation
486 If the \l active property is \c false at the time when this function is called,
487 the given \a source component will not be loaded but the \a source and initial
488 \a properties will be cached. When the loader is made \l active, an instance of
489 the \a source component will be created with the initial \a properties set.
491 Setting the initial property values of an instance of a component in this manner
492 will \b{not} trigger any associated \l{Behavior}s.
494 Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
495 is changed after calling this function but prior to setting the loader \l active.
502 // ExampleComponent.qml
514 to: (rect.width + 20)
527 onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
530 Component.onCompleted: {
531 squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
532 // will trigger the onLoaded code when complete.
540 void QQuickLoader::setSource(QQmlV8Function *args)
545 bool ipvError = false;
546 args->returnValue(v8::Undefined());
547 v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
552 QUrl sourceUrl = d->resolveSourceUrl(args);
553 if (!ipv.IsEmpty()) {
554 d->disposeInitialPropertyValues();
555 d->initialPropertyValues = qPersistentNew(ipv);
556 d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
559 setSource(sourceUrl, false); // already cleared and set ipv above.
562 void QQuickLoaderPrivate::disposeInitialPropertyValues()
564 if (!initialPropertyValues.IsEmpty())
565 qPersistentDispose(initialPropertyValues);
566 if (!qmlGlobalForIpv.IsEmpty())
567 qPersistentDispose(qmlGlobalForIpv);
570 void QQuickLoaderPrivate::load()
574 if (!q->isComponentComplete() || !component)
577 if (!component->isLoading()) {
580 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
581 q, SLOT(_q_sourceLoaded()));
582 QObject::connect(component, SIGNAL(progressChanged(qreal)),
583 q, SIGNAL(progressChanged()));
584 emit q->statusChanged();
585 emit q->progressChanged();
586 if (loadingFromSource)
587 emit q->sourceChanged();
589 emit q->sourceComponentChanged();
590 emit q->itemChanged();
594 void QQuickLoaderIncubator::setInitialState(QObject *o)
596 loader->setInitialState(o);
599 void QQuickLoaderPrivate::setInitialState(QObject *obj)
603 QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
605 // If the item doesn't have an explicit size, but the Loader
606 // does, then set the item's size now before bindings are
607 // evaluated, otherwise we will end up resizing the item
608 // later and triggering any affected bindings/anchors.
609 if (widthValid && !QQuickItemPrivate::get(item)->widthValid)
610 item->setWidth(q->width());
611 if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
612 item->setHeight(q->height());
613 item->setParentItem(q);
616 QQml_setParent_noEvent(itemContext, obj);
617 QQml_setParent_noEvent(obj, q);
621 if (initialPropertyValues.IsEmpty())
624 QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
625 Q_ASSERT(d && d->engine);
626 d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj);
629 void QQuickLoaderIncubator::statusChanged(Status status)
631 loader->incubatorStateChanged(status);
634 void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
637 if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
640 if (status == QQmlIncubator::Ready) {
641 object = incubator->object();
642 item = qmlobject_cast<QQuickItem*>(object);
643 emit q->itemChanged();
646 } else if (status == QQmlIncubator::Error) {
647 if (!incubator->errors().isEmpty())
648 QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
651 delete incubator->object();
653 emit q->itemChanged();
655 if (loadingFromSource)
656 emit q->sourceChanged();
658 emit q->sourceComponentChanged();
659 emit q->statusChanged();
660 emit q->progressChanged();
661 if (status == QQmlIncubator::Ready)
663 disposeInitialPropertyValues(); // cleanup
666 void QQuickLoaderPrivate::_q_sourceLoaded()
669 if (!component || !component->errors().isEmpty()) {
671 QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
672 if (loadingFromSource)
673 emit q->sourceChanged();
675 emit q->sourceComponentChanged();
676 emit q->statusChanged();
677 emit q->progressChanged();
678 disposeInitialPropertyValues(); // cleanup
682 QQmlContext *creationContext = component->creationContext();
683 if (!creationContext) creationContext = qmlContext(q);
684 itemContext = new QQmlContext(creationContext);
685 itemContext->setContextObject(q);
688 incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
690 component->create(*incubator, itemContext);
692 if (incubator && incubator->status() == QQmlIncubator::Loading)
693 emit q->statusChanged();
697 \qmlproperty enumeration QtQuick2::Loader::status
699 This property holds the status of QML loading. It can be one of:
701 \li Loader.Null - the loader is inactive or no QML source has been set
702 \li Loader.Ready - the QML source has been loaded
703 \li Loader.Loading - the QML source is currently being loaded
704 \li Loader.Error - an error occurred while loading the QML source
707 Use this status to provide an update or respond to the status change in some way.
708 For example, you could:
711 \li Trigger a state change:
713 State { name: 'loaded'; when: loader.status == Loader.Ready }
716 \li Implement an \c onStatusChanged signal handler:
720 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
724 \li Bind to the status value:
726 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
730 Note that if the source is a local file, the status will initially be Ready (or Error). While
731 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
736 QQuickLoader::Status QQuickLoader::status() const
738 Q_D(const QQuickLoader);
744 switch (d->component->status()) {
745 case QQmlComponent::Loading:
747 case QQmlComponent::Error:
749 case QQmlComponent::Null:
757 switch (d->incubator->status()) {
758 case QQmlIncubator::Loading:
760 case QQmlIncubator::Error:
770 return d->source.isEmpty() ? Null : Error;
773 void QQuickLoader::componentComplete()
776 QQuickItem::componentComplete();
778 if (d->loadingFromSource) {
779 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
780 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
787 \qmlsignal QtQuick2::Loader::onLoaded()
789 This handler is called when the \l status becomes \c Loader.Ready, or on successful
795 \qmlproperty real QtQuick2::Loader::progress
797 This property holds the progress of loading QML data from the network, from
798 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
799 this value will rapidly change from 0 to 1.
803 qreal QQuickLoader::progress() const
805 Q_D(const QQuickLoader);
811 return d->component->progress();
817 \qmlproperty bool QtQuick2::Loader::asynchronous
819 This property holds whether the component will be instantiated asynchronously.
821 When used in conjunction with the \l source property, loading and compilation
822 will also be performed in a background thread.
824 Loading asynchronously creates the objects declared by the component
825 across multiple frames, and reduces the
826 likelihood of glitches in animation. When loading asynchronously the status
827 will change to Loader.Loading. Once the entire component has been created, the
828 \l item will be available and the status will change to Loader.Ready.
830 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
834 source: "mycomponent.qml"
836 visible: status == Loader.Ready
840 Note that this property affects object instantiation only; it is unrelated to
841 loading a component asynchronously via a network.
843 bool QQuickLoader::asynchronous() const
845 Q_D(const QQuickLoader);
846 return d->asynchronous;
849 void QQuickLoader::setAsynchronous(bool a)
852 if (d->asynchronous == a)
856 emit asynchronousChanged();
859 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
862 if (!item || updatingSize)
867 if (loaderGeometryChanged && q->widthValid())
868 item->setWidth(q->width());
869 if (loaderGeometryChanged && q->heightValid())
870 item->setHeight(q->height());
872 q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
874 updatingSize = false;
878 \qmlproperty object QtQuick2::Loader::item
879 This property holds the top-level object that is currently loaded.
881 Since QtQuick 2.0 Loader can load any object type.
883 QObject *QQuickLoader::item() const
885 Q_D(const QQuickLoader);
889 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
892 if (newGeometry != oldGeometry) {
895 QQuickItem::geometryChanged(newGeometry, oldGeometry);
898 QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV8Function *args)
900 QV8Engine *v8engine = args->engine();
901 QString arg = v8engine->toString((*args)[0]->ToString());
905 QQmlContextData *context = args->context();
907 return context->resolvedUrl(QUrl(arg));
910 v8::Handle<v8::Object> QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error)
912 v8::Local<v8::Object> valuemap;
913 if (args->Length() >= 2) {
914 v8::Local<v8::Value> v = (*args)[1];
915 if (!v->IsObject() || v->IsArray()) {
917 qmlInfo(loader) << loader->tr("setSource: value is not an object");
920 valuemap = v8::Local<v8::Object>::Cast(v);
927 #include <moc_qquickloader_p.cpp>