mirror of
https://github.com/slint-ui/slint.git
synced 2025-11-01 20:31:27 +00:00
docs: Rework the rust part of the documentation (#2240)
Co-authored-by: Simon Hausmann <hausmann@gmail.com>
This commit is contained in:
parent
daab51543f
commit
96f75bdd0c
3 changed files with 71 additions and 47 deletions
|
|
@ -32,7 +32,7 @@ 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>`.
|
{cpp:class}`slint::ComponentHandle` is in scope, similar to `std::shared_ptr<T>`.
|
||||||
|
|
||||||
For more complex user interfaces it's common to supply data in the form of an
|
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)
|
abstract data model, that's used with [`for` - `in`](../slint/repetition.html)
|
||||||
repetitions or [`ListView`](../slint/widgets.md#listview) elements in the
|
repetitions or [`ListView`](../slint/widgets.md#listview) elements in the
|
||||||
`.slint` language. All models in C++ are sub-classes of 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,
|
{cpp:class}`slint::Model` and you can sub-class it yourself. For convenience,
|
||||||
|
|
@ -44,9 +44,9 @@ by a `std::vector<T>`.
|
||||||
Let's assume we've this code in our `.slint` file:
|
Let's assume we've this code in our `.slint` file:
|
||||||
|
|
||||||
```slint,no-preview
|
```slint,no-preview
|
||||||
SampleComponent := Window {
|
component SampleComponent inherits Window {
|
||||||
property<int> counter;
|
in-out property<int> counter;
|
||||||
property<string> user_name;
|
in-out property<string> user_name;
|
||||||
callback hello;
|
callback hello;
|
||||||
// ... maybe more elements here
|
// ... maybe more elements here
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
of what is generated from the `.slint` file
|
of what is generated from the `.slint` file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// cSpell: ignore rustdoc
|
||||||
|
|
||||||
pub mod recipes {
|
pub mod recipes {
|
||||||
#![doc = include_str!("docs/recipes/recipes.md")]
|
#![doc = include_str!("docs/recipes/recipes.md")]
|
||||||
|
|
||||||
|
|
@ -30,10 +32,10 @@ pub mod generated_code {
|
||||||
/// what functions you can call and how you can pass data in and out.
|
/// what functions you can call and how you can pass data in and out.
|
||||||
/// This is the source code:
|
/// This is the source code:
|
||||||
/// ```slint
|
/// ```slint
|
||||||
/// SampleComponent := Window {
|
/// export component SampleComponent inherits Window {
|
||||||
/// property<int> counter;
|
/// in-out property<int> counter;
|
||||||
/// // note that dashes will be replaced by underscores in the generated code
|
/// // note that dashes will be replaced by underscores in the generated code
|
||||||
/// property<string> user-name;
|
/// in-out property<string> user-name;
|
||||||
/// callback hello();
|
/// callback hello();
|
||||||
/// // ... maybe more elements here
|
/// // ... maybe more elements here
|
||||||
/// }
|
/// }
|
||||||
|
|
@ -62,6 +64,7 @@ pub mod generated_code {
|
||||||
}
|
}
|
||||||
/// Assigns a new value to the `user_name` property.
|
/// Assigns a new value to the `user_name` property.
|
||||||
pub fn set_user_name(&self, value: crate::SharedString) {}
|
pub fn set_user_name(&self, value: crate::SharedString) {}
|
||||||
|
|
||||||
/// For each callback declared at the root of the component, a function to call that
|
/// For each callback declared at the root of the component, a function to call that
|
||||||
/// callback is generated. This is the function that calls the `hello` callback declared
|
/// callback is generated. This is the function that calls the `hello` callback declared
|
||||||
/// in the `.slint` design markup.
|
/// in the `.slint` design markup.
|
||||||
|
|
|
||||||
|
|
@ -13,36 +13,29 @@ Writing an application in Rust that runs on a MCU requires several prerequisites
|
||||||
* Install tools for flashing and debugging your code on the device.
|
* Install tools for flashing and debugging your code on the device.
|
||||||
|
|
||||||
We recommend reading the [Rust Embedded Book](https://docs.rust-embedded.org/book/),
|
We recommend reading the [Rust Embedded Book](https://docs.rust-embedded.org/book/),
|
||||||
as well as the curated list of [Awesome Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust) for a wide range of different
|
and the curated list of [Awesome Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust) for a wide range of
|
||||||
crates, tools and training materials. These resources should guide you through the initial setup and often come with "hello world" examples
|
crates, tools, and training materials. These resources should guide you through the initial setup. Many include a "hello world" example
|
||||||
to get started with your device.
|
to get started with your device.
|
||||||
|
|
||||||
Slint requires a global memory allocator. That is currently only possible in the nightly version of Rust, because the support for using a custom global
|
Slint requires a custom global memory allocator in a bare metal environment with `#![no_std]`.
|
||||||
allocator in a bare metal environment with `#![no_std]` has not been stabilized yet (see [#51540](https://github.com/rust-lang/rust/issues/51540) or
|
The Rust team hasn't yet stabilized this feature, so only the nightly version
|
||||||
[#66741](https://github.com/rust-lang/rust/issues/66741) for tracking issues).
|
of the Rust compiler will support it. See
|
||||||
|
[#51540](https://github.com/rust-lang/rust/issues/51540) or
|
||||||
|
[#66741](https://github.com/rust-lang/rust/issues/66741) for tracking issues.
|
||||||
|
|
||||||
|
Note: The rust compiler version 1.68 should have all features needed by Slint
|
||||||
|
stabilized. We have not tested this setup extensively yet though.
|
||||||
|
|
||||||
The following sections assume that your setup is complete and you have a non-graphical skeleton Rust program running on your MCU.
|
The following sections assume that your setup is complete and you have a non-graphical skeleton Rust program running on your MCU.
|
||||||
|
|
||||||
## Changes to `Cargo.toml`
|
## Changes to `Cargo.toml`
|
||||||
|
|
||||||
Start by adding a dependency to the `slint` and the `slint-build` crates to your `Cargo.toml`:
|
Start by adding a dependency to the `slint` and the `slint-build` crates to your `Cargo.toml` using the `cargo` command:
|
||||||
|
|
||||||
```toml
|
Start with the `slint` crate like this:
|
||||||
[package]
|
|
||||||
## ...
|
|
||||||
## Edition 2021 or later enables the feature resolver version 2.
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
```sh
|
||||||
## ... your other dependencies
|
cargo add slint@1.0.0 --no-default-features --features "compat-1-0 unsafe-single-threaded libm"
|
||||||
|
|
||||||
[dependencies.slint]
|
|
||||||
version = "0.3.4"
|
|
||||||
default-features = false
|
|
||||||
features = ["compat-1-0", "unsafe-single-threaded", "libm"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
slint-build = "0.3.4"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The default features of the `slint` crate are tailored towards hosted environments and includes the "std" feature. In bare metal environments,
|
The default features of the `slint` crate are tailored towards hosted environments and includes the "std" feature. In bare metal environments,
|
||||||
|
|
@ -55,12 +48,38 @@ In the snippet above, three features are selected:
|
||||||
This macro is only available in the Rust Standard Library (std), but not in bare metal environments. As a fallback, the `unsafe-single-threaded`
|
This macro is only available in the Rust Standard Library (std), but not in bare metal environments. As a fallback, the `unsafe-single-threaded`
|
||||||
feature changes Slint to use unsafe static for storage. This way, you guarantee to use Slint API only from a single thread, and not from interrupt handlers.
|
feature changes Slint to use unsafe static for storage. This way, you guarantee to use Slint API only from a single thread, and not from interrupt handlers.
|
||||||
* `libm`: We select this feature to enable the use of the [libm](https://crates.io/crates/libm) crate to provide traits and functions for floating point arithmetic.
|
* `libm`: We select this feature to enable the use of the [libm](https://crates.io/crates/libm) crate to provide traits and functions for floating point arithmetic.
|
||||||
They are typically provided by the Rust Standard Library (std), but that is not available in bare metal environments.
|
They're typically provided by the Rust Standard Library (std), but that's not available in bare metal environments.
|
||||||
|
|
||||||
It might be necessary to enable the [Feature resolver version 2](https://doc.rust-lang.org/cargo/reference/features.html#feature-resolver-version-2)
|
It might be necessary to enable the [Feature resolver version 2](https://doc.rust-lang.org/cargo/reference/features.html#feature-resolver-version-2)
|
||||||
in your Cargo.toml if you notice that your dependencies are attempting to build with `std` support even if it was disabled.
|
in your Cargo.toml if you notice that your dependencies are attempting to build with `std` support even when disabled.
|
||||||
This is the default when using the Rust 2021 Edition or later.
|
This is the default when using the Rust 2021 Edition or later.
|
||||||
|
|
||||||
|
Then add the `slint-build` crate as a build dependency:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo add --build slint-build@1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
For reference: These are the relevant parts of your `Cargo.toml` file,
|
||||||
|
ready to use Slint:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
## ...
|
||||||
|
## Edition 2021 or later enables the feature resolver version 2.
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
## ... your other dependencies
|
||||||
|
|
||||||
|
[dependencies.slint]
|
||||||
|
version = "1.0.0"
|
||||||
|
default-features = false
|
||||||
|
features = ["compat-1-0", "unsafe-single-threaded", "libm"]
|
||||||
|
[build-dependencies]
|
||||||
|
slint-build = "1.0.0"
|
||||||
|
```
|
||||||
|
|
||||||
## Changes to `build.rs`
|
## Changes to `build.rs`
|
||||||
|
|
||||||
Next, write a build script to compile the `.slint` files to Rust code for embedding into the program binary, using the `slint-build` crate:
|
Next, write a build script to compile the `.slint` files to Rust code for embedding into the program binary, using the `slint-build` crate:
|
||||||
|
|
@ -80,23 +99,23 @@ in a format that's suitable for the software based renderer we're going to use.
|
||||||
|
|
||||||
## Application Structure
|
## Application Structure
|
||||||
|
|
||||||
Typically, a graphical application in hosted environments is comprised of at least three different tasks:
|
Typically, a graphical application in hosted environments has at least three different tasks:
|
||||||
|
|
||||||
* Receives user input from operation system APIs.
|
* Receives user input from operation system APIs.
|
||||||
* Reacts to the input by performing application specific computations.
|
* Reacts to the input by performing application specific computations.
|
||||||
* Renders an updated user interface and presents it on the screen using device-independent operating system APIs.
|
* Renders an updated user interface and presents it on the screen using device-independent operating system APIs.
|
||||||
|
|
||||||
The operating system provides an event loop to connect and schedule these tasks. Slint implements the
|
The operating system provides an event loop to connect and schedule these tasks. Slint implements the
|
||||||
task of receiving user input and forwarding it to the user interface layer, as well as rendering the user interface to the screen.
|
task of receiving user input and forwarding it to the user interface layer, and rendering the user interface to the screen.
|
||||||
|
|
||||||
In bare metal environments it is your responsibility to substitute and connect functionality that is otherwise provided by the operating system:
|
In bare metal environments it's your responsibility to substitute and connect functionality that's otherwise provided by the operating system:
|
||||||
|
|
||||||
* Select crates that allow you to initialize the chips that operate peripherals, such as a touch input or display controller.
|
* Select crates that allow you to initialize the chips that operate peripherals, such as a touch input or display controller.
|
||||||
If there are no crates, you may have to to develop your own drivers.
|
If there are no crates, you may have to to develop your own drivers.
|
||||||
* Drive the event loop yourself by querying peripherals for input, forwarding that input into computational modules of your
|
* Drive the event loop yourself by querying peripherals for input, forwarding that input into computational modules of your
|
||||||
application and instructing Slint to render the user interface.
|
application and instructing Slint to render the user interface.
|
||||||
|
|
||||||
In Slint, the two primary APIs you need to use to accomplish these tasks are the [`slint::platform::Platform`] trait as well as the [`slint::Window`] struct.
|
In Slint, the two primary APIs you need to use to accomplish these tasks are the [`slint::platform::Platform`] trait and the [`slint::Window`] struct.
|
||||||
In the following sections we're going to cover how to use them and how they integrate into your event loop.
|
In the following sections we're going to cover how to use them and how they integrate into your event loop.
|
||||||
|
|
||||||
### The `Platform` Trait
|
### The `Platform` Trait
|
||||||
|
|
@ -114,8 +133,8 @@ This minimal implementation needs to cover two functions:
|
||||||
how much time has elapsed between two rendered frames. In a bare metal environment you need to provide a source of time. Often the HAL crate of your
|
how much time has elapsed between two rendered frames. In a bare metal environment you need to provide a source of time. Often the HAL crate of your
|
||||||
device provides a system timer API for this, which you can query in your impementation.
|
device provides a system timer API for this, which you can query in your impementation.
|
||||||
|
|
||||||
There are additional functions in the trait that you can implement, for example to handle debug output, to delegate the event loop or to implement
|
You may override more functions of this trait, for example to handle debug output, to delegate the event loop,
|
||||||
the interface to safely deliver events in multi-threaded environments.
|
or to deliver events in multi-threaded environments.
|
||||||
|
|
||||||
A typical minimal implementation of the [`Platform`] trait that uses the [`MinimalSoftwareWindow`] looks like this:
|
A typical minimal implementation of the [`Platform`] trait that uses the [`MinimalSoftwareWindow`] looks like this:
|
||||||
|
|
||||||
|
|
@ -233,13 +252,13 @@ loop {
|
||||||
### The Renderer
|
### The Renderer
|
||||||
|
|
||||||
In desktop and embedded environments, Slint typically uses operating system provided APIs to render the user interface using the GPU.
|
In desktop and embedded environments, Slint typically uses operating system provided APIs to render the user interface using the GPU.
|
||||||
In contrast, most MCUs don't have GPUs. Instead, the all the rendering is done by software on the CPU. This is called software rendering, and Slint provides a SoftwareRenderer for this task.
|
In contrast, most MCUs don't have GPUs. Instead, software rendering is used where all rendering is done by software on the CPU.
|
||||||
|
Slint provides a SoftwareRenderer for this task.
|
||||||
|
|
||||||
In the previous example, we've instantiated a [`slint::platform::software_renderer::MinimalSoftwareWindow`]. This struct implements the
|
In the earlier example, we've instantiated a [`slint::platform::software_renderer::MinimalSoftwareWindow`]. This struct implements the
|
||||||
`slint::platform::WindowAdapter` trait and also holds an instance of a [`slint::platform::software_renderer::SoftwareRenderer`]. You obtain access to it
|
`slint::platform::WindowAdapter` trait and also holds an instance of a [`slint::platform::software_renderer::SoftwareRenderer`]. You access it
|
||||||
through the callback parameter of the [`draw_if_needed()`](MinimalSoftwareWindow::draw_if_needed) function.
|
through the callback parameter of the [`draw_if_needed()`](MinimalSoftwareWindow::draw_if_needed) function.
|
||||||
|
Depending on the amount of RAM your MCU has, and the kind of screen attached, you can choose between two different ways of using the renderer:
|
||||||
Depending on the amount of RAM your MCU is equipped with, and the kind of screen that is attached, you can choose between two different ways of using the renderer:
|
|
||||||
|
|
||||||
* Use the [`SoftwareRenderer::render()`] function if you have enough RAM to allocate one, or even two, copies of the entire screen (also known as
|
* Use the [`SoftwareRenderer::render()`] function if you have enough RAM to allocate one, or even two, copies of the entire screen (also known as
|
||||||
frame buffer).
|
frame buffer).
|
||||||
|
|
@ -247,14 +266,16 @@ Depending on the amount of RAM your MCU is equipped with, and the kind of screen
|
||||||
typically via the SPI. This requires allocating at least enough RAM to store one single line of pixels.
|
typically via the SPI. This requires allocating at least enough RAM to store one single line of pixels.
|
||||||
|
|
||||||
With both methods Slint renders into a provided buffer, which is a slice of a type that implements the [`slint::platform::software_renderer::TargetPixel`] trait.
|
With both methods Slint renders into a provided buffer, which is a slice of a type that implements the [`slint::platform::software_renderer::TargetPixel`] trait.
|
||||||
For convenience, Slint provides an implementation for [`slint::Rgb8Pixel`] as well as [`slint::platform::software_renderer::Rgb565Pixel`].
|
For convenience, Slint provides an implementation for [`slint::Rgb8Pixel`] and [`slint::platform::software_renderer::Rgb565Pixel`].
|
||||||
|
|
||||||
#### Rendering into a Buffer
|
#### Rendering Into a Buffer
|
||||||
|
|
||||||
The following example uses double buffering and swaps between them. This requires a graphics driver that can be provided
|
The following example uses double buffering and swaps between two buffers. This
|
||||||
with the address of what should be the currently displayed frame buffer, also known as front buffer. A dedicated chip is then responsible for
|
requires a graphics driver that takes the address of the currently displayed
|
||||||
reading from RAM and transferring the contents to the attached screen, without any interference of the CPU. Meanwhile, Slint can render into
|
frame buffer, also known as front buffer. A dedicated chip is then responsible
|
||||||
the second buffer, the back buffer.
|
for reading from RAM and transferring the contents to the attached screen,
|
||||||
|
without any interference of the CPU. Meanwhile, Slint renders into the second
|
||||||
|
buffer, the back buffer.
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use slint::platform::software_renderer::Rgb565Pixel;
|
use slint::platform::software_renderer::Rgb565Pixel;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue