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