diff --git a/examples/demo_threading/qml/MainWindow.qml b/examples/demo_threading/qml/MainWindow.qml index e60524257..e7a02b3d1 100644 --- a/examples/demo_threading/qml/MainWindow.qml +++ b/examples/demo_threading/qml/MainWindow.qml @@ -26,10 +26,6 @@ Window { EnergyUsage { id: energyUsage - - // FIXME: have the ability to HandleInit so we can start the server - // https://github.com/KDAB/cxx-qt/issues/13 - Component.onCompleted: startServer() } Image { diff --git a/examples/demo_threading/rust/src/lib.rs b/examples/demo_threading/rust/src/lib.rs index 690b4ed1b..4ae0708f6 100644 --- a/examples/demo_threading/rust/src/lib.rs +++ b/examples/demo_threading/rust/src/lib.rs @@ -80,11 +80,9 @@ mod ffi { /// A Q_INVOKABLE that returns the current power usage for a given uuid #[qinvokable] fn sensor_power(self: Pin<&mut qobject::EnergyUsage>, uuid: &QString) -> f64; - - /// A Q_INVOKABLE which starts the TCP server - #[qinvokable] - fn start_server(self: Pin<&mut qobject::EnergyUsage>); } + + impl cxx_qt::Constructor<()> for qobject::EnergyUsage {} } use crate::{ @@ -113,9 +111,29 @@ impl ffi::EnergyUsageQt { 0.0 } } +} + +impl cxx_qt::Constructor<()> for qobject::EnergyUsage { + type NewArguments = (); + type BaseArguments = (); + type InitializeArguments = (); + + fn route_arguments( + _args: (), + ) -> ( + Self::NewArguments, + Self::BaseArguments, + Self::InitializeArguments, + ) { + ((), (), ()) + } + + fn new((): ()) -> EnergyUsage { + EnergyUsage::default() + } /// A Q_INVOKABLE which starts the TCP server - fn start_server(mut self: Pin<&mut Self>) { + fn initialize(mut self: core::pin::Pin<&mut Self>, _arguments: Self::InitializeArguments) { if self.rust().join_handles.is_some() { println!("Already running a server!"); return; diff --git a/examples/qml_features/CMakeLists.txt b/examples/qml_features/CMakeLists.txt index 2a19c49c7..c692c2934 100644 --- a/examples/qml_features/CMakeLists.txt +++ b/examples/qml_features/CMakeLists.txt @@ -15,10 +15,10 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT USE_QT5) - find_package(Qt6 COMPONENTS Core Gui Qml QuickControls2 QmlImportScanner QuickTest Test) + find_package(Qt6 COMPONENTS Core Gui Qml Quick QuickControls2 QmlImportScanner QuickTest Test) endif() if(NOT Qt6_FOUND) - find_package(Qt5 5.15 COMPONENTS Core Gui Qml QuickControls2 QmlImportScanner QuickTest Test REQUIRED) + find_package(Qt5 5.15 COMPONENTS Core Gui Qml Quick QuickControls2 QmlImportScanner QuickTest Test REQUIRED) endif() get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION) @@ -48,6 +48,7 @@ target_link_libraries(${APP_NAME}_lib INTERFACE Qt::Core Qt::Gui Qt::Qml + Qt::Quick Qt::QuickControls2 ) diff --git a/examples/qml_features/qml/main.qml b/examples/qml_features/qml/main.qml index 71e789a4c..cc0392999 100644 --- a/examples/qml_features/qml/main.qml +++ b/examples/qml_features/qml/main.qml @@ -112,6 +112,10 @@ ApplicationWindow { name: "Singleton" source: "qrc:/pages/SingletonPage.qml" } + ListElement { + name: "Custom Parent Class" + source: "qrc:/pages/CustomParentClassPage.qml" + } } } } diff --git a/examples/qml_features/qml/pages/CustomParentClassPage.qml b/examples/qml_features/qml/pages/CustomParentClassPage.qml new file mode 100644 index 000000000..20efb4617 --- /dev/null +++ b/examples/qml_features/qml/pages/CustomParentClassPage.qml @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import com.kdab.cxx_qt.demo 1.0 + +Page { + header: ToolBar { + RowLayout { + anchors.fill: parent + + ToolButton { + text: qsTr("Red") + + onClicked: customPainter.color = "red" + } + + ToolButton { + text: qsTr("Green") + + onClicked: customPainter.color = "green" + } + + ToolButton { + text: qsTr("Blue") + + onClicked: customPainter.color = "blue" + } + + Item { + Layout.fillWidth: true + } + } + } + + + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + CustomParentClass { + id: customPainter + color: "red" + Layout.alignment: Qt.AlignHCenter + height: 200 + width: 200 + } + + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: qsTr("In this demo the Rectangle is rendered in Rust by implementing a QQuickPaintedItem.") + wrapMode: Text.Wrap + } + } +} diff --git a/examples/qml_features/qml/qml.qrc b/examples/qml_features/qml/qml.qrc index aab5c7461..a1fd3253d 100644 --- a/examples/qml_features/qml/qml.qrc +++ b/examples/qml_features/qml/qml.qrc @@ -11,6 +11,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 main.qml pages/ContainersPage.qml pages/CustomBaseClassPage.qml + pages/CustomParentClassPage.qml pages/InvokablesPage.qml pages/MultipleQObjectsPage.qml pages/NestedQObjectsPage.qml diff --git a/examples/qml_features/rust/build.rs b/examples/qml_features/rust/build.rs index 8403e3108..d305d5763 100644 --- a/examples/qml_features/rust/build.rs +++ b/examples/qml_features/rust/build.rs @@ -11,6 +11,7 @@ fn main() { CxxQtBuilder::new() .file("src/containers.rs") .file("src/custom_base_class.rs") + .file("src/custom_parent_class.rs") .file("src/invokables.rs") .file("src/multiple_qobjects.rs") .file("src/nested_qobjects.rs") @@ -28,6 +29,9 @@ fn main() { cc.file("../cpp/custom_object.cpp"); }) .qobject_header("../cpp/custom_object.h") + // Ensure that Quick module is linked, so that cargo test can work. + // In a CMake project this isn't required as the linking happens in CMake. + .qt_module("Quick") .build(); } // ANCHOR_END: book_build_rs diff --git a/examples/qml_features/rust/src/custom_parent_class.rs b/examples/qml_features/rust/src/custom_parent_class.rs new file mode 100644 index 000000000..539fab99e --- /dev/null +++ b/examples/qml_features/rust/src/custom_parent_class.rs @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! This example shows how a custom parent class can be used to inherit from a QQuickItem based object. + +/// A CXX-Qt bridge which shows a custom parent class can be used +#[cxx_qt::bridge(cxx_file_stem = "custom_parent_class")] +mod ffi { + unsafe extern "C++" { + /// QColor from cxx_qt_lib + type QColor = cxx_qt_lib::QColor; + include!("cxx-qt-lib/qcolor.h"); + + /// QRectF from cxx_qt_lib + type QRectF = cxx_qt_lib::QRectF; + include!("cxx-qt-lib/qrectf.h"); + + /// QSizeF from cxx_qt_lib + type QSizeF = cxx_qt_lib::QSizeF; + include!("cxx-qt-lib/qsizef.h"); + } + + // Define the API from QPainter that we need + unsafe extern "C++" { + /// QPainter from Qt + type QPainter; + include!(); + + /// QPainter::fillRect from Qt + #[rust_name = "fill_rect"] + fn fillRect(self: Pin<&mut QPainter>, rectangle: &QRectF, color: &QColor); + } + + // Define the API from QtQuick that we need + unsafe extern "C++" { + /// Define QQuickItem as a type + type QQuickItem; + include!(); + + include!(); + } + + /// A struct which inherits from QQuickPaintedItem + /// + /// Which has a parent of the type QQuickItem rather than QObject. + #[cxx_qt::qobject( + base = "QQuickPaintedItem", + qml_uri = "com.kdab.cxx_qt.demo", + qml_version = "1.0" + )] + #[derive(Default)] + pub struct CustomParentClass { + #[qproperty] + color: QColor, + } + + impl cxx_qt::Constructor<()> for qobject::CustomParentClass {} + + unsafe extern "RustQt" { + /// Override QQuickPaintedItem::paint to draw two rectangles in Rust using QPainter + #[qinvokable(cxx_override)] + unsafe fn paint(self: Pin<&mut qobject::CustomParentClass>, painter: *mut QPainter); + + // Define that we need to inherit size() from the base class + #[inherit] + fn size(self: &qobject::CustomParentClass) -> QSizeF; + + // Define that we need to inherit update() from the base class + #[inherit] + fn update(self: Pin<&mut qobject::CustomParentClass>); + } +} + +use core::pin::Pin; +use cxx_qt_lib::QRectF; + +// TODO: this will change to qobject::RustInvokables once +// https://github.com/KDAB/cxx-qt/issues/559 is done +impl ffi::CustomParentClassQt { + /// Override QQuickPaintedItem::paint to draw two rectangles in Rust using QPainter + fn paint(self: Pin<&mut Self>, painter: *mut ffi::QPainter) { + // We need to convert the *mut QPainter to a Pin<&mut QPainter> so that we can reach the methods + if let Some(painter) = unsafe { painter.as_mut() } { + let mut pinned_painter = unsafe { Pin::new_unchecked(painter) }; + + // Now pinned painter can be used as normal + // to render a rectangle with two colours + let size = self.as_ref().size(); + pinned_painter.as_mut().fill_rect( + &QRectF::new(0.0, 0.0, size.width() / 2.0, size.height()), + self.as_ref().color(), + ); + let darker_color = self.as_ref().color().darker(150); + pinned_painter.as_mut().fill_rect( + &QRectF::new(size.width() / 2.0, 0.0, size.width() / 2.0, size.height()), + &darker_color, + ); + } + } +} + +impl cxx_qt::Constructor<()> for qobject::CustomParentClass { + type NewArguments = (); + type BaseArguments = (*mut ffi::QQuickItem,); + type InitializeArguments = (); + + fn route_arguments( + _args: (), + ) -> ( + Self::NewArguments, + Self::BaseArguments, + Self::InitializeArguments, + ) { + ((), (core::ptr::null_mut(),), ()) + } + + fn new((): ()) -> CustomParentClass { + CustomParentClass::default() + } + + fn initialize(self: core::pin::Pin<&mut Self>, _arguments: Self::InitializeArguments) { + self.on_color_changed(|qobject| qobject.update()).release(); + } +} diff --git a/examples/qml_features/rust/src/lib.rs b/examples/qml_features/rust/src/lib.rs index ae54ad867..241f244a8 100644 --- a/examples/qml_features/rust/src/lib.rs +++ b/examples/qml_features/rust/src/lib.rs @@ -12,6 +12,7 @@ pub mod containers; pub mod custom_base_class; +pub mod custom_parent_class; pub mod invokables; pub mod multiple_qobjects; pub mod nested_qobjects;