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>
This commit is contained in:
Chris Chinchilla 2024-03-11 15:09:01 +01:00 committed by GitHub
parent c3a2ad1c45
commit 9345638191
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 183 additions and 166 deletions

View file

@ -9,7 +9,7 @@ endif()
add_executable(memory_tutorial_initial main_initial.cpp) add_executable(memory_tutorial_initial main_initial.cpp)
target_link_libraries(memory_tutorial_initial PRIVATE Slint::Slint) target_link_libraries(memory_tutorial_initial PRIVATE Slint::Slint)
slint_target_sources(memory_tutorial_initial memory.slint) slint_target_sources(memory_tutorial_initial appwindow.slint)
add_executable(memory_tutorial_tiles_from_cpp main_tiles_from_cpp.cpp) add_executable(memory_tutorial_tiles_from_cpp main_tiles_from_cpp.cpp)
target_link_libraries(memory_tutorial_tiles_from_cpp PRIVATE Slint::Slint) target_link_libraries(memory_tutorial_tiles_from_cpp PRIVATE Slint::Slint)

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// ANCHOR: main_window // ANCHOR: main_window
// memory.slint // ui/appwindow.slint
export component MainWindow inherits Window { export component MainWindow inherits Window {
Text { Text {
text: "hello world"; text: "hello world";

View file

@ -1,12 +1,14 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Conclusion # Conclusion
In this tutorial, we have demonstrated how to combine some built-in Slint elements with C++ code to build a little This tutorial showed you how to combine built-in Slint elements with C++ code to build a
game. There are many more features that we haven't talked about, 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:
* [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. - [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. - [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. - [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. - [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.

View file

@ -1,21 +1,27 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Creating The Tiles From C++ # 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. This step places the game tiles randomly.
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.
We modify the main function like so: Change the `main` function and includes in `src/main.cpp` to the following:
```cpp ```cpp
{{#include main_tiles_from_cpp.cpp:main}} {{#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 code takes the list of tiles, duplicates it, and shuffles it, accessing the `memory_tiles` property through the C++ code.
the icons when clicking. There's only one last aspect missing now, the rules for the game.
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> <video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/creating-the-tiles-from-rust.mp4"></video>

View file

@ -1,34 +1,40 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# From One To Multiple Tiles # 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: This shall be an array where each element describes the tile data structure, such as the 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 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.
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 - URL of the image
to create many instances of the same element. In `.slint` the for loop is declarative and automatically updates when - Whether the image is visible
the model changes. We instantiate all the different <span class="hljs-title">MemoryTile</span> elements and place them on a grid based on their - If the player has solved this tile.
index with a little bit of spacing between the tiles.
First, we copy the tile data structure definition and paste it at top inside the `memory.slint` file: 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.
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, add the tile data structure definition at the top of the `ui/appwindow.slint` file:
```slint ```slint
{{#include ../../rust/src/main_multiple_tiles.rs:tile_data}} {{#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 ```slint
{{#include ../../rust/src/main_multiple_tiles.rs:main_window}} {{#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, 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, 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 the modulo and integer division to create a 4 by 4 grid. 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> <video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/from-one-to-multiple-tiles.mp4"></video>

View file

@ -1,33 +1,38 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Game Logic In C++ # 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 This step implements the rules of the game in C++ as well.
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.
We'll modify the `.slint` markup in the `memory.slint` file to signal to the C++ code when the user clicks on a tile. Slint's general philosophy is that you implement the user interface in Slint and the business logic in your favorite programming
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 language.
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 game rules enforce that at most two tiles have their curtain open. If the tiles match, then the game
the callback and property declarations into <span class="hljs-title">MainWindow</span>: 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 ```slint
{{#include ../../rust/src/main_game_logic_in_rust.rs:mainwindow_interface}} {{#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. This change adds a way for the <span class="hljs-title">MainWindow</span> to call to the C++ code that it should
We add the following handler in <span class="hljs-title">MainWindow</span>: 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 ```slint
{{#include ../../rust/src/main_game_logic_in_rust.rs:tile_click_logic}} {{#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 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.
two tiles are opened. If they match, the `solved` property is set to true in the model. If they don't If they match, the code sets the `solved` property 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 match, start a timer that closes the tiles after one second. While the timer is running, disable every tile so
one can't click anything during this time. a player can't click anything during this time.
Insert this code before the `main_window->run()` call: Insert this code before the `main_window->run()` call:
@ -35,10 +40,9 @@ Insert this code before the `main_window->run()` call:
{{#include main_game_logic.cpp:game_logic}} {{#include main_game_logic.cpp:game_logic}}
``` ```
Notice that we take a weak pointer of our `main_window`. This is very 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 a circular ownership. 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 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. 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 These were the last changes and running the code opens a window that allows a player to play the game by the rules.
to play the game by the rules.

View file

@ -1,88 +1,73 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Getting Started # 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/). [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/), Slint has an application template you can use to create a project with dependencies already set up that follows recommended best practices.
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. 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.
```cmake Clone or download template repository:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(memory LANGUAGES CXX)
include(FetchContent) ```sh
FetchContent_Declare( git clone https://github.com/slint-ui/slint-cpp-template memory
Slint cd memory
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()
``` ```
This should look familiar to people familiar with CMake. We see that this CMakeLists.txt The `CMakeLists.txt` uses the line `add_executable(my_application src/main.cpp)` to set `src/main.cpp` as the main C++ code file.
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:
```slint Change the content of `src/main.cpp` to the following:
{{#include memory.slint:main_window}}
```
What's still missing is the `main.cpp`:
```cpp ```cpp
{{#include main_initial.cpp:main}} {{#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.
We can now compile the program in a terminal: Change the contents of `ui/appwindow.slint` to the following:
```sh ```slint
cmake -GNinja . {{#include appwindow.slint:main_window}}
cmake --build .
``` ```
If you are on Linux or macOS, you can run the program: Configure with CMake:
```sh ```sh
./memory_game cmake -B build
``` ```
and a window will appear with the green "Hello World" greeting. 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 If you are stepping through this tutorial on a Windows machine, you can run it with
```sh ```sh
memory_game my_application
``` ```
![Screenshot of initial tutorial app showing Hello World](https://slint.dev/blog/memory-game-tutorial/getting-started.png "Hello World") ![Screenshot of initial tutorial app showing Hello World](https://slint.dev/blog/memory-game-tutorial/getting-started.png "Hello World")
Feel free to use your favorite IDE for this purpose, or use out-of-tree build, or Ninja, ... _Note_: When configuring with CMake, the FetchContent module fetches the source code of Slint via git.
We just keep it simple here for the purpose of this blog. This may take some time when building for the first time, as the process needs to build
the Slint runtime and compiler.
_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.

View file

@ -1,12 +1,13 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Ideas For The Reader # Ideas For The Reader
The game is visually a little bare. Here are some ideas how you could make further changes to enhance it: 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 a little less sharp. The [border-radius](https://slint.dev/docs/slint/src/language/builtins/elements#rectangle) - 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_ can be used to achieve that. 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 - 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)_ 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. 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. element around it to ensure that the image is clipped away when the curtain effect opens.

View file

@ -1,15 +1,16 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Introduction # Introduction
This tutorial will introduce you to the Slint UI framework in a playful way by implementing a little memory game. We're going to combine the `.slint` language for the graphics with the game rules implemented in C++. 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. The game consists of a grid of 16 rectangular tiles. Clicking on a tile uncovers an icon underneath.
We know that there are 8 different icons in total, so each tile has a sibling somewhere in the grid with the 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. You can uncover two tiles at the same time. If they 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 icons will be obscured again. aren't the same, the game obscures the icons again.
If you uncover two tiles with the same icon, then they remain visible - they're solved. If the player uncovers two tiles with the same icon, then they remain visible - they're solved.
This is how the game looks like in action: This is how the game looks in action:
<video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/memory_clip.mp4" <video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/memory_clip.mp4"
class="img-fluid img-thumbnail rounded"></video> class="img-fluid img-thumbnail rounded"></video>

View file

@ -21,11 +21,11 @@ int main()
std::default_random_engine rng {}; std::default_random_engine rng {};
std::shuffle(new_tiles.begin(), new_tiles.end(), rng); std::shuffle(new_tiles.begin(), new_tiles.end(), rng);
// ANCHOR: game_logic
auto tiles_model = std::make_shared<slint::VectorModel<TileData>>(new_tiles); auto tiles_model = std::make_shared<slint::VectorModel<TileData>>(new_tiles);
main_window->set_memory_tiles(tiles_model); main_window->set_memory_tiles(tiles_model);
// ANCHOR: game_logic
main_window->on_check_if_pair_solved( main_window->on_check_if_pair_solved(
[main_window_weak = slint::ComponentWeakHandle(main_window)] { [main_window_weak = slint::ComponentWeakHandle(main_window)] {
auto main_window = *main_window_weak.lock(); auto main_window = *main_window_weak.lock();
@ -63,8 +63,7 @@ int main()
}); });
} }
}); });
// ANCHOR_END: game_logic
main_window->run(); main_window->run();
} }
// ANCHOR_END: game_logic
// clang-format on // clang-format on

View file

@ -2,11 +2,11 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// ANCHOR: main // ANCHOR: main
// main.cpp // src/main.cpp
#include "memory.h" // generated header from memory.slint #include "appwindow.h" // generated header from memory.slint
int main() int main(int argc, char **argv)
{ {
auto main_window = MainWindow::create(); auto main_window = MainWindow::create();
main_window->run(); main_window->run();

View file

@ -1,44 +1,49 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Memory Tile # Memory Tile
With the skeleton in place, let's look at the first element of the game, the memory tile. It will be the 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 we'll add a 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.
covering rectangle that acts as a curtain. The background rectangle is declared to be 64 logical pixels wide and tall,
and it's filled with a soothing tone of blue. Note how lengths in the `.slint` language have a unit, here
the `px` suffix. That makes the code easier to read and the compiler can detect when your are accidentally
mixing values with different units attached to them.
We copy the following code into the `memory.slint` file: Declare the background rectangle as 64 logical pixels wide and tall
filled with a soothing tone of blue.
Note how 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 ```slint
{{#include memory_tile.slint:main_window}} {{#include memory_tile.slint:main_window}}
``` ```
Note that we export the <span class="hljs-title">MainWindow</span> component. This is necessary so that we can later access it The code exports the <span class="hljs-title">MainWindow</span> component. This is necessary so that the C++ code can access it later for business logic.
from our business logic.
Inside the <span class="hljs-built_in">Rectangle</span> we 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 folder in which 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`.
the `memory.slint` is located. This icon and others we're going to use later need to be installed first. You can download a
[Zip archive](https://slint.dev/blog/memory-game-tutorial/icons.zip) that we have prepared.
If you are on Linux or macOS, download and extract it with the following two commands: 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 and extract it with the
following commands:
```sh ```sh
cd ui
curl -O https://slint.dev/blog/memory-game-tutorial/icons.zip curl -O https://slint.dev/blog/memory-game-tutorial/icons.zip
unzip icons.zip unzip icons.zip
cd ..
``` ```
If you are on Windows, use the following commands: If you are on Windows, use the following commands:
``` ```sh
powershell curl -Uri https://slint.dev/blog/memory-game-tutorial/icons.zip -Outfile icons.zip powershell curl -Uri https://slint.dev/blog/memory-game-tutorial/icons.zip -Outfile icons.zip
powershell Expand-Archive -Path icons.zip -DestinationPath . powershell Expand-Archive -Path icons.zip -DestinationPath .
``` ```
This should unpack an `icons` directory containing a bunch of icons. This unpacks an `icons` directory containing several icons.
We compile the program with `cmake --build .` and running with the `./memory_game` gives us a Compile the program with `cmake --build build` and running with the `./build/my_application` gives us a
window on the screen that shows the icon of a bus on a blue background. window on the screen that shows the icon of a bus on a blue background.
![Screenshot of the first tile](https://slint.dev/blog/memory-game-tutorial/memory-tile.png "Memory Tile Screenshot") ![Screenshot of the first tile](https://slint.dev/blog/memory-game-tutorial/memory-tile.png "Memory Tile Screenshot")

View file

@ -1,33 +1,42 @@
<!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT --> <!-- Copyright © SixtyFPS GmbH <info@slint.dev> ; SPDX-License-Identifier: MIT -->
# Polishing the Tile # Polishing the Tile
Next, let's add a curtain like cover that opens up when clicking. We achieve this by declaring two rectangles 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 they are drawn afterwards and thus on top of the image. 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 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. We use that to forward a callback to the <em>MainWindow</em> 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.
that the tile was clicked on. In the <em>MainWindow</em> we react by flipping a custom <em>open_curtain</em> property.
That in turn is used in property bindings for the animated width and x properties. Let's look at the two states a bit
more in detail:
| *open_curtain* value: | false | true | 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.
| 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* is moved to the right, to slide the curtain open when animated |
In order to make our tile extensible, the hard-coded icon name is replaced with an *icon* The following table shows more detail on the two states:
property that can be set from the outside when instantiating the element. For the final polish, we add a
*solved* property that we use to animate the color to a shade of green when we've found a pair, later. We | _open_curtain_ value: | false | true |
replace the code inside the `memory.slint` file with the following: | ----------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| 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 `ui/appwindow.slint` file with the following:
```slint ```slint
{{#include ../../rust/src/main_polishing_the_tile.rs:tile}} {{#include ../../rust/src/main_polishing_the_tile.rs:tile}}
``` ```
Note the use of `root` and `self` in the code. `root` refers to the outermost The code uses `root` and `self`. `root` refers to the outermost
element in the component, that's 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
to the current element. to the current element.
Running this gives us a window on the screen with a rectangle that opens up to show us the bus icon, when clicking on The code exports the <span class="hljs-title">MainWindow</span> component. This is necessary so that you can later access it
it. Subsequent clicks will close and open the curtain again. 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> <video autoplay loop muted playsinline src="https://slint.dev/blog/memory-game-tutorial/polishing-the-tile.mp4"></video>

View file

@ -3,14 +3,13 @@
# Memory Tile # Memory Tile
With the skeleton in place, this step looks at the first element of the game, the memory tile. It's the With the skeleton 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 you'll add a 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.
covering rectangle that acts as a curtain.
You declare the background rectangle as 64 logical pixels wide and tall Declare the background rectangle as 64 logical pixels wide and tall
and it's filled with a soothing tone of blue. filled with a soothing tone of blue.
Note how lengths in the `.slint` language have a unit, here Note how lengths in the `.slint` language have a unit, here the `px` suffix.
the `px` suffix. That 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.
Copy the following code inside of the `slint!` macro, replacing the current content: Copy the following code inside of the `slint!` macro, replacing the current content: