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