aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2022-05-09 17:35:35 +0200
committerAndrei Golubev <andrei.golubev@qt.io>2022-05-12 17:36:12 +0200
commit5cd59bff0c2f077ab47c40710a5267c06d3d2aa9 (patch)
treeb3a1f34ce103f1834f20f5921c33f02ad646cff4
parentc8f9cb050df690696422df2f79f1124340037547 (diff)
Support querying indirect extensions for a given object
Introduce a private version of qmlExtendedObject() that returns an index-based extension (where 0 represents an extension on the leaf type and N represents a (N - 1)th base type's extension) Teach QQmlProxyMetaObject to distinguish different extension proxies. Its custom metaCall can now query up to 128 extensions (should be enough for the user needs) Change-Id: I5520a1e84501f1f9fe6a8e77d8269009a12c255c Reviewed-by: Maximilian Goldstein <max.goldstein@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/qml/qqml.cpp9
-rw-r--r--src/qml/qml/qqmlprivate.h2
-rw-r--r--src/qml/qml/qqmlproxymetaobject.cpp10
-rw-r--r--src/qml/qml/qqmlproxymetaobject_p.h18
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h29
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp58
6 files changed, 119 insertions, 7 deletions
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 728250dcf2..3c9f930eb9 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -151,6 +151,11 @@ QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc
QObject *qmlExtendedObject(QObject *object)
{
+ return QQmlPrivate::qmlExtendedObject(object, 0);
+}
+
+QObject *QQmlPrivate::qmlExtendedObject(QObject *object, int index)
+{
if (!object)
return nullptr;
@@ -161,8 +166,8 @@ QObject *qmlExtendedObject(QObject *object)
const int id = d->metaObject->metaCall(
object, QMetaObject::CustomCall,
- QQmlProxyMetaObject::ExtensionObjectId, &result);
- if (id != QQmlProxyMetaObject::ExtensionObjectId)
+ QQmlProxyMetaObject::extensionObjectId(index), &result);
+ if (id != QQmlProxyMetaObject::extensionObjectId(index))
return nullptr;
return static_cast<QObject *>(result);
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 817f25c112..9754f9dcf3 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -1087,6 +1087,8 @@ namespace QQmlPrivate
};
}
+ Q_QML_EXPORT QObject *qmlExtendedObject(QObject *, int);
+
} // namespace QQmlPrivate
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp
index e05c63794c..4844ca3720 100644
--- a/src/qml/qml/qqmlproxymetaobject.cpp
+++ b/src/qml/qml/qqmlproxymetaobject.cpp
@@ -144,11 +144,15 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void
break;
}
- case QMetaObject::CustomCall:
- if (id != ExtensionObjectId)
+ case QMetaObject::CustomCall: {
+ if ((id & ~MaxExtensionCount) != ExtensionObjectId)
break;
- a[0] = getProxy(0);
+ int index = id & MaxExtensionCount;
+ if (qsizetype(index) >= metaObjects->size())
+ break;
+ a[0] = getProxy(index);
return id;
+ }
default:
break;
}
diff --git a/src/qml/qml/qqmlproxymetaobject_p.h b/src/qml/qml/qqmlproxymetaobject_p.h
index a58818f22a..965d86c70f 100644
--- a/src/qml/qml/qqmlproxymetaobject_p.h
+++ b/src/qml/qml/qqmlproxymetaobject_p.h
@@ -65,8 +65,6 @@ QT_BEGIN_NAMESPACE
class QQmlProxyMetaObject : public QDynamicMetaObjectData
{
public:
- enum { ExtensionObjectId = std::numeric_limits<int>::min() };
-
struct ProxyData {
typedef QObject *(*CreateFunc)(QObject *);
QMetaObject *metaObject;
@@ -78,6 +76,13 @@ public:
QQmlProxyMetaObject(QObject *, QList<ProxyData> *);
~QQmlProxyMetaObject();
+ static constexpr int extensionObjectId(int id) noexcept
+ {
+ Q_ASSERT(id >= 0);
+ Q_ASSERT(id <= MaxExtensionCount); // MaxExtensionCount is a valid index
+ return ExtensionObjectId | id;
+ }
+
protected:
int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) override;
QMetaObject *toDynamicMetaObject(QObject *) override;
@@ -91,6 +96,15 @@ private:
QDynamicMetaObjectData *parent;
QMetaObject *metaObject;
QObject *object;
+
+ // ExtensionObjectId acts as a flag for whether we should interpret a
+ // QMetaObject::CustomCall as a call to fetch the extension object (see
+ // QQmlProxyMetaObject::metaCall()). MaxExtensionCount is a limit on how
+ // many extensions we can query via such mechanism
+ enum : int {
+ MaxExtensionCount = 127, // magic number so that low bits are all '1'
+ ExtensionObjectId = ~MaxExtensionCount,
+ };
};
QT_END_NAMESPACE
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index 8efccbc0fb..ca42ae02bc 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -1851,6 +1851,35 @@ public:
ExtendedInParentByIndirect(QObject *parent = nullptr) : ExtendedByIndirect(parent) { }
};
+class MultiExtensionThreeExtensions : public MultiExtension
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_EXTENDED(Extension)
+public:
+ MultiExtensionThreeExtensions(QObject *parent = nullptr) : MultiExtension(parent) { }
+};
+
+class MultiExtensionWithoutExtension : public MultiExtension
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+public:
+ MultiExtensionWithoutExtension(QObject *parent = nullptr) : MultiExtension(parent) { }
+};
+
+class MultiExtensionWithExtensionInBaseBase : public MultiExtensionWithoutExtension
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_EXTENDED(Extension)
+public:
+ MultiExtensionWithExtensionInBaseBase(QObject *parent = nullptr)
+ : MultiExtensionWithoutExtension(parent)
+ {
+ }
+};
+
class StringSignaler : public QObject
{
Q_OBJECT
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 1e3f06a9e9..bfcd146b10 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -365,6 +365,7 @@ private slots:
void qtbug_86482();
void multiExtension();
+ void multiExtensionExtra();
void multiExtensionIndirect();
void extensionSpecial();
void invalidInlineComponent();
@@ -6395,6 +6396,63 @@ void tst_qqmllanguage::multiExtension()
QCOMPARE(o->property("d").toInt(), 22);
QCOMPARE(o->property("f").toInt(), 31);
QCOMPARE(o->property("g").toInt(), 44); // NB: taken from the type, not from the extension!
+
+ QObject *extension = qmlExtendedObject(o.get());
+ QVERIFY(extension != nullptr);
+ QVERIFY(qobject_cast<ExtensionB *>(extension) != nullptr);
+
+ QCOMPARE(QQmlPrivate::qmlExtendedObject(o.get(), 0), extension);
+ QObject *baseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 1);
+ QVERIFY(baseTypeExtension);
+ QVERIFY(qobject_cast<ExtensionA *>(baseTypeExtension) != nullptr);
+}
+
+void tst_qqmllanguage::multiExtensionExtra()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent c(&engine);
+ c.setData("import StaticTest\nMultiExtensionThreeExtensions {}", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QObject *extension = qmlExtendedObject(o.get());
+ QVERIFY(extension != nullptr);
+ QVERIFY(qobject_cast<Extension *>(extension) != nullptr);
+
+ QCOMPARE(QQmlPrivate::qmlExtendedObject(o.get(), 0), extension);
+ QObject *baseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 1);
+ QVERIFY(baseTypeExtension);
+ QVERIFY(qobject_cast<ExtensionB *>(baseTypeExtension) != nullptr);
+ QObject *baseBaseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 2);
+ QVERIFY(baseBaseTypeExtension);
+ QVERIFY(qobject_cast<ExtensionA *>(baseBaseTypeExtension) != nullptr);
+ }
+
+ {
+ QQmlComponent c(&engine);
+ c.setData("import StaticTest\nMultiExtensionWithExtensionInBaseBase {}", QUrl());
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+
+ QObject *extension = qmlExtendedObject(o.get());
+ QVERIFY(extension != nullptr);
+ QVERIFY(qobject_cast<Extension *>(extension) != nullptr);
+
+ QCOMPARE(QQmlPrivate::qmlExtendedObject(o.get(), 0), extension);
+
+ QObject *pseudoExtension = QQmlPrivate::qmlExtendedObject(o.get(), 1);
+ QVERIFY(pseudoExtension);
+ QVERIFY(qobject_cast<ExtensionB *>(pseudoExtension) != nullptr);
+
+ QObject *baseBaseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 2);
+ QVERIFY(baseBaseTypeExtension);
+ QVERIFY(qobject_cast<ExtensionB *>(baseBaseTypeExtension) != nullptr);
+
+ QObject *baseBaseBaseTypeExtension = QQmlPrivate::qmlExtendedObject(o.get(), 3);
+ QVERIFY(baseBaseBaseTypeExtension);
+ QVERIFY(qobject_cast<ExtensionA *>(baseBaseBaseTypeExtension) != nullptr);
+ }
}
void tst_qqmllanguage::multiExtensionIndirect()