fdc3db9fb778bea6e875d7e0e430af8a242ecbbd
[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), 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->deleteLater();
122         item = 0;
123     }
124 }
125
126 void QQuickLoaderPrivate::initResize()
127 {
128     QQuickItemPrivate *p = QQuickItemPrivate::get(item);
129     p->addItemChangeListener(this, watchedChanges);
130     _q_updateSize();
131 }
132
133 qreal QQuickLoaderPrivate::getImplicitWidth() const
134 {
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.
139     if (item)
140         return q->widthValid() ? item->implicitWidth() : item->width();
141     return QQuickImplicitSizeItemPrivate::getImplicitWidth();
142 }
143
144 qreal QQuickLoaderPrivate::getImplicitHeight() const
145 {
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.
150     if (item)
151         return q->heightValid() ? item->implicitHeight() : item->height();
152     return QQuickImplicitSizeItemPrivate::getImplicitHeight();
153 }
154
155 /*!
156     \qmlclass Loader QQuickLoader
157     \inqmlmodule QtQuick 2
158     \ingroup qml-utility-elements
159     \inherits Item
160
161     \brief The Loader item allows dynamically loading an Item-based
162     subtree from a URL or Component.
163
164     Loader is used to dynamically load visual QML components.  For loading non-visual
165     components, see \l {Dynamic Object Management in QML}.
166
167     Loader can load a
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.
173
174     Here is a Loader that loads "Page1.qml" as a component when the
175     \l MouseArea is clicked:
176
177     \snippet doc/snippets/qml/loader/simple.qml 0
178
179     The loaded item can be accessed using the \l item property.
180
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.
185
186     \section2 Loader sizing behavior
187
188     Loader is like any other visual item and must be positioned and sized
189     accordingly to become visible.
190
191     \list
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
194     component is loaded.
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.
198     \endlist
199
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
202     to the loaded item.
203
204     \table
205     \row
206     \li sizeloader.qml
207     \li sizeitem.qml
208     \row
209     \li \snippet doc/snippets/qml/loader/sizeloader.qml 0
210     \li \snippet doc/snippets/qml/loader/sizeitem.qml 0
211     \row
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.
214     \endtable
215
216
217     \section2 Receiving signals from loaded items
218
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:
223
224     \table
225     \row
226     \li application.qml
227     \li MyItem.qml
228     \row
229     \li \snippet doc/snippets/qml/loader/connections.qml 0
230     \li \snippet doc/snippets/qml/loader/MyItem.qml 0
231     \endtable
232
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
235     its parent \l Item.
236
237
238     \section2 Focus and key events
239
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.
245
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
249     loaded object:
250
251     \table
252     \row
253     \li application.qml
254     \li KeyReader.qml
255     \row
256     \li \snippet doc/snippets/qml/loader/focus.qml 0
257     \li \snippet doc/snippets/qml/loader/KeyReader.qml 0
258     \endtable
259
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
262     parent \l Rectangle.
263
264     \sa {dynamic-object-creation}{Dynamic Object Creation}
265 */
266
267 QQuickLoader::QQuickLoader(QQuickItem *parent)
268   : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
269 {
270     setFlag(ItemIsFocusScope);
271 }
272
273 QQuickLoader::~QQuickLoader()
274 {
275     Q_D(QQuickLoader);
276     if (d->item) {
277         QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
278         p->removeItemChangeListener(d, watchedChanges);
279     }
280 }
281
282 /*!
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.
286
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.
289
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.
292
293     The \l status of an inactive loader is always \c Null.
294
295     \sa source, sourceComponent
296  */
297 bool QQuickLoader::active() const
298 {
299     Q_D(const QQuickLoader);
300     return d->active;
301 }
302
303 void QQuickLoader::setActive(bool newVal)
304 {
305     Q_D(QQuickLoader);
306     if (d->active != newVal) {
307         d->active = newVal;
308         if (newVal == true) {
309             if (d->loadingFromSource) {
310                 loadFromSource();
311             } else {
312                 loadFromSourceComponent();
313             }
314         } else {
315             // cancel any current incubation
316             if (d->incubator) {
317                 d->incubator->clear();
318                 delete d->itemContext;
319                 d->itemContext = 0;
320             }
321
322             if (d->item) {
323                 QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
324                 p->removeItemChangeListener(d, watchedChanges);
325
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();
331                 d->item = 0;
332                 emit itemChanged();
333             }
334             emit statusChanged();
335         }
336         emit activeChanged();
337     }
338 }
339
340
341 /*!
342     \qmlproperty url QtQuick2::Loader::source
343     This property holds the URL of the QML component to instantiate.
344
345     Note the QML component must be an \l{Item}-based component. The loader
346     cannot load non-visual components.
347
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.
351
352     \sa sourceComponent, status, progress
353 */
354 QUrl QQuickLoader::source() const
355 {
356     Q_D(const QQuickLoader);
357     return d->source;
358 }
359
360 void QQuickLoader::setSource(const QUrl &url)
361 {
362     setSource(url, true); // clear previous values
363 }
364
365 void QQuickLoader::setSource(const QUrl &url, bool needsClear)
366 {
367     Q_D(QQuickLoader);
368     if (d->source == url)
369         return;
370
371     if (needsClear)
372         d->clear();
373
374     d->source = url;
375     d->loadingFromSource = true;
376
377     if (d->active)
378         loadFromSource();
379     else
380         emit sourceChanged();
381 }
382
383 void QQuickLoader::loadFromSource()
384 {
385     Q_D(QQuickLoader);
386     if (d->source.isEmpty()) {
387         emit sourceChanged();
388         emit statusChanged();
389         emit progressChanged();
390         emit itemChanged();
391         return;
392     }
393
394     if (isComponentComplete()) {
395         QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
396         d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
397         d->load();
398     }
399 }
400
401 /*!
402     \qmlproperty Component QtQuick2::Loader::sourceComponent
403     This property holds the \l{Component} to instantiate.
404
405     \qml
406     Item {
407         Component {
408             id: redSquare
409             Rectangle { color: "red"; width: 10; height: 10 }
410         }
411
412         Loader { sourceComponent: redSquare }
413         Loader { sourceComponent: redSquare; x: 10 }
414     }
415     \endqml
416
417     To unload the currently loaded item, set this property to an empty string
418     or \c undefined.
419
420     \sa source, progress
421 */
422
423 QQmlComponent *QQuickLoader::sourceComponent() const
424 {
425     Q_D(const QQuickLoader);
426     return d->component;
427 }
428
429 void QQuickLoader::setSourceComponent(QQmlComponent *comp)
430 {
431     Q_D(QQuickLoader);
432     if (comp == d->component)
433         return;
434
435     d->clear();
436
437     d->component = comp;
438     d->loadingFromSource = false;
439
440     if (d->active)
441         loadFromSourceComponent();
442     else
443         emit sourceComponentChanged();
444 }
445
446 void QQuickLoader::resetSourceComponent()
447 {
448     setSourceComponent(0);
449 }
450
451 void QQuickLoader::loadFromSourceComponent()
452 {
453     Q_D(QQuickLoader);
454     if (!d->component) {
455         emit sourceComponentChanged();
456         emit statusChanged();
457         emit progressChanged();
458         emit itemChanged();
459         return;
460     }
461
462     if (isComponentComplete())
463         d->load();
464 }
465
466 /*!
467     \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
468
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
472     is complete.
473
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.
478
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.
481
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.
484
485     Example:
486     \table
487     \row
488     \li
489     \qml
490     // ExampleComponent.qml
491     import QtQuick 2.0
492     Rectangle {
493         id: rect
494         color: "red"
495         width: 10
496         height: 10
497
498         Behavior on color {
499             NumberAnimation {
500                 target: rect
501                 property: "width"
502                 to: (rect.width + 20)
503                 duration: 0
504             }
505         }
506     }
507     \endqml
508     \li
509     \qml
510     // example.qml
511     import QtQuick 2.0
512     Item {
513         Loader {
514             id: squareLoader
515             onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
516         }
517
518         Component.onCompleted: {
519             squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
520             // will trigger the onLoaded code when complete.
521         }
522     }
523     \endqml
524     \endtable
525
526     \sa source, active
527 */
528 void QQuickLoader::setSource(QQmlV8Function *args)
529 {
530     Q_ASSERT(args);
531     Q_D(QQuickLoader);
532
533     bool ipvError = false;
534     args->returnValue(v8::Undefined());
535     v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
536     if (ipvError)
537         return;
538
539     d->clear();
540     QUrl sourceUrl = d->resolveSourceUrl(args);
541     if (!ipv.IsEmpty()) {
542         d->disposeInitialPropertyValues();
543         d->initialPropertyValues = qPersistentNew(ipv);
544         d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
545     }
546
547     setSource(sourceUrl, false); // already cleared and set ipv above.
548 }
549
550 void QQuickLoaderPrivate::disposeInitialPropertyValues()
551 {
552     if (!initialPropertyValues.IsEmpty())
553         qPersistentDispose(initialPropertyValues);
554     if (!qmlGlobalForIpv.IsEmpty())
555         qPersistentDispose(qmlGlobalForIpv);
556 }
557
558 void QQuickLoaderPrivate::load()
559 {
560     Q_Q(QQuickLoader);
561
562     if (!q->isComponentComplete() || !component)
563         return;
564
565     if (!component->isLoading()) {
566         _q_sourceLoaded();
567     } else {
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();
576         else
577             emit q->sourceComponentChanged();
578         emit q->itemChanged();
579     }
580 }
581
582 void QQuickLoaderIncubator::setInitialState(QObject *o)
583 {
584     loader->setInitialState(o);
585 }
586
587 void QQuickLoaderPrivate::setInitialState(QObject *obj)
588 {
589     Q_Q(QQuickLoader);
590
591     QQuickItem *item = qmlobject_cast<QQuickItem*>(obj);
592     if (item) {
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);
604         itemContext = 0;
605     }
606
607     if (initialPropertyValues.IsEmpty())
608         return;
609
610     QQmlComponentPrivate *d = QQmlComponentPrivate::get(component);
611     Q_ASSERT(d && d->engine);
612     d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj);
613 }
614
615 void QQuickLoaderIncubator::statusChanged(Status status)
616 {
617     loader->incubatorStateChanged(status);
618 }
619
620 void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
621 {
622     Q_Q(QQuickLoader);
623     if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
624         return;
625
626     if (status == QQmlIncubator::Ready) {
627         QObject *obj = incubator->object();
628         item = qmlobject_cast<QQuickItem*>(obj);
629         if (item) {
630             emit q->itemChanged();
631             initResize();
632         } else {
633             qmlInfo(q) << QQuickLoader::tr("Loader does not support loading non-visual elements.");
634             delete itemContext;
635             itemContext = 0;
636             delete obj;
637             emit q->itemChanged();
638         }
639         incubator->clear();
640     } else if (status == QQmlIncubator::Error) {
641         if (!incubator->errors().isEmpty())
642             QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
643         delete itemContext;
644         itemContext = 0;
645         delete incubator->object();
646         source = QUrl();
647         emit q->itemChanged();
648     }
649     if (loadingFromSource)
650         emit q->sourceChanged();
651     else
652         emit q->sourceComponentChanged();
653     emit q->statusChanged();
654     emit q->progressChanged();
655     if (status == QQmlIncubator::Ready)
656         emit q->loaded();
657     disposeInitialPropertyValues(); // cleanup
658 }
659
660 void QQuickLoaderPrivate::_q_sourceLoaded()
661 {
662     Q_Q(QQuickLoader);
663     if (!component || !component->errors().isEmpty()) {
664         if (component)
665             QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
666         if (loadingFromSource)
667             emit q->sourceChanged();
668         else
669             emit q->sourceComponentChanged();
670         emit q->statusChanged();
671         emit q->progressChanged();
672         disposeInitialPropertyValues(); // cleanup
673         return;
674     }
675
676     QQmlContext *creationContext = component->creationContext();
677     if (!creationContext) creationContext = qmlContext(q);
678     itemContext = new QQmlContext(creationContext);
679     itemContext->setContextObject(q);
680
681     delete incubator;
682     incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
683
684     component->create(*incubator, itemContext);
685
686     if (incubator && incubator->status() == QQmlIncubator::Loading)
687         emit q->statusChanged();
688 }
689
690 /*!
691     \qmlproperty enumeration QtQuick2::Loader::status
692
693     This property holds the status of QML loading.  It can be one of:
694     \list
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
699     \endlist
700
701     Use this status to provide an update or respond to the status change in some way.
702     For example, you could:
703
704     \list
705     \li Trigger a state change:
706     \qml
707         State { name: 'loaded'; when: loader.status == Loader.Ready }
708     \endqml
709
710     \li Implement an \c onStatusChanged signal handler:
711     \qml
712         Loader {
713             id: loader
714             onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
715         }
716     \endqml
717
718     \li Bind to the status value:
719     \qml
720         Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
721     \endqml
722     \endlist
723
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.
726
727     \sa progress
728 */
729
730 QQuickLoader::Status QQuickLoader::status() const
731 {
732     Q_D(const QQuickLoader);
733
734     if (!d->active)
735         return Null;
736
737     if (d->component) {
738         switch (d->component->status()) {
739         case QQmlComponent::Loading:
740             return Loading;
741         case QQmlComponent::Error:
742             return Error;
743         case QQmlComponent::Null:
744             return Null;
745         default:
746             break;
747         }
748     }
749
750     if (d->incubator) {
751         switch (d->incubator->status()) {
752         case QQmlIncubator::Loading:
753             return Loading;
754         case QQmlIncubator::Error:
755             return Error;
756         default:
757             break;
758         }
759     }
760
761     if (d->item)
762         return Ready;
763
764     return d->source.isEmpty() ? Null : Error;
765 }
766
767 void QQuickLoader::componentComplete()
768 {
769     Q_D(QQuickLoader);
770     QQuickItem::componentComplete();
771     if (active()) {
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);
775         }
776         d->load();
777     }
778 }
779
780 /*!
781     \qmlsignal QtQuick2::Loader::onLoaded()
782
783     This handler is called when the \l status becomes \c Loader.Ready, or on successful
784     initial load.
785 */
786
787
788 /*!
789 \qmlproperty real QtQuick2::Loader::progress
790
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.
794
795 \sa status
796 */
797 qreal QQuickLoader::progress() const
798 {
799     Q_D(const QQuickLoader);
800
801     if (d->item)
802         return 1.0;
803
804     if (d->component)
805         return d->component->progress();
806
807     return 0.0;
808 }
809
810 /*!
811 \qmlproperty bool QtQuick2::Loader::asynchronous
812
813 This property holds whether the component will be instantiated asynchronously.
814
815 When used in conjunction with the \l source property, loading and compilation
816 will also be performed in a background thread.
817
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.
823
824 To avoid seeing the items loading progressively set \c visible appropriately, e.g.
825
826 \code
827 Loader {
828     source: "mycomponent.qml"
829     asynchronous: true
830     visible: status == Loader.Ready
831 }
832 \endcode
833
834 Note that this property affects object instantiation only; it is unrelated to
835 loading a component asynchronously via a network.
836 */
837 bool QQuickLoader::asynchronous() const
838 {
839     Q_D(const QQuickLoader);
840     return d->asynchronous;
841 }
842
843 void QQuickLoader::setAsynchronous(bool a)
844 {
845     Q_D(QQuickLoader);
846     if (d->asynchronous == a)
847         return;
848
849     d->asynchronous = a;
850     emit asynchronousChanged();
851 }
852
853 void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
854 {
855     Q_Q(QQuickLoader);
856     if (!item || updatingSize)
857         return;
858
859     updatingSize = true;
860
861     if (loaderGeometryChanged && q->widthValid())
862         item->setWidth(q->width());
863     if (loaderGeometryChanged && q->heightValid())
864         item->setHeight(q->height());
865
866     q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
867
868     updatingSize = false;
869 }
870
871 /*!
872     \qmlproperty Item QtQuick2::Loader::item
873     This property holds the top-level item that is currently loaded.
874 */
875 QQuickItem *QQuickLoader::item() const
876 {
877     Q_D(const QQuickLoader);
878     return d->item;
879 }
880
881 void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
882 {
883     Q_D(QQuickLoader);
884     if (newGeometry != oldGeometry) {
885         d->_q_updateSize();
886     }
887     QQuickItem::geometryChanged(newGeometry, oldGeometry);
888 }
889
890 QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV8Function *args)
891 {
892     QV8Engine *v8engine = args->engine();
893     QString arg = v8engine->toString((*args)[0]->ToString());
894     if (arg.isEmpty())
895         return QUrl();
896
897     QQmlContextData *context = args->context();
898     Q_ASSERT(context);
899     return context->resolvedUrl(QUrl(arg));
900 }
901
902 v8::Handle<v8::Object> QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error)
903 {
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()) {
908             *error = true;
909             qmlInfo(loader) << loader->tr("setSource: value is not an object");
910         } else {
911             *error = false;
912             valuemap = v8::Local<v8::Object>::Cast(v);
913         }
914     }
915
916     return valuemap;
917 }
918
919 #include <moc_qquickloader_p.cpp>
920
921 QT_END_NAMESPACE