mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 05:44:52 +00:00
add js memory tutorial (#2421)
This commit is contained in:
parent
16fbee01fe
commit
c6472f9662
23 changed files with 597 additions and 2 deletions
11
docs/tutorial/node/src/SUMMARY.md
Normal file
11
docs/tutorial/node/src/SUMMARY.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Summary
|
||||
|
||||
- [Introduction](./introduction.md)
|
||||
- [Getting Started](./getting_started.md)
|
||||
- [Memory Tile](./memory_tile.md)
|
||||
- [Polishing the Tile](./polishing_the_tile.md)
|
||||
- [From One To Multiple Tiles](./from_one_to_multiple_tiles.md)
|
||||
- [Creating The Tiles From JavaScript](./creating_the_tiles_from_js.md)
|
||||
- [Game Logic In JavaScript](./game_logic_in_js.md)
|
||||
- [Ideas For The Reader](./ideas_for_the_reader.md)
|
||||
- [Conclusion](./conclusion.md)
|
11
docs/tutorial/node/src/conclusion.md
Normal file
11
docs/tutorial/node/src/conclusion.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Conclusion
|
||||
|
||||
In this tutorial, we have demonstrated how to combine some built-in Slint elements with JavaScript code to build a little
|
||||
game. There are many more features that we haven't talked about, 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-ui.com/demos/memory/" target="_blank">play the wasm version</a> in your browser.
|
||||
* [Slint API Docs](https://slint-ui.com/docs/node/): The reference documentation for the NodeJS library.
|
19
docs/tutorial/node/src/creating_the_tiles_from_js.md
Normal file
19
docs/tutorial/node/src/creating_the_tiles_from_js.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Creating The Tiles From JavaScript
|
||||
|
||||
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 JavaScript code - in our case `memory_tiles`.
|
||||
Since `memory_tiles` is an array in the `.slint` language, it's represented as a JavaScript [`Array`](https://slint-ui.com/releases/0.3.5/docs/node/).
|
||||
We can't modify the model generated by the .slint, but we can extract the tiles from it, and put it
|
||||
in a [`slint.ArrayModel`](https://slint-ui.com/releases/0.3.5/docs/node/classes/arraymodel.html) which implements the [`Model`](https://slint-ui.com/releases/0.3.5/docs/node/interfaces/model.html) interface.
|
||||
`ArrayModel` allows us to make modifications and we can use it to replace the static generated model.
|
||||
|
||||
We modify the main function like so:
|
||||
|
||||
```js
|
||||
{{#include main_tiles_from_js.js: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.
|
||||
|
||||
<video autoplay loop muted playsinline src="https://slint-ui.com/blog/memory-game-tutorial/creating-the-tiles-from-rust.mp4"></video>
|
33
docs/tutorial/node/src/from_one_to_multiple_tiles.md
Normal file
33
docs/tutorial/node/src/from_one_to_multiple_tiles.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# 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:
|
||||
|
||||
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 JS 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
|
||||
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.
|
||||
|
||||
First, we copy the tile data structure definition and paste it at top inside the `memory.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:
|
||||
|
||||
```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.
|
||||
|
||||
Running this gives us a window that shows 8 tiles, which can be opened individually.
|
||||
|
||||
<video autoplay loop muted playsinline src="https://slint-ui.com/blog/memory-game-tutorial/from-one-to-multiple-tiles.mp4"></video>
|
38
docs/tutorial/node/src/game_logic_in_js.md
Normal file
38
docs/tutorial/node/src/game_logic_in_js.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Game Logic In JavaScript
|
||||
|
||||
We'll implement the rules of the game in JavaScript 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.
|
||||
|
||||
We'll modify the `.slint` markup in the `memory.slint` file to signal to the JavaScript 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 JavaScript code that it should
|
||||
check if a pair of tiles has been solved. And we need to add a property that JavaScript 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>:
|
||||
|
||||
```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>:
|
||||
|
||||
```slint
|
||||
{{#include ../../rust/src/main_game_logic_in_rust.rs:tile_click_logic}}
|
||||
```
|
||||
|
||||
On the JavaScript 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.
|
||||
|
||||
Insert this code before the `mainWindow.run()` call:
|
||||
|
||||
```js
|
||||
{{#include main_game_logic.js:game_logic}}
|
||||
```
|
||||
|
||||
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.
|
46
docs/tutorial/node/src/getting_started.md
Normal file
46
docs/tutorial/node/src/getting_started.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Getting Started
|
||||
|
||||
In this tutorial, we use JavaScript as the host programming language. We also support other programming languages like
|
||||
[Rust](https://slint-ui.com/docs/rust/slint/) or [C++](https://slint-ui.com/docs/cpp/).
|
||||
|
||||
You'll need a development environment with [Node.js 16](https://nodejs.org/download/release/v16.19.1/) and [npm](https://www.npmjs.com/) installed. More recent
|
||||
versions of NodeJS are currently not supported, for details check [Issue #2220](https://github.com/slint-ui/slint/issues/2220).
|
||||
Since Slint is implemented in the Rust programming language, you also need to install a Rust compiler (1.66 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 `slint-ui` as `npm` dependency.
|
||||
|
||||
In a new directory, we create a new `package.json` file.
|
||||
|
||||
```json
|
||||
{{#include package.json}}
|
||||
```
|
||||
|
||||
This should look familiar to people familiar with NodeJS. We see that this package.json
|
||||
references a `main.js`, which we will add later. We must then create, in the same directory,
|
||||
the `memory.slint` file. Let's just fill it with a hello world for now:
|
||||
|
||||
```slint
|
||||
{{#include memory.slint:main_window}}
|
||||
```
|
||||
|
||||
What's still missing is the `main.js`:
|
||||
|
||||
```js
|
||||
{{#include main_initial.js:main}}
|
||||
```
|
||||
|
||||
To recap, we now have a directory with a `package.json`, `memory.slint`, and `main.js`.
|
||||
|
||||
We can now compile and run the program:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
and a window will appear with the green "Hello World" greeting.
|
||||
|
||||

|
||||
|
||||
Feel free to use your favorite IDE for this purpose.
|
1
docs/tutorial/node/src/icons
Symbolic link
1
docs/tutorial/node/src/icons
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../examples/memory/icons
|
14
docs/tutorial/node/src/ideas_for_the_reader.md
Normal file
14
docs/tutorial/node/src/ideas_for_the_reader.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# 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 tiles could have rounded corners, to look a little less sharp. The [border-radius](https://slint-ui.com/docs/slint/src/builtins/elements.html#rectangle)
|
||||
property of _Rectangle_ can be used to achieve that.
|
||||
|
||||
- In real world memory games, the back of the tiles often have some common graphic. You could add an image with
|
||||
the help of another _[Image](https://slint-ui.com/docs/slint/src/builtins/elements.html#image)_
|
||||
element. Note that you may have to use _Rectangle_'s _clip property_
|
||||
element around it to ensure that the image is clipped away when the curtain effect opens.
|
||||
|
||||
Let us know in the comments on [Github Discussions](https://github.com/slint-ui/slint/discussions)
|
||||
how you polished your code, or feel free to ask questions about how to implement something.
|
14
docs/tutorial/node/src/introduction.md
Normal file
14
docs/tutorial/node/src/introduction.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# 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 JavaScript.
|
||||
|
||||
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.
|
||||
|
||||
This is how the game looks like in action:
|
||||
|
||||
<video autoplay loop muted playsinline src="https://slint-ui.com/blog/memory-game-tutorial/memory_clip.mp4"
|
||||
class="img-fluid img-thumbnail rounded"></video>
|
66
docs/tutorial/node/src/main_game_logic.js
Normal file
66
docs/tutorial/node/src/main_game_logic.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
|
||||
// main.js
|
||||
let slint = require("slint-ui");
|
||||
let ui = require("./memory.slint");
|
||||
let mainWindow = new ui.MainWindow();
|
||||
|
||||
let initial_tiles = mainWindow.memory_tiles;
|
||||
let tiles = initial_tiles.concat(initial_tiles.map((tile) => Object.assign({}, tile)));
|
||||
|
||||
for (let i = tiles.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * i);
|
||||
[tiles[i], tiles[j]] = [tiles[j], tiles[i]];
|
||||
}
|
||||
|
||||
// ANCHOR: game_logic
|
||||
let model = new slint.ArrayModel(tiles);
|
||||
mainWindow.memory_tiles = model;
|
||||
|
||||
mainWindow.check_if_pair_solved.setHandler(function () {
|
||||
let flipped_tiles = [];
|
||||
tiles.forEach((tile, index) => {
|
||||
if (tile.image_visible && !tile.solved) {
|
||||
flipped_tiles.push({
|
||||
index,
|
||||
tile
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (flipped_tiles.length == 2) {
|
||||
let {
|
||||
tile: tile1,
|
||||
index: tile1_index
|
||||
} = flipped_tiles[0];
|
||||
|
||||
let {
|
||||
tile: tile2,
|
||||
index: tile2_index
|
||||
} = flipped_tiles[1];
|
||||
|
||||
let is_pair_solved = tile1.image === tile2.image;
|
||||
if (is_pair_solved) {
|
||||
tile1.solved = true;
|
||||
model.setRowData(tile1_index, tile1);
|
||||
tile2.solved = true;
|
||||
model.setRowData(tile2_index, tile2);
|
||||
} else {
|
||||
mainWindow.disable_tiles = true;
|
||||
slint.Timer.singleShot(1000, () => {
|
||||
mainWindow.disable_tiles = false;
|
||||
tile1.image_visible = false;
|
||||
model.setRowData(tile1_index, tile1);
|
||||
tile2.image_visible = false;
|
||||
model.setRowData(tile2_index, tile2);
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.run();
|
||||
|
||||
// ANCHOR_END: game_logic
|
11
docs/tutorial/node/src/main_initial.js
Normal file
11
docs/tutorial/node/src/main_initial.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
// ANCHOR: main
|
||||
// main.js
|
||||
require("slint-ui");
|
||||
let ui = require("./memory.slint");
|
||||
let mainWindow = new ui.MainWindow();
|
||||
mainWindow.run();
|
||||
|
||||
// ANCHOR_END: main
|
23
docs/tutorial/node/src/main_tiles_from_js.js
Normal file
23
docs/tutorial/node/src/main_tiles_from_js.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
// ANCHOR: main
|
||||
// main.js
|
||||
let slint = require("slint-ui");
|
||||
let ui = require("./memory.slint");
|
||||
let mainWindow = new ui.MainWindow();
|
||||
|
||||
let initial_tiles = mainWindow.memory_tiles;
|
||||
let tiles = initial_tiles.concat(initial_tiles.map((tile) => Object.assign({}, tile)));
|
||||
|
||||
for (let i = tiles.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * i);
|
||||
[tiles[i], tiles[j]] = [tiles[j], tiles[i]];
|
||||
}
|
||||
|
||||
let model = new slint.ArrayModel(tiles);
|
||||
mainWindow.memory_tiles = model;
|
||||
|
||||
mainWindow.run();
|
||||
|
||||
// ANCHOR_END: main
|
12
docs/tutorial/node/src/memory.slint
Normal file
12
docs/tutorial/node/src/memory.slint
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
// ANCHOR: main_window
|
||||
// memory.slint
|
||||
export component MainWindow inherits Window {
|
||||
Text {
|
||||
text: "hello world";
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: main_window
|
93
docs/tutorial/node/src/memory_game_logic.slint
Normal file
93
docs/tutorial/node/src/memory_game_logic.slint
Normal file
|
@ -0,0 +1,93 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
struct TileData {
|
||||
image: image,
|
||||
image_visible: bool,
|
||||
solved: bool,
|
||||
}
|
||||
|
||||
component MemoryTile inherits Rectangle {
|
||||
callback clicked;
|
||||
in property <bool> open_curtain;
|
||||
in property <bool> solved;
|
||||
in property <image> icon;
|
||||
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
background: solved ? #34CE57 : #3960D5;
|
||||
animate background { duration: 800ms; }
|
||||
|
||||
Image {
|
||||
source: icon;
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
}
|
||||
|
||||
// Left curtain
|
||||
Rectangle {
|
||||
background: #193076;
|
||||
width: open_curtain ? 0px : (parent.width / 2);
|
||||
height: parent.height;
|
||||
animate width { duration: 250ms; easing: ease-in; }
|
||||
}
|
||||
|
||||
// Right curtain
|
||||
Rectangle {
|
||||
background: #193076;
|
||||
x: open_curtain ? parent.width : (parent.width / 2);
|
||||
width: open_curtain ? 0px : (parent.width / 2);
|
||||
height: parent.height;
|
||||
animate width { duration: 250ms; easing: ease-in; }
|
||||
animate x { duration: 250ms; easing: ease-in; }
|
||||
}
|
||||
|
||||
TouchArea {
|
||||
clicked => {
|
||||
// Delegate to the user of this element
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ANCHOR: mainwindow_interface
|
||||
export component MainWindow inherits Window {
|
||||
width: 326px;
|
||||
height: 326px;
|
||||
|
||||
callback check_if_pair_solved(); // Added
|
||||
in property <bool> disable_tiles; // Added
|
||||
|
||||
in-out property <[TileData]> memory_tiles: [
|
||||
{ image: @image-url("icons/at.png") },
|
||||
// ANCHOR_END: mainwindow_interface
|
||||
{ image: @image-url("icons/balance-scale.png") },
|
||||
{ image: @image-url("icons/bicycle.png") },
|
||||
{ image: @image-url("icons/bus.png") },
|
||||
{ image: @image-url("icons/cloud.png") },
|
||||
{ image: @image-url("icons/cogs.png") },
|
||||
{ image: @image-url("icons/motorcycle.png") },
|
||||
{ image: @image-url("icons/video.png") },
|
||||
];
|
||||
|
||||
// ANCHOR: tile_click_logic
|
||||
for tile[i] in memory_tiles : MemoryTile {
|
||||
x: mod(i, 4) * 74px;
|
||||
y: floor(i / 4) * 74px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
icon: tile.image;
|
||||
open_curtain: tile.image_visible || tile.solved;
|
||||
// propagate the solved status from the model to the tile
|
||||
solved: tile.solved;
|
||||
clicked => {
|
||||
// old: tile.image_visible = !tile.image_visible;
|
||||
// new:
|
||||
if (!root.disable_tiles) {
|
||||
tile.image_visible = !tile.image_visible;
|
||||
root.check_if_pair_solved();
|
||||
}
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: tile_click_logic
|
||||
}
|
42
docs/tutorial/node/src/memory_tile.md
Normal file
42
docs/tutorial/node/src/memory_tile.md
Normal file
|
@ -0,0 +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.
|
||||
|
||||
We copy the following code into the `memory.slint` file:
|
||||
|
||||
```slint
|
||||
{{#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
|
||||
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
|
||||
loads an icon with the <span class="hljs-built_in">@image-url()</span> 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-ui.com/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:
|
||||
|
||||
```sh
|
||||
curl -O https://slint-ui.com/blog/memory-game-tutorial/icons.zip
|
||||
unzip icons.zip
|
||||
```
|
||||
|
||||
If you are on Windows, use the following commands:
|
||||
|
||||
```
|
||||
powershell curl -Uri https://slint-ui.com/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.
|
||||
|
||||
We running the program with `npm start` and it gives us a window on the screen that shows the icon of a bus on a blue background.
|
||||
|
||||

|
20
docs/tutorial/node/src/memory_tile.slint
Normal file
20
docs/tutorial/node/src/memory_tile.slint
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
// ANCHOR: main_window
|
||||
component MemoryTile inherits Rectangle {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: #3960D5;
|
||||
|
||||
Image {
|
||||
source: @image-url("icons/bus.png");
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
}
|
||||
}
|
||||
|
||||
export component MainWindow inherits Window {
|
||||
MemoryTile {}
|
||||
}
|
||||
// ANCHOR_END: main_window
|
80
docs/tutorial/node/src/memory_tiles_from_cpp.slint
Normal file
80
docs/tutorial/node/src/memory_tiles_from_cpp.slint
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
struct TileData {
|
||||
image: image,
|
||||
image_visible: bool,
|
||||
solved: bool,
|
||||
}
|
||||
|
||||
component MemoryTile inherits Rectangle {
|
||||
callback clicked;
|
||||
in property <bool> open_curtain;
|
||||
in property <bool> solved;
|
||||
in property <image> icon;
|
||||
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
background: solved ? #34CE57 : #3960D5;
|
||||
animate background { duration: 800ms; }
|
||||
|
||||
Image {
|
||||
source: icon;
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
}
|
||||
|
||||
// Left curtain
|
||||
Rectangle {
|
||||
background: #193076;
|
||||
width: open_curtain ? 0px : (parent.width / 2);
|
||||
height: parent.height;
|
||||
animate width { duration: 250ms; easing: ease-in; }
|
||||
}
|
||||
|
||||
// Right curtain
|
||||
Rectangle {
|
||||
background: #193076;
|
||||
x: open_curtain ? parent.width : (parent.width / 2);
|
||||
width: open_curtain ? 0px : (parent.width / 2);
|
||||
height: parent.height;
|
||||
animate width { duration: 250ms; easing: ease-in; }
|
||||
animate x { duration: 250ms; easing: ease-in; }
|
||||
}
|
||||
|
||||
TouchArea {
|
||||
clicked => {
|
||||
// Delegate to the user of this element
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export component MainWindow inherits Window {
|
||||
width: 326px;
|
||||
height: 326px;
|
||||
|
||||
in-out property <[TileData]> memory_tiles: [
|
||||
{ image: @image-url("icons/at.png") },
|
||||
{ image: @image-url("icons/balance-scale.png") },
|
||||
{ image: @image-url("icons/bicycle.png") },
|
||||
{ image: @image-url("icons/bus.png") },
|
||||
{ image: @image-url("icons/cloud.png") },
|
||||
{ image: @image-url("icons/cogs.png") },
|
||||
{ image: @image-url("icons/motorcycle.png") },
|
||||
{ image: @image-url("icons/video.png") },
|
||||
];
|
||||
for tile[i] in memory_tiles : MemoryTile {
|
||||
x: mod(i, 4) * 74px;
|
||||
y: floor(i / 4) * 74px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
icon: tile.image;
|
||||
open_curtain: tile.image_visible || tile.solved;
|
||||
// propagate the solved status from the model to the tile
|
||||
solved: tile.solved;
|
||||
clicked => {
|
||||
tile.image_visible = !tile.image_visible;
|
||||
}
|
||||
}
|
||||
}
|
11
docs/tutorial/node/src/package.json
Normal file
11
docs/tutorial/node/src/package.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "memory",
|
||||
"version": "1.0.0",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"slint-ui": "^1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node ."
|
||||
}
|
||||
}
|
32
docs/tutorial/node/src/polishing_the_tile.md
Normal file
32
docs/tutorial/node/src/polishing_the_tile.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# 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 <span class="hljs-built_in">Image</span>, so that they are drawn afterwards and thus on top of the image.
|
||||
The <span class="hljs-built_in">TouchArea</span> element declares a transparent rectangular region that allows
|
||||
reacting to user input such as a mouse click or tap. We use that to forward a callback to the <em>MainWindow</em>
|
||||
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 |
|
||||
| --- | --- | --- |
|
||||
| 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*
|
||||
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:
|
||||
|
||||
```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 <span class="hljs-title">MemoryTile</span> 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.
|
||||
|
||||
<video autoplay loop muted playsinline src="https://slint-ui.com/blog/memory-game-tutorial/polishing-the-tile.mp4"></video>
|
Loading…
Add table
Add a link
Reference in a new issue