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

WIP: cxx-qt-build: use filename for rust name #200

Closed
Closed
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
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ impl From<Data> for DataSerde {
}
}

#[cxx_qt::bridge(namespace = "cxx_qt::my_object")]
mod my_object {
use super::DataSerde;
const DEFAULT_STR: &str = r#"{"number": 1, "string": "Hello World!"}"#;

const DEFAULT_STR: &str = r#"{"number": 1, "string": "Hello World!"}"#;
#[cxx_qt::bridge(namespace = "cxx_qt::my_object")]
mod ffi {
use super::{DataSerde, DEFAULT_STR};

#[namespace = ""]
unsafe extern "C++" {
Expand Down Expand Up @@ -85,10 +85,11 @@ mod my_object {
}
}

#[cxx_qt::qobject]
#[derive(Default)]
pub struct RustObj;
pub struct MyObject;

impl cxx_qt::QObject<RustObj> {
impl cxx_qt::QObject<MyObject> {
#[invokable]
pub fn increment(&self, cpp: &mut CppObj) {
cpp.set_number(cpp.number() + 1);
Expand Down
2 changes: 1 addition & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0
- [QObject](./qobject/index.md)
- [Macro](./qobject/macro.md)
- [Data Struct](./qobject/data_struct.md)
- [RustObj Struct](./qobject/rustobj_struct.md)
- [QObject marked Struct](./qobject/qobject_struct.md)
- [Cpp Object](./qobject/cpp_object.md)
- [Signals enum](./qobject/signals_enum.md)
- [Handlers](./qobject/handlers.md)
Expand Down
2 changes: 1 addition & 1 deletion book/src/concepts/bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ We provide [Qt types](./types.md) to help pass common data types across the brid

When Rust items are exposed to C++ we automatically perform a conversion between Snake case and Camel case. So that items (such as properties and invokables) appear as Camel case to C++ but Snake case to Rust.

Note that the Rust [`RustObj`](../qobject/rustobj_struct.md) of a constructed Qt object is owned by the C++ side of the bridge representing it. So when the C++ object is destroyed the Rust object will be destroyed. In the future there will be [handlers](../qobject/handlers.md) for executing Rust code from the (de)constructor of the C++ object [https://github.com/KDAB/cxx-qt/issues/13](https://github.com/KDAB/cxx-qt/issues/13).
Note that the Rust [`QObject marked struct`](../qobject/qobject_struct.md) of a constructed Qt object is owned by the C++ side of the bridge representing it. So when the C++ object is destroyed the Rust object will be destroyed. In the future there will be [handlers](../qobject/handlers.md) for executing Rust code from the (de)constructor of the C++ object [https://github.com/KDAB/cxx-qt/issues/13](https://github.com/KDAB/cxx-qt/issues/13).
6 changes: 1 addition & 5 deletions book/src/concepts/qt.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0

## Invokables

Invokables can be defined using the [RustObj Struct](../qobject/rustobj_struct.md), these will be exposed as methods on the C++ class with `Q_INVOKABLE` so that they are accessible for QML too.
Invokables can be defined using the [QObject Struct](../qobject/qobject_struct.md), these will be exposed as methods on the C++ class with `Q_INVOKABLE` so that they are accessible for QML too.

## Properties

Expand All @@ -18,7 +18,3 @@ Properties can be defined using the [Data struct](../qobject/data_struct.md), th
## Signals

Signals can be defined using the [Signals enum](../qobject/signals_enum.md), these will be exposed as `Q_SIGNALS` on the C++ class and therefore to QML as well.

## Change events

You can listen to property changes via the [handlers](../qobject/handlers.md) available in the RustObj Struct. These handlers are called from the Qt event loop thread to remain [thread safe](./threading.md).
2 changes: 1 addition & 1 deletion book/src/concepts/threading.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ We provide a solution to prevent entering deadlocks from signal connections, eg

To achieve safe multi-threading on the Rust side we use an `UpdateRequester`. Where the Rust thread is started (eg an invokable) the `UpdateRequester` should be cloned into the thread.

Then when the background thread needs to update a value in the Qt object it requests an update, this is posted into the same queue as above. Once the event loop occurs this calls `UpdateRequestHandler` in the [RustObj Handlers](../qobject/handlers.md) so that you can safely call setters or emit signals from the Qt thread and synchronise your state to the foreground.
Then when the background thread needs to update a value in the Qt object it requests an update, this is posted into the same queue as above. Once the event loop occurs this calls `UpdateRequestHandler` in the [Handlers](../qobject/handlers.md) so that you can safely call setters or emit signals from the Qt thread and synchronise your state to the foreground.

We recommend using a channel in the thread to send enums or values which are then processed in `UpdateRequestHandler`.

Expand Down
17 changes: 9 additions & 8 deletions book/src/getting-started/1-qobjects-in-rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@ Qt's design is inherently object-oriented, which is true both for C++ and QML as
Therefore, in order to integrate well with Qt, Rust needs to be able to extend the Qt object system with its own QObject subclasses and instances.
This is exactly what CXX-Qt allows you to do.

As Rust doesn't offer classes with inheritance and polymorphism, CXX-Qt uses a Rust module when defining new QObject subclasses.
As Rust doesn't offer classes with inheritance and polymorphism, CXX-Qt uses macros when defining new QObject subclasses.

These CXX-Qt modules consist of multiple parts:
- A `Data` struct
- Defines which Properties will be in the QObject subclass.
- Needs to implement the `Default` trait.
- This data will live as properties in the C++ subclass that is generated by CXX-Qt.
- A `RustObj` struct
- A struct marked with a `#[cxx_qt::qobject]` macro
- Defines the C++ QObject subclass name
- A normal Rust struct.
- One struct instance is created per class instance.
- Contains any Rust-only data.
- Needs to implement the `Default` trait.
- The `impl` of the `RustObj` struct (optional):
- The `impl` of the `#[cxx_qt::qobject]` marked struct (optional):
- Contains any Rust code.
- Functions marked with `#[invokable]` will be callable from QML and C++.
- The `Signal` enum
Expand All @@ -54,27 +55,27 @@ These CXX-Qt modules consist of multiple parts:

CXX-Qt will then expand this Rust module into two separate parts:
- A C++ subclass of QObject with the same name as the module
- The Rust struct `RustObj`
- The `#[cxx_qt::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 `RustObj` struct using the [CXX library](https://cxx.rs/).
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/).
For more details, see the [Concepts: Bridge](../concepts/bridge.md) page.
Additionally, CXX-Qt wraps some Qt types for us, so they can be used easily by the Rust side.
See the [Concepts: Qt Types](../concepts/types.md) page for the list of available types.

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 exists purely on the C++ side, as well as an instance of the `RustObj` struct.
These classes are made up of the actual QObject subclass instance that exists purely on the C++ side, as well as an instance of the `#[cxx_qt::qobject]` marked struct.
The lifetime and GUI data is therefore managed by the QObject instance on the C++ side.
Typically this will be instantiated by QML and the lifetime will be directly associated with the corresponding QML widget.
Any properties declared in the `Data` struct will be stored as a member of the C++ QObject.

However, the generated QObject subclass will defer to the `RustObj` struct for any behavior, which is then defined in Rust.
The `RustObj` struct can expose additional functionality with functions marked as `#[invokable]`, which will generate a function on the C++ side that will directly call the appropriate Rust method.
However, the generated QObject subclass will defer to the `#[cxx_qt::qobject]` marked struct for any behavior, which is then defined in Rust.
The `#[cxx_qt::qobject]` marked struct can expose additional functionality with functions marked as `#[invokable]`, which will generate a function on the C++ side that will directly call the appropriate Rust method.
These Rust methods can take a reference to the members of the C++ object via a wrapper called `CppObj`, so the Rust code can modify them.

Now that we have taken a look the theory of it all, lets jump in and write [our first CXX-Qt module](./2-our-first-cxx-qt-module.md).
Expand Down
19 changes: 9 additions & 10 deletions book/src/getting-started/2-our-first-cxx-qt-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,15 @@ Starting with the module definition:
{{#include ../../../examples/qml_minimal/src/lib.rs:book_bridge_macro}}
```

Because we add the `#[cxx_qt::bridge(namespace = "cxx_qt::my_object")]` macro to the module definition, CXX-Qt will create a new QObject subclass from this module.
The new QObject subclass in our case will be named `MyObject`, as CXX-Qt automatically converts Rusts snake_case to the Qt default PascalCase.
CXX-Qt is all about idiomatic code in both Rust and C++, so it will do its best to keep styling consistent for C++ and Rust as well.
Because we add the `#[cxx_qt::bridge(namespace = "cxx_qt::my_object")]` macro to the module definition,
CXX-Qt will look inside the module for further macros which can define the QObject.

For the `#[cxx_qt::bridge(namespace = "cxx_qt::my_object")]` macro to work, we first need to define the data that will live in the new C++ object.
This is done with the `Data` struct:
```rust,ignore
{{#include ../../../examples/qml_minimal/src/lib.rs:book_data_struct}}
```
That means the newly created QObject subclass will have two properties as members: `number` and `string`. For names that contain multiple words, like `my_number`, CXX-Qt will again perform the snake_case to camelCase conversion to fit with C++/QML naming conventions.
That means the newly created QObject subclass will have two properties as members: `number` and `string`. For names that contain multiple words, like `my_number`, CXX-Qt will perform the snake_case to camelCase conversion to fit with C++/QML naming conventions.

Note that the data types we use here are normal Rust data types.
CXX-Qt will automatically convert these types to their C++/Qt equivalent.
Expand All @@ -56,14 +55,14 @@ Now that we've defined the data that will live on the C++ side of things, let's
```rust,ignore
{{#include ../../../examples/qml_minimal/src/lib.rs:book_rustobj_struct}}
```
In our case, this is just an empty struct.
However, the `RustObj` could contain any data we want.
The name of this struct is used as the name of the C++ QObject subclass, in our case this is just an empty struct.
However, the `#[cxx_qt::qobject]` marked struct could contain any data we want.
It is not converted into a C++ class, so it isn't limited to the Qt-compatible types that the `Data` struct is.

An important point to note here is that the `RustObj`, like the `Data` struct must implement the `Default` trait.
Every instance of the `MyObject` class will automatically create a corresponding `RustObj` instance by using the `Default` trait.
An important point to note here is that the `#[cxx_qt::qobject]` marked struct, like the `Data` struct must implement the `Default` trait.
Every instance of the `MyObject` class will automatically create a corresponding `#[cxx_qt::qobject]` marked struct instance by using the `Default` trait.

Just because the `RustObj` struct doesn't contain any data, that still doesn't mean its not an important part of our `MyObject` class.
Just because the `#[cxx_qt::qobject]` marked struct struct doesn't contain any data, that still doesn't mean its not an important part of our `MyObject` class.
That is because it actually defines the behavior of our class through its `impl`:
```rust,ignore
{{#include ../../../examples/qml_minimal/src/lib.rs:book_rustobj_impl}}
Expand All @@ -80,7 +79,7 @@ In our case, we define two new functions:

Both functions are marked with the `#[invokable]` macro, which means the functions will be added to the C++ code of `MyObject` and will be callable from QML as well.

Apart from functions marked with the `#[invokable]` macro, the `RustObj` impl is just a normal Rust struct impl and can contain normal Rust functions, which the invokable functions can call as usual.
Apart from functions marked with the `#[invokable]` macro, the `#[cxx_qt::qobject]` marked struct impl is just a normal Rust struct impl and can contain normal Rust functions, which the invokable functions can call as usual.

And that's it. We've defined our first QObject subclass in Rust. That wasn't so hard, was it?

Expand Down
2 changes: 1 addition & 1 deletion book/src/qobject/cpp_object.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ If the [`Data` struct](./data_struct.md) has a field called `number: i32`, then

If there is a [`Signals` enum](./signals_enum.md) then you can call `emit_queued(&mut self, Signals)` or `unsafe emit_immediate(&mut self, Signals)` on the `CppObj` to emit a signal.

Note that `emit_immediate` is unsafe as it can cause deadlocks if the `Q_EMIT` is `Qt::DirectConnection` connected to a Rust invokable on the same QObject that has caused the `Q_EMIT`, as this would then try to lock the `RustObj` which is already locked.
Note that `emit_immediate` is unsafe as it can cause deadlocks if the `Q_EMIT` is `Qt::DirectConnection` connected to a Rust invokable on the same QObject that has caused the `Q_EMIT`, as this would then try to lock the internal Rust object which is already locked.

```rust,ignore,noplayground
{{#include ../../../examples/qml_features/src/signals.rs:book_rust_obj_impl}}
Expand Down
4 changes: 2 additions & 2 deletions book/src/qobject/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ A QObject is constructed with the following parts

* [A macro around the module](./macro.md)
* [A Data struct defining properties](./data_struct.md)
* [A RustObj defining invokables](./rustobj_struct.md)
* [A QObject marked struct defining invokables](./qobject_struct.md)
* [Cpp Object wrapper](./cpp_object.md)
* [A Signals enum for defining signals](./signals_enum.md)
* [Handlers on RustObj for processing events on the Qt thread](./handlers.md)
* [Handlers for processing events on the Qt thread](./handlers.md)
4 changes: 2 additions & 2 deletions book/src/qobject/macro.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ The namespace specified is used in C++ to allow segmenting generated code from y

Note that currently each QObject needs to be in its own namespace otherwise there will be collisions from generated free functions.

The example below would export the contents of the module as `DataStructProperties` to Qt / QML.
The example below would export the struct marked with `#[cxx_qt::qobject]` as `DataStructProperties` to Qt / QML.

Note that the object name needs to be unique to avoid clashes, in the future full module paths may be used to aid avoiding collisions [https://github.com/KDAB/cxx-qt/issues/19](https://github.com/KDAB/cxx-qt/issues/19) - but this doesn't prevent attempting to register two QML types with the same name.

```rust,ignore,noplayground
{{#include ../../../examples/qml_features/src/data_struct_properties.rs:book_macro_code}}
```

Note: this might change in the future to allow for defining the base class or options when exporting to QML and could become namespaced to `#[cxx_qt(QObject)]` ( [https://github.com/KDAB/cxx-qt/issues/22](https://github.com/KDAB/cxx-qt/issues/22) ).
Note: this might change in the future to allow for defining the base class or options when exporting to QML and could become namespaced to `#[cxx_qt::qobject(base = "QAbstractListModel")]` ( [https://github.com/KDAB/cxx-qt/issues/22](https://github.com/KDAB/cxx-qt/issues/22) ).
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ SPDX-FileContributor: Andrew Hayzen <[email protected]>
SPDX-License-Identifier: MIT OR Apache-2.0
-->

# RustObj Struct
# QObject Marked Struct

The RustObj struct allows you to define the following items
The `#[cxx_qt::qobject]` marked struct allows you to define the following items

* The name of the C++ class for the QObject
* Invokable methods that are exposed to Qt
* Private methods and fields for RustObj to use (eg this is useful for storing the channels for [threading](../concepts/threading.md))
* Private methods and fields for struct to use (eg this is useful for storing the channels for [threading](../concepts/threading.md))
* Mutate C++ state with [`CppObj`](./cpp_object.md)
* Implement [handlers](./handlers.md) for property or update requests

Expand All @@ -20,7 +21,7 @@ The RustObj struct allows you to define the following items

## Invokables

A `impl cxx_qt::QObject<RustObj>` is used to define invokables, the `impl cxx_qt::QObject<RustObj>` defines that the methods are implemented onto the C++ QObject.
A `impl cxx_qt::QObject<T>` is used to define invokables, the `impl cxx_qt::QObject<T>` defines that the methods are implemented onto the C++ QObject `T`.
Therefore they have access to both C++ and Rust methods. Also CXX-Qt adds wrapper code around your invokables to automatically perform any conversion between the [C++ and Rust types](../concepts/types.md).

To mark a method as invokable simply add the `#[invokable]` attribute to the Rust method. This then causes `Q_INVOKABLE` to be set on the C++ definition of the method, allowing QML to call the invokable.
Expand All @@ -29,8 +30,8 @@ Note to access properties on the C++ object use [Cpp Object](./cpp_object.md).

## Private Methods and Fields

Unlike the [Data Struct](./data_struct.md) fields which are defined on the `RustObj` struct 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.
Unlike the [Data Struct](./data_struct.md) fields which are defined on the `#[cxx_qt::qobject]` marked struct 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.

Methods implemented using `impl RustObj` (and not `impl cxx_qt::QObject<RustObj>`) are just normal Rust member methods.
Methods implemented using `impl T` (and not `impl cxx_qt::QObject<T>`) are just normal Rust member methods.
Therefore they do not have access to any C++ or QObject functionality (e.g. emitting Signals, changing properties, etc.)
You will usually only need to use `impl RustObj` if you want to also use your RustObj struct as a normal Rust struct, that is not wrapped in a QObject.
You will usually only need to use `impl T` if you want to also use your struct as a normal Rust struct, that is not wrapped in a QObject.
2 changes: 1 addition & 1 deletion book/src/qobject/signals_enum.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The `cxx_qt::signals(T)` attribute is used on an enum to define which signals sh

To emit a signal from Rust use the [`CppObj`](./cpp_object.md) and call either the `emit_queued(Signal)` or `unsafe emit_immediate(Signal)` method.

Note that `emit_immediate` is unsafe as it can cause deadlocks if the `Q_EMIT` is `Qt::DirectConnection` connected to a Rust invokable on the same QObject that has caused the `Q_EMIT`, as this would then try to lock the `RustObj` which is already locked.
Note that `emit_immediate` is unsafe as it can cause deadlocks if the `Q_EMIT` is `Qt::DirectConnection` connected to a Rust invokable on the same QObject that has caused the `Q_EMIT`, as this would then try to lock the internal Rust object which is already locked.

```rust,ignore,noplayground
{{#include ../../../examples/qml_features/src/signals.rs:book_rust_obj_impl}}
Expand Down
Loading