From 2b629123f92d37d75875893fa511ad5cb171aad2 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 29 Feb 2024 13:31:26 +0100 Subject: [PATCH] questionable shadow class removed, using QQmlPrivate::RegisterType again. Qt5 is using public APIs only - too many #ifdefs otherwise --- qmlexport/CMakeLists.txt | 17 +- qmlexport/QskQml.cpp | 11 +- qmlexport/QskQml.hpp | 288 ---------------------------------- qmlexport/QskQmlClassInfo.cpp | 85 ++++++++++ qmlexport/QskQmlClassInfo.h | 84 ++++++++++ qmlexport/QskQmlModule.h | 17 ++ qmlexport/QskQmlRegister.h | 131 ++++++++++++++++ 7 files changed, 333 insertions(+), 300 deletions(-) delete mode 100644 qmlexport/QskQml.hpp create mode 100644 qmlexport/QskQmlClassInfo.cpp create mode 100644 qmlexport/QskQmlClassInfo.h create mode 100644 qmlexport/QskQmlModule.h create mode 100644 qmlexport/QskQmlRegister.h diff --git a/qmlexport/CMakeLists.txt b/qmlexport/CMakeLists.txt index a4660b24..e6d51add 100644 --- a/qmlexport/CMakeLists.txt +++ b/qmlexport/CMakeLists.txt @@ -3,16 +3,25 @@ # SPDX-License-Identifier: BSD-3-Clause ############################################################################ -set(HEADERS +list(APPEND HEADERS QskQmlGlobal.h QskShortcutQml.h QskLayoutQml.h - QskQml.h) + QskQmlModule.h + QskQmlRegister.h + QskQml.h +) -set(SOURCES +list(APPEND SOURCES QskShortcutQml.cpp QskLayoutQml.cpp - QskQml.cpp) + QskQml.cpp +) + +if (QT_VERSION_MAJOR GREATER_EQUAL 6) + list(APPEND HEADERS QskQmlClassInfo.h) + list(APPEND SOURCES QskQmlClassInfo.cpp) +endif() set(target qskqmlexport) diff --git a/qmlexport/QskQml.cpp b/qmlexport/QskQml.cpp index 54a00043..e1a251db 100644 --- a/qmlexport/QskQml.cpp +++ b/qmlexport/QskQml.cpp @@ -4,8 +4,8 @@ *****************************************************************************/ #include "QskQml.h" -#include "QskQml.hpp" +#include "QskQmlRegister.h" #include "QskLayoutQml.h" #include "QskShortcutQml.h" @@ -44,8 +44,6 @@ #include #include #include -#include -#include #include #include #include @@ -61,9 +59,9 @@ #include #if QT_VERSION < QT_VERSION_CHECK( 6, 2, 0 ) - QSK_QT_PRIVATE_BEGIN +QSK_QT_PRIVATE_BEGIN #include - QSK_QT_PRIVATE_END +QSK_QT_PRIVATE_END #endif #if QT_VERSION < QT_VERSION_CHECK( 6, 5, 0 ) @@ -191,9 +189,6 @@ namespace void QskQml::registerTypes() { - qmlRegisterUncreatableType< QskSkin >( QSK_MODULE_NAME, 1, 0, "Skin", QString() ); - qRegisterMetaType< QskSkin* >(); - registerObject< QskShortcutQml >( "Shortcut" ); registerObject< QskWindow >(); diff --git a/qmlexport/QskQml.hpp b/qmlexport/QskQml.hpp deleted file mode 100644 index 7e298b0e..00000000 --- a/qmlexport/QskQml.hpp +++ /dev/null @@ -1,288 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#ifndef QSK_QML_HPP -#define QSK_QML_HPP - -#include -#include - -#define QSK_MODULE_NAME "Skinny" -#define QSK_VERSION_MAJOR 1 -#define QSK_VERSION_MINOR 0 - -#if QT_VERSION < QT_VERSION_CHECK( 6, 3, 0 ) - #define QSK_STRUCT_VERSION 0 -#elif QT_VERSION < QT_VERSION_CHECK( 6, 5, 0 ) - #define QSK_STRUCT_VERSION 1 -#else - #define QSK_STRUCT_VERSION 2 -#endif - -// Required for QFlags to be constructed from an enum value -#define QSK_REGISTER_FLAGS( Type ) \ - QMetaType::registerConverter< int, Type >( []( int value ) { return Type( value ); } ) - -namespace QskQml -{ - inline const char* classNameQml( const QMetaObject& metaObject ) - { - // without the "Qsk" prefix - return metaObject.className() + 3; - } - - /* - ClassInfo corresponds to the most reecent QQmlPrivate::RegisterType - ( structVersion: 2 introduced with Qt 6.5 ) - */ - class ClassInfo - { - public: - - template< typename T > - void setTypeInfo() - { - using namespace QQmlPrivate; - - constexpr bool isObject = std::is_base_of_v< QObject, T >; - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - typeId = qMetaTypeId< T* >( ); -#else - if ( isObject ) - typeId = QMetaType::fromType< T* >(); - else - typeId = QMetaType::fromType< T >(); - - createValueType = ValueType< T, void >::create; -#endif - -#if 0 - -/* - For the moment we do not export lists - QMetaType::fromType< QList< T > >() - creates so many symbols, that we would have to enable -mbig-obj for mingw - TODO ... - */ -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - const char* className = T::staticMetaObject.className(); \ - - const int nameLen = int(strlen(className) ); \ - const int listLen = int(strlen("QQmlListProperty<") ); \ - - QVarLengthArray< char, 64 > listName( listLen + nameLen + 2 ); - memcpy( listName.data(), "QQmlListProperty<", size_t( listLen ) ); - memcpy(listName.data() + listLen, className, size_t( nameLen ) ); - listName[listLen + nameLen] = '>'; - listName[listLen + nameLen + 1] = '\0'; - - listId = qRegisterNormalizedMetaType< QQmlListProperty< T > >( listName.constData() ); -#else - if ( isObject ); - listId = QMetaType::fromType< QQmlListProperty< T > >( ); - else - listId = QMetaType::fromType< QList< T > >( ); -#endif - -#endif - - parserStatusCast = StaticCastSelector< T,QQmlParserStatus >::cast(); - valueSourceCast = StaticCastSelector< T,QQmlPropertyValueSource >::cast(); - valueInterceptorCast = StaticCastSelector< T,QQmlPropertyValueInterceptor >::cast(); -#if QSK_STRUCT_VERSION >= 1 - finalizerCast = StaticCastSelector< T,QQmlFinalizerHook >::cast(); -#endif - } - - public: - const int structVersion = QSK_STRUCT_VERSION; - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - QMetaType typeId; - QMetaType listId; -#else - int typeId = 0; - int listId = 0; -#endif - - int objectSize = 0; - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - void ( *create )( void* ) = nullptr; -#else - void ( *create )( void*, void* ) = nullptr; - void* const userdata = nullptr; // unused -#endif - - const QString noCreationReason; // unused - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - /* - This one was introdued with Qt 6.x, but never worked - as expected. With Qt 6.5 it has been replaced by adding - the creationMethod that is triggering to look for - invokable constructors. - Let's check if it makes any sense to initialize it below - at all. TODO ... - */ - QVariant ( *createValueType )( const QJSValue& ) = nullptr; -#endif - - const char* const uri = QSK_MODULE_NAME; - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - const QTypeRevision version = - QTypeRevision::fromVersion( QSK_VERSION_MAJOR, QSK_VERSION_MINOR ); -#else - const int versionMajor = QSK_VERSION_MAJOR; - const int versionMinor = QSK_VERSION_MINOR; -#endif - const char* elementName = nullptr; - const QMetaObject* metaObject = nullptr; - - /* - We do not use attached properties as it always comes with - creating extra QObjects. - */ - QObject* (* const attachedPropertiesFunction)( QObject* ) = nullptr; - const QMetaObject* const attachedPropertiesMetaObject = nullptr; - - int parserStatusCast = -1; - int valueSourceCast = -1; - int valueInterceptorCast = -1; - - /* - We do not use extensions as it always comes with - creating extra QObjects. - */ - QObject* (* const extensionObjectCreate )( QObject* ) = nullptr; - const QMetaObject* const extensionMetaObject = nullptr; - - void* const customParser = nullptr; // QQmlCustomParser, unused - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - const QTypeRevision revision = QTypeRevision::zero(); -#else - const int revision = 0; -#endif - int finalizerCast = -1; - - const int creationMethod = 2; // ValueTypeCreationMethod::Structured - }; - - template< typename T > - inline int registerType( const char* qmlName ) - { - using namespace QQmlPrivate; - - ClassInfo type; - - type.setTypeInfo< T >(); - - type.objectSize = sizeof( T ); -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - type.create = Constructors< T >::createInto; -#else - type.create = createInto< T >; -#endif - - type.elementName = qmlName; - type.metaObject = &T::staticMetaObject; - - return qmlregister( TypeRegistration, &type ); - } - - template< typename T > - inline int registerUncreatableType( const char* qmlName ) - { - using namespace QQmlPrivate; - - ClassInfo type; - - type.setTypeInfo< T >(); - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - type.objectSize = sizeof( T ); - type.create = Constructors< T >::createInto; -#endif - - type.elementName = qmlName; - type.metaObject = &T::staticMetaObject; - - return qmlregister( TypeRegistration, &type ); - } - - inline int registerUncreatableMetaObject( - const QMetaObject& staticMetaObject, const char* qmlName ) - { - using namespace QQmlPrivate; - - ClassInfo type; - - type.elementName = qmlName; - type.metaObject = &staticMetaObject; - - return qmlregister( TypeRegistration, &type ); - } - - template< typename T > - inline void registerObject( const char* qmlName = nullptr ) - { - // the class name without the "Qsk" prefix - if ( qmlName == nullptr ) - qmlName = classNameQml( T::staticMetaObject ); - - ( void ) registerType< T >( qmlName ); - } - - template< typename T > - inline void registerGadget() - { - auto className = classNameQml( T::staticMetaObject ); - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - registerUncreatableType< T >( className ); -#else - /* - According to the QML naming rules uncreatables have to - start with a lowercase letter ( since Qt6 ), while namespaces - and creatable items usually start with a upper letter. - This results in an odd naming scheme for the enums defined inside of gadgets. - - To work around this we register the gadget twice - starting with - upper or lower letter. - - Maybe it would make sense to only pass stripped metaObjects, where all - enums are removed from the first and everything else than the enums from - the second. TODO ... - */ - - if ( T::staticMetaObject.enumeratorCount() > 0 ) - { - registerUncreatableMetaObject( T::staticMetaObject, className ); - } - - QByteArray name = className; - name.data()[0] = std::tolower( name.data()[0] ); - registerUncreatableType< T >( name.constData() ); -#endif - } - - inline int registerNamespace( const QMetaObject& metaObject ) - { - return registerUncreatableMetaObject( metaObject, classNameQml( metaObject ) ); - } - - template< typename T > - inline int registerSingleton( QObject* singleton ) - { - const auto name = classNameQml( T::staticMetaObject ); - - return qmlRegisterSingletonInstance( QSK_MODULE_NAME, - QSK_VERSION_MAJOR, QSK_VERSION_MINOR, name, singleton ); - } -} - -#endif diff --git a/qmlexport/QskQmlClassInfo.cpp b/qmlexport/QskQmlClassInfo.cpp new file mode 100644 index 00000000..d862f639 --- /dev/null +++ b/qmlexport/QskQmlClassInfo.cpp @@ -0,0 +1,85 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskQmlClassInfo.h" +#include "QskQmlModule.h" +#include + +using namespace QskQml; + +ClassInfo::ClassInfo( const char* qmlName, const QMetaObject* metaObject ) +{ + m_info.structVersion = QSK_STRUCT_VERSION; + + m_info.objectSize = 0; + m_info.create = nullptr; + m_info.userdata = nullptr; // unused + + /* + This one was introdued with Qt 6.x, but never worked + as expected. With Qt 6.5 it has been replaced by adding + the creationMethod that is triggering to look for + invokable constructors. + Let's check if it makes any sense to initialize it below + at all. TODO ... + */ + m_info.createValueType = nullptr; + + m_info.uri = QskQmlModule::name; + m_info.version = QTypeRevision::fromVersion( QskQmlModule::name[0], QskQmlModule::name[1] ); + + m_info.elementName = qmlName; + m_info.metaObject = metaObject; + + /* + We do not use attached properties as it always comes with + creating extra QObjects. + */ + m_info.attachedPropertiesFunction = nullptr; + m_info.attachedPropertiesMetaObject = nullptr; + + m_info.parserStatusCast = m_info.valueSourceCast = m_info.valueInterceptorCast = -1; + + /* + We do not use extensions as it always comes with + creating extra QObjects. + */ + m_info.extensionObjectCreate = nullptr; + m_info.extensionMetaObject = nullptr; + + m_info.customParser = nullptr; // QQmlCustomParser, unused + + m_info.revision = QTypeRevision::zero(); + m_info.finalizerCast = -1; + + m_info.creationMethod = QQmlPrivate::ValueTypeCreationMethod::Structured; +} + +QByteArray ClassInfo::normalizedListName( + const char* containerName, const QMetaObject& metaObject ) +{ + static QByteArray name; + name.reserve( 256 ); + + const int length1 = strlen( containerName ); + const int length2 = strlen( metaObject.className() ); + + name.resize( length1 + length2 + 3 ); + + auto p = name.data(); + + memcpy( p, containerName, size_t( length1 ) ); + p += length1; + + *p++ = '<'; + + memcpy( p, metaObject.className(), size_t( length2 ) ); + p += length2; + + *p++ = '>'; + *p++ = '\0'; + + return name; +} diff --git a/qmlexport/QskQmlClassInfo.h b/qmlexport/QskQmlClassInfo.h new file mode 100644 index 00000000..e3698c6d --- /dev/null +++ b/qmlexport/QskQmlClassInfo.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_QML_CLASSINFO_H +#define QSK_QML_CLASSINFO_H + +#include + +class QByteArray; + +#if QT_VERSION < QT_VERSION_CHECK( 6, 3, 0 ) + #define QSK_STRUCT_VERSION 0 +#elif QT_VERSION < QT_VERSION_CHECK( 6, 5, 0 ) + #define QSK_STRUCT_VERSION 1 +#else + #define QSK_STRUCT_VERSION 2 +#endif + +namespace QskQml +{ + class ClassInfo + { + public: + ClassInfo( const char* qmlName, const QMetaObject* ); + template< typename T > void setTypeInfo(); + + int registerType(); + + private: + static QByteArray normalizedListName( + const char* containerName, const QMetaObject& ); + + QQmlPrivate::RegisterType m_info; + }; + + template< typename T > + inline void ClassInfo::setTypeInfo() + { + using namespace QQmlPrivate; + + constexpr bool isObject = std::is_base_of_v< QObject, T >; + + if ( isObject ) + m_info.typeId = QMetaType::fromType< T* >(); + else + m_info.typeId = QMetaType::fromType< T >(); + + m_info.objectSize = sizeof( T ); + m_info.create = Constructors< T >::createInto; + + m_info.createValueType = ValueType< T, void >::create; + + const auto name = normalizedListName( + isObject ? "QQmlListProperty" : "QList", T::staticMetaObject ); + + /* + QMetaType::fromType< QList< T >() creates a lot of symbols + that end up in QskQml.o for all gadgets. So we export only + registered lists. Registration might be done in the qskinny library + itself - or in QskQml.cpp. + + As we do not design plain data being a QObject I'm not sure + if we need to have QQmlListProperty< T > at all ... + */ + m_info.listId = QMetaType::fromName( name.constData() ); + + m_info.parserStatusCast = StaticCastSelector< T, QQmlParserStatus >::cast(); + m_info.valueSourceCast = StaticCastSelector< T, QQmlPropertyValueSource >::cast(); + m_info.valueInterceptorCast = StaticCastSelector< T, QQmlPropertyValueInterceptor >::cast(); + +#if QSK_STRUCT_VERSION >= 1 + m_info.finalizerCast = StaticCastSelector< T, QQmlFinalizerHook >::cast(); +#endif + } + + inline int ClassInfo::registerType() + { + return qmlregister( QQmlPrivate::TypeRegistration, &m_info ); + } +} + +#endif diff --git a/qmlexport/QskQmlModule.h b/qmlexport/QskQmlModule.h new file mode 100644 index 00000000..e036ef8a --- /dev/null +++ b/qmlexport/QskQmlModule.h @@ -0,0 +1,17 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_QML_MODULE_H +#define QSK_QML_MODULE_H + +namespace QskQmlModule +{ + const char name[] = "Skinny"; + + // major, minor + const int version[] = { 1, 0 }; +} + +#endif diff --git a/qmlexport/QskQmlRegister.h b/qmlexport/QskQmlRegister.h new file mode 100644 index 00000000..134309b2 --- /dev/null +++ b/qmlexport/QskQmlRegister.h @@ -0,0 +1,131 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_QML_REGISTER_H +#define QSK_QML_REGISTER_H + +#include "QskQmlModule.h" + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) +#include "QskQmlClassInfo.h" +#endif + +#include + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + #define QSK_QML_REGISTER 0 +#else + #define QSK_QML_REGISTER 1 +#endif + +// Required for QFlags to be constructed from an enum value +#define QSK_REGISTER_FLAGS( Type ) \ + QMetaType::registerConverter< int, Type >( []( int value ) { return Type( value ); } ) + +namespace QskQml +{ + inline const char* classNameQml( const QMetaObject& metaObject ) + { + // without the "Qsk" prefix + return metaObject.className() + 3; + } + + template< typename T > + inline int registerUncreatableType( const char* qmlName ) + { +#if QSK_QML_REGISTER + ClassInfo typeInfo( qmlName, &T::staticMetaObject ); + typeInfo.setTypeInfo< T >(); + + return typeInfo.registerType(); +#else + return qmlRegisterUncreatableType< T >( QskQmlModule::name, + QskQmlModule::version[0], QskQmlModule::version[1], qmlName, QString() ); +#endif + } + + inline int registerUncreatableMetaObject( + const QMetaObject& staticMetaObject, const char* qmlName ) + { +#if QSK_QML_REGISTER + ClassInfo typeInfo( qmlName, &staticMetaObject ); + return typeInfo.registerType(); +#else + return qmlRegisterUncreatableMetaObject( staticMetaObject, + QskQmlModule::name, QskQmlModule::version[0], QskQmlModule::version[1], + qmlName, QString() ); +#endif + } + + template< typename T > + inline int registerObject( const char* qmlName = nullptr ) + { + // the class name without the "Qsk" prefix + if ( qmlName == nullptr ) + qmlName = classNameQml( T::staticMetaObject ); + +#if QSK_QML_REGISTER + ClassInfo typeInfo( qmlName, &T::staticMetaObject ); + typeInfo.setTypeInfo< T >(); + + return typeInfo.registerType(); +#else + return qmlRegisterType< T >( QskQmlModule::name, + QskQmlModule::version[0], QskQmlModule::version[1], qmlName ); +#endif + } + + template< typename T > + inline int registerGadget() + { + auto className = classNameQml( T::staticMetaObject ); + +#if QSK_QML_REGISTER + /* + According to the QML naming rules uncreatables have to + start with a lowercase letter, while namespaces + and creatable items usually start with a upper letter. + This results in an odd naming scheme for the enums defined inside of gadgets. + + To work around this we register the gadget twice - starting with + upper or lower letter. + + Maybe it would make sense to only pass stripped metaObjects, where all + enums are removed from the first and everything else than the enums from + the second. TODO ... + */ + + if ( T::staticMetaObject.enumeratorCount() > 0 ) + registerUncreatableMetaObject( T::staticMetaObject, className ); + + QByteArray name = className; + name.data()[0] = std::tolower( name.data()[0] ); + + return registerUncreatableType< T >( name.constData() ); +#else + return registerUncreatableType< T >( className ); +#endif + } + + inline int registerNamespace( const QMetaObject& metaObject ) + { + return registerUncreatableMetaObject( metaObject, classNameQml( metaObject ) ); + } + + template< typename T > + inline int registerSingleton( QObject* singleton ) + { + const auto name = classNameQml( T::staticMetaObject ); + + return qmlRegisterSingletonInstance( QskQmlModule::name, + QskQmlModule::version[0], QskQmlModule::version[1], name, singleton ); + } +} + +#ifdef QSK_QML_REGISTER + #undef QSK_QML_REGISTER +#endif + +#endif