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 "qqmlincubator.h"
5#include "qqmlcomponent.h"
6#include "qqmlincubator_p.h"
7
8#include "qqmlobjectcreator_p.h"
9#include <private/qqmlcomponent_p.h>
10
11void QQmlEnginePrivate::incubate(
12 QQmlIncubator &i, const QQmlRefPointer<QQmlContextData> &forContext)
13{
14 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(i.d);
15
16 QQmlIncubator::IncubationMode mode = i.incubationMode();
17
18 if (!incubationController)
19 mode = QQmlIncubator::Synchronous;
20
21 if (mode == QQmlIncubator::AsynchronousIfNested) {
22 mode = QQmlIncubator::Synchronous;
23
24 // Need to find the first constructing context and see if it is asynchronous
25 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> parentIncubator;
26 QQmlRefPointer<QQmlContextData> cctxt = forContext;
27 while (cctxt) {
28 if (QQmlIncubatorPrivate *incubator = cctxt->incubator()) {
29 parentIncubator = incubator;
30 break;
31 }
32 cctxt = cctxt->parent();
33 }
34
35 if (parentIncubator && parentIncubator->isAsynchronous) {
36 mode = QQmlIncubator::Asynchronous;
37 p->waitingOnMe = parentIncubator;
38 parentIncubator->waitingFor.insert(n: p.data());
39 }
40 }
41
42 p->isAsynchronous = (mode != QQmlIncubator::Synchronous);
43
44 inProgressCreations++;
45
46 if (mode == QQmlIncubator::Synchronous) {
47 QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(p.data());
48
49 p->changeStatus(QQmlIncubator::Loading);
50
51 if (!watcher.hasRecursed()) {
52 QQmlInstantiationInterrupt i;
53 p->incubate(i);
54 }
55 } else {
56 incubatorList.insert(n: p.data());
57 incubatorCount++;
58
59 p->vmeGuard.guard(p->creator.data());
60 p->changeStatus(QQmlIncubator::Loading);
61
62 if (incubationController)
63 incubationController->incubatingObjectCountChanged(incubatorCount);
64 }
65}
66
67/*!
68Sets the engine's incubation \a controller. The engine can only have one active controller
69and it does not take ownership of it.
70
71\sa incubationController()
72*/
73void QQmlEngine::setIncubationController(QQmlIncubationController *controller)
74{
75 Q_D(QQmlEngine);
76 if (d->incubationController)
77 d->incubationController->d = nullptr;
78 d->incubationController = controller;
79 if (controller) controller->d = d;
80}
81
82/*!
83Returns the currently set incubation controller, or 0 if no controller has been set.
84
85\sa setIncubationController()
86*/
87QQmlIncubationController *QQmlEngine::incubationController() const
88{
89 Q_D(const QQmlEngine);
90 return d->incubationController;
91}
92
93QQmlIncubatorPrivate::QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m)
94 : q(q), status(QQmlIncubator::Null), mode(m), isAsynchronous(false), progress(Execute),
95 result(nullptr), enginePriv(nullptr), waitingOnMe(nullptr)
96{
97}
98
99QQmlIncubatorPrivate::~QQmlIncubatorPrivate()
100{
101 clear();
102}
103
104void QQmlIncubatorPrivate::clear()
105{
106 // reset the tagged pointer
107 if (requiredPropertiesFromComponent)
108 requiredPropertiesFromComponent = decltype(requiredPropertiesFromComponent){};
109 compilationUnit.reset();
110 if (next.isInList()) {
111 next.remove();
112 enginePriv->incubatorCount--;
113 QQmlIncubationController *controller = enginePriv->incubationController;
114 if (controller)
115 controller->incubatingObjectCountChanged(enginePriv->incubatorCount);
116 }
117 enginePriv = nullptr;
118 if (!rootContext.isNull()) {
119 if (rootContext->incubator())
120 rootContext->setIncubator(nullptr);
121 rootContext.setContextData({});
122 }
123
124 if (nextWaitingFor.isInList()) {
125 Q_ASSERT(waitingOnMe);
126 nextWaitingFor.remove();
127 waitingOnMe = nullptr;
128 }
129
130 // if we're waiting on any incubators then they should be cleared too.
131 while (waitingFor.first()) {
132 QQmlIncubator * i = static_cast<QQmlIncubatorPrivate*>(waitingFor.first())->q;
133 if (i)
134 i->clear();
135 }
136
137 bool guardOk = vmeGuard.isOK();
138
139 vmeGuard.clear();
140 if (creator && guardOk)
141 creator->clear();
142 creator.reset(other: nullptr);
143}
144
145/*!
146\class QQmlIncubationController
147\brief QQmlIncubationController instances drive the progress of QQmlIncubators.
148\inmodule QtQml
149
150In order to behave asynchronously and not introduce stutters or freezes in an application,
151the process of creating objects a QQmlIncubators must be driven only during the
152application's idle time. QQmlIncubationController allows the application to control
153exactly when, how often and for how long this processing occurs.
154
155A QQmlIncubationController derived instance should be created and set on a
156QQmlEngine by calling the QQmlEngine::setIncubationController() method.
157Processing is then controlled by calling the QQmlIncubationController::incubateFor()
158or QQmlIncubationController::incubateWhile() methods as dictated by the application's
159requirements.
160
161For example, this is an example of a incubation controller that will incubate for a maximum
162of 5 milliseconds out of every 16 milliseconds.
163
164\code
165class PeriodicIncubationController : public QObject,
166 public QQmlIncubationController
167{
168public:
169 PeriodicIncubationController() {
170 startTimer(16);
171 }
172
173protected:
174 void timerEvent(QTimerEvent *) override {
175 incubateFor(5);
176 }
177};
178\endcode
179
180Although the previous example would work, it is not optimal. Real world incubation
181controllers should try and maximize the amount of idle time they consume - rather
182than a static amount like 5 milliseconds - while not disturbing the application.
183*/
184
185/*!
186Create a new incubation controller.
187*/
188QQmlIncubationController::QQmlIncubationController()
189: d(nullptr)
190{
191}
192
193/*! \internal */
194QQmlIncubationController::~QQmlIncubationController()
195{
196 if (d) QQmlEnginePrivate::get(p: d)->setIncubationController(nullptr);
197 d = nullptr;
198}
199
200/*!
201Return the QQmlEngine this incubation controller is set on, or 0 if it
202has not been set on any engine.
203*/
204QQmlEngine *QQmlIncubationController::engine() const
205{
206 return QQmlEnginePrivate::get(p: d);
207}
208
209/*!
210Return the number of objects currently incubating.
211*/
212int QQmlIncubationController::incubatingObjectCount() const
213{
214 return d ? d->incubatorCount : 0;
215}
216
217/*!
218Called when the number of incubating objects changes. \a incubatingObjectCount is the
219new number of incubating objects.
220
221The default implementation does nothing.
222*/
223void QQmlIncubationController::incubatingObjectCountChanged(int incubatingObjectCount)
224{
225 Q_UNUSED(incubatingObjectCount);
226}
227
228void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i)
229{
230 while (QQmlIncubator::Loading == status) {
231 while (QQmlIncubator::Loading == status && !waitingFor.isEmpty())
232 waitingFor.first()->forceCompletion(i);
233 if (QQmlIncubator::Loading == status)
234 incubate(i);
235 }
236}
237
238
239void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
240{
241 if (!compilationUnit)
242 return;
243
244 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this);
245
246 QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this);
247 // get a copy of the engine pointer as it might get reset;
248 QQmlEnginePrivate *enginePriv = this->enginePriv;
249
250 // Incubating objects takes quite a bit more stack space than our usual V4 function
251 enum { EstimatedSizeInV4Frames = 2 };
252 QV4::ExecutionEngineCallDepthRecorder<EstimatedSizeInV4Frames> callDepthRecorder(
253 compilationUnit->engine);
254 if (callDepthRecorder.hasOverflow()) {
255 QQmlError error;
256 error.setMessageType(QtCriticalMsg);
257 error.setUrl(compilationUnit->url());
258 error.setDescription(QQmlComponent::tr(s: "Maximum call stack size exceeded."));
259 errors << error;
260 progress = QQmlIncubatorPrivate::Completed;
261 goto finishIncubate;
262 }
263
264 if (!vmeGuard.isOK()) {
265 QQmlError error;
266 error.setMessageType(QtInfoMsg);
267 error.setUrl(compilationUnit->url());
268 error.setDescription(QQmlComponent::tr(s: "Object or context destroyed during incubation"));
269 errors << error;
270 progress = QQmlIncubatorPrivate::Completed;
271
272 goto finishIncubate;
273 }
274
275 vmeGuard.clear();
276
277 if (progress == QQmlIncubatorPrivate::Execute) {
278 enginePriv->referenceScarceResources();
279 QObject *tresult = nullptr;
280 tresult = creator->create(subComponentIndex: subComponentToCreate, /*parent*/nullptr, interrupt: &i);
281 if (!tresult)
282 errors = creator->errors;
283 else {
284 RequiredProperties* requiredProperties = creator->requiredProperties();
285 for (auto it = initialProperties.cbegin(); it != initialProperties.cend(); ++it) {
286 auto component = tresult;
287 auto name = it.key();
288 QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(
289 createdComponent: component, name, requiredProperties, engine: QQmlEnginePrivate::get(p: enginePriv));
290 if (!prop.isValid() || !prop.write(it.value())) {
291 QQmlError error{};
292 error.setUrl(compilationUnit->url());
293 error.setDescription(QLatin1String("Could not set property %1").arg(args&: name));
294 errors.push_back(t: error);
295 }
296 }
297 }
298 enginePriv->dereferenceScarceResources();
299
300 if (watcher.hasRecursed())
301 return;
302
303 result = tresult;
304 if (errors.isEmpty() && result == nullptr)
305 goto finishIncubate;
306
307 if (result) {
308 QQmlData *ddata = QQmlData::get(object: result);
309 Q_ASSERT(ddata);
310 //see QQmlComponent::beginCreate for explanation of indestructible
311 ddata->indestructible = true;
312 ddata->explicitIndestructibleSet = true;
313 ddata->rootObjectInCreation = false;
314 if (q) {
315 q->setInitialState(result);
316 if (creator && !creator->requiredProperties()->empty()) {
317 const RequiredProperties *unsetRequiredProperties = creator->requiredProperties();
318 for (const auto& unsetRequiredProperty: *unsetRequiredProperties)
319 errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
320 }
321 }
322 }
323
324 if (watcher.hasRecursed())
325 return;
326
327 if (errors.isEmpty())
328 progress = QQmlIncubatorPrivate::Completing;
329 else
330 progress = QQmlIncubatorPrivate::Completed;
331
332 changeStatus(calculateStatus());
333
334 if (watcher.hasRecursed())
335 return;
336
337 if (i.shouldInterrupt())
338 goto finishIncubate;
339 }
340
341 if (progress == QQmlIncubatorPrivate::Completing) {
342 do {
343 if (watcher.hasRecursed())
344 return;
345
346 if (creator->finalize(interrupt&: i)) {
347 rootContext = creator->rootContext();
348 progress = QQmlIncubatorPrivate::Completed;
349 goto finishIncubate;
350 }
351 } while (!i.shouldInterrupt());
352 }
353
354finishIncubate:
355 if (progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty()) {
356 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> isWaiting = waitingOnMe;
357 clear();
358
359 if (isWaiting) {
360 QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(isWaiting.data());
361 changeStatus(calculateStatus());
362 if (!watcher.hasRecursed())
363 isWaiting->incubate(i);
364 } else {
365 changeStatus(calculateStatus());
366 }
367
368 enginePriv->inProgressCreations--;
369
370 if (0 == enginePriv->inProgressCreations) {
371 while (enginePriv->erroredBindings)
372 enginePriv->warning(enginePriv->erroredBindings->removeError());
373 }
374 } else if (!creator.isNull()) {
375 vmeGuard.guard(creator.data());
376 }
377}
378
379/*!
380 \internal
381 This is used to mimic the behavior of incubate when the
382 Component we want to incubate refers to a creatable
383 QQmlType (i.e., it is the result of loadFromModule).
384 */
385void QQmlIncubatorPrivate::incubateCppBasedComponent(QQmlComponent *component, QQmlContext *context)
386{
387 auto compPriv = QQmlComponentPrivate::get(c: component);
388 Q_ASSERT(compPriv->loadedType.isCreatable());
389 std::unique_ptr<QObject> object(component->beginCreate(context));
390 component->setInitialProperties(component: object.get(), properties: initialProperties);
391 if (auto props = compPriv->state.requiredProperties()) {
392 requiredPropertiesFromComponent = props;
393 requiredPropertiesFromComponent.setTag(HadTopLevelRequired::Yes);
394 }
395 q->setInitialState(object.get());
396 if (requiredPropertiesFromComponent && !requiredPropertiesFromComponent->isEmpty()) {
397 for (const RequiredPropertyInfo &unsetRequiredProperty :
398 std::as_const(t&: *requiredPropertiesFromComponent)) {
399 errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
400 }
401 } else {
402 compPriv->completeCreate();
403 result = object.release();
404 progress = QQmlIncubatorPrivate::Completed;
405 }
406 changeStatus(calculateStatus());
407
408}
409
410/*!
411Incubate objects for \a msecs, or until there are no more objects to incubate.
412*/
413void QQmlIncubationController::incubateFor(int msecs)
414{
415 if (!d || !d->incubatorCount)
416 return;
417
418 QDeadlineTimer deadline(msecs);
419 QQmlInstantiationInterrupt i(deadline);
420 do {
421 static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i);
422 } while (d && d->incubatorCount != 0 && !i.shouldInterrupt());
423}
424
425/*!
426\since 5.15
427
428Incubate objects while the atomic bool pointed to by \a flag is true,
429or until there are no more objects to incubate, or up to \a msecs if \a
430msecs is not zero.
431
432Generally this method is used in conjunction with a thread or a UNIX signal that sets
433the bool pointed to by \a flag to false when it wants incubation to be interrupted.
434
435\note \a flag is read using acquire memory ordering.
436*/
437void QQmlIncubationController::incubateWhile(std::atomic<bool> *flag, int msecs)
438{
439 if (!d || !d->incubatorCount)
440 return;
441
442 QQmlInstantiationInterrupt i(flag, msecs ? QDeadlineTimer(msecs) : QDeadlineTimer::Forever);
443 do {
444 static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i);
445 } while (d && d->incubatorCount != 0 && !i.shouldInterrupt());
446}
447
448/*!
449\class QQmlIncubator
450\brief The QQmlIncubator class allows QML objects to be created asynchronously.
451\inmodule QtQml
452
453Creating QML objects - like delegates in a view, or a new page in an application - can take
454a noticeable amount of time, especially on resource constrained mobile devices. When an
455application uses QQmlComponent::create() directly, the QML object instance is created
456synchronously which, depending on the complexity of the object, can cause noticeable pauses or
457stutters in the application.
458
459The use of QQmlIncubator gives more control over the creation of a QML object,
460including allowing it to be created asynchronously using application idle time. The following
461example shows a simple use of QQmlIncubator.
462
463\code
464QQmlIncubator incubator;
465component->create(incubator);
466
467while (!incubator.isReady()) {
468 QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
469}
470
471QObject *object = incubator.object();
472\endcode
473
474Asynchronous incubators are controlled by a QQmlIncubationController that is
475set on the QQmlEngine, which lets the engine know when the application is idle and
476incubating objects should be processed. If an incubation controller is not set on the
477QQmlEngine, QQmlIncubator creates objects synchronously regardless of the
478specified IncubationMode.
479
480QQmlIncubator supports three incubation modes:
481\list
482\li Synchronous The creation occurs synchronously. That is, once the
483QQmlComponent::create() call returns, the incubator will already be in either the
484Error or Ready state. A synchronous incubator has no real advantage compared to using
485the synchronous creation methods on QQmlComponent directly, but it may simplify an
486application's implementation to use the same API for both synchronous and asynchronous
487creations.
488
489\li Asynchronous (default) The creation occurs asynchronously, assuming a
490QQmlIncubatorController is set on the QQmlEngine.
491
492The incubator will remain in the Loading state until either the creation is complete or an error
493occurs. The statusChanged() callback can be used to be notified of status changes.
494
495Applications should use the Asynchronous incubation mode to create objects that are not needed
496immediately. For example, the ListView type uses Asynchronous incubation to create objects
497that are slightly off screen while the list is being scrolled. If, during asynchronous creation,
498the object is needed immediately the QQmlIncubator::forceCompletion() method can be called
499to complete the creation process synchronously.
500
501\li AsynchronousIfNested The creation will occur asynchronously if part of a nested asynchronous
502creation, or synchronously if not.
503
504In most scenarios where a QML component wants the appearance of a synchronous
505instantiation, it should use this mode.
506
507This mode is best explained with an example. When the ListView type is first created, it needs
508to populate itself with an initial set of delegates to show. If the ListView was 400 pixels high,
509and each delegate was 100 pixels high, it would need to create four initial delegate instances. If
510the ListView used the Asynchronous incubation mode, the ListView would always be created empty and
511then, sometime later, the four initial items would appear.
512
513Conversely, if the ListView was to use the Synchronous incubation mode it would behave correctly
514but it may introduce stutters into the application. As QML would have to stop and instantiate the
515ListView's delegates synchronously, if the ListView was part of a QML component that was being
516instantiated asynchronously this would undo much of the benefit of asynchronous instantiation.
517
518The AsynchronousIfNested mode reconciles this problem. By using AsynchronousIfNested, the ListView
519delegates are instantiated asynchronously if the ListView itself is already part of an asynchronous
520instantiation, and synchronously otherwise. In the case of a nested asynchronous instantiation, the
521outer asynchronous instantiation will not complete until after all the nested instantiations have also
522completed. This ensures that by the time the outer asynchronous instantitation completes, inner
523items like ListView have already completed loading their initial delegates.
524
525It is almost always incorrect to use the Synchronous incubation mode - elements or components that
526want the appearance of synchronous instantiation, but without the downsides of introducing freezes
527or stutters into the application, should use the AsynchronousIfNested incubation mode.
528\endlist
529*/
530
531/*!
532Create a new incubator with the specified \a mode
533*/
534QQmlIncubator::QQmlIncubator(IncubationMode mode)
535 : d(new QQmlIncubatorPrivate(this, mode))
536{
537 d->ref.ref();
538}
539
540/*! \internal */
541QQmlIncubator::~QQmlIncubator()
542{
543 d->q = nullptr;
544
545 if (!d->ref.deref()) {
546 delete d;
547 }
548 d = nullptr;
549}
550
551/*!
552\enum QQmlIncubator::IncubationMode
553
554Specifies the mode the incubator operates in. Regardless of the incubation mode, a
555QQmlIncubator will behave synchronously if the QQmlEngine does not have
556a QQmlIncubationController set.
557
558\value Asynchronous The object will be created asynchronously.
559\value AsynchronousIfNested If the object is being created in a context that is already part
560of an asynchronous creation, this incubator will join that existing incubation and execute
561asynchronously. The existing incubation will not become Ready until both it and this
562incubation have completed. Otherwise, the incubation will execute synchronously.
563\value Synchronous The object will be created synchronously.
564*/
565
566/*!
567\enum QQmlIncubator::Status
568
569Specifies the status of the QQmlIncubator.
570
571\value Null Incubation is not in progress. Call QQmlComponent::create() to begin incubating.
572\value Ready The object is fully created and can be accessed by calling object().
573\value Loading The object is in the process of being created.
574\value Error An error occurred. The errors can be access by calling errors().
575*/
576
577/*!
578Clears the incubator. Any in-progress incubation is aborted. If the incubator is in the
579Ready state, the created object is \b not deleted.
580*/
581void QQmlIncubator::clear()
582{
583 QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(d);
584
585 Status s = status();
586
587 if (s == Null)
588 return;
589
590 QQmlEnginePrivate *enginePriv = d->enginePriv;
591 if (s == Loading) {
592 Q_ASSERT(d->compilationUnit);
593 if (d->result) d->result->deleteLater();
594 d->result = nullptr;
595 }
596
597 d->clear();
598
599 Q_ASSERT(d->compilationUnit.isNull());
600 Q_ASSERT(d->waitingOnMe.data() == nullptr);
601 Q_ASSERT(d->waitingFor.isEmpty());
602
603 d->errors.clear();
604 d->progress = QQmlIncubatorPrivate::Execute;
605 d->result = nullptr;
606
607 if (s == Loading) {
608 Q_ASSERT(enginePriv);
609
610 enginePriv->inProgressCreations--;
611 if (0 == enginePriv->inProgressCreations) {
612 while (enginePriv->erroredBindings)
613 enginePriv->warning(enginePriv->erroredBindings->removeError());
614 }
615 }
616
617 d->changeStatus(Null);
618}
619
620/*!
621Force any in-progress incubation to finish synchronously. Once this call
622returns, the incubator will not be in the Loading state.
623*/
624void QQmlIncubator::forceCompletion()
625{
626 QQmlInstantiationInterrupt i;
627 d->forceCompletion(i);
628}
629
630/*!
631Returns true if the incubator's status() is Null.
632*/
633bool QQmlIncubator::isNull() const
634{
635 return status() == Null;
636}
637
638/*!
639Returns true if the incubator's status() is Ready.
640*/
641bool QQmlIncubator::isReady() const
642{
643 return status() == Ready;
644}
645
646/*!
647Returns true if the incubator's status() is Error.
648*/
649bool QQmlIncubator::isError() const
650{
651 return status() == Error;
652}
653
654/*!
655Returns true if the incubator's status() is Loading.
656*/
657bool QQmlIncubator::isLoading() const
658{
659 return status() == Loading;
660}
661
662/*!
663Return the list of errors encountered while incubating the object.
664*/
665QList<QQmlError> QQmlIncubator::errors() const
666{
667 return d->errors;
668}
669
670/*!
671Return the incubation mode passed to the QQmlIncubator constructor.
672*/
673QQmlIncubator::IncubationMode QQmlIncubator::incubationMode() const
674{
675 return d->mode;
676}
677
678/*!
679Return the current status of the incubator.
680*/
681QQmlIncubator::Status QQmlIncubator::status() const
682{
683 return d->status;
684}
685
686/*!
687Return the incubated object if the status is Ready, otherwise 0.
688*/
689QObject *QQmlIncubator::object() const
690{
691 if (status() != Ready)
692 return nullptr;
693 else
694 return d->result;
695}
696
697/*!
698Return a pointer to a list of properties which are required but haven't
699been set yet.
700This list can be modified, so that subclasses which implement special logic
701setInitialProperties can mark properties set there as no longer required.
702
703\sa QQmlIncubator::setInitialProperties
704\since 5.15
705*/
706RequiredProperties *QQmlIncubatorPrivate::requiredProperties()
707{
708 if (creator)
709 return creator->requiredProperties();
710 else
711 return requiredPropertiesFromComponent.data();
712}
713
714bool QQmlIncubatorPrivate::hadTopLevelRequiredProperties() const
715{
716 if (creator)
717 return creator->componentHadTopLevelRequiredProperties();
718 else
719 return requiredPropertiesFromComponent.tag() == HadTopLevelRequired::Yes;
720}
721
722/*!
723Stores a mapping from property names to initial values, contained in
724\a initialProperties, with which the incubated component will be initialized.
725
726\sa QQmlComponent::setInitialProperties
727\since 5.15
728*/
729void QQmlIncubator::setInitialProperties(const QVariantMap &initialProperties)
730{
731 d->initialProperties = initialProperties;
732}
733
734/*!
735Called when the status of the incubator changes. \a status is the new status.
736
737The default implementation does nothing.
738*/
739void QQmlIncubator::statusChanged(Status status)
740{
741 Q_UNUSED(status);
742}
743
744/*!
745Called after the \a object is first created, but before property bindings are
746evaluated and, if applicable, QQmlParserStatus::componentComplete() is
747called. This is equivalent to the point between QQmlComponent::beginCreate()
748and QQmlComponent::completeCreate(), and can be used to assign initial values
749to the object's properties.
750
751The default implementation does nothing.
752*/
753void QQmlIncubator::setInitialState(QObject *object)
754{
755 Q_UNUSED(object);
756}
757
758void QQmlIncubatorPrivate::changeStatus(QQmlIncubator::Status s)
759{
760 if (s == status)
761 return;
762
763 status = s;
764 if (q)
765 q->statusChanged(status);
766}
767
768QQmlIncubator::Status QQmlIncubatorPrivate::calculateStatus() const
769{
770 if (!errors.isEmpty())
771 return QQmlIncubator::Error;
772 else if (result && progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty())
773 return QQmlIncubator::Ready;
774 else if (compilationUnit)
775 return QQmlIncubator::Loading;
776 else
777 return QQmlIncubator::Null;
778}
779
780

source code of qtdeclarative/src/qml/qml/qqmlincubator.cpp