diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2023-11-08 17:19:42 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-11-15 08:30:02 +0100 |
commit | f5aecbde9138b9bdde4c747cb6c7e8f145e96630 (patch) | |
tree | b98886959815dfb356610265a08ac8d01efacfd5 /src | |
parent | 63783a21ab76c4278c42466ea5881cb7cbe904c6 (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.cpp | 401 | ||||
-rw-r--r-- | src/qml/qml/qqmlprivate.h | 23 | ||||
-rw-r--r-- | src/qmltyperegistrar/qmetatypesjsonprocessor.cpp | 12 | ||||
-rw-r--r-- | src/qmltyperegistrar/qqmltyperegistrar.cpp | 14 | ||||
-rw-r--r-- | src/qmltyperegistrar/qqmltypesclassdescription.cpp | 47 | ||||
-rw-r--r-- | src/qmltyperegistrar/qqmltypesclassdescription_p.h | 2 | ||||
-rw-r--r-- | src/qmltyperegistrar/qqmltypescreator.cpp | 27 |
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>())); } |