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
4#include "qquickdesignersupportmetainfo_p.h"
5#include "qquickdesignersupportproperties_p.h"
6
7#include "qquickdesignercustomobjectdata_p.h"
8
9#include <QGlobalStatic>
10#include <QQmlContext>
11#include <QQmlEngine>
12
13#include <private/qqmlanybinding_p.h>
14
15QT_BEGIN_NAMESPACE
16
17using namespace Qt::StringLiterals;
18
19typedef QHash<QObject*, QQuickDesignerCustomObjectData*> CustomObjectDataHash;
20Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash)
21
22struct HandleDestroyedFunctor {
23 QQuickDesignerCustomObjectData *data;
24 void operator()() { data->handleDestroyed(); }
25};
26
27QQuickDesignerCustomObjectData::QQuickDesignerCustomObjectData(QObject *object)
28 : m_object(object)
29{
30 if (object) {
31 populateResetHashes();
32 s_designerObjectToDataHash()->insert(key: object, value: this);
33
34 HandleDestroyedFunctor functor;
35 functor.data = this;
36 QObject::connect(sender: object, signal: &QObject::destroyed, slot&: functor);
37 }
38}
39
40void QQuickDesignerCustomObjectData::registerData(QObject *object)
41{
42 new QQuickDesignerCustomObjectData(object);
43}
44
45QQuickDesignerCustomObjectData *QQuickDesignerCustomObjectData::get(QObject *object)
46{
47 return s_designerObjectToDataHash()->value(key: object);
48}
49
50QVariant QQuickDesignerCustomObjectData::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
51{
52 QQuickDesignerCustomObjectData* data = get(object);
53
54 if (data)
55 return data->getResetValue(propertyName);
56
57 return QVariant();
58}
59
60void QQuickDesignerCustomObjectData::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
61{
62 QQuickDesignerCustomObjectData* data = get(object);
63
64 if (data)
65 data->doResetProperty(context, propertyName);
66}
67
68bool QQuickDesignerCustomObjectData::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
69{
70 QQuickDesignerCustomObjectData* data = get(object);
71
72 if (data)
73 return data->hasValidResetBinding(propertyName);
74
75 return false;
76}
77
78bool QQuickDesignerCustomObjectData::hasBindingForProperty(QObject *object,
79 QQmlContext *context,
80 const QQuickDesignerSupport::PropertyName &propertyName,
81 bool *hasChanged)
82{
83 QQuickDesignerCustomObjectData* data = get(object);
84
85 if (data)
86 return data->hasBindingForProperty(context, propertyName, hasChanged);
87
88 return false;
89}
90
91void QQuickDesignerCustomObjectData::setPropertyBinding(QObject *object,
92 QQmlContext *context,
93 const QQuickDesignerSupport::PropertyName &propertyName,
94 const QString &expression)
95{
96 QQuickDesignerCustomObjectData* data = get(object);
97
98 if (data)
99 data->setPropertyBinding(context, propertyName, expression);
100}
101
102void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QObject *object,
103 QQmlContext *context,
104 const QQuickDesignerSupport::PropertyName &propertyName)
105{
106 QQuickDesignerCustomObjectData* data = get(object);
107
108 if (data)
109 data->keepBindingFromGettingDeleted(context, propertyName);
110}
111
112void QQuickDesignerCustomObjectData::populateResetHashes()
113{
114 const QQuickDesignerSupport::PropertyNameList propertyNameList =
115 QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object: object());
116
117 const QMetaObject *mo = object()->metaObject();
118 QByteArrayList deferredPropertyNames;
119 const int namesIndex = mo->indexOfClassInfo(name: "DeferredPropertyNames");
120 if (namesIndex != -1) {
121 QMetaClassInfo classInfo = mo->classInfo(index: namesIndex);
122 deferredPropertyNames = QByteArray(classInfo.value()).split(sep: ',');
123 }
124
125 for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) {
126
127 if (deferredPropertyNames.contains(t: propertyName))
128 continue;
129
130 QQmlProperty property(object(), QString::fromUtf8(ba: propertyName), QQmlEngine::contextForObject(object()));
131
132 auto binding = QQmlAnyBinding::ofProperty(prop: property);
133
134 if (binding) {
135 m_resetBindingHash.insert(key: propertyName, value: binding);
136 } else if (property.isWritable()) {
137 m_resetValueHash.insert(key: propertyName, value: property.read());
138 }
139 }
140}
141
142QObject *QQuickDesignerCustomObjectData::object() const
143{
144 return m_object;
145}
146
147QVariant QQuickDesignerCustomObjectData::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const
148{
149 return m_resetValueHash.value(key: propertyName);
150}
151
152void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
153{
154 QQmlProperty property(object(), QString::fromUtf8(ba: propertyName), context);
155
156 if (!property.isValid())
157 return;
158
159 // remove existing binding
160 QQmlAnyBinding::takeFrom(prop: property);
161
162
163 if (hasValidResetBinding(propertyName)) {
164 QQmlAnyBinding binding = getResetBinding(propertyName);
165 binding.installOn(target: property);
166
167 if (binding.isAbstractPropertyBinding()) {
168 // for new style properties, we will evaluate during setBinding anyway
169 static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
170 }
171
172 } else if (property.isResettable()) {
173 property.reset();
174 } else if (property.propertyTypeCategory() == QQmlProperty::List) {
175 QQmlListReference list = qvariant_cast<QQmlListReference>(v: property.read());
176
177 if (!QQuickDesignerSupportProperties::hasFullImplementedListInterface(list)) {
178 qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!";
179 return;
180 }
181
182 list.clear();
183 } else if (property.isWritable()) {
184 if (property.read() == getResetValue(propertyName))
185 return;
186
187 property.write(getResetValue(propertyName));
188 }
189}
190
191bool QQuickDesignerCustomObjectData::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
192{
193 return m_resetBindingHash.contains(key: propertyName) && m_resetBindingHash.value(key: propertyName);
194}
195
196QQmlAnyBinding QQuickDesignerCustomObjectData::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
197{
198 return m_resetBindingHash.value(key: propertyName);
199}
200
201bool QQuickDesignerCustomObjectData::hasBindingForProperty(QQmlContext *context,
202 const QQuickDesignerSupport::PropertyName &propertyName,
203 bool *hasChanged) const
204{
205 if (QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName))
206 return false;
207
208 QQmlProperty property(object(), QString::fromUtf8(ba: propertyName), context);
209
210 bool hasBinding = QQmlAnyBinding::ofProperty(prop: property);
211
212 if (hasChanged) {
213 *hasChanged = hasBinding != m_hasBindingHash.value(key: propertyName, defaultValue: false);
214 if (*hasChanged)
215 m_hasBindingHash.insert(key: propertyName, value: hasBinding);
216 }
217
218 return hasBinding;
219}
220
221void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context,
222 const QQuickDesignerSupport::PropertyName &propertyName,
223 const QString &expression)
224{
225 QQmlProperty property(object(), QString::fromUtf8(ba: propertyName), context);
226
227 if (!property.isValid())
228 return;
229
230 if (property.isProperty()) {
231 QString url = u"@designer"_s;
232 int lineNumber = 0;
233 QQmlAnyBinding binding = QQmlAnyBinding::createFromCodeString(prop: property,
234 code: expression, obj: object(), ctxt: QQmlContextData::get(context), url, lineNumber);
235
236 binding.installOn(target: property);
237 if (binding.isAbstractPropertyBinding()) {
238 // for new style properties, we will evaluate during setBinding anyway
239 static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
240 }
241
242 if (binding.hasError()) {
243 if (property.property().userType() == QMetaType::QString)
244 property.write(QVariant(QLatin1Char('#') + expression + QLatin1Char('#')));
245 }
246
247 } else {
248 qWarning() << Q_FUNC_INFO << ": Cannot set binding for property" << propertyName << ": property is unknown for type";
249 }
250}
251
252void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QQmlContext *context,
253 const QQuickDesignerSupport::PropertyName &propertyName)
254{
255 //Refcounting is taking care
256 Q_UNUSED(context);
257 Q_UNUSED(propertyName);
258}
259
260void QQuickDesignerCustomObjectData::handleDestroyed()
261{
262 s_designerObjectToDataHash()->remove(key: m_object);
263 delete this;
264}
265
266QT_END_NAMESPACE
267
268

source code of qtdeclarative/src/quick/designer/qquickdesignercustomobjectdata.cpp