From 15c43d07baae689b3c92d730440c9810d50f0427 Mon Sep 17 00:00:00 2001 From: Ben Ford Date: Tue, 8 Apr 2025 18:31:35 +0100 Subject: [PATCH 1/4] Add Transitive casting and testing code --- crates/cxx-qt-gen/src/generator/rust/fragment.rs | 15 +++++++++++---- examples/qml_minimal/qml/main.qml | 11 +++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/cxx-qt-gen/src/generator/rust/fragment.rs b/crates/cxx-qt-gen/src/generator/rust/fragment.rs index 03624d57c..03f2e2bcf 100644 --- a/crates/cxx-qt-gen/src/generator/rust/fragment.rs +++ b/crates/cxx-qt-gen/src/generator/rust/fragment.rs @@ -83,11 +83,18 @@ impl GeneratedRustFragment { #upcast_fn_qualified(this) } - unsafe fn from_base_ptr(base: *const #base_qualified) -> *const Self { - #downcast_fn_qualified(base) + unsafe fn from_base_ptr(base: *const #base_qualified) -> *const Self { + #downcast_fn_qualified(base) + } } - } - }], + }, + // Add back once we figure out the bug with QObject, for automatic transitive casts + // parse_quote! { + // unsafe impl ::cxx_qt::MainCast for #struct_name { + // type Base = #base_qualified; + // } + // } + ], }) } diff --git a/examples/qml_minimal/qml/main.qml b/examples/qml_minimal/qml/main.qml index 0b32505e4..5b7c3b6ec 100644 --- a/examples/qml_minimal/qml/main.qml +++ b/examples/qml_minimal/qml/main.qml @@ -28,6 +28,11 @@ ApplicationWindow { string: qsTr("My String with my number: %1").arg(myObject.number) } + A { + id: a + number: 10 + } + Column { anchors.fill: parent anchors.margins: 10 @@ -55,6 +60,12 @@ ApplicationWindow { onClicked: myObject.sayHi(myObject.string, myObject.number) } + Button { + text: qsTr("Test A") + + onClicked: a.test() + } + Button { text: qsTr("Quit") From 1376df26796042ffe816922b0c72ebda03479f31 Mon Sep 17 00:00:00 2001 From: Ben Ford Date: Tue, 8 Apr 2025 18:31:35 +0100 Subject: [PATCH 2/4] Add Transitive casting and testing code --- crates/cxx-qt-gen/src/generator/rust/fragment.rs | 15 ++++----------- crates/cxx-qt/src/casting.rs | 1 + examples/qml_minimal/qml/main.qml | 11 ----------- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/crates/cxx-qt-gen/src/generator/rust/fragment.rs b/crates/cxx-qt-gen/src/generator/rust/fragment.rs index 03f2e2bcf..03624d57c 100644 --- a/crates/cxx-qt-gen/src/generator/rust/fragment.rs +++ b/crates/cxx-qt-gen/src/generator/rust/fragment.rs @@ -83,18 +83,11 @@ impl GeneratedRustFragment { #upcast_fn_qualified(this) } - unsafe fn from_base_ptr(base: *const #base_qualified) -> *const Self { - #downcast_fn_qualified(base) - } + unsafe fn from_base_ptr(base: *const #base_qualified) -> *const Self { + #downcast_fn_qualified(base) } - }, - // Add back once we figure out the bug with QObject, for automatic transitive casts - // parse_quote! { - // unsafe impl ::cxx_qt::MainCast for #struct_name { - // type Base = #base_qualified; - // } - // } - ], + } + }], }) } diff --git a/crates/cxx-qt/src/casting.rs b/crates/cxx-qt/src/casting.rs index e4ba48f96..5b658d6b4 100644 --- a/crates/cxx-qt/src/casting.rs +++ b/crates/cxx-qt/src/casting.rs @@ -127,6 +127,7 @@ unsafe impl Upcast for T { self } } + /// Implements transitive casting in a chain for a type and all its ancestors /// /// Suppose you have 3 types, A, B and C where A -> B and B -> C casting relationships exist, diff --git a/examples/qml_minimal/qml/main.qml b/examples/qml_minimal/qml/main.qml index 5b7c3b6ec..0b32505e4 100644 --- a/examples/qml_minimal/qml/main.qml +++ b/examples/qml_minimal/qml/main.qml @@ -28,11 +28,6 @@ ApplicationWindow { string: qsTr("My String with my number: %1").arg(myObject.number) } - A { - id: a - number: 10 - } - Column { anchors.fill: parent anchors.margins: 10 @@ -60,12 +55,6 @@ ApplicationWindow { onClicked: myObject.sayHi(myObject.string, myObject.number) } - Button { - text: qsTr("Test A") - - onClicked: a.test() - } - Button { text: qsTr("Quit") From 9fec352a67f8e1d19fab0821e30ebb3c4c841dd4 Mon Sep 17 00:00:00 2001 From: Ben Ford Date: Tue, 20 May 2025 11:28:18 +0100 Subject: [PATCH 3/4] Add book entry and rename macro --- crates/cxx-qt/src/casting.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cxx-qt/src/casting.rs b/crates/cxx-qt/src/casting.rs index 5b658d6b4..e4ba48f96 100644 --- a/crates/cxx-qt/src/casting.rs +++ b/crates/cxx-qt/src/casting.rs @@ -127,7 +127,6 @@ unsafe impl Upcast for T { self } } - /// Implements transitive casting in a chain for a type and all its ancestors /// /// Suppose you have 3 types, A, B and C where A -> B and B -> C casting relationships exist, From b010ed19c093e72359c69f8eda467843e0b2138f Mon Sep 17 00:00:00 2001 From: Ben Ford Date: Fri, 2 May 2025 16:46:01 +0100 Subject: [PATCH 4/4] Add blanket implementation of QObjectExt - All objects which upcast to QObject now have QObjectExt implemented - Via transitive casting and using the QObjectExt trait, any descendent of QObject can access its methods --- crates/cxx-qt-lib/src/core/qobject.rs | 33 +++++++++++++++++++-------- crates/cxx-qt/src/lib.rs | 22 +++++++++++++++++- crates/cxx-qt/src/qobject.rs | 4 ---- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/crates/cxx-qt-lib/src/core/qobject.rs b/crates/cxx-qt-lib/src/core/qobject.rs index 0f818865d..80b44f636 100644 --- a/crates/cxx-qt-lib/src/core/qobject.rs +++ b/crates/cxx-qt-lib/src/core/qobject.rs @@ -3,6 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use cxx_qt::casting::Upcast; pub use cxx_qt::QObject; use std::pin::Pin; @@ -33,6 +34,9 @@ pub mod ffi { #[rust_name = "set_parent"] pub unsafe fn setParent(self: Pin<&mut Self>, parent: *mut QObjectExternal); + + #[rust_name = "dump_object_info"] + fn dumpObjectInfo(&self); } } @@ -49,9 +53,11 @@ pub trait QObjectExt { fn object_name(&self) -> QString; - fn parent(&self) -> *mut Self; + fn parent(&self) -> *mut QObjectExternal; fn set_parent(self: Pin<&mut Self>, parent: &Self); + + fn dump_object_info(&self); } /// Used to convert the QObject type from the library type to the C++ type, as a pin @@ -70,31 +76,38 @@ fn cast(obj: &QObject) -> &QObjectExternal { } } -impl QObjectExt for QObject { +impl QObjectExt for T +where + T: Upcast, +{ fn block_signals(self: Pin<&mut Self>, block: bool) -> bool { - cast_pin(self).block_signals(block) + cast_pin(self.upcast_pin()).block_signals(block) } fn signals_blocked(&self) -> bool { - cast(self).signals_blocked() + cast(self.upcast()).signals_blocked() } fn set_object_name(self: Pin<&mut Self>, name: &QString) { - cast_pin(self).set_object_name(name) + cast_pin(self.upcast_pin()).set_object_name(name) } fn object_name(&self) -> QString { - cast(self).object_name() + cast(self.upcast()).object_name() } - fn parent(&self) -> *mut Self { - cast(self).parent() as *mut Self + fn parent(&self) -> *mut QObjectExternal { + cast(self.upcast()).parent() } fn set_parent(self: Pin<&mut Self>, parent: &Self) { + let s = cast_pin(self.upcast_pin()); unsafe { - cast_pin(self) - .set_parent(cast(parent) as *const QObjectExternal as *mut QObjectExternal); + s.set_parent(cast(parent.upcast()) as *const QObjectExternal as *mut QObjectExternal) } } + + fn dump_object_info(&self) { + cast(self.upcast()).dump_object_info() + } } diff --git a/crates/cxx-qt/src/lib.rs b/crates/cxx-qt/src/lib.rs index 41fbe1668..9ba9e064b 100644 --- a/crates/cxx-qt/src/lib.rs +++ b/crates/cxx-qt/src/lib.rs @@ -9,7 +9,7 @@ //! //! See the [book](https://kdab.github.io/cxx-qt/book/) for more information. -use std::{fs::File, io::Write, path::Path}; +use std::{fs::File, io::Write, path::Path, pin::Pin}; #[doc(hidden)] pub mod casting; @@ -125,6 +125,26 @@ pub use threading::{CxxQtThread, ThreadingQueueError}; #[doc(hidden)] pub use static_assertions; +/// This trait provides wrappers for objects which are QObjects or can be turned into a [QObject]. +/// It is automatically implemented for any types which implement [casting::Upcast] to a [QObject]. +pub trait AsObject { + /// Cast self reference into a [QObject] reference + fn as_qobject(&self) -> &QObject; + + /// Cast pinned mutable reference into a pinned mutable [QObject] reference + fn as_qobject_mut(self: Pin<&mut Self>) -> Pin<&mut QObject>; +} + +impl> AsObject for T { + fn as_qobject(&self) -> &QObject { + self.upcast() + } + + fn as_qobject_mut(self: Pin<&mut Self>) -> Pin<&mut QObject> { + self.upcast_pin() + } +} + /// This trait is automatically implemented for all QObject types generated by CXX-Qt. /// It provides information about the inner Rust struct that is wrapped by the QObject, as well as the methods /// that Cxx-Qt will generate for the QObject. diff --git a/crates/cxx-qt/src/qobject.rs b/crates/cxx-qt/src/qobject.rs index f1f02edf9..588199a83 100644 --- a/crates/cxx-qt/src/qobject.rs +++ b/crates/cxx-qt/src/qobject.rs @@ -14,10 +14,6 @@ mod ffi { /// Most methods available on this type are within the [cxx_qt_lib::core::QObjectExt] trait, /// which needs to be imported in order to access these. type QObject; - - #[cxx_name = "dumpObjectInfo"] - /// Dump information about this QObjects name and signals - fn dump_object_info(&self); } }