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 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | using namespace Qt::StringLiterals; |
18 | |
19 | typedef QHash<QObject*, QQuickDesignerCustomObjectData*> CustomObjectDataHash; |
20 | Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash) |
21 | |
22 | struct HandleDestroyedFunctor { |
23 | QQuickDesignerCustomObjectData *data; |
24 | void operator()() { data->handleDestroyed(); } |
25 | }; |
26 | |
27 | QQuickDesignerCustomObjectData::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 | |
40 | void QQuickDesignerCustomObjectData::registerData(QObject *object) |
41 | { |
42 | new QQuickDesignerCustomObjectData(object); |
43 | } |
44 | |
45 | QQuickDesignerCustomObjectData *QQuickDesignerCustomObjectData::get(QObject *object) |
46 | { |
47 | return s_designerObjectToDataHash()->value(key: object); |
48 | } |
49 | |
50 | QVariant 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 | |
60 | void 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 | |
68 | bool 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 | |
78 | bool 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 | |
91 | void 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 | |
102 | void 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 | |
112 | void 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 | |
142 | QObject *QQuickDesignerCustomObjectData::object() const |
143 | { |
144 | return m_object; |
145 | } |
146 | |
147 | QVariant QQuickDesignerCustomObjectData::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const |
148 | { |
149 | return m_resetValueHash.value(key: propertyName); |
150 | } |
151 | |
152 | void 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 | |
191 | bool QQuickDesignerCustomObjectData::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const |
192 | { |
193 | return m_resetBindingHash.contains(key: propertyName) && m_resetBindingHash.value(key: propertyName); |
194 | } |
195 | |
196 | QQmlAnyBinding QQuickDesignerCustomObjectData::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const |
197 | { |
198 | return m_resetBindingHash.value(key: propertyName); |
199 | } |
200 | |
201 | bool 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 | |
221 | void 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 | |
252 | void 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 | |
260 | void QQuickDesignerCustomObjectData::handleDestroyed() |
261 | { |
262 | s_designerObjectToDataHash()->remove(key: m_object); |
263 | delete this; |
264 | } |
265 | |
266 | QT_END_NAMESPACE |
267 | |
268 | |