questionable shadow class removed, using QQmlPrivate::RegisterType

again. Qt5 is using public APIs only - too many #ifdefs otherwise
This commit is contained in:
Uwe Rathmann 2024-02-29 13:31:26 +01:00 committed by uwerat
parent 5ecb85c725
commit 2b629123f9
7 changed files with 333 additions and 300 deletions

View File

@ -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)

View File

@ -4,8 +4,8 @@
*****************************************************************************/
#include "QskQml.h"
#include "QskQml.hpp"
#include "QskQmlRegister.h"
#include "QskLayoutQml.h"
#include "QskShortcutQml.h"
@ -44,8 +44,6 @@
#include <QskSeparator.h>
#include <QskShadowMetrics.h>
#include <QskSimpleListBox.h>
#include <QskSkin.h>
#include <QskSkinManager.h>
#include <QskSlider.h>
#include <QskSpinBox.h>
#include <QskStandardSymbol.h>
@ -61,9 +59,9 @@
#include <QskWindow.h>
#if QT_VERSION < QT_VERSION_CHECK( 6, 2, 0 )
QSK_QT_PRIVATE_BEGIN
QSK_QT_PRIVATE_BEGIN
#include <private/qqmlmetatype_p.h>
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 >();

View File

@ -1,288 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_QML_HPP
#define QSK_QML_HPP
#include <qqml.h>
#include <ctype.h>
#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

View File

@ -0,0 +1,85 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskQmlClassInfo.h"
#include "QskQmlModule.h"
#include <cstring>
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;
}

View File

@ -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 <qqml.h>
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

17
qmlexport/QskQmlModule.h Normal file
View File

@ -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

131
qmlexport/QskQmlRegister.h Normal file
View File

@ -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 <qqml.h>
#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