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.
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 thanimpl T
whereqobject
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++ keyword | CXX-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.
on_<signal_name>
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,
);
}