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
qqmlpropertymap.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 <private/qmetaobjectbuilder_p.h>
7#include <private/qqmlopenmetaobject_p.h>
8
9#include <QDebug>
10
12
13//QQmlPropertyMapMetaObject lets us listen for changes coming from QML
14//so we can emit the changed signal.
16{
17public:
18 QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv, const QMetaObject *staticMetaObject);
19
20protected:
21 QVariant propertyWriteValue(int, const QVariant &) override;
22 void propertyWritten(int index) override;
23 void propertyCreated(int, QMetaPropertyBuilder &) override;
24
25 const QString &propertyName(int index);
26
27private:
28 QQmlPropertyMap *map;
30};
31
33{
34 Q_DECLARE_PUBLIC(QQmlPropertyMap)
35public:
38
39 QVariant updateValue(const QString &key, const QVariant &input);
40 void emitChanged(const QString &key, const QVariant &value);
41 static bool validKeyName(const QString& name);
42
43 const QString &propertyName(int index) const;
44};
45
46bool QQmlPropertyMapPrivate::validKeyName(const QString& name)
47{
48 //The following strings shouldn't be used as property names
49 return name != QLatin1String("keys")
50 && name != QLatin1String("valueChanged")
51 && name != QLatin1String("QObject")
52 && name != QLatin1String("destroyed")
53 && name != QLatin1String("deleteLater");
54}
55
56QVariant QQmlPropertyMapPrivate::updateValue(const QString &key, const QVariant &input)
57{
58 Q_Q(QQmlPropertyMap);
59 return q->updateValue(key, input);
60}
61
62void QQmlPropertyMapPrivate::emitChanged(const QString &key, const QVariant &value)
63{
64 Q_Q(QQmlPropertyMap);
65 emit q->valueChanged(key, value);
66}
67
69{
70 Q_ASSERT(index < keys.size());
71 return keys[index];
72}
73
74QQmlPropertyMapMetaObject::QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv, const QMetaObject *staticMetaObject)
76{
77 map = obj;
78 priv = objPriv;
79}
80
81QVariant QQmlPropertyMapMetaObject::propertyWriteValue(int index, const QVariant &input)
82{
83 return priv->updateValue(priv->propertyName(index), input);
84}
85
87{
88 priv->emitChanged(priv->propertyName(index), value(index));
89}
90
91void QQmlPropertyMapMetaObject::propertyCreated(int, QMetaPropertyBuilder &b)
92{
93 priv->keys.append(QString::fromUtf8(b.name()));
94}
95
96/*!
97 \class QQmlPropertyMap
98 \brief The QQmlPropertyMap class allows you to set key-value pairs that can be used in QML bindings.
99 \inmodule QtQml
100
101 QQmlPropertyMap provides a convenient way to expose domain data to the UI layer.
102 The following example shows how you might declare data in C++ and then
103 access it in QML.
104
105 In the C++ file:
106 \code
107 // create our data
108 QQmlPropertyMap ownerData;
109 ownerData.insert("name", QVariant(QString("John Smith")));
110 ownerData.insert("phone", QVariant(QString("555-5555")));
111
112 // expose it to the UI layer
113 QQuickView view;
114 QQmlContext *ctxt = view.rootContext();
115 ctxt->setContextProperty("owner", &ownerData);
116
117 view.setSource(QUrl::fromLocalFile("main.qml"));
118 view.show();
119 \endcode
120
121 Then, in \c main.qml:
122 \code
123 Text { text: owner.name + " " + owner.phone }
124 \endcode
125
126 The binding is dynamic - whenever a key's value is updated, anything bound to that
127 key will be updated as well.
128
129 To detect value changes made in the UI layer you can connect to the valueChanged() signal.
130 However, note that valueChanged() is \b NOT emitted when changes are made by calling insert()
131 or clear() - it is only emitted when a value is updated from QML.
132
133 \note It is not possible to remove keys from the map; once a key has been added, you can only
134 modify or clear its associated value.
135
136 \note When deriving a class from QQmlPropertyMap, use the
137 \l {QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)} {protected two-argument constructor}
138 which ensures that the class is correctly registered with the Qt \l {Meta-Object System}.
139
140 \note The QMetaObject of a QQmlPropertyMap is dynamically generated and modified.
141 Operations on that meta object are not thread safe, so applications need to take
142 care to explicitly synchronize access to the meta object.
143*/
144
145#if QT_DEPRECATED_SINCE(6, 11)
146/*!
147 \deprecated [6.11] Use the factory or the protected two-argument constructor instead.
148
149 Constructs a bindable map with parent object \a parent.
150*/
151QQmlPropertyMap::QQmlPropertyMap(QObject *parent)
152: QQmlPropertyMap(&staticMetaObject, parent)
153{
154}
155#endif // QT_DEPRECATED_SINCE(6, 11)
156
157/*!
158 Creates a bindable map with parent object \a parent.
159*/
160QQmlPropertyMap *QQmlPropertyMap::create(QObject *parent)
161{
162 return new QQmlPropertyMap(&staticMetaObject, parent);
163}
164
165/*!
166 Destroys the bindable map.
167*/
168QQmlPropertyMap::~QQmlPropertyMap()
169{
170}
171
172/*!
173 Clears the value (if any) associated with \a key.
174*/
175void QQmlPropertyMap::clear(const QString &key)
176{
177 Q_D(QQmlPropertyMap);
178 if (d->validKeyName(key))
179 d->mo->setValue(key.toUtf8(), QVariant());
180}
181
182/*!
183 \since 6.1
184
185 Disallows any further properties to be added to this property map.
186 Existing properties can be modified or cleared.
187
188 In turn, an internal cache is turned on for the existing properties, which
189 may result in faster access from QML.
190 */
191void QQmlPropertyMap::freeze()
192{
193 Q_D(QQmlPropertyMap);
194 d->mo->setAutoCreatesProperties(false);
195 d->mo->setCached(true);
196}
197
198/*!
199 Returns the value associated with \a key.
200
201 If no value has been set for this key (or if the value has been cleared),
202 an invalid QVariant is returned.
203*/
204QVariant QQmlPropertyMap::value(const QString &key) const
205{
206 Q_D(const QQmlPropertyMap);
207 return d->mo->value(key.toUtf8());
208}
209
210/*!
211 Sets the value associated with \a key to \a value.
212
213 If the key doesn't exist, it is automatically created.
214*/
215void QQmlPropertyMap::insert(const QString &key, const QVariant &value)
216{
217 Q_D(QQmlPropertyMap);
218
219 if (d->validKeyName(key)) {
220 d->mo->setValue(key.toUtf8(), value);
221 } else {
222 qWarning() << "Creating property with name"
223 << key
224 << "is not permitted, conflicts with internal symbols.";
225 }
226}
227
228/*!
229 \since 6.1
230
231 Inserts the \a values into the QQmlPropertyMap.
232
233 Keys that don't exist are automatically created.
234
235 This method is substantially faster than calling \c{insert(key, value)}
236 many times in a row.
237*/
238void QQmlPropertyMap::insert(const QVariantHash &values)
239{
240 Q_D(QQmlPropertyMap);
241
242 QHash<QByteArray, QVariant> checkedValues;
243 for (auto it = values.begin(), end = values.end(); it != end; ++it) {
244 const QString &key = it.key();
245 if (!d->validKeyName(key)) {
246 qWarning() << "Creating property with name"
247 << key
248 << "is not permitted, conflicts with internal symbols.";
249 return;
250 }
251
252 checkedValues.insert(key.toUtf8(), it.value());
253 }
254 d->mo->setValues(checkedValues);
255
256}
257
258/*!
259 Returns the list of keys.
260
261 Keys that have been cleared will still appear in this list, even though their
262 associated values are invalid QVariants.
263*/
264QStringList QQmlPropertyMap::keys() const
265{
266 Q_D(const QQmlPropertyMap);
267 return d->keys;
268}
269
270/*!
271 \overload
272
273 Same as size().
274*/
275int QQmlPropertyMap::count() const
276{
277 Q_D(const QQmlPropertyMap);
278 return d->keys.size();
279}
280
281/*!
282 Returns the number of keys in the map.
283
284 \sa isEmpty(), count()
285*/
286int QQmlPropertyMap::size() const
287{
288 Q_D(const QQmlPropertyMap);
289 return d->keys.size();
290}
291
292/*!
293 Returns true if the map contains no keys; otherwise returns
294 false.
295
296 \sa size()
297*/
298bool QQmlPropertyMap::isEmpty() const
299{
300 Q_D(const QQmlPropertyMap);
301 return d->keys.isEmpty();
302}
303
304/*!
305 Returns true if the map contains \a key.
306
307 \sa size()
308*/
309bool QQmlPropertyMap::contains(const QString &key) const
310{
311 Q_D(const QQmlPropertyMap);
312 return d->keys.contains(key);
313}
314
315/*!
316 Returns the value associated with the key \a key as a modifiable
317 reference.
318
319 If the map contains no item with key \a key, the function inserts
320 an invalid QVariant into the map with key \a key, and
321 returns a reference to it.
322
323 \sa insert(), value()
324*/
325QVariant &QQmlPropertyMap::operator[](const QString &key)
326{
327 //### optimize
328 Q_D(QQmlPropertyMap);
329 QByteArray utf8key = key.toUtf8();
330 if (!d->keys.contains(key))
331 insert(key, QVariant());//force creation -- needed below
332
333 return d->mo->valueRef(utf8key);
334}
335
336/*!
337 \overload
338
339 Same as value().
340*/
341QVariant QQmlPropertyMap::operator[](const QString &key) const
342{
343 return value(key);
344}
345
346/*!
347 Returns the new value to be stored for the key \a key. This function is provided
348 to intercept updates to a property from QML, where the value provided from QML is \a input.
349
350 Override this function to manipulate the property value as it is updated. Note that
351 this function is only invoked when the value is updated from QML.
352*/
353QVariant QQmlPropertyMap::updateValue(const QString &key, const QVariant &input)
354{
355 Q_UNUSED(key);
356 return input;
357}
358
359/*! \internal */
360QQmlPropertyMap::QQmlPropertyMap(const QMetaObject *staticMetaObject, QObject *parent)
361 : QObject(*(new QQmlPropertyMapPrivate), parent)
362{
363 Q_D(QQmlPropertyMap);
364 d->mo = new QQmlPropertyMapMetaObject(this, d, staticMetaObject);
365}
366
367/*!
368 \fn void QQmlPropertyMap::valueChanged(const QString &key, const QVariant &value)
369 This signal is emitted whenever one of the values in the map is changed. \a key
370 is the key corresponding to the \a value that was changed.
371
372 \note valueChanged() is \b NOT emitted when changes are made by calling insert()
373 or clear() - it is only emitted when a value is updated from QML.
374*/
375
376/*!
377 \fn template<class DerivedType> QQmlPropertyMap::QQmlPropertyMap(DerivedType *derived, QObject *parent)
378
379 Constructs a bindable map with parent object \a parent. Use this constructor
380 in classes derived from QQmlPropertyMap.
381
382 The type of \a derived is used to register the property map with the \l {Meta-Object System},
383 which is necessary to ensure that properties of the derived class are accessible.
384 This type must be derived from QQmlPropertyMap.
385
386 In the C++ file:
387 \snippet code/src_qml_qqmlpropertymap.cpp 0
388
389 \snippet code/src_qml_qqmlpropertymap.cpp 1
390
391 Then, in \c main.qml:
392 \code
393 MyQmlPropertyMap
394 {
395 id : owner
396 Component.onCompleted: { owner.updateEmail("new.email@example.com") }
397 }
398 Text { text : owner.name + " " + owner.phone + " " + owner.email }
399 \endcode
400*/
401
402QT_END_NAMESPACE
403
404#include "moc_qqmlpropertymap.cpp"
const QString & propertyName(int index)
void propertyWritten(int index) override
QVariant propertyWriteValue(int, const QVariant &) override
void propertyCreated(int, QMetaPropertyBuilder &) override
QQmlPropertyMapMetaObject(QQmlPropertyMap *obj, QQmlPropertyMapPrivate *objPriv, const QMetaObject *staticMetaObject)
QVariant updateValue(const QString &key, const QVariant &input)
void emitChanged(const QString &key, const QVariant &value)
const QString & propertyName(int index) const
static bool validKeyName(const QString &name)