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