extern "RustQt"

#[cxx_qt::bridge]
mod ffi {
    extern "RustQt" {

    }
}

The extern "RustQt" section of a CXX-Qt bridge declares Rust types and signatures to be made available to Qt and C++.

The CXX-Qt code generator uses your extern "RustQt" section(s) to produce a C++ header file containing the corresponding C++ declarations. The generated header has the same file name as the input rust file but with .cxxqt.h file extension.

A bridge module may contain zero or more extern "RustQt" blocks.

This complements the extern "Rust" CXX section but allows for declaring Qt specific features on C++ types.

Automatically converting to camel or snake case can be done through an attribute at the block level.

QObjects

The #[qobject] attribute may be placed on a type alias to generate a QObject type in C++.

The left side of the type alias specifies the QObject type generated in C++. When referring to the C++ context this should be used. The right side of the type specifies which Rust type provides the inner implementation of the type (for example fields).

#[cxx_qt::bridge]
mod ffi {
    extern "RustQt" {
        #[qobject]
        type MyObject = super::MyObjectRust;
    }
}

#[derive(Default)]
struct MyObjectRust;

๐Ÿ“ Note: At the moment, only super:: is allowed as the path for the inner Rust type. Therefore, the Rust type must be available just outside the bridge module. You can bring any type into scope with a pub use directive if you want to reuse an existing type.

QML Attributes

QObjects can be registered as a QML type directly at build time by using the #[qml_element] attribute.

    unsafe extern "RustQt" {
        // The QObject definition
        // We tell CXX-Qt that we want a QObject class with the name MyObject
        // based on the Rust struct MyObjectRust.
        #[qobject]
        #[qml_element]
        #[qproperty(i32, number)]
        #[qproperty(QString, string)]
        #[namespace = "my_object"]
        type MyObject = super::MyObjectRust;
    }

Additionally, you can configure the QML registration with these attributes:

  • #[qml_element]: Declare type as a qml element. An alternative type name for QML can be used like #[qml_element = "MyName"]
  • #[qml_uncreatable]: Mark the type as uncreatable from QML. It may still be returned by C++/Rust code.
  • #[qml_singleton]: An instance of the QObject will be instantiated as a singleton in QML.

The Rust file must be included within a QML module in the build.rs file

base attribute

Use the base attribute to specify a C++ class that the C++ QObject will inherit from. The base class must inherit from QObject (directly or indirectly). If you do not specify a base attribute, it will inherit directly from QObject.

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

Use the CXX include! macro to include the appropriate C++ header for the base class:

    unsafe extern "C++" {
        include!(<QtCore/QAbstractListModel>);
        /// Base for Qt type
        type QAbstractListModel;
    }

For more information on inheritance and how to override methods see the Inheritance & Overriding page.

Full Example

Traits

The Default trait needs to be implemented for the #[qobject] marked struct either by hand or by using the derive macro #[derive(Default)]. Or the cxx_qt::Constructor trait needs to be implemented for the type.

For further documentation see the traits page.

Properties

The #[qproperty(TYPE, NAME, ...)] attribute can be specified on a #[qobject] marked type to expose a Q_PROPERTY on the generated QObject.

    unsafe extern "RustQt" {
        // The QObject definition
        // We tell CXX-Qt that we want a QObject class with the name MyObject
        // based on the Rust struct MyObjectRust.
        #[qobject]
        #[qml_element]
        #[qproperty(i32, number)]
        #[qproperty(QString, string)]
        #[namespace = "my_object"]
        type MyObject = super::MyObjectRust;
    }

If no other attributes are specified on the property, CXX-Qt will generate setters and getters, as well as a "changed" signal automatically. The type and name of the property must then match a field in the inner Rust struct.

#[derive(Default)]
pub struct MyObjectRust {
    number: i32,
    string: QString,
}

CXX-Qt will then generate these functions:

C++Rust
setterset<Property>1set_<property>
getterget<Property>1<property>
changed signal<property>Changed<property>_changed

As with any signal, CXX-Qt will generate the corresponding connection functions on the Rust side:

  • connect: connect_<property>_changed
  • on: on_<property>_changed

Where <property> is the name of the property.

These setters and getters assure that the changed signal is emitted every time the property is edited.

1

For the C++ getters and setters, the first character of the property name will automatically be upper-cased. For single-word property names, this leads to camelCase naming, which is the default in Qt.

Custom Properties

In case the automatically generated functions do not work for your use-case, you can disable CXX-Qts auto-generation and write a totally custom property. For example, this could be the case if your property doesn't correspond to any single field in the inner Rust struct.

You can specify custom getters, setters and notify signals, using flags passed like so: #[qproperty(TYPE, NAME, READ = myGetter, WRITE = mySetter, NOTIFY = myOnChanged)]

๐Ÿ“ Note: the key for the flags use all capitals like in the Qt version of qproperty

It is possible to use any combination of flags or omit some of them entirely, but if any flags are specified, the READ flag must be included.

If a custom function is specified for a flag, the function must be declared in the bridge and a corresponding implementation must exist.

Some of the flags may be passed with or without specifying a function (e.g. READ and READ=...). For these flags CXX-Qt will auto-generate the implementation if no function was provided, as outlined in the previous section. E.g. #[qproperty(i32, num, READ)] will automatically generate a getter function called get_num in Rust, and getNum in C++. Therefore, #[qproperty(i32, num)] is just shorthand for #[qproperty(i32, num, READ, WRITE, NOTIFY)].

Additionally, using cxx_name and rust_name is possible similarly to the attributes available on other items. e.g. #[qproperty(i32, num, cxx_name = "numberProp")]

Examples

  • #[qproperty(TYPE, NAME, READ)] A read only property with auto-generated getter
  • #[qproperty(TYPE, NAME, READ = myGetter, WRITE, NOTIFY)] custom getter provided, but auto-generated setter and changed signal
  • #[qproperty(TYPE, NAME)] is shorthand for #[qproperty(TYPE, NAME, READ, WRITE, NOTIFY)]
  • #[qproperty(TYPE, NAME, WRITE)] is an error as the READ flag is required

Available Flags

  • READ or READ = my_getter
    • Specifies that the property should be readable (always required if flags are passed), with optional user defined getter
  • WRITE or WRITE = my_setter
    • Specifies that the property should be writeable, with optional user defined setter
  • NOTIFY or NOTIFY = my_on_changed
    • Specifies that the property should emit a notify signal on change, with optional user defined signal name
  • CONSTANT
    • Specifies that the property should be constant (implication is that the getter returns the same value every time for that particular instance)
    • CONSTANT is not available for properties which use WRITE or NOTIFY and will not compile
  • REQUIRED
    • Specifies that the property must be set by a user of the class, useful in QML as the class cannot be instantiated unless the property has been set
  • FINAL
    • Specifies that the property will not be overriden by a derived class
  • RESET = my_reset
    • Specifies a function to reset the property to a default value, user function must be provided or it will not compile
  • cxx_name = "myCxxName"
    • Specifies an alternative name to use on the C++ side, applying to the property name as well as autogenerated functions
  • rust_name = "my_rust_name"
    • Specifies an alternative name to use on the rust side, applying to the property name as well as autogenerated functions

Methods

Any signature with a self parameter is interpreted as a Rust method and exposed to C++ method for the given type. The type must be either a shared reference self: &T or a pinned mutable reference self: Pin<&mut T>, where T is the QObject type.

    unsafe extern "RustQt" {
        /// C++ only method which returns the red value
        #[cxx_name = "redValue"]
        fn red_value(self: &RustInvokables) -> f32;
    }

Implementations of the method are then written as normal outside the bridge.

impl qobject::RustInvokables {
    /// C++ only method which returns the red value
    pub fn red_value(&self) -> f32 {
        self.red
    }
}

Note how this uses impl qobject::T rather than impl T where qobject is the bridge module name.

Invokables

The #[qinvokable] attribute can be specified on signatures to expose them as a Q_INVOKABLE in C++.

    unsafe extern "RustQt" {
        /// Immutable invokable method that returns the QColor
        #[qinvokable]
        #[cxx_name = "loadColor"]
        fn load_color(self: &RustInvokables) -> Result<QColor>;

        /// Mutable invokable method that stores a color
        #[qinvokable]
        #[cxx_name = "storeColor"]
        fn store_color(self: Pin<&mut RustInvokables>, red: f32, green: f32, blue: f32);

        /// Mutable invokable method that stores a color with an enum
        #[qinvokable]
        #[cxx_name = "storeColorWithEnum"]
        fn store_color_with_enum(self: Pin<&mut RustInvokables>, color: Color);

        /// Mutable invokable method with no parameters that resets the color
        #[qinvokable]
        fn reset(self: Pin<&mut RustInvokables>);
    }

Implementations then have no difference to non invokable methods.

impl qobject::RustInvokables {
    /// Immutable invokable method that returns the QColor
    pub fn load_color(&self) -> Result<QColor, i32> {
        Ok(self.as_qcolor())
    }

    /// Mutable invokable method that stores a color
    pub fn store_color(self: Pin<&mut Self>, red: f32, green: f32, blue: f32) {
        self.store_helper(red, green, blue);
    }

    /// QENUMS!
    pub fn store_color_with_enum(self: Pin<&mut Self>, color: qobject::Color) {
        use qobject::Color;
        let (r, g, b) = match color {
            Color::Red => (1.0, 0.0, 0.0),
            Color::Green => (0.0, 1.0, 0.0),
            Color::Blue => (0.0, 0.0, 1.0),
            _ => (0.0, 0.0, 0.0),
        };
        self.store_helper(r, g, b);
    }

    /// Mutable invokable method with no parameters that resets the color
    pub fn reset(self: Pin<&mut Self>) {
        self.store_helper(0.0, 0.4667, 0.7843);
    }
}

Inheritance

Methods or signals that already exist on the base class of an object can be accessed via the #[inherit] attribute.

For documentation see the inheritance page.

Specifiers

Generated methods can have C++ specifiers necessary to implement inheritance.

C++ keywordCXX-Qt attribute
override#[cxx_override]
virtual#[cxx_virtual]
final#[cxx_final]

These are specified as an attribute on the method signature.

    unsafe extern "RustQt" {
        #[qinvokable]
        #[cxx_override]
        fn data(self: &CustomBaseClass, index: &QModelIndex, role: i32) -> QVariant;
    }

Signals

The qsignal attribute is used in an extern "RustQt" block to define signals for a QObject.

    unsafe extern "RustQt" {
        /// A Q_SIGNAL emitted when a connection occurs
        #[qsignal]
        fn connected(self: Pin<&mut RustSignals>, url: &QUrl);

        /// A Q_SIGNAL emitted when a disconnect occurs
        #[qsignal]
        fn disconnected(self: Pin<&mut RustSignals>);

        /// A Q_SIGNAL emitted when an error occurs
        #[qsignal]
        fn error(self: Pin<&mut RustSignals>, message: QString);
    }

For every function signature in the extern block, CXX-Qt will generate a signal on the corresponding QObject. If the function has parameters, they will become the parameters for the corresponding signal. Signal functions do not need to be implemented manually.

If a signal is defined on the base class of the QObject then #[inherit] can be used, which will cause CXX-Qt to access the existing Q_SIGNAL from the base class.

A full example can be found in the qml features example.

๐Ÿ“ Note: #[cxx_name="..."] and #[rust_name="..."] can be used on a signal to declare a different name in C++ to Rust

๐Ÿ“ Note: using pub(self) as the visibility of the signal allows for declaring private signals

Connecting to a signal

For every signal, CXX-Qt will generate two methods to connect to it.

  1. on_<signal_name>
  2. connect_<signal_name>

The on_<signal_name> method takes a handler function as the parameter, which will be called when the signal is emitted. That handler function's first argument is the QObject that emitted the signal and the remaining arguments are the signal parameters.

The connect_<signal_name> function additionally takes the Qt connection type as a parameter.

                    let connections = [
                        qobject.as_mut().on_connected(|_, url| {
                            println!("Connected: {}", url);
                        }),
                        qobject.as_mut().on_disconnected(|_| {
                            println!("Disconnected");
                        }),
                        // Demonstration of connecting with a different connection type
                        qobject.as_mut().connect_error(
                            |_, message| {
                                println!("Error: {}", message);
                            },
                            ConnectionType::QueuedConnection,
                        ),
                    ];
                    qobject.as_mut().rust_mut().connections = Some(connections);

Each connection returns a QMetaObjectConnectionGuard, which is a RAII wrapper around the QMetaObject::Connection and automatically disconnects the connection when the guard is dropped. This is similar to C++ std::lock_guard, std::unique_ptr, or Rusts Box.

Example:

                // By making connections None, we trigger a drop on the connections
                // this then causes disconnections
                qobject.as_mut().rust_mut().connections = None;

If you don't want to store the QMetaObjectConnectionGuard, call release, which will turn it into the internal QMetaObjectConnection, which is a direct wrapper of QMetaObject::Connection and doesn't disconnect on drop.

๐Ÿ“ Note: The QMetaObjectConnection has a disconnect method which can be called manually later

Emitting a signal

Call the function signature defined in the extern "RustQt" block to emit the signal.

Note that these are defined on the generated QObject qobject::T, so can be called from any mutable #[qinvokable].

The function will immediately emit the signal. Depending on the connection type, the connected slots will be called either immediately or from the event loop (See the different connection types). To queue the call until the next cycle of the Qt event loop, you can use the CxxQtThread.

Signal Inheritance

If a signal is defined on the base class of the QObject then the #[inherit] attribute can be used to indicate to CXX-Qt that the Q_SIGNAL does not need to be created in C++.

    unsafe extern "RustQt" {
        /// Inherit the DataChanged signal from the QAbstractListModel base class
        #[inherit]
        #[qsignal]
        #[cxx_name = "dataChanged"]
        fn data_changed(
            self: Pin<&mut CustomBaseClass>,
            top_left: &QModelIndex,
            bottom_right: &QModelIndex,
            roles: &QVector_i32,
        );
    }