Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlincubator.cpp
Go to the documentation of this file.
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
7
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(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(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(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 example works, it is heavily simplified. Real world incubation controllers
181try and maximize the amount of idle time they consume while not disturbing the
182application. Using a static amount of 5 milliseconds like above may both leave idle
183time on the table in some frames and disturb the application in others.
184
185\l{QQuickWindow}, \l{QQuickView}, and \l{QQuickWidget} all pre-create an incubation
186controller that spaces out incubation over multiple frames using a more intelligent
187algorithm. You rarely have to write your own.
188
189*/
190
191/*!
192Create a new incubation controller.
193*/
194QQmlIncubationController::QQmlIncubationController()
195: d(nullptr)
196{
197}
198
199/*! \internal */
200QQmlIncubationController::~QQmlIncubationController()
201{
202 if (d) QQmlEnginePrivate::get(d)->setIncubationController(nullptr);
203 d = nullptr;
204}
205
206/*!
207Return the QQmlEngine this incubation controller is set on, or 0 if it
208has not been set on any engine.
209*/
210QQmlEngine *QQmlIncubationController::engine() const
211{
212 return QQmlEnginePrivate::get(d);
213}
214
215/*!
216Return the number of objects currently incubating.
217*/
218int QQmlIncubationController::incubatingObjectCount() const
219{
220 return d ? d->incubatorCount : 0;
221}
222
223/*!
224Called when the number of incubating objects changes. \a incubatingObjectCount is the
225new number of incubating objects.
226
227The default implementation does nothing.
228*/
229void QQmlIncubationController::incubatingObjectCountChanged(int incubatingObjectCount)
230{
231 Q_UNUSED(incubatingObjectCount);
232}
233
234void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i)
235{
236 while (QQmlIncubator::Loading == status) {
237 while (QQmlIncubator::Loading == status && !waitingFor.isEmpty())
238 waitingFor.first()->forceCompletion(i);
239 if (QQmlIncubator::Loading == status)
240 incubate(i);
241 }
242}
243
244
245void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
246{
247 if (!compilationUnit)
248 return;
249
250 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this);
251
252 QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this);
253 // get a copy of the engine pointer as it might get reset;
254 QQmlEnginePrivate *enginePriv = this->enginePriv;
255
256 // Incubating objects takes quite a bit more stack space than our usual V4 function
257 enum { EstimatedSizeInV4Frames = 2 };
258 QV4::ExecutionEngineCallDepthRecorder<EstimatedSizeInV4Frames> callDepthRecorder(
259 compilationUnit->engine);
260 if (callDepthRecorder.hasOverflow()) {
261 QQmlError error;
262 error.setMessageType(QtCriticalMsg);
263 error.setUrl(compilationUnit->url());
264 error.setDescription(QQmlComponent::tr("Maximum call stack size exceeded."));
265 errors << error;
266 progress = QQmlIncubatorPrivate::Completed;
267 goto finishIncubate;
268 }
269
270 if (!vmeGuard.isOK()) {
271 QQmlError error;
272 error.setMessageType(QtInfoMsg);
273 error.setUrl(compilationUnit->url());
274 error.setDescription(QQmlComponent::tr("Object or context destroyed during incubation"));
275 errors << error;
276 progress = QQmlIncubatorPrivate::Completed;
277
278 goto finishIncubate;
279 }
280
281 vmeGuard.clear();
282
283 if (progress == QQmlIncubatorPrivate::Execute) {
284 enginePriv->referenceScarceResources();
285 QObject *tresult = nullptr;
286 tresult = creator->create(subComponentToCreate, /*parent*/nullptr, &i);
287 if (!tresult)
288 errors = creator->errors;
289 else {
290 RequiredProperties* requiredProperties = creator->requiredProperties();
291 for (auto it = initialProperties.cbegin(); it != initialProperties.cend(); ++it) {
292 auto component = tresult;
293 auto name = it.key();
294 QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(
295 component, name, requiredProperties, QQmlEnginePrivate::get(enginePriv));
296 if (!prop.isValid() || !prop.write(it.value())) {
297 QQmlError error{};
298 error.setUrl(compilationUnit->url());
299 error.setDescription(QLatin1String("Could not set property %1").arg(name));
300 errors.push_back(error);
301 }
302 }
303 }
304 enginePriv->dereferenceScarceResources();
305
306 if (watcher.hasRecursed())
307 return;
308
309 result = tresult;
310 if (errors.isEmpty() && result == nullptr)
311 goto finishIncubate;
312
313 if (result) {
314 QQmlData *ddata = QQmlData::get(result);
315 Q_ASSERT(ddata);
316 //see QQmlComponent::beginCreate for explanation of indestructible
317 ddata->indestructible = true;
318 ddata->explicitIndestructibleSet = true;
319 ddata->rootObjectInCreation = false;
320 if (q) {
321 q->setInitialState(result);
322 if (creator && !creator->requiredProperties()->empty()) {
323 const RequiredProperties *unsetRequiredProperties = creator->requiredProperties();
324 for (const auto& unsetRequiredProperty: *unsetRequiredProperties)
325 errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
326 }
327 }
328 }
329
330 if (watcher.hasRecursed())
331 return;
332
333 if (errors.isEmpty())
334 progress = QQmlIncubatorPrivate::Completing;
335 else
336 progress = QQmlIncubatorPrivate::Completed;
337
338 changeStatus(calculateStatus());
339
340 if (watcher.hasRecursed())
341 return;
342
343 if (i.shouldInterrupt())
344 goto finishIncubate;
345 }
346
347 if (progress == QQmlIncubatorPrivate::Completing) {
348 do {
349 if (watcher.hasRecursed())
350 return;
351
352 if (creator->finalize(i)) {
353 rootContext = creator->rootContext();
354 progress = QQmlIncubatorPrivate::Completed;
355 goto finishIncubate;
356 }
357 } while (!i.shouldInterrupt());
358 }
359
360finishIncubate:
361 if (progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty()) {
362 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> isWaiting = waitingOnMe;
363 clear();
364
365 if (isWaiting) {
366 QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(isWaiting.data());
367 changeStatus(calculateStatus());
368 if (!watcher.hasRecursed())
369 isWaiting->incubate(i);
370 } else {
371 changeStatus(calculateStatus());
372 }
373
374 enginePriv->inProgressCreations--;
375
376 if (0 == enginePriv->inProgressCreations) {
377 while (enginePriv->erroredBindings)
378 enginePriv->warning(enginePriv->erroredBindings->removeError());
379 }
380 } else if (!creator.isNull()) {
381 vmeGuard.guard(creator.data());
382 }
383}
384
385/*!
386 \internal
387 This is used to mimic the behavior of incubate when the
388 Component we want to incubate refers to a creatable
389 QQmlType (i.e., it is the result of loadFromModule).
390 */
391void QQmlIncubatorPrivate::incubateCppBasedComponent(QQmlComponent *component, QQmlContext *context)
392{
393 auto compPriv = QQmlComponentPrivate::get(component);
394 Q_ASSERT(compPriv->loadedType().isCreatable());
395 std::unique_ptr<QObject> object(component->beginCreate(context));
396 component->setInitialProperties(object.get(), initialProperties);
397 if (auto props = compPriv->requiredProperties()) {
398 requiredPropertiesFromComponent = props;
399 requiredPropertiesFromComponent.setTag(HadTopLevelRequired::Yes);
400 }
401 q->setInitialState(object.get());
402 if (requiredPropertiesFromComponent && !requiredPropertiesFromComponent->isEmpty()) {
403 for (const RequiredPropertyInfo &unsetRequiredProperty :
404 std::as_const(*requiredPropertiesFromComponent)) {
405 errors << QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
406 }
407 } else {
408 compPriv->completeCreate();
409 result = object.release();
410 progress = QQmlIncubatorPrivate::Completed;
411 }
412 changeStatus(calculateStatus());
413
414}
415
416/*!
417Incubate objects for \a msecs, or until there are no more objects to incubate.
418*/
419void QQmlIncubationController::incubateFor(int msecs)
420{
421 if (!d || !d->incubatorCount)
422 return;
423
424 QDeadlineTimer deadline(msecs);
425 QQmlInstantiationInterrupt i(deadline);
426 do {
427 static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i);
428 } while (d && d->incubatorCount != 0 && !i.shouldInterrupt());
429}
430
431/*!
432\since 5.15
433
434Incubate objects while the atomic bool pointed to by \a flag is true,
435or until there are no more objects to incubate, or up to \a msecs if \a
436msecs is not zero.
437
438Generally this method is used in conjunction with a thread or a UNIX signal that sets
439the bool pointed to by \a flag to false when it wants incubation to be interrupted.
440
441\note \a flag is read using acquire memory ordering.
442*/
443void QQmlIncubationController::incubateWhile(std::atomic<bool> *flag, int msecs)
444{
445 if (!d || !d->incubatorCount)
446 return;
447
448 QQmlInstantiationInterrupt i(flag, msecs ? QDeadlineTimer(msecs) : QDeadlineTimer::Forever);
449 do {
450 static_cast<QQmlIncubatorPrivate*>(d->incubatorList.first())->incubate(i);
451 } while (d && d->incubatorCount != 0 && !i.shouldInterrupt());
452}
453
454/*!
455\class QQmlIncubator
456\brief The QQmlIncubator class allows QML objects to be created asynchronously.
457\inmodule QtQml
458
459Creating QML objects - like delegates in a view, or a new page in an application - can take
460a noticeable amount of time, especially on resource constrained mobile devices. When an
461application uses QQmlComponent::create() directly, the QML object instance is created
462synchronously which, depending on the complexity of the object, can cause noticeable pauses or
463stutters in the application.
464
465The use of QQmlIncubator gives more control over the creation of a QML object,
466including allowing it to be created asynchronously using application idle time. The following
467example shows a simple use of QQmlIncubator.
468
469\code
470// Initialize the incubator
471QQmlIncubator incubator;
472component->create(incubator);
473\endcode
474
475Let the incubator run for a while (normally by returning control to the event loop),
476then poll it. There are a number of ways to get back to the incubator later. You may
477want to connect to one of the signals sent by \l{QQuickWindow}, or you may want to run
478a \l{QTimer} especially for that. You may also need the object for some specific
479purpose and poll the incubator when that purpose arises.
480
481\code
482// Poll the incubator
483if (incubator.isReady()) {
484 QObject *object = incubator.object();
485 // Use created object
486}
487\endcode
488
489Asynchronous incubators are controlled by a \l{QQmlIncubationController} that is
490set on the \l{QQmlEngine}, which lets the engine know when the application is idle and
491incubating objects should be processed. If an incubation controller is not set on the
492\l{QQmlEngine}, \l{QQmlIncubator} creates objects synchronously regardless of the
493specified IncubationMode. By default, no incubation controller is set. However,
494\l{QQuickView}, \l{QQuickWindow} and \l{QQuickWidget} all set incubation controllers
495on their respective \l{QQmlEngine}s. These incubation controllers space out incubations
496across multiple frames while the view is being rendered.
497
498QQmlIncubator supports three incubation modes:
499\list
500\li Synchronous The creation occurs synchronously. That is, once the
501QQmlComponent::create() call returns, the incubator will already be in either the
502Error or Ready state. A synchronous incubator has no real advantage compared to using
503the synchronous creation methods on QQmlComponent directly, but it may simplify an
504application's implementation to use the same API for both synchronous and asynchronous
505creations.
506
507\li Asynchronous (default) The creation occurs asynchronously, assuming a
508QQmlIncubatorController is set on the QQmlEngine.
509
510The incubator will remain in the Loading state until either the creation is complete or an error
511occurs. The statusChanged() callback can be used to be notified of status changes.
512
513Applications should use the Asynchronous incubation mode to create objects that are not needed
514immediately. For example, the ListView type uses Asynchronous incubation to create objects
515that are slightly off screen while the list is being scrolled. If, during asynchronous creation,
516the object is needed immediately the QQmlIncubator::forceCompletion() method can be called
517to complete the creation process synchronously.
518
519\li AsynchronousIfNested The creation will occur asynchronously if part of a nested asynchronous
520creation, or synchronously if not.
521
522In most scenarios where a QML component wants the appearance of a synchronous
523instantiation, it should use this mode.
524
525This mode is best explained with an example. When the ListView type is first created, it needs
526to populate itself with an initial set of delegates to show. If the ListView was 400 pixels high,
527and each delegate was 100 pixels high, it would need to create four initial delegate instances. If
528the ListView used the Asynchronous incubation mode, the ListView would always be created empty and
529then, sometime later, the four initial items would appear.
530
531Conversely, if the ListView was to use the Synchronous incubation mode it would behave correctly
532but it may introduce stutters into the application. As QML would have to stop and instantiate the
533ListView's delegates synchronously, if the ListView was part of a QML component that was being
534instantiated asynchronously this would undo much of the benefit of asynchronous instantiation.
535
536The AsynchronousIfNested mode reconciles this problem. By using AsynchronousIfNested, the ListView
537delegates are instantiated asynchronously if the ListView itself is already part of an asynchronous
538instantiation, and synchronously otherwise. In the case of a nested asynchronous instantiation, the
539outer asynchronous instantiation will not complete until after all the nested instantiations have also
540completed. This ensures that by the time the outer asynchronous instantitation completes, inner
541items like ListView have already completed loading their initial delegates.
542
543It is almost always incorrect to use the Synchronous incubation mode - elements or components that
544want the appearance of synchronous instantiation, but without the downsides of introducing freezes
545or stutters into the application, should use the AsynchronousIfNested incubation mode.
546\endlist
547*/
548
549/*!
550Create a new incubator with the specified \a mode
551*/
552QQmlIncubator::QQmlIncubator(IncubationMode mode)
553 : d(new QQmlIncubatorPrivate(this, mode))
554{
555 d->ref.ref();
556}
557
558/*! \internal */
559QQmlIncubator::~QQmlIncubator()
560{
561 d->q = nullptr;
562
563 if (!d->ref.deref()) {
564 delete d;
565 }
566 d = nullptr;
567}
568
569/*!
570\enum QQmlIncubator::IncubationMode
571
572Specifies the mode the incubator operates in. Regardless of the incubation mode, a
573QQmlIncubator will behave synchronously if the QQmlEngine does not have
574a QQmlIncubationController set.
575
576\value Asynchronous The object will be created asynchronously.
577\value AsynchronousIfNested If the object is being created in a context that is already part
578of an asynchronous creation, this incubator will join that existing incubation and execute
579asynchronously. The existing incubation will not become Ready until both it and this
580incubation have completed. Otherwise, the incubation will execute synchronously.
581\value Synchronous The object will be created synchronously.
582*/
583
584/*!
585\enum QQmlIncubator::Status
586
587Specifies the status of the QQmlIncubator.
588
589\value Null Incubation is not in progress. Call QQmlComponent::create() to begin incubating.
590\value Ready The object is fully created and can be accessed by calling object().
591\value Loading The object is in the process of being created.
592\value Error An error occurred. The errors can be access by calling errors().
593*/
594
595/*!
596Clears the incubator. Any in-progress incubation is aborted. If the incubator is in the
597Ready state, the created object is \b not deleted.
598*/
599void QQmlIncubator::clear()
600{
601 QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(d);
602
603 Status s = status();
604
605 if (s == Null)
606 return;
607
608 QQmlEnginePrivate *enginePriv = d->enginePriv;
609 if (s == Loading) {
610 Q_ASSERT(d->compilationUnit);
611 if (d->result) d->result->deleteLater();
612 d->result = nullptr;
613 }
614
615 d->clear();
616
617 Q_ASSERT(d->compilationUnit.isNull());
618 Q_ASSERT(d->waitingOnMe.data() == nullptr);
619 Q_ASSERT(d->waitingFor.isEmpty());
620
621 d->errors.clear();
622 d->progress = QQmlIncubatorPrivate::Execute;
623 d->result = nullptr;
624
625 if (s == Loading) {
626 Q_ASSERT(enginePriv);
627
628 enginePriv->inProgressCreations--;
629 if (0 == enginePriv->inProgressCreations) {
630 while (enginePriv->erroredBindings)
631 enginePriv->warning(enginePriv->erroredBindings->removeError());
632 }
633 }
634
635 d->changeStatus(Null);
636}
637
638/*!
639Force any in-progress incubation to finish synchronously. Once this call
640returns, the incubator will not be in the Loading state.
641*/
642void QQmlIncubator::forceCompletion()
643{
644 QQmlInstantiationInterrupt i;
645 d->forceCompletion(i);
646}
647
648/*!
649Returns true if the incubator's status() is Null.
650*/
651bool QQmlIncubator::isNull() const
652{
653 return status() == Null;
654}
655
656/*!
657Returns true if the incubator's status() is Ready.
658*/
659bool QQmlIncubator::isReady() const
660{
661 return status() == Ready;
662}
663
664/*!
665Returns true if the incubator's status() is Error.
666*/
667bool QQmlIncubator::isError() const
668{
669 return status() == Error;
670}
671
672/*!
673Returns true if the incubator's status() is Loading.
674*/
675bool QQmlIncubator::isLoading() const
676{
677 return status() == Loading;
678}
679
680/*!
681Return the list of errors encountered while incubating the object.
682*/
683QList<QQmlError> QQmlIncubator::errors() const
684{
685 return d->errors;
686}
687
688/*!
689Return the incubation mode passed to the QQmlIncubator constructor.
690*/
691QQmlIncubator::IncubationMode QQmlIncubator::incubationMode() const
692{
693 return d->mode;
694}
695
696/*!
697Return the current status of the incubator.
698*/
699QQmlIncubator::Status QQmlIncubator::status() const
700{
701 return d->status;
702}
703
704/*!
705Return the incubated object if the status is Ready, otherwise 0.
706*/
707QObject *QQmlIncubator::object() const
708{
709 if (status() != Ready)
710 return nullptr;
711 else
712 return d->result;
713}
714
715/*!
716Return a pointer to a list of properties which are required but haven't
717been set yet.
718This list can be modified, so that subclasses which implement special logic
719setInitialProperties can mark properties set there as no longer required.
720
721\sa QQmlIncubator::setInitialProperties
722\since 5.15
723*/
724RequiredProperties *QQmlIncubatorPrivate::requiredProperties()
725{
726 if (creator)
727 return creator->requiredProperties();
728 else
729 return requiredPropertiesFromComponent.data();
730}
731
732bool QQmlIncubatorPrivate::hadTopLevelRequiredProperties() const
733{
734 if (creator)
735 return creator->componentHadTopLevelRequiredProperties();
736 else
737 return requiredPropertiesFromComponent.tag() == HadTopLevelRequired::Yes;
738}
739
740/*!
741Stores a mapping from property names to initial values, contained in
742\a initialProperties, with which the incubated component will be initialized.
743
744\sa QQmlComponent::setInitialProperties
745\since 5.15
746*/
747void QQmlIncubator::setInitialProperties(const QVariantMap &initialProperties)
748{
749 d->initialProperties = initialProperties;
750}
751
752/*!
753Called when the status of the incubator changes. \a status is the new status.
754
755The default implementation does nothing.
756*/
757void QQmlIncubator::statusChanged(Status status)
758{
759 Q_UNUSED(status);
760}
761
762/*!
763Called after the \a object is first created, but before complex property
764bindings are evaluated and, if applicable, QQmlParserStatus::componentComplete()
765is called. This is equivalent to the point between QQmlComponent::beginCreate()
766and QQmlComponent::completeCreate(), and can be used to assign initial values
767to the object's properties.
768
769The default implementation does nothing.
770
771\note Simple bindings such as numeric literals are evaluated before
772setInitialState() is called. The categorization of bindings into simple and
773complex ones is intentionally unspecified and may change between versions of
774Qt and depending on whether and how you are using \l{qmlcachegen}. You should
775not rely on any particular binding to be evaluated either before or after
776setInitialState() is called. For example, a constant expression like
777\e{MyType.EnumValue} may be recognized as such at compile time or deferred
778to be executed as binding. The same holds for constant expressions like
779\e{-(5)} or \e{"a" + " constant string"}.
780*/
781void QQmlIncubator::setInitialState(QObject *object)
782{
783 Q_UNUSED(object);
784}
785
786void QQmlIncubatorPrivate::changeStatus(QQmlIncubator::Status s)
787{
788 if (s == status)
789 return;
790
791 status = s;
792 if (q)
793 q->statusChanged(status);
794}
795
796QQmlIncubator::Status QQmlIncubatorPrivate::calculateStatus() const
797{
798 if (!errors.isEmpty())
799 return QQmlIncubator::Error;
800 else if (result && progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty())
801 return QQmlIncubator::Ready;
802 else if (compilationUnit)
803 return QQmlIncubator::Loading;
804 else
805 return QQmlIncubator::Null;
806}