Skip to content

Commit

Permalink
examples: add example of using a signal on an external object
Browse files Browse the repository at this point in the history
Related to #577
  • Loading branch information
ahayzen-kdab committed Nov 15, 2023
1 parent c3a0272 commit 49d870e
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 6 deletions.
12 changes: 12 additions & 0 deletions examples/qml_features/cpp/custom_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,15 @@ qvariantCanConvertCustomStruct(const QVariant& variant)
{
return variant.canConvert<CustomStruct>();
}

CustomObject::CustomObject(QObject* parent)
: QObject(parent)
, m_value(0)
{
}

CustomStruct
CustomObject::asStruct() const
{
return CustomStruct{ m_value };
}
8 changes: 2 additions & 6 deletions examples/qml_features/cpp/custom_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,9 @@ class CustomObject : public QObject

Q_PROPERTY(int value MEMBER m_value)
public:
explicit CustomObject(QObject* parent = nullptr)
: QObject(parent)
, m_value(0)
{
}
explicit CustomObject(QObject* parent = nullptr);

Q_INVOKABLE CustomStruct asStruct() const { return CustomStruct{ m_value }; }
Q_INVOKABLE CustomStruct asStruct() const;

private:
int m_value;
Expand Down
21 changes: 21 additions & 0 deletions examples/qml_features/cpp/external_qobject.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// clang-format off
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#include "external_qobject.h"

ExternalQObject::ExternalQObject(QObject* parent)
: QObject(parent)
{
}

void
ExternalQObject::trigger(::std::uint32_t amount)
{
for (::std::uint32_t i = 0; i < amount; i++) {
Q_EMIT triggered();
Q_EMIT triggeredPrivateSignal(QPrivateSignal());
}
}
25 changes: 25 additions & 0 deletions examples/qml_features/cpp/external_qobject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// clang-format off
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once

#include <QtCore/QObject>

#include <cstdint>

class ExternalQObject : public QObject
{
Q_OBJECT

public:
explicit ExternalQObject(QObject* parent = nullptr);

Q_INVOKABLE void trigger(::std::uint32_t amount);

Q_SIGNALS:
void triggered();
void triggeredPrivateSignal(QPrivateSignal);
};
3 changes: 3 additions & 0 deletions examples/qml_features/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <QtQml/QQmlApplicationEngine>

#include "custom_object.h"
#include "external_qobject.h"

int
main(int argc, char* argv[])
Expand All @@ -35,6 +36,8 @@ main(int argc, char* argv[])
// the elements generated by Rust will be available to the QML engine.
qmlRegisterType<CustomObject>(
"com.kdab.cxx_qt.demo_cpp", 1, 0, "CustomObject");
qmlRegisterType<ExternalQObject>(
"com.kdab.cxx_qt.demo_cpp", 1, 0, "ExternalQObject");

engine.load(url);

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 @@ -116,6 +116,10 @@ ApplicationWindow {
name: "Custom Parent Class"
source: "pages/CustomParentClassPage.qml"
}
ListElement {
name: "ExternCxxQt"
source: "pages/ExternCxxQtPage.qml"
}
}
}
}
Expand Down
69 changes: 69 additions & 0 deletions examples/qml_features/qml/pages/ExternCxxQtPage.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 QtQuick.Window 2.12

import com.kdab.cxx_qt.demo 1.0
import com.kdab.cxx_qt.demo_cpp 1.0

Page {
property int amount: 5

header: ToolBar {
RowLayout {
anchors.fill: parent

ToolButton {
text: qsTr("Trigger")

onClicked: rustExternCxxQt.triggerOnExternal(externalQObject, amountSpinBox.value)
}

Item {
Layout.fillWidth: true
}
}
}

ExternalQObject {
id: externalQObject
}

ExternalCxxQtHelper {
id: rustExternCxxQt
}

ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter

Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: qsTr("Connecting to signals in external QObject can run closures as normal.")
wrapMode: Text.Wrap
}

SpinBox {
id: amountSpinBox
Layout.alignment: Qt.AlignHCenter
from: 1
to: 10
value: 5
}

Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: qsTr("Count: %1").arg(rustExternCxxQt.count)
wrapMode: Text.Wrap
}
}

Component.onCompleted: rustExternCxxQt.connectToExternal(externalQObject)
}
4 changes: 4 additions & 0 deletions examples/qml_features/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn main() {
"src/containers.rs",
"src/custom_base_class.rs",
"src/custom_parent_class.rs",
"src/externcxxqt.rs",
"src/invokables.rs",
"src/multiple_qobjects.rs",
"src/nested_qobjects.rs",
Expand All @@ -31,6 +32,7 @@ fn main() {
"../qml/pages/ContainersPage.qml",
"../qml/pages/CustomBaseClassPage.qml",
"../qml/pages/CustomParentClassPage.qml",
"../qml/pages/ExternCxxQtPage.qml",
"../qml/pages/InvokablesPage.qml",
"../qml/pages/MultipleQObjectsPage.qml",
"../qml/pages/NestedQObjectsPage.qml",
Expand All @@ -48,8 +50,10 @@ fn main() {
.cc_builder(|cc| {
cc.include("../cpp");
cc.file("../cpp/custom_object.cpp");
cc.file("../cpp/external_qobject.cpp");
})
.qobject_header("../cpp/custom_object.h")
.qobject_header("../cpp/external_qobject.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")
Expand Down
106 changes: 106 additions & 0 deletions examples/qml_features/rust/src/externcxxqt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: 2022 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 an external QObject with signals can be used
/// A CXX-Qt bridge which shows how an external QObject with signals can be used
// ANCHOR: book_cxx_file_stem
#[cxx_qt::bridge(cxx_file_stem = "externcxxqt")]
pub mod ffi {
unsafe extern "C++Qt" {
include!("external_qobject.h");
/// ExternalQObject C++ class
type ExternalQObject;

/// Trigger emitting the signal "amount" times
fn trigger(self: Pin<&mut ExternalQObject>, amount: u32);

/// Signal that is emitted when trigger is fired
#[qsignal]
fn triggered(self: Pin<&mut ExternalQObject>);

/// Private signal that is emitted when trigger is fired
#[qsignal]
#[rust_name = "triggered_private_signal"]
pub(self) fn triggeredPrivateSignal(self: Pin<&mut ExternalQObject>);
}

unsafe extern "RustQt" {
#[qobject]
#[qml_element]
#[qproperty(u32, count)]
#[qproperty(u32, private_count)]
type ExternalCxxQtHelper = super::ExternalCxxQtHelperRust;

#[qinvokable]
unsafe fn connect_to_external(
self: Pin<&mut ExternalCxxQtHelper>,
external: *mut ExternalQObject,
);

#[qinvokable]
unsafe fn trigger_on_external(
self: Pin<&mut ExternalCxxQtHelper>,
external: *mut ExternalQObject,
amount: u32,
);
}

impl cxx_qt::Threading for ExternalCxxQtHelper {}
}

use core::pin::Pin;
use cxx_qt::Threading;

/// Test struct
#[derive(Default)]
pub struct ExternalCxxQtHelperRust {
count: u32,
private_count: u32,
}

impl ffi::ExternalCxxQtHelper {
unsafe fn connect_to_external(self: Pin<&mut Self>, external: *mut ffi::ExternalQObject) {
if let Some(external) = external.as_mut() {
let qt_thread = self.qt_thread();
let mut pinned_external = Pin::new_unchecked(external);
pinned_external
.as_mut()
.on_triggered(move |_| {
qt_thread
.queue(|mut qobject| {
let new_count = qobject.as_ref().count() + 1;
qobject.as_mut().set_count(new_count);
})
.unwrap();
})
.release();

let qt_thread = self.qt_thread();
pinned_external
.as_mut()
.on_triggered_private_signal(move |_| {
qt_thread
.queue(|mut qobject| {
let new_private_count = qobject.as_ref().private_count() + 1;
qobject.as_mut().set_private_count(new_private_count);
})
.unwrap();
})
.release();
}
}

unsafe fn trigger_on_external(
self: Pin<&mut Self>,
external: *mut ffi::ExternalQObject,
amount: u32,
) {
if let Some(external) = external.as_mut() {
let pinned_external = Pin::new_unchecked(external);
pinned_external.trigger(amount);
}
}
}
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 @@ -13,6 +13,7 @@
pub mod containers;
pub mod custom_base_class;
pub mod custom_parent_class;
pub mod externcxxqt;
pub mod invokables;
pub mod multiple_qobjects;
pub mod nested_qobjects;
Expand Down
3 changes: 3 additions & 0 deletions examples/qml_features/tests/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <QtQuickTest/quicktest.h>

#include "custom_object.h"
#include "external_qobject.h"

class Setup : public QObject
{
Expand All @@ -23,6 +24,8 @@ class Setup : public QObject
// of the elements generated by Rust will be available to the QML engine.
qmlRegisterType<CustomObject>(
"com.kdab.cxx_qt.demo_cpp", 1, 0, "CustomObject");
qmlRegisterType<ExternalQObject>(
"com.kdab.cxx_qt.demo_cpp", 1, 0, "ExternalQObject");
}
};

Expand Down
Loading

0 comments on commit 49d870e

Please sign in to comment.