Inheritance
Some Qt APIs require you to override certain methods from an abstract base class, for example QAbstractItemModel
.
To support creating such subclasses directly from within Rust, CXX-Qt provides you with multiple helpers.
Some superclasses may require special parameters for construction. This can be achieved by using a custom constructor.
Accessing base class methods
To access the methods of a base class in Rust, use the #[inherit]
macro.
It can be placed in front of a function in a extern "RustQt"
block in a #[cxx_qt::bridge]
.
extern "RustQt" {
#[qobject]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(State, state)]
type CustomBaseClass = super::CustomBaseClassRust;
}
// Create Rust bindings for C++ functions of the base class (QAbstractItemModel)
extern "RustQt" {
/// Inherited beginInsertRows from the base class
#[inherit]
#[cxx_name = "beginInsertRows"]
unsafe fn begin_insert_rows(
self: Pin<&mut CustomBaseClass>,
parent: &QModelIndex,
first: i32,
last: i32,
);
/// Inherited endInsertRows from the base class
#[inherit]
#[cxx_name = "endInsertRows"]
unsafe fn end_insert_rows(self: Pin<&mut CustomBaseClass>);
/// Inherited beginRemoveRows from the base class
#[inherit]
#[cxx_name = "beginRemoveRows"]
unsafe fn begin_remove_rows(
self: Pin<&mut CustomBaseClass>,
parent: &QModelIndex,
first: i32,
last: i32,
);
/// Inherited endRemoveRows from the base class
#[inherit]
#[cxx_name = "endRemoveRows"]
unsafe fn end_remove_rows(self: Pin<&mut CustomBaseClass>);
/// Inherited beginResetModel from the base class
#[inherit]
#[cxx_name = "beginResetModel"]
unsafe fn begin_reset_model(self: Pin<&mut CustomBaseClass>);
/// Inherited endResetModel from the base class
#[inherit]
#[cxx_name = "endResetModel"]
unsafe fn end_reset_model(self: Pin<&mut CustomBaseClass>);
}
unsafe extern "RustQt" {
/// Clear the rows in the QAbstractListModel
#[qinvokable]
pub fn clear(self: Pin<&mut CustomBaseClass>);
}
impl qobject::CustomBaseClass {
/// Clear the rows in the QAbstractListModel
pub fn clear(mut self: Pin<&mut Self>) {
unsafe {
self.as_mut().begin_reset_model();
self.as_mut().rust_mut().id = 0;
self.as_mut().rust_mut().vector.clear();
self.as_mut().end_reset_model();
}
}
}
This code implements a QAbstractListModel
subclass.
For this, the clear
method implemented in Rust needs to call beginResetModel
and related methods from the base class, which are made accessible by using #[inherit]
.
See the Qt docs for more details on the specific subclassing requirements.
Methods in a extern "RustQt"
block similar to CXX can be tagged with an #[inherit]
attribute, with the same restrictions regarding which types can be used.
Additionally, the self
type must be either self: Pin<&mut qobject::T>
or self: &qobject::T
, where qobject::T
must refer to a QObject
marked with #[qobject]
in the #[cxx_qt::bridge]
If the Rust name should differ from the C++ method name, (e.g. due to snake_case vs. camelCase) use the #[cxx_name = "myFunctionName"]
or #[rust_name = "my_function_name"]
attributes.
#[inherit]
can also be used on signals that exist on the base class in anextern RustQt
block
Overriding base class methods
CXX-Qt allows invokables to be generated with the C++ modifiers necessary to implement inheritance.
This way methods can be overridden, declared as virtual
or final
.
C++ keyword | CXX-Qt attribute |
---|---|
override | #[cxx_override] |
virtual | #[cxx_virtual] |
final | #[cxx_final] |
The example below overrides the data
method inherited from the QAbstractListModel
.
extern "RustQt" {
#[qobject]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(State, state)]
type CustomBaseClass = super::CustomBaseClassRust;
}
unsafe extern "RustQt" {
#[qinvokable]
#[cxx_override]
fn data(self: &CustomBaseClass, index: &QModelIndex, role: i32) -> QVariant;
}
impl qobject::CustomBaseClass {
/// Retrieve the data for a given index and role
pub fn data(&self, index: &QModelIndex, role: i32) -> QVariant {
let role = qobject::Roles { repr: role };
if let Some((id, value)) = self.vector.get(index.row() as usize) {
return match role {
qobject::Roles::Id => QVariant::from(id),
qobject::Roles::Value => QVariant::from(value),
_ => QVariant::default(),
};
}
QVariant::default()
}
}
When a method is overridden using cxx_override
, the base class version of the method can be accessed by using #[inherit]
in combination with the #[cxx_name]
attribute.
In this case the base class version of the function must get a different name because Rust can't have two functions with the same name on one type.
Example:
extern "RustQt" {
#[qobject]
#[base = QAbstractListModel]
#[qml_element]
#[qproperty(State, state)]
type CustomBaseClass = super::CustomBaseClassRust;
}
unsafe extern "RustQt" {
/// Inherited canFetchMore from the base class
#[cxx_name = "canFetchMore"]
#[inherit]
fn base_can_fetch_more(self: &CustomBaseClass, parent: &QModelIndex) -> bool;
/// Inherited index from the base class
#[inherit]
fn index(
self: &CustomBaseClass,
row: i32,
column: i32,
parent: &QModelIndex,
) -> QModelIndex;
}
unsafe extern "RustQt" {
/// Return whether the base class can fetch more
// Example of overriding a C++ virtual method and calling the base class implementation.
#[qinvokable]
#[cxx_override]
#[cxx_name = "canFetchMore"]
fn can_fetch_more(self: &CustomBaseClass, parent: &QModelIndex) -> bool;
}
impl qobject::CustomBaseClass {
/// Return whether the base class can fetch more
// Example of overriding a C++ virtual method and calling the base class implementation.
pub fn can_fetch_more(&self, parent: &QModelIndex) -> bool {
self.base_can_fetch_more(parent)
}
}