From 72ff79dc576de5bf02806d08fe05d055a0fc3aea Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Thu, 29 Feb 2024 11:01:20 +0100 Subject: [PATCH 01/11] Refactor CPP Quickstart to use project template project --- .../cpp/src/{memory.slint => appwindow.slint} | 2 +- docs/tutorial/cpp/src/conclusion.md | 14 +-- .../cpp/src/creating_the_tiles_from_cpp.md | 26 +++-- .../cpp/src/from_one_to_multiple_tiles.md | 34 ++++--- docs/tutorial/cpp/src/game_logic_in_cpp.md | 44 +++++---- docs/tutorial/cpp/src/getting_started.md | 98 +++++++++---------- docs/tutorial/cpp/src/ideas_for_the_reader.md | 9 +- docs/tutorial/cpp/src/introduction.md | 13 +-- docs/tutorial/cpp/src/main_game_logic.cpp | 7 +- docs/tutorial/cpp/src/main_initial.cpp | 8 +- docs/tutorial/cpp/src/memory_tile.md | 44 ++++----- docs/tutorial/cpp/src/memory_tile.slint | 4 +- docs/tutorial/cpp/src/polishing_the_tile.md | 45 +++++---- docs/tutorial/rust/src/memory_tile.md | 11 +-- 14 files changed, 189 insertions(+), 170 deletions(-) rename docs/tutorial/cpp/src/{memory.slint => appwindow.slint} (91%) diff --git a/docs/tutorial/cpp/src/memory.slint b/docs/tutorial/cpp/src/appwindow.slint similarity index 91% rename from docs/tutorial/cpp/src/memory.slint rename to docs/tutorial/cpp/src/appwindow.slint index 7976ba8f494..ec39ae5a98b 100644 --- a/docs/tutorial/cpp/src/memory.slint +++ b/docs/tutorial/cpp/src/appwindow.slint @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT // ANCHOR: main_window -// memory.slint +// ui/appwindow.slint export component MainWindow inherits Window { Text { text: "hello world"; diff --git a/docs/tutorial/cpp/src/conclusion.md b/docs/tutorial/cpp/src/conclusion.md index 713d8db74f9..d58bbf70a35 100644 --- a/docs/tutorial/cpp/src/conclusion.md +++ b/docs/tutorial/cpp/src/conclusion.md @@ -1,12 +1,14 @@ + # 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 play the wasm version 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 play the wasm version 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. diff --git a/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md b/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md index 00bbf61cb36..0f9c64597ea 100644 --- a/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md +++ b/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md @@ -1,21 +1,27 @@ + # 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`](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 imports 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`](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. diff --git a/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md b/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md index c31bf3fe768..5372d6e4649 100644 --- a/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md +++ b/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md @@ -1,34 +1,40 @@ + # 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 for 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 for 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 MemoryTile elements and place them on a grid based on their -index with a little bit of spacing between the tiles. +With Slint the for loop is declarative and automatically updates when +the model changes. The loop instantiates all the MemoryTile 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 MainWindow inherits Window { ... }_ section at the bottom of the `memory.slint` file with the following snippet: +Next, replace the _export component MainWindow 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 for tile\[i\] in memory_tiles: 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. diff --git a/docs/tutorial/cpp/src/game_logic_in_cpp.md b/docs/tutorial/cpp/src/game_logic_in_cpp.md index 06187ee47b4..8ae2182b353 100644 --- a/docs/tutorial/cpp/src/game_logic_in_cpp.md +++ b/docs/tutorial/cpp/src/game_logic_in_cpp.md @@ -1,33 +1,38 @@ + # 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 MainWindow 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 MainWindow: +Add the following code inside the MainWindow 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 MemoryTile signals that it was clicked on. -We add the following handler in MainWindow: +This change adds a way for the MainWindow 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 MemoryTile signals that a player clicked it. + +Add the following handler in the MainWindow `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: @@ -35,10 +40,9 @@ Insert this code before the `main_window->run()` call: {{#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 [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 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. diff --git a/docs/tutorial/cpp/src/getting_started.md b/docs/tutorial/cpp/src/getting_started.md index b96f54d60b4..f23bc758414 100644 --- a/docs/tutorial/cpp/src/getting_started.md +++ b/docs/tutorial/cpp/src/getting_started.md @@ -1,88 +1,82 @@ + # 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/), +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 +``` + + + +Change the references to `my_application` in the `CMakeLists.txt` file to `memory` + +The `CMakeLists.txt` uses the line `add_executable(memory src/main.cpp)` to set `src/main.cpp` as the main C++ code file. + +```cpp +{{#include main_initial.cpp:main}} ``` -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: +Also in `CMakeLists.txt` the line +`slint_target_sources(memory ui/appwindow.slint)` is a Slint function used to +add the `appwindow.slint` file to the target. + +Change the contents of `appwindow.slint` to the following: ```slint -{{#include memory.slint:main_window}} +{{#include appwindow.slint:main_window}} ``` -What's still missing is the `main.cpp`: +Configure with CMake: -```cpp -{{#include main_initial.cpp:main}} +```sh +mkdir build +cmake -B build ``` -To recap, we now have a directory with a `CMakeLists.txt`, `memory.slint` and `main.cpp`. +Build with CMake: -We can now compile the program in a terminal: +```sh +cmake --build build +``` + +Run the application binary on Linux or macOS: ```sh -cmake -GNinja . -cmake --build . +./build/memory ``` -If you are on Linux or macOS, you can run the program: +Windows: ```sh -./memory_game +build\my_application.exe ``` -and a window will appear with the green "Hello World" greeting. +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 +memory ``` ![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, ... -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. diff --git a/docs/tutorial/cpp/src/ideas_for_the_reader.md b/docs/tutorial/cpp/src/ideas_for_the_reader.md index dd6c5145ae9..b27522e3067 100644 --- a/docs/tutorial/cpp/src/ideas_for_the_reader.md +++ b/docs/tutorial/cpp/src/ideas_for_the_reader.md @@ -1,12 +1,13 @@ + # 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) - property of _Rectangle_ can be used to achieve that. +- 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 +- 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. diff --git a/docs/tutorial/cpp/src/introduction.md b/docs/tutorial/cpp/src/introduction.md index aa5716402a6..d84052f9808 100644 --- a/docs/tutorial/cpp/src/introduction.md +++ b/docs/tutorial/cpp/src/introduction.md @@ -1,15 +1,16 @@ + # 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. -We know that 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 -aren't the same, the icons will be obscured again. -If you uncover two tiles with the same icon, then they remain visible - they're solved. +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 like in action: +This is how the game looks in action: diff --git a/docs/tutorial/cpp/src/main_game_logic.cpp b/docs/tutorial/cpp/src/main_game_logic.cpp index d843a2b92e2..8c65ad2067b 100644 --- a/docs/tutorial/cpp/src/main_game_logic.cpp +++ b/docs/tutorial/cpp/src/main_game_logic.cpp @@ -21,11 +21,11 @@ int main() std::default_random_engine rng {}; std::shuffle(new_tiles.begin(), new_tiles.end(), rng); - // ANCHOR: game_logic - auto tiles_model = std::make_shared>(new_tiles); main_window->set_memory_tiles(tiles_model); + // ANCHOR: game_logic + main_window->on_check_if_pair_solved( [main_window_weak = slint::ComponentWeakHandle(main_window)] { auto main_window = *main_window_weak.lock(); @@ -63,8 +63,7 @@ int main() }); } }); - + // ANCHOR_END: game_logic main_window->run(); } -// ANCHOR_END: game_logic // clang-format on diff --git a/docs/tutorial/cpp/src/main_initial.cpp b/docs/tutorial/cpp/src/main_initial.cpp index 6004a4a574b..3ea4ebfaa1f 100644 --- a/docs/tutorial/cpp/src/main_initial.cpp +++ b/docs/tutorial/cpp/src/main_initial.cpp @@ -2,13 +2,13 @@ // SPDX-License-Identifier: MIT // 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(); } // ANCHOR_END: main diff --git a/docs/tutorial/cpp/src/memory_tile.md b/docs/tutorial/cpp/src/memory_tile.md index 2b9e16e3b0b..bc48b37bf35 100644 --- a/docs/tutorial/cpp/src/memory_tile.md +++ b/docs/tutorial/cpp/src/memory_tile.md @@ -1,44 +1,42 @@ + # Memory Tile -With the skeleton in place, let's look at the first element of the game, the memory tile. It will be the -visual building block that consists of an underlying filled rectangle background, the icon image. Later we'll add a -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. +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. + +Declare the background rectangle as 64 logical pixels wide and tall +filled with a soothing tone of blue. -We copy the following code into the `memory.slint` file: +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 {{#include memory_tile.slint:main_window}} ``` -Note that we export the MainWindow component. This is necessary so that we can later access it -from our business logic. +The code exports the MainWindow component. This is necessary so that the C++ code can access it later for business logic. -Inside the Rectangle we place an Image element that -loads an icon with the @image-url() macro. The path is relative to the folder in which -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. +Inside the Rectangle place an Image element that +loads an icon with the @image-url() macro. The path is relative to the location of `ui/appwindow.slint`. -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 +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: - -``` -powershell curl -Uri https://slint.dev/blog/memory-game-tutorial/icons.zip -Outfile icons.zip -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/memory` gives us a 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") diff --git a/docs/tutorial/cpp/src/memory_tile.slint b/docs/tutorial/cpp/src/memory_tile.slint index 6c1223f242a..573b52c466c 100644 --- a/docs/tutorial/cpp/src/memory_tile.slint +++ b/docs/tutorial/cpp/src/memory_tile.slint @@ -8,13 +8,13 @@ component MemoryTile inherits Rectangle { background: #3960D5; Image { - source: @image-url("icons/bus.png"); + source: @image-url("../icons/bus.png"); width: parent.width; height: parent.height; } } -export component MainWindow inherits Window { +export component AppWindow inherits Window { MemoryTile {} } // ANCHOR_END: main_window diff --git a/docs/tutorial/cpp/src/polishing_the_tile.md b/docs/tutorial/cpp/src/polishing_the_tile.md index 0592298b51d..fc2da066263 100644 --- a/docs/tutorial/cpp/src/polishing_the_tile.md +++ b/docs/tutorial/cpp/src/polishing_the_tile.md @@ -1,33 +1,42 @@ + # Polishing the Tile -Next, let's add a curtain like cover that opens up when clicking. We achieve this by declaring two rectangles -below the Image, so that they are drawn afterwards and thus on top of the image. +In this step, you add a curtain-like cover that opens when clicked. You do this by declaring two rectangles +below the Image, so that Slint draws them after the Image and thus on top of the image. + The TouchArea 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 MainWindow -that the tile was clicked on. In the MainWindow we react by flipping a custom open_curtain 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: +reacting to user input such as a mouse click or tap. The element forwards a callback to the MainWindow indicating that a user clicked the tile. + +The MainWindow reacts by flipping a custom open_curtain property. +Property bindings for the animated width and x properties also use the custom open_curtain 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* is moved to the right, to slide the curtain open when animated | +| _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 | -In order to make our tile extensible, the hard-coded icon name is replaced with an *icon* -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 -replace the code inside the `memory.slint` file with the following: +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 {{#include ../../rust/src/main_polishing_the_tile.rs:tile}} ``` -Note the use of `root` and `self` in the code. `root` refers to the outermost -element in the component, that's the MemoryTile in this case. `self` refers +The code uses `root` and `self`. `root` refers to the outermost +element in the component, the MemoryTile in this case. `self` refers 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 -it. Subsequent clicks will close and open the curtain again. +The code exports the MainWindow 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. diff --git a/docs/tutorial/rust/src/memory_tile.md b/docs/tutorial/rust/src/memory_tile.md index d561cb1a54f..0f26f95a150 100644 --- a/docs/tutorial/rust/src/memory_tile.md +++ b/docs/tutorial/rust/src/memory_tile.md @@ -3,14 +3,13 @@ # Memory Tile 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 -covering rectangle that acts as a curtain. +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 -and it's filled with a soothing tone of blue. +Declare the background rectangle as 64 logical pixels wide and tall +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 you accidentally +Note how lengths in the `.slint` language 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: From ccc9ab59b792e7b45f313d574cd52763f1cb3703 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Thu, 29 Feb 2024 11:38:47 +0100 Subject: [PATCH 02/11] Update docs/tutorial/cpp/src/main_initial.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/tutorial/cpp/src/main_initial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/cpp/src/main_initial.cpp b/docs/tutorial/cpp/src/main_initial.cpp index 3ea4ebfaa1f..35b896fd90f 100644 --- a/docs/tutorial/cpp/src/main_initial.cpp +++ b/docs/tutorial/cpp/src/main_initial.cpp @@ -8,7 +8,7 @@ int main(int argc, char **argv) { - auto main_window = MainWindow::create(); + auto main_window = MainWindow::create(); main_window->run(); } // ANCHOR_END: main From edd160402d6cc40482656a8b48ea092bc3bca38f Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 10:47:02 +0100 Subject: [PATCH 03/11] Update docs/tutorial/cpp/src/game_logic_in_cpp.md Co-authored-by: Simon Hausmann --- docs/tutorial/cpp/src/game_logic_in_cpp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/cpp/src/game_logic_in_cpp.md b/docs/tutorial/cpp/src/game_logic_in_cpp.md index 8ae2182b353..2573b2f5053 100644 --- a/docs/tutorial/cpp/src/game_logic_in_cpp.md +++ b/docs/tutorial/cpp/src/game_logic_in_cpp.md @@ -40,7 +40,7 @@ Insert this code before the `main_window->run()` call: {{#include main_game_logic.cpp:game_logic}} ``` -The code uses a [Weak](https://slint.dev/docs/rust/slint/struct.Weak) pointer of the `main_window`. This is +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. From 1bf86982f465bfe8f93a58d14615fc1868e03070 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 10:47:16 +0100 Subject: [PATCH 04/11] Update docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md Co-authored-by: Simon Hausmann --- docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md b/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md index 0f9c64597ea..f25fa1c8f88 100644 --- a/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md +++ b/docs/tutorial/cpp/src/creating_the_tiles_from_cpp.md @@ -4,7 +4,7 @@ This step places the game tiles randomly. -Change the `main` function and imports in `src/main.cpp` to the following: +Change the `main` function and includes in `src/main.cpp` to the following: ```cpp {{#include main_tiles_from_cpp.cpp:main}} From 8641b2ed4647785311e7515babbcffe611da2648 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 10:47:27 +0100 Subject: [PATCH 05/11] Update docs/tutorial/cpp/src/from_one_to_multiple_tiles.md Co-authored-by: Simon Hausmann --- docs/tutorial/cpp/src/from_one_to_multiple_tiles.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md b/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md index 5372d6e4649..3b77cce24de 100644 --- a/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md +++ b/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md @@ -15,7 +15,7 @@ After modeling a single tile, this step creates a grid of them. For the grid to With Slint you declare an array of structures based on a model using square brackets. Use a for loop to create multiple instances of the same element. -With Slint the for loop is declarative and automatically updates when +With Slint the for loop is declarative and automatically updates when the model changes. The loop instantiates all the MemoryTile elements and places them on a grid based on their index with spacing between the tiles. From 61dabde397fd1794348488b29dc14573612d9b9a Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 10:48:34 +0100 Subject: [PATCH 06/11] Update docs/tutorial/cpp/src/from_one_to_multiple_tiles.md Co-authored-by: Simon Hausmann --- docs/tutorial/cpp/src/from_one_to_multiple_tiles.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md b/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md index 3b77cce24de..8f9c31a5040 100644 --- a/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md +++ b/docs/tutorial/cpp/src/from_one_to_multiple_tiles.md @@ -25,7 +25,7 @@ First, add the tile data structure definition at the top of the `ui/appwindow.sl {{#include ../../rust/src/main_multiple_tiles.rs:tile_data}} ``` -Next, replace the _export component MainWindow inherits Window { ... }_ section at the bottom of the memory.slint file with the following: +Next, replace the _export component MainWindow 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}} From 235c108a3ab91bbf6332993ca9570ddfa8df1da6 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 10:48:55 +0100 Subject: [PATCH 07/11] Update docs/tutorial/cpp/src/getting_started.md Co-authored-by: Simon Hausmann --- docs/tutorial/cpp/src/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/cpp/src/getting_started.md b/docs/tutorial/cpp/src/getting_started.md index f23bc758414..26e79b1bfa5 100644 --- a/docs/tutorial/cpp/src/getting_started.md +++ b/docs/tutorial/cpp/src/getting_started.md @@ -24,7 +24,7 @@ you also need to install a Rust compiler (1.70 or newer). You can easily install 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. --> -Change the references to `my_application` in the `CMakeLists.txt` file to `memory` +Change all the references to `my_application` in the `CMakeLists.txt` file to `memory`. The `CMakeLists.txt` uses the line `add_executable(memory src/main.cpp)` to set `src/main.cpp` as the main C++ code file. From 232505f572ff22e9c9e91e02493f241dfcb76713 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 10:56:16 +0100 Subject: [PATCH 08/11] Remove commented text --- docs/tutorial/cpp/src/getting_started.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/tutorial/cpp/src/getting_started.md b/docs/tutorial/cpp/src/getting_started.md index f23bc758414..05866646d96 100644 --- a/docs/tutorial/cpp/src/getting_started.md +++ b/docs/tutorial/cpp/src/getting_started.md @@ -16,14 +16,6 @@ git clone https://github.com/slint-ui/slint-cpp-template memory cd memory ``` - - Change the references to `my_application` in the `CMakeLists.txt` file to `memory` The `CMakeLists.txt` uses the line `add_executable(memory src/main.cpp)` to set `src/main.cpp` as the main C++ code file. From 645e33808981152df9a2f0002542245fce9816f5 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 11:22:07 +0100 Subject: [PATCH 09/11] Re-add removed powershell icon commands --- docs/tutorial/cpp/src/memory_tile.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/tutorial/cpp/src/memory_tile.md b/docs/tutorial/cpp/src/memory_tile.md index bc48b37bf35..15f1d69f1e4 100644 --- a/docs/tutorial/cpp/src/memory_tile.md +++ b/docs/tutorial/cpp/src/memory_tile.md @@ -34,6 +34,13 @@ unzip icons.zip cd .. ``` +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 Expand-Archive -Path icons.zip -DestinationPath . +``` + This unpacks an `icons` directory containing several icons. Compile the program with `cmake --build build` and running with the `./build/memory` gives us a From b4ffaa39f9ffb80886c3fd410c0ba2ad53a9f3d0 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Fri, 1 Mar 2024 15:02:13 +0100 Subject: [PATCH 10/11] Undo rename --- docs/tutorial/cpp/src/getting_started.md | 13 ++++++------- docs/tutorial/cpp/src/memory_tile.md | 2 +- docs/tutorial/cpp/src/memory_tile.slint | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/tutorial/cpp/src/getting_started.md b/docs/tutorial/cpp/src/getting_started.md index 6e3350e905b..eb6c1f82f31 100644 --- a/docs/tutorial/cpp/src/getting_started.md +++ b/docs/tutorial/cpp/src/getting_started.md @@ -16,19 +16,19 @@ git clone https://github.com/slint-ui/slint-cpp-template memory cd memory ``` -Change all the references to `my_application` in the `CMakeLists.txt` file to `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. -The `CMakeLists.txt` uses the line `add_executable(memory 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(memory ui/appwindow.slint)` is a Slint function used to +`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 `appwindow.slint` to the following: +Change the contents of `ui/appwindow.slint` to the following: ```slint {{#include appwindow.slint:main_window}} @@ -37,7 +37,6 @@ Change the contents of `appwindow.slint` to the following: Configure with CMake: ```sh -mkdir build cmake -B build ``` @@ -50,7 +49,7 @@ cmake --build build Run the application binary on Linux or macOS: ```sh -./build/memory +./build/my_application ``` Windows: @@ -64,7 +63,7 @@ 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 +my_application ``` ![Screenshot of initial tutorial app showing Hello World](https://slint.dev/blog/memory-game-tutorial/getting-started.png "Hello World") diff --git a/docs/tutorial/cpp/src/memory_tile.md b/docs/tutorial/cpp/src/memory_tile.md index 15f1d69f1e4..969d69b17e3 100644 --- a/docs/tutorial/cpp/src/memory_tile.md +++ b/docs/tutorial/cpp/src/memory_tile.md @@ -43,7 +43,7 @@ powershell Expand-Archive -Path icons.zip -DestinationPath . This unpacks an `icons` directory containing several icons. -Compile the program with `cmake --build build` and running with the `./build/memory` 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. ![Screenshot of the first tile](https://slint.dev/blog/memory-game-tutorial/memory-tile.png "Memory Tile Screenshot") diff --git a/docs/tutorial/cpp/src/memory_tile.slint b/docs/tutorial/cpp/src/memory_tile.slint index 573b52c466c..6c1223f242a 100644 --- a/docs/tutorial/cpp/src/memory_tile.slint +++ b/docs/tutorial/cpp/src/memory_tile.slint @@ -8,13 +8,13 @@ component MemoryTile inherits Rectangle { background: #3960D5; Image { - source: @image-url("../icons/bus.png"); + source: @image-url("icons/bus.png"); width: parent.width; height: parent.height; } } -export component AppWindow inherits Window { +export component MainWindow inherits Window { MemoryTile {} } // ANCHOR_END: main_window From af12965c0cc82574abb648f37f523f955ede90f4 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Mon, 11 Mar 2024 14:45:57 +0100 Subject: [PATCH 11/11] Correct path in CMakeLists.txt --- docs/tutorial/cpp/src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/cpp/src/CMakeLists.txt b/docs/tutorial/cpp/src/CMakeLists.txt index e408ed49ee9..b3bea0e0d82 100644 --- a/docs/tutorial/cpp/src/CMakeLists.txt +++ b/docs/tutorial/cpp/src/CMakeLists.txt @@ -9,7 +9,7 @@ endif() add_executable(memory_tutorial_initial main_initial.cpp) 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) target_link_libraries(memory_tutorial_tiles_from_cpp PRIVATE Slint::Slint)