extern "RustQt"

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

    }
}

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

The CXX code generator uses your extern "Rust" section(s) to produce a C++ header file containing the corresponding C++ declarations. The generated header has a file name matching the module ident or the cxx_file_stem field in the #[cxx_qt::bridge] attribute and with a .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.

QObjects

Types specified with a #[qobject] attribute are generated in C++ as a QObject.

The left side of the type specifies the C++ generated type and name, 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;

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)]
        type MyObject = super::MyObjectRust;
    }

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

  • qml_name: Use a different type name for QML.
  • 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>);
    }

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.

        #[qobject]
        #[qml_element]
        #[qproperty(bool, connected)]
        #[qproperty(QUrl, connected_url)]
        #[qproperty(QUrl, previous_connected_url)]
        #[qproperty(QString, status_message)]
        type RustProperties = super::RustPropertiesRust;

The type and name of the

/// A QObject which has Q_PROPERTYs
pub struct RustPropertiesRust {
    /// A connected Q_PROPERTY
    connected: bool,

    /// A connected_url Q_PROPERTY
    pub(crate) connected_url: QUrl,

    /// A previous_connected_url Q_PROPERTY
    previous_connected_url: QUrl,

    /// A status_message Q_PROPERTY
    status_message: QString,
}

For every #[qproperty], CXX-Qt will generate setters and getters, as well as a "changed" signal.

On the C++ side:

  • setter: set<Property>
  • getter: get<Property>
  • changed: <Property>Changed

On the Rust side:

  • setter: set_<Property>
  • getter: <Property>
  • changed: <Property>_changed

Also the generated Rust methods for signals

  • 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.

Note that in the future it will be possible to specify custom getters and setters

Methods

Any signature with a self parameter is interpreted as a Rust method and exposed to C++ method for the given type. The type much 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
        fn red_value(self: &RustInvokables) -> f32;
    }

Implementations of the method are then written as normal on the C++ type 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]
        fn load_color(self: &RustInvokables) -> Result<QColor>;

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

        /// Mutable invokable method that stores a color with an enum
        #[qinvokable]
        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 the 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.

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

A full example can be found in the qml features.

Note that #[cxx_name = "..."] can also 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 defined in the enum, two methods are generated.

  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 and the remaining arguments are the signal parameters.

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

Note that by using the #[inherit] macro on a signal, connections can be made to property changes using the signal name <property>Changed with no parameters.

                    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 QMetaObject::Connection which is used to manage the connection.

Note that the QMetaObjectConnection returned by CXX-Qt behaves a bit different from the Qt C++ implementation.

When the QMetaObjectConnection is dropped, it automatically disconnects the connection, similar to how a C++ std::unique_ptr or Rusts Box behaves. If you don't want to store the QMetaObjectConnection, call release, which will drop the object without disconnecting. In this case, it is no longer possible to disconnect later.

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

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.

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]
        fn data_changed(
            self: Pin<&mut CustomBaseClass>,
            top_left: &QModelIndex,
            bottom_right: &QModelIndex,
            roles: &QVector_i32,
        );
    }