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
qqmlabstractbinding.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
5
6#include <QtQml/qqmlinfo.h>
7#include <private/qqmlbinding_p.h>
8#include <private/qqmlvaluetypeproxybinding_p.h>
9#include <private/qqmlvmemetaobject_p.h>
10
12
13QQmlAbstractBinding::QQmlAbstractBinding()
14 : m_targetIndex(-1)
15{
16 Q_ASSERT(!isAddedToObject());
17}
18
19QQmlAbstractBinding::~QQmlAbstractBinding()
20{
21 Q_ASSERT(!ref);
22 Q_ASSERT(!isAddedToObject());
23
24 if (m_nextBinding.data() && !m_nextBinding->ref.deref())
25 delete m_nextBinding.data();
26}
27
28/*!
29Add this binding to \a object.
30
31This transfers ownership of the binding to the object, marks the object's property as
32being bound.
33
34However, it does not enable the binding itself or call update() on it.
35*/
36void QQmlAbstractBinding::addToObject()
37{
38 Q_ASSERT(!nextBinding());
39 Q_ASSERT(isAddedToObject() == false);
40
41 QObject *obj = targetObject();
42 Q_ASSERT(obj);
43
44 QQmlData *data = QQmlData::get(obj, true);
45
46 int coreIndex = targetPropertyIndex().coreIndex();
47 if (targetPropertyIndex().hasValueTypeIndex()) {
48 // Value type
49
50 // Find the value type proxy (if there is one)
51 QQmlValueTypeProxyBinding *proxy = nullptr;
52 QQmlAbstractBinding *b = data->bindings;
53 while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
54 b->targetPropertyIndex().hasValueTypeIndex())) {
55 b = b->nextBinding();
56 }
57
58 if (b && b->kind() == QQmlAbstractBinding::ValueTypeProxy) {
59 proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
60 } else {
61 proxy = new QQmlValueTypeProxyBinding(obj, QQmlPropertyIndex(coreIndex));
62
63 Q_ASSERT(proxy->targetPropertyIndex().coreIndex() == coreIndex);
64 Q_ASSERT(!proxy->targetPropertyIndex().hasValueTypeIndex());
65 Q_ASSERT(proxy->targetObject() == obj);
66
67 proxy->addToObject();
68 }
69
70 setNextBinding(proxy->m_bindings.data());
71 proxy->m_bindings = this;
72
73 } else {
74 setNextBinding(data->bindings);
75 if (data->bindings) {
76 data->bindings->ref.deref();
77 Q_ASSERT(data->bindings->ref.refCount > 0);
78 }
79 data->bindings = this;
80 ref.ref();
81
82 data->setBindingBit(obj, coreIndex);
83 }
84
85 setAddedToObject(true);
86}
87
88/*!
89Remove the binding from the object.
90*/
91void QQmlAbstractBinding::removeFromObject()
92{
93 if (!isAddedToObject())
94 return;
95
96 setAddedToObject(false);
97
98 QObject *obj = targetObject();
99 QQmlData *data = QQmlData::get(obj, false);
100 Q_ASSERT(data);
101
102 QQmlAbstractBinding::Ptr next;
103 next = nextBinding();
104 setNextBinding(nullptr);
105
106 int coreIndex = targetPropertyIndex().coreIndex();
107 if (targetPropertyIndex().hasValueTypeIndex()) {
108
109 // Find the value type binding
110 QQmlAbstractBinding *vtbinding = data->bindings;
111 Q_ASSERT(vtbinding);
112 while (vtbinding->targetPropertyIndex().coreIndex() != coreIndex
113 || vtbinding->targetPropertyIndex().hasValueTypeIndex()) {
114 vtbinding = vtbinding->nextBinding();
115 Q_ASSERT(vtbinding);
116 }
117 Q_ASSERT(vtbinding->kind() == QQmlAbstractBinding::ValueTypeProxy);
118
119 QQmlValueTypeProxyBinding *vtproxybinding =
120 static_cast<QQmlValueTypeProxyBinding *>(vtbinding);
121
122 QQmlAbstractBinding *binding = vtproxybinding->m_bindings.data();
123 if (binding == this) {
124 vtproxybinding->m_bindings = next;
125 } else {
126 while (binding->nextBinding() != this) {
127 binding = binding->nextBinding();
128 Q_ASSERT(binding);
129 }
130 binding->setNextBinding(next.data());
131 }
132
133 // Value type - we don't remove the proxy from the object. It will sit their happily
134 // doing nothing until it is removed by a write, a binding change or it is reused
135 // to hold more sub-bindings.
136 return;
137 }
138
139 if (data->bindings == this) {
140 if (next.data())
141 next->ref.ref();
142 data->bindings = next.data();
143 if (!ref.deref())
144 delete this;
145 } else {
146 QQmlAbstractBinding *binding = data->bindings;
147 while (binding->nextBinding() != this) {
148 binding = binding->nextBinding();
149 Q_ASSERT(binding);
150 }
151 binding->setNextBinding(next.data());
152 }
153
154 data->clearBindingBit(coreIndex);
155}
156
157void QQmlAbstractBinding::printBindingLoopError(const QQmlProperty &prop)
158{
159 qmlWarning(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(prop.name());
160}
161
162void QQmlAbstractBinding::getPropertyData(
163 const QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
164{
165 Q_ASSERT(propertyData);
166
167 QQmlData *data = QQmlData::get(m_target.data(), false);
168 Q_ASSERT(data);
169
170 if (Q_UNLIKELY(!data->propertyCache))
171 data->propertyCache = QQmlMetaType::propertyCache(m_target->metaObject());
172
173 *propertyData = data->propertyCache->property(m_targetIndex.coreIndex());
174 Q_ASSERT(*propertyData);
175
176 if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) {
177 const QMetaObject *valueTypeMetaObject
178 = QQmlMetaType::metaObjectForValueType((*propertyData)->propType());
179 Q_ASSERT(valueTypeMetaObject);
180 QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex());
181 valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
182 valueTypeData->setPropType(vtProp.metaType());
183 valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
184 }
185}
186
187void QQmlAbstractBinding::updateCanUseAccessor()
188{
189 setCanUseAccessor(true); // Always use accessors, except when:
190 if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) {
191 if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(m_targetIndex))
192 setCanUseAccessor(false);
193 }
194}
195
196void QQmlAbstractBinding::setTarget(const QQmlProperty &prop)
197{
198 auto pd = QQmlPropertyPrivate::get(prop);
199 setTarget(prop.object(), pd->core, &pd->valueTypeData);
200}
201
202bool QQmlAbstractBinding::setTarget(
203 QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
204{
205 return setTarget(object, core.coreIndex(), core.isAlias(),
206 valueType ? valueType->coreIndex() : -1);
207}
208
209static const QQmlPropertyData *getObjectPropertyData(QObject *object, int coreIndex)
210{
211 QQmlData *data = QQmlData::get(object, true);
212 if (!data)
213 return nullptr;
214
215 if (!data->propertyCache) {
216 data->propertyCache = QQmlMetaType::propertyCache(object);
217 if (!data->propertyCache)
218 return nullptr;
219 }
220
221 const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
222 Q_ASSERT(propertyData);
223 return propertyData;
224}
225
226bool QQmlAbstractBinding::setTarget(
227 QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex)
228{
229 auto invalidate = [this]() {
230 m_target = nullptr;
231 m_targetIndex = QQmlPropertyIndex();
232 return false;
233 };
234
235 if (!object)
236 return invalidate();
237
238 m_target = object;
239
240 for (bool isAlias = coreIsAlias; isAlias;) {
241 QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
242
243 int aValueTypeIndex;
244 if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
245 // can't resolve id (yet)
246 return invalidate();
247 }
248
249 const QQmlPropertyData *propertyData = getObjectPropertyData(object, coreIndex);
250 if (!propertyData)
251 return invalidate();
252
253 if (aValueTypeIndex != -1) {
254 if (propertyData->propType().flags().testFlag(QMetaType::PointerToQObject)) {
255 // deep alias
256 propertyData->readProperty(object, &object);
257 coreIndex = aValueTypeIndex;
258 valueTypeIndex = -1;
259 propertyData = getObjectPropertyData(object, coreIndex);
260 if (!propertyData)
261 return invalidate();
262 } else {
263 valueTypeIndex = aValueTypeIndex;
264 }
265 }
266
267 m_target = object;
268 isAlias = propertyData->isAlias();
269 coreIndex = propertyData->coreIndex();
270 }
271 m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex);
272
273 QQmlData *data = QQmlData::get(m_target.data(), true);
274 if (!data->propertyCache)
275 data->propertyCache = QQmlMetaType::propertyCache(m_target->metaObject());
276
277 return true;
278}
279
280
281QString QQmlAbstractBinding::expression() const
282{
283 return QLatin1String("<Unknown>");
284}
285
286QT_END_NAMESPACE
static const QQmlPropertyData * getObjectPropertyData(QObject *object, int coreIndex)