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

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