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

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