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 \nativetype 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 emit q->itemChanged();
677 initResize();
678 incubator->clear();
679 } else if (status == QQmlIncubator::Error) {
680 if (!incubator->errors().isEmpty())
681 QQmlEnginePrivate::warning(qmlEngine(q), incubator->errors());
682 delete itemContext;
683 itemContext = nullptr;
684 delete incubator->object();
685 source = QUrl();
686 emit q->itemChanged();
687 }
688 if (loadingFromSource)
689 emit q->sourceChanged();
690 else
691 emit q->sourceComponentChanged();
692 updateStatus();
693 emit q->progressChanged();
694 if (status == QQmlIncubator::Ready)
695 emit q->loaded();
696}
697
698void QQuickLoaderPrivate::_q_sourceLoaded()
699{
700 Q_Q(QQuickLoader);
701 if (!component || !component->errors().isEmpty()) {
702 if (component)
703 QQmlEnginePrivate::warning(qmlEngine(q), component->errors());
704 if (loadingFromSource)
705 emit q->sourceChanged();
706 else
707 emit q->sourceComponentChanged();
708 updateStatus();
709 emit q->progressChanged();
710 emit q->itemChanged(); //Like clearing source, emit itemChanged even if previous item was also null
711 disposeInitialPropertyValues(); // cleanup
712 return;
713 }
714
715 if (!active)
716 return;
717
718 QQmlContext *creationContext = component->creationContext();
719 if (!creationContext)
720 creationContext = qmlContext(q);
721
722 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(c: component);
723 QQmlContext *context = [&](){
724 if (cp->isBound())
725 return creationContext;
726 itemContext = new QQmlContext(creationContext);
727 itemContext->setContextObject(q);
728 return itemContext;
729 }();
730
731 delete incubator;
732 incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
733
734 component->create(*incubator, context);
735
736 if (incubator && incubator->status() == QQmlIncubator::Loading)
737 updateStatus();
738}
739
740/*!
741 \qmlproperty enumeration QtQuick::Loader::status
742
743 This property holds the status of QML loading. It can be one of:
744 \list
745 \li Loader.Null - the loader is inactive or no QML source has been set
746 \li Loader.Ready - the QML source has been loaded
747 \li Loader.Loading - the QML source is currently being loaded
748 \li Loader.Error - an error occurred while loading the QML source
749 \endlist
750
751 Use this status to provide an update or respond to the status change in some way.
752 For example, you could:
753
754 \list
755 \li Trigger a state change:
756 \qml
757 State { name: 'loaded'; when: loader.status == Loader.Ready }
758 \endqml
759
760 \li Implement an \c onStatusChanged signal handler:
761 \qml
762 Loader {
763 id: loader
764 onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
765 }
766 \endqml
767
768 \li Bind to the status value:
769 \qml
770 Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
771 \endqml
772 \endlist
773
774 Note that if the source is a local file, the status will initially be Ready (or Error). While
775 there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
776
777 \sa progress
778*/
779
780QQuickLoader::Status QQuickLoader::status() const
781{
782 Q_D(const QQuickLoader);
783
784 return static_cast<Status>(d->status);
785}
786
787void QQuickLoader::componentComplete()
788{
789 Q_D(QQuickLoader);
790 QQuickItem::componentComplete();
791 if (active() && (status() != Ready)) {
792 if (d->loadingFromSource)
793 d->createComponent();
794 d->load();
795 }
796}
797
798void QQuickLoader::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
799{
800 switch (change) {
801 case ItemChildAddedChange:
802 Q_ASSERT(value.item);
803 if (value.item->flags().testFlag(flag: QQuickItem::ItemObservesViewport))
804 // Re-trigger the parent traversal to get subtreeTransformChangedEnabled turned on
805 value.item->setFlag(flag: QQuickItem::ItemObservesViewport);
806 break;
807 default:
808 break;
809 }
810 QQuickItem::itemChange(change, value);
811}
812
813/*!
814 \qmlsignal QtQuick::Loader::loaded()
815
816 This signal is emitted when the \l status becomes \c Loader.Ready, or on successful
817 initial load.
818*/
819
820
821/*!
822\qmlproperty real QtQuick::Loader::progress
823
824This property holds the progress of loading QML data from the network, from
8250.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
826this value will rapidly change from 0 to 1.
827
828\sa status
829*/
830qreal QQuickLoader::progress() const
831{
832 Q_D(const QQuickLoader);
833
834 if (d->object)
835 return 1.0;
836
837 if (d->component)
838 return d->component->progress();
839
840 return 0.0;
841}
842
843/*!
844\qmlproperty bool QtQuick::Loader::asynchronous
845
846This property holds whether the component will be instantiated asynchronously.
847By default it is \c false.
848
849When used in conjunction with the \l source property, loading and compilation
850will also be performed in a background thread.
851
852Loading asynchronously creates the objects declared by the component
853across multiple frames, and reduces the
854likelihood of glitches in animation. When loading asynchronously the status
855will change to Loader.Loading. Once the entire component has been created, the
856\l item will be available and the status will change to Loader.Ready.
857
858Changing the value of this property to \c false while an asynchronous load is in
859progress will force immediate, synchronous completion. This allows beginning an
860asynchronous load and then forcing completion if the Loader content must be
861accessed before the asynchronous load has completed.
862
863To avoid seeing the items loading progressively set \c visible appropriately, e.g.
864
865\code
866Loader {
867 source: "mycomponent.qml"
868 asynchronous: true
869 visible: status == Loader.Ready
870}
871\endcode
872
873Note that this property affects object instantiation only; it is unrelated to
874loading a component asynchronously via a network.
875*/
876bool QQuickLoader::asynchronous() const
877{
878 Q_D(const QQuickLoader);
879 return d->asynchronous;
880}
881
882void QQuickLoader::setAsynchronous(bool a)
883{
884 Q_D(QQuickLoader);
885 if (d->asynchronous == a)
886 return;
887
888 d->asynchronous = a;
889
890 if (!d->asynchronous && isComponentComplete() && d->active) {
891 if (d->loadingFromSource && d->component && d->component->isLoading()) {
892 // Force a synchronous component load
893 QUrl currentSource = d->source;
894 d->clear();
895 d->source = currentSource;
896 loadFromSource();
897 } else if (d->incubator && d->incubator->isLoading()) {
898 d->incubator->forceCompletion();
899 }
900 }
901
902 emit asynchronousChanged();
903}
904
905void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
906{
907 Q_Q(QQuickLoader);
908 if (!item)
909 return;
910
911 const bool needToUpdateWidth = loaderGeometryChanged && q->widthValid();
912 const bool needToUpdateHeight = loaderGeometryChanged && q->heightValid();
913
914 if (needToUpdateWidth && needToUpdateHeight) {
915 /* setSize keeps bindings intact (for backwards compatibility reasons),
916 but here we actually want the loader to control the size, so any
917 prexisting bindings ought to be removed
918 */
919 auto *itemPriv = QQuickItemPrivate::get(item);
920 // takeBinding would work without the check, but this is more efficient
921 // for the common case where we don't have a binding
922 if (itemPriv->width.hasBinding())
923 itemPriv->width.takeBinding();
924 if (itemPriv->height.hasBinding())
925 itemPriv->height.takeBinding();
926 item->setSize(QSizeF(q->width(), q->height()));
927 } else if (needToUpdateWidth) {
928 item->setWidth(q->width());
929 } else if (needToUpdateHeight) {
930 item->setHeight(q->height());
931 }
932
933 if (updatingSize)
934 return;
935
936 updatingSize = true;
937
938 q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
939
940 updatingSize = false;
941}
942
943/*!
944 \qmlproperty QtObject QtQuick::Loader::item
945 This property holds the top-level object that is currently loaded.
946
947 Since \c {QtQuick 2.0}, Loader can load any object type.
948*/
949QObject *QQuickLoader::item() const
950{
951 Q_D(const QQuickLoader);
952 return d->object;
953}
954
955void QQuickLoader::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
956{
957 Q_D(QQuickLoader);
958 if (newGeometry != oldGeometry) {
959 d->_q_updateSize();
960 }
961 QQuickItem::geometryChange(newGeometry, oldGeometry);
962}
963
964QQuickLoader::Status QQuickLoaderPrivate::computeStatus() const
965{
966 if (!active)
967 return QQuickLoader::Status::Null;
968
969 if (component) {
970 switch (component->status()) {
971 case QQmlComponent::Loading:
972 return QQuickLoader::Status::Loading;
973 case QQmlComponent::Error:
974 return QQuickLoader::Status::Error;
975 case QQmlComponent::Null:
976 return QQuickLoader::Status::Null;
977 default:
978 break;
979 }
980 }
981
982 if (incubator) {
983 switch (incubator->status()) {
984 case QQmlIncubator::Loading:
985 return QQuickLoader::Status::Loading;
986 case QQmlIncubator::Error:
987 return QQuickLoader::Status::Error;
988 default:
989 break;
990 }
991 }
992
993 if (object)
994 return QQuickLoader::Status::Ready;
995
996 return source.isEmpty() ? QQuickLoader::Status::Null : QQuickLoader::Status::Error;
997}
998
999void QQuickLoaderPrivate::updateStatus()
1000{
1001 Q_Q(QQuickLoader);
1002 auto newStatus = computeStatus();
1003 if (status != newStatus) {
1004 status = newStatus;
1005 emit q->statusChanged();
1006 }
1007}
1008
1009void QQuickLoaderPrivate::createComponent()
1010{
1011 Q_Q(QQuickLoader);
1012 const QQmlComponent::CompilationMode mode = asynchronous
1013 ? QQmlComponent::Asynchronous
1014 : QQmlComponent::PreferSynchronous;
1015 if (QQmlContext *context = qmlContext(q)) {
1016 if (QQmlEngine *engine = context->engine()) {
1017 component.setObject(obj: new QQmlComponent(
1018 engine, context->resolvedUrl(source), mode, q), parent: q);
1019 return;
1020 }
1021 }
1022
1023 qmlWarning(me: q) << "createComponent: Cannot find a QML engine.";
1024}
1025
1026QT_END_NAMESPACE
1027
1028#include <moc_qquickloader_p.cpp>
1029

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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