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