Fix QQmlContext::nameForObject()

nameForObject() should not search the linked contexts. Linked contexts
are not reachable from the "head" context in QML. However, it should
search context objects, just like contextProperty() does.

[ChangeLog][QtQml][Important Behavior Changes]
QQmlContext::nameForObject() now finds properties of context objects,
but it does not search unrelated other ("linked") contexts anymore.

Change-Id: Ic6e01fddf3e2d4b3a1bc7308e126f47fae7cd6d1
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2021-04-15 19:13:05 +02:00
parent 7fc9e67107
commit 9cd1e7902c
3 changed files with 48 additions and 14 deletions

View File

@ -367,19 +367,26 @@ static bool readObjectProperty(
}
/*!
Returns the value of the \a name property for this context
as a QVariant.
Returns the value of the \a name property for this context as a QVariant.
If you know that the property you're looking for is a QObject assigned using
a QML id in the current context, \l objectForName() is more convenient and
faster. In contrast to \l objectForName() and \l nameForObject(), this method
does traverse the context hierarchy and searches in parent contexts if the
\a name is not found in the current one. It also considers any
\l contextObject() you may have set.
\sa objectForName(), nameForObject(), contextObject()
*/
QVariant QQmlContext::contextProperty(const QString &name) const
{
Q_D(const QQmlContext);
QVariant value;
QQmlRefPointer<QQmlContextData> data = d->m_data;
const QQmlRefPointer<QQmlContextData> data = d->m_data;
const int idx = data->propertyIndex(name);
if (idx == -1) {
if (QObject *obj = data->contextObject()) {
QVariant value;
if (readObjectProperty(data, obj, name, &value))
return value;
}
@ -388,20 +395,27 @@ QVariant QQmlContext::contextProperty(const QString &name) const
return parentContext()->contextProperty(name);
} else {
if (idx >= d->numPropertyValues())
value = QVariant::fromValue(data->idValue(idx - d->numPropertyValues()));
return QVariant::fromValue(data->idValue(idx - d->numPropertyValues()));
else
value = d->propertyValue(idx);
return d->propertyValue(idx);
}
return value;
return QVariant();
}
/*!
Returns the name of \a object in this context, or an empty string if \a object
is not named in the context. Objects are named by setContextProperty(), or by ids in
the case of QML created contexts.
Returns the name of \a object in this context, or an empty string if \a object
is not named in the context. Objects are named by \l setContextProperty(), or
as properties of a context object, or by ids in the case of QML created
contexts.
If the object has multiple names, the first is returned.
If the object has multiple names, the first is returned.
In contrast to \l contextProperty(), this method does not traverse the
context hierarchy. If the name is not found in the current context, an empty
String is returned.
\sa contextProperty(), objectForName()
*/
QString QQmlContext::nameForObject(const QObject *object) const
{

View File

@ -286,15 +286,29 @@ QString QQmlContextData::findObjectId(const QObject *obj) const
return propertyName(ii);
}
const QVariant objVariant = QVariant::fromValue(obj);
if (m_publicContext) {
QQmlContextPrivate *p = QQmlContextPrivate::get(m_publicContext);
for (int ii = 0; ii < p->numPropertyValues(); ++ii)
if (p->propertyValue(ii) == QVariant::fromValue(const_cast<QObject *>(obj)))
if (p->propertyValue(ii) == objVariant)
return propertyName(ii);
}
if (m_linkedContext)
return m_linkedContext->findObjectId(obj);
if (m_contextObject) {
// This is expensive, but nameForObject should really mirror contextProperty()
for (const QMetaObject *metaObject = m_contextObject->metaObject();
metaObject; metaObject = metaObject->superClass()) {
for (int i = metaObject->propertyOffset(), end = metaObject->propertyCount();
i != end; ++i) {
const QMetaProperty prop = metaObject->property(i);
if (prop.metaType().flags() & QMetaType::PointerToQObject
&& prop.read(m_contextObject) == objVariant) {
return QString::fromUtf8(prop.name());
}
}
}
}
return QString();
}

View File

@ -595,6 +595,10 @@ void tst_qqmlcontext::objectsAndNames()
QObject *containmentMask = qvariant_cast<QObject *>(ctxtObj->property("containmentMask"));
QCOMPARE(containmentMask->objectName(), QStringLiteral("ddd"));
QCOMPARE(extraContext->nameForObject(aa), QStringLiteral("aa"));
QCOMPARE(extraContext->nameForObject(bb), QStringLiteral("bb"));
QCOMPARE(extraContext->nameForObject(cc), QStringLiteral("cc"));
QCOMPARE(extraContext->nameForObject(containmentMask), QStringLiteral("containmentMask"));
QCOMPARE(extraContext->objectForName(QStringLiteral("aa")), aa);
QCOMPARE(extraContext->objectForName(QStringLiteral("bb")), bb);
QCOMPARE(extraContext->objectForName(QStringLiteral("cc")), cc);
@ -612,6 +616,8 @@ void tst_qqmlcontext::objectsAndNames()
// objectForName and nameForObject deliberately don't
QCOMPARE(extraContext->objectForName(QStringLiteral("root")), nullptr);
QCOMPARE(extraContext->objectForName(QStringLiteral("nested")), nullptr);
QCOMPARE(extraContext->nameForObject(o.data()), QString());
QCOMPARE(extraContext->nameForObject(qvariant_cast<QObject*>(o->property("o"))), QString());
}
class DeleteCommand : public QObject