1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickloader_p_p.h"
5
6#include <QtQml/qqmlinfo.h>
7#include <QtQml/qqmlcomponent.h>
8
9#include <private/qqmlengine_p.h>
10#include <private/qqmlglobal_p.h>
11
12#include <private/qqmlcomponent_p.h>
13#include <private/qqmlincubator_p.h>
14
15QT_BEGIN_NAMESPACE
16
17static const QQuickItemPrivate::ChangeTypes watchedChanges
18 = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
19
20QQuickLoaderPrivate::QQuickLoaderPrivate()
21 : item(nullptr), object(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false),
22 active(true), loadingFromSource(false), asynchronous(false), status(computeStatus())
23{
24}
25
26QQuickLoaderPrivate::~QQuickLoaderPrivate()
27{
28 delete itemContext;
29 itemContext = nullptr;
30 delete incubator;
31 disposeInitialPropertyValues();
32}
33
34void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
35 const QRectF &oldGeometry)
36{
37 if (resizeItem == item)
38 _q_updateSize(loaderGeometryChanged: false);
39 QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, oldGeometry);
40}
41
42void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *)
43{
44 Q_Q(QQuickLoader);
45 q->setImplicitWidth(getImplicitWidth());
46}
47
48void QQuickLoaderPrivate::itemImplicitHeightChanged(QQuickItem *)
49{
50 Q_Q(QQuickLoader);
51 q->setImplicitHeight(getImplicitHeight());
52}
53
54void QQuickLoaderPrivate::clear()
55{
56 Q_Q(QQuickLoader);
57 disposeInitialPropertyValues();
58
59 if (incubator)
60 incubator->clear();
61
62 delete itemContext;
63 itemContext = nullptr;
64
65 // Prevent any bindings from running while waiting for deletion. Without
66 // this we may get transient errors from use of 'parent', for example.
67 QQmlContext *context = qmlContext(object);
68 if (context)
69 QQmlContextData::get(context)->clearContextRecursively();
70
71 if (loadingFromSource && component) {
72 // disconnect since we deleteLater
73 QObject::disconnect(sender: component, SIGNAL(statusChanged(QQmlComponent::Status)),
74 receiver: q, SLOT(_q_sourceLoaded()));
75 QObject::disconnect(sender: component, SIGNAL(progressChanged(qreal)),
76 receiver: q, SIGNAL(progressChanged()));
77 component->deleteLater();
78 component.setObject(obj: nullptr, parent: q);
79 } else if (component) {
80 component.setObject(obj: nullptr, parent: q);
81 }
82 source = QUrl();
83
84 if (item) {
85 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
86 p->removeItemChangeListener(this, types: watchedChanges);
87
88 // We can't delete immediately because our item may have triggered
89 // the Loader to load a different item.
90 item->setParentItem(nullptr);
91 item->setVisible(false);
92 item = nullptr;
93 }
94 if (object) {
95 object->deleteLater();
96 object = nullptr;
97 }
98}
99
100void QQuickLoaderPrivate::initResize()
101{
102 if (!item)
103 return;
104 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
105 p->addItemChangeListener(listener: this, types: watchedChanges);
106 _q_updateSize();
107}
108
109qreal QQuickLoaderPrivate::getImplicitWidth() const
110{
111 Q_Q(const QQuickLoader);
112 // If the Loader has a valid width then Loader has set an explicit width on the
113 // item, and we want the item's implicitWidth. If the Loader's width has
114 // not been set then its implicitWidth is the width of the item.
115 if (item)
116 return q->widthValid() ? item->implicitWidth() : item->width();
117 return QQuickImplicitSizeItemPrivate::getImplicitWidth();
118}
119
120qreal QQuickLoaderPrivate::getImplicitHeight() const
121{
122 Q_Q(const QQuickLoader);
123 // If the Loader has a valid height then Loader has set an explicit height on the
124 // item, and we want the item's implicitHeight. If the Loader's height has
125 // not been set then its implicitHeight is the height of the item.
126 if (item)
127 return q->heightValid() ? item->implicitHeight() : item->height();
128 return QQuickImplicitSizeItemPrivate::getImplicitHeight();
129}
130
131/*!
132 \qmltype Loader
133 \nativetype QQuickLoader
134 \inqmlmodule QtQuick
135 \ingroup qtquick-dynamic
136 \inherits Item
137
138 \brief Allows dynamic loading of a subtree from a URL or Component.
139
140 Loader is used to dynamically load QML components.
141
142 Loader can load a
143 QML file (using the \l source property) or a \l Component object (using
144 the \l sourceComponent property). It is useful for delaying the creation
145 of a component until it is required: for example, when a component should
146 be created on demand, or when a component should not be created
147 unnecessarily for performance reasons.
148
149 Here is a Loader that loads "Page1.qml" as a component when the
150 \l MouseArea is clicked:
151
152 \snippet qml/loader/simple.qml 0
153
154 The loaded object can be accessed using the \l item property.
155
156 If the \l source or \l sourceComponent changes, any previously instantiated
157 items are destroyed. Setting \l source to an empty string or setting
158 \l sourceComponent to \c undefined destroys the currently loaded object,
159 freeing resources and leaving the Loader empty.
160
161 \section2 Loader Sizing Behavior
162
163 When used to load visual types, Loader applies the following sizing rules:
164
165 \list
166 \li If an explicit size is not specified for the Loader, the Loader
167 is automatically resized to the size of the loaded item once the
168 component is loaded.
169 \li If the size of the Loader is specified explicitly by setting
170 the width, height or by anchoring, the loaded item will be resized
171 to the size of the Loader.
172 \endlist
173
174 In both scenarios the size of the item and the Loader are identical.
175 This ensures that anchoring to the Loader is equivalent to anchoring
176 to the loaded item.
177
178 \table
179 \row
180 \li sizeloader.qml
181 \li sizeitem.qml
182 \row
183 \li \snippet qml/loader/sizeloader.qml 0
184 \li \snippet qml/loader/sizeitem.qml 0
185 \row
186 \li The red rectangle will be sized to the size of the root item.
187 \li The red rectangle will be 50x50, centered in the root item.
188 \endtable
189
190 If the source component is not an Item type, Loader does not apply any
191 special sizing rules.
192
193 \section2 Receiving Signals from Loaded Objects
194
195 Any signals emitted from the loaded object can be received using the
196 \l Connections type. For example, the following \c application.qml
197 loads \c MyItem.qml, and is able to receive the \c message signal from
198 the loaded item through a \l Connections object:
199
200 \table 70%
201 \row
202 \li application.qml
203 \li MyItem.qml
204 \row
205 \li \snippet qml/loader/connections.qml 0
206 \li \snippet qml/loader/MyItem.qml 0
207 \endtable
208
209 Alternatively, since \c MyItem.qml is loaded within the scope of the
210 Loader, it could also directly call any function defined in the Loader or
211 its parent \l Item.
212
213
214 \section2 Focus and Key Events
215
216 Loader is a focus scope. Its \l {Item::}{focus} property must be set to
217 \c true for any of its children to get the \e {active focus}. (See
218 \l{Keyboard Focus in Qt Quick}
219 for more details.) Any key events received in the loaded item should likely
220 also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
221
222 For example, the following \c application.qml loads \c KeyReader.qml when
223 the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
224 set to \c true for the Loader as well as the \l Item in the dynamically
225 loaded object:
226
227 \table
228 \row
229 \li application.qml
230 \li KeyReader.qml
231 \row
232 \li \snippet qml/loader/focus.qml 0
233 \li \snippet qml/loader/KeyReader.qml 0
234 \endtable
235
236 Once \c KeyReader.qml is loaded, it accepts key events and sets
237 \c event.accepted to \c true so that the event is not propagated to the
238 parent \l Rectangle.
239
240 Since \c {QtQuick 2.0}, Loader can also load non-visual components.
241
242 \section2 Using a Loader within a View Delegate
243
244 In some cases you may wish to use a Loader within a view delegate to improve delegate
245 loading performance. This works well in most cases, but there is one important issue to
246 be aware of related to the \l{QtQml::Component#Creation Context}{creation context} of a Component.
247
248 In the following example, the \c index context property inserted by the ListView into \c delegateComponent's
249 context will be inaccessible to Text, as the Loader will use the creation context of \c myComponent as the parent
250 context when instantiating it, and \c index does not refer to anything within that context chain.
251
252 \snippet qml/loader/creationContext1.qml 0
253
254 In this situation we can either move the component inline,
255
256 \snippet qml/loader/creationContext2.qml 0
257
258 into a separate file,
259
260 \snippet qml/loader/creationContext3.qml 0
261
262 or explicitly set the required information as a property of the Loader (this works because the
263 Loader sets itself as the context object for the component it is loading).
264
265 \snippet qml/loader/creationContext4.qml 0
266
267 \sa {dynamic-object-creation}{Dynamic Object Creation}
268*/
269
270QQuickLoader::QQuickLoader(QQuickItem *parent)
271 : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent)
272{
273 setFlag(flag: ItemIsFocusScope);
274}
275
276QQuickLoader::~QQuickLoader()
277{
278 Q_D(QQuickLoader);
279 d->clear();
280}
281
282/*!
283 \qmlproperty bool QtQuick::Loader::active
284 This property is \c true if the Loader is currently active.
285 The default value for this 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 */
297bool QQuickLoader::active() const
298{
299 Q_D(const QQuickLoader);
300 return d->active;
301}
302
303void QQuickLoader::setActive(bool newVal)
304{
305 Q_D(QQuickLoader);
306 if (d->active == newVal)
307 return;
308
309 d->active = newVal;
310 if (newVal == true) {
311 if (d->loadingFromSource) {
312 loadFromSource();
313 } else {
314 loadFromSourceComponent();
315 }
316 } else {
317 // cancel any current incubation
318 if (d->incubator) {
319 d->incubator->clear();
320 delete d->itemContext;
321 d->itemContext = nullptr;
322 }
323
324 // Prevent any bindings from running while waiting for deletion. Without
325 // this we may get transient errors from use of 'parent', for example.
326 QQmlContext *context = qmlContext(d->object);
327 if (context)
328 QQmlContextData::get(context)->clearContextRecursively();
329
330 if (d->item) {
331 QQuickItemPrivate *p = QQuickItemPrivate::get(item: d->item);
332 p->removeItemChangeListener(d, types: watchedChanges);
333
334 // We can't delete immediately because our item may have triggered
335 // the Loader to load a different item.
336 d->item->setParentItem(nullptr);
337 d->item->setVisible(false);
338 d->item = nullptr;
339 }
340 if (d->object) {
341 d->object->deleteLater();
342 d->object = nullptr;
343 emit itemChanged();
344 }
345 d->updateStatus();
346 }
347 emit activeChanged();
348}
349
350
351/*!
352 \qmlproperty url QtQuick::Loader::source
353 This property holds the URL of the QML component to instantiate.
354
355 Since \c {QtQuick 2.0}, Loader is able to load any type of object; it
356 is not restricted to Item types.
357
358 To unload the currently loaded object, set this property to an empty string,
359 or set \l sourceComponent to \c undefined. Setting \c source to a
360 new URL will also cause the item created by the previous URL to be unloaded.
361
362 \sa sourceComponent, status, progress
363*/
364QUrl QQuickLoader::source() const
365{
366 Q_D(const QQuickLoader);
367 return d->source;
368}
369
370void QQuickLoader::setSourceWithoutResolve(const QUrl &url)
371{
372 setSource(sourceUrl: url, needsClear: true); // clear previous values
373}
374
375void QQuickLoader::setSource(const QUrl &url, bool needsClear)
376{
377 Q_D(QQuickLoader);
378 if (d->source == url)
379 return;
380
381 if (needsClear)
382 d->clear();
383
384 d->source = url;
385 d->loadingFromSource = true;
386
387 if (d->active)
388 loadFromSource();
389 else
390 emit sourceChanged();
391}
392
393void QQuickLoader::loadFromSource()
394{
395 Q_D(QQuickLoader);
396 if (d->source.isEmpty()) {
397 emit sourceChanged();
398 d->updateStatus();
399 emit progressChanged();
400 emit itemChanged();
401 return;
402 }
403
404 if (isComponentComplete()) {
405 if (!d->component)
406 d->createComponent();
407 d->load();
408 }
409}
410
411/*!
412 \qmlproperty Component QtQuick::Loader::sourceComponent
413 This property holds the \l{Component} to instantiate.
414
415 \qml
416 Item {
417 Component {
418 id: redSquare
419 Rectangle { color: "red"; width: 10; height: 10 }
420 }
421
422 Loader { sourceComponent: redSquare }
423 Loader { sourceComponent: redSquare; x: 10 }
424 }
425 \endqml
426
427 To unload the currently loaded object, set this property to \c undefined.
428
429 Since \c {QtQuick 2.0}, Loader is able to load any type of object; it
430 is not restricted to Item types.
431
432 \sa source, progress
433*/
434
435QQmlComponent *QQuickLoader::sourceComponent() const
436{
437 Q_D(const QQuickLoader);
438 return d->component;
439}
440
441void QQuickLoader::setSourceComponent(QQmlComponent *comp)
442{
443 Q_D(QQuickLoader);
444 if (comp == d->component)
445 return;
446
447 d->clear();
448
449 d->component.setObject(obj: comp, parent: this);
450 d->loadingFromSource = false;
451
452 if (d->active)
453 loadFromSourceComponent();
454 else
455 emit sourceComponentChanged();
456}
457
458void QQuickLoader::resetSourceComponent()
459{
460 setSourceComponent(nullptr);
461}
462
463void QQuickLoader::loadFromSourceComponent()
464{
465 Q_D(QQuickLoader);
466 if (!d->component) {
467 emit sourceComponentChanged();
468 d->updateStatus();
469 emit progressChanged();
470 emit itemChanged();
471 return;
472 }
473
474 if (isComponentComplete())
475 d->load();
476}
477
478
479QUrl QQuickLoader::setSourceUrlHelper(const QUrl &unresolvedUrl)
480{
481 Q_D(QQuickLoader);
482
483 // 1. If setSource is called with a valid url, clear the old component and its corresponding url
484 // 2. If setSource is called with an invalid url(e.g. empty url), clear the old component but
485 // hold the url for old one.(we will compare it with new url later and may update status of loader to Loader.Null)
486 QUrl oldUrl = d->source;
487 d->clear();
488 QUrl sourceUrl = qmlEngine(this)->handle()->callingQmlContext()->resolvedUrl(unresolvedUrl);
489 if (!sourceUrl.isValid())
490 d->source = oldUrl;
491 return sourceUrl;
492}
493
494/*!
495 \qmlmethod object QtQuick::Loader::setSource(url source, object properties)
496
497 Creates an object instance of the given \a source component that will have
498 the given \a properties. The \a properties argument is optional. The instance
499 will be accessible via the \l item property once loading and instantiation
500 is complete.
501
502 If the \l active property is \c false at the time when this function is called,
503 the given \a source component will not be loaded but the \a source and initial
504 \a properties will be cached. When the loader is made \l active, an instance of
505 the \a source component will be created with the initial \a properties set.
506
507 Setting the initial property values of an instance of a component in this manner
508 will \b{not} trigger any associated \l{Behavior}s.
509
510 Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
511 is changed after calling this function but prior to setting the loader \l active.
512
513 Example:
514 \table 70%
515 \row
516 \li
517 \qml
518 // ExampleComponent.qml
519 import QtQuick 2.0
520 Rectangle {
521 id: rect
522 color: "red"
523 width: 10
524 height: 10
525
526 Behavior on color {
527 NumberAnimation {
528 target: rect
529 property: "width"
530 to: (rect.width + 20)
531 duration: 0
532 }
533 }
534 }
535 \endqml
536 \li
537 \qml
538 // example.qml
539 import QtQuick 2.0
540 Item {
541 Loader {
542 id: squareLoader
543 onLoaded: console.log(squareLoader.item.width);
544 // prints [10], not [30]
545 }
546
547 Component.onCompleted: {
548 squareLoader.setSource("ExampleComponent.qml",
549 { "color": "blue" });
550 // will trigger the onLoaded code when complete.
551 }
552 }
553 \endqml
554 \endtable
555
556 \sa source, active
557*/
558void QQuickLoader::setSource(const QUrl &source, QJSValue properties)
559{
560 Q_D(QQuickLoader);
561
562 if (!(properties.isArray() || properties.isObject())) {
563 qmlWarning(me: this) << QQuickLoader::tr(s: "setSource: value is not an object");
564 return;
565 }
566
567 QUrl sourceUrl = setSourceUrlHelper(source);
568
569 d->disposeInitialPropertyValues();
570 auto engine = qmlEngine(this)->handle();
571 d->initialPropertyValues.set(engine, value: QJSValuePrivate::takeManagedValue(jsval: &properties)->asReturnedValue());
572 d->qmlCallingContext.set(engine, obj: engine->qmlContext());
573
574 setSource(url: sourceUrl, needsClear: false); // already cleared and set ipv above.
575}
576
577void QQuickLoader::setSource(const QUrl &source)
578{
579 Q_D(QQuickLoader);
580
581 QUrl sourceUrl = setSourceUrlHelper(source);
582
583 d->disposeInitialPropertyValues();
584 auto engine = qmlEngine(this)->handle();
585 d->qmlCallingContext.set(engine, obj: engine->qmlContext());
586
587 setSource(url: sourceUrl, needsClear: false); // already cleared and set ipv above.
588}
589
590void QQuickLoaderPrivate::disposeInitialPropertyValues()
591{
592 initialPropertyValues.clear();
593}
594
595void QQuickLoaderPrivate::load()
596{
597 Q_Q(QQuickLoader);
598
599 if (!q->isComponentComplete() || !component)
600 return;
601
602 if (!component->isLoading()) {
603 _q_sourceLoaded();
604 } else {
605 QObject::connect(sender: component, SIGNAL(statusChanged(QQmlComponent::Status)),
606 receiver: q, SLOT(_q_sourceLoaded()));
607 QObject::connect(sender: component, SIGNAL(progressChanged(qreal)),
608 receiver: q, SIGNAL(progressChanged()));
609 updateStatus();
610 emit q->progressChanged();
611 if (loadingFromSource)
612 emit q->sourceChanged();
613 else
614 emit q->sourceComponentChanged();
615 emit q->itemChanged();
616 }
617}
618
619void QQuickLoaderIncubator::setInitialState(QObject *o)
620{
621 loader->setInitialState(o);
622}
623
624void QQuickLoaderPrivate::setInitialState(QObject *obj)
625{
626 Q_Q(QQuickLoader);
627
628 QQuickItem *item = qmlobject_cast<QQuickItem*>(object: obj);
629 if (item) {
630 // If the item doesn't have an explicit size, but the Loader
631 // does, then set the item's size now before bindings are
632 // evaluated, otherwise we will end up resizing the item
633 // later and triggering any affected bindings/anchors.
634 if (widthValid() && !QQuickItemPrivate::get(item)->widthValid())
635 item->setWidth(q->width());
636 if (heightValid() && !QQuickItemPrivate::get(item)->heightValid())
637 item->setHeight(q->height());
638 item->setParentItem(q);
639 }
640 if (obj) {
641 if (itemContext)
642 QQml_setParent_noEvent(object: itemContext, parent: obj);
643 QQml_setParent_noEvent(object: obj, parent: q);
644 itemContext = nullptr;
645 }
646
647 if (initialPropertyValues.isUndefined())
648 return;
649
650 QQmlComponentPrivate *d = QQmlComponentPrivate::get(c: component);
651 Q_ASSERT(d && d->engine());
652 QV4::ExecutionEngine *v4 = d->engine()->handle();
653 Q_ASSERT(v4);
654 QV4::Scope scope(v4);
655 QV4::ScopedValue ipv(scope, initialPropertyValues.value());
656 QV4::Scoped<QV4::QmlContext> qmlContext(scope, qmlCallingContext.value());
657 auto incubatorPriv = QQmlIncubatorPrivate::get(incubator);
658 d->initializeObjectWithInitialProperties(qmlContext, valuemap: ipv, toCreate: obj, requiredProperties: incubatorPriv->requiredProperties());
659}
660
661void QQuickLoaderIncubator::statusChanged(Status status)
662{
663 loader->incubatorStateChanged(status);
664}
665
666void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
667{
668 Q_Q(QQuickLoader);
669 if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
670 return;
671
672 if (status == QQmlIncubator::Ready) {
673 object = incubator->object();
674 item = qmlobject_cast<QQuickItem*>(object);
675 emit q->itemChanged();
676 initResize();
677 incubator->clear();
678 } else if (status == QQmlIncubator::Error) {
679 if (!incubator->errors().isEmpty())
680 QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
681 delete itemContext;
682 itemContext = nullptr;
683 delete incubator->object();
684 source = QUrl();
685 emit q->itemChanged();
686 }
687 if (loadingFromSource)
688 emit q->sourceChanged();
689 else
690 emit q->sourceComponentChanged();
691 updateStatus();
692 emit q->progressChanged();
693 if (status == QQmlIncubator::Ready)
694 emit q->loaded();
695}
696
697void 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 updateStatus();
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 if (!active)
715 return;
716
717 QQmlContext *creationContext = component->creationContext();
718 if (!creationContext)
719 creationContext = qmlContext(q);
720
721 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(c: component);
722 QQmlContext *context = [&](){
723 if (cp->isBound())
724 return creationContext;
725 itemContext = new QQmlContext(creationContext);
726 itemContext->setContextObject(q);
727 return itemContext;
728 }();
729
730 delete incubator;
731 incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
732
733 component->create(*incubator, context);
734
735 if (incubator && incubator->status() == QQmlIncubator::Loading)
736 updateStatus();
737}
738
739/*!
740 \qmlproperty enumeration QtQuick::Loader::status
741
742 This property holds the status of QML loading. It can be one of:
743 \list
744 \li Loader.Null - the loader is inactive or no QML source has been set
745 \li Loader.Ready - the QML source has been loaded
746 \li Loader.Loading - the QML source is currently being loaded
747 \li Loader.Error - an error occurred while loading the QML source
748 \endlist
749
750 Use this status to provide an update or respond to the status change in some way.
751 For example, you could:
752
753 \list
754 \li Trigger a state change:
755 \qml
756 State { name: 'loaded'; when: loader.status == Loader.Ready }
757 \endqml
758
759 \li Implement an \c onStatusChanged signal handler:
760 \qml
761 Loader {
762 id: loader
763 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
764 }
765 \endqml
766
767 \li Bind to the status value:
768 \qml
769 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
770 \endqml
771 \endlist
772
773 Note that if the source is a local file, the status will initially be Ready (or Error). While
774 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
775
776 \sa progress
777*/
778
779QQuickLoader::Status QQuickLoader::status() const
780{
781 Q_D(const QQuickLoader);
782
783 return static_cast<Status>(d->status);
784}
785
786void QQuickLoader::componentComplete()
787{
788 Q_D(QQuickLoader);
789 QQuickItem::componentComplete();
790 if (active() && (status() != Ready)) {
791 if (d->loadingFromSource)
792 d->createComponent();
793 d->load();
794 }
795}
796
797void QQuickLoader::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
798{
799 switch (change) {
800 case ItemChildAddedChange:
801 Q_ASSERT(value.item);
802 if (value.item->flags().testFlag(flag: QQuickItem::ItemObservesViewport))
803 // Re-trigger the parent traversal to get subtreeTransformChangedEnabled turned on
804 value.item->setFlag(flag: QQuickItem::ItemObservesViewport);
805 break;
806 default:
807 break;
808 }
809 QQuickItem::itemChange(change, value);
810}
811
812/*!
813 \qmlsignal QtQuick::Loader::loaded()
814
815 This signal is emitted when the \l status becomes \c Loader.Ready, or on successful
816 initial load.
817*/
818
819
820/*!
821\qmlproperty real QtQuick::Loader::progress
822
823This property holds the progress of loading QML data from the network, from
8240.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
825this value will rapidly change from 0 to 1.
826
827\sa status
828*/
829qreal QQuickLoader::progress() const
830{
831 Q_D(const QQuickLoader);
832
833 if (d->object)
834 return 1.0;
835
836 if (d->component)
837 return d->component->progress();
838
839 return 0.0;
840}
841
842/*!
843\qmlproperty bool QtQuick::Loader::asynchronous
844
845This property holds whether the component will be instantiated asynchronously.
846By default it is \c false.
847
848When used in conjunction with the \l source property, loading and compilation
849will also be performed in a background thread.
850
851Loading asynchronously creates the objects declared by the component
852across multiple frames, and reduces the
853likelihood of glitches in animation. When loading asynchronously the status
854will 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
857Changing the value of this property to \c false while an asynchronous load is in
858progress will force immediate, synchronous completion. This allows beginning an
859asynchronous load and then forcing completion if the Loader content must be
860accessed before the asynchronous load has completed.
861
862To avoid seeing the items loading progressively set \c visible appropriately, e.g.
863
864\code
865Loader {
866 source: "mycomponent.qml"
867 asynchronous: true
868 visible: status == Loader.Ready
869}
870\endcode
871
872Note that this property affects object instantiation only; it is unrelated to
873loading a component asynchronously via a network.
874*/
875bool QQuickLoader::asynchronous() const
876{
877 Q_D(const QQuickLoader);
878 return d->asynchronous;
879}
880
881void QQuickLoader::setAsynchronous(bool a)
882{
883 Q_D(QQuickLoader);
884 if (d->asynchronous == a)
885 return;
886
887 d->asynchronous = a;
888
889 if (!d->asynchronous && isComponentComplete() && d->active) {
890 if (d->loadingFromSource && d->component && d->component->isLoading()) {
891 // Force a synchronous component load
892 QUrl currentSource = d->source;
893 d->clear();
894 d->source = currentSource;
895 loadFromSource();
896 } else if (d->incubator && d->incubator->isLoading()) {
897 d->incubator->forceCompletion();
898 }
899 }
900
901 emit asynchronousChanged();
902}
903
904void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
905{
906 Q_Q(QQuickLoader);
907 if (!item)
908 return;
909
910 const bool needToUpdateWidth = loaderGeometryChanged && q->widthValid();
911 const bool needToUpdateHeight = loaderGeometryChanged && q->heightValid();
912
913 if (needToUpdateWidth && needToUpdateHeight) {
914 /* setSize keeps bindings intact (for backwards compatibility reasons),
915 but here we actually want the loader to control the size, so any
916 prexisting bindings ought to be removed
917 */
918 auto *itemPriv = QQuickItemPrivate::get(item);
919 // takeBinding would work without the check, but this is more efficient
920 // for the common case where we don't have a binding
921 if (itemPriv->width.hasBinding())
922 itemPriv->width.takeBinding();
923 if (itemPriv->height.hasBinding())
924 itemPriv->height.takeBinding();
925 item->setSize(QSizeF(q->width(), q->height()));
926 } else if (needToUpdateWidth) {
927 item->setWidth(q->width());
928 } else if (needToUpdateHeight) {
929 item->setHeight(q->height());
930 }
931
932 if (updatingSize)
933 return;
934
935 updatingSize = true;
936
937 q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
938
939 updatingSize = false;
940}
941
942/*!
943 \qmlproperty QtObject QtQuick::Loader::item
944 This property holds the top-level object that is currently loaded.
945
946 Since \c {QtQuick 2.0}, Loader can load any object type.
947*/
948QObject *QQuickLoader::item() const
949{
950 Q_D(const QQuickLoader);
951 return d->object;
952}
953
954void QQuickLoader::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
955{
956 Q_D(QQuickLoader);
957 if (newGeometry != oldGeometry) {
958 d->_q_updateSize();
959 }
960 QQuickItem::geometryChange(newGeometry, oldGeometry);
961}
962
963QQuickLoader::Status QQuickLoaderPrivate::computeStatus() const
964{
965 if (!active)
966 return QQuickLoader::Status::Null;
967
968 if (component) {
969 switch (component->status()) {
970 case QQmlComponent::Loading:
971 return QQuickLoader::Status::Loading;
972 case QQmlComponent::Error:
973 return QQuickLoader::Status::Error;
974 case QQmlComponent::Null:
975 return QQuickLoader::Status::Null;
976 default:
977 break;
978 }
979 }
980
981 if (incubator) {
982 switch (incubator->status()) {
983 case QQmlIncubator::Loading:
984 return QQuickLoader::Status::Loading;
985 case QQmlIncubator::Error:
986 return QQuickLoader::Status::Error;
987 default:
988 break;
989 }
990 }
991
992 if (object)
993 return QQuickLoader::Status::Ready;
994
995 return source.isEmpty() ? QQuickLoader::Status::Null : QQuickLoader::Status::Error;
996}
997
998void QQuickLoaderPrivate::updateStatus()
999{
1000 Q_Q(QQuickLoader);
1001 auto newStatus = computeStatus();
1002 if (status != newStatus) {
1003 status = newStatus;
1004 emit q->statusChanged();
1005 }
1006}
1007
1008void QQuickLoaderPrivate::createComponent()
1009{
1010 Q_Q(QQuickLoader);
1011 const QQmlComponent::CompilationMode mode = asynchronous
1012 ? QQmlComponent::Asynchronous
1013 : QQmlComponent::PreferSynchronous;
1014 if (QQmlContext *context = qmlContext(q)) {
1015 if (QQmlEngine *engine = context->engine()) {
1016 component.setObject(obj: new QQmlComponent(
1017 engine, context->resolvedUrl(source), mode, q), parent: q);
1018 return;
1019 }
1020 }
1021
1022 qmlWarning(me: q) << "createComponent: Cannot find a QML engine.";
1023}
1024
1025QT_END_NAMESPACE
1026
1027#include <moc_qquickloader_p.cpp>
1028

source code of qtdeclarative/src/quick/items/qquickloader.cpp