docs: Polish the C++ docs (#2229)

This commit is contained in:
Tobias Hunger 2023-02-15 15:47:45 +01:00 committed by GitHub
parent 255fe56c94
commit 1a31c9a50b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 289 additions and 197 deletions

View file

@ -1,4 +1,6 @@
# Installing or Building with CMake
<!-- cSpell: ignore ccmake dslint femtovg skia winit -->
# Installing Or Building With CMake
Slint comes with a CMake integration that automates the compilation step of the `.slint` markup language files and
offers a CMake target for convenient linkage.
@ -8,7 +10,8 @@ You can select the CMake Ninja backend by passing `-GNinja` or setting the `CMAK
## Binary Packages
We also provide binary packages of Slint for use with C++, which eliminates the need to have Rust installed in your development environment.
We offer binary packages of Slint for use with C++. These work without any Rust
development environment.
You can download one of our pre-built binaries for Linux or Windows on x86-64 architectures:
@ -16,18 +19,23 @@ You can download one of our pre-built binaries for Linux or Windows on x86-64 ar
2. Click on the latest release
3. From "Assets" download either `slint-cpp-XXX-Linux-x86_64.tar.gz` for a Linux archive
or `slint-cpp-XXX-win64.exe` for a Windows installer. ("XXX" refers to the version of the latest release)
4. Uncompress the downloaded archive or run the installer.
4. Unpack the downloaded archive or run the installer.
After extracting the artifact or running the installer, you can place the `lib` sub-directory into your `CMAKE_PREFIX_PATH` and `find_package(Slint)` should succeed in locating the package.
You also need to place the `lib` sub-directory in the `PATH` envionment variable on Windows, and the `LD_LIBRARY_PATH` on Linux so that
the DLL can be found at runtime.
After extracting the artifact or running the installer, you need to place the
`lib` sub-directory into your `CMAKE_PREFIX_PATH`. `find_package(Slint)` will
then be able to find Slint from within a `CMakeLists.txt` file.
At runtime you might also need to add the `lib` sub-directory to the `PATH`
environment variable on Windows or the `LD_LIBRARY_PATH` on Linux. This is
necessary to find the Slint libraries when trying to run your program.
In the next section you will learn how to use the installed library in your application
and load `.slint` UI files.
and how to work with `.slint` UI files.
## Building from Sources
## Building From Sources
The recommended and most flexible way to use the C++ API is to build Slint from sources.
The recommended and most flexible way to use the C++ API is to build Slint from
sources.
First you need to install the prerequisites:
@ -37,80 +45,101 @@ First you need to install the prerequisites:
* **[cmake](https://cmake.org/download/)** (3.21 or newer)
* A C++ compiler that supports C++20 (e.g., **MSVC 2019 16.6** on Windows)
You can include Slint in your CMake project using CMake's [`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html) feature.
Insert the following snippet into your `CMakeLists.txt` to make CMake download the latest release, compile it and make the CMake integration available:
You can include Slint into your CMake project using CMake's
[`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html)
feature. Insert the following snippet into your `CMakeLists.txt` to make CMake
download the latest released 1.x version, compile it, and make the CMake
integration available:
```cmake
include(FetchContent)
FetchContent_Declare(
Slint
GIT_REPOSITORY https://github.com/slint-ui/slint.git
GIT_TAG release/0.3
GIT_TAG release/1
SOURCE_SUBDIR api/cpp
)
FetchContent_MakeAvailable(Slint)
```
If you prefer to treat Slint as an external CMake package, then you can also build Slint from source like a regular
If you prefer to use Slint as an external CMake package, then you build Slint from source like a regular
CMake project, install it into a prefix directory of your choice and use `find_package(Slint)` in your `CMakeLists.txt`.
### Features
The Slint run-time library supports different features that can be toggled. You might want to enable a feature that is
not enabled by default but that is revelant for you, or you may want to disable a feature that you know you do not need and
therefore reduce the size of the resulting library.
The Slint library supports a set of features, not all of them enabled by default.
You might want to adapt the set of enabled features to optimize your binary
size. For example you might want to support only the wayland stack on Linux.
Enable the `backend-winit-wayland` feature while turning off the
`backend-winit-x11` feature to do so.
The CMake configure step offers CMake options for various feature that are all prefixed with `SLINT_FEATURE_`. For example
you can make a build that exclusively supports Wayland on Linux by enabling the `SLINT_FEATURE_BACKEND_WINIT_WAYLAND` feature and turning
off `SLINT_FEATURE_BACKEND_WINIT`. There are different ways of toggling CMake options. For example on the command line using the `-D` parameter:
Slint's CMake configuration uses CMake options prefixed with `SLINT_FEATURE_` to
expose Slint's feature flags at compile time. To have a wayland-only stack with
the CMake setup you would for example use:
`cmake -DSLINT_FEATURE_BACKEND_WINIT=OFF -DSLINT_FEATURE_BACKEND_WINIT_WAYLAND=ON ...`
Alternatively, after the configure step you can use `cmake-gui` or `ccmake` on the build directory for a list of all features
and their description.
Alternatively, you can use `cmake-gui` or `ccmake` for a more interactive way
to discover and toggle features.
This works when compiling Slint as a package, using `cmake --build` and `cmake --install`, or when including Slint
using `FetchContent`.
This works when compiling Slint as a package, using `cmake --build` and
`cmake --install`, or when including Slint using `FetchContent`.
### Backends
### Back-Ends
Slint needs a backend that will act as liaison between Slint and the OS.
By default, Slint will use the Qt backend, if Qt is installed, otherwise, it
will use [Winit](https://crates.io/crates/winit) with [Femtovg](https://crates.io/crates/femtovg).
Both backends are compiled in. If you want to not compile one of these you need
to disable the `SLINT_FEATURE_BACKEND_WINIT` and `SLINT_FEATURE_RENDERER_WINIT_FEMTOVG` features and enable
the backend and renderer features you choose.
Slint needs a back-end that acts as liaison between Slint and the OS. Several
back-ends can be built into the Slint library at the same time, but only one
is used a run time.
If you enable the Winit backend, you need to also include a renderer.
`SLINT_FEATURE_RENDERER_WINIT_FEMTOVG` is the only stable renderer, the other ones are experimental
It is also possible to select the backend and renderer at runtime when several
are enabled, using the `SLINT_BACKEND` environment variable.
* `SLINT_BACKEND=Qt` selects the Qt backend
* `SLINT_BACKEND=winit` selects the winit backend
* `SLINT_BACKEND=winit-femtovg` selects the winit backend with the femtovg renderer
* `SLINT_BACKEND=winit-skia` selects the winit backend with the skia renderer
* `SLINT_BACKEND=winit-software` selects the winit backend with the software renderer
If the selected backend is not available, the default will be used.
#### Compile Time Back-End Selection
By default Slint will include both the Qt and
[winit](https://crates.io/crates/winit) back-ends -- if both are detected at
compile time. You can enable or disable back-ends using the
`SLINT_FEATURE_BACKEND_` features. For example, to exclude the winit back-end,
you would disable the `SLINT_FEATURE_BACKEND_WINIT` option in your CMake
project configuration.
The winit back-end needs a renderer. `SLINT_FEATURE_RENDERER_WINIT_FEMTOVG` is
the only stable renderer, the other ones are experimental. If you disable the
`SLINT_FEATURE_BACKEND_WINIT`, you will also want to disable the renderer!
#### Run Time Back-End Selection
It's also possible to select any of the compiled in back-ends and renderer at
runtime, using the `SLINT_BACKEND` environment variable.
* `SLINT_BACKEND=Qt` selects the Qt back-end
* `SLINT_BACKEND=winit` selects the winit back-end
* `SLINT_BACKEND=winit-femtovg` selects the winit back-end with the femtovg renderer
* `SLINT_BACKEND=winit-skia` selects the winit back-end with the skia renderer
* `SLINT_BACKEND=winit-software` selects the winit back-end with the software renderer
If the selected back-end or renderer isn't available, the default will be used
instead.
### Cross-compiling
It is possible to cross-compile Slint to a different target architecture when building with CMake. In order to complete
that, you need to make sure that your CMake setup is ready for cross-compilation. You can find more information about
how to set this up in the [upstream CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling).
It's possible to cross-compile Slint to a different target architecture when
building with CMake. You need to make sure your CMake setup is ready for
cross-compilation, as documented in the [upstream CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling).
If you are building against a Yocto SDK, it is sufficient to source the SDK's environment setup file.
Since Slint is implemented using the Rust programming language, you need to determine which Rust target
matches the target architecture that you're compiling to. Please consult the [upstream Rust documentation](https://doc.rust-lang.org/nightly/rustc/platform-support.html) to find the correct target name. Now you need to install the Rust toolchain:
Since Slint is implemented using the Rust programming language, you need to
determine which Rust target matches the target architecture that you're
compiling for. Please consult the [upstream Rust documentation](https://doc.rust-lang.org/nightly/rustc/platform-support.html) to find the correct target name. Now you need to install the Rust toolchain:
```sh
rustup target add <target-name>
```
Then you're ready to invoke CMake and you need to add `-DRust_CARGO_TARGET=<target name>` to the CMake command line.
Then you're ready to iconfigure your CMake project you need to add
`-DRust_CARGO_TARGET=<target name>` to the CMake command line.
This ensures that the Slint library is built for the correct architecture.
For example if you are building against an embedded Linux Yocto SDK targeting an ARM64 board, the following commands
show how to compile:
For example if you are building against an embedded Linux Yocto SDK targeting
an ARM64 board, the following commands show how to compile:
Install the Rust targe toolchain once:
@ -125,10 +154,10 @@ Set up the environment and build:
<!-- cSpell:disable -->
```sh
. /path/to/yocto/sdk/environment-setup-cortexa53-crypto-poky-linux
cd slint
cd <PROJECT_ROOT>
mkdir build
cd build
cmake -DRust_CARGO_TARGET=aarch64-unknown-linux-gnu -DCMAKE_INSTALL_PREFIX=/slint/install/path ..
cmake -DRust_CARGO_TARGET=aarch64-unknown-linux-gnu -DCMAKE_INSTALL_PREFIX=/slint/install/path ...
cmake --build .
cmake --install .
```

View file

@ -1,39 +1,47 @@
# Generated code
The Slint compiler called by the build system will generate a header file for the root `.slint`
file. This header file will contain a `class` with the same name as the component.
file. This header file will contain a `class` with the same name as the root
component.
This class will have the following public member functions:
* A `create` constructor function and a destructor.
* A `show` function, which will show the component on the screen. Note that in order to render
and react to user input, it's still necessary to spin the event loop, by calling {cpp:func}`slint::run_event_loop()`
or using the convenience `fun` function in this class.
* A `show` function, which will show the component on the screen.
You still need to spin the event loop by {cpp:func}`slint::run_event_loop()`
or using the convenience `run` function in this class to render and react to
user input!
* A `hide` function, which de-registers the component from the windowing system.
* A `window` function that provides access to the {cpp:class}`slint::Window`, allow for further customization
towards the windowing system.
* A `run` convenience function, which will show the component and starts the event loop.
* for each properties:
* A `window` function that provides access to the {cpp:class}`slint::Window`,
to allow for further customization towards the windowing system.
* A `run` convenience function, which will show the component and starts the
event loop.
* For each property:
* A getter `get_<property_name>` returning the property type.
* A setter `set_<property_name>` taking the new value of the property by const reference
* for each callbacks:
* A setter `set_<property_name>` taking the new value of the property by
const reference
* For each callback:
* `invoke_<callback_name>` function which takes the callback argument as parameter and call the callback.
* `on_<callback_name>` function which takes a functor as an argument and sets the callback handler
for this callback. the functor must accept the type parameter of the callback
* A `global` function, to provide access to any exported global singletons.
* A `global` function to access exported global singletons.
The class is instantiated with the `create` function, which returns the type wrapped in {cpp:class}`slint::ComponentHandle`.
This is a smart pointer that owns the actual instance and keeps it alive as long as at least one {cpp:class}`slint::ComponentHandle`
is in scope, similar to `std::shared_ptr<T>`.
The `create` function creates a new instance of the component, which is wrapped
in {cpp:class}`slint::ComponentHandle`. This is a smart pointer that owns the
actual instance and keeps it alive as long as at least one
{cpp:class}`slint::ComponentHandle` is in scope, similar to `std::shared_ptr<T>`.
For more complex UIs it is common to supply data in the form of an abstract data model, that is used with
[`for` - `in`](markdown/langref.md#repetition) repetitions or [`ListView`](markdown/widgets.md#listview) elements in the `.slint` language.
All models in C++ are sub-classes of the {cpp:class}`slint::Model` and you can sub-class it yourself. For convenience,
the {cpp:class}`slint::VectorModel` provides an implementation that is backed by a `std::vector<T>`.
For more complex user interfaces it's common to supply data in the form of an
abstract data model, that is used with [`for` - `in`](../slint/repetition.html)
repetitions or [`ListView`](../slint/widgets.md#listview) elements in the
`.slint` language. All models in C++ are sub-classes of the
{cpp:class}`slint::Model` and you can sub-class it yourself. For convenience,
the {cpp:class}`slint::VectorModel` provides an implementation that's backed
by a `std::vector<T>`.
## Example
Let's assume we have this code in our `.slint` file
Let's assume we've this code in our `.slint` file:
```slint,no-preview
SampleComponent := Window {
@ -44,7 +52,7 @@ SampleComponent := Window {
}
```
This will generate a header with the following contents (edited for documentation purpose)
This generates a header with the following contents (edited for documentation purpose)
```cpp
#include <array>
@ -98,10 +106,11 @@ private:
## Global Singletons
In `.slint` files it is possible to declare [singletons that are globally available](markdown/langref.md#global-singletons).
You can access them from to your C++ code by exporting them and using the `global()` getter function in the
C++ class generated for your entry component. Each global singleton creates a class that has getter/setter functions
for properties and callbacks, similar to API that's created for your `.slint` component, as demonstrated in the previous section.
You can declare [globally available singletons](../slint/globals.html) in your
`.slint` files. If exported, these singletons are available via the
`global()` getter function on the generated C++ class. Each global singleton
maps to a class iwith getter/setter functions for properties and callbacks,
similar to API that's created for your `.slint` component.
For example the following `.slint` markup defines a global `Logic` singleton that's also exported:
@ -111,8 +120,8 @@ export global Logic := {
}
```
If this were used together with the `SampleComponent` from the previous section, then you can access it
like this:
Assuming this global is used together with the `SampleComponent` from the
previous section, you can access `Logic` like this:
```cpp
auto app = SampleComponent::create();

View file

@ -1,17 +1,21 @@
# Getting Started
Once Slint is built, you can use it in your CMake application or library target in two steps:
Once Slint is built, you can use it in your CMake application or library
target in two steps:
1. Associate the `.slint` files that you'd like to use by calling the `slint_target_sources` cmake command. The first parameter is
your application (or library) CMake target, and the parameters following are the names of the `.slint` files. This will result in the
`.slint` files to be compiled into C++ source code.
2. The generated C++ source code also needs the Slint run-time library. This dependency is satisfied by linking `Slint::Slint`
into your target with the `target_link_libraries` command.
1. Associate the `.slint` files that you'd like to use by calling the
`slint_target_sources` CMake command. The first parameter is
your application (or library) build target, and the parameters following are
the names of the `.slint` files you want to include. This will compile
the `.slint` files to C++ source code and included that into your
built target.
2. The generated C++ source code needs the Slint run-time library. Use
`target_link_libraries` to link your build target to `Slint::Slint`.
A typical example looks like this:
A minimal CMake `CMakeLists.txt` file looks like this:
```cmake
cmake_minimum_required(VERSION 3.19)
cmake_minimum_required(VERSION 3.21)
project(my_application LANGUAGES CXX)
# Note: Use find_package(Slint) instead of the following three commands,
@ -20,7 +24,7 @@ include(FetchContent)
FetchContent_Declare(
Slint
GIT_REPOSITORY https://github.com/slint-ui/slint.git
GIT_TAG release/0.3
GIT_TAG release/1
SOURCE_SUBDIR api/cpp
)
FetchContent_MakeAvailable(Slint)
@ -53,7 +57,7 @@ export component HelloWorld inherits Window {
}
```
then you can use the following code in you `main` function to show the [`Window`](markdown/builtin_elements.md#window)
then you can use the following code in you `main` function to show the [`Window`](../slint/builtin_elements.html#window)
and change the text:
```cpp
@ -70,16 +74,20 @@ int main(int argc, char **argv)
```
This works because the Slint compiler translated `my_application_ui.slint` to C++ code, in the `my_application_ui.h`
header file. That generated code has a C++ class that corresponds to the `HelloWorld` element and has API to create
the ui, read or write properties or set callbacks. You can learn more about how this API looks like in general in the
header file. That generated code contains a C++ class that corresponds to the `HelloWorld` element and has API to create
the UI, read or write properties, and set callbacks. You can learn more about how this API looks like in general in the
[](generated_code.md) section.
## Tutorial
For an in-depth walk-through, you may be interested in reading our walk-through <a href="../tutorial/cpp">Slint Memory Game Tutorial Tutorial</a>.
It will guide you through the `.slint` mark-up language and the C++ API by building a little memory game.
For an in-depth walk-through, read our <a href="../tutorial/cpp">Slint Memory Game Tutorial</a>.
It will guide you through the `.slint` mark-up language and the C++ API by building a simple memory
game.
## Template
You can clone the [Template Repository](https://github.com/slint-ui/slint-cpp-template) repository with
the code of a minimal C++ application using Slint that can be used as a starting point to your program.
the code of a minimal C++ application using Slint. It provides a convenient starting point to a new program.
Of course you can also read on: We will cover some recipes to handle common
use-cases next.

View file

@ -1,54 +1,70 @@
# Overview
The following two sections explain how you can integrate your `.slint` designs into your
C++ application. The entry point is a `.slint` file that contains your primary component
that you instantiate from C++.
The following sections explain how to integrate your `.slint` designs into your
C++ application. The entry point is the `.slint` file containing the primary
component you need to instantiate from C++.
There are two ways in that you can instantiate your `.slint` designs in your C++ application,
either by compiling them ahead of time or by dynamically loading them at run-time.
Slint is a very flexible system and allows for different integration options.
Once instantiated you feed data into it, for example by setting properties, populating
data models or setting up callbacks that are invoked when the user activates certain elements.
First you can compile your Slint designs ahead of time into C++ code. This
code is then built into your application. This allows for the smallest
possible memory footprint and the best possible performance.
The second approach is to load your Slint designs at run-time, interpreting
them as needed. This enables even more dynamic user interfaces that can be
changed at run-time, but comes at the price of having less opportunity to apply
optimizations.
## Compiled `.slint` designs
Either way, once your user interface is shown, you interact with it from C++,
for example by setting properties, populating data models or setting up and
handling callbacks to react to events triggered by the user.
You can choose to compile a `.slint` file to C++, which provides the best performance
and lowest memory consumption.
## Compiled `.slint` Designs
The `slint_target_sources` cmake command makes the translation automatic
and [generated code](generated_code.md) has an API that allows setting and getting
property values, etc. That API will use types from the {ref}`slint <namespace_slint>`
namespace, for example {cpp:class}`slint::SharedString` or {cpp:class}`slint::Color`.
The provided CMake integration makes it easy to compile your Slint sources:
The `slint_target_sources` CMake command makes the translation automatic. The
[generated code](generated_code.md) has an API to set and get property values,
etc. This API uses types from the {ref}`slint <namespace_slint>` namespace, for
example {cpp:class}`slint::SharedString` or {cpp:class}`slint::Color`.
## Run-time interpreted `.slint` designs
## Run-Time Interpreted `.slint` Designs
Instead of compiling `.slint` designs to C++, you can also choose to dynamically load `.slint`
files at run-time. This is slower than compiling them ahead of time and requires more memory,
however it provides more flexibility in your application design.
Instead of compiling `.slint` designs to C++, you can dynamically load `.slint`
files at run-time. This is slower than compiling them ahead of time and requires
more memory, however it provides more flexibility in your application design.
The entry point to loading a `.slint` file is the {cpp:class}`slint::interpreter::ComponentCompiler`
class in the {ref}`slint::interpreter <namespace_slint__interpreter>` namespace.
The entry point to loading a `.slint` file is the
{cpp:class}`slint::interpreter::ComponentCompiler` class in the
{ref}`slint::interpreter <namespace_slint__interpreter>` namespace.
With the help of {cpp:class}`slint::interpreter::ComponentCompiler` you create a {cpp:class}`slint::interpreter::ComponentDefinition`,
which provides you with information about properties and callbacks that are common to all instances. The
{cpp:func}`slint::interpreter::ComponentDefinition::create()` function creates new instances, which
are wrapped in {cpp:class}`slint::ComponentHandle`. This is a smart pointer that owns the actual instance
and keeps it alive as long as at least one {cpp:class}`slint::ComponentHandle` is in scope, similar to `std::shared_ptr<T>`.
With the help of {cpp:class}`slint::interpreter::ComponentCompiler` you create
a {cpp:class}`slint::interpreter::ComponentDefinition`, which provides
information on properties and callbacks common to all instances. The
{cpp:func}`slint::interpreter::ComponentDefinition::create()` function creates
new instances, wrapped in a {cpp:class}`slint::ComponentHandle`. This is a smart
pointer that owns the actual instance and keeps it alive as long as at least one
{cpp:class}`slint::ComponentHandle` is in scope, similar to
`std::shared_ptr<T>`.
All property values in `.slint` are mapped to {cpp:class}`slint::interpreter::Value` in C++. This is a
polymorphic data type that can hold different kinds of values, such as numbers, strings or even data models.
All property values in `.slint` are mapped to
{cpp:class}`slint::interpreter::Value` in C++. This is a polymorphic data type
that can hold different kinds of values, such as numbers, strings or even data
models.
For more complex UIs it is common to supply data in the form of an abstract data model, that is used with
[`for` - `in`](markdown/langref.md#repetition) repetitions or [`ListView`](markdown/widgets.md#listview) elements in the `.slint` language.
All models in C++ with the interpreter API are sub-classes of the {cpp:class}`slint::Model` where the template
parameter is {cpp:class}`slint::interpreter::Value`. Therefore to provide your own data model, you can subclass
`slint::Model<slint::interpreter::Value>`.
More complex user interfaces commonly consume data in the form of an abstract
data model, that is used with [`for` - `in`](markdown/langref.md#repetition)
repetitions or [`ListView`](markdown/widgets.md#listview) elements in the
`.slint` language. All models in C++ with the interpreter API are sub-classes
of the {cpp:class}`slint::Model` where the template parameter is
{cpp:class}`slint::interpreter::Value`. To provide your own data model, you can
subclass `slint::Model<slint::interpreter::Value>`.
In `.slint` files it is possible to declare [singletons that are globally available](markdown/langref.md#global-singletons).
You can access them from to your C++ code by exporting them and using the getter and setter functions on
{cpp:class}`slint::interpreter::ComponentInstance` to change properties and callbacks:
It's possible to declare [singletons that are globally available](markdown/langref.md#global-singletons)
in `.slint` files. You can access them from to your C++ code by exporting them
and using the getter and setter functions on
{cpp:class}`slint::interpreter::ComponentInstance` to change properties and
callbacks:
1. {cpp:func}`slint::interpreter::ComponentInstance::set_global_property()`
1. {cpp:func}`slint::interpreter::ComponentInstance::get_global_property()`

View file

@ -24,10 +24,11 @@ The follow table summarizes the entire mapping:
```
## Structures
For user-defined structures in the .slint code, a `class` of the same name is generated with data member
in lexicographic order.
The Slint compiler generates a `class` with all data members in
lexicographic order for any user-defined, exported `struct` in the `.slint`
code.
For example, if you have this structure in the .slint file
For example, this `struct` in a `.slint` file
```slint,ignore
export struct MyStruct := {
@ -36,7 +37,7 @@ export struct MyStruct := {
}
```
It would result in the following type being generated:
will generate the following type in C++:
```cpp
class MyStruct {