mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 13:51:13 +00:00
Consolidate language tutorials (#5037)
All the language tutorials are merged into the Slint reference as "quick starts".
This commit is contained in:
parent
5389367895
commit
ab9d7f342b
78 changed files with 632 additions and 1135 deletions
15
.github/workflows/build_docs.yaml
vendored
15
.github/workflows/build_docs.yaml
vendored
|
@ -90,15 +90,6 @@ jobs:
|
||||||
cp -r target/aarch64-linux-android/doc/i_slint_backend_android_activity/ target/doc/
|
cp -r target/aarch64-linux-android/doc/i_slint_backend_android_activity/ target/doc/
|
||||||
cp -r target/aarch64-linux-android/doc/i_slint_backend_winit/ target/doc/
|
cp -r target/aarch64-linux-android/doc/i_slint_backend_winit/ target/doc/
|
||||||
cp -r target/aarch64-linux-android/doc/i_slint_backend_testing/ target/doc/
|
cp -r target/aarch64-linux-android/doc/i_slint_backend_testing/ target/doc/
|
||||||
- name: "Rust QuickStart"
|
|
||||||
run: mdbook build
|
|
||||||
working-directory: docs/quickstart/rust
|
|
||||||
- name: "C++ QuickStart"
|
|
||||||
run: mdbook build
|
|
||||||
working-directory: docs/quickstart/cpp
|
|
||||||
- name: "NodeJS QuickStart"
|
|
||||||
run: mdbook build
|
|
||||||
working-directory: docs/quickstart/node
|
|
||||||
- name: "Slint Language Documentation"
|
- name: "Slint Language Documentation"
|
||||||
run: cargo xtask slintdocs --show-warnings
|
run: cargo xtask slintdocs --show-warnings
|
||||||
- name: "Node docs"
|
- name: "Node docs"
|
||||||
|
@ -116,11 +107,9 @@ jobs:
|
||||||
target/slintdocs/html
|
target/slintdocs/html
|
||||||
api/node/docs
|
api/node/docs
|
||||||
docs/site
|
docs/site
|
||||||
docs/quickstart/rust/book/html
|
|
||||||
docs/quickstart/cpp/book/html
|
|
||||||
docs/quickstart/node/book/html
|
|
||||||
- name: "Check for docs warnings in internal crates"
|
- name: "Check for docs warnings in internal crates"
|
||||||
run: cargo doc --workspace --no-deps --all-features --exclude slint-node --exclude pyslint --exclude mcu-board-support --exclude printerdemo_mcu --exclude carousel --exclude test-* --exclude plotter --exclude uefi-demo --exclude ffmpeg --exclude gstreamer-player --exclude slint-cpp --exclude slint-python
|
run: cargo doc --workspace --no-deps --all-features --exclude slint-node --exclude pyslint --exclude mcu-board-support --exclude printerdemo_mcu --exclude carousel --exclude test-* --exclude plotter --exclude uefi-demo --exclude ffmpeg --exclude gstreamer-player --exclude slint-cpp --exclude slint-python
|
||||||
- name: Clean cache # Don't cache docs to avoid them including removed classes being published
|
- name: Clean cache # Don't cache docs to avoid them including removed classes being published
|
||||||
run: |
|
run: |
|
||||||
rm -rf target/doc target/cppdocs target/slintdocs api/node/docs docs/quickstart/rust/book docs/quickstart/cpp/book docs/quickstart/node/book
|
rm -rf target/doc target/cppdocs target/slintdocs api/node/docs
|
||||||
|
|
||||||
|
|
2
.github/workflows/nightly_snapshot.yaml
vendored
2
.github/workflows/nightly_snapshot.yaml
vendored
|
@ -385,7 +385,7 @@ jobs:
|
||||||
# Fix up link to Slint language documentation
|
# Fix up link to Slint language documentation
|
||||||
sed -i "s!https://slint.dev/releases/.*/docs/!../../!" $output_path/docs/rust/slint/*.html
|
sed -i "s!https://slint.dev/releases/.*/docs/!../../!" $output_path/docs/rust/slint/*.html
|
||||||
|
|
||||||
for lang in rust cpp node; do
|
for lang in canon; do
|
||||||
mkdir -p $output_path/docs/quickstart/$lang
|
mkdir -p $output_path/docs/quickstart/$lang
|
||||||
cp -a ../docs/quickstart/$lang/book/html/* $output_path/docs/quickstart/$lang
|
cp -a ../docs/quickstart/$lang/book/html/* $output_path/docs/quickstart/$lang
|
||||||
done
|
done
|
||||||
|
|
|
@ -39,7 +39,7 @@ if(SLINT_BUILD_EXAMPLES)
|
||||||
add_subdirectory(examples)
|
add_subdirectory(examples)
|
||||||
endif()
|
endif()
|
||||||
if(SLINT_BUILD_TESTING AND (SLINT_FEATURE_COMPILER OR SLINT_COMPILER))
|
if(SLINT_BUILD_TESTING AND (SLINT_FEATURE_COMPILER OR SLINT_COMPILER))
|
||||||
add_subdirectory(docs/quickstart/cpp/src/)
|
add_subdirectory(docs/reference/src/quickstart/)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
|
feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
|
||||||
|
|
|
@ -10,7 +10,7 @@ members = [
|
||||||
'api/rs/slint',
|
'api/rs/slint',
|
||||||
'api/python',
|
'api/python',
|
||||||
'api/wasm-interpreter',
|
'api/wasm-interpreter',
|
||||||
'docs/quickstart/rust/src',
|
'docs/reference/src/quickstart',
|
||||||
'examples/7guis',
|
'examples/7guis',
|
||||||
'examples/gallery',
|
'examples/gallery',
|
||||||
'examples/imagefilter/rust',
|
'examples/imagefilter/rust',
|
||||||
|
|
126
docs/building.md
126
docs/building.md
|
@ -1,12 +1,11 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
||||||
|
|
||||||
# Slint Build Guide
|
# Slint Build Guide
|
||||||
|
|
||||||
This page explains how to build and test Slint.
|
This page explains how to build and test Slint.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Installing Rust
|
### Installing Rust
|
||||||
|
|
||||||
Install Rust by following the [Rust Getting Started Guide](https://www.rust-lang.org/learn/get-started). If you already
|
Install Rust by following the [Rust Getting Started Guide](https://www.rust-lang.org/learn/get-started). If you already
|
||||||
|
@ -14,11 +13,13 @@ have Rust installed, make sure that it's at least version 1.73 or newer. You can
|
||||||
by running `rustc --version`.
|
by running `rustc --version`.
|
||||||
|
|
||||||
Once this is done, you should have the `rustc` compiler and the `cargo` build system installed in your path.
|
Once this is done, you should have the `rustc` compiler and the `cargo` build system installed in your path.
|
||||||
### Dependencies
|
|
||||||
* **FFMPEG**
|
|
||||||
|
|
||||||
* **Skia** (only few available binaries):
|
### Dependencies
|
||||||
<center>
|
|
||||||
|
- **FFMPEG**
|
||||||
|
|
||||||
|
- **Skia** (only few available binaries):
|
||||||
|
<center>
|
||||||
|
|
||||||
| Platform | Binaries |
|
| Platform | Binaries |
|
||||||
| --------------------------------- | -------------------------------------------------- |
|
| --------------------------------- | -------------------------------------------------- |
|
||||||
|
@ -28,10 +29,10 @@ Once this is done, you should have the `rustc` compiler and the `cargo` build sy
|
||||||
| Android | `aarch64-linux-android`<br/>`x86_64-linux-android` |
|
| Android | `aarch64-linux-android`<br/>`x86_64-linux-android` |
|
||||||
| iOS | `aarch64-apple-ios`<br/>`x86_64-apple-ios` |
|
| iOS | `aarch64-apple-ios`<br/>`x86_64-apple-ios` |
|
||||||
| WebAssembly | `wasm32-unknown-emscripten` |
|
| WebAssembly | `wasm32-unknown-emscripten` |
|
||||||
|
|
||||||
</center>
|
|
||||||
|
|
||||||
- Use Skia capable toolchain `rustup default stable-x86_64-pc-windows-msvc`
|
</center>
|
||||||
|
|
||||||
|
- Use Skia capable toolchain `rustup default stable-x86_64-pc-windows-msvc`
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
|
@ -48,42 +49,42 @@ For Linux a few additional packages beyond the usual build essentials are needed
|
||||||
|
|
||||||
### macOS
|
### macOS
|
||||||
|
|
||||||
- Make sure the "Xcode Command Line Tools" are installed: `xcode-select --install`
|
- Make sure the "Xcode Command Line Tools" are installed: `xcode-select --install`
|
||||||
- (optional) Qt will be used when `qmake` is found in `PATH`
|
- (optional) Qt will be used when `qmake` is found in `PATH`
|
||||||
- FFMPEG `brew install pkg-config ffmpeg`
|
- FFMPEG `brew install pkg-config ffmpeg`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
- See [System Link](#symlinks-in-the-repository-windows)
|
|
||||||
- Make sure the MSVC Build Tools are installed: `winget install Microsoft.VisualStudio.2022.BuildTools`
|
|
||||||
- (optional) make sure Qt is installed and `qmake` is in the `Path`
|
|
||||||
- FFMPEG
|
|
||||||
- Option 1:
|
|
||||||
- install [vcpkg](https://github.com/microsoft/vcpkg#quick-start-windows)
|
|
||||||
- `vcpkg install ffmpeg --triplet x64-windows`
|
|
||||||
- Make sure `VCPKG_ROOT` is set to where `vcpkg` is installed
|
|
||||||
- Make sure `%VCPKG_ROOT%\installed\x64-windows\bin` is in your path
|
|
||||||
|
|
||||||
- Option 2:
|
- See [System Link](#symlinks-in-the-repository-windows)
|
||||||
- Download FFMPEG 4.4 shared and extract (https://github.com/BtbN/FFmpeg-Builds/releases/tag/latest)
|
- Make sure the MSVC Build Tools are installed: `winget install Microsoft.VisualStudio.2022.BuildTools`
|
||||||
- Add FFMPEG to path: `*\ffmpeg\bin` `*\ffmpeg\include\libavutil` `*\ffmpeg\lib`
|
- (optional) make sure Qt is installed and `qmake` is in the `Path`
|
||||||
|
- FFMPEG
|
||||||
|
|
||||||
|
- Option 1:
|
||||||
|
|
||||||
|
- install [vcpkg](https://github.com/microsoft/vcpkg#quick-start-windows)
|
||||||
|
- `vcpkg install ffmpeg --triplet x64-windows`
|
||||||
|
- Make sure `VCPKG_ROOT` is set to where `vcpkg` is installed
|
||||||
|
- Make sure `%VCPKG_ROOT%\installed\x64-windows\bin` is in your path
|
||||||
|
|
||||||
|
- Option 2:
|
||||||
|
- Download FFMPEG 4.4 shared and extract (https://github.com/BtbN/FFmpeg-Builds/releases/tag/latest)
|
||||||
|
- Add FFMPEG to path: `*\ffmpeg\bin` `*\ffmpeg\include\libavutil` `*\ffmpeg\lib`
|
||||||
|
|
||||||
### C++ API (optional)
|
### C++ API (optional)
|
||||||
|
|
||||||
To use Slint from C++, the following extra dependencies are needed:
|
To use Slint from C++, the following extra dependencies are needed:
|
||||||
|
|
||||||
- **[cmake](https://cmake.org/download/)** (3.21 or newer)
|
- **[cmake](https://cmake.org/download/)** (3.21 or newer)
|
||||||
- **[Ninja](https://ninja-build.org)** (Optional, or remove the `-GNinja` when invoking `cmake`)
|
- **[Ninja](https://ninja-build.org)** (Optional, or remove the `-GNinja` when invoking `cmake`)
|
||||||
- A C++ compiler that supports C++20 (e.g., **MSVC 2022 17.3** on Windows, or **GCC 10**)
|
- A C++ compiler that supports C++20 (e.g., **MSVC 2022 17.3** on Windows, or **GCC 10**)
|
||||||
|
|
||||||
### Node.js API (optional)
|
### Node.js API (optional)
|
||||||
|
|
||||||
To use Slint from Node.js, the following extra dependencies are needed.
|
To use Slint from Node.js, the following extra dependencies are needed.
|
||||||
|
|
||||||
- **[Node.js](https://nodejs.org/en/)** (including npm) At this time you will need to use the version 16.
|
- **[Node.js](https://nodejs.org/en/)** (including npm) At this time you will need to use the version 16.
|
||||||
- **[Python](https://www.python.org)**
|
- **[Python](https://www.python.org)**
|
||||||
|
|
||||||
### Symlinks in the repository (Windows)
|
### Symlinks in the repository (Windows)
|
||||||
|
|
||||||
|
@ -111,17 +112,14 @@ cargo test
|
||||||
|
|
||||||
<center> ** <strong> Not recommended</strong> ** </center>
|
<center> ** <strong> Not recommended</strong> ** </center>
|
||||||
|
|
||||||
|
To build all examples install the entire workplace to executables
|
||||||
To build all examples install the entire workplace to executables
|
|
||||||
(excluding [UEFI-demo](https://github.com/slint-ui/slint/tree/master/examples/uefi-demo) - different target)
|
(excluding [UEFI-demo](https://github.com/slint-ui/slint/tree/master/examples/uefi-demo) - different target)
|
||||||
|
|
||||||
|
- Build workspace
|
||||||
- Build workspace
|
|
||||||
```sh
|
```sh
|
||||||
cargo build --workspace --exclude uefi-demo --release
|
cargo build --workspace --exclude uefi-demo --release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
**Important:** Note that `cargo test` does not work without first calling `cargo build` because the
|
**Important:** Note that `cargo test` does not work without first calling `cargo build` because the
|
||||||
the required dynamic library won't be found.
|
the required dynamic library won't be found.
|
||||||
|
|
||||||
|
@ -148,8 +146,6 @@ cargo build -p test-driver-nodejs
|
||||||
|
|
||||||
For more details about the tests and how they are implemented, see [testing.md](./testing.md).
|
For more details about the tests and how they are implemented, see [testing.md](./testing.md).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## C++ API Build
|
## C++ API Build
|
||||||
|
|
||||||
The Slint C++ API is implemented as a normal cmake build:
|
The Slint C++ API is implemented as a normal cmake build:
|
||||||
|
@ -225,52 +221,29 @@ cargo run --release --bin slint-viewer -- examples/printerdemo/ui/printerdemo.sl
|
||||||
|
|
||||||
The Slint documentation consists of five parts:
|
The Slint documentation consists of five parts:
|
||||||
|
|
||||||
- The tutorials
|
- The quickstart guide
|
||||||
- The Rust API documentation
|
- The Rust API documentation
|
||||||
- The C++ API documentation
|
- The C++ API documentation
|
||||||
- The Node.js API documentation
|
- The Node.js API documentation
|
||||||
- The DSL documentation
|
- The DSL documentation
|
||||||
|
|
||||||
### Tutorials
|
The quickstart guide is part of the DSL documentation.
|
||||||
|
|
||||||
There are three tutorials built with mdbook, one for each of the three languages supported by Slint.
|
### Quickstart and DSL docs
|
||||||
|
|
||||||
|
The quickstart and DSL docs are written in markdown and built with Sphinx, using the myst parser extension.
|
||||||
|
|
||||||
**Prerequisites**:
|
**Prerequisites**:
|
||||||
|
|
||||||
- [mdbook](https://rust-lang.github.io/mdBook/guide/installation.html)
|
- [pipenv](https://pipenv.pypa.io/en/latest/)
|
||||||
|
- [Python](https://www.python.org/downloads/)
|
||||||
|
|
||||||
#### Rust tutorial
|
Use the following command line to build the documentation using `rustdoc` to the `target/slintdocs/html` folder:
|
||||||
|
|
||||||
```shell
|
|
||||||
mdbook build docs/quickstart/rust
|
|
||||||
```
|
|
||||||
|
|
||||||
#### C++ tutorial
|
|
||||||
|
|
||||||
```shell
|
|
||||||
mdbook build docs/quickstart/cpp
|
|
||||||
```
|
|
||||||
|
|
||||||
#### NodeJS tutorial
|
|
||||||
|
|
||||||
```shell
|
|
||||||
mdbook build docs/quickstart/node
|
|
||||||
```
|
|
||||||
|
|
||||||
### Slint DSL docs
|
|
||||||
|
|
||||||
**Prerequisites**:
|
|
||||||
|
|
||||||
- [pipenv](https://pipenv.pypa.io/en/latest/)
|
|
||||||
- [Python](https://www.python.org/downloads/)
|
|
||||||
|
|
||||||
Use the following command line to build the documentation for the Slint DSL using `rustdoc` to the `target/slintdocs/html` folder:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo xtask slintdocs --show-warnings
|
cargo xtask slintdocs --show-warnings
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Rust API docs
|
### Rust API docs
|
||||||
|
|
||||||
Run the following command to generate the documentation using rustdoc in the `target/doc/` sub-folder:
|
Run the following command to generate the documentation using rustdoc in the `target/doc/` sub-folder:
|
||||||
|
@ -279,12 +252,13 @@ Run the following command to generate the documentation using rustdoc in the `ta
|
||||||
RUSTDOCFLAGS="--html-in-header=$PWD/docs/resources/slint-docs-preview.html --html-in-header=$PWD/docs/resources/slint-docs-highlight.html" cargo doc --no-deps --features slint/document-features,slint/log
|
RUSTDOCFLAGS="--html-in-header=$PWD/docs/resources/slint-docs-preview.html --html-in-header=$PWD/docs/resources/slint-docs-highlight.html" cargo doc --no-deps --features slint/document-features,slint/log
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: `--html-in-header` arguments passed to rustdoc via `RUSTDOCFLAGS` are used to enable syntax highlighting and live-preview for Slint example snippets.
|
Note: `--html-in-header` arguments passed to rustdoc via `RUSTDOCFLAGS` are used to enable syntax highlighting and live-preview for Slint example snippets.
|
||||||
|
|
||||||
### C++ API docs
|
### C++ API docs
|
||||||
|
|
||||||
**Prerequisites**:
|
**Prerequisites**:
|
||||||
|
|
||||||
- [Doxygen](https://www.doxygen.nl/download.html)
|
- [Doxygen](https://www.doxygen.nl/download.html)
|
||||||
|
|
||||||
Run the following command to generate the documentation using sphinx/exhale/breathe/doxygen/myst_parser in the `target/cppdocs` sub-folder:
|
Run the following command to generate the documentation using sphinx/exhale/breathe/doxygen/myst_parser in the `target/cppdocs` sub-folder:
|
||||||
|
|
||||||
|
@ -299,4 +273,4 @@ Run the following commands from the `/api/node` sub-folder to generate the docs
|
||||||
```sh
|
```sh
|
||||||
npm install
|
npm install
|
||||||
npm run docs
|
npm run docs
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
[book]
|
|
||||||
authors = ["Slint Developers <info@slint.dev>"]
|
|
||||||
language = "en"
|
|
||||||
multilingual = false
|
|
||||||
src = "src"
|
|
||||||
title = "Slint Memory Game QuickStart (C++)"
|
|
||||||
|
|
||||||
[output.html]
|
|
||||||
theme = "../theme"
|
|
||||||
|
|
||||||
[output.linkcheck] # enable the "mdbook-linkcheck" renderer
|
|
||||||
optional = true
|
|
|
@ -1,12 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
# Summary
|
|
||||||
|
|
||||||
- [Introduction](./introduction.md)
|
|
||||||
- [Getting Started](./getting_started.md)
|
|
||||||
- [Memory Tile](./memory_tile.md)
|
|
||||||
- [Polishing the Tile](./polishing_the_tile.md)
|
|
||||||
- [From One To Multiple Tiles](./from_one_to_multiple_tiles.md)
|
|
||||||
- [Creating The Tiles From C++](./creating_the_tiles_from_cpp.md)
|
|
||||||
- [Game Logic In C++](./game_logic_in_cpp.md)
|
|
||||||
- [Ideas For The Reader](./ideas_for_the_reader.md)
|
|
||||||
- [Conclusion](./conclusion.md)
|
|
|
@ -1,14 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Conclusion
|
|
||||||
|
|
||||||
This tutorial showed you how to combine built-in Slint elements with C++ code to build a
|
|
||||||
game. There is much more to Slint, such as layouts, widgets, or styling.
|
|
||||||
|
|
||||||
We recommend the following links to continue:
|
|
||||||
|
|
||||||
- [Examples](https://github.com/slint-ui/slint/tree/master/examples): The Slint repository has several demos and examples. These are a great starting point to learn how to use many Slint features.
|
|
||||||
- [Todo Example](https://github.com/slint-ui/slint/tree/master/examples/todo): This is one of the examples that implements a classic use-case.
|
|
||||||
- [Memory Puzzle](https://github.com/slint-ui/slint/tree/master/examples/memory): This is a slightly more polished version of the code in this example and you can <a href="https://slint.dev/demos/memory/" target="_blank">play the wasm version</a> in your browser.
|
|
||||||
- [Slint API Docs](https://slint.dev/docs/rust/slint/): The reference documentation for the main Rust crate.
|
|
||||||
- [Slint Interpreter API Docs](https://slint.dev/docs/rust/slint_interpreter/): The reference documentation for the Rust crate that allows you to dynamically load Slint files.
|
|
|
@ -1,27 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Creating The Tiles From C++
|
|
||||||
|
|
||||||
This step places the game tiles randomly.
|
|
||||||
|
|
||||||
Change the `main` function and includes in `src/main.cpp` to the following:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
{{#include main_tiles_from_cpp.cpp:main}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code takes the list of tiles, duplicates it, and shuffles it, accessing the `memory_tiles` property through the C++ code.
|
|
||||||
|
|
||||||
For each top-level property, Slint generates a getter and a setter function. In this case `get_memory_tiles` and `set_memory_tiles`.
|
|
||||||
Since `memory_tiles` is a Slint array, it's represented as a [`std::shared_ptr<slint::Model>`](https://slint.dev/docs/cpp/api/classslint_1_1model).
|
|
||||||
|
|
||||||
You can't change the model generated by Slint, but you can extract the tiles from it and put them
|
|
||||||
in a [`slint::VectorModel`](https://slint.dev/docs/cpp/api/classslint_1_1vectormodel) which inherits from `Model`.
|
|
||||||
`VectorModel` lets you make changes and you can use it to replace the static generated model.
|
|
||||||
|
|
||||||
Running this code opens a window that now shows a 4 by 4 grid of rectangles, which show or hide
|
|
||||||
the icons when a player clicks on them.
|
|
||||||
|
|
||||||
There's one last aspect missing now, the rules for the game.
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/creating-the-tiles-from-rust.mp4"></video>
|
|
|
@ -1,48 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Game Logic In C++
|
|
||||||
|
|
||||||
This step implements the rules of the game in C++.
|
|
||||||
|
|
||||||
Slint's general philosophy is that you implement the user interface in Slint and the business logic in your favorite programming
|
|
||||||
language.
|
|
||||||
|
|
||||||
The game rules enforce that at most two tiles have their curtain open. If the tiles match, then the game
|
|
||||||
considers them solved and they remain open. Otherwise, the game waits briefly so the player can memorize
|
|
||||||
the location of the icons, and then closes the curtains again.
|
|
||||||
|
|
||||||
Add the following code inside the <span class="hljs-title">MainWindow</span> component to signal to the C++ code when the user clicks on a tile.
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include ../../rust/src/main_game_logic_in_rust.rs:mainwindow_interface}}
|
|
||||||
```
|
|
||||||
|
|
||||||
This change adds a way for the <span class="hljs-title">MainWindow</span> to call to the C++ code that it should
|
|
||||||
check if a player has solved a pair of tiles. The Rust code needs an additional property to toggle to disable further
|
|
||||||
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed!
|
|
||||||
|
|
||||||
The last change to the code is to act when the <span class="hljs-title">MemoryTile</span> signals that a player clicked it.
|
|
||||||
|
|
||||||
Add the following handler in the <span class="hljs-title">MainWindow</span> `for` loop `clicked` handler:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include ../../rust/src/main_game_logic_in_rust.rs:tile_click_logic}}
|
|
||||||
```
|
|
||||||
|
|
||||||
On the C++ side, you can now add a handler to the `check_if_pair_solved` callback, that checks if a player opened two tiles.
|
|
||||||
If they match, the code sets the `solved` property to true in the model. If they don't
|
|
||||||
match, start a timer that closes the tiles after one second. While the timer is running, disable every tile so
|
|
||||||
a player can't click anything during this time.
|
|
||||||
|
|
||||||
Insert this code before the `main_window->run()` call:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
{{#include main_game_logic.cpp:game_logic}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code uses a [ComponentWeakHandle](https://slint.dev/docs/cpp/api/classslint_1_1ComponentWeakHandle) pointer of the `main_window`. This is
|
|
||||||
important because capturing a copy of the `main_window` itself within the callback handler would result in circular ownership.
|
|
||||||
The `MainWindow` owns the callback handler, which itself owns a reference to the `MainWindow`, which must be weak
|
|
||||||
instead of strong to avoid a memory leak.
|
|
||||||
|
|
||||||
These were the last changes and running the code opens a window that allows a player to play the game by the rules.
|
|
|
@ -1,75 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
This tutorial uses C++ as the host programming language. Slint also supports other programming languages like
|
|
||||||
[Rust](https://slint.dev/docs/rust/slint/) or [JavaScript](https://slint.dev/docs/node/).
|
|
||||||
|
|
||||||
We recommend using [our editor integrations for Slint](https://github.com/slint-ui/slint/tree/master/editors) for following this tutorial.
|
|
||||||
|
|
||||||
Slint has an application template you can use to create a project with dependencies already set up that follows recommended best practices.
|
|
||||||
|
|
||||||
Before using the template, you need a C++ compiler that supports C++ 20 and to install [CMake](https://cmake.org/download/) 3.21 or newer.
|
|
||||||
|
|
||||||
Clone or download template repository:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/slint-ui/slint-cpp-template memory
|
|
||||||
cd memory
|
|
||||||
```
|
|
||||||
|
|
||||||
The `CMakeLists.txt` uses the line `add_executable(my_application src/main.cpp)` to set `src/main.cpp` as the main C++ code file.
|
|
||||||
|
|
||||||
Change the content of `src/main.cpp` to the following:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
{{#include main_initial.cpp:main}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Also in `CMakeLists.txt` the line
|
|
||||||
`slint_target_sources(my_application ui/appwindow.slint)` is a Slint function used to
|
|
||||||
add the `appwindow.slint` file to the target.
|
|
||||||
|
|
||||||
Change the contents of `ui/appwindow.slint` to the following:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include appwindow.slint:main_window}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Configure with CMake:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake -B build
|
|
||||||
```
|
|
||||||
|
|
||||||
_Note_: When configuring with CMake, the FetchContent module fetches the source code of Slint via git.
|
|
||||||
This may take some time when building for the first time, as the process needs to build
|
|
||||||
the Slint runtime and compiler.
|
|
||||||
|
|
||||||
Build with CMake:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake --build build
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the application binary on Linux or macOS:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./build/my_application
|
|
||||||
```
|
|
||||||
|
|
||||||
Windows:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
build\my_application.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
This opens a window with a green "Hello World" greeting.
|
|
||||||
|
|
||||||
If you are stepping through this tutorial on a Windows machine, you can run it with
|
|
||||||
|
|
||||||
```sh
|
|
||||||
my_application
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
|
@ -1,16 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Introduction
|
|
||||||
|
|
||||||
This tutorial introduces you to the Slint UI framework in a playful way by implementing a memory game. It combines the Slint language for the graphics with the game rules implemented in C++.
|
|
||||||
|
|
||||||
The game consists of a grid of 16 rectangular tiles. Clicking on a tile uncovers an icon underneath.
|
|
||||||
There are 8 different icons in total, so each tile has a sibling somewhere in the grid with the
|
|
||||||
same icon. The objective is to locate all icon pairs. The player can uncover two tiles at the same time. If they
|
|
||||||
aren't the same, the game obscures the icons again.
|
|
||||||
If the player uncovers two tiles with the same icon, then they remain visible - they're solved.
|
|
||||||
|
|
||||||
This is how the game looks in action:
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/memory_clip.mp4"
|
|
||||||
class="img-fluid img-thumbnail rounded"></video>
|
|
|
@ -1,94 +0,0 @@
|
||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
struct TileData {
|
|
||||||
image: image,
|
|
||||||
image_visible: bool,
|
|
||||||
solved: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
component MemoryTile inherits Rectangle {
|
|
||||||
callback clicked;
|
|
||||||
in property <bool> open_curtain;
|
|
||||||
in property <bool> solved;
|
|
||||||
in property <image> icon;
|
|
||||||
|
|
||||||
height: 64px;
|
|
||||||
width: 64px;
|
|
||||||
background: solved ? #34CE57 : #3960D5;
|
|
||||||
animate background { duration: 800ms; }
|
|
||||||
|
|
||||||
Image {
|
|
||||||
source: icon;
|
|
||||||
width: parent.width;
|
|
||||||
height: parent.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left curtain
|
|
||||||
Rectangle {
|
|
||||||
background: #193076;
|
|
||||||
x: 0px;
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
|
||||||
height: parent.height;
|
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right curtain
|
|
||||||
Rectangle {
|
|
||||||
background: #193076;
|
|
||||||
x: open_curtain ? parent.width : (parent.width / 2);
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
|
||||||
height: parent.height;
|
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
|
||||||
animate x { duration: 250ms; easing: ease-in; }
|
|
||||||
}
|
|
||||||
|
|
||||||
TouchArea {
|
|
||||||
clicked => {
|
|
||||||
// Delegate to the user of this element
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ANCHOR: mainwindow_interface
|
|
||||||
export component MainWindow inherits Window {
|
|
||||||
width: 326px;
|
|
||||||
height: 326px;
|
|
||||||
|
|
||||||
callback check_if_pair_solved(); // Added
|
|
||||||
in property <bool> disable_tiles; // Added
|
|
||||||
|
|
||||||
in-out property <[TileData]> memory_tiles: [
|
|
||||||
{ image: @image-url("icons/at.png") },
|
|
||||||
// ANCHOR_END: mainwindow_interface
|
|
||||||
{ image: @image-url("icons/balance-scale.png") },
|
|
||||||
{ image: @image-url("icons/bicycle.png") },
|
|
||||||
{ image: @image-url("icons/bus.png") },
|
|
||||||
{ image: @image-url("icons/cloud.png") },
|
|
||||||
{ image: @image-url("icons/cogs.png") },
|
|
||||||
{ image: @image-url("icons/motorcycle.png") },
|
|
||||||
{ image: @image-url("icons/video.png") },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ANCHOR: tile_click_logic
|
|
||||||
for tile[i] in memory_tiles : MemoryTile {
|
|
||||||
x: mod(i, 4) * 74px;
|
|
||||||
y: floor(i / 4) * 74px;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
icon: tile.image;
|
|
||||||
open_curtain: tile.image_visible || tile.solved;
|
|
||||||
// propagate the solved status from the model to the tile
|
|
||||||
solved: tile.solved;
|
|
||||||
clicked => {
|
|
||||||
// old: tile.image_visible = !tile.image_visible;
|
|
||||||
// new:
|
|
||||||
if (!root.disable_tiles) {
|
|
||||||
tile.image_visible = !tile.image_visible;
|
|
||||||
root.check_if_pair_solved();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: tile_click_logic
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
[book]
|
|
||||||
authors = ["Slint Developers <info@slint.dev>"]
|
|
||||||
language = "en"
|
|
||||||
multilingual = false
|
|
||||||
src = "src"
|
|
||||||
title = "Slint Memory Game QuickStart (node)"
|
|
||||||
|
|
||||||
[output.html]
|
|
||||||
theme = "../theme"
|
|
||||||
|
|
||||||
[output.linkcheck] # enable the "mdbook-linkcheck" renderer
|
|
||||||
optional = true
|
|
|
@ -1,12 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
# Summary
|
|
||||||
|
|
||||||
- [Introduction](./introduction.md)
|
|
||||||
- [Getting Started](./getting_started.md)
|
|
||||||
- [Memory Tile](./memory_tile.md)
|
|
||||||
- [Polishing the Tile](./polishing_the_tile.md)
|
|
||||||
- [From One To Multiple Tiles](./from_one_to_multiple_tiles.md)
|
|
||||||
- [Creating The Tiles From JavaScript](./creating_the_tiles_from_js.md)
|
|
||||||
- [Game Logic In JavaScript](./game_logic_in_js.md)
|
|
||||||
- [Ideas For The Reader](./ideas_for_the_reader.md)
|
|
||||||
- [Conclusion](./conclusion.md)
|
|
|
@ -1,25 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Creating The Tiles From JavaScript
|
|
||||||
|
|
||||||
This step places the game tiles randomly.
|
|
||||||
|
|
||||||
Change `main.js` to the following:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{{#include main_tiles_from_js.js:main}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code takes the list of tiles, duplicates it, and shuffles it, accessing the `memory_tiles` property through the JavaScript code.
|
|
||||||
|
|
||||||
As `memory_tiles` is an array, it's represented as a JavaScript [`Array`](https://slint.dev/docs/node/).
|
|
||||||
You can't change the model generated by Slint, but you can extract the tiles from it and put them
|
|
||||||
in a [`slint.ArrayModel`](https://slint.dev/docs/node/classes/arraymodel.html) which implements the [`Model`](https://slint.dev/docs/node/interfaces/model.html) interface.
|
|
||||||
`ArrayModel` allows you to make changes and you can use it to replace the static generated model.
|
|
||||||
|
|
||||||
Running this code opens a window that now shows a 4 by 4 grid of rectangles, which show or hide
|
|
||||||
the icons when a player clicks on them.
|
|
||||||
|
|
||||||
There's one last aspect missing now, the rules for the game.
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/creating-the-tiles-from-rust.mp4"></video>
|
|
|
@ -1,40 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# From One To Multiple Tiles
|
|
||||||
|
|
||||||
After modeling a single tile, this step creates a grid of them. For the grid to be a game board, you need two features:
|
|
||||||
|
|
||||||
1. **A data model**: An array created as a JavaScript model, where each element describes the tile data structure, such as:
|
|
||||||
|
|
||||||
- URL of the image
|
|
||||||
- Whether the image is visible
|
|
||||||
- If the player has solved this tile.
|
|
||||||
|
|
||||||
2. A way of creating multiple instances of the tiles.
|
|
||||||
|
|
||||||
With Slint you declare an array of structures based on a model using square brackets. Use a <span class="hljs-keyword">for</span> loop
|
|
||||||
to create multiple instances of the same element.
|
|
||||||
|
|
||||||
The <span class="hljs-keyword">for</span> loop is declarative and automatically updates when
|
|
||||||
the model changes. The loop instantiates all the <span class="hljs-title">MemoryTile</span> elements and places them on a grid based on their
|
|
||||||
index with spacing between the tiles.
|
|
||||||
|
|
||||||
First, add the tile data structure definition at the top of the `memory.slint` file:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include ../../rust/src/main_multiple_tiles.rs:tile_data}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, replace the _export component <span class="hljs-title">MainWindow</span> inherits Window { ... }_ section at the bottom of the `memory.slint` file with the following:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include ../../rust/src/main_multiple_tiles.rs:main_window}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The <code><span class="hljs-keyword">for</span> tile\[i\] <span class="hljs-keyword">in</span> memory_tiles:</code> syntax declares a variable `tile` which contains the data of one element from the `memory_tiles` array,
|
|
||||||
and a variable `i` which is the index of the tile. The code uses the `i` index to calculate the position of a tile, based on its row and column,
|
|
||||||
using modulo and integer division to create a 4 by 4 grid.
|
|
||||||
|
|
||||||
Running the code opens a window that shows 8 tiles, which a player can open individually.
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/from-one-to-multiple-tiles.mp4"></video>
|
|
|
@ -1,42 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Game Logic In JavaScript
|
|
||||||
|
|
||||||
This step implements the rules of the game in JavaScript.
|
|
||||||
|
|
||||||
Slint's general philosophy is that you implement the user interface in Slint and the business logic in your favorite programming
|
|
||||||
language.
|
|
||||||
|
|
||||||
The game rules enforce that at most two tiles have their curtain open. If the tiles match, then the game
|
|
||||||
considers them solved and they remain open. Otherwise, the game waits briefly so the player can memorize
|
|
||||||
the location of the icons, and then closes the curtains again.
|
|
||||||
|
|
||||||
Change the contents of `memory.slint` to signal to the JavaScript code when the user clicks on a tile.
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include ../../rust/src/main_game_logic_in_rust.rs:mainwindow_interface}}
|
|
||||||
```
|
|
||||||
|
|
||||||
This change adds a way for the <span class="hljs-title">MainWindow</span> to call to the JavaScript code that it should
|
|
||||||
check if a player has solved a pair of tiles. The Rust code needs an additional property to toggle to disable further
|
|
||||||
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed!
|
|
||||||
|
|
||||||
The last change to the code is to act when the <span class="hljs-title">MemoryTile</span> signals that a player clicked it.
|
|
||||||
|
|
||||||
Add the following handler in the <span class="hljs-title">MainWindow</span> `for` loop `clicked` handler:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include ../../rust/src/main_game_logic_in_rust.rs:tile_click_logic}}
|
|
||||||
```
|
|
||||||
|
|
||||||
On the JavaScript side, now add a handler to the `check_if_pair_solved` callback, that checks if a player opened two tiles. If they match, the code sets the `solved` property to true in the model. If they don't
|
|
||||||
match, start a timer that closes the tiles after one second. While the timer is running, disable every tile so
|
|
||||||
a player can't click anything during this time.
|
|
||||||
|
|
||||||
Insert this code before the `mainWindow.run()` call:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{{#include main_game_logic.js:game_logic}}
|
|
||||||
```
|
|
||||||
|
|
||||||
These were the last changes and running the code opens a window that allows a player to play the game by the rules.
|
|
|
@ -1,43 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
This tutorial uses JavaScript as the host programming language. Slint also supports other programming languages like
|
|
||||||
[Rust](https://slint.dev/docs/rust/slint/) or [C++](https://slint.dev/docs/cpp/).
|
|
||||||
|
|
||||||
We recommend using [our editor integrations for Slint](https://github.com/slint-ui/slint/tree/master/editors) for following this tutorial.
|
|
||||||
|
|
||||||
Slint has an application template you can use to create a project with dependencies already set up that follows recommended best practices.
|
|
||||||
|
|
||||||
Clone the template with the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/slint-ui/slint-nodejs-template memory
|
|
||||||
cd memory
|
|
||||||
```
|
|
||||||
|
|
||||||
Install dependencies with npm:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
The `package.json` file references `src/main.js` as the entry point for the application and `src/main.js` references `memory.slint` as the UI file.
|
|
||||||
|
|
||||||
Replace the contents of `src/main.js` with the following:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{{#include main_initial.js:main}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `slint.loadFile` method resolves files from the process's current working directory, so from the `package.json` file's location.
|
|
||||||
|
|
||||||
Replace the contents of `ui/appwindow.slint` with the following:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include memory.slint:main_window}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the example with `npm start` and a window appears with the green "Hello World" greeting.
|
|
||||||
|
|
||||||

|
|
|
@ -1 +0,0 @@
|
||||||
../../../../examples/memory/icons
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Ideas For The Reader
|
|
||||||
|
|
||||||
The game is visually bare. Here are some ideas on how you could make further changes to enhance it:
|
|
||||||
|
|
||||||
- The tiles could have rounded corners, to look less sharp. Use the [border-radius](https://slint.dev/docs/slint/src/language/builtins/elements#rectangle)
|
|
||||||
property of _[Rectangle](https://slint.dev/docs/slint/src/language/builtins/elements#rectangle)_ to achieve that.
|
|
||||||
|
|
||||||
- In real-world memory games, the back of the tiles often have some common graphic. You could add an image with
|
|
||||||
the help of another _[Image](https://slint.dev/docs/slint/src/language/builtins/elements#image)_
|
|
||||||
element. Note that you may have to use _Rectangle_'s _clip property_
|
|
||||||
element around it to ensure that the image is clipped away when the curtain effect opens.
|
|
||||||
|
|
||||||
Let us know in the comments on [Github Discussions](https://github.com/slint-ui/slint/discussions)
|
|
||||||
how you polished your code, or feel free to ask questions about how to implement something.
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Introduction
|
|
||||||
|
|
||||||
This tutorial introduces you to the Slint UI framework in a playful way by implementing a memory game. It combines the Slint language for the graphics with the game rules implemented in JavaScript.
|
|
||||||
|
|
||||||
The game consists of a grid of 16 rectangular tiles. Clicking on a tile uncovers an icon underneath.
|
|
||||||
There are 8 different icons in total, so each tile has a sibling somewhere in the grid with the
|
|
||||||
same icon. The objective is to locate all icon pairs. The player can uncover two tiles at the same time. If they
|
|
||||||
aren't the same, the game obscures the icons again.
|
|
||||||
If the player uncovers two tiles with the same icon, then they remain visible - they're solved.
|
|
||||||
|
|
||||||
This is how the game looks in action:
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/memory_clip.mp4"
|
|
||||||
class="img-fluid img-thumbnail rounded"></video>
|
|
|
@ -1,52 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Memory Tile
|
|
||||||
|
|
||||||
With the skeleton code in place, this step looks at the first element of the game, the memory tile. It's the
|
|
||||||
visual building block that consists of an underlying filled rectangle background, the icon image. Later steps add a covering rectangle that acts as a curtain.
|
|
||||||
|
|
||||||
You declare the background rectangle as 64 logical pixels wide and tall
|
|
||||||
filled with a soothing tone of blue.
|
|
||||||
|
|
||||||
Lengths in Slint have a unit, here, the `px` suffix.
|
|
||||||
This makes the code easier to read and the compiler can detect when you accidentally
|
|
||||||
mix values with different units attached to them.
|
|
||||||
|
|
||||||
Copy the following code into `ui/appwindow.slint` file, replacing the current content:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include memory_tile.slint:main_window}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code exports the <span class="hljs-title">MainWindow</span> component so that the JavaScript code can access it later.
|
|
||||||
|
|
||||||
Inside the <span class="hljs-built_in">Rectangle</span> place an <span class="hljs-built_in">Image</span> element that
|
|
||||||
loads an icon with the <span class="hljs-built_in">@image-url()</span> macro. The path is relative to the location of `ui/appwindow.slint`.
|
|
||||||
|
|
||||||
You need to install this icon and others you use later first. You can download a pre-prepared
|
|
||||||
[Zip archive](https://slint.dev/blog/memory-game-tutorial/icons.zip) to the `ui` folder,
|
|
||||||
|
|
||||||
If you are on Linux or macOS, download and extract it with the following commands:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cd ui
|
|
||||||
curl -O https://slint.dev/blog/memory-game-tutorial/icons.zip
|
|
||||||
unzip icons.zip
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are on Windows, use the following commands:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cd ui
|
|
||||||
powershell curl -Uri https://slint.dev/blog/memory-game-tutorial/icons.zip -Outfile icons.zip
|
|
||||||
powershell Expand-Archive -Path icons.zip -DestinationPath .
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
This unpacks an `icons` directory containing several icons.
|
|
||||||
|
|
||||||
Running the program with `npm start` opens a window that shows the icon of a bus on a
|
|
||||||
blue background.
|
|
||||||
|
|
||||||

|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
// ANCHOR: main_window
|
|
||||||
component MemoryTile inherits Rectangle {
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
background: #3960D5;
|
|
||||||
|
|
||||||
Image {
|
|
||||||
source: @image-url("icons/bus.png");
|
|
||||||
width: parent.width;
|
|
||||||
height: parent.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export component MainWindow inherits Window {
|
|
||||||
MemoryTile {}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: main_window
|
|
|
@ -1,81 +0,0 @@
|
||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
struct TileData {
|
|
||||||
image: image,
|
|
||||||
image_visible: bool,
|
|
||||||
solved: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
component MemoryTile inherits Rectangle {
|
|
||||||
callback clicked;
|
|
||||||
in property <bool> open_curtain;
|
|
||||||
in property <bool> solved;
|
|
||||||
in property <image> icon;
|
|
||||||
|
|
||||||
height: 64px;
|
|
||||||
width: 64px;
|
|
||||||
background: solved ? #34CE57 : #3960D5;
|
|
||||||
animate background { duration: 800ms; }
|
|
||||||
|
|
||||||
Image {
|
|
||||||
source: icon;
|
|
||||||
width: parent.width;
|
|
||||||
height: parent.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left curtain
|
|
||||||
Rectangle {
|
|
||||||
background: #193076;
|
|
||||||
x: 0px;
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
|
||||||
height: parent.height;
|
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right curtain
|
|
||||||
Rectangle {
|
|
||||||
background: #193076;
|
|
||||||
x: open_curtain ? parent.width : (parent.width / 2);
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
|
||||||
height: parent.height;
|
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
|
||||||
animate x { duration: 250ms; easing: ease-in; }
|
|
||||||
}
|
|
||||||
|
|
||||||
TouchArea {
|
|
||||||
clicked => {
|
|
||||||
// Delegate to the user of this element
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export component MainWindow inherits Window {
|
|
||||||
width: 326px;
|
|
||||||
height: 326px;
|
|
||||||
|
|
||||||
in-out property <[TileData]> memory_tiles: [
|
|
||||||
{ image: @image-url("icons/at.png") },
|
|
||||||
{ image: @image-url("icons/balance-scale.png") },
|
|
||||||
{ image: @image-url("icons/bicycle.png") },
|
|
||||||
{ image: @image-url("icons/bus.png") },
|
|
||||||
{ image: @image-url("icons/cloud.png") },
|
|
||||||
{ image: @image-url("icons/cogs.png") },
|
|
||||||
{ image: @image-url("icons/motorcycle.png") },
|
|
||||||
{ image: @image-url("icons/video.png") },
|
|
||||||
];
|
|
||||||
for tile[i] in memory_tiles : MemoryTile {
|
|
||||||
x: mod(i, 4) * 74px;
|
|
||||||
y: floor(i / 4) * 74px;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
icon: tile.image;
|
|
||||||
open_curtain: tile.image_visible || tile.solved;
|
|
||||||
// propagate the solved status from the model to the tile
|
|
||||||
solved: tile.solved;
|
|
||||||
clicked => {
|
|
||||||
tile.image_visible = !tile.image_visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Polishing the Tile
|
|
||||||
|
|
||||||
In this step, you add a curtain-like cover that opens when clicked. You do this by declaring two rectangles
|
|
||||||
below the <span class="hljs-built_in">Image</span>, so that Slint draws them after the Image and thus on top of the image.
|
|
||||||
|
|
||||||
The <span class="hljs-built_in">TouchArea</span> element declares a transparent rectangular region that allows
|
|
||||||
reacting to user input such as a mouse click or tap. The element forwards a callback to the <em>MainWindow</em> indicating that a user clicked the tile.
|
|
||||||
|
|
||||||
The <em>MainWindow</em> reacts by flipping a custom <em>open_curtain</em> property.
|
|
||||||
Property bindings for the animated width and x properties also use the custom <em>open_curtain</em> property.
|
|
||||||
|
|
||||||
The following table shows more detail on the two states:
|
|
||||||
|
|
||||||
| _open_curtain_ value: | false | true |
|
|
||||||
| ----------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| Left curtain rectangle | Fill the left half by setting the width _width_ to half the parent's width | Width of zero makes the rectangle invisible |
|
|
||||||
| Right curtain rectangle | Fill the right half by setting _x_ and _width_ to half of the parent's width | _width_ of zero makes the rectangle invisible. _x_ moves to the right, sliding the curtain open when animated |
|
|
||||||
|
|
||||||
To make the tile extensible, replace the hard-coded icon name with an _icon_
|
|
||||||
property that can be set when instantiating the element.
|
|
||||||
|
|
||||||
For the final polish, add a
|
|
||||||
_solved_ property used to animate the color to a shade of green when a player finds a pair.
|
|
||||||
|
|
||||||
Replace the code in `ui/appwindow.slint` with the following:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include ../../rust/src/main_polishing_the_tile.rs:tile}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code uses `root` and `self`. `root` refers to the outermost
|
|
||||||
element in the component, the <span class="hljs-title">MemoryTile</span> in this case. `self` refers
|
|
||||||
to the current element.
|
|
||||||
|
|
||||||
The code exports the <span class="hljs-title">MainWindow</span> component. This is necessary so that you can later access it
|
|
||||||
from application business logic.
|
|
||||||
|
|
||||||
Running the code opens a window with a rectangle that opens up to show the bus icon when clicked. Subsequent clicks close and open the curtain again.
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/polishing-the-tile.mp4"></video>
|
|
|
@ -1,12 +0,0 @@
|
||||||
[book]
|
|
||||||
authors = ["Slint Developers <info@slint.dev>"]
|
|
||||||
language = "en"
|
|
||||||
multilingual = false
|
|
||||||
src = "src"
|
|
||||||
title = "Slint Memory Game QuickStart (Rust)"
|
|
||||||
|
|
||||||
[output.html]
|
|
||||||
theme = "../theme"
|
|
||||||
|
|
||||||
[output.linkcheck] # enable the "mdbook-linkcheck" renderer
|
|
||||||
optional = true
|
|
|
@ -1,13 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
# Summary
|
|
||||||
|
|
||||||
- [Introduction](./introduction.md)
|
|
||||||
- [Getting Started](./getting_started.md)
|
|
||||||
- [Memory Tile](./memory_tile.md)
|
|
||||||
- [Polishing the Tile](./polishing_the_tile.md)
|
|
||||||
- [From One To Multiple Tiles](./from_one_to_multiple_tiles.md)
|
|
||||||
- [Creating The Tiles From Rust](./creating_the_tiles_from_rust.md)
|
|
||||||
- [Game Logic In Rust](./game_logic_in_rust.md)
|
|
||||||
- [Ideas For The Reader](./ideas_for_the_reader.md)
|
|
||||||
- [Running In A Browser Using WebAssembly](./running_in_a_browser.md)
|
|
||||||
- [Conclusion](./conclusion.md)
|
|
|
@ -1,14 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Conclusion
|
|
||||||
|
|
||||||
This tutorial showed you how to combine built-in Slint elements with Rust code to build a
|
|
||||||
game. There is much more to Slint, such as layouts, widgets, or styling.
|
|
||||||
|
|
||||||
We recommend the following links to continue:
|
|
||||||
|
|
||||||
- [Examples](https://github.com/slint-ui/slint/tree/master/examples): The Slint repository has several demos and examples. These are a great starting point to learn how to use many Slint features.
|
|
||||||
- [Todo Example](https://github.com/slint-ui/slint/tree/master/examples/todo): This is one of the examples that implements a classic use-case.
|
|
||||||
- [Memory Puzzle](https://github.com/slint-ui/slint/tree/master/examples/memory): This is a slightly more polished version of the code in this example and you can <a href="https://slint.dev/demos/memory/" target="_blank">play the wasm version</a> in your browser.
|
|
||||||
- [Slint API Docs](https://slint.dev/docs/rust/slint/): The reference documentation for the main Rust crate.
|
|
||||||
- [Slint Interpreter API Docs](https://slint.dev/docs/rust/slint_interpreter/): The reference documentation for the Rust crate that allows you to dynamically load Slint files.
|
|
|
@ -1,34 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Creating The Tiles From Rust
|
|
||||||
|
|
||||||
This step places the game tiles randomly. The code uses the `rand` dependency for the randomization. Add it to the `Cargo.toml` file using the `cargo` command.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo add rand@0.8
|
|
||||||
```
|
|
||||||
|
|
||||||
Change the main function to the following:
|
|
||||||
|
|
||||||
```rust,noplayground
|
|
||||||
{{#include main_tiles_from_rust.rs:tiles}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code takes the list of tiles, duplicates it, and shuffles it, accessing the `memory_tiles` property through the Rust code.
|
|
||||||
|
|
||||||
For each top-level property,
|
|
||||||
Slint generates a getter and a setter function. In this case `get_memory_tiles` and `set_memory_tiles`.
|
|
||||||
Since `memory_tiles` is a Slint array represented as a [`Rc<dyn slint::Model>`](https://slint.dev/docs/rust/slint/trait.Model).
|
|
||||||
|
|
||||||
You can't change the model generated by Slint, but you can extract the tiles from it and put them
|
|
||||||
in a [`VecModel`](https://slint.dev/docs/rust/slint/struct.VecModel) which implements the `Model` trait.
|
|
||||||
`VecModel` lets you make changes and you can use it to replace the static generated model.
|
|
||||||
|
|
||||||
The code clones the `tiles_model` because you use it later to update the game logic.
|
|
||||||
|
|
||||||
Running this code opens a window that now shows a 4 by 4 grid of rectangles, which show or hide
|
|
||||||
the icons when a player clicks on them.
|
|
||||||
|
|
||||||
There's one last aspect missing now, the rules for the game.
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/creating-the-tiles-from-rust.mp4"></video>
|
|
|
@ -1,39 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
# From One To Multiple Tiles
|
|
||||||
|
|
||||||
After modeling a single tile, this step creates a grid of them. For the grid to be a game board, you need two features:
|
|
||||||
|
|
||||||
1. **A data model**: An array created as a Rust model, where each element describes the tile data structure, such as:
|
|
||||||
|
|
||||||
- URL of the image
|
|
||||||
- Whether the image is visible
|
|
||||||
- If the player has solved this tile.
|
|
||||||
|
|
||||||
2. A way of creating multiple instances of the tiles.
|
|
||||||
|
|
||||||
With Slint you declare an array of structures based on a model using square brackets. Use a <span class="hljs-keyword">for</span> loop
|
|
||||||
to create multiple instances of the same element.
|
|
||||||
|
|
||||||
The <span class="hljs-keyword">for</span> loop is declarative and automatically updates when
|
|
||||||
the model changes. The loop instantiates all the <span class="hljs-title">MemoryTile</span> elements and places them on a grid based on their
|
|
||||||
index with spacing between the tiles.
|
|
||||||
|
|
||||||
First, add the tile data structure definition at the top of the `slint!` macro:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include main_multiple_tiles.rs:tile_data}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, replace the _export component <span class="hljs-title">MainWindow</span> inherits Window { ... }_ section at the bottom of the `slint!` macro with the following:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include main_multiple_tiles.rs:main_window}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The <code><span class="hljs-keyword">for</span> tile\[i\] <span class="hljs-keyword">in</span> memory_tiles:</code> syntax declares a variable `tile` which contains the data of one element from the `memory_tiles` array,
|
|
||||||
and a variable `i` which is the index of the tile. The code uses the `i` index to calculate the position of a tile, based on its row and column,
|
|
||||||
using modulo and integer division to create a 4 by 4 grid.
|
|
||||||
|
|
||||||
Running the code opens a window that shows 8 tiles, which a player can open individually.
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/from-one-to-multiple-tiles.mp4"></video>
|
|
|
@ -1,48 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Game Logic In Rust
|
|
||||||
|
|
||||||
This step implements the rules of the game in Rust.
|
|
||||||
|
|
||||||
Slint's general philosophy is that you implement the user interface in Slint and the business logic in your favorite programming
|
|
||||||
language.
|
|
||||||
|
|
||||||
The game rules enforce that at most two tiles have their curtain open. If the tiles match, then the game
|
|
||||||
considers them solved and they remain open. Otherwise, the game waits briefly so the player can memorize
|
|
||||||
the location of the icons, and then closes the curtains again.
|
|
||||||
|
|
||||||
Add the following code inside the <span class="hljs-title">MainWindow</span> component to signal to the Rust code when the user clicks on a tile.
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include main_game_logic_in_rust.rs:mainwindow_interface}}
|
|
||||||
```
|
|
||||||
|
|
||||||
This change adds a way for the <span class="hljs-title">MainWindow</span> to call to the Rust code that it should
|
|
||||||
check if a player has solved a pair of tiles. The Rust code needs an additional property to toggle to disable further
|
|
||||||
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed!
|
|
||||||
|
|
||||||
The last change to the code is to act when the <span class="hljs-title">MemoryTile</span> signals that a player clicked it.
|
|
||||||
|
|
||||||
Add the following handler in the <span class="hljs-title">MainWindow</span> `for` loop `clicked` handler:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include main_game_logic_in_rust.rs:tile_click_logic}}
|
|
||||||
```
|
|
||||||
|
|
||||||
On the Rust side, you can now add a handler to the `check_if_pair_solved` callback, that checks if a player opened two tiles.
|
|
||||||
If they match, the code sets the `solved` property to true in the model. If they don't
|
|
||||||
match, start a timer that closes the tiles after one second. While the timer is running, disable every tile so
|
|
||||||
a player can't click anything during this time.
|
|
||||||
|
|
||||||
Add this code before the `main_window.run().unwrap();` call:
|
|
||||||
|
|
||||||
```rust,noplayground
|
|
||||||
{{#include main_game_logic_in_rust.rs:game_logic}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code uses a [Weak](https://slint.dev/docs/rust/slint/struct.Weak) pointer of the `main_window`. This is
|
|
||||||
important because capturing a copy of the `main_window` itself within the callback handler would result in circular ownership.
|
|
||||||
The `MainWindow` owns the callback handler, which itself owns a reference to the `MainWindow`, which must be weak
|
|
||||||
instead of strong to avoid a memory leak.
|
|
||||||
|
|
||||||
These were the last changes and running the code opens a window that allows a player to play the game by the rules.
|
|
|
@ -1,33 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
This tutorial uses Rust as the host programming language. Slint also supports other programming languages like
|
|
||||||
[C++](https://slint.dev/docs/cpp/) or [JavaScript](https://slint.dev/docs/node/).
|
|
||||||
|
|
||||||
We recommend using [rust-analyzer](https://rust-analyzer.github.io) and [our editor integrations for Slint](https://github.com/slint-ui/slint/tree/master/editors) for following this tutorial.
|
|
||||||
|
|
||||||
Slint has an application template you can use to create a project with dependencies already set up that follows recommended best practices.
|
|
||||||
|
|
||||||
Before using the template, install `[cargo-generate](https://github.com/cargo-generate/cargo-generate)`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo install cargo-generate
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the template to create a new project with the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo generate --git https://github.com/slint-ui/slint-rust-template --name memory
|
|
||||||
cd memory
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace the contents of `src/main.rs` with the following:
|
|
||||||
|
|
||||||
```rust,noplayground
|
|
||||||
{{#include main_initial.rs:main}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the example with `cargo run` and a window appears with the green "Hello World" greeting.
|
|
||||||
|
|
||||||

|
|
|
@ -1 +0,0 @@
|
||||||
../../../../examples/memory/icons
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Ideas For The Reader
|
|
||||||
|
|
||||||
The game is visually bare. Here are some ideas on how you could make further changes to enhance it:
|
|
||||||
|
|
||||||
- The tiles could have rounded corners, to look less sharp. Use the [border-radius](https://slint.dev/docs/slint/src/language/builtins/elements#rectangle)
|
|
||||||
property of _[Rectangle](https://slint.dev/docs/slint/src/language/builtins/elements#rectangle)_ to achieve that.
|
|
||||||
|
|
||||||
- In real-world memory games, the back of the tiles often have some common graphic. You could add an image with
|
|
||||||
the help of another _[Image](https://slint.dev/docs/slint/src/language/builtins/elements#image)_
|
|
||||||
element. Note that you may have to use _Rectangle_'s _clip property_
|
|
||||||
element around it to ensure that the image is clipped away when the curtain effect opens.
|
|
||||||
|
|
||||||
Let us know in the comments on [Github Discussions](https://github.com/slint-ui/slint/discussions)
|
|
||||||
how you polished your code, or feel free to ask questions about how to implement something.
|
|
|
@ -1,15 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
# Introduction
|
|
||||||
|
|
||||||
This tutorial introduces you to the Slint UI framework in a playful way by implementing a memory game. It combines the Slint language for the graphics with the game rules implemented in Rust.
|
|
||||||
|
|
||||||
The game consists of a grid of 16 rectangular tiles. Clicking on a tile uncovers an icon underneath.
|
|
||||||
There are 8 different icons in total, so each tile has a sibling somewhere in the grid with the
|
|
||||||
same icon. The objective is to locate all icon pairs. The player can uncover two tiles at the same time. If they
|
|
||||||
aren't the same, the game obscures the icons again.
|
|
||||||
If the player uncovers two tiles with the same icon, then they remain visible - they're solved.
|
|
||||||
|
|
||||||
This is how the game looks in action:
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/memory_clip.mp4"
|
|
||||||
class="img-fluid img-thumbnail rounded"></video>
|
|
|
@ -1,41 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Memory Tile
|
|
||||||
|
|
||||||
With the skeleton code in place, this step looks at the first element of the game, the memory tile. It's the
|
|
||||||
visual building block that consists of an underlying filled rectangle background, the icon image. Later steps add a covering rectangle that acts as a curtain.
|
|
||||||
|
|
||||||
You declare the background rectangle as 64 logical pixels wide and tall
|
|
||||||
filled with a soothing tone of blue.
|
|
||||||
|
|
||||||
Lengths in Slint have a unit, here, the `px` suffix.
|
|
||||||
This makes the code easier to read and the compiler can detect when you accidentally
|
|
||||||
mix values with different units attached to them.
|
|
||||||
|
|
||||||
Copy the following code inside of the `slint!` macro, replacing the current content:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include main_memory_tile.rs:tile}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Inside the <span class="hljs-built_in">Rectangle</span> place an <span class="hljs-built_in">Image</span> element that
|
|
||||||
loads an icon with the <span class="hljs-built_in">@image-url()</span> macro.
|
|
||||||
|
|
||||||
When using the `slint!` macro, the path is relative to the folder that contains the `Cargo.toml` file.
|
|
||||||
When using Slint files, it's relative to the folder of the Slint file containing it.
|
|
||||||
|
|
||||||
You need to install this icon and others you use later first. You can download a pre-prepared
|
|
||||||
[Zip archive](https://slint.dev/blog/memory-game-tutorial/icons.zip) and extract it with the
|
|
||||||
following commands:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -O https://slint.dev/blog/memory-game-tutorial/icons.zip
|
|
||||||
unzip icons.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
This unpacks an `icons` directory containing several icons.
|
|
||||||
|
|
||||||
Running the program with `cargo run` opens a window that shows the icon of a bus on a
|
|
||||||
blue background.
|
|
||||||
|
|
||||||

|
|
|
@ -1,42 +0,0 @@
|
||||||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
|
||||||
|
|
||||||
# Polishing the Tile
|
|
||||||
|
|
||||||
In this step, you add a curtain-like cover that opens when clicked. You do this by declaring two rectangles
|
|
||||||
below the <span class="hljs-built_in">Image</span>, so that Slint draws them after the Image and thus on top of the image.
|
|
||||||
|
|
||||||
The <span class="hljs-built_in">TouchArea</span> element declares a transparent rectangular region that allows
|
|
||||||
reacting to user input such as a mouse click or tap. The element forwards a callback to the <em>MainWindow</em> indicating that a user clicked the tile.
|
|
||||||
|
|
||||||
The <em>MainWindow</em> reacts by flipping a custom <em>open_curtain</em> property.
|
|
||||||
Property bindings for the animated width and x properties also use the custom <em>open_curtain</em> property.
|
|
||||||
|
|
||||||
The following table shows more detail on the two states:
|
|
||||||
|
|
||||||
| _open_curtain_ value: | false | true |
|
|
||||||
| ----------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| Left curtain rectangle | Fill the left half by setting the width _width_ to half the parent's width | Width of zero makes the rectangle invisible |
|
|
||||||
| Right curtain rectangle | Fill the right half by setting _x_ and _width_ to half of the parent's width | _width_ of zero makes the rectangle invisible. _x_ moves to the right, sliding the curtain open when animated |
|
|
||||||
|
|
||||||
To make the tile extensible, replace the hard-coded icon name with an _icon_
|
|
||||||
property that can be set when instantiating the element.
|
|
||||||
|
|
||||||
For the final polish, add a
|
|
||||||
_solved_ property used to animate the color to a shade of green when a player finds a pair.
|
|
||||||
|
|
||||||
Replace the code inside the `slint!` macro with the following:
|
|
||||||
|
|
||||||
```slint
|
|
||||||
{{#include main_polishing_the_tile.rs:tile}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code uses `root` and `self`. `root` refers to the outermost
|
|
||||||
element in the component, the <span class="hljs-title">MemoryTile</span> in this case. `self` refers
|
|
||||||
to the current element.
|
|
||||||
|
|
||||||
The code exports the <span class="hljs-title">MainWindow</span> component. This is necessary so that you can later access it
|
|
||||||
from application business logic.
|
|
||||||
|
|
||||||
Running the code opens a window with a rectangle that opens up to show the bus icon when clicked. Subsequent clicks close and open the curtain again.
|
|
||||||
|
|
||||||
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/polishing-the-tile.mp4"></video>
|
|
|
@ -1 +0,0 @@
|
||||||
../../resources/slint-docs-highlight.html
|
|
|
@ -1 +0,0 @@
|
||||||
/* Disabled as part of disabling mdbook's highlight.js version, see highlight.js */
|
|
|
@ -1,6 +0,0 @@
|
||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
// This file is empty to override and disable mdbook's built-in highlight.js
|
|
||||||
// version, which doesn't include CMake support. Instead the appropriate version
|
|
||||||
// of highlight.js is included via head.hbs
|
|
|
@ -10,7 +10,9 @@ verify_ssl = true
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
sphinx = "==7.1.2"
|
sphinx = "==7.1.2"
|
||||||
myst_parser = ">=0.17.2"
|
myst_parser = ">=2.0.0"
|
||||||
|
sphinx-design = ">=0.5.0"
|
||||||
|
sphinx-tabs = ">=3.4.5"
|
||||||
sphinx-markdown-tables = ">=0.0.15"
|
sphinx-markdown-tables = ">=0.0.15"
|
||||||
furo = ">=2022.12.7"
|
furo = ">=2022.12.7"
|
||||||
sphinxcontrib-jquery = ">=4.1"
|
sphinxcontrib-jquery = ">=4.1"
|
||||||
|
|
16
docs/reference/_static/expand_tabs.js
Normal file
16
docs/reference/_static/expand_tabs.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Based on code from stsewd her https://github.com/readthedocs/readthedocs.org/commit/738b6b2836a7e0cadad48e7f407fdeaf7ba7a1d7
|
||||||
|
*/
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
const tabName = location.hash.substring(1);
|
||||||
|
if (tabName !== null) {
|
||||||
|
const tab = $('[data-sync-id~="' + tabName + '"]');
|
||||||
|
if (tab.length > 0) {
|
||||||
|
tab.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -34,7 +34,7 @@ author = "Slint Developers <info@slint.dev>"
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
# ones.
|
# ones.
|
||||||
extensions = ["myst_parser", "sphinx_markdown_tables", "sphinx.ext.autosectionlabel", "sphinxcontrib.jquery"]
|
extensions = ["myst_parser", "sphinx_markdown_tables", "sphinx.ext.autosectionlabel", "sphinxcontrib.jquery", "sphinx_tabs.tabs", "sphinx_design"]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ["_templates"]
|
templates_path = ["_templates"]
|
||||||
|
@ -86,7 +86,7 @@ html_theme_options = {
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ["_static"]
|
html_static_path = ["_static"]
|
||||||
|
html_js_files = ['expand_tabs.js']
|
||||||
html_show_sourcelink = False
|
html_show_sourcelink = False
|
||||||
|
|
||||||
html_logo = "https://slint.dev/logo/slint-logo-small-light.svg"
|
html_logo = "https://slint.dev/logo/slint-logo-small-light.svg"
|
||||||
|
@ -96,6 +96,7 @@ myst_enable_extensions = [
|
||||||
]
|
]
|
||||||
|
|
||||||
myst_url_schemes = {
|
myst_url_schemes = {
|
||||||
|
"slint-qs": f"https://slint.dev/releases/{version}/docs/quickstart/{{{{path}}}}",
|
||||||
"slint-cpp": f"https://slint.dev/releases/{version}/docs/cpp/{{{{path}}}}",
|
"slint-cpp": f"https://slint.dev/releases/{version}/docs/cpp/{{{{path}}}}",
|
||||||
"slint-rust": f"https://slint.dev/releases/{version}/docs/rust/slint/{{{{path}}}}",
|
"slint-rust": f"https://slint.dev/releases/{version}/docs/rust/slint/{{{{path}}}}",
|
||||||
"slint-build-rust": f"https://slint.dev/releases/{version}/docs/rust/slint_build/{{{{path}}}}",
|
"slint-build-rust": f"https://slint.dev/releases/{version}/docs/rust/slint_build/{{{{path}}}}",
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
src/introduction/index.rst
|
src/introduction/index.rst
|
||||||
src/introduction/supported_platforms.md
|
src/introduction/supported_platforms.md
|
||||||
|
src/quickstart/index.rst
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:hidden:
|
:hidden:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Conclusion
|
# Conclusion
|
||||||
|
|
||||||
This tutorial showed you how to combine built-in Slint elements with JavaScript code to build a
|
This tutorial showed you how to combine built-in Slint elements with C++, Rust, or NodeJS code to build a
|
||||||
game. There is much more to Slint, such as layouts, widgets, or styling.
|
game. There is much more to Slint, such as layouts, widgets, or styling.
|
||||||
|
|
||||||
We recommend the following links to continue:
|
We recommend the following links to continue:
|
79
docs/reference/src/quickstart/creating_the_tiles.md
Normal file
79
docs/reference/src/quickstart/creating_the_tiles.md
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
||||||
|
|
||||||
|
# Creating The Tiles From Code
|
||||||
|
|
||||||
|
This step places the game tiles randomly.
|
||||||
|
|
||||||
|
:::::{tab-set}
|
||||||
|
::::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
|
Change the `main` function and includes in `src/main.cpp` to the following:
|
||||||
|
|
||||||
|
:::{literalinclude} main_tiles_from_cpp.cpp
|
||||||
|
:lines: 10-28
|
||||||
|
:::
|
||||||
|
|
||||||
|
The code takes the list of tiles, duplicates it, and shuffles it, accessing the `memory_tiles` property through the C++ code.
|
||||||
|
|
||||||
|
For each top-level property, Slint generates a getter and a setter function. In this case `get_memory_tiles` and `set_memory_tiles`.
|
||||||
|
Since `memory_tiles` is a Slint array, it's represented as a [`std::shared_ptr<slint::Model>`](https://slint.dev/docs/cpp/api/classslint_1_1model).
|
||||||
|
|
||||||
|
You can't change the model generated by Slint, but you can extract the tiles from it and put them
|
||||||
|
in a [`slint::VectorModel`](https://slint.dev/docs/cpp/api/classslint_1_1vectormodel) which inherits from `Model`.
|
||||||
|
`VectorModel` lets you make changes and you can use it to replace the static generated model.
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
::::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
Change `main.js` to the following:
|
||||||
|
|
||||||
|
:::{literalinclude} main_tiles_from_js.js
|
||||||
|
:lines: 6-21
|
||||||
|
:::
|
||||||
|
|
||||||
|
The code takes the list of tiles, duplicates it, and shuffles it, accessing the `memory_tiles` property through the JavaScript code.
|
||||||
|
|
||||||
|
As `memory_tiles` is an array, it's represented as a JavaScript [`Array`](https://slint.dev/docs/node/).
|
||||||
|
You can't change the model generated by Slint, but you can extract the tiles from it and put them
|
||||||
|
in a [`slint.ArrayModel`](https://slint.dev/docs/node/classes/arraymodel.html) which implements the [`Model`](https://slint.dev/docs/node/interfaces/model.html) interface.
|
||||||
|
`ArrayModel` allows you to make changes and you can use it to replace the static generated model.
|
||||||
|
::::
|
||||||
|
|
||||||
|
::::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
The code uses the `rand` dependency for the randomization. Add it to the `Cargo.toml` file using the `cargo` command.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo add rand@0.8
|
||||||
|
```
|
||||||
|
|
||||||
|
Change the main function to the following:
|
||||||
|
|
||||||
|
:::{literalinclude} main_tiles_from_rust.rs
|
||||||
|
:lines: 6-26
|
||||||
|
:::
|
||||||
|
|
||||||
|
The code takes the list of tiles, duplicates it, and shuffles it, accessing the `memory_tiles` property through the Rust code.
|
||||||
|
|
||||||
|
For each top-level property,
|
||||||
|
Slint generates a getter and a setter function. In this case `get_memory_tiles` and `set_memory_tiles`.
|
||||||
|
Since `memory_tiles` is a Slint array represented as a [`Rc<dyn slint::Model>`](https://slint.dev/docs/rust/slint/trait.Model).
|
||||||
|
|
||||||
|
You can't change the model generated by Slint, but you can extract the tiles from it and put them
|
||||||
|
in a [`VecModel`](https://slint.dev/docs/rust/slint/struct.VecModel) which implements the `Model` trait.
|
||||||
|
`VecModel` lets you make changes and you can use it to replace the static generated model.
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
:::::
|
||||||
|
|
||||||
|
Running this code opens a window that now shows a 4 by 4 grid of rectangles, which show or hide
|
||||||
|
the icons when a player clicks on them.
|
||||||
|
|
||||||
|
There's one last aspect missing now, the rules for the game.
|
||||||
|
|
||||||
|
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/creating-the-tiles-from-rust.mp4"></video>
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
After modeling a single tile, this step creates a grid of them. For the grid to be a game board, you need two features:
|
After modeling a single tile, this step creates a grid of them. For the grid to be a game board, you need two features:
|
||||||
|
|
||||||
1. **A data model**: An array created as a C++ model, where each element describes the tile data structure, such as:
|
1. **A data model**: An array created as a model in code, where each element describes the tile data structure, such as:
|
||||||
|
|
||||||
- URL of the image
|
- URL of the image
|
||||||
- Whether the image is visible
|
- Whether the image is visible
|
||||||
|
@ -19,17 +19,63 @@ The <span class="hljs-keyword">for</span> loop is declarative and automatically
|
||||||
the model changes. The loop instantiates all the <span class="hljs-title">MemoryTile</span> elements and places them on a grid based on their
|
the model changes. The loop instantiates all the <span class="hljs-title">MemoryTile</span> elements and places them on a grid based on their
|
||||||
index with spacing between the tiles.
|
index with spacing between the tiles.
|
||||||
|
|
||||||
|
::::{tab-set}
|
||||||
|
:::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
First, add the tile data structure definition at the top of the `ui/appwindow.slint` file:
|
First, add the tile data structure definition at the top of the `ui/appwindow.slint` file:
|
||||||
|
|
||||||
```slint
|
:::
|
||||||
{{#include ../../rust/src/main_multiple_tiles.rs:tile_data}}
|
|
||||||
```
|
:::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
First, add the tile data structure definition at the top of the `ui/appwindow.slint` file:
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
First, add the tile data structure definition at the top of the `slint!` macro:
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
:::{literalinclude} main_multiple_tiles.rs
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 11-15
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::{tab-set}
|
||||||
|
:::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
Next, replace the _export component <span class="hljs-title">MainWindow</span> inherits Window { ... }_ section at the bottom of the `ui/appwindow.slint` file with the following:
|
Next, replace the _export component <span class="hljs-title">MainWindow</span> inherits Window { ... }_ section at the bottom of the `ui/appwindow.slint` file with the following:
|
||||||
|
|
||||||
```slint
|
:::
|
||||||
{{#include ../../rust/src/main_multiple_tiles.rs:main_window}}
|
|
||||||
```
|
:::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
Next, replace the _export component <span class="hljs-title">MainWindow</span> inherits Window { ... }_ section at the bottom of the `ui/appwindow.slint` file with the following:
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
Next, replace the _export component <span class="hljs-title">MainWindow</span> inherits Window { ... }_ section at the bottom of the `slint!` macro with the following:
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
:::{literalinclude} main_multiple_tiles.rs
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 63-90
|
||||||
|
:::
|
||||||
|
|
||||||
The <code><span class="hljs-keyword">for</span> tile\[i\] <span class="hljs-keyword">in</span> memory_tiles:</code> syntax declares a variable `tile` which contains the data of one element from the `memory_tiles` array,
|
The <code><span class="hljs-keyword">for</span> tile\[i\] <span class="hljs-keyword">in</span> memory_tiles:</code> syntax declares a variable `tile` which contains the data of one element from the `memory_tiles` array,
|
||||||
and a variable `i` which is the index of the tile. The code uses the `i` index to calculate the position of a tile, based on its row and column,
|
and a variable `i` which is the index of the tile. The code uses the `i` index to calculate the position of a tile, based on its row and column,
|
132
docs/reference/src/quickstart/game_logic.md
Normal file
132
docs/reference/src/quickstart/game_logic.md
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
||||||
|
|
||||||
|
# Game Logic
|
||||||
|
|
||||||
|
This step implements the rules of the game in your coding language of choice.
|
||||||
|
|
||||||
|
Slint's general philosophy is that you implement the user interface in Slint and the business logic in your favorite programming
|
||||||
|
language.
|
||||||
|
|
||||||
|
The game rules enforce that at most two tiles have their curtain open. If the tiles match, then the game
|
||||||
|
considers them solved and they remain open. Otherwise, the game waits briefly so the player can memorize
|
||||||
|
the location of the icons, and then closes the curtains again.
|
||||||
|
|
||||||
|
:::::{tab-set}
|
||||||
|
::::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
|
Add the following code inside the <span class="hljs-title">MainWindow</span> component to signal to the C++ code when the user clicks on a tile.
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic_in_rust.rs
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 107-115
|
||||||
|
:::
|
||||||
|
|
||||||
|
This change adds a way for the <span class="hljs-title">MainWindow</span> to call to the C++ code that it should
|
||||||
|
check if a player has solved a pair of tiles. The Rust code needs an additional property to toggle to disable further
|
||||||
|
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed!
|
||||||
|
|
||||||
|
The last change to the code is to act when the <span class="hljs-title">MemoryTile</span> signals that a player clicked it.
|
||||||
|
|
||||||
|
Add the following handler in the <span class="hljs-title">MainWindow</span> `for` loop `clicked` handler:
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic_in_rust.rs
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 126-143
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
On the C++ side, you can now add a handler to the `check_if_pair_solved` callback, that checks if a player opened two tiles.
|
||||||
|
If they match, the code sets the `solved` property to true in the model. If they don't
|
||||||
|
match, start a timer that closes the tiles after one second. While the timer is running, disable every tile so
|
||||||
|
a player can't click anything during this time.
|
||||||
|
|
||||||
|
Insert this code before the `main_window->run()` call:
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic.cpp
|
||||||
|
:lines: 29-65
|
||||||
|
:::
|
||||||
|
|
||||||
|
The code uses a [ComponentWeakHandle](https://slint.dev/docs/cpp/api/classslint_1_1ComponentWeakHandle) pointer of the `main_window`. This is
|
||||||
|
important because capturing a copy of the `main_window` itself within the callback handler would result in circular ownership.
|
||||||
|
The `MainWindow` owns the callback handler, which itself owns a reference to the `MainWindow`, which must be weak
|
||||||
|
instead of strong to avoid a memory leak.
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
::::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
Change the contents of `memory.slint` to signal to the JavaScript code when the user clicks on a tile.
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic_in_rust.rs
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 107-115
|
||||||
|
:::
|
||||||
|
|
||||||
|
This change adds a way for the <span class="hljs-title">MainWindow</span> to call to the JavaScript code that it should
|
||||||
|
check if a player has solved a pair of tiles. The Rust code needs an additional property to toggle to disable further
|
||||||
|
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed!
|
||||||
|
|
||||||
|
The last change to the code is to act when the <span class="hljs-title">MemoryTile</span> signals that a player clicked it.
|
||||||
|
|
||||||
|
Add the following handler in the <span class="hljs-title">MainWindow</span> `for` loop `clicked` handler:
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic_in_rust.rs
|
||||||
|
:lines: 126-143
|
||||||
|
:::
|
||||||
|
|
||||||
|
On the JavaScript side, now add a handler to the `check_if_pair_solved` callback, that checks if a player opened two tiles. If they match, the code sets the `solved` property to true in the model. If they don't
|
||||||
|
match, start a timer that closes the tiles after one second. While the timer is running, disable every tile so
|
||||||
|
a player can't click anything during this time.
|
||||||
|
|
||||||
|
Insert this code before the `mainWindow.run()` call:
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic.js
|
||||||
|
:lines: 23-63
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
::::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
Add the following code inside the <span class="hljs-title">MainWindow</span> component to signal to the Rust code when the user clicks on a tile.
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic_in_rust.rs
|
||||||
|
:lines: 107-115
|
||||||
|
:::
|
||||||
|
|
||||||
|
This change adds a way for the <span class="hljs-title">MainWindow</span> to call to the Rust code that it should
|
||||||
|
check if a player has solved a pair of tiles. The Rust code needs an additional property to toggle to disable further
|
||||||
|
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed!
|
||||||
|
|
||||||
|
The last change to the code is to act when the <span class="hljs-title">MemoryTile</span> signals that a player clicked it.
|
||||||
|
|
||||||
|
Add the following handler in the <span class="hljs-title">MainWindow</span> `for` loop `clicked` handler:
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic_in_rust.rs
|
||||||
|
:lines: 126-143
|
||||||
|
:::
|
||||||
|
|
||||||
|
On the Rust side, you can now add a handler to the `check_if_pair_solved` callback, that checks if a player opened two tiles.
|
||||||
|
If they match, the code sets the `solved` property to true in the model. If they don't
|
||||||
|
match, start a timer that closes the tiles after one second. While the timer is running, disable every tile so
|
||||||
|
a player can't click anything during this time.
|
||||||
|
|
||||||
|
Add this code before the `main_window.run().unwrap();` call:
|
||||||
|
|
||||||
|
:::{literalinclude} main_game_logic_in_rust.rs
|
||||||
|
:lines: 21-52
|
||||||
|
:::
|
||||||
|
|
||||||
|
The code uses a [Weak](https://slint.dev/docs/rust/slint/struct.Weak) pointer of the `main_window`. This is
|
||||||
|
important because capturing a copy of the `main_window` itself within the callback handler would result in circular ownership.
|
||||||
|
The `MainWindow` owns the callback handler, which itself owns a reference to the `MainWindow`, which must be weak
|
||||||
|
instead of strong to avoid a memory leak.
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
:::::
|
||||||
|
|
||||||
|
These were the last changes and running the code opens a window that allows a player to play the game by the rules.
|
167
docs/reference/src/quickstart/getting_started.md
Normal file
167
docs/reference/src/quickstart/getting_started.md
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
|
||||||
|
|
||||||
|
# Getting started
|
||||||
|
|
||||||
|
This tutorial shows you how to use the languages Slint supports as the host programming language.
|
||||||
|
|
||||||
|
We recommend using [our editor integrations for Slint](https://github.com/slint-ui/slint/tree/master/editors) for following this tutorial.
|
||||||
|
|
||||||
|
Slint has application templates you can use to create a project with dependencies already set up that follows recommended best practices.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
:::::{tab-set}
|
||||||
|
|
||||||
|
::::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
|
Before using the template, you need a C++ compiler that supports C++ 20 and to install [CMake](https://cmake.org/download/) 3.21 or newer.
|
||||||
|
|
||||||
|
Clone or download the template repository:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/slint-ui/slint-cpp-template memory
|
||||||
|
cd memory
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the project
|
||||||
|
|
||||||
|
The `CMakeLists.txt` uses the line `add_executable(my_application src/main.cpp)` to set `src/main.cpp` as the main C++ code file.
|
||||||
|
|
||||||
|
Change the content of `src/main.cpp` to the following:
|
||||||
|
|
||||||
|
:::{literalinclude} main_initial.cpp
|
||||||
|
:lines: 9-13
|
||||||
|
:::
|
||||||
|
|
||||||
|
Also in `CMakeLists.txt` the line
|
||||||
|
`slint_target_sources(my_application ui/appwindow.slint)` is a Slint function used to
|
||||||
|
add the `appwindow.slint` file to the target.
|
||||||
|
|
||||||
|
Change the contents of `ui/appwindow.slint` to the following:
|
||||||
|
|
||||||
|
:::{literalinclude} appwindow.slint
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 6-11
|
||||||
|
:::
|
||||||
|
|
||||||
|
Configure with CMake:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -B build
|
||||||
|
```
|
||||||
|
|
||||||
|
:::{tip}
|
||||||
|
When configuring with CMake, the FetchContent module fetches the source code of Slint via git.
|
||||||
|
This may take some time when building for the first time, as the process needs to build
|
||||||
|
the Slint runtime and compiler.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Build with CMake:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run the application
|
||||||
|
|
||||||
|
Run the application binary on Linux or macOS:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./build/my_application
|
||||||
|
```
|
||||||
|
|
||||||
|
Or on Windows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
build\my_application.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
This opens a window with a green "Hello World" greeting.
|
||||||
|
|
||||||
|
If you are stepping through this tutorial on a Windows machine, you can run the application at each step with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
my_application
|
||||||
|
```
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
::::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
Clone the template with the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/slint-ui/slint-nodejs-template memory
|
||||||
|
cd memory
|
||||||
|
```
|
||||||
|
|
||||||
|
Install dependencies with npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the project
|
||||||
|
|
||||||
|
The `package.json` file references `src/main.js` as the entry point for the application and `src/main.js` references `memory.slint` as the UI file.
|
||||||
|
|
||||||
|
Replace the contents of `src/main.js` with the following:
|
||||||
|
|
||||||
|
:::{literalinclude} main_initial.js
|
||||||
|
:lines: 6-10
|
||||||
|
:::
|
||||||
|
|
||||||
|
The `slint.loadFile` method resolves files from the process's current working directory, so from the `package.json` file's location.
|
||||||
|
|
||||||
|
Replace the contents of `ui/appwindow.slint` with the following:
|
||||||
|
|
||||||
|
:::{literalinclude} memory.slint
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 6-11
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Run the application
|
||||||
|
|
||||||
|
Run the example with `npm start` and a window appears with the green "Hello World" greeting.
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
::::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
We recommend using [rust-analyzer](https://rust-analyzer.github.io) and [our editor integrations for Slint](https://github.com/slint-ui/slint/tree/master/editors) for following this tutorial.
|
||||||
|
|
||||||
|
Slint has an application template you can use to create a project with dependencies already set up that follows recommended best practices.
|
||||||
|
|
||||||
|
Before using the template, install [`cargo-generate`](https://github.com/cargo-generate/cargo-generate):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo install cargo-generate
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the template to create a new project with the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo generate --git https://github.com/slint-ui/slint-rust-template --name memory
|
||||||
|
cd memory
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the project
|
||||||
|
|
||||||
|
Replace the contents of `src/main.rs` with the following:
|
||||||
|
|
||||||
|
:::{literalinclude} main_initial.rs
|
||||||
|
:lines: 6-17
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Run the application
|
||||||
|
|
||||||
|
Run the example with `cargo run` and a window appears with the green "Hello World" greeting.
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
:::::
|
||||||
|
|
||||||
|

|
34
docs/reference/src/quickstart/index.rst
Normal file
34
docs/reference/src/quickstart/index.rst
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
.. Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
.. SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
Quickstart
|
||||||
|
==========
|
||||||
|
|
||||||
|
This tutorial introduces you to the Slint UI framework in a playful way by implementing a memory game. It combines the Slint language for the graphics with the game rules implemented in C++, Rust, or NodeJS.
|
||||||
|
|
||||||
|
The game consists of a grid of 16 rectangular tiles. Clicking on a tile uncovers an icon underneath.
|
||||||
|
There are 8 different icons in total, so each tile has a sibling somewhere in the grid with the
|
||||||
|
same icon. The objective is to locate all icon pairs. The player can uncover two tiles at the same time. If they
|
||||||
|
aren't the same, the game obscures the icons again.
|
||||||
|
If the player uncovers two tiles with the same icon, then they remain visible - they're solved.
|
||||||
|
|
||||||
|
This is how the game looks in action:
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/memory_clip.mp4" class="img-fluid img-thumbnail rounded"></video>
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:hidden:
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Quickstart
|
||||||
|
|
||||||
|
getting_started.md
|
||||||
|
memory_tile.md
|
||||||
|
polishing_the_tile.md
|
||||||
|
from_one_to_multiple_tiles.md
|
||||||
|
creating_the_tiles.md
|
||||||
|
game_logic.md
|
||||||
|
running_in_a_browser.md
|
||||||
|
ideas_for_the_reader.md
|
||||||
|
conclusion.md
|
|
@ -8,7 +8,7 @@ import * as slint from "slint-ui";
|
||||||
let ui = slint.loadFile("./memory.slint");
|
let ui = slint.loadFile("./memory.slint");
|
||||||
let mainWindow = new ui.MainWindow();
|
let mainWindow = new ui.MainWindow();
|
||||||
|
|
||||||
let initial_tiles = [...mainWindow.memory_tiles];
|
let initial_tiles = mainWindow.memory_tiles;
|
||||||
let tiles = initial_tiles.concat(initial_tiles.map((tile) => Object.assign({}, tile)));
|
let tiles = initial_tiles.concat(initial_tiles.map((tile) => Object.assign({}, tile)));
|
||||||
|
|
||||||
for (let i = tiles.length - 1; i > 0; i--) {
|
for (let i = tiles.length - 1; i > 0; i--) {
|
|
@ -81,7 +81,6 @@ slint::slint! {
|
||||||
// Left curtain
|
// Left curtain
|
||||||
Rectangle {
|
Rectangle {
|
||||||
background: #193076;
|
background: #193076;
|
||||||
x: 0px;
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
width: open_curtain ? 0px : (parent.width / 2);
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
animate width { duration: 250ms; easing: ease-in; }
|
|
@ -37,7 +37,6 @@ component MemoryTile inherits Rectangle {
|
||||||
// Left curtain
|
// Left curtain
|
||||||
Rectangle {
|
Rectangle {
|
||||||
background: #193076;
|
background: #193076;
|
||||||
x: 0px;
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
width: open_curtain ? 0px : (parent.width / 2);
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
animate width { duration: 250ms; easing: ease-in; }
|
|
@ -7,7 +7,7 @@ import * as slint from "slint-ui";
|
||||||
let ui = slint.loadFile("./ui/appwindow.slint");
|
let ui = slint.loadFile("./ui/appwindow.slint");
|
||||||
let mainWindow = new ui.MainWindow();
|
let mainWindow = new ui.MainWindow();
|
||||||
|
|
||||||
let initial_tiles = [...mainWindow.memory_tiles];
|
let initial_tiles = mainWindow.memory_tiles;
|
||||||
let tiles = initial_tiles.concat(initial_tiles.map((tile) => Object.assign({}, tile)));
|
let tiles = initial_tiles.concat(initial_tiles.map((tile) => Object.assign({}, tile)));
|
||||||
|
|
||||||
for (let i = tiles.length - 1; i > 0; i--) {
|
for (let i = tiles.length - 1; i > 0; i--) {
|
|
@ -53,7 +53,6 @@ slint::slint! {
|
||||||
// Left curtain
|
// Left curtain
|
||||||
Rectangle {
|
Rectangle {
|
||||||
background: #193076;
|
background: #193076;
|
||||||
x: 0px;
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
width: open_curtain ? 0px : (parent.width / 2);
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
animate width { duration: 250ms; easing: ease-in; }
|
|
@ -27,7 +27,6 @@ component MemoryTile inherits Rectangle {
|
||||||
// Left curtain
|
// Left curtain
|
||||||
Rectangle {
|
Rectangle {
|
||||||
background: #193076;
|
background: #193076;
|
||||||
x: 0px;
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
width: open_curtain ? 0px : (parent.width / 2);
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
animate width { duration: 250ms; easing: ease-in; }
|
|
@ -12,17 +12,67 @@ Lengths in Slint have a unit, here, the `px` suffix.
|
||||||
This makes the code easier to read and the compiler can detect when you accidentally
|
This makes the code easier to read and the compiler can detect when you accidentally
|
||||||
mix values with different units attached to them.
|
mix values with different units attached to them.
|
||||||
|
|
||||||
|
::::{tab-set}
|
||||||
|
|
||||||
|
:::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
Copy the following code into `ui/appwindow.slint` file, replacing the current content:
|
Copy the following code into `ui/appwindow.slint` file, replacing the current content:
|
||||||
|
|
||||||
```slint
|
:::
|
||||||
{{#include memory_tile.slint:main_window}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The code exports the <span class="hljs-title">MainWindow</span> component so that the C++ code can access it later.
|
:::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
Copy the following code into `ui/appwindow.slint` file, replacing the current content:
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
Copy the following code inside of the `slint!` macro, replacing the current content:
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
:::{literalinclude} memory_tile.slint
|
||||||
|
:language: slint,no-preview
|
||||||
|
:lines: 5-19
|
||||||
|
:::
|
||||||
|
|
||||||
|
This exports the <span class="hljs-title">MainWindow</span> component so that the game logic code can access it later.
|
||||||
|
|
||||||
|
::::{tab-set}
|
||||||
|
:::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
Inside the <span class="hljs-built_in">Rectangle</span> place an <span class="hljs-built_in">Image</span> element that
|
Inside the <span class="hljs-built_in">Rectangle</span> place an <span class="hljs-built_in">Image</span> element that
|
||||||
loads an icon with the <span class="hljs-built_in">@image-url()</span> macro. The path is relative to the location of `ui/appwindow.slint`.
|
loads an icon with the <span class="hljs-built_in">@image-url()</span> macro. The path is relative to the location of `ui/appwindow.slint`.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
Inside the <span class="hljs-built_in">Rectangle</span> place an <span class="hljs-built_in">Image</span> element that
|
||||||
|
loads an icon with the <span class="hljs-built_in">@image-url()</span> macro. The path is relative to the location of `ui/appwindow.slint`.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
Inside the <span class="hljs-built_in">Rectangle</span> place an <span class="hljs-built_in">Image</span> element that
|
||||||
|
loads an icon with the <span class="hljs-built_in">@image-url()</span> macro.
|
||||||
|
When using the `slint!` macro, the path is relative to the folder that contains the `Cargo.toml` file.
|
||||||
|
When using Slint files, it's relative to the folder of the Slint file containing it.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
You need to install this icon and others you use later first. You can download a pre-prepared
|
You need to install this icon and others you use later first. You can download a pre-prepared
|
||||||
[Zip archive](https://slint.dev/blog/memory-game-tutorial/icons.zip) to the `ui` folder,
|
[Zip archive](https://slint.dev/blog/memory-game-tutorial/icons.zip) to the `ui` folder,
|
||||||
|
|
||||||
|
@ -46,6 +96,28 @@ cd ..
|
||||||
|
|
||||||
This unpacks an `icons` directory containing several icons.
|
This unpacks an `icons` directory containing several icons.
|
||||||
|
|
||||||
|
::::{tab-set}
|
||||||
|
:::{tab-item} C++
|
||||||
|
:sync: cpp
|
||||||
|
|
||||||
Compiling the program with `cmake --build build` and running with the `./build/my_application` opens a window that shows the icon of a bus on a blue background.
|
Compiling the program with `cmake --build build` and running with the `./build/my_application` opens a window that shows the icon of a bus on a blue background.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} NodeJS
|
||||||
|
:sync: nodejs
|
||||||
|
|
||||||
|
Running the program with `npm start` opens a window that shows the icon of a bus on a blue background.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} Rust
|
||||||
|
:sync: rust
|
||||||
|
|
||||||
|
Running the program with `cargo run` opens a window that shows the icon of a bus on a blue background.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||

|

|
|
@ -27,7 +27,6 @@ component MemoryTile inherits Rectangle {
|
||||||
// Left curtain
|
// Left curtain
|
||||||
Rectangle {
|
Rectangle {
|
||||||
background: #193076;
|
background: #193076;
|
||||||
x: 0px;
|
|
||||||
width: open_curtain ? 0px : (parent.width / 2);
|
width: open_curtain ? 0px : (parent.width / 2);
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
animate width { duration: 250ms; easing: ease-in; }
|
animate width { duration: 250ms; easing: ease-in; }
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "memory",
|
"name": "memory",
|
||||||
"version": "1.6.0",
|
"version": "1.5.1",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
|
@ -26,9 +26,10 @@ _solved_ property used to animate the color to a shade of green when a player fi
|
||||||
|
|
||||||
Replace the code inside the `ui/appwindow.slint` file with the following:
|
Replace the code inside the `ui/appwindow.slint` file with the following:
|
||||||
|
|
||||||
```slint
|
:::{literalinclude} main_polishing_the_tile.rs
|
||||||
{{#include ../../rust/src/main_polishing_the_tile.rs:tile}}
|
:language: slint,no-preview
|
||||||
```
|
:lines: 10-61
|
||||||
|
:::
|
||||||
|
|
||||||
The code uses `root` and `self`. `root` refers to the outermost
|
The code uses `root` and `self`. `root` refers to the outermost
|
||||||
element in the component, the <span class="hljs-title">MemoryTile</span> in this case. `self` refers
|
element in the component, the <span class="hljs-title">MemoryTile</span> in this case. `self` refers
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
# Running In A Browser Using WebAssembly
|
# Running In A Browser Using WebAssembly
|
||||||
|
|
||||||
The tutorial so far used `cargo run` to build and run the code as a native application.
|
:::{warning}
|
||||||
|
Only Rust supports using Slint with WebAssembly.
|
||||||
|
:::
|
||||||
|
|
||||||
|
If you're using Rust, the tutorial so far used `cargo run` to build and run the code as a native application.
|
||||||
Native applications are the primary target of the Slint framework, but it also supports WebAssembly
|
Native applications are the primary target of the Slint framework, but it also supports WebAssembly
|
||||||
for demonstration purposes. This section uses the standard rust tool `wasm-bindgen` and
|
for demonstration purposes. This section uses the standard rust tool `wasm-bindgen` and
|
||||||
`wasm-pack` to run the game in the browser. Read the [wasm-bindgen documentation](https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html)
|
`wasm-pack` to run the game in the browser. Read the [wasm-bindgen documentation](https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html)
|
Loading…
Add table
Add a link
Reference in a new issue