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