Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qqmlcontext.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
5
#
include
"qqmlcontext_p.h"
6
#
include
"qqmlcomponentattached_p.h"
7
8
#
include
"qqmlcomponent_p.h"
9
#
include
"qqmlexpression_p.h"
10
#
include
"qqmlengine_p.h"
11
#
include
"qqmlengine.h"
12
#
include
"qqmlinfo.h"
13
#
include
"qqmlabstracturlinterceptor.h"
14
15
#
include
<
qjsengine
.
h
>
16
#
include
<
QtCore
/
qvarlengtharray
.
h
>
17
#
include
<
private
/
qmetaobject_p
.
h
>
18
#
include
<
QtCore
/
qdebug
.
h
>
19
#
include
<
QtCore
/
qqueue
.
h
>
20
21
QT_BEGIN_NAMESPACE
22
23
/*!
24
\class QQmlContext
25
\brief The QQmlContext class defines a context within a QML engine.
26
\inmodule QtQml
27
28
Contexts hold the objects identified by \e id in a QML document. You
29
can use \l{nameForObject()} and \l{objectForName()} to retrieve them.
30
31
\note It is the responsibility of the creator to delete any QQmlContext it
32
constructs. If a QQmlContext is no longer needed, it must be destroyed
33
explicitly. The simplest way to ensure this is to give the QQmlContext a
34
\l{QObject::setParent()}{parent}.
35
36
\section1 The Context Hierarchy
37
38
Contexts form a hierarchy. The root of this hierarchy is the QML engine's
39
\l {QQmlEngine::rootContext()}{root context}. Each QML component creates its
40
own context when instantiated and some QML elements create extra contexts
41
for themselves.
42
43
While QML objects instantiated in a context are not strictly owned by that
44
context, their bindings are. If a context is destroyed, the property bindings of
45
outstanding QML objects will stop evaluating.
46
47
\section1 Context Properties
48
49
Contexts also allow data to be exposed to the QML components instantiated
50
by the QML engine. Such data is invisible to any tooling, including the
51
\l{Qt Quick Compiler} and to future readers of the QML documents in
52
question. It will only be exposed if the QML component is instantiated in
53
the specific C++ context you are envisioning. In other places, different
54
context data may be exposed instead.
55
56
Instead of using the QML context to expose data to your QML components, you
57
should either create additional object properties to hold the data or use
58
\l{QML_SINGLETON}{singletons}. See
59
\l{qtqml-cppintegration-exposecppstate.html}{Exposing C++ State to QML} for
60
a detailed explanation.
61
62
Each QQmlContext contains a set of properties, distinct from its QObject
63
properties, that allow data to be explicitly bound to a context by name. The
64
context properties can be defined and updated by calling
65
QQmlContext::setContextProperty().
66
67
To simplify binding and maintaining larger data sets, a context object can be set
68
on a QQmlContext. All the properties of the context object are available
69
by name in the context, as though they were all individually added through calls
70
to QQmlContext::setContextProperty(). Changes to the property's values are
71
detected through the property's notify signal. Setting a context object is both
72
faster and easier than manually adding and maintaining context property values.
73
74
All properties added explicitly by QQmlContext::setContextProperty() take
75
precedence over the context object's properties.
76
77
Child contexts inherit the context properties of their parents; if a child
78
context sets a context property that already exists in its parent, the new
79
context property overrides that of the parent.
80
81
\warning Setting the context object or adding new context properties after
82
an object has been created in that context is an expensive operation
83
(essentially forcing all bindings to re-evaluate). Thus, if you need to use
84
context properties, you should at least complete the "setup" of the context
85
before using it to create any objects.
86
87
\sa {qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Types to QML}
88
*/
89
90
/*! \internal */
91
QQmlContext::QQmlContext(QQmlEngine *e,
bool
)
92
: QObject(*(
new
QQmlContextPrivate(
this
, QQmlRefPointer<QQmlContextData>(), e)))
93
{
94
}
95
96
/*!
97
Create a new QQmlContext as a child of \a engine's root context, and the
98
QObject \a parent.
99
*/
100
QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent)
101
: QObject(*(
new
QQmlContextPrivate(
this
, engine
102
? QQmlContextData::get(engine->rootContext())
103
: QQmlRefPointer<QQmlContextData>())), parent)
104
{
105
}
106
107
/*!
108
Create a new QQmlContext with the given \a parentContext, and the
109
QObject \a parent.
110
*/
111
QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent)
112
: QObject(*(
new
QQmlContextPrivate(
this
, parentContext
113
? QQmlContextData::get(parentContext)
114
: QQmlRefPointer<QQmlContextData>())), parent)
115
{
116
}
117
118
/*!
119
\internal
120
*/
121
QQmlContext::QQmlContext(QQmlContextPrivate &dd, QObject *parent)
122
: QObject(dd, parent)
123
{
124
}
125
126
/*!
127
Destroys the QQmlContext.
128
129
Any expressions, or sub-contexts dependent on this context will be
130
invalidated, but not destroyed (unless they are parented to the QQmlContext
131
object).
132
*/
133
QQmlContext::~QQmlContext()
134
{
135
Q_D(QQmlContext);
136
d->m_data->clearPublicContext();
137
}
138
139
/*!
140
Returns whether the context is valid.
141
142
To be valid, a context must have a engine, and it's contextObject(), if any,
143
must not have been deleted.
144
*/
145
bool
QQmlContext::isValid()
const
146
{
147
Q_D(
const
QQmlContext);
148
return
d->m_data->isValid();
149
}
150
151
/*!
152
Return the context's QQmlEngine, or \nullptr if the context has no QQmlEngine or the
153
QQmlEngine was destroyed.
154
*/
155
QQmlEngine *QQmlContext::engine()
const
156
{
157
Q_D(
const
QQmlContext);
158
return
d->m_data->engine();
159
}
160
161
/*!
162
Return the context's parent QQmlContext, or \nullptr if this context has no
163
parent or if the parent has been destroyed.
164
*/
165
QQmlContext *QQmlContext::parentContext()
const
166
{
167
Q_D(
const
QQmlContext);
168
169
if
(QQmlRefPointer<QQmlContextData> parent = d->m_data->parent())
170
return
parent->asQQmlContext();
171
return
nullptr
;
172
}
173
174
/*!
175
\since 6.11
176
177
Return the context's immediate child QQmlContexts.
178
179
\sa parentContext(), findObjectRecursively(), findObjectsRecursively()
180
*/
181
QList<QQmlContext *> QQmlContext::childContexts()
const
182
{
183
Q_D(
const
QQmlContext);
184
QList<QQmlContext *> result;
185
for
(
auto
child = d->m_data->childContexts(); child; child = child->nextChild())
186
result.append(child->asQQmlContext());
187
return
result;
188
}
189
190
/*!
191
Return the context object, or \nullptr if there is no context object.
192
*/
193
QObject *QQmlContext::contextObject()
const
194
{
195
Q_D(
const
QQmlContext);
196
return
d->m_data->contextObject();
197
}
198
199
/*!
200
Set the context \a object.
201
202
\note You should not use context objects to inject values into your QML
203
components. Use singletons or regular object properties instead.
204
*/
205
void
QQmlContext::setContextObject(QObject *object)
206
{
207
Q_D(QQmlContext);
208
209
QQmlRefPointer<QQmlContextData> data = d->m_data;
210
211
if
(data->isInternal()) {
212
qWarning(
"QQmlContext: Cannot set context object for internal context."
);
213
return
;
214
}
215
216
if
(!data->isValid()) {
217
qWarning(
"QQmlContext: Cannot set context object on invalid context."
);
218
return
;
219
}
220
221
data->setContextObject(object);
222
data->refreshExpressions();
223
}
224
225
/*!
226
Set a the \a value of the \a name property on this context.
227
228
\note You should not use context properties to inject values into your QML
229
components. Use singletons or regular object properties instead.
230
*/
231
void
QQmlContext::setContextProperty(
const
QString &name,
const
QVariant &value)
232
{
233
Q_D(QQmlContext);
234
if
(d->notifyIndex() == -1)
235
d->setNotifyIndex(QMetaObjectPrivate::absoluteSignalCount(&QQmlContext::staticMetaObject));
236
237
QQmlRefPointer<QQmlContextData> data = d->m_data;
238
239
if
(data->isInternal()) {
240
qWarning(
"QQmlContext: Cannot set property on internal context."
);
241
return
;
242
}
243
244
if
(!data->isValid()) {
245
qWarning(
"QQmlContext: Cannot set property on invalid context."
);
246
return
;
247
}
248
249
if
(
bool
isNumber =
false
; name.toUInt(&isNumber), isNumber) {
250
qWarning(
"QQmlContext: Using numbers as context properties will be disallowed in a future Qt version."
);
251
QT7_ONLY(
return
;)
252
}
253
254
int
idx = data->propertyIndex(name);
255
if
(idx == -1) {
256
data->addPropertyNameAndIndex(name, data->numIdValues() + d->numPropertyValues());
257
d->appendPropertyValue(value);
258
data->refreshExpressions();
259
}
else
{
260
d->setPropertyValue(idx, value);
261
QMetaObject::activate(
this
, d->notifyIndex(), idx,
nullptr
);
262
}
263
264
if
(
auto
*obj = qvariant_cast<QObject *>(value)) {
265
connect(obj, &QObject::destroyed,
this
, [d, name](QObject *destroyed) {
266
d->dropDestroyedQObject(name, destroyed);
267
});
268
}
269
}
270
271
/*!
272
Set the \a value of the \a name property on this context.
273
274
QQmlContext does \b not take ownership of \a value.
275
276
\note You should not use context properties to inject values into your QML
277
components. Use singletons or regular object properties instead.
278
*/
279
void
QQmlContext::setContextProperty(
const
QString &name, QObject *value)
280
{
281
setContextProperty(name, QVariant::fromValue(value));
282
}
283
284
/*!
285
\since 5.11
286
287
Set a batch of \a properties on this context.
288
289
Setting all properties in one batch avoids unnecessary
290
refreshing expressions, and is therefore recommended
291
instead of calling \l setContextProperty() for each individual property.
292
293
\note You should not use context properties to inject values into your QML
294
components. Use singletons or regular object properties instead.
295
296
\sa QQmlContext::setContextProperty()
297
*/
298
void
QQmlContext::setContextProperties(
const
QList<PropertyPair> &properties)
299
{
300
Q_D(
const
QQmlContext);
301
302
QQmlRefPointer<QQmlContextData> data = d->m_data;
303
QQmlJavaScriptExpression *expressions = data->takeExpressions();
304
QQmlRefPointer<QQmlContextData> childContexts = data->takeChildContexts();
305
306
for
(
const
auto
&property : properties)
307
setContextProperty(property.name, property.value);
308
309
data->setExpressions(expressions);
310
data->setChildContexts(childContexts);
311
data->refreshExpressions();
312
}
313
314
/*!
315
\since 5.11
316
317
\class QQmlContext::PropertyPair
318
\inmodule QtQml
319
320
This struct contains a property name and a property value.
321
It is used as a parameter for the \c setContextProperties function.
322
323
\sa QQmlContext::setContextProperties()
324
*/
325
326
static
bool
readObjectProperty
(
327
const
QQmlRefPointer<QQmlContextData> &data, QObject *object,
const
QString &name,
328
QVariant *target)
329
{
330
QQmlPropertyData local;
331
if
(
const
QQmlPropertyData *property = QQmlPropertyCache::property(object, name, data, &local)) {
332
*target = object->metaObject()->property(property->coreIndex()).read(object);
333
return
true
;
334
}
335
return
false
;
336
}
337
338
/*!
339
Returns the value of the \a name property for this context as a QVariant.
340
If you know that the property you're looking for is a QObject assigned using
341
a QML id in the current context, \l objectForName() is more convenient and
342
faster. In contrast to \l objectForName() and \l nameForObject(), this method
343
does traverse the context hierarchy and searches in parent contexts if the
344
\a name is not found in the current one. It also considers any
345
\l contextObject() you may have set.
346
347
\sa objectForName(), nameForObject(), contextObject()
348
*/
349
QVariant QQmlContext::contextProperty(
const
QString &name)
const
350
{
351
Q_D(
const
QQmlContext);
352
353
const
QQmlRefPointer<QQmlContextData> data = d->m_data;
354
355
const
int
idx = data->propertyIndex(name);
356
if
(idx == -1) {
357
if
(QObject *obj = data->contextObject()) {
358
QVariant value;
359
if
(readObjectProperty(data, obj, name, &value))
360
return
value;
361
}
362
363
if
(parentContext())
364
return
parentContext()->contextProperty(name);
365
}
else
{
366
if
(idx >= d->numPropertyValues())
367
return
QVariant::fromValue(data->idValue(idx - d->numPropertyValues()));
368
else
369
return
d->propertyValue(idx);
370
}
371
372
return
QVariant();
373
}
374
375
/*!
376
Returns the name of \a object in this context, or an empty string if \a object
377
is not named in the context. Objects are named by \l setContextProperty(), or
378
as properties of a context object, or by ids in the case of QML created
379
contexts.
380
381
If the object has multiple names, the first is returned.
382
383
In contrast to \l contextProperty(), this method does not traverse the
384
context hierarchy. If the name is not found in the current context, an empty
385
String is returned.
386
387
\sa contextProperty(), objectForName()
388
*/
389
QString QQmlContext::nameForObject(
const
QObject *object)
const
390
{
391
Q_D(
const
QQmlContext);
392
393
return
d->m_data->findObjectId(object);
394
}
395
396
static
QObject
*
objectForNameInQQmlContextData
(
397
const
QQmlRefPointer<QQmlContextData> &data,
const
QQmlContextPrivate
*d,
398
const
QString &name)
399
{
400
if
(
const
int
propertyIndex = data->propertyIndex(name); propertyIndex >= 0) {
401
const
int
numPropertyValues = d ? d
->
numPropertyValues
(
)
: 0;
402
if
(propertyIndex < numPropertyValues)
403
return
qvariant_cast<QObject *>(d->propertyValue(propertyIndex));
404
return
data->idValue(propertyIndex - numPropertyValues);
405
}
406
407
if
(QObject *obj = data->contextObject()) {
408
QVariant result;
409
if
(readObjectProperty(data, obj, name, &result))
410
return
qvariant_cast<QObject *>(result);
411
}
412
413
return
nullptr
;
414
}
415
416
/*!
417
\since 6.2
418
419
Returns the object for a given \a name in this context. Returns nullptr if
420
\a name is not available in the context or if the value associated with
421
\a name is not a QObject. Objects are named by \l setContextProperty(),
422
or as properties of a context object, or by ids in the case of QML created
423
contexts. In contrast to \l contextProperty(), this method does not traverse
424
the context hierarchy. If the name is not found in the current context,
425
nullptr is returned.
426
427
\sa contextProperty(), nameForObject()
428
*/
429
QObject *QQmlContext::objectForName(
const
QString &name)
const
430
{
431
Q_D(
const
QQmlContext);
432
return
objectForNameInQQmlContextData(d->m_data, d, name);
433
}
434
435
/*!
436
\since 6.11
437
438
Searches this context and its children recursively for an object with
439
ID \a id. If such an object is found, the object is returned. Otherwise
440
returns \nullptr.
441
442
There can only be one object with the given \a id in any given context,
443
but you can create many contexts within one document, for example with
444
views and delegates. Each of those contexts can contain an object with
445
the given \a id. Only the first one found is returned here. The search
446
is conducted using breadth-first search.
447
448
\sa findObjectsRecursively(), objectForName(), childContexts()
449
*/
450
QObject *QQmlContext::findObjectRecursively(
const
QString &id)
const
451
{
452
Q_D(
const
QQmlContext);
453
QQueue<QQmlRefPointer<QQmlContextData>> contexts;
454
contexts.enqueue(d->m_data);
455
456
while
(!contexts.isEmpty()) {
457
const
QQmlRefPointer<QQmlContextData> context = contexts.dequeue();
458
const
QQmlContext *c = context->publicContext();
459
if
(QObject *found = objectForNameInQQmlContextData(context, c ? c->d_func() :
nullptr
, id))
460
return
found;
461
462
for
(
auto
child = context->childContexts(); child; child = child->nextChild())
463
contexts.enqueue(child);
464
}
465
466
return
nullptr
;
467
}
468
469
/*!
470
\since 6.11
471
472
Searches this context and its children recursively for objects with
473
ID \a id. Returns a list of these objects.
474
475
There can only be one object with the given \a id in any given context,
476
but you can create many contexts within one document, for example with
477
views and delegates. Each of those contexts can contain an object with
478
the given \a id.
479
480
\sa findObjectRecursively(), objectForName(), childContexts()
481
*/
482
QList<QObject *> QQmlContext::findObjectsRecursively(
const
QString &id)
const
483
{
484
Q_D(
const
QQmlContext);
485
486
QList<QObject *> result;
487
QQueue<QQmlRefPointer<QQmlContextData>> contexts;
488
contexts.enqueue(d->m_data);
489
490
while
(!contexts.isEmpty()) {
491
const
QQmlRefPointer<QQmlContextData> context = contexts.dequeue();
492
const
QQmlContext *c = context->publicContext();
493
if
(QObject *found = objectForNameInQQmlContextData(context, c ? c->d_func() :
nullptr
, id))
494
result.append(found);
495
496
for
(
auto
child = context->childContexts(); child; child = child->nextChild())
497
contexts.enqueue(child);
498
}
499
500
return
result;
501
}
502
503
/*!
504
Resolves the URL \a src relative to the URL of the
505
containing component.
506
507
\sa QQmlEngine::baseUrl(), setBaseUrl()
508
*/
509
QUrl QQmlContext::resolvedUrl(
const
QUrl &src)
const
510
{
511
Q_D(
const
QQmlContext);
512
return
d->m_data->resolvedUrl(src);
513
}
514
515
/*!
516
Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl.
517
518
Calling this function will override the url of the containing
519
component used by default.
520
521
\sa resolvedUrl()
522
*/
523
void
QQmlContext::setBaseUrl(
const
QUrl &baseUrl)
524
{
525
Q_D(QQmlContext);
526
d->m_data->setBaseUrl(baseUrl);
527
d->m_data->setBaseUrlString(baseUrl.toString());
528
}
529
530
/*!
531
Returns the base url of the component, or the containing component
532
if none is set.
533
*/
534
QUrl QQmlContext::baseUrl()
const
535
{
536
Q_D(
const
QQmlContext);
537
return
d->m_data->baseUrl();
538
}
539
540
/*!
541
* \internal
542
*/
543
QJSValue QQmlContext::importedScript(
const
QString &name)
const
544
{
545
Q_D(
const
QQmlContext);
546
547
QV4::ExecutionEngine *v4 = engine()->handle();
548
QQmlTypeNameCache::Result r = d->m_data->imports()->query(name, v4->typeLoader());
549
QV4::Scope scope(v4);
550
QV4::ScopedObject scripts(scope, d->m_data->importedScripts());
551
return
scripts ? QJSValuePrivate::fromReturnedValue(scripts->get(r.scriptIndex))
552
: QJSValue(QJSValue::UndefinedValue);
553
}
554
555
qsizetype
QQmlContextPrivate
::context_count(QQmlListProperty<QObject> *prop)
556
{
557
QQmlContext *context =
static_cast
<QQmlContext*>(prop->object);
558
QQmlContextPrivate
*d = QQmlContextPrivate::get(context);
559
int
contextProperty = (
int
)(quintptr)prop->data;
560
561
if
(d->propertyValue(contextProperty).userType() != qMetaTypeId<QList<QObject*> >())
562
return
0;
563
else
564
return
((
const
QList<QObject> *)d->propertyValue(contextProperty).constData())->size();
565
}
566
567
QObject *
QQmlContextPrivate
::context_at(QQmlListProperty<QObject> *prop, qsizetype index)
568
{
569
QQmlContext *context =
static_cast
<QQmlContext*>(prop->object);
570
QQmlContextPrivate
*d = QQmlContextPrivate::get(context);
571
int
contextProperty = (
int
)(quintptr)prop->data;
572
573
if
(d->propertyValue(contextProperty).userType() != qMetaTypeId<QList<QObject*> >())
574
return
nullptr
;
575
else
576
return
((
const
QList<QObject*> *)d->propertyValue(contextProperty).constData())->at(index);
577
}
578
579
void
QQmlContextPrivate
::dropDestroyedQObject(
const
QString &name, QObject *destroyed)
580
{
581
if
(!m_data->isValid())
582
return
;
583
584
const
int
idx = m_data->propertyIndex(name);
585
Q_ASSERT(idx >= 0);
586
if
(qvariant_cast<QObject *>(propertyValue(idx)) != destroyed)
587
return
;
588
589
setPropertyValue(idx, QVariant::fromValue<QObject *>(
nullptr
));
590
QMetaObject::activate(q_func(), notifyIndex(), idx,
nullptr
);
591
}
592
593
void
QQmlContextPrivate
::
emitDestruction
()
594
{
595
m_data->emitDestruction();
596
}
597
598
// m_data is owned by the public context. When the public context is reset to nullptr, it will be
599
// deref'd. It's OK to pass a half-created publicContext here. We will not dereference it during
600
// construction.
601
QQmlContextPrivate
::QQmlContextPrivate(
602
QQmlContext *publicContext,
const
QQmlRefPointer<QQmlContextData> &parent,
603
QQmlEngine *engine) :
604
m_data(
new
QQmlContextData(QQmlContextData::OwnedByPublicContext, publicContext,
605
parent, engine))
606
{
607
Q_ASSERT(publicContext !=
nullptr
);
608
}
609
610
QT_END_NAMESPACE
611
612
#
include
"moc_qqmlcontext.cpp"
QQmlContextPrivate
Definition
qqmlcontext_p.h:35
QQmlContextPrivate::numPropertyValues
int numPropertyValues() const
Definition
qqmlcontext_p.h:54
QQmlContextPrivate::emitDestruction
void emitDestruction()
Definition
qqmlcontext.cpp:593
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
readObjectProperty
static bool readObjectProperty(const QQmlRefPointer< QQmlContextData > &data, QObject *object, const QString &name, QVariant *target)
Definition
qqmlcontext.cpp:326
objectForNameInQQmlContextData
static QObject * objectForNameInQQmlContextData(const QQmlRefPointer< QQmlContextData > &data, const QQmlContextPrivate *d, const QString &name)
Definition
qqmlcontext.cpp:396
qtdeclarative
src
qml
qml
qqmlcontext.cpp
Generated on
for Qt by
1.18.0