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), 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);
126 void QQuickLoaderPrivate::initResize()
128 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
129 p->addItemChangeListener(this, watchedChanges);
133 qreal QQuickLoaderPrivate::getImplicitWidth() const
135 Q_Q(const QQuickLoader);
136 // If the Loader has a valid width then Loader has set an explicit width on the
137 // item, and we want the item's implicitWidth. If the Loader's width has
138 // not been set then its implicitWidth is the width of the item.
140 return q->widthValid() ? item->implicitWidth() : item->width();
141 return QQuickImplicitSizeItemPrivate::getImplicitWidth();
144 qreal QQuickLoaderPrivate::getImplicitHeight() const
146 Q_Q(const QQuickLoader);
147 // If the Loader has a valid height then Loader has set an explicit height on the
148 // item, and we want the item's implicitHeight. If the Loader's height has
149 // not been set then its implicitHeight is the height of the item.
151 return q->heightValid() ? item->implicitHeight() : item->height();
152 return QQuickImplicitSizeItemPrivate::getImplicitHeight();
156 \qmlclass Loader QQuickLoader
157 \inqmlmodule QtQuick 2
158 \ingroup qml-utility-elements
161 \brief The Loader item allows dynamically loading an Item-based
162 subtree from a URL or Component.
164 Loader is used to dynamically load visual QML components. For loading non-visual
165 components, see \l {Dynamic Object Management in QML}.
168 QML file (using the \l source property) or a \l Component object (using
169 the \l sourceComponent property). It is useful for delaying the creation
170 of a component until it is required: for example, when a component should
171 be created on demand, or when a component should not be created
172 unnecessarily for performance reasons.
174 Here is a Loader that loads "Page1.qml" as a component when the
175 \l MouseArea is clicked:
177 \snippet doc/snippets/qml/loader/simple.qml 0
179 The loaded item can be accessed using the \l item property.
181 If the \l source or \l sourceComponent changes, any previously instantiated
182 items are destroyed. Setting \l source to an empty string or setting
183 \l sourceComponent to \c undefined destroys the currently loaded item,
184 freeing resources and leaving the Loader empty.
186 \section2 Loader sizing behavior
188 Loader is like any other visual item and must be positioned and sized
189 accordingly to become visible.
192 \li If an explicit size is not specified for the Loader, the Loader
193 is automatically resized to the size of the loaded item once the
195 \li If the size of the Loader is specified explicitly by setting
196 the width, height or by anchoring, the loaded item will be resized
197 to the size of the Loader.
200 In both scenarios the size of the item and the Loader are identical.
201 This ensures that anchoring to the Loader is equivalent to anchoring
209 \li \snippet doc/snippets/qml/loader/sizeloader.qml 0
210 \li \snippet doc/snippets/qml/loader/sizeitem.qml 0
212 \li The red rectangle will be sized to the size of the root item.
213 \li The red rectangle will be 50x50, centered in the root item.
217 \section2 Receiving signals from loaded items
219 Any signals emitted from the loaded item can be received using the
220 \l Connections element. For example, the following \c application.qml
221 loads \c MyItem.qml, and is able to receive the \c message signal from
222 the loaded item through a \l Connections object:
229 \li \snippet doc/snippets/qml/loader/connections.qml 0
230 \li \snippet doc/snippets/qml/loader/MyItem.qml 0
233 Alternatively, since \c MyItem.qml is loaded within the scope of the
234 Loader, it could also directly call any function defined in the Loader or
238 \section2 Focus and key events
240 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
241 \c true for any of its children to get the \e {active focus}. (See
242 \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
243 for more details.) Any key events received in the loaded item should likely
244 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
246 For example, the following \c application.qml loads \c KeyReader.qml when
247 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
248 set to \c true for the Loader as well as the \l Item in the dynamically
256 \li \snippet doc/snippets/qml/loader/focus.qml 0
257 \li \snippet doc/snippets/qml/loader/KeyReader.qml 0
260 Once \c KeyReader.qml is loaded, it accepts key events and sets
261 \c event.accepted to \c true so that the event is not propagated to the
264 \sa {dynamic-object-creation}{Dynamic Object Creation}
267 QQuickLoader::QQuickLoader(QQuickItem *parent)
268 : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
270 setFlag(ItemIsFocusScope);
273 QQuickLoader::~QQuickLoader()
277 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
278 p->removeItemChangeListener(d, watchedChanges);
283 \qmlproperty bool QtQuick2::Loader::active
284 This property is \c true if the Loader is currently active.
285 The default value for the \l active property is \c true.
287 If the Loader is inactive, changing the \l source or \l sourceComponent
288 will not cause the item to be instantiated until the Loader is made active.
290 Setting the value to inactive will cause any \l item loaded by the loader
291 to be released, but will not affect the \l source or \l sourceComponent.
293 The \l status of an inactive loader is always \c Null.
295 \sa source, sourceComponent
297 bool QQuickLoader::active() const
299 Q_D(const QQuickLoader);
303 void QQuickLoader::setActive(bool newVal)
306 if (d->active != newVal) {
308 if (newVal == true) {
309 if (d->loadingFromSource) {
312 loadFromSourceComponent();
315 // cancel any current incubation
317 d->incubator->clear();
318 delete d->itemContext;
323 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
324 p->removeItemChangeListener(d, watchedChanges);
326 // We can't delete immediately because our item may have triggered
327 // the Loader to load a different item.
328 d->item->setParentItem(0);
329 d->item->setVisible(false);
330 d->item->deleteLater();
334 emit statusChanged();
336 emit activeChanged();
342 \qmlproperty url QtQuick2::Loader::source
343 This property holds the URL of the QML component to instantiate.
345 Note the QML component must be an \l{Item}-based component. The loader
346 cannot load non-visual components.
348 To unload the currently loaded item, set this property to an empty string,
349 or set \l sourceComponent to \c undefined. Setting \c source to a
350 new URL will also cause the item created by the previous URL to be unloaded.
352 \sa sourceComponent, status, progress
354 QUrl QQuickLoader::source() const
356 Q_D(const QQuickLoader);
360 void QQuickLoader::setSource(const QUrl &url)
362 setSource(url, true); // clear previous values
365 void QQuickLoader::setSource(const QUrl &url, bool needsClear)
368 if (d->source == url)
375 d->loadingFromSource = true;
380 emit sourceChanged();
383 void QQuickLoader::loadFromSource()
386 if (d->source.isEmpty()) {
387 emit sourceChanged();
388 emit statusChanged();
389 emit progressChanged();
394 if (isComponentComplete()) {
395 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
396 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
402 \qmlproperty Component QtQuick2::Loader::sourceComponent
403 This property holds the \l{Component} to instantiate.
409 Rectangle { color: "red"; width: 10; height: 10 }
412 Loader { sourceComponent: redSquare }
413 Loader { sourceComponent: redSquare; x: 10 }
417 To unload the currently loaded item, set this property to an empty string
423 QQmlComponent *QQuickLoader::sourceComponent() const
425 Q_D(const QQuickLoader);
429 void QQuickLoader::setSourceComponent(QQmlComponent *comp)
432 if (comp == d->component)
438 d->loadingFromSource = false;
441 loadFromSourceComponent();
443 emit sourceComponentChanged();
446 void QQuickLoader::resetSourceComponent()
448 setSourceComponent(0);
451 void QQuickLoader::loadFromSourceComponent()
455 emit sourceComponentChanged();
456 emit statusChanged();
457 emit progressChanged();
462 if (isComponentComplete())
467 \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
469 Creates an object instance of the given \a source component that will have
470 the given \a properties. The \a properties argument is optional. The instance
471 will be accessible via the \l item property once loading and instantiation
474 If the \l active property is \c false at the time when this function is called,
475 the given \a source component will not be loaded but the \a source and initial
476 \a properties will be cached. When the loader is made \l active, an instance of
477 the \a source component will be created with the initial \a properties set.
479 Setting the initial property values of an instance of a component in this manner
480 will \b{not} trigger any associated \l{Behavior}s.
482 Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
483 is changed after calling this function but prior to setting the loader \l active.
490 // ExampleComponent.qml
502 to: (rect.width + 20)
515 onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
518 Component.onCompleted: {
519 squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
520 // will trigger the onLoaded code when complete.
528 void QQuickLoader::setSource(QQmlV8Function *args)
533 bool ipvError = false;
534 args->returnValue(v8::Undefined());
535 v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
540 QUrl sourceUrl = d->resolveSourceUrl(args);
541 if (!ipv.IsEmpty()) {
542 d->disposeInitialPropertyValues();
543 d->initialPropertyValues = qPersistentNew(ipv);
544 d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
547 setSource(sourceUrl, false); // already cleared and set ipv above.
550 void QQuickLoaderPrivate::disposeInitialPropertyValues()
552 if (!initialPropertyValues.IsEmpty())
553 qPersistentDispose(initialPropertyValues);
554 if (!qmlGlobalForIpv.IsEmpty())
555 qPersistentDispose(qmlGlobalForIpv);
558 void QQuickLoaderPrivate::load()
562 if (!q->isComponentComplete() || !component)
565 if (!component->isLoading()) {
568 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
569 q, SLOT(_q_sourceLoaded()));
570 QObject::connect(component, SIGNAL(progressChanged(qreal)),
571 q, SIGNAL(progressChanged()));
572 emit q->statusChanged();
573 emit q->progressChanged();
574 if (loadingFromSource)
575 emit q->sourceChanged();
577 emit q->sourceComponentChanged();
578 emit q->itemChanged();
582 void QQuickLoaderIncubator::setInitialState(QObject *o)
584 loader->setInitialState(o);
587 void QQuickLoaderPrivate::setInitialState(QObject *obj)
591 QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
593 // If the item doesn't have an explicit size, but the Loader
594 // does, then set the item's size now before bindings are
595 // evaluated, otherwise we will end up resizing the item
596 // later and triggering any affected bindings/anchors.
597 if (widthValid && !QQuickItemPrivate::get(item)->widthValid)
598 item->setWidth(q->width());
599 if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
600 item->setHeight(q->height());
601 QQml_setParent_noEvent(itemContext, obj);
602 QQml_setParent_noEvent(item, q);
603 item->setParentItem(q);
607 if (initialPropertyValues.IsEmpty())
610 QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
611 Q_ASSERT(d && d->engine);
612 d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj);
615 void QQuickLoaderIncubator::statusChanged(Status status)
617 loader->incubatorStateChanged(status);
620 void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
623 if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
626 if (status == QQmlIncubator::Ready) {
627 QObject *obj = incubator->object();
628 item = qmlobject_cast<QQuickItem*>(obj);
630 emit q->itemChanged();
633 qmlInfo(q) << QQuickLoader::tr("Loader does not support loading non-visual elements.");
637 emit q->itemChanged();
640 } else if (status == QQmlIncubator::Error) {
641 if (!incubator->errors().isEmpty())
642 QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
645 delete incubator->object();
647 emit q->itemChanged();
649 if (loadingFromSource)
650 emit q->sourceChanged();
652 emit q->sourceComponentChanged();
653 emit q->statusChanged();
654 emit q->progressChanged();
655 if (status == QQmlIncubator::Ready)
657 disposeInitialPropertyValues(); // cleanup
660 void QQuickLoaderPrivate::_q_sourceLoaded()
663 if (!component || !component->errors().isEmpty()) {
665 QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
666 if (loadingFromSource)
667 emit q->sourceChanged();
669 emit q->sourceComponentChanged();
670 emit q->statusChanged();
671 emit q->progressChanged();
672 disposeInitialPropertyValues(); // cleanup
676 QQmlContext *creationContext = component->creationContext();
677 if (!creationContext) creationContext = qmlContext(q);
678 itemContext = new QQmlContext(creationContext);
679 itemContext->setContextObject(q);
682 incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
684 component->create(*incubator, itemContext);
686 if (incubator && incubator->status() == QQmlIncubator::Loading)
687 emit q->statusChanged();
691 \qmlproperty enumeration QtQuick2::Loader::status
693 This property holds the status of QML loading. It can be one of:
695 \li Loader.Null - the loader is inactive or no QML source has been set
696 \li Loader.Ready - the QML source has been loaded
697 \li Loader.Loading - the QML source is currently being loaded
698 \li Loader.Error - an error occurred while loading the QML source
701 Use this status to provide an update or respond to the status change in some way.
702 For example, you could:
705 \li Trigger a state change:
707 State { name: 'loaded'; when: loader.status == Loader.Ready }
710 \li Implement an \c onStatusChanged signal handler:
714 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
718 \li Bind to the status value:
720 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
724 Note that if the source is a local file, the status will initially be Ready (or Error). While
725 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
730 QQuickLoader::Status QQuickLoader::status() const
732 Q_D(const QQuickLoader);
738 switch (d->component->status()) {
739 case QQmlComponent::Loading:
741 case QQmlComponent::Error:
743 case QQmlComponent::Null:
751 switch (d->incubator->status()) {
752 case QQmlIncubator::Loading:
754 case QQmlIncubator::Error:
764 return d->source.isEmpty() ? Null : Error;
767 void QQuickLoader::componentComplete()
770 QQuickItem::componentComplete();
772 if (d->loadingFromSource) {
773 QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
774 d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
781 \qmlsignal QtQuick2::Loader::onLoaded()
783 This handler is called when the \l status becomes \c Loader.Ready, or on successful
789 \qmlproperty real QtQuick2::Loader::progress
791 This property holds the progress of loading QML data from the network, from
792 0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
793 this value will rapidly change from 0 to 1.
797 qreal QQuickLoader::progress() const
799 Q_D(const QQuickLoader);
805 return d->component->progress();
811 \qmlproperty bool QtQuick2::Loader::asynchronous
813 This property holds whether the component will be instantiated asynchronously.
815 When used in conjunction with the \l source property, loading and compilation
816 will also be performed in a background thread.
818 Loading asynchronously creates the objects declared by the component
819 across multiple frames, and reduces the
820 likelihood of glitches in animation. When loading asynchronously the status
821 will change to Loader.Loading. Once the entire component has been created, the
822 \l item will be available and the status will change to Loader.Ready.
824 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
828 source: "mycomponent.qml"
830 visible: status == Loader.Ready
834 Note that this property affects object instantiation only; it is unrelated to
835 loading a component asynchronously via a network.
837 bool QQuickLoader::asynchronous() const
839 Q_D(const QQuickLoader);
840 return d->asynchronous;
843 void QQuickLoader::setAsynchronous(bool a)
846 if (d->asynchronous == a)
850 emit asynchronousChanged();
853 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
856 if (!item || updatingSize)
861 if (loaderGeometryChanged && q->widthValid())
862 item->setWidth(q->width());
863 if (loaderGeometryChanged && q->heightValid())
864 item->setHeight(q->height());
866 q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
868 updatingSize = false;
872 \qmlproperty Item QtQuick2::Loader::item
873 This property holds the top-level item that is currently loaded.
875 QQuickItem *QQuickLoader::item() const
877 Q_D(const QQuickLoader);
881 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
884 if (newGeometry != oldGeometry) {
887 QQuickItem::geometryChanged(newGeometry, oldGeometry);
890 QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV8Function *args)
892 QV8Engine *v8engine = args->engine();
893 QString arg = v8engine->toString((*args)[0]->ToString());
897 QQmlContextData *context = args->context();
899 return context->resolvedUrl(QUrl(arg));
902 v8::Handle<v8::Object> QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error)
904 v8::Local<v8::Object> valuemap;
905 if (args->Length() >= 2) {
906 v8::Local<v8::Value> v = (*args)[1];
907 if (!v->IsObject() || v->IsArray()) {
909 qmlInfo(loader) << loader->tr("setSource: value is not an object");
912 valuemap = v8::Local<v8::Object>::Cast(v);
919 #include <moc_qquickloader_p.cpp>