Nested Objects

Rust Qt objects can be nested as properties or parameters of each other.

A nested object is referred to by using a pointer to its QObject representation.

First define a type within an extern block for your bridge as normal.

extern "RustQt" { #[qobject] #[qml_element] #[qproperty(i32, counter)] type InnerObject = super::InnerObjectRust; }

This can then be used as a property, invokable parameter, or signal parameter by using *mut T. As seen in the example below which nests InnerObject into OuterObject.

The C++ CXX type needs to be used as the T type not the Rust struct

To reach mutable invokables and property setters of the nested object *mut T needs to be converted to Pin<&mut T>.

#[cxx_qt::bridge] pub mod qobject { extern "RustQt" { #[qobject] #[qml_element] #[qproperty(i32, counter)] type InnerObject = super::InnerObjectRust; } extern "RustQt" { /// A signal showing how to refer to another QObject as an argument /// /// # Safety /// /// Due to a raw pointer this is considered unsafe in CXX #[qsignal] unsafe fn called(self: Pin<&mut InnerObject>, inner: *mut InnerObject); } extern "RustQt" { #[qobject] #[qml_element] #[qproperty(*mut InnerObject, inner)] type OuterObject = super::OuterObjectRust; /// A signal showing how to refer to another QObject as an argument /// /// # Safety /// /// Due to a raw pointer this is considered unsafe in CXX #[qsignal] unsafe fn called(self: Pin<&mut OuterObject>, inner: *mut InnerObject); } extern "RustQt" { /// Print the count of the given inner QObject /// /// # Safety /// /// As we deref a pointer in a public method this needs to be marked as unsafe #[qinvokable] #[cxx_name = "printCount"] unsafe fn print_count(self: Pin<&mut OuterObject>, inner: *mut InnerObject); /// Reset the counter of the inner QObject stored in the Q_PROPERTY #[qinvokable] fn reset(self: Pin<&mut OuterObject>); } impl cxx_qt::Initialize for OuterObject {} } use core::pin::Pin; /// The inner QObject #[derive(Default)] pub struct InnerObjectRust { counter: i32, } /// The outer QObject which has a Q_PROPERTY pointing to the inner QObject pub struct OuterObjectRust { inner: *mut qobject::InnerObject, } impl Default for OuterObjectRust { fn default() -> Self { Self { inner: std::ptr::null_mut(), } } } impl qobject::OuterObject { /// Print the count of the given inner QObject /// /// # Safety /// /// As we deref a pointer in a public method this needs to be marked as unsafe pub unsafe fn print_count(self: Pin<&mut Self>, inner: *mut qobject::InnerObject) { if let Some(inner) = inner.as_ref() { println!("Inner object's counter property: {}", inner.counter()); } self.called(inner); } /// Reset the counter of the inner QObject stored in the Q_PROPERTY pub fn reset(self: Pin<&mut Self>) { // We need to convert the *mut T to a Pin<&mut T> so that we can reach the methods if let Some(inner) = unsafe { self.inner().as_mut() } { let pinned_inner = unsafe { Pin::new_unchecked(inner) }; // Now pinned inner can be used as normal pinned_inner.set_counter(10); } // Retrieve *mut T let inner = *self.inner(); unsafe { self.called(inner) }; } } impl cxx_qt::Initialize for qobject::OuterObject { /// Initialize the QObject, creating a connection from one signal to another fn initialize(self: core::pin::Pin<&mut Self>) { // Example of connecting a signal from one QObject to another QObject // this causes OuterObject::Called to trigger InnerObject::Called self.on_called(|qobject, obj| { // We need to convert the *mut T to a Pin<&mut T> so that we can reach the methods if let Some(inner) = unsafe { qobject.inner().as_mut() } { let pinned_inner = unsafe { Pin::new_unchecked(inner) }; // Now pinned inner can be used as normal unsafe { pinned_inner.called(obj); } } }) .release(); } }