Search code examples
c++qtqmlqt5

It's possible to register already defined enum for MOC?


For instance I have enum from thirdparty library:

namespace Lib {
  enum class Foo {
    Bar,
    Baz
  };
};

I have tried use next wrapper

namespace Qml {
    Q_NAMESPACE
    using Foo = Lib::Foo;
    Q_ENUMS(Foo)
}

with qmlRegisterUncreatableMetaObject, but its don't work for me.

Can I register one in Meta Object System for using in QML, but without duplicates like:

class QmlObject {
    Q_GADGET

public:
    enum Foo {
        Bar = Lib::Bar,
        Baz = Lib::Baz
    };
    Q_ENUM(Foo)
};

Version of Qt is 5.15.2. Thanks.


Solution

  • I have found solution using the library magic_enum (at least v0.8.1, see limitations) and bit of modification in implementation of qmlRegisterType:

    #include <QtQml/qqmlprivate.h>
    #include <QtCore/private/qmetaobjectbuilder_p.h>
    #include <magic_enum.hpp>
        
    template<class T, class ... En>
    int qmlRegisterTypeWithEnums(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
    {
        QML_GETTYPENAMES
    
        //! register our enums here
        QMetaObjectBuilder builder(&T::staticMetaObject);
    
        ([](QMetaObjectBuilder & builder)
        {
            QMetaEnumBuilder enumBuilder {
                builder.addEnumerator(QByteArray::fromStdString(std::string{magic_enum::enum_type_name<En>()}))
            };
            enumBuilder.setIsScoped(magic_enum::is_scoped_enum_v<En>);
            constexpr auto entries = magic_enum::enum_entries<En>();
            for (const auto & pair : entries) {
                enumBuilder.addKey(QByteArray::fromStdString(std::string{pair.second}), static_cast<int>(pair.first));
            }
        }(builder), ...);
        //! ***************************
    
        QQmlPrivate::RegisterType type = {
            0,
    
            qRegisterNormalizedMetaType<T *>(pointerName.constData()),
            qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
            sizeof(T), QQmlPrivate::createInto<T>,
            QString(),
    
            uri, versionMajor, versionMinor, qmlName, builder.toMetaObject(),
    
            QQmlPrivate::attachedPropertiesFunc<T>(),
            QQmlPrivate::attachedPropertiesMetaObject<T>(),
    
            QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(),
            QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(),
            QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(),
    
            nullptr, nullptr,
    
            nullptr,
            0
        };
    
        return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
    }
    

    Now for thirdparty enum

    namespace Lib {
      enum class Foo {
        Bar = 2,
        Baz = 4
      };
    };
    

    and target Qt class

    namespace SomeQml {
    
        class QmlItem : public QObject
        {
            Q_OBJECT
        
            using Foo = Lib::Foo; // its important use without namespace in declaration
        
            Q_PROPERTY(Foo prop MEMBER m_val NOTIFY propChanged)
            ...
            Q_INVOKABLE handleFoo(Foo v);
            ...
        };
    
    }
    

    just call and use on QML side as QmlItem.Bar or QmlItem.Baz as an integer :

    qmlRegisterTypeWithEnums<QmlItem, Lib::Foo>("my.pack", 1, 0, "QmlItem");