Shared types

#[qenum] - Support for Q_ENUM and Q_ENUM_NS

Qt allows exposing enums to Qt's meta-object system, and thereby QML, with a set of macros:

  • Q_ENUM is used to expose an enum that is a member of a QObject
  • Q_ENUM_NS is used to expose an enum that is in a namespace.

CXX-Qt has support for both of these macros through the #[qenum] attribute.

QObject class enum (Q_ENUM)

CXX-Qt relies on CXX to expose enums from Rust to C++ and vice-versa. However, CXX only supports free enums that are not defined as part of a class. CXX-Qt doesn't change this, it only additionally exposes the enum as part of a QObject type to the meta-object system. So any #[qenum] in CXX-Qt is available as both a normal shared CXX enum and a Q_ENUM inside the associated QObject.

To expose a shared enum as a Q_ENUM inside a QObject class, add the #[qenum(...)] attribute to the enum definition. The argument to #[qenum(...)] must be the name of a #[qobject] that is defined in a extern "RustQt" block.

It is currently not possible to add a #[qenum(...)] to any extern "C++Qt" QObjects or a QObject that is defined in another #[cxx_qt::bridge].

Example:

#[cxx_qt::bridge]
pub mod qobject {
    #[qenum(CustomBaseClass)]
    /// State of the CustomBaseClass list model
    enum State {
        /// Another item is being added in the background
        Running,
        /// No items are being added in the background
        Idle,
    }

    extern "RustQt" {
        #[qobject]
        #[base = QAbstractListModel]
        #[qml_element]
        #[qproperty(State, state)]
        type CustomBaseClass = super::CustomBaseClassRust;
    }
}

Registering the class enum with QML

Note that Qt provides access to enum variants through the name of the class it is registered with, not the enum name itself. A side effect of this behavior is that the enum itself doesn't have to be registered with QML. Only the QObject class has to be registered.

In the previous example, the #[qml_element] attribute on the #[qobject] takes care of the registration.

Usage from QML:

    BusyIndicator {
        anchors {
            right: content.right
            bottom: content.bottom
            margins: 15
        }
        running: root.activeModel.state === CustomBaseClass.Running
    }

Namespaced enum (Q_ENUM_NS)

If there is no class that the enum should be associated with, Qt still allows exposing the enum to the meta-object system, as long as it is inside a namespace.

If there is a namespace associated with a shared enum simply add the #[qenum] attribute and CXX-Qt will expose it using Q_ENUM_NS.

Note that the namespace doesn't have to be specified on the enum directly, the enum can inherit the namespace from the surrounding bridge. This follows normal CXX namespacing rules.

Example:

#[cxx_qt::bridge]
pub mod qobject {
    #[qenum]
    #[namespace = "Colors"]
    /// An enum of colors
    enum Color {
        /// Red
        Red,
        /// Green
        Green,
        /// Blue
        Blue,
    }
}

📝 Note: Unfortunately, an important Qt limitation also applies to CXX-Qt. Namely, for any given namespace, there must be at most one bridge that exposes #[qenum] enums through that namespace. One bridge may expose enums through multiple namespaces, however.

Registering the namespaced enum with QML

Whilst Q_ENUM_NS creates the appropriate meta-objects, it doesn't add them to QML automatically. Like with Q_ENUM, access to the enum variants also doesn't happen through the enum directly, but rather the surrounding namespace.

Therefore, the namespace must be registered with the meta-object system and then exposed to QML. CXX-Qt automatically registers the namespace of a namespaced #[qenum] with the meta-object system.

Registration with QML can then be done by placing a qnamespace!("...") macro inside the bridge that defines the namespaced #[qenum] and adding a #[qml_element] attribute.

#[cxx_qt::bridge]
pub mod qobject {
    #[qml_element]
    qnamespace!("Colors");

    #[qenum]
    #[namespace = "Colors"]
    /// An enum of colors
    enum Color {
        /// Red
        Red,
        /// Green
        Green,
        /// Blue
        Blue,
    }
}

Usage from QML:

            ToolButton {
                text: qsTr("Red")
                onClicked: rustInvokables.storeColorWithEnum(Colors.Red);
            }