aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-11-08 17:19:42 +0100
committerUlf Hermann <ulf.hermann@qt.io>2023-11-15 08:30:02 +0100
commitf5aecbde9138b9bdde4c747cb6c7e8f145e96630 (patch)
treeb98886959815dfb356610265a08ac8d01efacfd5 /src
parent63783a21ab76c4278c42466ea5881cb7cbe904c6 (diff)
QtQml: Allow multiple names for C++-based QML types
[ChangeLog][QtQml] You can now have multiple QML_NAMED_ELEMENT macros in the same C++ class to make one C++ type available with more than one QML name. Task-number: QTBUG-101143 Change-Id: I5aeab00a3cd8b3bb11a5a7d5b87ff2e82f57bd7f Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qml/qml/qqml.cpp401
-rw-r--r--src/qml/qml/qqmlprivate.h23
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor.cpp12
-rw-r--r--src/qmltyperegistrar/qqmltyperegistrar.cpp14
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription.cpp47
-rw-r--r--src/qmltyperegistrar/qqmltypesclassdescription_p.h2
-rw-r--r--src/qmltyperegistrar/qqmltypescreator.cpp27
7 files changed, 311 insertions, 215 deletions
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index bac2d663b5..6533deebf5 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -507,13 +507,250 @@ static int finalizeType(const QQmlType &dtype)
return dtype.index();
}
+using ElementNames = QVarLengthArray<const char *, 8>;
+static ElementNames classElementNames(const QMetaObject *metaObject)
+{
+ Q_ASSERT(metaObject);
+ const char *key = "QML.Element";
+
+ const int offset = metaObject->classInfoOffset();
+ const int start = metaObject->classInfoCount() + offset - 1;
+
+ ElementNames elementNames;
+
+ for (int i = start; i >= offset; --i) {
+ const QMetaClassInfo classInfo = metaObject->classInfo(i);
+ if (qstrcmp(key, classInfo.name()) == 0) {
+ const char *elementName = classInfo.value();
+
+ if (qstrcmp(elementName, "auto") == 0) {
+ const char *strippedClassName = metaObject->className();
+ for (const char *c = strippedClassName; *c != '\0'; c++) {
+ if (*c == ':')
+ strippedClassName = c + 1;
+ }
+ elementName = strippedClassName;
+ } else if (qstrcmp(elementName, "anonymous") == 0) {
+ if (elementNames.isEmpty())
+ elementNames.push_back(nullptr);
+ else if (elementNames[0] != nullptr)
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ continue;
+ }
+
+ if (!elementNames.isEmpty() && elementNames[0] == nullptr) {
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ elementNames[0] = elementName;
+ } else {
+ elementNames.push_back(elementName);
+ }
+ }
+ }
+
+ return elementNames;
+}
+
+struct AliasRegistrar
+{
+ AliasRegistrar(const ElementNames *elementNames) : elementNames(elementNames) {}
+
+ void registerAliases(int typeId)
+ {
+ if (elementNames) {
+ for (int i = 1, end = elementNames->length(); i < end; ++i)
+ otherNames.append(QString::fromUtf8(elementNames->at(i)));
+ elementNames = nullptr;
+ }
+
+ for (const QString &otherName : std::as_const(otherNames))
+ QQmlMetaType::registerTypeAlias(typeId, otherName);
+ }
+
+private:
+ const ElementNames *elementNames;
+ QVarLengthArray<QString, 8> otherNames;
+};
+
+
+static void doRegisterTypeAndRevisions(
+ const QQmlPrivate::RegisterTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
+ const bool creatable = (elementNames[0] != nullptr || isValueType)
+ && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
+
+ QString noCreateReason;
+ ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
+
+ if (!creatable) {
+ noCreateReason = QString::fromUtf8(
+ classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
+ if (noCreateReason.isEmpty())
+ noCreateReason = QLatin1String("Type cannot be created in QML.");
+ } else if (isValueType) {
+ const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
+ if (qstrcmp(method, "structured") == 0)
+ creationMethod = ValueTypeCreationMethod::Structured;
+ else if (qstrcmp(method, "construct") == 0)
+ creationMethod = ValueTypeCreationMethod::Construct;
+ }
+
+ RegisterType typeRevision = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ type.typeId,
+ type.listId,
+ creatable ? type.objectSize : 0,
+ nullptr,
+ nullptr,
+ noCreateReason,
+ type.createValueType,
+ type.uri,
+ type.version,
+ nullptr,
+ type.metaObject,
+ type.attachedPropertiesFunction,
+ type.attachedPropertiesMetaObject,
+ type.parserStatusCast,
+ type.valueSourceCast,
+ type.valueInterceptorCast,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ nullptr,
+ QTypeRevision(),
+ type.structVersion > 0 ? type.finalizerCast : -1,
+ creationMethod
+ };
+
+ QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
+ 0,
+ type.uri,
+ type.version,
+ nullptr,
+ type.listId,
+ type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
+ QTypeRevision(),
+ };
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
+ if (type.attachedPropertiesMetaObject)
+ revisions += availableRevisions(type.attachedPropertiesMetaObject);
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : revisions) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&typeRevision, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (typeRevision.version < added
+ || (removed.isValid() && !(typeRevision.version < removed))) {
+ typeRevision.elementName = nullptr;
+ typeRevision.create = nullptr;
+ typeRevision.userdata = nullptr;
+ } else {
+ typeRevision.elementName = elementNames[0];
+ typeRevision.create = creatable ? type.create : nullptr;
+ typeRevision.userdata = type.userdata;
+ }
+
+ typeRevision.customParser = type.customParserFactory();
+ const int id = qmlregister(TypeRegistration, &typeRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (typeRevision.elementName)
+ aliasRegistrar.registerAliases(id);
+
+ if (sequenceRevision.metaSequence != QMetaSequence()) {
+ sequenceRevision.version = typeRevision.version;
+ sequenceRevision.revision = typeRevision.revision;
+ const int id = QQmlPrivate::qmlregister(
+ QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+ }
+ }
+}
+
+static void doRegisterSingletonAndRevisions(
+ const QQmlPrivate::RegisterSingletonTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ RegisterSingletonType revisionRegistration = {
+ 0,
+ type.uri,
+ type.version,
+ elementNames[0],
+ nullptr,
+ type.qObjectApi,
+ type.instanceMetaObject,
+ type.typeId,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ QTypeRevision()
+ };
+ const QQmlType::SingletonInstanceInfo::ConstPtr siinfo
+ = singletonInstanceInfo(revisionRegistration);
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : std::as_const(revisions)) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&revisionRegistration, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (revisionRegistration.version < added
+ || (removed.isValid() && !(revisionRegistration.version < removed))) {
+ revisionRegistration.typeName = nullptr;
+ revisionRegistration.qObjectApi = nullptr;
+ } else {
+ revisionRegistration.typeName = elementNames[0];
+ revisionRegistration.qObjectApi = type.qObjectApi;
+ }
+
+ const int id = finalizeType(
+ QQmlMetaType::registerSingletonType(revisionRegistration, siinfo));
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (revisionRegistration.typeName)
+ aliasRegistrar.registerAliases(id);
+ }
+}
+
/*
This method is "over generalized" to allow us to (potentially) register more types of things in
the future without adding exported symbols.
*/
int QQmlPrivate::qmlregister(RegistrationType type, void *data)
{
-
switch (type) {
case AutoParentRegistration:
return QQmlMetaType::registerAutoParentFunction(
@@ -523,163 +760,29 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
*reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
case TypeAndRevisionsRegistration: {
const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
- const char *elementName = (type.structVersion > 1 && type.forceAnonymous)
- ? nullptr
- : classElementName(type.classInfoMetaObject);
- const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
- const bool creatable = (elementName != nullptr || isValueType)
- && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
-
- QString noCreateReason;
- ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
-
- if (!creatable) {
- noCreateReason = QString::fromUtf8(
- classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
- if (noCreateReason.isEmpty())
- noCreateReason = QLatin1String("Type cannot be created in QML.");
- } else if (isValueType) {
- const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
- if (qstrcmp(method, "structured") == 0)
- creationMethod = ValueTypeCreationMethod::Structured;
- else if (qstrcmp(method, "construct") == 0)
- creationMethod = ValueTypeCreationMethod::Construct;
- }
-
- RegisterType typeRevision = {
- QQmlPrivate::RegisterType::CurrentVersion,
- type.typeId,
- type.listId,
- creatable ? type.objectSize : 0,
- nullptr,
- nullptr,
- noCreateReason,
- type.createValueType,
- type.uri,
- type.version,
- nullptr,
- type.metaObject,
- type.attachedPropertiesFunction,
- type.attachedPropertiesMetaObject,
- type.parserStatusCast,
- type.valueSourceCast,
- type.valueInterceptorCast,
- type.extensionObjectCreate,
- type.extensionMetaObject,
- nullptr,
- QTypeRevision(),
- type.structVersion > 0 ? type.finalizerCast : -1,
- creationMethod
- };
-
- QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
- 0,
- type.uri,
- type.version,
- nullptr,
- type.listId,
- type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
- QTypeRevision(),
- };
-
- const QTypeRevision added = revisionClassInfo(
- type.classInfoMetaObject, "QML.AddedInVersion",
- QTypeRevision::fromVersion(type.version.majorVersion(), 0));
- const QTypeRevision removed = revisionClassInfo(
- type.classInfoMetaObject, "QML.RemovedInVersion");
- const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
- "QML.ExtraVersion");
-
- auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
- if (type.attachedPropertiesMetaObject)
- revisions += availableRevisions(type.attachedPropertiesMetaObject);
- uniqueRevisions(&revisions, type.version, added);
-
- for (QTypeRevision revision : revisions) {
- if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
- break;
-
- assignVersions(&typeRevision, revision, type.version);
-
- // When removed or before added, we still add revisions, but anonymous ones
- if (typeRevision.version < added
- || (removed.isValid() && !(typeRevision.version < removed))) {
- typeRevision.elementName = nullptr;
- typeRevision.create = nullptr;
- typeRevision.userdata = nullptr;
+ if (type.structVersion > 1 && type.forceAnonymous) {
+ doRegisterTypeAndRevisions(type, {nullptr});
+ } else {
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
} else {
- typeRevision.elementName = elementName;
- typeRevision.create = creatable ? type.create : nullptr;
- typeRevision.userdata = type.userdata;
+ doRegisterTypeAndRevisions(type, names);
}
- typeRevision.customParser = type.customParserFactory();
- const int id = qmlregister(TypeRegistration, &typeRevision);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
-
- if (sequenceRevision.metaSequence != QMetaSequence()) {
- sequenceRevision.version = typeRevision.version;
- sequenceRevision.revision = typeRevision.revision;
- const int id = QQmlPrivate::qmlregister(
- QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
- }
}
break;
}
case SingletonAndRevisionsRegistration: {
const RegisterSingletonTypeAndRevisions &type
= *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
- const char *elementName = classElementName(type.classInfoMetaObject);
- RegisterSingletonType revisionRegistration = {
- 0,
- type.uri,
- type.version,
- elementName,
- nullptr,
- type.qObjectApi,
- type.instanceMetaObject,
- type.typeId,
- type.extensionObjectCreate,
- type.extensionMetaObject,
- QTypeRevision()
- };
- const QQmlType::SingletonInstanceInfo::ConstPtr siinfo
- = singletonInstanceInfo(revisionRegistration);
-
- const QTypeRevision added = revisionClassInfo(
- type.classInfoMetaObject, "QML.AddedInVersion",
- QTypeRevision::fromVersion(type.version.majorVersion(), 0));
- const QTypeRevision removed = revisionClassInfo(
- type.classInfoMetaObject, "QML.RemovedInVersion");
- const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
- "QML.ExtraVersion");
-
- auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
- uniqueRevisions(&revisions, type.version, added);
-
- for (QTypeRevision revision : std::as_const(revisions)) {
- if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
- break;
-
- assignVersions(&revisionRegistration, revision, type.version);
-
- // When removed or before added, we still add revisions, but anonymous ones
- if (revisionRegistration.version < added
- || (removed.isValid() && !(revisionRegistration.version < removed))) {
- revisionRegistration.typeName = nullptr;
- revisionRegistration.qObjectApi = nullptr;
- } else {
- revisionRegistration.typeName = elementName;
- revisionRegistration.qObjectApi = type.qObjectApi;
- }
-
- const int id = finalizeType(
- QQmlMetaType::registerSingletonType(revisionRegistration, siinfo));
- if (type.qmlTypeIds)
- type.qmlTypeIds->append(id);
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
+ } else {
+ doRegisterSingletonAndRevisions(type, names);
}
break;
}
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 54e4bfb9b0..2db69be470 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -838,29 +838,6 @@ namespace QQmlPrivate
return qstrcmp(metaObject->classInfo(index).value(), "true") == 0;
}
- inline const char *classElementName(const QMetaObject *metaObject)
- {
- const char *elementName = classInfo(metaObject, "QML.Element");
- if (qstrcmp(elementName, "auto") == 0) {
- const char *strippedClassName = metaObject->className();
- for (const char *c = strippedClassName; *c != '\0'; c++) {
- if (*c == ':')
- strippedClassName = c + 1;
- }
-
- return strippedClassName;
- }
- if (qstrcmp(elementName, "anonymous") == 0)
- return nullptr;
-
- if (!elementName) {
- qWarning().nospace() << "Missing QML.Element class info \"" << elementName << "\""
- << " for " << metaObject->className();
- }
-
- return elementName;
- }
-
template<class T, class = std::void_t<>>
struct QmlExtended
{
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
index 6cfb2de4ed..932607496b 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
@@ -152,7 +152,7 @@ QString MetaTypesJsonProcessor::extractRegisteredTypes() const
const QString className = obj[S_CLASS_NAME].toString();
const QString foreignClassName = className + u"Foreign";
const auto classInfos = obj[S_CLASS_INFOS].toArray();
- QString qmlElement;
+ QStringList qmlElements;
QString qmlUncreatable;
QString qmlAttached;
bool isSingleton = false;
@@ -163,11 +163,11 @@ QString MetaTypesJsonProcessor::extractRegisteredTypes() const
const auto value = toStringView(entry, S_VALUE);
if (name == S_ELEMENT) {
if (value == S_AUTO) {
- qmlElement = u"QML_NAMED_ELEMENT("_s + className + u")"_s;
+ qmlElements.append(u"QML_NAMED_ELEMENT("_s + className + u")"_s);
} else if (value == S_ANONYMOUS) {
- qmlElement = u"QML_ANONYMOUS"_s;
+ qmlElements.append(u"QML_ANONYMOUS"_s);
} else {
- qmlElement = u"QML_NAMED_ELEMENT("_s + value.toString() + u")";
+ qmlElements.append(u"QML_NAMED_ELEMENT("_s + value.toString() + u")");
}
} else if (name == S_CREATABLE && value == S_FALSE) {
isExplicitlyUncreatable = true;
@@ -179,12 +179,12 @@ QString MetaTypesJsonProcessor::extractRegisteredTypes() const
isSingleton = true;
}
}
- if (qmlElement.isEmpty())
+ if (qmlElements.isEmpty())
continue; // no relevant entries found
const QString spaces = u" "_s;
registrationHelper += u"\nstruct "_s + foreignClassName + u"{\n Q_GADGET\n"_s;
registrationHelper += spaces + u"QML_FOREIGN(" + className + u")\n"_s;
- registrationHelper += spaces + qmlElement + u"\n"_s;
+ registrationHelper += spaces + qmlElements.join(u"\n"_s) + u"\n"_s;
if (isSingleton)
registrationHelper += spaces + u"QML_SINGLETON\n"_s;
if (isExplicitlyUncreatable) {
diff --git a/src/qmltyperegistrar/qqmltyperegistrar.cpp b/src/qmltyperegistrar/qqmltyperegistrar.cpp
index 44122b1c8b..9e5dcae1e3 100644
--- a/src/qmltyperegistrar/qqmltyperegistrar.cpp
+++ b/src/qmltyperegistrar/qqmltyperegistrar.cpp
@@ -222,8 +222,7 @@ void QmlTypeRegistrar::write(QTextStream &output)
bool targetIsNamespace = classDef.value(S_NAMESPACE).toBool();
QAnyStringView extendedName;
- bool seenQmlElement = false;
- QString qmlElementName;
+ QStringList qmlElementNames;
QTypeRevision addedIn;
QTypeRevision removedIn;
@@ -232,8 +231,7 @@ void QmlTypeRegistrar::write(QTextStream &output)
const QCborMap v = info.toMap();
const QAnyStringView name = toStringView(v, S_NAME);
if (name == S_ELEMENT) {
- seenQmlElement = true;
- qmlElementName = v[S_VALUE].toString();
+ qmlElementNames.append(v[S_VALUE].toString());
} else if (name == S_FOREIGN) {
targetName = v[S_VALUE].toString();
} else if (name == S_FOREIGN_IS_NAMESPACE) {
@@ -251,7 +249,9 @@ void QmlTypeRegistrar::write(QTextStream &output)
}
}
- if (seenQmlElement && qmlElementName != S_ANONYMOUS) {
+ for (QString &qmlElementName : qmlElementNames) {
+ if (qmlElementName == S_ANONYMOUS)
+ continue;
if (qmlElementName == S_AUTO)
qmlElementName = className;
qmlElementInfos[qmlElementName].append({ className, addedIn, removedIn });
@@ -300,7 +300,7 @@ void QmlTypeRegistrar::write(QTextStream &output)
return result;
};
- if (seenQmlElement) {
+ if (!qmlElementNames.isEmpty()) {
output << uR"(
qmlRegisterNamespaceAndRevisions(%1, "%2", %3, nullptr, %4, %5);)"_s
.arg(metaObjectPointer(targetName), m_module)
@@ -310,7 +310,7 @@ void QmlTypeRegistrar::write(QTextStream &output)
: metaObjectPointer(extendedName));
}
} else {
- if (seenQmlElement) {
+ if (!qmlElementNames.isEmpty()) {
auto checkRevisions = [&](const QCborArray &array, QLatin1StringView type) {
for (auto it = array.constBegin(); it != array.constEnd(); ++it) {
auto object = it->toMap();
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription.cpp b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
index f48d2cd520..ad31f01897 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription.cpp
+++ b/src/qmltyperegistrar/qqmltypesclassdescription.cpp
@@ -179,9 +179,9 @@ void QmlTypesClassDescription::collect(
// These only apply to the original class
if (name == S_ELEMENT) {
if (value == S_AUTO)
- elementName = classDefName;
+ elementNames.append(classDefName);
else if (value != S_ANONYMOUS)
- elementName = value;
+ elementNames.append(value);
} else if (name == S_CREATABLE) {
isCreatable = (value != S_FALSE);
} else if (name == S_CREATION_METHOD) {
@@ -228,7 +228,7 @@ void QmlTypesClassDescription::collect(
}
}
- if (addedInRevision.isValid() && !elementName.isEmpty())
+ if (addedInRevision.isValid() && !elementNames.isEmpty())
revisions.append(addedInRevision);
// If the local type is a namespace the result can only be a namespace,
@@ -280,7 +280,7 @@ void QmlTypesClassDescription::collect(
}
if (classDef) {
- if (mode == RelatedType || !elementName.isEmpty()) {
+ if (mode == RelatedType || !elementNames.isEmpty()) {
collectExtraVersions(classDef, S_PROPERTIES, revisions);
collectExtraVersions(classDef, S_SLOTS, revisions);
collectExtraVersions(classDef, S_METHODS, revisions);
@@ -324,23 +324,34 @@ void QmlTypesClassDescription::collect(
isCreatable = isConstructible;
if (!classDef) {
- if (elementName.isEmpty() || elementName.front().isLower()) {
+ if (elementNames.isEmpty()) {
// If no classDef, we generally assume it's a value type defined by the
// foreign/extended trick.
accessSemantics = DotQmltypes::S_VALUE;
- } else {
- // Objects and namespaces always have metaobjects and therefore classDefs.
- // However, we may not be able to resolve the metaobject at compile time. See
- // the "Invisible" test case. In that case, we must not assume anything about
- // access semantics.
-
- qWarning() << "Warning: Refusing to generate non-lowercase name"
- << elementName.toString() << "for unknown foreign type";
- elementName = {};
-
- // Make it completely inaccessible.
- // We cannot get enums from anonymous types after all.
- accessSemantics = DotQmltypes::S_NONE;
+ }
+
+ for (auto elementName = elementNames.begin(); elementName != elementNames.end();) {
+ if (elementName->isEmpty() || elementName->front().isLower()) {
+ // If no classDef, we generally assume it's a value type defined by the
+ // foreign/extended trick.
+ accessSemantics = DotQmltypes::S_VALUE;
+ ++elementName;
+ } else {
+ // Objects and namespaces always have metaobjects and therefore classDefs.
+ // However, we may not be able to resolve the metaobject at compile time. See
+ // the "Invisible" test case. In that case, we must not assume anything about
+ // access semantics.
+
+ qWarning() << "Warning: Refusing to generate non-lowercase name"
+ << elementName->toString() << "for unknown foreign type";
+ elementName = elementNames.erase(elementName);
+
+ if (elementNames.isEmpty()) {
+ // Make it completely inaccessible.
+ // We cannot get enums from anonymous types after all.
+ accessSemantics = DotQmltypes::S_NONE;
+ }
+ }
}
} else if (classDef->value(S_GADGET).toBool()) {
accessSemantics = DotQmltypes::S_VALUE;
diff --git a/src/qmltyperegistrar/qqmltypesclassdescription_p.h b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
index d96fc75ae9..4cf01721b5 100644
--- a/src/qmltyperegistrar/qqmltypesclassdescription_p.h
+++ b/src/qmltyperegistrar/qqmltypesclassdescription_p.h
@@ -31,7 +31,7 @@ struct QmlTypesClassDescription
const QCborMap *resolvedClass = nullptr;
QAnyStringView file;
QAnyStringView className;
- QAnyStringView elementName;
+ QList<QAnyStringView> elementNames;
QAnyStringView defaultProp;
QAnyStringView parentProp;
QAnyStringView superClass;
diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp
index 8dc5dc5ac7..dd764404bd 100644
--- a/src/qmltyperegistrar/qqmltypescreator.cpp
+++ b/src/qmltyperegistrar/qqmltypescreator.cpp
@@ -71,11 +71,13 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (!collector.immediateNames.isEmpty())
m_qml.writeStringListBinding(S_IMMEDIATE_NAMES, collector.immediateNames);
- if (collector.elementName.isEmpty()) // e.g. if QML_ANONYMOUS
+ if (collector.elementNames.isEmpty()) // e.g. if QML_ANONYMOUS
return;
if (!collector.sequenceValueType.isEmpty()) {
- qWarning() << "Ignoring name of sequential container:" << collector.elementName.toString();
+ qWarning() << "Ignoring names of sequential container:";
+ for (const QAnyStringView &name : std::as_const(collector.elementNames))
+ qWarning() << " - " << name.toString();
qWarning() << "Sequential containers are anonymous. Use QML_ANONYMOUS to register them.";
return;
}
@@ -92,16 +94,19 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (revision.hasMajorVersion() && revision.majorVersion() > m_version.majorVersion())
break;
- QByteArray exportEntry = m_module + '/';
- collector.elementName.visit([&](auto view) {
- processAsUtf8(view, [&](QByteArrayView view) { exportEntry.append(view); });
- });
- exportEntry += ' ' + QByteArray::number(revision.hasMajorVersion()
- ? revision.majorVersion()
- : m_version.majorVersion());
- exportEntry += '.' + QByteArray::number(revision.minorVersion());
+ for (const QAnyStringView &elementName : std::as_const(collector.elementNames)) {
+ QByteArray exportEntry = m_module + '/';
- exports.append(exportEntry);
+ elementName.visit([&](auto view) {
+ processAsUtf8(view, [&](QByteArrayView view) { exportEntry.append(view); });
+ });
+ exportEntry += ' ' + QByteArray::number(revision.hasMajorVersion()
+ ? revision.majorVersion()
+ : m_version.majorVersion());
+ exportEntry += '.' + QByteArray::number(revision.minorVersion());
+
+ exports.append(exportEntry);
+ }
metaObjects.append(QByteArray::number(revision.toEncodedVersion<quint16>()));
}