Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add QQuickPaintedItem example using Constructor trait #513

Merged
merged 2 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions examples/demo_threading/qml/MainWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
28 changes: 23 additions & 5 deletions examples/demo_threading/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions examples/qml_features/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -48,6 +48,7 @@ target_link_libraries(${APP_NAME}_lib INTERFACE
Qt::Core
Qt::Gui
Qt::Qml
Qt::Quick
Qt::QuickControls2
)

Expand Down
4 changes: 4 additions & 0 deletions examples/qml_features/qml/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ ApplicationWindow {
name: "Singleton"
source: "qrc:/pages/SingletonPage.qml"
}
ListElement {
name: "Custom Parent Class"
source: "qrc:/pages/CustomParentClassPage.qml"
}
}
}
}
Expand Down
61 changes: 61 additions & 0 deletions examples/qml_features/qml/pages/CustomParentClassPage.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// 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
}
}
}
1 change: 1 addition & 0 deletions examples/qml_features/qml/qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0
<file>main.qml</file>
<file>pages/ContainersPage.qml</file>
<file>pages/CustomBaseClassPage.qml</file>
<file>pages/CustomParentClassPage.qml</file>
<file>pages/InvokablesPage.qml</file>
<file>pages/MultipleQObjectsPage.qml</file>
<file>pages/NestedQObjectsPage.qml</file>
Expand Down
4 changes: 4 additions & 0 deletions examples/qml_features/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
126 changes: 126 additions & 0 deletions examples/qml_features/rust/src/custom_parent_class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// 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!(<QtGui/QPainter>);

/// 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!(<QtQuick/QQuickItem>);

include!(<QtQuick/QQuickPaintedItem>);
}

/// 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();
}
}
1 change: 1 addition & 0 deletions examples/qml_features/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down