diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 10f386380..645272595 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -7,28 +7,27 @@ SPDX-License-Identifier: MIT OR Apache-2.0 # Summary -[Introduction](./index.md) - ---- - +- [Introduction](./index.md) - [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) - [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) - - [`#[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) -- [Concepts](./concepts/index.md) - - [Bridge](./concepts/bridge.md) - - [Qt](./concepts/qt.md) - - [Types](./concepts/types.md) +- [Core Concepts](./concepts/index.md) - [Build Systems](./concepts/build_systems.md) - - [Threading](./concepts/threading.md) + - [Generated QObject](./concepts/generated_qobject.md) + - [Types](./concepts/types.md) - [Nested Objects](./concepts/nested_objects.md) - [Inheritance & Overriding](./concepts/inheritance.md) - - [Custom Constructors](./concepts/constructor.md) +- [Reference: the bridge module](./bridge/index.md) + - [extern "RustQt"](./bridge/extern_rustqt.md) + - [extern "C++Qt"](./bridge/extern_cppqt.md) + - [Shared types](./bridge/shared_types.md) + - [Attributes](./bridge/attributes.md) +- [Reference: traits](./traits/index.md) + - [CxxQtType](./traits/cxxqttype.md) + - [Constructor](./traits/constructor.md) + - [Initialize](./traits/initialize.md) + - [Locking](./traits/locking.md) + - [Threading](./traits/threading.md) diff --git a/book/src/bridge/attributes.md b/book/src/bridge/attributes.md new file mode 100644 index 000000000..76b5f948e --- /dev/null +++ b/book/src/bridge/attributes.md @@ -0,0 +1,47 @@ + + +# Attributes + +## namespace + +The C++ namespace which to emit `extern "RustQt"` items and the namespace to find `extern "C++Qt"` items. + +An item will inherit the namespace specified on it's surrounding `extern` block if any, +otherwise the namespace specified with the top level `cxx_qt::bridge` attribute, if any, will be used. + +```rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/threading.rs:book_namespace_macro}} +``` + +> Note that `#[namespace = "..."]` may not work on all items, +> we hope to improve this in future this support in the future. + +## cxx_name and rust_name + +The `#[cxx_name = "..."]` attribute replaces the name that C++ should use for this item. + +The `#[rust_name = "..."]` attribute replaces the name that Rust should use for this item. + +> Note that `#[cxx_name = "..."]` and `#[rust_name = "..."]` may not work on all items, +> we hope to improve this in future this support in the future. + +If no `#[cxx_name = "..."]` or `#[rust_name = "..."]` is specified, CXX-Qt will perform an automatic conversion to function names as specified in the table below. + +| | Rust | C++ | +|------------------|------------|-----------| +| `extern "C++Qt"` | - | camelCase | +| `extern "RustQt"`| - | camelCase | + +> Note that in some cases `snake_case` conversions may occur for generated functions in Rust (eg `on_`). + +> Note that this table may change to the following conversions in the future. +> +> | | Rust | C++ | +> |------------------|------------|-----------| +> | `extern "C++Qt"` | snake_case | - | +> | `extern "RustQt"`| - | camelCase | diff --git a/book/src/bridge/extern_cppqt.md b/book/src/bridge/extern_cppqt.md new file mode 100644 index 000000000..b19c1e1b9 --- /dev/null +++ b/book/src/bridge/extern_cppqt.md @@ -0,0 +1,104 @@ + + +# extern "C++Qt" + +- [QObjects](#qobjects) +- [Methods](#methods) +- [Signals](#signals) + +```rust,ignore,noplayground +#[cxx_qt::bridge] +mod ffi { + extern "C++Qt" { + + } +} +``` + +The `extern "C++Qt"` section of a CXX-Qt bridge declares Qt types and signatures to be made available to Rust, +and gives the paths of the headers which contain the corresponding Qt declarations. + +A bridge module may contain zero or more `extern "C++Qt"` blocks. + +This complements the [`extern "C++"` CXX section](https://cxx.rs/extern-c++.html) +but allows for declaring Qt specific features on C++ types. + +## QObjects + +Types defined in C++ that are made available to Rust, but only behind an indirection. + +This is the same as [CXX Opaque C++ types](https://cxx.rs/extern-c++.html#opaque-c-types). + +```rust,ignore,noplayground +#[cxx_qt::bridge] +mod ffi { + extern "C++Qt" { + include!(); + type QPushButton; + } +} +``` + + + +## Methods + +Methods can be specified on the Qt type in the same way as [`extern "RustQt"` blocks](./extern_rustqt.md#methods). + +This is the same as [CXX Functions and member functions](https://cxx.rs/extern-c++.html#functions-and-member-functions). + +```rust,ignore,noplayground +#[cxx_qt::bridge] +mod ffi { + unsafe extern "C++" { + include!("cxx-qt-lib/qstring.h"); + type QString = cxx_qt_lib::QString; + } + + extern "C++Qt" { + include!(); + type QPushButton; + + fn text(self: &QPushButton) -> QString; + fn setText(self: Pin<&mut QPushButton>, text: &QString); + } +} +``` + + + +## Signals + +Signals can be specified on the Qt type in the same way as [`extern "RustQt"` blocks](./extern_rustqt.md#signals). + +```rust,ignore,noplayground +#[cxx_qt::bridge] +mod ffi { + extern "C++Qt" { + include!(); + type QPushButton; + + #[qsignal] + fn clicked(self: Pin<&mut QPushButton>, checked: bool); + } +} +``` + +This then causes CXX-Qt to generate Rust methods to connect to the `#[qsignal]` with a closure, +in the same way as a `#[qsignal]` in a [`extern "RustQt"` block](./extern_rustqt.md#signals). + +> Note using `pub(self)` as the visibility of the signal +> allows for declaring private signals + + diff --git a/book/src/bridge/extern_rustqt.md b/book/src/bridge/extern_rustqt.md new file mode 100644 index 000000000..97ff3959f --- /dev/null +++ b/book/src/bridge/extern_rustqt.md @@ -0,0 +1,258 @@ + + +# extern "RustQt" + +- [QObjects](#qobjects) +- [Properties](#properties) +- [Methods](#methods) +- [Signals](#signals) + +```rust,ignore,noplayground +#[cxx_qt::bridge] +mod ffi { + extern "RustQt" { + + } +} +``` + +The `extern "RustQt"` section of a CXX bridge declares Rust types and signatures to be made available to Qt and C++. + +The CXX code generator uses your `extern "Rust"` section(s) to produce a C++ header file containing the corresponding C++ declarations. The generated header has a file name matching the module ident or the `cxx_file_stem` field in the `#[cxx_qt::bridge]` attribute and with a `.cxxqt.h` file extension. + +A bridge module may contain zero or more `extern "RustQt"` blocks. + +This complements the [`extern "Rust"` CXX section](https://cxx.rs/extern-rust.html) +but allows for declaring Qt specific features on C++ types. + +## QObjects + +Types specified with a `#[qobject]` attribute are generated in C++ as a [`QObject`](https://doc.qt.io/qt-6/qobject.html). + +The left side of the type specifies the C++ generated type and name, when referring to the C++ context this should be used. The right side of the type specifies which Rust type provides the inner implementation of the type (for example fields ). + +```rust,ignore,noplayground +#[cxx_qt::bridge] +mod ffi { + extern "RustQt" { + #[qobject] + type MyObject = super::MyObjectRust; + } +} + +#[derive(Default)] +struct MyObjectRust; +``` + +### QML Attributes + +QObjects can be registered as a QML type directly at build time by using the [`#[qml_element]`](https://doc.qt.io/qt-6/qqmlengine.html#QML_ELEMENT) attribute. + +``` rust,ignore,noplayground +{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct_signature}} +``` + +Additionally, you can configure the QML registration with these attributes: + + +- [`qml_name`](https://doc.qt.io/qt-6/qqmlengine.html#QML_NAMED_ELEMENT): Use a different type name for QML. +- [`qml_uncreatable`](https://doc.qt.io/qt-6/qqmlengine.html#QML_UNCREATABLE): Mark the type as uncreatable from QML. It may still be returned by C++/Rust code. +- [`qml_singleton`](https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON): An instance of the QObject will be instantiated as a singleton in QML. + +Note that the rust file must be included within a Qml module in the `build.rs` file. + + + +### `base` attribute + +Use the `base` attribute to specify a C++ class that the C++ QObject will inherit from. +The base class must inherit from QObject (directly or indirectly). If you do not specify a base attribute, it will inherit directly from QObject. + +``` rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_qobject_base}} +``` + +Use the CXX `include!` macro to include the appropriate C++ header for the base class: +``` rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_base_include}} +``` + +For more information on inheritance and how to override methods see the [Inheritance & Overriding](../concepts/inheritance.md) page. + +[Full Example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/custom_base_class.rs) + + +### Traits + +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)]`. Or the [`Contructor`](../traits/constructor.md) trait needs to be implemented for the type. + + + +## Properties + +The `#[qproperty(TYPE, NAME, ...)]` attribute can be specified on a [`#[qobject]` marked type](#qobjects) to expose a [`Q_PROPERTY`](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_signature}} +``` + +The type and name of the + +```rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/properties.rs:book_properties_struct}} +``` + +For every `#[qproperty]`, CXX-Qt will generate setters and getters, as well as a "changed" signal. + +On the C++ side: + * setter: `set` + * getter: `get` + * changed: `Changed` + +On the Rust side: + * setter: `set_` + * getter: `` + * changed: `_changed` + +Also the generated Rust methods for [signals](#signals) + + * connect: `connect__changed` + * on: `on__changed` + +Where `` is the name of the property. + +These setters and getters assure that the changed signal is emitted every time the property is edited. + +> Note that in the future it will be possible to specify custom getters and setters + +## Methods + +Any signature with a `self` parameter is interpreted as a Rust method and exposed to C++ method for the given type. +The type much be either a shared reference `self: &T` or a pinned mutable reference `self: Pin<&mut T>`, where `T` is the [QObject](#qobjects) type. + +``` rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/invokables.rs:book_cpp_method_signature}} +``` + +Implementations of the method are then written as normal on the C++ type outside the bridge. + +``` rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/invokables.rs:book_cpp_method_impl}} +``` + +> Note how this uses `impl qobject::T` rather than `impl T` where `qobject` is the bridge module name. + +### Invokables + +The `#[qinvokable]` attribute can be specified on signatures to expose them as a [`Q_INVOKABLE`](https://doc.qt.io/qt-6/qobject.html#Q_INVOKABLE) in C++. + +``` rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/invokables.rs:book_invokable_signature}} +``` + +Implementations then have no difference to non invokable methods. + +``` rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/invokables.rs:book_invokable_impl}} +``` + +### Inheritance + + + +### Specifiers + +Generated methods can have C++ specifiers necessary to implement inheritance. + +| C++ keyword | CXX-Qt attribute | +|-------------|-------------------------------| +| `override` | `#[cxx_override]` | +| `virtual` | `#[cxx_virtual]` | +| `final` | `#[cxx_final]` | + +These are specified as an attribute on the method signature. + +```rust,ignore +{{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_inherit_data_signature}} +``` + +## Signals + +The `qsignal` attribute is used in an `extern "RustQt"` block to define [signals](https://doc.qt.io/qt-6/signalsandslots.html) for the a QObject. + +```rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/signals.rs:book_signals_block}} +``` + +For every function signature in the extern block, CXX-Qt will generate a signal on the corresponding QObject. +If the function has parameters, they will become the parameters for the corresponding signal. + +If a signal is defined on the base class of the QObject then `#[inherit]` can be used to indicate to CXX-Qt that the `Q_SIGNAL` does not need to be created in C++. + +A full example can be found in the [qml features](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/signals.rs). + +> Note that `#[cxx_name = "..."]` can also be used on a signal to declare a different name in C++ to Rust + +> Note using `pub(self)` as the visibility of the signal +> allows for declaring private signals + +### Connecting to a signal + +For every signal defined in the enum, two methods are generated. + + 1. `on_` + 2. `connect_` + +The `on_` method takes a handler function as the parameter, which will be called when the signal is emitted. +That handler function's first argument is the qobject and the remaining arguments are the signal parameters. + +The `connect_` function additionally takes the [Qt connection type](https://doc.qt.io/qt-6/qt.html#ConnectionType-enum) as a parameter. + +Note that by using the `#[inherit]` macro on a signal, connections can be made to property changes +using the signal name `Changed` with no parameters. + +```rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/signals.rs:book_signals_connect}} +``` + +Each connection returns a [`QMetaObject::Connection`](https://doc.qt.io/qt-6/qmetaobject-connection.html) which is used to manage the connection. + +Note that the `QMetaObjectConnection` returned by CXX-Qt behaves a bit different from the Qt C++ implementation. + +When the `QMetaObjectConnection` is dropped, it automatically disconnects the connection, similar to how a C++ `std::unique_ptr` or Rusts `Box` behaves. +If you don't want to store the QMetaObjectConnection, call `release`, which will drop the object without disconnecting. +In this case, it is no longer possible to disconnect later. + +```rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/signals.rs:book_signals_disconnect}} +``` + +### Emitting a signal + +Call the function signature defined in the `extern "RustQt"` block to emit the signal. + +Note that these are defined on the generated QObject [`qobject::T`](../concepts/generated_qobject.md), so can be called from any mutable `#[qinvokable]`. + +The function will immediately emit the signal. +Depending on the connection type, the connected slots will be called either immediately or from the event loop (See [the different connection types](https://doc.qt.io/qt-6/qt.html#ConnectionType-enum)). +To queue the call until the next cycle of the Qt event loop, you can use the [`CxxQtThread`](../traits/threading.md). + +### Inheritance + +If a signal is defined on the base class of the QObject then the `#[inherit]` attribute can be used to indicate to CXX-Qt that the `Q_SIGNAL` does not need to be created in C++. + +```rust,ignore,noplayground +{{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_qsignals_inherit}} +``` diff --git a/book/src/bridge/index.md b/book/src/bridge/index.md new file mode 100644 index 000000000..b4feffe1a --- /dev/null +++ b/book/src/bridge/index.md @@ -0,0 +1,42 @@ + + +# The bridge module reference + +- [extern "RustQt"](./extern_rustqt.md) - exposing Rust types to Qt as QObject, Q_SIGNAL, Q_PROPERTY etc +- [extern "C++Qt"](./extern_cppqt.md) - binding Qt features and types to Rust, such as QObject, Q_SIGNAL etc +- [Shared types](./shared_types.md) - shared enums between Rust and Qt, such as Q_ENUM, Q_ENUM_NS etc +- [Attributes](./attributes.md) - working with namespaces, giving functions different names + +The `#[cxx_qt::bridge]` macro functions very similarly to [`#[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. + +> 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/4-cmake-integration.md). + +The `#[cxx_qt::bridge]` macro supports two options in it's attribute +- [`cxx_file_stem`](#cxx_file_stem) +- [`namespace`](./attributes.md#namespace) + +## cxx_file_stem + +By default, the name of the generated C++ header file will be the name of the module, followed by `.cxxqt.h` (and `.cxx.h` for CXX files). + +This can cause issues as the module is normally called `ffi` or `qobject` so collisions would occur. + +The `cxx_file_stem` option allow a file name to be specified to avoid collisions. + +```rust,ignore +{{#include ../../../examples/qml_features/rust/src/types.rs:book_cxx_file_stem}} +``` + +> Currently, cxx-qt-gen writes all generated header files into a single folder. +> Therefore you need to be careful to not produce two header files with the same filename. + +> We want to use the name of the Rust source file that the macro is located in (the same as CXX). +> However this requires [inspection APIs from `proc_macro::Span`](https://github.com/rust-lang/rust/issues/54725) +> which is currently a nightly feature. diff --git a/book/src/bridge/shared_types.md b/book/src/bridge/shared_types.md new file mode 100644 index 000000000..3851e9466 --- /dev/null +++ b/book/src/bridge/shared_types.md @@ -0,0 +1,14 @@ + + +# Shared types + +## Enums + + diff --git a/book/src/concepts/bridge.md b/book/src/concepts/bridge.md deleted file mode 100644 index d987de7a1..000000000 --- a/book/src/concepts/bridge.md +++ /dev/null @@ -1,18 +0,0 @@ - - -# Bridge - -CXX-Qt uses [CXX](https://cxx.rs/) for bridging between C++ and Rust in a safe way. - -CXX-Qt provides macros for declaring Qt objects such as [QObject](../qobject/index.md) while still being idomatic Rust code. - -We provide [Qt types](./types.md) to help pass common data types across the bridge between Rust and Qt. - -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 [`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. diff --git a/book/src/concepts/build_systems.md b/book/src/concepts/build_systems.md index aec9f6229..249cfe614 100644 --- a/book/src/concepts/build_systems.md +++ b/book/src/concepts/build_systems.md @@ -14,3 +14,7 @@ CXX-Qt can be integrated into existing CMake projects or built with only cargo. 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/4-cmake-integration.md). However, using C++ build systems besides CMake with CXX-Qt is untested. + + diff --git a/book/src/qobject/generated-qobject.md b/book/src/concepts/generated_qobject.md similarity index 96% rename from book/src/qobject/generated-qobject.md rename to book/src/concepts/generated_qobject.md index cdef3fd03..3fb89e960 100644 --- a/book/src/qobject/generated-qobject.md +++ b/book/src/concepts/generated_qobject.md @@ -5,10 +5,14 @@ SPDX-FileContributor: Andrew Hayzen SPDX-License-Identifier: MIT OR Apache-2.0 --> -# `qobject::T` - The generated QObject +# 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 [`#[qobject]` macro](./qobject_struct.md) is for. +This is what the [`#[qobject]` macro](../bridge/extern_rustqt.md#qobjects) is for. This page serves to document the details of what is generated and how to interact with the generated QObject from Rust. The `#[qobject]` macro generates a QObject for a given Rust struct. @@ -24,9 +28,12 @@ Therefore implementing `Default` on the Rust struct is currently mandatory. The C++ object will defer any property state to the Rust struct, and is therefore only a thin wrapper. The data for `Q_PROPERTY`s is stored in the Rust struct. The property getters and setters modify the Rust struct internally. + + diff --git a/book/src/concepts/index.md b/book/src/concepts/index.md index b8c10508f..3bf46df42 100644 --- a/book/src/concepts/index.md +++ b/book/src/concepts/index.md @@ -6,21 +6,16 @@ SPDX-FileContributor: Leon Matthes SPDX-License-Identifier: MIT OR Apache-2.0 --> -# Concepts +# Core Concepts -## Basic Concepts +CXX-Qt uses [CXX](https://cxx.rs/) for bridging between C++ and Rust in a safe way. - * [Bridge](./bridge.md) - * [Qt features that are supported](./qt.md) - * [Supported types between Rust and C++](./types.md) +The main purpose of CXX-Qt is to expose Qt's extensions to the C++ language to CXX. -## Build tooling +> Note that the Rust QObject 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. + * [Supported types between Rust and C++](./types.md) * [Build Systems](./build_systems.md) - -## Advanced Concepts - - * [Threading concept and safety](./threading.md) + * [Generated QObject](./generated_qobject.md) * [Nesting Rust objects](./nested_objects.md) * [Inheriting QObjects and overriding methods](./inheritance.md) - * [Defining custom C++/QML Constructors](./constructor.md) diff --git a/book/src/concepts/inheritance.md b/book/src/concepts/inheritance.md index fd224d68c..ff8d8311c 100644 --- a/book/src/concepts/inheritance.md +++ b/book/src/concepts/inheritance.md @@ -7,12 +7,16 @@ SPDX-License-Identifier: MIT OR Apache-2.0 # Inheritance + + Some Qt APIs require you to override certain methods from an abstract base class, for example [QAbstractItemModel](https://doc.qt.io/qt-6/qabstractitemmodel.html). To support creating such subclasses directly from within Rust, CXX-Qt provides you with multiple helpers. Some superclasses may require special parameters for construction. -This can be achieved by using a [custom constructor](./constructor.md). +This can be achieved by using a [custom constructor](../traits/constructor.md). ## Accessing base class methods To access the methods of a base class in Rust, use the `#[inherit]` macro. diff --git a/book/src/concepts/nested_objects.md b/book/src/concepts/nested_objects.md index cb5708d71..35f4d464c 100644 --- a/book/src/concepts/nested_objects.md +++ b/book/src/concepts/nested_objects.md @@ -7,6 +7,10 @@ SPDX-License-Identifier: MIT OR Apache-2.0 # Nested Objects + + Rust Qt objects can be nested as properties or parameters of each other. A nested object is referred to by using a pointer to its QObject representation. diff --git a/book/src/concepts/qt.md b/book/src/concepts/qt.md deleted file mode 100644 index 1d6dbd2f4..000000000 --- a/book/src/concepts/qt.md +++ /dev/null @@ -1,22 +0,0 @@ - - -# Qt - -The main purpose of CXX-Qt is to expose Qt's extensions to the C++ language to CXX. - -## Invokables - -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 - -Properties can be defined using the [QObject struct](../qobject/qobject_struct.md), these will be exposed as a getter and setter method, a changed signal, and a `Q_PROPERTY` on the C++ class and therefore as QML properties too. - -## Signals - -Signals can be defined using the [QSignals macros](../qobject/signals.md), these will be exposed as `Q_SIGNALS` on the C++ class and therefore to QML as well. diff --git a/book/src/concepts/threading.md b/book/src/concepts/threading.md deleted file mode 100644 index f40376530..000000000 --- a/book/src/concepts/threading.md +++ /dev/null @@ -1,40 +0,0 @@ - - -# Threading - -## Concept - -The general concept for threading is that when Rust code is being executed a lock has been acquired on the C++ side to prevent Rust code being executed from multiple threads. - -This means that Rust code, such as invokables and properties, which are directly called from C++ are executed on the Qt thread. - -Note that a recursive mutex is used internally, this allows for signals to be emitted and then call slots on the same object without deadlocks. - -### Locking - -In certain scenarios it might be useful to disable locking that occurs when the context switches from C++ to Rust. - -To disable the generation of locking use an unsafe negation `unsafe impl !cxx_qt::Locking for qobject::T {}`. - -However if locking is disabled the `cxx_qt::Threading` trait can not be enabled on the object. - -## Multi threading - -To achieve safe multi-threading on the Rust side we use an [`CxxQtThread`](../qobject/cxxqtthread.md). -A `CxxQtThread` represents a reference to the Qt thread that the QObject of type `T` lives in. -When a new Rust thread is started (e.g. in an invokable) the `CxxQtThread` can be moved into the thread to later update the QObject in a thread safe manner. - -When the Rust thread needs to update a value in the QObject it can then queue a closure to the thread. -This closure will be executed on the thread the QObject lives in while holding a lock on the Rust object. -Updating the QObject is then thread-safe. - -Below is a complete Rust example of a multi-threaded object. - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/threading.rs:book_macro_code}} -``` diff --git a/book/src/concepts/types.md b/book/src/concepts/types.md index f806be8a9..3eaa4a0b3 100644 --- a/book/src/concepts/types.md +++ b/book/src/concepts/types.md @@ -7,6 +7,10 @@ SPDX-License-Identifier: MIT OR Apache-2.0 # Types + + CXX-Qt supports most types supported by CXX. These can be used in properties, invokables, and signals. ## `cxx-qt-lib` Types diff --git a/book/src/getting-started/1-qobjects-in-rust.md b/book/src/getting-started/1-qobjects-in-rust.md index 0f29faa62..15dc549ed 100644 --- a/book/src/getting-started/1-qobjects-in-rust.md +++ b/book/src/getting-started/1-qobjects-in-rust.md @@ -40,18 +40,16 @@ These concepts include: - Custom QObject classes - Properties - Invokables/Slots -- Signals + - Signals +- Enums -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. -- `#[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. +As with CXX, to use these features you mark a Rust module with an attribute macro ([`#[cxx_qt::bridge]`](../bridge/index.md)). -CXX-Qt will then expand this Rust module into two separate parts: -- C++ files that define a QObject subclass for each `#[qobject]` marked struct. -- The Rust code for the `#[qobject]` marked Rust struct +Then you can use the afformentioned features with the help of extern blocks and macros to express them in a [Rust bridge](../bridge/index.md). + +CXX-Qt will then expand this Rust bridge into two separate parts: +- C++ files that define a QObject, enums etc to Qt. +- The Rust code which provides the implementation of the features
@@ -60,18 +58,18 @@ CXX-Qt will then expand this Rust module into two separate parts:
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. +For more details, see the [Bridge](../bridge/index.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 `#[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 Rust 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. Typically this will be instantiated by QML and the lifetime will be directly associated with the corresponding QML item. The generated QObject subclass will then defer to the Rust struct for any behavior, which is then defined in Rust. -For example, using the `#[qinvokable]` attribute, we can define functions that will be exposed to C++, but will execute Rust code. -Also, any fields in the Rust struct can be exposed to Qt as `Q_PROPERTY` fields by using the `#[qproperty(T, NAME)]` attribute on the struct. +For example, using the [`#[qinvokable]`](../bridge/extern_rustqt.md#invokables) attribute, we can define functions that will be exposed to C++, but will execute Rust code. +Also, any fields in the Rust struct can be exposed to Qt as `Q_PROPERTY` fields by using the [`#[qproperty(T, NAME)]`](../bridge/extern_rustqt.md#properties) attribute. Therefore allowing you to assign them from QML as well. But enough theory for now, lets jump in and write [our first CXX-Qt module](./2-our-first-cxx-qt-module.md). diff --git a/book/src/getting-started/2-our-first-cxx-qt-module.md b/book/src/getting-started/2-our-first-cxx-qt-module.md index 341195fb8..26dc53ce4 100644 --- a/book/src/getting-started/2-our-first-cxx-qt-module.md +++ b/book/src/getting-started/2-our-first-cxx-qt-module.md @@ -52,13 +52,19 @@ 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 `#[qobject]`. +To create a new QObject subclass, we can define a type alias within a `extern "RustQt"` block in our module and mark it with `#[qobject]`. + +```rust,ignore +{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct_signature}} +``` + +The Rust struct can be defined outside the module just like a normal Rust struct and can contain any kind of field, even Rust-only types. ```rust,ignore {{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct}} ``` -Optionally, add `qml_uri` and `qml_version` inside `#[qobject]` to tell the Rust build script to generate a QML plugin +Optionally, add `qml_element` inside `#[qobject]` to tell the Rust build script to generate a QML_ELEMENT 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). @@ -68,14 +74,13 @@ Additionally, we need to either `impl Default` or `#[derive(Default)]` for our s {{#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 tagged as `#[qproperty]` it will be exposed to the C++ side as a `Q_PROPERTY`. +The `#[qproperty]` attribute on the type alias will create properties to the fields matching the name and 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 tagged as `#[qproperty]` must be types that CXX can translate to C++ types. +Do note though that any fields exposed as `#[qproperty]` must be types that CXX can translate to C++ types. In our case that means: - `number: i32` -> `::std::int32_t number` - `string: QString` -> `QString string` @@ -83,17 +88,17 @@ In our case that means: For `i32`, CXX-Qt already knows how to translate it. A `QString` however is unknown to CXX. Luckily, the [`cxx_qt_lib`](https://docs.rs/cxx-qt-lib/latest/cxx_qt_lib/) crate already wraps many Qt types for us. -We can just import them like any other CXX type: +We can just define them in the bridge like any other CXX type: ``` rust, ignore {{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_qstring_import}} ``` For more details on the available types, see the [Qt types page](../concepts/types.md). -## qobject::T +## Invokables -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 `#[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`. +CXX-Qt will then automatically generate a new QObject subclass for our `MyObjectRust` struct and expose it as an [`extern "C++"` opaque type](https://cxx.rs/extern-c++.html#opaque-c-types) to Rust. +For any type alias `type T = super::S` marked with `#[qobject]`, CXX-Qt will expose the corresponding C++ QObject under `T`. +In our case, this means we can refer to the C++ QObject for our `MyObjectRust` struct, as `MyObject`. This type can be used like any other CXX opaque type. Additionally, CXX-Qt allows us to add functionality to this QObject by referring to the type as the self type of functions in an `extern "RustQt"` block in together with `#[qinvokable]`. @@ -102,10 +107,21 @@ Additionally, CXX-Qt allows us to add functionality to this QObject by referring ``` And then implementing the invokables outside the bridge using `impl qobject::MyObject`. + ```rust,ignore {{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_invokable_impl}} ``` +> Note that by using `qobject` instead of `ffi` as the module name for the bridge, +> referring to the C++ QObject outside the bridge becomes a natural `qobject::MyObject` +> instead of `ffi::MyObject`. + +Also do not forget to use the relevant imports that are required for the invokable implementation. + +```rust,ignore +{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_use}} +``` + In our case, we define two new functions: - `increment_number` - Increments the number of the `MyObject`. @@ -119,8 +135,8 @@ As this type is a CXX Opaque type, we can't actually instantiate it. Qt/C++ takes care of this. However, we can still define Rust functions on this type. They will just be normal Rust functions, but will be executed on a C++ type. -CXX-Qt will already generate getters and setters for all fields of your struct this way. -If you additionally mark any of these functions with `#[qinvokable]`, they will also be callable from C++ and QML. +CXX-Qt will already generate getters and setters for all properties of your struct this way. +If you additionally define these in the `extern "RustQt"` block of the bridge, they will also be callable from C++ and QML. In this case, the types of the arguments also need to convertable to C++, like with any `#[qproperty]`. And that's it. We've defined our first QObject subclass in Rust. That wasn't so hard, was it? diff --git a/book/src/getting-started/3-qml-gui.md b/book/src/getting-started/3-qml-gui.md index 4be2587e0..c90d0e3a3 100644 --- a/book/src/getting-started/3-qml-gui.md +++ b/book/src/getting-started/3-qml-gui.md @@ -35,12 +35,18 @@ For QML, this doesn't make a difference though. # 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 the `qml_files` part of our `build.rs` file: -```qrc,ignore -{{#include ../../../examples/qml_minimal/rust/build.rs:book_qml_files}} +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 the `qml_files` part of our QML module in the `build.rs` file: + + + +```rust,ignore +{{#include ../../../examples/qml_minimal/rust/build.rs:book_qml_module}} ``` -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. +In the `main.cpp` we then use the URL of the `main.qml` file inside the QML module. + ``` cpp, ignore {{#include ../../../examples/qml_minimal/cpp/main.cpp:book_qml_url}} ``` diff --git a/book/src/getting-started/4-cmake-integration.md b/book/src/getting-started/4-cmake-integration.md index 2b7eb5187..5637fd030 100644 --- a/book/src/getting-started/4-cmake-integration.md +++ b/book/src/getting-started/4-cmake-integration.md @@ -26,7 +26,9 @@ You can add as much C++ code as you want in addition to this. 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` +So in our case: `#include cxx-qt-gen/qobject.cxxqt.h` + +> Note that the [`cxx_file_stem`](../bridge/index.md#cxx_file_stem) option can be specified in the bridge macro to choose the file name. Including the generated header allows accessing the `MyObject` C++ class, just like any other C++ class. Inherit from it, connect signals and slots to it, put it in a QVector, do whatever you want with it. @@ -71,6 +73,9 @@ This is what generates and compiles the C++ code for our `MyObject` class at bui Every Rust source file 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. +This is also where the QML module is defined with a QML uri and version. +The files and resources in the module are then exposed in the same way as the [qt_add_qml_module CMake function](https://doc.qt.io/qt-6/qt-add-qml-module.html). + ## CMake setup Now add a `CMakeLists.txt` file in the root of the `tutorial` folder. Start the `CMakeLists.txt` file like any other C++ project using Qt. For this example, we are [supporting both @@ -123,13 +128,13 @@ You should now see the two Labels that display the state of our `MyObject`, as w ### Windows with MSVC -If you're building CXX-Qt on Windows using MSVC generator, you need to add the `-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` flag to the cmake command when building with the `Debug` configuration. This flag is necessary to ensure that the correct C Runtime Library is used. Then you can build using `cmake --build build --config Debug`. +If you're building CXX-Qt on Windows using MSVC generator, you need to ensure that `set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")` is set in CMake (or use the `-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` flag) when building with the `Debug` configuration. This flag is necessary to ensure that the correct C Runtime Library is used. Then you can build using `cmake --build build --config Debug`. This issue is caused by a bug in the [cc](https://docs.rs/cc/latest/cc/index.html) crate (as described in https://github.com/rust-lang/cc-rs/pull/717), which has not been merged yet. Specifically, the problem is that cc generated code always links to the MultiThreaded runtime, even when building in Debug mode. We hope that this step won't be necessary in the future, once the cc crate fix is merged and released. ## 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. +For further reading, you can take a look at the [bridge chapter](../bridge/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. In the next, optional chapter, we will show how to build the same QML application with Cargo without needing CMake. diff --git a/book/src/index.md b/book/src/index.md index 62c06e5ac..2923eda7b 100644 --- a/book/src/index.md +++ b/book/src/index.md @@ -13,11 +13,11 @@ This library provides a safe mechanism for bridging between Qt code and Rust cod We acknowledge that Qt code and Rust code have different idioms so cannot be directly wrapped from one to another. -Instead of one-to-one bindings we use [CXX](https://cxx.rs/) to [bridge](./concepts/bridge.md) between, this allows for normal Qt code and normal Rust code. +Instead of one-to-one bindings we use [CXX](https://cxx.rs/) to [bridge](./bridge/index.md) between, this allows for normal Qt code and normal Rust code. -We feel this is more powerful than typical bindings as this allows us to provide a safe API and safe [multi-threading](./concepts/threading.md) between Qt and Rust. +We feel this is more powerful than typical bindings as this allows us to provide a safe API and safe [multi-threading](./traits/threading.md) between Qt and Rust. -To aid integration of Qt and Rust code we provide common [Qt types](./concepts/types.md) for Rust which can pass across the bridge and provide ways to express common [Qt idioms](./concepts/qt.md). +To aid integration of Qt and Rust code we provide common [Qt types](./concepts/types.md) for Rust which can pass across the bridge and provide ways to express common Qt idioms. Through the use of macros and code generation as seen in the figure below, the developer describes a `QObject` with CXX-Qt macro annotations. Then CXX-Qt generates the C++ representation of the object and uses macro expansion to define the [CXX](https://cxx.rs/) bridge for the interop between C++ and Rust. @@ -29,7 +29,7 @@ Through the use of macros and code generation as seen in the figure below, the d If you are new to CXX-Qt, we recommend you visit our [Getting Started Guide](./getting-started/index.md). -To get detailed information on which features of a QObject are available in CXX-Qt, see the [QObject chapter](./qobject/index.md). +To get detailed information on which features are available in CXX-Qt, see the [bridge chapter](./bridge/index.md). Should you be interested in a deeper dive into the concepts of CXX-Qt, take a look at the [concepts chapter](./concepts/index.md), which explains the concepts CXX-Qt introduces in detail. **Note:** CXX-Qt is tested on CI on Linux, Windows, and macOS (all on x86_64). It should work on other platforms that Qt and Rust both support, however, these are not tested regularly. diff --git a/book/src/qobject/bridge-macro.md b/book/src/qobject/bridge-macro.md deleted file mode 100644 index 8d143b615..000000000 --- a/book/src/qobject/bridge-macro.md +++ /dev/null @@ -1,42 +0,0 @@ - - -# `#[cxx_qt::bridge]` Macro - -The `#[cxx_qt::bridge]` macro functions very similarly to [`#[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/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). - -By default, the name of the generated C++ header file will be the name of the module, followed by `.cxxqt.h`. -Our plan is to change this to use the Rust file name instead. Progress on this can be tracked in [#200](https://github.com/KDAB/cxx-qt/pull/200). - -This filename can also be changed using the `cxx_file_stem` attribute. -The following example results in a header file named: `types.cxxqt.h`. -``` rust, ignore -{{#include ../../../examples/qml_features/rust/src/types.rs:book_cxx_file_stem}} - // ... -} -``` - -Currently, cxx-qt-gen writes all generated header files into a single folder. -Therefore you need to be careful to not produce two header files with the same filename. -In future we plan to use the entire module path to disambiguate this. -Progress on this can be tracked in [#19](https://github.com/KDAB/cxx-qt/issues/19). - -## C++ namespace -Just like on a `#[cxx::bridge]`, the C++ namespace of the bridge can be changed using the `namespace` attribute. - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/threading.rs:book_namespace_macro}} - // ... -} -``` -This will generate a header file named `threading_website.cxxqt.h` with all C++ items included in the `cxx_qt::website` namespace. diff --git a/book/src/qobject/index.md b/book/src/qobject/index.md deleted file mode 100644 index 623b603da..000000000 --- a/book/src/qobject/index.md +++ /dev/null @@ -1,22 +0,0 @@ - - -# QObject - -A QObject defined by CXX-Qt supports many features and is made up of quite a few parts. - -This chapter goes into details on these. -For a simpler introduction, take a look at our [Getting Started guide](../getting-started/index.md). - -QObject Features and Parts: - * [`#[cxx_qt::bridge]` - The macro around the module](./bridge-macro.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) - - diff --git a/book/src/qobject/qobject_struct.md b/book/src/qobject/qobject_struct.md deleted file mode 100644 index f89a73a3a..000000000 --- a/book/src/qobject/qobject_struct.md +++ /dev/null @@ -1,153 +0,0 @@ - - -# `#[qobject]` Macro - Defining QObjects in Rust - -Defining QObjects is at the heart of 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 `#[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 -Adding the macro to a Rust struct `MyObject` has a few effects. - -However, first it's important to mention that this macro **does not modify the contents of the struct in any way!** -This means you can always rely on the struct to behave like any other Rust struct. -You can use it in normal Rust code, without interacting with Qt in any way. - -The macro does multiple other things for you though: -- Generate a C++ QObject subclass that wraps the `MyObject` Rust struct. -- Expose the generated QObject subclass to Rust as [`qobject::MyObject`](./generated-qobject.md) -- Generate getters/setters for all fields. -- Generate `Q_PROPERTY`s for all fields that are tagged as `#[qproperty]`. -- Generate signals if paired with a [`#[qsignal]` macro](./signals.md). - -## Exposing to QML -`#[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 `#[qobject]` macro. -``` rust,ignore,noplayground -{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct}} -``` - -Additionally, you can configure the QML registration with these attributes: -- `qml_name`: Use a different type name for QML. -- `qml_uncreatable`: Mark the type as uncreatable from QML. It may still be returned by C++/Rust code. -- `qml_singleton`: An instance of the QObject will be instantiated as a singleton in QML. - -## `base` attribute -Use the `base` attribute to specify a C++ class that the C++ QObject will inherit from. -The base class must inherit from QObject (directly or indirectly). If you do not specify a base attribute, it will inherit directly from QObject. - -``` rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_qobject_base}} - // ... - } -``` - -Use the CXX `include!` macro to include the appropriate C++ header for the base class: -``` rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_base_include}} - } -``` - -For more information on inheritance and how to override methods see the [Inheritance & Overriding](../concepts/inheritance.md) page. - -[Full Example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/custom_base_class.rs) - -## Properties - -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}} -``` - -Any type that CXX supports may be tagged as a `#[qproperty]`. -See the [Types page](../concepts/types.md) for a list of supported types. - -For every `#[qproperty]`, CXX-Qt will generate setters and getters, as well as a "changed" signal. - -On the C++ side: - * setter: `set` - * getter: `get` - * changed: `Changed` - -On the Rust side: - * setter: `set_` - * getter: `` - * changed: `_changed` - -where `` is the name of the property. - -These setters and getters assure that the changed signal is emitted every time the property is edited. - -Any field that's not tagged as `#[qproperty]` won't be accessible from C++, but it will be accessible from Rust. -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 `#[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) - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/properties.rs:book_properties_default}} -``` - -[Full Example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/properties.rs) - -## Invokables - -Invokables are functions that are registered with the Qt meta-object system using [`Q_INVOKABLE`](https://doc.qt.io/qt-6/qobject.html#Q_INVOKABLE). -This allows them to be called from QML and JavaScript (running in a [QJSEngine](https://doc.qt.io/qt-6/qjsengine.html)), in addition to C++ and Rust. - -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 `#[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`. -Therefore they have access to both C++ and Rust methods. CXX-Qt adds wrapper code around your invokables to automatically convert between the [C++ and Rust types](../concepts/types.md). - -To mark a method as invokable, simply add the `#[qinvokable]` attribute to the Rust function in the `extern "RustQt"` block. This tells CXX-Qt to expose the method on the generated C++ class. -`Q_INVOKABLE` will be added to the C++ definition of the method, allowing QML to call the invokable. - -``` rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/invokables.rs:book_invokable_signature}} -``` - -``` rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/invokables.rs:book_invokable_impl}} -``` - -Note that an invokable may only use `self: Pin<&mut Self>` or `&self` as self types. -It is not possible to have a `self`, or `&mut self` invokable, as that may move the QObject in memory, which would invalidate C++ pointers and references to the QObject. -Furthermore, invokables are restricted to only use types that are compatible with CXX. - -It is also possible to define methods in the `impl qobject::T` block that are *not* marked as `#[qinvokable]`. -These methods won't be available from C++ or QML. -But they can still access the QObject features like emitting signals and changing properties by accessing `Pin <&mut Self>`. -These are normal Rust methods, so they aren't restricted to CXX-compatible types. - -[Full example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/invokables.rs) - -## Private Methods and Fields - -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. - -Methods implemented using `impl T` (and not `impl 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 T` if you want to also use your struct as a normal Rust struct that is not wrapped in a QObject. diff --git a/book/src/qobject/signals.md b/book/src/qobject/signals.md deleted file mode 100644 index 76d911e53..000000000 --- a/book/src/qobject/signals.md +++ /dev/null @@ -1,72 +0,0 @@ - - -# Signals - -The `qsignal` attribute is used in an `extern "RustQt"` block to define [signals](https://doc.qt.io/qt-6/signalsandslots.html) for the a QObject. - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/signals.rs:book_signals_block}} -``` - -For every function signature in the extern block, CXX-Qt will generate a signal on the corresponding QObject. -If the function has parameters, they will become the parameters for the corresponding signal. - -If a signal is defined on the base class of the QObject then `#[inherit]` can be used to indicate to CXX-Qt that the `Q_SIGNAL` does not need to be created in C++. - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_qsignals_inherit}} -``` - -Note that `#[cxx_name = "..."]` can also be used on a signal to declare a different name in C++ to Rust. - -## Connecting to a signal - -For every signal defined in the enum, two methods are generated. - - 1. `on_` - 2. `connect_` - -The `on_` method takes a handler function as the parameter, which will be called when the signal is emitted. -That handler function's first argument is the qobject and the remaining arguments are the signal parameters. - -The `connect_` function additionally takes the [Qt connection type](https://doc.qt.io/qt-6/qt.html#ConnectionType-enum) as a parameter. - -Note that by using the `#[inherit]` macro on a signal, connections can be made to property changes -using the signal name `Changed` with no parameters. - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/signals.rs:book_signals_connect}} -``` - -Each connection returns a [`QMetaObject::Connection`](https://doc.qt.io/qt-6/qmetaobject-connection.html) which is used to manage the connection. - -Note that the `QMetaObjectConnection` returned by CXX-Qt behaves a bit different from the Qt C++ implementation. - -When the `QMetaObjectConnection` is dropped, it automatically disconnects the connection, similar to how a C++ `std::unique_ptr` or Rusts `Box` behaves. -If you don't want to store the QMetaObjectConnection, call `release`, which will drop the object without disconnecting. -In this case, it is no longer possible to disconnect later. - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/signals.rs:book_signals_disconnect}} -``` - -## Emitting a signal - -Call the function signature defined in the `extern "RustQt"` block to emit the signal. - -Note that these are defined on the generated QObject [`qobject::T`](./generated-qobject.md), so can be called from any mutable `#[qinvokable]`. - -The function will immediately emit the signal. -Depending on the connection type, the connected slots will be called either immediately or from the event loop (See [the different connection types](https://doc.qt.io/qt-6/qt.html#ConnectionType-enum)). -To queue the call until the next cycle of the Qt event loop, you can use the [`CxxQtThread`](./cxxqtthread.md). - -### [Example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/signals.rs) - -```rust,ignore,noplayground -{{#include ../../../examples/qml_features/rust/src/signals.rs:book_macro_code}} -``` diff --git a/book/src/concepts/constructor.md b/book/src/traits/constructor.md similarity index 98% rename from book/src/concepts/constructor.md rename to book/src/traits/constructor.md index c8f84bb35..e6f45c8ae 100644 --- a/book/src/concepts/constructor.md +++ b/book/src/traits/constructor.md @@ -1,4 +1,4 @@ - + By default, CXX-Qt will generate a constructor that takes a single `QObject` pointer as optional argument (usually the QObject parent). It then: @@ -22,7 +26,7 @@ To facilitate these use-cases CXX-Qt provides the [`Constructor` trait][construc ## Implementing & Declaring a Constructor In order for CXX-Qt to generate a custom constructor, the `qobject::T` must implement the [`cxx_qt::Constructor` trait][constructor-trait]. -Additionally, the constructor must be declared within the [cxx_qt::bridge](./bridge.md). +Additionally, the constructor must be declared within the [cxx_qt::bridge](../bridge/index.md). This declaration uses Rust's `impl ... for` syntax, but with a few special rules: * The implementation block must be empty diff --git a/book/src/traits/cxxqttype.md b/book/src/traits/cxxqttype.md new file mode 100644 index 000000000..84ddc1662 --- /dev/null +++ b/book/src/traits/cxxqttype.md @@ -0,0 +1,18 @@ + + +# CxxQtType + + + +The `cxx_qt::CxxQtType` trait is automatically implemented for all types which are marked with a [`#[qobject]`](../bridge/extern_rustqt.md#qobjects) attribute. + +It provides `rust` and `rust_mut` accessors to reach the inner Rust type that is wrapped by the QObject. + +> Note that the `rust_mut` method needs a `Pin<&mut T>` where `T` is the QObject type diff --git a/book/src/traits/index.md b/book/src/traits/index.md new file mode 100644 index 000000000..650018b6e --- /dev/null +++ b/book/src/traits/index.md @@ -0,0 +1,26 @@ + + +# Reference: traits + +Traits can be implemented (or in some cases negated) inside the [`#[cxx_qt::bridge]`](../bridge/index.md) +in a similar way to [explicit shim trait impls](https://cxx.rs/extern-c++.html#explicit-shim-trait-impls) in CXX. + +Except instead of the `T` being a generic it is the struct the trait is implemented for. +This is because some of the traits themselves require generics. + +```rust,ignore +impl UniquePtr {} // explicit CXX trait implementation of UniquePtr for A + +impl cxx_qt::Trait for A {} // explicit CXX-Qt trait implementation of Trait for A +``` + +- [CxxQtType](./cxxqttype.md) - trait to reach the Rust implementation of a QObject +- [Constructor](./constructor.md) - custom constructor +- [Initialize](./initialize.md) - execute Rust code when the object is constructed +- [Locking](./locking.md) - marker trait whether locking is enabled +- [Threading](./threading.md) - marker trait whether CXX-Qt threading should be enabled diff --git a/book/src/traits/initialize.md b/book/src/traits/initialize.md new file mode 100644 index 000000000..536bc4424 --- /dev/null +++ b/book/src/traits/initialize.md @@ -0,0 +1,12 @@ + + +# Initialize + + diff --git a/book/src/traits/locking.md b/book/src/traits/locking.md new file mode 100644 index 000000000..a8d8b192f --- /dev/null +++ b/book/src/traits/locking.md @@ -0,0 +1,23 @@ + + +# Locking + +CXX-Qt locking is enabled by default to ensure that Rust compiler assumptions remain true. +With these assumptions CXX-Qt can then also provide features such as [threading](./threading.md) safely. + +For example the locking prevents C++ from triggering two Rust functions in the same QObject at the same time. + +In certain scenarios it might be useful to disable locking that occurs when the context switches from C++ to Rust. + +To disable the generation of locking use an unsafe negation inside the [`#[cxx_qt::bridge]`](../bridge/index.md). + +```rust,ignore +{{#include ../../../tests/basic_cxx_qt/rust/src/locking.rs:book_disable_locking}} +``` + +> If locking is disabled the [`cxx_qt::Threading`](./threading.md) trait can not be enabled on the object. diff --git a/book/src/qobject/cxxqtthread.md b/book/src/traits/threading.md similarity index 59% rename from book/src/qobject/cxxqtthread.md rename to book/src/traits/threading.md index 6b809d6cd..5d2410849 100644 --- a/book/src/qobject/cxxqtthread.md +++ b/book/src/traits/threading.md @@ -1,5 +1,5 @@ + +The general concept for threading is that when Rust code is being executed a lock has been acquired on the C++ side to prevent Rust code being executed from multiple threads. + +This means that Rust code, such as invokables and properties, which are directly called from C++ are executed on the Qt thread. + +> Note that a recursive mutex is used internally, this allows for signals to be emitted and then call slots on the same object without deadlocks. + +To achieve safe multi-threading on the Rust side we use an `CxxQtThread`. +A `CxxQtThread` represents a reference to the Qt thread that the QObject of type `T` lives in. +When a new Rust thread is started (e.g. in an invokable) the `CxxQtThread` can be moved into the thread to later update the QObject in a thread safe manner. + +When the Rust thread needs to update a value in the QObject it can then queue a closure to the thread. +This closure will be executed on the thread the QObject lives in while holding a lock on the Rust object. +Updating the QObject is then thread-safe. + + + +`CxxQtThread` is used for easy threading with QObjects. The QObjects generated by CXX-Qt are neither [`Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) nor [`Sync`](https://doc.rust-lang.org/std/marker/trait.Sync.html). Therefore they may not be passed between threads nor accessed from multiple threads. @@ -15,7 +35,7 @@ Therefore they may not be passed between threads nor accessed from multiple thre This object is `Send` and can therefore be moved into other threads. It allows you to queue events from a different thread to occur on the thread of the `qobject::T` by using the Qt Event Loop. -First threading needs to be enabled for the [`qobject::T`](./generated-qobject.md) by using `impl cxx_qt::Threading for qobject::T {}`. +First threading needs to be enabled for the QObject by using `impl cxx_qt::Threading for T {}`. ```rust,ignore,noplayground {{#include ../../../examples/qml_features/rust/src/threading.rs:book_qt_thread}} @@ -23,7 +43,7 @@ First threading needs to be enabled for the [`qobject::T`](./generated-qobject.m Note that locking must not be disabled for the object (eg `unsafe impl cxx_qt::Locking for qobject::T`) for `cxx_qt::Threading` to be allowed. -Then to access the `CxxQtThread` use the `qt_thread(&self)` method on a [`qobject::T`](./generated-qobject.md). +Then to access the `CxxQtThread` use the `qt_thread(&self)` method on a QObject. ```rust,ignore,noplayground {{#include ../../../examples/qml_features/rust/src/threading.rs:book_qt_thread}} diff --git a/examples/qml_features/rust/src/custom_base_class.rs b/examples/qml_features/rust/src/custom_base_class.rs index 7b6cfb1a1..cf739f442 100644 --- a/examples/qml_features/rust/src/custom_base_class.rs +++ b/examples/qml_features/rust/src/custom_base_class.rs @@ -11,9 +11,11 @@ pub mod qobject { // ANCHOR: book_base_include unsafe extern "C++" { - include!(< QAbstractListModel >); - // ANCHOR_END: book_base_include + include!(< QtCore/QAbstractListModel >); + } + // ANCHOR_END: book_base_include + unsafe extern "C++" { include!("cxx-qt-lib/qhash.h"); /// QHash from cxx_qt_lib type QHash_i32_QByteArray = cxx_qt_lib::QHash; diff --git a/examples/qml_features/rust/src/empty.rs b/examples/qml_features/rust/src/empty.rs new file mode 100644 index 000000000..e69de29bb diff --git a/examples/qml_features/rust/src/invokables.rs b/examples/qml_features/rust/src/invokables.rs index 8312d5b98..7d29a75a5 100644 --- a/examples/qml_features/rust/src/invokables.rs +++ b/examples/qml_features/rust/src/invokables.rs @@ -53,11 +53,15 @@ pub mod qobject { /// Mutable invokable method with no parameters that resets the color #[qinvokable] fn reset(self: Pin<&mut RustInvokables>); + } + // ANCHOR_END: book_invokable_signature + // ANCHOR: book_cpp_method_signature + unsafe extern "RustQt" { /// C++ only method which returns the red value fn red_value(self: &RustInvokables) -> f32; } - // ANCHOR_END: book_invokable_signature + // ANCHOR_END: book_cpp_method_signature } use core::pin::Pin; @@ -109,12 +113,19 @@ impl qobject::RustInvokables { pub fn reset(self: Pin<&mut Self>) { self.store_helper(0.0, 0.4667, 0.7843); } +} +// ANCHOR_END: book_invokable_impl +// ANCHOR: book_cpp_method_impl +impl qobject::RustInvokables { /// C++ only method which returns the red value pub fn red_value(&self) -> f32 { self.red } +} +// ANCHOR_END: book_cpp_method_impl +impl qobject::RustInvokables { /// Mutable C++ context method that helps to store the color fn store_helper(mut self: Pin<&mut Self>, red: f32, green: f32, blue: f32) { let mut rust_mut = self.as_mut().rust_mut(); @@ -123,7 +134,6 @@ impl qobject::RustInvokables { rust_mut.blue = blue; } } -// ANCHOR_END: book_invokable_impl impl RustInvokablesRust { /// Immutable Rust context method that returns the QColor diff --git a/examples/qml_features/rust/src/properties.rs b/examples/qml_features/rust/src/properties.rs index 3837f3f4c..9bed5c02e 100644 --- a/examples/qml_features/rust/src/properties.rs +++ b/examples/qml_features/rust/src/properties.rs @@ -18,7 +18,7 @@ pub mod qobject { } unsafe extern "RustQt" { - // ANCHOR: book_properties_struct + // ANCHOR: book_properties_signature #[qobject] #[qml_element] #[qproperty(bool, connected)] @@ -26,7 +26,7 @@ pub mod qobject { #[qproperty(QUrl, previous_connected_url)] #[qproperty(QString, status_message)] type RustProperties = super::RustPropertiesRust; - // ANCHOR_END: book_properties_struct + // ANCHOR_END: book_properties_signature /// Connect to the given url #[qinvokable] @@ -55,6 +55,7 @@ use core::pin::Pin; use cxx_qt::CxxQtType; use cxx_qt_lib::{QString, QUrl}; +// ANCHOR: book_properties_struct /// A QObject which has Q_PROPERTYs pub struct RustPropertiesRust { /// A connected Q_PROPERTY @@ -69,6 +70,7 @@ pub struct RustPropertiesRust { /// A status_message Q_PROPERTY status_message: QString, } +// ANCHOR_END: book_properties_struct // ANCHOR: book_properties_default impl Default for RustPropertiesRust { diff --git a/examples/qml_minimal/qml/main.qml b/examples/qml_minimal/qml/main.qml index 4151a7fe4..c3d9f6d28 100644 --- a/examples/qml_minimal/qml/main.qml +++ b/examples/qml_minimal/qml/main.qml @@ -10,8 +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 #[qobject] macro in Rust. +// This must match the uri and version +// specified in the qml_module in the build.rs script. import com.kdab.cxx_qt.demo 1.0 // ANCHOR_END: book_qml_import diff --git a/examples/qml_minimal/rust/build.rs b/examples/qml_minimal/rust/build.rs index 075986f20..cbccb39aa 100644 --- a/examples/qml_minimal/rust/build.rs +++ b/examples/qml_minimal/rust/build.rs @@ -9,14 +9,14 @@ use cxx_qt_build::{CxxQtBuilder, QmlModule}; fn main() { CxxQtBuilder::new() + // ANCHOR: book_qml_module .qml_module(QmlModule { uri: "com.kdab.cxx_qt.demo", rust_files: &["src/cxxqt_object.rs"], - // ANCHOR: book_qml_files qml_files: &["../qml/main.qml"], - // ANCHOR_END: book_qml_files ..Default::default() }) + // ANCHOR_END: book_qml_module .build(); } // ANCHOR_END: book_build_rs diff --git a/examples/qml_minimal/rust/src/cxxqt_object.rs b/examples/qml_minimal/rust/src/cxxqt_object.rs index 9fb7a6a48..9be9646e0 100644 --- a/examples/qml_minimal/rust/src/cxxqt_object.rs +++ b/examples/qml_minimal/rust/src/cxxqt_object.rs @@ -42,8 +42,10 @@ pub mod qobject { // ANCHOR_END: book_rustobj_invokable_signature } +// ANCHOR: book_use use core::pin::Pin; use cxx_qt_lib::QString; +// ANCHOR_END: book_use /// The Rust struct for the QObject // ANCHOR: book_rustobj_struct diff --git a/tests/basic_cxx_qt/rust/src/locking.rs b/tests/basic_cxx_qt/rust/src/locking.rs index e3b8db181..53271b0a0 100644 --- a/tests/basic_cxx_qt/rust/src/locking.rs +++ b/tests/basic_cxx_qt/rust/src/locking.rs @@ -26,7 +26,9 @@ pub mod qobject { fn increment(self: Pin<&mut RustLockingDisabled>); } + // ANCHOR: book_disable_locking unsafe impl !cxx_qt::Locking for RustLockingDisabled {} + // ANCHOR_END: book_disable_locking } use core::pin::Pin;