Fix crash in QQuickLoader when source component is garbage collected
[qt:qtdeclarative.git] / src / quick / items / qquickloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickloader_p_p.h"
43
44 #include <QtQml/qqmlinfo.h>
45
46 #include <private/qqmlengine_p.h>
47 #include <private/qqmlglobal_p.h>
48
49 #include <private/qqmlcomponent_p.h>
50
51 QT_BEGIN_NAMESPACE
52
53 static const QQuickItemPrivate::ChangeTypes watchedChanges
54     = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
55
56 QQuickLoaderPrivate::QQuickLoaderPrivate()
57     : item(0), object(0), component(0), itemContext(0), incubator(0), updatingSize(false),
58       active(true), loadingFromSource(false), asynchronous(false)
59 {
60 }
61
62 QQuickLoaderPrivate::~QQuickLoaderPrivate()
63 {
64     delete itemContext;
65     itemContext = 0;
66     delete incubator;
67     disposeInitialPropertyValues();
68 }
69
70 void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
71 {
72     if (resizeItem == item)
73         _q_updateSize(false);
74     QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
75 }
76
77 void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *)
78 {
79     Q_Q(QQuickLoader);
80     q->setImplicitWidth(getImplicitWidth());
81 }
82
83 void QQuickLoaderPrivate::itemImplicitHeightChanged(QQuickItem *)
84 {
85     Q_Q(QQuickLoader);
86     q->setImplicitHeight(getImplicitHeight());
87 }
88
89 void QQuickLoaderPrivate::clear()
90 {
91     Q_Q(QQuickLoader);
92     disposeInitialPropertyValues();
93
94     if (incubator)
95         incubator->clear();
96
97     delete itemContext;
98     itemContext = 0;
99
100     if (loadingFromSource && component) {
101         // disconnect since we deleteLater
102         QObject::disconnect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
103                 q, SLOT(_q_sourceLoaded()));
104         QObject::disconnect(component, SIGNAL(progressChanged(qreal)),
105                 q, SIGNAL(progressChanged()));
106         component->deleteLater();
107         component = 0;
108     }
109     componentStrongReference.clear();
110     source = QUrl();
111
112     if (item) {
113         QQuickItemPrivate *p = QQuickItemPrivate::get(item);
114         p->removeItemChangeListener(this, watchedChanges);
115
116         // We can't delete immediately because our item may have triggered
117         // the Loader to load a different item.
118         item->setParentItem(0);
119         item->setVisible(false);
120         item = 0;
121     }
122     if (object) {
123         object->deleteLater();
124         object = 0;
125     }
126 }
127
128 void QQuickLoaderPrivate::initResize()
129 {
130     if (!item)
131         return;
132     QQuickItemPrivate *p = QQuickItemPrivate::get(item);
133     p->addItemChangeListener(this, watchedChanges);
134     _q_updateSize();
135 }
136
137 qreal QQuickLoaderPrivate::getImplicitWidth() const
138 {
139     Q_Q(const QQuickLoader);
140     // If the Loader has a valid width then Loader has set an explicit width on the
141     // item, and we want the item's implicitWidth.  If the Loader's width has
142     // not been set then its implicitWidth is the width of the item.
143     if (item)
144         return q->widthValid() ? item->implicitWidth() : item->width();
145     return QQuickImplicitSizeItemPrivate::getImplicitWidth();
146 }
147
148 qreal QQuickLoaderPrivate::getImplicitHeight() const
149 {
150     Q_Q(const QQuickLoader);
151     // If the Loader has a valid height then Loader has set an explicit height on the
152     // item, and we want the item's implicitHeight.  If the Loader's height has
153     // not been set then its implicitHeight is the height of the item.
154     if (item)
155         return q->heightValid() ? item->implicitHeight() : item->height();
156     return QQuickImplicitSizeItemPrivate::getImplicitHeight();
157 }
158
159 /*!
160     \qmltype Loader
161     \instantiates QQuickLoader
162     \inqmlmodule QtQuick
163     \ingroup qtquick-dynamic
164     \inherits Item
165
166     \brief Allows dynamic loading of a subtree from a URL or Component
167
168     Loader is used to dynamically load QML components.
169
170     Loader can load a
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.
176
177     Here is a Loader that loads "Page1.qml" as a component when the
178     \l MouseArea is clicked:
179
180     \snippet qml/loader/simple.qml 0
181
182     The loaded object can be accessed using the \l item property.
183
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.
188
189     \section2 Loader sizing behavior
190
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:
194
195     \list
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
198     component is loaded.
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.
202     \endlist
203
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
206     to the loaded item.
207
208     \table
209     \row
210     \li sizeloader.qml
211     \li sizeitem.qml
212     \row
213     \li \snippet qml/loader/sizeloader.qml 0
214     \li \snippet qml/loader/sizeitem.qml 0
215     \row
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.
218     \endtable
219
220
221     \section2 Receiving signals from loaded objects
222
223     Any signals emitted from the loaded object can be received using the
224     \l Connections type. 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:
227
228     \table
229     \row
230     \li application.qml
231     \li MyItem.qml
232     \row
233     \li \snippet qml/loader/connections.qml 0
234     \li \snippet qml/loader/MyItem.qml 0
235     \endtable
236
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
239     its parent \l Item.
240
241
242     \section2 Focus and key events
243
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{Keyboard Focus in Qt Quick}
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.
249
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
253     loaded object:
254
255     \table
256     \row
257     \li application.qml
258     \li KeyReader.qml
259     \row
260     \li \snippet qml/loader/focus.qml 0
261     \li \snippet qml/loader/KeyReader.qml 0
262     \endtable
263
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
266     parent \l Rectangle.
267
268     Since \c {QtQuick 2.0}, Loader can also load non-visual components.
269
270     \section2 Using a Loader within a view delegate
271
272     In some cases you may wish to use a Loader within a view delegate to improve delegate
273     loading performance. This works well in most cases, but there is one important issue to
274     be aware of related to the \l{QtQuick::Component#creation-context}{creation context} of a Component.
275
276     In the following example, the \c index context property inserted by the ListView into \c delegateComponent's
277     context will be inaccessible to Text, as the Loader will use the creation context of \c myComponent as the parent
278     context when instantiating it, and \c index does not refer to anything within that context chain.
279
280     \snippet qml/loader/creationContext1.qml 0
281
282     In this situation we can either move the component inline,
283
284     \snippet qml/loader/creationContext2.qml 0
285
286     into a separate file,
287
288     \snippet qml/loader/creationContext3.qml 0
289
290     or explicitly set the required information as a property of the Loader (this works because the
291     Loader sets itself as the context object for the component it is loading).
292
293     \snippet qml/loader/creationContext4.qml 0
294
295     \sa {dynamic-object-creation}{Dynamic Object Creation}
296 */
297
298 QQuickLoader::QQuickLoader(QQuickItem *parent)
299   : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
300 {
301     setFlag(ItemIsFocusScope);
302 }
303
304 QQuickLoader::~QQuickLoader()
305 {
306     Q_D(QQuickLoader);
307     if (d->item) {
308         QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
309         p->removeItemChangeListener(d, watchedChanges);
310     }
311 }
312
313 /*!
314     \qmlproperty bool QtQuick::Loader::active
315     This property is \c true if the Loader is currently active.
316     The default value for this property is \c true.
317
318     If the Loader is inactive, changing the \l source or \l sourceComponent
319     will not cause the item to be instantiated until the Loader is made active.
320
321     Setting the value to inactive will cause any \l item loaded by the loader
322     to be released, but will not affect the \l source or \l sourceComponent.
323
324     The \l status of an inactive loader is always \c Null.
325
326     \sa source, sourceComponent
327  */
328 bool QQuickLoader::active() const
329 {
330     Q_D(const QQuickLoader);
331     return d->active;
332 }
333
334 void QQuickLoader::setActive(bool newVal)
335 {
336     Q_D(QQuickLoader);
337     if (d->active == newVal)
338         return;
339
340     d->active = newVal;
341     if (newVal == true) {
342         if (d->loadingFromSource) {
343             loadFromSource();
344         } else {
345             loadFromSourceComponent();
346         }
347     } else {
348         // cancel any current incubation
349         if (d->incubator) {
350             d->incubator->clear();
351             delete d->itemContext;
352             d->itemContext = 0;
353         }
354
355         if (d->item) {
356             QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
357             p->removeItemChangeListener(d, watchedChanges);
358
359             // We can't delete immediately because our item may have triggered
360             // the Loader to load a different item.
361             d->item->setParentItem(0);
362             d->item->setVisible(false);
363             d->item = 0;
364         }
365         if (d->object) {
366             d->object->deleteLater();
367             d->object = 0;
368             emit itemChanged();
369         }
370         emit statusChanged();
371     }
372     emit activeChanged();
373 }
374
375
376 /*!
377     \qmlproperty url QtQuick::Loader::source
378     This property holds the URL of the QML component to instantiate.
379
380     Since \c {QtQuick 2.0}, Loader is able to load any type of object; it
381     is not restricted to Item types.
382
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.
386
387     \sa sourceComponent, status, progress
388 */
389 QUrl QQuickLoader::source() const
390 {
391     Q_D(const QQuickLoader);
392     return d->source;
393 }
394
395 void QQuickLoader::setSource(const QUrl &url)
396 {
397     setSource(url, true); // clear previous values
398 }
399
400 void QQuickLoader::setSource(const QUrl &url, bool needsClear)
401 {
402     Q_D(QQuickLoader);
403     if (d->source == url)
404         return;
405
406     if (needsClear)
407         d->clear();
408
409     d->source = url;
410     d->loadingFromSource = true;
411
412     if (d->active)
413         loadFromSource();
414     else
415         emit sourceChanged();
416 }
417
418 void QQuickLoader::loadFromSource()
419 {
420     Q_D(QQuickLoader);
421     if (d->source.isEmpty()) {
422         emit sourceChanged();
423         emit statusChanged();
424         emit progressChanged();
425         emit itemChanged();
426         return;
427     }
428
429     if (isComponentComplete()) {
430         QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
431         d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
432         d->load();
433     }
434 }
435
436 /*!
437     \qmlproperty Component QtQuick::Loader::sourceComponent
438     This property holds the \l{Component} to instantiate.
439
440     \qml
441     Item {
442         Component {
443             id: redSquare
444             Rectangle { color: "red"; width: 10; height: 10 }
445         }
446
447         Loader { sourceComponent: redSquare }
448         Loader { sourceComponent: redSquare; x: 10 }
449     }
450     \endqml
451
452     To unload the currently loaded object, set this property to an empty string
453     or \c undefined.
454
455     Since \c {QtQuick 2.0}, Loader is able to load any type of object; it
456     is not restricted to Item types.
457
458     \sa source, progress
459 */
460
461 QQmlComponent *QQuickLoader::sourceComponent() const
462 {
463     Q_D(const QQuickLoader);
464     return d->component;
465 }
466
467 void QQuickLoader::setSourceComponent(QQmlComponent *comp)
468 {
469     Q_D(QQuickLoader);
470     if (comp == d->component)
471         return;
472
473     d->clear();
474
475     d->component = comp;
476     if (comp) {
477         if (QQmlData *ddata = QQmlData::get(comp))
478             d->componentStrongReference = ddata->jsWrapper.value();
479     }
480     d->loadingFromSource = false;
481
482     if (d->active)
483         loadFromSourceComponent();
484     else
485         emit sourceComponentChanged();
486 }
487
488 void QQuickLoader::resetSourceComponent()
489 {
490     setSourceComponent(0);
491 }
492
493 void QQuickLoader::loadFromSourceComponent()
494 {
495     Q_D(QQuickLoader);
496     if (!d->component) {
497         emit sourceComponentChanged();
498         emit statusChanged();
499         emit progressChanged();
500         emit itemChanged();
501         return;
502     }
503
504     if (isComponentComplete())
505         d->load();
506 }
507
508 /*!
509     \qmlmethod object QtQuick::Loader::setSource(url source, object properties)
510
511     Creates an object instance of the given \a source component that will have
512     the given \a properties. The \a properties argument is optional.  The instance
513     will be accessible via the \l item property once loading and instantiation
514     is complete.
515
516     If the \l active property is \c false at the time when this function is called,
517     the given \a source component will not be loaded but the \a source and initial
518     \a properties will be cached.  When the loader is made \l active, an instance of
519     the \a source component will be created with the initial \a properties set.
520
521     Setting the initial property values of an instance of a component in this manner
522     will \b{not} trigger any associated \l{Behavior}s.
523
524     Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
525     is changed after calling this function but prior to setting the loader \l active.
526
527     Example:
528     \table
529     \row
530     \li
531     \qml
532     // ExampleComponent.qml
533     import QtQuick 2.0
534     Rectangle {
535         id: rect
536         color: "red"
537         width: 10
538         height: 10
539
540         Behavior on color {
541             NumberAnimation {
542                 target: rect
543                 property: "width"
544                 to: (rect.width + 20)
545                 duration: 0
546             }
547         }
548     }
549     \endqml
550     \li
551     \qml
552     // example.qml
553     import QtQuick 2.0
554     Item {
555         Loader {
556             id: squareLoader
557             onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
558         }
559
560         Component.onCompleted: {
561             squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
562             // will trigger the onLoaded code when complete.
563         }
564     }
565     \endqml
566     \endtable
567
568     \sa source, active
569 */
570 void QQuickLoader::setSource(QQmlV4Function *args)
571 {
572     Q_ASSERT(args);
573     Q_D(QQuickLoader);
574
575     bool ipvError = false;
576     args->setReturnValue(QV4::Encode::undefined());
577     QV4::Scope scope(args->v4engine());
578     QV4::ScopedValue ipv(scope, d->extractInitialPropertyValues(args, this, &ipvError));
579     if (ipvError)
580         return;
581
582     d->clear();
583     QUrl sourceUrl = d->resolveSourceUrl(args);
584     if (!ipv->isUndefined()) {
585         d->disposeInitialPropertyValues();
586         d->initialPropertyValues = ipv.asReturnedValue();
587         d->qmlGlobalForIpv = args->qmlGlobal();
588     }
589
590     setSource(sourceUrl, false); // already cleared and set ipv above.
591 }
592
593 void QQuickLoaderPrivate::disposeInitialPropertyValues()
594 {
595 }
596
597 void QQuickLoaderPrivate::load()
598 {
599     Q_Q(QQuickLoader);
600
601     if (!q->isComponentComplete() || !component)
602         return;
603
604     if (!component->isLoading()) {
605         _q_sourceLoaded();
606     } else {
607         QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
608                 q, SLOT(_q_sourceLoaded()));
609         QObject::connect(component, SIGNAL(progressChanged(qreal)),
610                 q, SIGNAL(progressChanged()));
611         emit q->statusChanged();
612         emit q->progressChanged();
613         if (loadingFromSource)
614             emit q->sourceChanged();
615         else
616             emit q->sourceComponentChanged();
617         emit q->itemChanged();
618     }
619 }
620
621 void QQuickLoaderIncubator::setInitialState(QObject *o)
622 {
623     loader->setInitialState(o);
624 }
625
626 void QQuickLoaderPrivate::setInitialState(QObject *obj)
627 {
628     Q_Q(QQuickLoader);
629
630     QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
631     if (item) {
632         // If the item doesn't have an explicit size, but the Loader
633         // does, then set the item's size now before bindings are
634         // evaluated, otherwise we will end up resizing the item
635         // later and triggering any affected bindings/anchors.
636         if (widthValid && !QQuickItemPrivate::get(item)->widthValid)
637             item->setWidth(q->width());
638         if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
639             item->setHeight(q->height());
640         item->setParentItem(q);
641     }
642     if (obj) {
643         QQml_setParent_noEvent(itemContext, obj);
644         QQml_setParent_noEvent(obj, q);
645         itemContext = 0;
646     }
647
648     if (initialPropertyValues.isUndefined())
649         return;
650
651     QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
652     Q_ASSERT(d && d->engine);
653     QV4::ExecutionEngine *v4 = qmlGlobalForIpv.engine();
654     Q_ASSERT(v4);
655     QV4::Scope scope(v4);
656     QV4::ScopedValue ipv(scope, initialPropertyValues.value());
657     d->initializeObjectWithInitialProperties(qmlGlobalForIpv, ipv, obj);
658 }
659
660 void QQuickLoaderIncubator::statusChanged(Status status)
661 {
662     loader->incubatorStateChanged(status);
663 }
664
665 void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
666 {
667     Q_Q(QQuickLoader);
668     if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
669         return;
670
671     if (status == QQmlIncubator::Ready) {
672         object = incubator->object();
673         item = qmlobject_cast<QQuickItem*>(object);
674         emit q->itemChanged();
675         initResize();
676         incubator->clear();
677     } else if (status == QQmlIncubator::Error) {
678         if (!incubator->errors().isEmpty())
679             QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
680         delete itemContext;
681         itemContext = 0;
682         delete incubator->object();
683         source = QUrl();
684         emit q->itemChanged();
685     }
686     if (loadingFromSource)
687         emit q->sourceChanged();
688     else
689         emit q->sourceComponentChanged();
690     emit q->statusChanged();
691     emit q->progressChanged();
692     if (status == QQmlIncubator::Ready)
693         emit q->loaded();
694     disposeInitialPropertyValues(); // cleanup
695 }
696
697 void QQuickLoaderPrivate::_q_sourceLoaded()
698 {
699     Q_Q(QQuickLoader);
700     if (!component || !component->errors().isEmpty()) {
701         if (component)
702             QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
703         if (loadingFromSource)
704             emit q->sourceChanged();
705         else
706             emit q->sourceComponentChanged();
707         emit q->statusChanged();
708         emit q->progressChanged();
709         emit q->itemChanged(); //Like clearing source, emit itemChanged even if previous item was also null
710         disposeInitialPropertyValues(); // cleanup
711         return;
712     }
713
714     QQmlContext *creationContext = component->creationContext();
715     if (!creationContext) creationContext = qmlContext(q);
716     itemContext = new QQmlContext(creationContext);
717     itemContext->setContextObject(q);
718
719     delete incubator;
720     incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
721
722     component->create(*incubator, itemContext);
723
724     if (incubator && incubator->status() == QQmlIncubator::Loading)
725         emit q->statusChanged();
726 }
727
728 /*!
729     \qmlproperty enumeration QtQuick::Loader::status
730
731     This property holds the status of QML loading.  It can be one of:
732     \list
733     \li Loader.Null - the loader is inactive or no QML source has been set
734     \li Loader.Ready - the QML source has been loaded
735     \li Loader.Loading - the QML source is currently being loaded
736     \li Loader.Error - an error occurred while loading the QML source
737     \endlist
738
739     Use this status to provide an update or respond to the status change in some way.
740     For example, you could:
741
742     \list
743     \li Trigger a state change:
744     \qml
745         State { name: 'loaded'; when: loader.status == Loader.Ready }
746     \endqml
747
748     \li Implement an \c onStatusChanged signal handler:
749     \qml
750         Loader {
751             id: loader
752             onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
753         }
754     \endqml
755
756     \li Bind to the status value:
757     \qml
758         Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
759     \endqml
760     \endlist
761
762     Note that if the source is a local file, the status will initially be Ready (or Error). While
763     there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
764
765     \sa progress
766 */
767
768 QQuickLoader::Status QQuickLoader::status() const
769 {
770     Q_D(const QQuickLoader);
771
772     if (!d->active)
773         return Null;
774
775     if (d->component) {
776         switch (d->component->status()) {
777         case QQmlComponent::Loading:
778             return Loading;
779         case QQmlComponent::Error:
780             return Error;
781         case QQmlComponent::Null:
782             return Null;
783         default:
784             break;
785         }
786     }
787
788     if (d->incubator) {
789         switch (d->incubator->status()) {
790         case QQmlIncubator::Loading:
791             return Loading;
792         case QQmlIncubator::Error:
793             return Error;
794         default:
795             break;
796         }
797     }
798
799     if (d->object)
800         return Ready;
801
802     return d->source.isEmpty() ? Null : Error;
803 }
804
805 void QQuickLoader::componentComplete()
806 {
807     Q_D(QQuickLoader);
808     QQuickItem::componentComplete();
809     if (active()) {
810         if (d->loadingFromSource) {
811             QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
812             d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
813         }
814         d->load();
815     }
816 }
817
818 /*!
819     \qmlsignal QtQuick::Loader::onLoaded()
820
821     This handler is called when the \l status becomes \c Loader.Ready, or on successful
822     initial load.
823 */
824
825
826 /*!
827 \qmlproperty real QtQuick::Loader::progress
828
829 This property holds the progress of loading QML data from the network, from
830 0.0 (nothing loaded) to 1.0 (finished).  Most QML files are quite small, so
831 this value will rapidly change from 0 to 1.
832
833 \sa status
834 */
835 qreal QQuickLoader::progress() const
836 {
837     Q_D(const QQuickLoader);
838
839     if (d->object)
840         return 1.0;
841
842     if (d->component)
843         return d->component->progress();
844
845     return 0.0;
846 }
847
848 /*!
849 \qmlproperty bool QtQuick::Loader::asynchronous
850
851 This property holds whether the component will be instantiated asynchronously.
852
853 When used in conjunction with the \l source property, loading and compilation
854 will also be performed in a background thread.
855
856 Loading asynchronously creates the objects declared by the component
857 across multiple frames, and reduces the
858 likelihood of glitches in animation.  When loading asynchronously the status
859 will change to Loader.Loading.  Once the entire component has been created, the
860 \l item will be available and the status will change to Loader.Ready.
861
862 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
863
864 \code
865 Loader {
866     source: "mycomponent.qml"
867     asynchronous: true
868     visible: status == Loader.Ready
869 }
870 \endcode
871
872 Note that this property affects object instantiation only; it is unrelated to
873 loading a component asynchronously via a network.
874 */
875 bool QQuickLoader::asynchronous() const
876 {
877     Q_D(const QQuickLoader);
878     return d->asynchronous;
879 }
880
881 void QQuickLoader::setAsynchronous(bool a)
882 {
883     Q_D(QQuickLoader);
884     if (d->asynchronous == a)
885         return;
886
887     d->asynchronous = a;
888     emit asynchronousChanged();
889 }
890
891 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
892 {
893     Q_Q(QQuickLoader);
894     if (!item)
895         return;
896
897     if (loaderGeometryChanged && q->widthValid())
898         item->setWidth(q->width());
899     if (loaderGeometryChanged && q->heightValid())
900         item->setHeight(q->height());
901
902     if (updatingSize)
903         return;
904
905     updatingSize = true;
906
907     q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
908
909     updatingSize = false;
910 }
911
912 /*!
913     \qmlproperty object QtQuick::Loader::item
914     This property holds the top-level object that is currently loaded.
915
916     Since \c {QtQuick 2.0}, Loader can load any object type.
917 */
918 QObject *QQuickLoader::item() const
919 {
920     Q_D(const QQuickLoader);
921     return d->object;
922 }
923
924 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
925 {
926     Q_D(QQuickLoader);
927     if (newGeometry != oldGeometry) {
928         d->_q_updateSize();
929     }
930     QQuickItem::geometryChanged(newGeometry, oldGeometry);
931 }
932
933 QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV4Function *args)
934 {
935     QV4::Scope scope(args->v4engine());
936     QV4::ScopedValue v(scope, (*args)[0]);
937     QString arg = v->toQString();
938     if (arg.isEmpty())
939         return QUrl();
940
941     QQmlContextData *context = args->context();
942     Q_ASSERT(context);
943     return context->resolvedUrl(QUrl(arg));
944 }
945
946 QV4::ReturnedValue QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV4Function *args, QObject *loader, bool *error)
947 {
948     QV4::Scope scope(args->v4engine());
949     QV4::ScopedValue valuemap(scope, QV4::Primitive::undefinedValue());
950     if (args->length() >= 2) {
951         QV4::ScopedValue v(scope, (*args)[1]);
952         if (!v->isObject() || v->asArrayObject()) {
953             *error = true;
954             qmlInfo(loader) << QQuickLoader::tr("setSource: value is not an object");
955         } else {
956             *error = false;
957             valuemap = v;
958         }
959     }
960
961     return valuemap.asReturnedValue();
962 }
963
964 #include <moc_qquickloader_p.cpp>
965
966 QT_END_NAMESPACE