-
Notifications
You must be signed in to change notification settings - Fork 644
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor CPP Quickstart to use project template project (#4722)
* Refactor CPP Quickstart to use project template project * Update docs/tutorial/cpp/src/main_initial.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update docs/tutorial/cpp/src/game_logic_in_cpp.md Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev> * Update docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev> * Update docs/tutorial/cpp/src/from_one_to_multiple_tiles.md Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev> * Update docs/tutorial/cpp/src/from_one_to_multiple_tiles.md Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev> * Update docs/tutorial/cpp/src/getting_started.md Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev> * Remove commented text * Re-add removed powershell icon commands * Undo rename * Correct path in CMakeLists.txt --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev>
- Loading branch information
1 parent
c3a2ad1
commit 9345638
Showing
14 changed files
with
184 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> | ||
|
||
# Conclusion | ||
|
||
In this tutorial, we have demonstrated how to combine some built-in Slint elements with C++ code to build a little | ||
game. There are many more features that we haven't talked about, such as layouts, widgets, or styling. | ||
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): In the Slint repository we have collected a few 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/cpp/): The reference documentation for the C++ library. | ||
- [Examples](https://github.com/slint-ui/slint/tree/master/examples): In the Slint repository we have collected 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 Rust crate that allows you to dynamically load Slint files. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,27 @@ | ||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> | ||
|
||
# Creating The Tiles From C++ | ||
|
||
What we'll do is take the list of tiles declared in the .slint language, duplicate it, and shuffle it. | ||
We'll do so by accessing the `memory_tiles` property through the C++ code. For each top-level property, | ||
a getter and a setter function is generated - in our case `get_memory_tiles` and `set_memory_tiles`. | ||
Since `memory_tiles` is an array in the `.slint` language, it's represented as a [`std::shared_ptr<slint::Model>`](https://slint.dev/docs/cpp/api/classslint_1_1model). | ||
We can't modify the model generated by the .slint, but we can extract the tiles from it, and put it | ||
in a [`slint::VectorModel`](https://slint.dev/docs/cpp/api/classslint_1_1vectormodel) which inherits from `Model`. | ||
`VectorModel` allows us to make modifications and we can use it to replace the static generated model. | ||
This step places the game tiles randomly. | ||
|
||
We modify the main function like so: | ||
Change the `main` function and includes in `src/main.cpp` to the following: | ||
|
||
```cpp | ||
{{#include main_tiles_from_cpp.cpp:main}} | ||
``` | ||
Running this gives us a window on the screen that now shows a 4 by 4 grid of rectangles, which can show or obscure | ||
the icons when clicking. There's only one last aspect missing now, the rules for the game. | ||
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,40 @@ | ||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> | ||
|
||
# From One To Multiple Tiles | ||
|
||
After modeling a single tile, let's create a grid of them. For the grid to be our game board, we 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: | ||
|
||
- 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. | ||
|
||
1. A data model: This shall be an array where each element describes the tile data structure, such as the | ||
url of the image, whether the image shall be visible and if this tile has been solved. We modify the model | ||
from C++ code. | ||
2. A way of creating many instances of the tiles, with the above `.slint` markup code. | ||
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. | ||
|
||
In Slint we can declare an array of structures using brackets, to create a model. We can use the <span class="hljs-keyword">for</span> loop | ||
to create many instances of the same element. In `.slint` the for loop is declarative and automatically updates when | ||
the model changes. We instantiate all the different <span class="hljs-title">MemoryTile</span> elements and place them on a grid based on their | ||
index with a little bit of spacing between the tiles. | ||
With Slint 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, we copy the tile data structure definition and paste it at top inside the `memory.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}} | ||
``` | ||
|
||
Next, we replace the _export component <span class="hljs-title">MainWindow</span> inherits Window { ... }_ section at the bottom of the `memory.slint` file with the following snippet: | ||
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}} | ||
``` | ||
|
||
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. We use the `i` index to calculate the position of tile based on its row and column, | ||
using the modulo and integer division to create a 4 by 4 grid. | ||
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 this gives us a window that shows 8 tiles, which can be opened individually. | ||
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,48 @@ | ||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> | ||
|
||
# Game Logic In C++ | ||
|
||
We'll implement the rules of the game in C++ as well. The general philosophy of Slint is that merely the user | ||
interface is implemented in the `.slint` language and the business logic in your favorite programming | ||
language. The game rules shall enforce that at most two tiles have their curtain open. If the tiles match, then we | ||
consider them solved and they remain open. Otherwise we wait for a little while, so the player can memorize | ||
the location of the icons, and then close them again. | ||
This step implements the rules of the game in C++ as well. | ||
|
||
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. | ||
|
||
We'll modify the `.slint` markup in the `memory.slint` file to signal to the C++ code when the user clicks on a tile. | ||
Two changes to <span class="hljs-title">MainWindow</span> are needed: We need to add a way for the MainWindow to call to the C++ code that it should | ||
check if a pair of tiles has been solved. And we need to add a property that C++ code can toggle to disable further | ||
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed! First, we paste | ||
the callback and property declarations into <span class="hljs-title">MainWindow</span>: | ||
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}} | ||
``` | ||
|
||
The last change to the `.slint` markup is to act when the <span class="hljs-title">MemoryTile</span> signals that it was clicked on. | ||
We add the following handler in <span class="hljs-title">MainWindow</span>: | ||
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 C++ 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, we can now add an handler to the `check_if_pair_solved` callback, that will check if | ||
two tiles are opened. If they match, the `solved` property is set to true in the model. If they don't | ||
match, start a timer that will close them after one second. While the timer is running, we disable every tile so | ||
one can't click anything during this time. | ||
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}} | ||
``` | ||
Notice that we take a weak pointer of our `main_window`. This is very | ||
important because capturing a copy of the `main_window` itself within the callback handler would result in a circular ownership. | ||
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 result gives us a window on the screen that allows us | ||
to play the game by the rules. | ||
These were the last changes and running the code opens a window that allows a player to play the game by the rules. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,73 @@ | ||
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> | ||
|
||
# Getting Started | ||
|
||
In this tutorial, we use C++ as the host programming language. We also support other programming languages like | ||
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/). | ||
|
||
You will need a development environment that can compile C++20, [CMake 3.21](https://cmake.org/download/), | ||
and we recommend [Ninja](https://ninja-build.org) for `-GNinja`. | ||
We don't provide binaries of Slint yet, so we will use the CMake integration that will automatically build | ||
the tools and library from source. Since it's implemented in the Rust programming language, this means that | ||
you also need to install a Rust compiler (1.70 or newer). You can easily install a Rust compiler | ||
following the instruction from [the Rust website](https://www.rust-lang.org/learn/get-started). | ||
We're going to use `cmake`'s builtin FetchContent module to fetch the source code of Slint. | ||
|
||
In a new directory, we create a new `CMakeLists.txt` file. | ||
|
||
```cmake | ||
# CMakeLists.txt | ||
cmake_minimum_required(VERSION 3.21) | ||
project(memory LANGUAGES CXX) | ||
include(FetchContent) | ||
FetchContent_Declare( | ||
Slint | ||
GIT_REPOSITORY https://github.com/slint-ui/slint.git | ||
# `release/1` will auto-upgrade to the latest Slint >= 1.0.0 and < 2.0.0 | ||
# `release/1.0` will auto-upgrade to the latest Slint >= 1.0.0 and < 1.1.0 | ||
GIT_TAG release/1 | ||
SOURCE_SUBDIR api/cpp | ||
) | ||
FetchContent_MakeAvailable(Slint) | ||
add_executable(memory_game main.cpp) | ||
target_link_libraries(memory_game PRIVATE Slint::Slint) | ||
slint_target_sources(memory_game memory.slint) | ||
# On Windows, copy the Slint DLL next to the application binary so that it's found. | ||
if (WIN32) | ||
add_custom_command(TARGET memory_game POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:memory_game> $<TARGET_FILE_DIR:memory_game> COMMAND_EXPAND_LISTS) | ||
endif() | ||
``` | ||
Slint has an application template you can use to create a project with dependencies already set up that follows recommended best practices. | ||
|
||
This should look familiar to people familiar with CMake. We see that this CMakeLists.txt | ||
references a `main.cpp`, which we will add later, and it also has a line | ||
`slint_target_sources(memory_game memory.slint)`, which is a Slint function used to | ||
add the `memory.slint` file to the target. We must then create, in the same directory, | ||
the `memory.slint` file. Let's just fill it with a hello world for now: | ||
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. | ||
|
||
```slint | ||
{{#include memory.slint:main_window}} | ||
Clone or download template repository: | ||
|
||
```sh | ||
git clone https://github.com/slint-ui/slint-cpp-template memory | ||
cd memory | ||
``` | ||
|
||
What's still missing is the `main.cpp`: | ||
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}} | ||
``` | ||
To recap, we now have a directory with a `CMakeLists.txt`, `memory.slint` and `main.cpp`. | ||
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}} | ||
``` | ||
|
||
We can now compile the program in a terminal: | ||
Configure with CMake: | ||
|
||
```sh | ||
cmake -GNinja . | ||
cmake --build . | ||
cmake -B build | ||
``` | ||
|
||
If you are on Linux or macOS, you can run the program: | ||
Build with CMake: | ||
|
||
```sh | ||
./memory_game | ||
cmake --build build | ||
``` | ||
|
||
and a window will appear with the green "Hello World" greeting. | ||
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 | ||
memory_game | ||
my_application | ||
``` | ||
|
||
 | ||
|
||
Feel free to use your favorite IDE for this purpose, or use out-of-tree build, or Ninja, ... | ||
We just keep it simple here for the purpose of this blog. | ||
|
||
_Note_: When configuring with CMake, the FetchContent module will fetch the source code of Slint via git. | ||
this may take some time. When building for the first time, the first thing that need to be build | ||
is the Slint runtime and compiler, this can take a few minutes. | ||
_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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.