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 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | static QHash<QDynamicMetaObjectData *, bool> nodeInstanceMetaObjectList; |
16 | static void (*notifyPropertyChangeCallBack)(QObject*, const QQuickDesignerSupport::PropertyName &propertyName) = nullptr; |
17 | |
18 | struct 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 | |
45 | static 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 | |
54 | QQmlDesignerMetaObject* 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 | |
74 | void 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 | |
91 | QQmlDesignerMetaObject::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 | |
108 | QQmlDesignerMetaObject::~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 | |
116 | void 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 | |
130 | void 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 | |
138 | QVariant QQmlDesignerMetaObject::propertyWriteValue(int, const QVariant &value) |
139 | { |
140 | return value; |
141 | } |
142 | |
143 | QDynamicMetaObjectData *QQmlDesignerMetaObject::dynamicMetaObjectParent() const |
144 | { |
145 | if (QQmlVMEMetaObject::parent.isT1()) |
146 | return QQmlVMEMetaObject::parent.asT1(); |
147 | else |
148 | return nullptr; |
149 | } |
150 | |
151 | const 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 | |
159 | int QQmlDesignerMetaObject::propertyOffset() const |
160 | { |
161 | return cache->propertyOffset(); |
162 | } |
163 | |
164 | int 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 | |
192 | int 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 | |
244 | void 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 | |
257 | int QQmlDesignerMetaObject::count() const |
258 | { |
259 | return type()->propertyCount(); |
260 | } |
261 | |
262 | QByteArray QQmlDesignerMetaObject::name(int idx) const |
263 | { |
264 | return type()->propertyName(idx); |
265 | } |
266 | |
267 | void QQmlDesignerMetaObject::copyTypeMetaObject() |
268 | { |
269 | metaObject = m_openMetaObject.get(); |
270 | } |
271 | |
272 | void QQmlDesignerMetaObject::registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const QQuickDesignerSupport::PropertyName &)) |
273 | { |
274 | notifyPropertyChangeCallBack = callback; |
275 | } |
276 | |
277 | QT_END_NAMESPACE |
278 | |