Skip to content

Commit

Permalink
Merge branch 'main' into fix/macos_qt_libs_as_framework
Browse files Browse the repository at this point in the history
  • Loading branch information
jacquetc committed Aug 1, 2023
2 parents 2f04f5e + 48bd4c2 commit e61d693
Show file tree
Hide file tree
Showing 86 changed files with 1,297 additions and 682 deletions.
2 changes: 1 addition & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0
- [Building with Cargo](./getting-started/5-cargo-executable.md)
- [QObject](./qobject/index.md)
- [`#[cxx_qt::bridge]` - Bridge Macro](./qobject/bridge-macro.md)
- [`#[cxx_qt::qobject]` - Defining QObjects](./qobject/qobject_struct.md)
- [`#[qobject]` - Defining QObjects](./qobject/qobject_struct.md)
- [`#[qsignal]` - Signal macro](./qobject/signals.md)
- [`qobject::T` - The generated QObject](./qobject/generated-qobject.md)
- [CxxQtThread](./qobject/cxxqtthread.md)
Expand Down
2 changes: 1 addition & 1 deletion book/src/concepts/inheritance.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ For this, the `clear` method implemented in Rust needs to call `beginResetModel`
See [the Qt docs](https://doc.qt.io/qt-6/qabstractlistmodel.html) for more details on the specific subclassing requirements.

Methods in a `extern "RustQt"` block similar to CXX can be tagged with an `#[inherit]` attribute, with the same restrictions regarding which types can be used.
Additionally, the `self` type must be either `self: Pin<&mut qobject::T>` or `self: &qobject::T`, where `qobject::T` must refer to a QObject marked with `#[cxx_qt::qobject]` in the `#[cxx_qt::bridge]`
Additionally, the `self` type must be either `self: Pin<&mut qobject::T>` or `self: &qobject::T`, where `qobject::T` must refer to a QObject marked with `#[qobject]` in the `#[cxx_qt::bridge]`

The declared methods will be case-converted as in other CXX-Qt APIs.
To explicitly declare the C++ method name, use the `#[cxx_name="myFunctionName"]` attribute.
Expand Down
10 changes: 5 additions & 5 deletions book/src/getting-started/1-qobjects-in-rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,26 @@ These concepts include:

As with CXX, to use these features you mark a Rust module with an attribute macro (`#[cxx_qt::bridge]`).
Then you can use the afformentioned features with the help of more macros.
- `#[cxx_qt::qobject]` - Expose a Rust struct to Qt as a QObject subclass.
- `#[qobject]` - Expose a Rust struct to Qt as a QObject subclass.
- `#[qproperty]` - Expose a field of the Rust struct to QML/C++ as a [`Q_PROPERTY`](https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html#exposing-properties).
- `#[qinvokable]` - Expose a function on the QObject to QML and C++ as a [`Q_INVOKABLE`](https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html#exposing-methods-including-qt-slots).
- `#[qsignal]` - Define the [Signals](https://doc.qt.io/qt-6/signalsandslots.html#signals) of a QObject T.

CXX-Qt will then expand this Rust module into two separate parts:
- C++ files that define a QObject subclass for each `#[cxx_qt::qobject]` marked struct.
- The Rust code for the `#[cxx_qt::qobject]` marked Rust struct
- C++ files that define a QObject subclass for each `#[qobject]` marked struct.
- The Rust code for the `#[qobject]` marked Rust struct

<div style="background-color: white; padding: 1rem; text-align: center;">

![Overview of CXX-Qt module generation](../images/overview_abstract.svg)

</div>

CXX-Qt also generates the code needed for interaction of the C++ QObject subclass and the `#[cxx_qt::qobject]` marked struct using the [CXX library](https://cxx.rs/).
CXX-Qt also generates the code needed for interaction of the C++ QObject subclass and the `#[qobject]` marked struct using the [CXX library](https://cxx.rs/).
For more details, see the [Concepts: Bridge](../concepts/bridge.md) page.

The important take away here is the duality of any subclass generated by CXX-Qt.
These classes are made up of the actual QObject subclass instance that C++ interacts with, as well as an instance of the `#[cxx_qt::qobject]` marked struct on the Rust side.
These classes are made up of the actual QObject subclass instance that C++ interacts with, as well as an instance of the `#[qobject]` marked struct on the Rust side.
When such a QObject is instantiated, it will always also construct an instance of the Rust struct as well.
The lifetime of the Rust struct will be bound to that of the QObject.
If the QObject is deleted, the Rust struct will be deleted as well.
Expand Down
6 changes: 3 additions & 3 deletions book/src/getting-started/2-our-first-cxx-qt-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ Additionally, a `#[cxx_qt::bridge]` gives you a few more features that allow you

## QObject struct

To create a new QObject subclass, we can define a struct within our module and mark it with `#[cxx_qt::qobject]`.
To create a new QObject subclass, we can define a struct within our module and mark it with `#[qobject]`.

```rust,ignore
{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct}}
```

Optionally, add `qml_uri` and `qml_version` inside `#[cxx_qt::qobject]` to tell the Rust build script to generate a QML plugin
Optionally, add `qml_uri` and `qml_version` inside `#[qobject]` to tell the Rust build script to generate a QML plugin
that will register the QObject with QML engine at startup. If you want the name of the QML type and the Rust type to be different,
you can also add `qml_name = "OtherName"`. This takes the place of the
[qt_add_qml_module CMake function](https://doc.qt.io/qt-6/qt-add-qml-module.html) (because that doesn't work with CXX-Qt's build system).
Expand Down Expand Up @@ -92,7 +92,7 @@ For more details on the available types, see the [Qt types page](../concepts/typ
## qobject::T

CXX-Qt will then automatically generate a new QObject subclass for our `MyObject` struct and expose it as an [`extern "C++"` opaque type](https://cxx.rs/extern-c++.html#opaque-c-types) to Rust.
For any Rust struct `T` that is marked with `#[cxx_qt::qobject]`, CXX-Qt will expose the corresponding C++ QObject under `qobject::T`.
For any Rust struct `T` that is marked with `#[qobject]`, CXX-Qt will expose the corresponding C++ QObject under `qobject::T`.
In our case, this means we can refer to the C++ QObject for our `MyObject` struct, as `qobject::MyObject`.

This type can be used like any other CXX opaque type.
Expand Down
8 changes: 4 additions & 4 deletions book/src/qobject/generated-qobject.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ SPDX-License-Identifier: MIT OR Apache-2.0
# `qobject::T` - The generated QObject

One of the key features of CXX-Qt is the ability to create your own QObjects from Rust.
This is what the [`#[cxx_qt::qobject]` macro](./qobject_struct.md) is for.
This is what the [`#[qobject]` macro](./qobject_struct.md) is for.
This page serves to document the details of what is generated and how to interact with the generated QObject from Rust.

The `#[cxx_qt::qobject]` macro generates a QObject for a given Rust struct.
The `#[qobject]` macro generates a QObject for a given Rust struct.
Whilst this QObject is a C++ type, CXX-Qt will automatically wrap it as a [CXX Opaque Type](https://cxx.rs/extern-c++.html#opaque-c-types).
These generated QObjects are accessible to Rust in a generated module with the name `qobject`. Each struct `T`'s generated QObject is accessible as `qobject::T`.

Expand All @@ -37,7 +37,7 @@ Example:
// In file qt_types.rs
#[cxx_qt::bridge]
mod ffi {
#[cxx_qt::qobject]
#[qobject]
#[derive(Default)]
pub struct MyObject {}
}
Expand Down Expand Up @@ -89,7 +89,7 @@ There is also an advanced way to access the data in the internal Rust struct:
fn rust(&self) -> &T
fn rust_mut(self: Pin<&mut Self>) -> &mut T
```
Where `T` is the struct with the `#[cxx_qt::qobject]` macro.
Where `T` is the struct with the `#[qobject]` macro.

This allows you to directly manipulate the internal Rust struct without having to use the generated accessor methods.

Expand Down
2 changes: 1 addition & 1 deletion book/src/qobject/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ For a simpler introduction, take a look at our [Getting Started guide](../gettin

QObject Features and Parts:
* [`#[cxx_qt::bridge]` - The macro around the module](./bridge-macro.md)
* [`#[cxx_qt::qobject]` - Marking a Rust struct as a QObject](./qobject_struct.md)
* [`#[qobject]` - Marking a Rust struct as a QObject](./qobject_struct.md)
* [`#[qsignal]` - A macro for defining signals](./signals.md)
* [`qobject:T` - The generated QObject](./generated-qobject.md)
* [`CxxQtThread` - Queueing closures onto the Qt event loop](./cxxqtthread.md)
Expand Down
18 changes: 9 additions & 9 deletions book/src/qobject/qobject_struct.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ SPDX-FileContributor: Andrew Hayzen <[email protected]>
SPDX-License-Identifier: MIT OR Apache-2.0
-->

# `#[cxx_qt::qobject]` Macro - Defining QObjects in Rust
# `#[qobject]` Macro - Defining QObjects in Rust

Defining QObjects is at the heart of CXX-Qt.
Therefore `#[cxx_qt::qobject]` can be considered the most important macro in CXX-Qt.
Therefore `#[qobject]` can be considered the most important macro in CXX-Qt.

## Requirements
- Like most other CXX-Qt macros, it can only be used from within a [`#[cxx_qt::bridge]`](./bridge-macro.md).
- The `#[cxx_qt::qobject]` macro must be placed on a Rust struct.
- The `#[qobject]` macro must be placed on a Rust struct.
- The struct must [`impl Default`](#default), so that it can be constructed as part of a QObject.

## Effects
Expand All @@ -30,10 +30,10 @@ The macro does multiple other things for you though:
- Generate signals if paired with a [`#[qsignal]` macro](./signals.md).

## Exposing to QML
`#[cxx_qt::qobject]` supports registering the Qt object as a QML type directly at build time.
`#[qobject]` supports registering the Qt object as a QML type directly at build time.
This is comparable to [adding `QML_ELEMENT` in C++](https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html).

For this, add the `qml_uri` and `qml_version` attributes to the `#[cxx_qt::qobject]` macro.
For this, add the `qml_uri` and `qml_version` attributes to the `#[qobject]` macro.
``` rust,ignore,noplayground
{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct}}
```
Expand Down Expand Up @@ -65,7 +65,7 @@ For more information on inheritance and how to override methods see the [Inherit

## Properties

Fields within the `#[cxx_qt::qobject]` marked struct can be tagged with `#[qproperty]` to be exposed as [`Q_PROPERTY`s](https://doc.qt.io/qt-6/properties.html) on the generated QObject:
Fields within the `#[qobject]` marked struct can be tagged with `#[qproperty]` to be exposed as [`Q_PROPERTY`s](https://doc.qt.io/qt-6/properties.html) on the generated QObject:

```rust,ignore,noplayground
{{#include ../../../examples/qml_features/rust/src/properties.rs:book_properties_struct}}
Expand Down Expand Up @@ -95,7 +95,7 @@ See the [Private fields section](#private-methods-and-fields)

## Default

The [`Default` trait](https://doc.rust-lang.org/std/default/trait.Default.html) needs to be implemented for the `#[cxx_qt::qobject]` marked struct either by hand or by using the derive macro `#[derive(Default)]`.
The [`Default` trait](https://doc.rust-lang.org/std/default/trait.Default.html) needs to be implemented for the `#[qobject]` marked struct either by hand or by using the derive macro `#[derive(Default)]`.

This needs to provide default values for every [`#[qproperty]`](#properties) and [private field](#private-methods-and-fields)

Expand All @@ -114,7 +114,7 @@ CXX-Qt allows you to define invokables using Rust code.
This way you can easily add a Rust-powered backend to your QML frontend.

Invokables, by definition, must be defined on a C++ class however.
This is where the QObject subclass generated by `#[cxx_qt::qobject]` comes into play.
This is where the QObject subclass generated by `#[qobject]` comes into play.
For details on this, see the [`qobject::T` page](./generated-qobject.md).

The important part for invokables is that they need to be implemented on the `qobject::T`, not `T`.
Expand Down Expand Up @@ -144,7 +144,7 @@ These are normal Rust methods, so they aren't restricted to CXX-compatible types

## Private Methods and Fields

Fields within your `#[cxx_qt::qobject]` struct that aren't tagged as `#[qproperty]` are not exposed as properties to Qt. These can be considered as "private to Rust" fields, and are useful for storing channels for threading or internal information for the QObject.
Fields within your `#[qobject]` struct that aren't tagged as `#[qproperty]` are not exposed as properties to Qt. These can be considered as "private to Rust" fields, and are useful for storing channels for threading or internal information for the QObject.
Because they aren't available from C++, they also don't have any special type requirements and can be any Rust type.
Use the `rust` and `rust_mut` methods to access the struct and therefore the fields.

Expand Down
8 changes: 7 additions & 1 deletion crates/cxx-qt-gen/include/cxxqt_thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ cxxQtThreadQueue(const CxxQtThread<T>& cxxQtThread,
} // namespace cxxqtlib1
} // namespace rust

// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust {

template<typename T>
struct rust::IsRelocatable<::rust::cxxqtlib1::CxxQtThread<T>> : ::std::true_type
struct IsRelocatable<::rust::cxxqtlib1::CxxQtThread<T>> : ::std::true_type
{
};

} // namespace rust
4 changes: 3 additions & 1 deletion crates/cxx-qt-gen/src/generator/cpp/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ mod tests {
namespace_internals: "rust".to_string(),
base_class: "BaseClass".to_string(),
blocks: GeneratedCppQObjectBlocks::default(),
locking: true,
}
}

Expand All @@ -153,6 +152,8 @@ mod tests {
base_arguments: vec![],
new_arguments: vec![],
initialize_arguments: vec![],
lifetime: None,
// dummy impl
imp: parse_quote! { impl X {} },
}
}
Expand Down Expand Up @@ -275,6 +276,7 @@ mod tests {
new_arguments: vec![parse_quote! { i16}, parse_quote! { i32 }],
initialize_arguments: vec![parse_quote! { i32 }, parse_quote! { i64 }],
base_arguments: vec![parse_quote! { i64 }, parse_quote! { *mut QObject }],
lifetime: Some(parse_quote! { 'a_lifetime }),
..mock_constructor()
}],
&["initializer".to_string()],
Expand Down
109 changes: 109 additions & 0 deletions crates/cxx-qt-gen/src/generator/cpp/cxxqttype.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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

use crate::generator::{
cpp::{fragment::CppFragment, qobject::GeneratedCppQObjectBlocks},
naming::qobject::QObjectName,
};
use indoc::formatdoc;
use syn::Result;

pub fn generate(qobject_idents: &QObjectName) -> Result<GeneratedCppQObjectBlocks> {
let mut result = GeneratedCppQObjectBlocks::default();

let rust_ident = qobject_idents.rust_struct.cpp.to_string();
let qobject_ident = qobject_idents.cpp_class.cpp.to_string();

result.includes.insert("#include <memory>".to_owned());

result.methods.push(CppFragment::Pair {
header: formatdoc! {
r#"
{rust_ident} const& unsafeRust() const;
{rust_ident}& unsafeRustMut();
"#
},
source: formatdoc! {
r#"
{rust_ident} const&
{qobject_ident}::unsafeRust() const
{{
return *m_rustObj;
}}
{rust_ident}&
{qobject_ident}::unsafeRustMut()
{{
return *m_rustObj;
}}
"#
},
});

result
.members
.push(format!("::rust::Box<{rust_ident}> m_rustObj;"));

Ok(result)
}

#[cfg(test)]
mod tests {
use super::*;

use crate::generator::naming::qobject::tests::create_qobjectname;
use indoc::indoc;
use pretty_assertions::assert_str_eq;

#[test]
fn test_generate_cpp_locking() {
let qobject_idents = create_qobjectname();

let generated = generate(&qobject_idents).unwrap();

// includes
assert_eq!(generated.includes.len(), 1);
assert!(generated.includes.contains("#include <memory>"));

// members
assert_eq!(generated.members.len(), 1);
assert_str_eq!(
&generated.members[0],
"::rust::Box<MyObjectRust> m_rustObj;"
);

// methods
assert_eq!(generated.methods.len(), 1);

let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[0] {
(header, source)
} else {
panic!("Expected pair")
};
assert_str_eq!(
header,
indoc! {r#"
MyObjectRust const& unsafeRust() const;
MyObjectRust& unsafeRustMut();
"#}
);
assert_str_eq!(
source,
indoc! {r#"
MyObjectRust const&
MyObject::unsafeRust() const
{
return *m_rustObj;
}
MyObjectRust&
MyObject::unsafeRustMut()
{
return *m_rustObj;
}
"#}
);
}
}
Loading

0 comments on commit e61d693

Please sign in to comment.