Skip to content

Commit

Permalink
update book for build time QML type registration
Browse files Browse the repository at this point in the history
  • Loading branch information
Be-ing committed Feb 8, 2023
1 parent efe0aed commit 8e72555
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 116 deletions.
7 changes: 3 additions & 4 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ SPDX-License-Identifier: MIT OR Apache-2.0
- [Getting Started](./getting-started/index.md)
- [QObjects in Rust](./getting-started/1-qobjects-in-rust.md)
- [Our first CXX-Qt module](./getting-started/2-our-first-cxx-qt-module.md)
- [Exposing to QML](./getting-started/3-exposing-to-qml.md)
- [Creating the QML GUI](./getting-started/4-qml-gui.md)
- [Building with CMake](./getting-started/5-cmake-integration.md)
- [Building with Cargo](./getting-started/6-cargo-executable.md)
- [Creating the QML GUI](./getting-started/3-qml-gui.md)
- [Building with CMake](./getting-started/4-cmake-integration.md)
- [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)
Expand Down
6 changes: 3 additions & 3 deletions book/src/concepts/build_systems.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0

CXX-Qt can be integrated into existing CMake projects or built with only cargo. The getting started guide provides documentation on how to setup your project:

* [CMake Integration](../getting-started/5-cmake-integration.md)
* [Cargo Integration](../getting-started/6-cargo-executable.md)
* [CMake Integration](../getting-started/4-cmake-integration.md)
* [Cargo Integration](../getting-started/5-cargo-executable.md)

CXX-Qt could work with any C++ build system so long as the `QMAKE` and `CXXQT_EXPORT_DIR` environment variables are set before calling Cargo,
as documented in [CMake integration](../getting-started/5-cmake-integration.md). However, using C++ build systems besides CMake with CXX-Qt is untested.
as documented in [CMake integration](../getting-started/4-cmake-integration.md). However, using C++ build systems besides CMake with CXX-Qt is untested.
38 changes: 0 additions & 38 deletions book/src/concepts/register_types.md

This file was deleted.

24 changes: 21 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 @@ -21,7 +21,7 @@ As with all things Rust, we'll want to create a cargo project, run the following
cargo init --lib rust
```
Note the `--lib` option here. For this example, we will create a static library in Rust and use CMake to
link this into a C++ executable. We'll discuss details of this later, when we [integrate our Rust project with CMake](./5-cmake-integration.md).
link this into a C++ executable. We'll discuss details of this later, when we [integrate our Rust project with CMake](./4-cmake-integration.md).

As outlined in the previous section, to define a new QObject subclass, we'll create a Rust module within this library crate.
First, in the `rust/src/lib.rs`, we tell Cargo about the module we're about to create:
Expand All @@ -38,6 +38,8 @@ It will include our `#[cxx_qt::bridge]` that allows us to create our own qobject
```

This is a lot to take in, so let's go one step at a time.

## CXX-Qt bridge module
Starting with the module definition:
```rust,ignore
{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_bridge_macro}}
Expand All @@ -48,16 +50,30 @@ Starting with the module definition:
A `#[cxx_qt::bridge]` is the same as a `#[cxx::bridge]` and you can use all features of CXX in it.
Additionally, a `#[cxx_qt::bridge]` gives you a few more features that allow you to create QObjects.

## QObject struct

To create a new QObject subclass, we can define a struct within our module and mark it with `#[cxx_qt::qobject]`.
Additionally, we need to either `impl Default` or `#[derive(Default)]` for our struct.

```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
that will register the QObject with QML engine at startup. 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).

Additionally, we need to either `impl Default` or `#[derive(Default)]` for our struct.
```rust,ignore
{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_default}}
```

The Rust struct can be defined just like a normal Rust struct and can contain any kind of field, even Rust-only types.
If a field is marked as `#[qproperty]` it will be exposed to the C++ side as a `Q_PROPERTY`.

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 automatically rename the field from snake_case to camelCase to fit with C++/QML naming conventions (e.g. `myNumber`).

### Types

Do note though that any fields marked as `#[qproperty]` must be types that CXX can translate to C++ types.
In our case that means:
- `number: i32` -> `::std::int32_t number`
Expand All @@ -72,6 +88,8 @@ We can just import them like any other CXX type:
```
For more details on the available types, see the [Qt types page](../concepts/types.md).

## 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`.
In our case, this means we can refer to the C++ QObject for our `MyObject` struct, as `qobject::MyObject`.
Expand Down Expand Up @@ -101,4 +119,4 @@ In this case, the types of the arguments also need to convertable to C++, like w

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

Now let's get to [using it in Qt](./3-exposing-to-qml.md).
Now let's get to [using it in Qt](./3-qml-gui.md).
51 changes: 0 additions & 51 deletions book/src/getting-started/3-exposing-to-qml.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ So let's add a `main.qml` file in a `qml` folder:
{{#include ../../../examples/qml_minimal/qml/main.qml:book_main_qml}}
```

If you're not familiar with QML, I recommend you take a look at the [Qt QML intro](https://doc.qt.io/qt-6/qmlapplications.html).
If you're not familiar with QML, take a look at the [Qt QML intro](https://doc.qt.io/qt-6/qmlapplications.html).

This code will create a pretty simple GUI that consists of two Labels and two Buttons.
The important part here is the use of the `MyObject` type.
Expand All @@ -33,4 +33,16 @@ It is again important to emphasize here that `MyObject` is just another QObject
The only difference being that any invokable functions that are defined are defined in Rust, instead of C++.
For QML, this doesn't make a difference though.

But enough of that, let's get this project [building and running](./5-cmake-integration.md).
# Qt resources

To include the `main.qml` file inside the application, use the [Qt resource system](https://doc.qt.io/qt-6/resources.html) by listing it in a `qml.qrc` file in the `qml` folder:
```qrc,ignore
{{#include ../../../examples/qml_minimal/qml/qml.qrc:book_rcc_block}}
```

You can omit this, but then you should change the url of the `main.qml` file, so that Qt can find it on your computer.
``` cpp, ignore
{{#include ../../../examples/qml_minimal/cpp/main.cpp:book_qml_url}}
```

Now that we have some application code, let's get this project [building and running](./4-cmake-integration.md).
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,27 @@ SPDX-License-Identifier: MIT OR Apache-2.0
In this example, we will demonstrate how to integrate cxx-qt code into a C++ application. Cargo builds the cxx-qt code
as a static library, then CMake links it into a C++ executable.

> If you don't want to use CMake, and only want to use Cargo to build your project, you can [skip to the next chapter](./6-cargo-executable.md).
> Note that using CMake is currently still easier!
> It also allows you to integrate Rust into existing Qt projects and tooling.
> If you don't want to use CMake, and only want to use Cargo to build your project, you can [skip to the next chapter](./5-cargo-executable.md).
## C++ executable

To start our QML application, we'll need a small `main.cpp` file with an ordinary `main` function. Puts this in a `cpp` folder to clearly separate the C++ and Rust code:
```cpp,ignore
{{#include ../../../examples/qml_minimal/cpp/main.cpp:book_main_cpp}}
```

You can add as much C++ code as you want in addition to this.

## Using Rust QObjects in C++

For every `#[cxx_qt::bridge]` that we define in Rust, CXX-Qt will generate a corresponding C++ header file.
They will always be in the `cxx-qt-gen/` include path and use the snake_case naming convention.
The name of the header file will be the name of the Rust module of your `#[cxx_qt::bridge]`, followed by `.cxxqt.h`.
So in our case: `#include cxx-qt-gen/my_object.cxxqt.h`

Including the generated header allows accessing the `MyObject` C++ class, just like any other C++ class.
Inhert from it, connect signals and slots to it, put it in a QVector, do whatever you want with it.
That's the power of CXX-Qt.

## Cargo setup
Before we can get started on building Qt with CMake, we first need to make our Cargo build ready for it.
Expand Down Expand Up @@ -44,10 +62,9 @@ We'll then also need to add a script named `build.rs` next to our `Cargo.toml`:
```rust,ignore
{{#include ../../../examples/qml_minimal/rust/build.rs:book_build_rs}}
```
This is what generates the C++ code for our `MyObject` class at compile-time.
It will output the `cxx-qt-gen/include/my_object.h` file we included earlier in `main.cpp`.
This is what generates and compiles the C++ code for our `MyObject` class at build time.

Note that all Rust source files that uses the `#[cxx_qt::bridge]` macro need to be included in this script!
Every Rust source files that uses the `#[cxx_qt::bridge]` macro need to be included in this script.
In our case, this is only the `src/cxxqt_object.rs` file.

## CMake setup
Expand Down Expand Up @@ -94,12 +111,12 @@ If this fails for any reason, take a look at the [`examples/qml_minimal`](https:
This should now configure and compile our project.
If this was successful, you can now run our little project.
```shell
$ build/qml_minimal
$ build/examples/qml_minimal/example_qml_minimal
```

You should now see the two Labels that display the state of our `MyObject`, as well as the two buttons to call our two Rust functions.

## Success 🥳
## Success 🥳

For further reading, you can take a look at the [QObject chapter](../qobject/index.md) which goes into detail about all features that CXX-Qt exposes to new QObject subclasses.
As well as the [Concepts chapter](../concepts/index.md), which explains the concepts underlying CXX-Qt.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The complete example code is available in [`examples/cargo_without_cmake`](https
in the cxx-qt repository.

## Cargo setup
The Cargo.toml file still requires dependencies to `cxx`, `cxx-qt`, `cxx-qt-lib` and `cxx-qt-build` as in our [CMake example](./5-cmake-integration.md). However, we are not building a `staticlib` this time:
The Cargo.toml file still requires dependencies to `cxx`, `cxx-qt`, `cxx-qt-lib` and `cxx-qt-build` as in our [CMake example](./4-cmake-integration.md). However, we are not building a `staticlib` this time:

```toml,ignore
{{#include ../../../examples/cargo_without_cmake/Cargo.toml:book_cargo_toml_no_cmake}}
Expand Down
7 changes: 3 additions & 4 deletions book/src/getting-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@ Take a look at the CXX documentation here: [https://cxx.rs/](https://cxx.rs/)

During this getting started guide we'll first take a look at how CXX-Qt integrates with Qt's object system to allow the [definition of QObjects in Rust](./1-qobjects-in-rust.md).
Then we'll dive straight into practice and define our first [QObject in Rust](./2-our-first-cxx-qt-module.md).
Once we've done that, its time to [expose the defined QObject to QML](./3-exposing-to-qml.md).
Followed by actually [defining our GUI using QML](./4-qml-gui.md).
Followed by actually [defining our GUI using QML](./3-qml-gui.md).

We will show two different ways to build the project.
First, we will show how to integrate cxx-qt into a C++ application by [building with CMake](./5-cmake-integration.md).
We will also demonstrate how to build the same cxx-qt code [as a Rust executable](./6-cargo-executable.md) without requiring a C++ build system.
First, we will show how to integrate cxx-qt into a C++ application by [building with CMake](./4-cmake-integration.md).
We will also demonstrate how to build the same cxx-qt code [as a Rust executable](./5-cargo-executable.md) without requiring a C++ build system.

**Note:** CXX-Qt is tested on CI on Linux, Windows, and macOS (all on x86). It should work on other platforms that Qt and Rust both support, however, these are not tested regularly.

Expand Down
2 changes: 1 addition & 1 deletion book/src/qobject/bridge-macro.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0
The `#[cxx_qt::bridge]` macro functions very similarly to the [`#[cxx::bridge]`](https://docs.rs/cxx/latest/cxx/attr.bridge.html). This macro needs to be written above a Rust module definition.
This Rust module will then function like a normal CXX bridge, whilst also supporting the additional features added by CXX-Qt. Refer to the [the CXX documentation](https://cxx.rs/) for details on how to describe the language boundary.
Also don't forget to add the Rust source file to the CxxQtBuilder in your build.rs script.
For instructions, see the [Getting Started guide](../getting-started/5-cmake-integration.md).
For instructions, see the [Getting Started guide](../getting-started/4-cmake-integration.md).

## Filename
A C++ header file will be generated for every Rust file with a `#[cxx_qt::bridge]` module listed with [`CxxQtBuilder::file`](https://docs.rs/cxx-qt-build/latest/cxx_qt_build/struct.CxxQtBuilder.html#method.file).
Expand Down
2 changes: 2 additions & 0 deletions examples/qml_minimal/qml/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import QtQuick.Controls 2.12
import QtQuick.Window 2.12

// ANCHOR: book_qml_import
// This must match the qml_uri and qml_version
// specified with the #[cxx_qt::qobject] macro in Rust.
import com.kdab.cxx_qt.demo 1.0
// ANCHOR_END: book_qml_import

Expand Down
4 changes: 3 additions & 1 deletion examples/qml_minimal/rust/src/cxxqt_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ mod my_object {
#[qproperty]
string: QString,
}
// ANCHOR_END: book_rustobj_struct

// ANCHOR: book_rustobj_default
impl Default for MyObject {
fn default() -> Self {
Self {
Expand All @@ -36,7 +38,7 @@ mod my_object {
}
}
}
// ANCHOR_END: book_rustobj_struct
// ANCHOR_END: book_rustobj_default

// ANCHOR: book_rustobj_impl
impl qobject::MyObject {
Expand Down

0 comments on commit 8e72555

Please sign in to comment.