Skip to content

Commit

Permalink
cxx-qt-gen: use #[qobject] #[A] instead of #[cxx_qt::qobject(A)]
Browse files Browse the repository at this point in the history
Related to KDAB#555
  • Loading branch information
ahayzen-kdab committed Jul 27, 2023
1 parent d22007d commit caa98a6
Show file tree
Hide file tree
Showing 44 changed files with 214 additions and 207 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
6 changes: 3 additions & 3 deletions crates/cxx-qt-gen/src/generator/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ mod tests {
#[cxx_qt::bridge]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand All @@ -73,7 +73,7 @@ mod tests {
#[cxx_qt::bridge(cxx_file_stem = "my_object")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand All @@ -92,7 +92,7 @@ mod tests {
#[cxx_qt::bridge(namespace = "cxx_qt")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand Down
16 changes: 11 additions & 5 deletions crates/cxx-qt-gen/src/generator/cpp/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ mod tests {
#[cxx_qt::bridge]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand All @@ -191,7 +191,8 @@ mod tests {
#[cxx_qt::bridge(namespace = "cxx_qt")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject(base = "QStringListModel")]
#[qobject]
#[base = "QStringListModel"]
type MyObject = super::MyObjectRust;
}
}
Expand All @@ -214,7 +215,8 @@ mod tests {
#[cxx_qt::bridge(namespace = "cxx_qt")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject(qml_element = "MyQmlElement")]
#[qobject]
#[qml_element = "MyQmlElement"]
type MyNamedObject = super::MyNamedObjectRust;
}
}
Expand All @@ -240,7 +242,9 @@ mod tests {
#[cxx_qt::bridge(namespace = "cxx_qt")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject(qml_element, qml_singleton)]
#[qobject]
#[qml_element]
#[qml_singleton]
type MyObject = super::MyObjectRust;
}
}
Expand All @@ -267,7 +271,9 @@ mod tests {
#[cxx_qt::bridge(namespace = "cxx_qt")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject(qml_element, qml_uncreatable)]
#[qobject]
#[qml_element]
#[qml_uncreatable]
type MyObject = super::MyObjectRust;
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/cxx-qt-gen/src/generator/rust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ mod tests {
#[cxx_qt::bridge]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand Down Expand Up @@ -109,7 +109,7 @@ mod tests {
#[cxx_qt::bridge(namespace = "cxx_qt")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand All @@ -130,7 +130,7 @@ mod tests {
#[cxx_qt::bridge(cxx_file_stem = "my_object")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/cxx-qt-gen/src/generator/rust/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,9 @@ mod tests {
#[cxx_qt::bridge(namespace = "cxx_qt")]
mod ffi {
extern "RustQt" {
#[cxx_qt::qobject(qml_element, qml_singleton)]
#[qobject]
#[qml_element]
#[qml_singleton]
type MyObject = super::MyObjectRust;
}
}
Expand Down
22 changes: 13 additions & 9 deletions crates/cxx-qt-gen/src/parser/cxxqtdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,18 @@ impl ParsedCxxQtData {
{
for foreign_item in &foreign_mod.items {
if let ForeignItem::Verbatim(tokens) = foreign_item {
let foreign_alias: ForeignTypeIdentAlias = syn::parse2(tokens.clone())?;
let mut foreign_alias: ForeignTypeIdentAlias =
syn::parse2(tokens.clone())?;

// TODO: in the future qobject macro will be removed and all types in RustQt will be QObjects
// Check this type is tagged with a #[qobject]
if let Some(index) =
attribute_find_path(&foreign_alias.attrs, &["cxx_qt", "qobject"])
attribute_find_path(&foreign_alias.attrs, &["qobject"])
{
foreign_alias.attrs.remove(index);

// Load the QObject
let mut qobject =
ParsedQObject::from_foreign_item_type(&foreign_alias, index)?;
ParsedQObject::from_foreign_item_type(&foreign_alias)?;

// Inject the bridge namespace if the qobject one is empty
if qobject.namespace.is_empty() && !self.namespace.is_empty() {
Expand Down Expand Up @@ -346,7 +349,7 @@ mod tests {
extern "RustQt" {
type Other = super::OtherRust;

#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
}
}
Expand All @@ -366,9 +369,9 @@ mod tests {
extern "RustQt" {
type Other = super::OtherRust;

#[cxx_qt::qobject]
#[qobject]
type MyObject = super::MyObjectRust;
#[cxx_qt::qobject]
#[qobject]
type SecondObject = super::SecondObjectRust;
}
}
Expand All @@ -391,9 +394,10 @@ mod tests {
mod module {
extern "RustQt" {
type Other = super::OtherRust;
#[cxx_qt::qobject(namespace = "qobject_namespace")]
#[qobject]
#[namespace = "qobject_namespace"]
type MyObject = super::MyObjectRust;
#[cxx_qt::qobject]
#[qobject]
type SecondObject = super::SecondObjectRust;
}
}
Expand Down
Loading

0 comments on commit caa98a6

Please sign in to comment.