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