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 "qqmldesignermetaobject_p.h"
5
6#include <QSharedPointer>
7#include <QMetaProperty>
8#include <QtCore/private/qnumeric_p.h>
9#include <QDebug>
10
11#include <private/qqmlengine_p.h>
12
13QT_BEGIN_NAMESPACE
14
15static QHash<QDynamicMetaObjectData *, bool> nodeInstanceMetaObjectList;
16static void (*notifyPropertyChangeCallBack)(QObject*, const QQuickDesignerSupport::PropertyName &propertyName) = nullptr;
17
18struct MetaPropertyData {
19 inline QPair<QVariant, bool> &getDataRef(int idx) {
20 while (m_data.size() <= idx)
21 m_data << QPair<QVariant, bool>(QVariant(), false);
22 return m_data[idx];
23 }
24
25 inline QVariant &getData(int idx) {
26 QPair<QVariant, bool> &prop = getDataRef(idx);
27 if (!prop.second) {
28 prop.first = QVariant();
29 prop.second = true;
30 }
31 return prop.first;
32 }
33
34 inline bool hasData(int idx) const {
35 if (idx >= m_data.size())
36 return false;
37 return m_data[idx].second;
38 }
39
40 inline int count() { return m_data.size(); }
41
42 QVector<QPair<QVariant, bool> > m_data;
43};
44
45static QQmlPropertyCache::ConstPtr cacheForObject(QObject *object)
46{
47 QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(obj: object);
48 if (metaObject)
49 return metaObject->cache;
50
51 return QQmlMetaType::propertyCache(object);
52}
53
54QQmlDesignerMetaObject* QQmlDesignerMetaObject::getNodeInstanceMetaObject(QObject *object, QQmlEngine *engine)
55{
56 //Avoid setting up multiple MetaObjects on the same QObject
57 QObjectPrivate *op = QObjectPrivate::get(o: object);
58 QDynamicMetaObjectData *parent = op->metaObject;
59 if (nodeInstanceMetaObjectList.contains(key: parent))
60 return static_cast<QQmlDesignerMetaObject *>(parent);
61
62 // we just create one and the ownership goes automatically to the object in nodeinstance see init method
63
64 QQmlData *ddata = QQmlData::get(object, create: false);
65
66 const bool hadVMEMetaObject = ddata ? ddata->hasVMEMetaObject : false;
67 QQmlDesignerMetaObject *mo = new QQmlDesignerMetaObject(object, engine);
68 //If our parent is not a VMEMetaObject we just set the flag to false again
69 if (ddata)
70 ddata->hasVMEMetaObject = hadVMEMetaObject;
71 return mo;
72}
73
74void QQmlDesignerMetaObject::init(QObject *object)
75{
76 //Creating QQmlOpenMetaObjectType
77 m_openMetaObject = std::make_unique<QQmlOpenMetaObject>(args&: object, args: metaObjectParent());
78 //Assigning type to this
79 copyTypeMetaObject();
80
81 //Assign this to object
82 QObjectPrivate *op = QObjectPrivate::get(o: object);
83 op->metaObject = this;
84
85 m_cache = QQmlPropertyCache::createStandalone(metaObject.data());
86 cache = m_cache;
87
88 nodeInstanceMetaObjectList.insert(key: this, value: true);
89}
90
91QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine)
92 : QQmlVMEMetaObject(engine->handle(), object, cacheForObject(object), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1),
93 m_context(engine->contextForObject(object)),
94 m_data(new MetaPropertyData)
95{
96 init(object);
97
98 QQmlData *ddata = QQmlData::get(object, create: false);
99 //Assign cache to object
100 if (ddata && ddata->propertyCache) {
101 m_cache->setParent(ddata->propertyCache);
102 m_cache->invalidate(metaObject.data());
103 ddata->propertyCache = m_cache;
104 }
105
106}
107
108QQmlDesignerMetaObject::~QQmlDesignerMetaObject()
109{
110 // m_openMetaObject has this metaobject as its parent.
111 // We need to remove it in order to avoid a dtor recursion
112 m_openMetaObject->unparent();
113 nodeInstanceMetaObjectList.remove(key: this);
114}
115
116void QQmlDesignerMetaObject::createNewDynamicProperty(const QString &name)
117{
118 int id = type()->createProperty(name: name.toUtf8());
119 copyTypeMetaObject();
120 setValue(id, value: QVariant());
121 Q_ASSERT(id >= 0);
122
123 //Updating cache
124 m_cache->invalidate(metaObject.data());
125
126 QQmlProperty property(myObject(), name, m_context);
127 Q_ASSERT(property.isValid());
128}
129
130void QQmlDesignerMetaObject::setValue(int id, const QVariant &value)
131{
132 QPair<QVariant, bool> &prop = m_data->getDataRef(idx: id);
133 prop.first = propertyWriteValue(id, value);
134 prop.second = true;
135 QMetaObject::activate(sender: myObject(), signal_index: id + type()->signalOffset(), argv: nullptr);
136}
137
138QVariant QQmlDesignerMetaObject::propertyWriteValue(int, const QVariant &value)
139{
140 return value;
141}
142
143QDynamicMetaObjectData *QQmlDesignerMetaObject::dynamicMetaObjectParent() const
144{
145 if (QQmlVMEMetaObject::parent.isT1())
146 return QQmlVMEMetaObject::parent.asT1();
147 else
148 return nullptr;
149}
150
151const QMetaObject *QQmlDesignerMetaObject::metaObjectParent() const
152{
153 if (QQmlVMEMetaObject::parent.isT1())
154 return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object);
155
156 return QQmlVMEMetaObject::parent.asT2();
157}
158
159int QQmlDesignerMetaObject::propertyOffset() const
160{
161 return cache->propertyOffset();
162}
163
164int QQmlDesignerMetaObject::openMetaCall(QObject *o, QMetaObject::Call call, int id, void **a)
165{
166 if ((call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty)
167 && id >= type()->propertyOffset()) {
168 int propId = id - type()->propertyOffset();
169 if (call == QMetaObject::ReadProperty) {
170 //propertyRead(propId);
171 *reinterpret_cast<QVariant *>(a[0]) = m_data->getData(idx: propId);
172 } else if (call == QMetaObject::WriteProperty) {
173 if (propId <= m_data->count() || m_data->m_data[propId].first != *reinterpret_cast<QVariant *>(a[0])) {
174 //propertyWrite(propId);
175 QPair<QVariant, bool> &prop = m_data->getDataRef(idx: propId);
176 prop.first = propertyWriteValue(propId, value: *reinterpret_cast<QVariant *>(a[0]));
177 prop.second = true;
178 //propertyWritten(propId);
179 activate(myObject(), type()->signalOffset() + propId, nullptr);
180 }
181 }
182 return -1;
183 } else {
184 QDynamicMetaObjectData *dynamicParent = dynamicMetaObjectParent();
185 if (dynamicParent)
186 return dynamicParent->metaCall(o, call, id: id, a);
187 else
188 return myObject()->qt_metacall(call, id, a);
189 }
190}
191
192int QQmlDesignerMetaObject::metaCall(QObject *o, QMetaObject::Call call, int id, void **a)
193{
194 Q_ASSERT(myObject() == o);
195
196 int metaCallReturnValue = -1;
197
198 const QMetaProperty propertyById = metaObject->property(index: id);
199
200 if (call == QMetaObject::WriteProperty
201 && propertyById.userType() == QMetaType::QVariant
202 && reinterpret_cast<QVariant *>(a[0])->userType() == QMetaType::Double
203 && qt_is_nan(d: reinterpret_cast<QVariant *>(a[0])->toDouble())) {
204 return -1;
205 }
206
207 if (call == QMetaObject::WriteProperty
208 && propertyById.userType() == QMetaType::Double
209 && qt_is_nan(d: *reinterpret_cast<double*>(a[0]))) {
210 return -1;
211 }
212
213 if (call == QMetaObject::WriteProperty
214 && propertyById.userType() == QMetaType::Float
215 && qt_is_nan(f: *reinterpret_cast<float*>(a[0]))) {
216 return -1;
217 }
218
219 QVariant oldValue;
220
221 if (call == QMetaObject::WriteProperty && !propertyById.hasNotifySignal())
222 {
223 oldValue = propertyById.read(obj: myObject());
224 }
225
226 QDynamicMetaObjectData *dynamicParent = dynamicMetaObjectParent();
227 const QMetaObject *staticParent = dynamicParent
228 ? dynamicParent->toDynamicMetaObject(QQmlVMEMetaObject::object)
229 : nullptr;
230 if (staticParent && id < staticParent->propertyOffset())
231 metaCallReturnValue = dynamicParent->metaCall(o, call, id: id, a);
232 else
233 openMetaCall(o, call, id, a);
234
235
236 if (call == QMetaObject::WriteProperty
237 && !propertyById.hasNotifySignal()
238 && oldValue != propertyById.read(obj: myObject()))
239 notifyPropertyChange(id);
240
241 return metaCallReturnValue;
242}
243
244void QQmlDesignerMetaObject::notifyPropertyChange(int id)
245{
246 const QMetaProperty propertyById = metaObject->property(index: id);
247
248 if (id < propertyOffset()) {
249 if (notifyPropertyChangeCallBack)
250 notifyPropertyChangeCallBack(myObject(), propertyById.name());
251 } else {
252 if (notifyPropertyChangeCallBack)
253 notifyPropertyChangeCallBack(myObject(), name(id - propertyOffset()));
254 }
255}
256
257int QQmlDesignerMetaObject::count() const
258{
259 return type()->propertyCount();
260}
261
262QByteArray QQmlDesignerMetaObject::name(int idx) const
263{
264 return type()->propertyName(idx);
265}
266
267void QQmlDesignerMetaObject::copyTypeMetaObject()
268{
269 metaObject = m_openMetaObject.get();
270}
271
272void QQmlDesignerMetaObject::registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const QQuickDesignerSupport::PropertyName &))
273{
274 notifyPropertyChangeCallBack = callback;
275}
276
277QT_END_NAMESPACE
278

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