Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
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 #include <private/qv8_p.h>
52
53 QT_BEGIN_NAMESPACE
54
55 static const QQuickItemPrivate::ChangeTypes watchedChanges
56     = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
57
58 QQuickLoaderPrivate::QQuickLoaderPrivate()
59     : item(0), object(0), component(0), itemContext(0), incubator(0), updatingSize(false),
60       active(true), loadingFromSource(false), asynchronous(false)
61 {
62 }
63
64 QQuickLoaderPrivate::~QQuickLoaderPrivate()
65 {
66     delete itemContext;
67     itemContext = 0;
68     delete incubator;
69     disposeInitialPropertyValues();
70 }
71
72 void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
73 {
74     if (resizeItem == item)
75         _q_updateSize(false);
76     QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
77 }
78
79 void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *)
80 {
81     Q_Q(QQuickLoader);
82     q->setImplicitWidth(getImplicitWidth());
83 }
84
85 void QQuickLoaderPrivate::itemImplicitHeightChanged(QQuickItem *)
86 {
87     Q_Q(QQuickLoader);
88     q->setImplicitHeight(getImplicitHeight());
89 }
90
91 void QQuickLoaderPrivate::clear()
92 {
93     Q_Q(QQuickLoader);
94     disposeInitialPropertyValues();
95
96     if (incubator)
97         incubator->clear();
98
99     delete itemContext;
100     itemContext = 0;
101
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();
109         component = 0;
110     }
111     source = QUrl();
112
113     if (item) {
114         QQuickItemPrivate *p = QQuickItemPrivate::get(item);
115         p->removeItemChangeListener(this, watchedChanges);
116
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);
121         item = 0;
122     }
123     if (object) {
124         object->deleteLater();
125         object = 0;
126     }
127 }
128
129 void QQuickLoaderPrivate::initResize()
130 {
131     if (!item)
132         return;
133     QQuickItemPrivate *p = QQuickItemPrivate::get(item);
134     p->addItemChangeListener(this, watchedChanges);
135     _q_updateSize();
136 }
137
138 qreal QQuickLoaderPrivate::getImplicitWidth() const
139 {
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.
144     if (item)
145         return q->widthValid() ? item->implicitWidth() : item->width();
146     return QQuickImplicitSizeItemPrivate::getImplicitWidth();
147 }
148
149 qreal QQuickLoaderPrivate::getImplicitHeight() const
150 {
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.
155     if (item)
156         return q->heightValid() ? item->implicitHeight() : item->height();
157     return QQuickImplicitSizeItemPrivate::getImplicitHeight();
158 }
159
160 /*!
161     \qmltype Loader
162     \instantiates QQuickLoader
163     \inqmlmodule QtQuick 2
164     \ingroup qtquick-dynamic
165     \inherits Item
166
167     \brief Allows dynamic loading of a subtree from a URL or Component
168
169     Loader is used to dynamically load QML components.
170
171     Loader can load a
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.
177
178     Here is a Loader that loads "Page1.qml" as a component when the
179     \l MouseArea is clicked:
180
181     \snippet qml/loader/simple.qml 0
182
183     The loaded object can be accessed using the \l item property.
184
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.
189
190     \section2 Loader sizing behavior
191
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:
195
196     \list
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
199     component is loaded.
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.
203     \endlist
204
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
207     to the loaded item.
208
209     \table
210     \row
211     \li sizeloader.qml
212     \li sizeitem.qml
213     \row
214     \li \snippet qml/loader/sizeloader.qml 0
215     \li \snippet qml/loader/sizeitem.qml 0
216     \row
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.
219     \endtable
220
221
222     \section2 Receiving signals from loaded objects
223
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:
228
229     \table
230     \row
231     \li application.qml
232     \li MyItem.qml
233     \row
234     \li \snippet qml/loader/connections.qml 0
235     \li \snippet qml/loader/MyItem.qml 0
236     \endtable
237
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
240     its parent \l Item.
241
242
243     \section2 Focus and key events
244
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.
250
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
254     loaded object:
255
256     \table
257     \row
258     \li application.qml
259     \li KeyReader.qml
260     \row
261     \li \snippet qml/loader/focus.qml 0
262     \li \snippet qml/loader/KeyReader.qml 0
263     \endtable
264
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
267     parent \l Rectangle.
268
269     Since QtQuick 2.0 Loader can also load non-visual components.
270
271     \section2 Using a Loader within a view delegate
272
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.
276
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.
280
281     \snippet qml/loader/creationContext1.qml 0
282
283     In this situation we can either move the component inline,
284
285     \snippet qml/loader/creationContext2.qml 0
286
287     into a separate file,
288
289     \snippet qml/loader/creationContext3.qml 0
290
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).
293
294     \snippet qml/loader/creationContext4.qml 0
295
296     \sa {dynamic-object-creation}{Dynamic Object Creation}
297 */
298
299 QQuickLoader::QQuickLoader(QQuickItem *parent)
300   : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
301 {
302     setFlag(ItemIsFocusScope);
303 }
304
305 QQuickLoader::~QQuickLoader()
306 {
307     Q_D(QQuickLoader);
308     if (d->item) {
309         QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
310         p->removeItemChangeListener(d, watchedChanges);
311     }
312 }
313
314 /*!
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.
318
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.
321
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.
324
325     The \l status of an inactive loader is always \c Null.
326
327     \sa source, sourceComponent
328  */
329 bool QQuickLoader::active() const
330 {
331     Q_D(const QQuickLoader);
332     return d->active;
333 }
334
335 void QQuickLoader::setActive(bool newVal)
336 {
337     Q_D(QQuickLoader);
338     if (d->active != newVal) {
339         d->active = newVal;
340         if (newVal == true) {
341             if (d->loadingFromSource) {
342                 loadFromSource();
343             } else {
344                 loadFromSourceComponent();
345             }
346         } else {
347             // cancel any current incubation
348             if (d->incubator) {
349                 d->incubator->clear();
350                 delete d->itemContext;
351                 d->itemContext = 0;
352             }
353
354             if (d->item) {
355                 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
356                 p->removeItemChangeListener(d, watchedChanges);
357
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);
362                 d->item = 0;
363             }
364             if (d->object) {
365                 d->object->deleteLater();
366                 d->object = 0;
367                 emit itemChanged();
368             }
369             emit statusChanged();
370         }
371         emit activeChanged();
372     }
373 }
374
375
376 /*!
377     \qmlproperty url QtQuick2::Loader::source
378     This property holds the URL of the QML component to instantiate.
379
380     Since 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 QtQuick2::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 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     d->loadingFromSource = false;
477
478     if (d->active)
479         loadFromSourceComponent();
480     else
481         emit sourceComponentChanged();
482 }
483
484 void QQuickLoader::resetSourceComponent()
485 {
486     setSourceComponent(0);
487 }
488
489 void QQuickLoader::loadFromSourceComponent()
490 {
491     Q_D(QQuickLoader);
492     if (!d->component) {
493         emit sourceComponentChanged();
494         emit statusChanged();
495         emit progressChanged();
496         emit itemChanged();
497         return;
498     }
499
500     if (isComponentComplete())
501         d->load();
502 }
503
504 /*!
505     \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
506
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
510     is complete.
511
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.
516
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.
519
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.
522
523     Example:
524     \table
525     \row
526     \li
527     \qml
528     // ExampleComponent.qml
529     import QtQuick 2.0
530     Rectangle {
531         id: rect
532         color: "red"
533         width: 10
534         height: 10
535
536         Behavior on color {
537             NumberAnimation {
538                 target: rect
539                 property: "width"
540                 to: (rect.width + 20)
541                 duration: 0
542             }
543         }
544     }
545     \endqml
546     \li
547     \qml
548     // example.qml
549     import QtQuick 2.0
550     Item {
551         Loader {
552             id: squareLoader
553             onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
554         }
555
556         Component.onCompleted: {
557             squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
558             // will trigger the onLoaded code when complete.
559         }
560     }
561     \endqml
562     \endtable
563
564     \sa source, active
565 */
566 void QQuickLoader::setSource(QQmlV8Function *args)
567 {
568     Q_ASSERT(args);
569     Q_D(QQuickLoader);
570
571     bool ipvError = false;
572     args->returnValue(v8::Undefined());
573     v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
574     if (ipvError)
575         return;
576
577     d->clear();
578     QUrl sourceUrl = d->resolveSourceUrl(args);
579     if (!ipv.IsEmpty()) {
580         d->disposeInitialPropertyValues();
581         d->initialPropertyValues = qPersistentNew(ipv);
582         d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
583     }
584
585     setSource(sourceUrl, false); // already cleared and set ipv above.
586 }
587
588 void QQuickLoaderPrivate::disposeInitialPropertyValues()
589 {
590     if (!initialPropertyValues.IsEmpty())
591         qPersistentDispose(initialPropertyValues);
592     if (!qmlGlobalForIpv.IsEmpty())
593         qPersistentDispose(qmlGlobalForIpv);
594 }
595
596 void QQuickLoaderPrivate::load()
597 {
598     Q_Q(QQuickLoader);
599
600     if (!q->isComponentComplete() || !component)
601         return;
602
603     if (!component->isLoading()) {
604         _q_sourceLoaded();
605     } else {
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();
614         else
615             emit q->sourceComponentChanged();
616         emit q->itemChanged();
617     }
618 }
619
620 void QQuickLoaderIncubator::setInitialState(QObject *o)
621 {
622     loader->setInitialState(o);
623 }
624
625 void QQuickLoaderPrivate::setInitialState(QObject *obj)
626 {
627     Q_Q(QQuickLoader);
628
629     QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
630     if (item) {
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);
640     }
641     if (obj) {
642         QQml_setParent_noEvent(itemContext, obj);
643         QQml_setParent_noEvent(obj, q);
644         itemContext = 0;
645     }
646
647     if (initialPropertyValues.IsEmpty())
648         return;
649
650     QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
651     Q_ASSERT(d && d->engine);
652     d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj);
653 }
654
655 void QQuickLoaderIncubator::statusChanged(Status status)
656 {
657     loader->incubatorStateChanged(status);
658 }
659
660 void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
661 {
662     Q_Q(QQuickLoader);
663     if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
664         return;
665
666     if (status == QQmlIncubator::Ready) {
667         object = incubator->object();
668         item = qmlobject_cast<QQuickItem*>(object);
669         emit q->itemChanged();
670         initResize();
671         incubator->clear();
672     } else if (status == QQmlIncubator::Error) {
673         if (!incubator->errors().isEmpty())
674             QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
675         delete itemContext;
676         itemContext = 0;
677         delete incubator->object();
678         source = QUrl();
679         emit q->itemChanged();
680     }
681     if (loadingFromSource)
682         emit q->sourceChanged();
683     else
684         emit q->sourceComponentChanged();
685     emit q->statusChanged();
686     emit q->progressChanged();
687     if (status == QQmlIncubator::Ready)
688         emit q->loaded();
689     disposeInitialPropertyValues(); // cleanup
690 }
691
692 void QQuickLoaderPrivate::_q_sourceLoaded()
693 {
694     Q_Q(QQuickLoader);
695     if (!component || !component->errors().isEmpty()) {
696         if (component)
697             QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
698         if (loadingFromSource)
699             emit q->sourceChanged();
700         else
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
706         return;
707     }
708
709     QQmlContext *creationContext = component->creationContext();
710     if (!creationContext) creationContext = qmlContext(q);
711     itemContext = new QQmlContext(creationContext);
712     itemContext->setContextObject(q);
713
714     delete incubator;
715     incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
716
717     component->create(*incubator, itemContext);
718
719     if (incubator && incubator->status() == QQmlIncubator::Loading)
720         emit q->statusChanged();
721 }
722
723 /*!
724     \qmlproperty enumeration QtQuick2::Loader::status
725
726     This property holds the status of QML loading.  It can be one of:
727     \list
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
732     \endlist
733
734     Use this status to provide an update or respond to the status change in some way.
735     For example, you could:
736
737     \list
738     \li Trigger a state change:
739     \qml
740         State { name: 'loaded'; when: loader.status == Loader.Ready }
741     \endqml
742
743     \li Implement an \c onStatusChanged signal handler:
744     \qml
745         Loader {
746             id: loader
747             onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
748         }
749     \endqml
750
751     \li Bind to the status value:
752     \qml
753         Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
754     \endqml
755     \endlist
756
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.
759
760     \sa progress
761 */
762
763 QQuickLoader::Status QQuickLoader::status() const
764 {
765     Q_D(const QQuickLoader);
766
767     if (!d->active)
768         return Null;
769
770     if (d->component) {
771         switch (d->component->status()) {
772         case QQmlComponent::Loading:
773             return Loading;
774         case QQmlComponent::Error:
775             return Error;
776         case QQmlComponent::Null:
777             return Null;
778         default:
779             break;
780         }
781     }
782
783     if (d->incubator) {
784         switch (d->incubator->status()) {
785         case QQmlIncubator::Loading:
786             return Loading;
787         case QQmlIncubator::Error:
788             return Error;
789         default:
790             break;
791         }
792     }
793
794     if (d->object)
795         return Ready;
796
797     return d->source.isEmpty() ? Null : Error;
798 }
799
800 void QQuickLoader::componentComplete()
801 {
802     Q_D(QQuickLoader);
803     QQuickItem::componentComplete();
804     if (active()) {
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);
808         }
809         d->load();
810     }
811 }
812
813 /*!
814     \qmlsignal QtQuick2::Loader::onLoaded()
815
816     This handler is called when the \l status becomes \c Loader.Ready, or on successful
817     initial load.
818 */
819
820
821 /*!
822 \qmlproperty real QtQuick2::Loader::progress
823
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.
827
828 \sa status
829 */
830 qreal QQuickLoader::progress() const
831 {
832     Q_D(const QQuickLoader);
833
834     if (d->object)
835         return 1.0;
836
837     if (d->component)
838         return d->component->progress();
839
840     return 0.0;
841 }
842
843 /*!
844 \qmlproperty bool QtQuick2::Loader::asynchronous
845
846 This property holds whether the component will be instantiated asynchronously.
847
848 When used in conjunction with the \l source property, loading and compilation
849 will also be performed in a background thread.
850
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.
856
857 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
858
859 \code
860 Loader {
861     source: "mycomponent.qml"
862     asynchronous: true
863     visible: status == Loader.Ready
864 }
865 \endcode
866
867 Note that this property affects object instantiation only; it is unrelated to
868 loading a component asynchronously via a network.
869 */
870 bool QQuickLoader::asynchronous() const
871 {
872     Q_D(const QQuickLoader);
873     return d->asynchronous;
874 }
875
876 void QQuickLoader::setAsynchronous(bool a)
877 {
878     Q_D(QQuickLoader);
879     if (d->asynchronous == a)
880         return;
881
882     d->asynchronous = a;
883     emit asynchronousChanged();
884 }
885
886 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
887 {
888     Q_Q(QQuickLoader);
889     if (!item || updatingSize)
890         return;
891
892     updatingSize = true;
893
894     if (loaderGeometryChanged && q->widthValid())
895         item->setWidth(q->width());
896     if (loaderGeometryChanged && q->heightValid())
897         item->setHeight(q->height());
898
899     q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
900
901     updatingSize = false;
902 }
903
904 /*!
905     \qmlproperty object QtQuick2::Loader::item
906     This property holds the top-level object that is currently loaded.
907
908     Since QtQuick 2.0 Loader can load any object type.
909 */
910 QObject *QQuickLoader::item() const
911 {
912     Q_D(const QQuickLoader);
913     return d->object;
914 }
915
916 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
917 {
918     Q_D(QQuickLoader);
919     if (newGeometry != oldGeometry) {
920         d->_q_updateSize();
921     }
922     QQuickItem::geometryChanged(newGeometry, oldGeometry);
923 }
924
925 QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV8Function *args)
926 {
927     QV8Engine *v8engine = args->engine();
928     QString arg = v8engine->toString((*args)[0]->ToString());
929     if (arg.isEmpty())
930         return QUrl();
931
932     QQmlContextData *context = args->context();
933     Q_ASSERT(context);
934     return context->resolvedUrl(QUrl(arg));
935 }
936
937 v8::Handle<v8::Object> QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error)
938 {
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()) {
943             *error = true;
944             qmlInfo(loader) << loader->tr("setSource: value is not an object");
945         } else {
946             *error = false;
947             valuemap = v8::Local<v8::Object>::Cast(v);
948         }
949     }
950
951     return valuemap;
952 }
953
954 #include <moc_qquickloader_p.cpp>
955
956 QT_END_NAMESPACE