1// Copyright (C) 2017 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
4#include "qquickdeferredexecute_p_p.h"
5
6#include <QtCore/qhash.h>
7#include <QtQml/qqmlengine.h>
8#include <QtQml/private/qqmldata_p.h>
9#include <QtQml/private/qqmlcomponent_p.h>
10#include <QtQml/private/qqmlobjectcreator_p.h>
11
12#include <deque>
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 \internal
18
19 For the history behind why these functions were introduced, see
20 the comments of QTBUG-50992, specifically
21 \l {https://bugreports.qt.io/browse/QTBUG-50992?focusedId=325677&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-325677}{this}.
22
23 The first commit to the QML engine to support deferred properties seems to
24 be 59b8d719d6122d86a4cc650911788cc4d778ce29.
25
26 The first commit to add support for it in Controls was
27 458eb65f730178bc93ba7b18f0e4dd2a13efad2e.
28
29 In short, deferred execution solved two issues:
30
31 \list 1
32 \li Incubation issues when using asynchronous loaders, AKA QTBUG-50992.
33 \li Performance issues from creating two items unnecessarily when a
34 styled control was customized, which is explained in more detail
35 in the commit message of 458eb65f730178bc93ba7b18f0e4dd2a13efad2e.
36 \endlist
37
38 \sa qmlExecuteDeferred
39*/
40
41namespace QtQuickPrivate {
42
43static void cancelDeferred(QQmlData *ddata, int propertyIndex)
44{
45 auto dit = ddata->deferredData.rbegin();
46 while (dit != ddata->deferredData.rend()) {
47 (*dit)->bindings.remove(key: propertyIndex);
48 ++dit;
49 }
50}
51
52static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &property, QQmlComponentPrivate::DeferredState *deferredState)
53{
54 QObject *object = property.object();
55 QQmlData *ddata = QQmlData::get(object);
56 Q_ASSERT(!ddata->deferredData.isEmpty());
57
58 if (!ddata->propertyCache)
59 ddata->propertyCache = QQmlMetaType::propertyCache(metaObject: object->metaObject());
60
61 int propertyIndex = property.index();
62 int wasInProgress = enginePriv->inProgressCreations;
63
64 /* we don't want deferred properties to suddenly depend on arbitrary
65 other properties which might have trigerred the construction of
66 objects as a consequence of a read.
67 */
68 auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
69 auto cleanup = qScopeGuard(f: [&](){
70 QtPrivate::restoreBindingStatus(status: bindingStatus);
71 });
72
73 for (auto dit = ddata->deferredData.rbegin(); dit != ddata->deferredData.rend(); ++dit) {
74 QQmlData::DeferredData *deferData = *dit;
75
76 auto bindings = deferData->bindings;
77 auto range = bindings.equal_range(key: propertyIndex);
78 if (range.first == bindings.end())
79 continue;
80
81 QQmlComponentPrivate::ConstructionState state;
82 state.setCompletePending(true);
83
84 QQmlContextData *creationContext = nullptr;
85 state.initCreator(parentContext: deferData->context->parent(), compilationUnit: deferData->compilationUnit, creationContext);
86
87 enginePriv->inProgressCreations++;
88
89 std::deque<const QV4::CompiledData::Binding *> reversedBindings;
90 std::copy(first: range.first, last: range.second, result: std::front_inserter(x&: reversedBindings));
91 state.creator()->beginPopulateDeferred(context: deferData->context);
92 for (const QV4::CompiledData::Binding *binding : reversedBindings)
93 state.creator()->populateDeferredBinding(qmlProperty: property, deferredIndex: deferData->deferredIdx, binding);
94 state.creator()->finalizePopulateDeferred();
95 state.appendCreatorErrors();
96
97 deferredState->push_back(x: std::move(state));
98
99 // Cleanup any remaining deferred bindings for this property, also in inner contexts,
100 // to avoid executing them later and overriding the property that was just populated.
101 cancelDeferred(ddata, propertyIndex);
102 break;
103 }
104
105 return enginePriv->inProgressCreations > wasInProgress;
106}
107
108void beginDeferred(QObject *object, const QString &property,
109 QQuickUntypedDeferredPointer *delegate, bool isOwnState)
110{
111 QQmlData *data = QQmlData::get(object);
112 if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object) && data->context) {
113 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine());
114
115 QQmlComponentPrivate::DeferredState state;
116 if (beginDeferred(enginePriv: ep, property: QQmlProperty(object, property), deferredState: &state)) {
117 if (QQmlComponentPrivate::DeferredState *delegateState = delegate->deferredState())
118 delegateState->swap(x&: state);
119 } else if (isOwnState) {
120 delegate->clearDeferredState();
121 }
122
123 // Release deferred data for those compilation units that no longer have deferred bindings
124 data->releaseDeferredData();
125 } else if (isOwnState) {
126 delegate->clearDeferredState();
127 }
128}
129
130void cancelDeferred(QObject *object, const QString &property)
131{
132 QQmlData *data = QQmlData::get(object);
133 if (data)
134 cancelDeferred(ddata: data, propertyIndex: QQmlProperty(object, property).index());
135}
136
137void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate)
138{
139 Q_UNUSED(property);
140 QQmlComponentPrivate::DeferredState *state = delegate->deferredState();
141 if (!state)
142 return;
143
144 QQmlData *data = QQmlData::get(object);
145 if (data && !data->wasDeleted(object)) {
146 /* we don't want deferred properties to suddenly depend on arbitrary
147 other properties which might have trigerred the construction of
148 objects as a consequence of a read.
149 */
150 auto bindingStatus = QtPrivate::suspendCurrentBindingStatus();
151 auto cleanup = qScopeGuard(f: [&](){
152 QtPrivate::restoreBindingStatus(status: bindingStatus);
153 });
154
155 QQmlComponentPrivate::DeferredState localState = std::move(*state);
156 delegate->clearDeferredState();
157 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine());
158 QQmlComponentPrivate::completeDeferred(enginePriv: ep, deferredState: &localState);
159 } else {
160 delegate->clearDeferredState();
161 }
162}
163
164} // QtQuickPrivate
165
166QT_END_NAMESPACE
167

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/quicktemplates/qquickdeferredexecute.cpp