slint 1.4.0

GUI toolkit to efficiently develop fluid graphical user interfaces for embedded devices and desktop applications
Documentation
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Game Logic In C++ - SixtyFPS Memory Game Tutorial (C++)</title>
                

        <!-- Custom HTML head -->
        <!--
    This file is used to add syntax highlighting of the `.60` snippets in the generated rustdoc, sphinx and mdbook documentation.
    It can be injected via the `--html-in-header sixtyfps-docs-highlight.html` option of rustdoc, is included via _templates/layout.html
    in sphinx and via head.hbs in mdbook.
-->
<link rel="stylesheet" href="https://sixtyfps.io/resources/highlightjs/11.0.1/default.min.css">
<script src="https://sixtyfps.io/resources/highlightjs/11.0.1/highlight.min.js"></script>
<script>
  hljs.registerLanguage("60", function (hljs) {
    const KEYWORDS = {
      keyword:
        'struct export import signal property animate for in if states transitions parent root self',
      literal:
        'true false',
      built_in:
        'Rectangle Image Text TouchArea Flickable Clip TextInput Window GridLayout Row HorizontalLayout VerticalLayout Path MoveTo LineTo ArcTo CubicTo QuadraticTo Close FocusScope Clip PopupWindow',
      type:
        'bool string int float length logical_length duration resource',
    };

    return {
      name: 'sixtyfps',
      aliases: ['60'],
      case_insensitive: false,
      keywords: KEYWORDS,
      contains: [
        hljs.QUOTE_STRING_MODE,
        hljs.C_LINE_COMMENT_MODE,
        hljs.C_BLOCK_COMMENT_MODE,
        hljs.COMMENT('/\\*', '\\*/', {
          contains: ['self']
        }),
        {
          className: 'number',
          begin: '\\b\\d+(\\.\\d+)?(\\w+)?',
          relevance: 0
        },
        {
          className: 'title',
          begin: '\\b[_a-zA-Z][_\\-a-zA-Z0-9]* *:=',
        },
        {
          className: 'symbol',
          begin: '\\b[_a-zA-Z][_\\-a-zA-Z0-9]*(:| *=>)',
        },
        {
          className: 'built_in',
          begin: '\\b[_a-zA-Z][_\\-a-zA-Z0-9]*!',
        },
      ],
      illegal: /@/
    };
  });

  window.addEventListener("DOMContentLoaded", () => {
    const rustDoc = document.querySelector('meta[name="generator"]')?.content == "rustdoc";
    if (rustDoc) {
      // Only highlight .60 blocks, leave the others to rustdoc
      for (dot60Block of document.querySelectorAll("pre code.language-60")) {
        hljs.highlightElement(dot60Block)
      }

      // Some of the rustdoc selectors require the pre element to have the rust class
      for (codeBlock of document.querySelectorAll(".language-60.hljs")) {
        codeBlock.parentElement.classList.add("rust")
      }

      // Change the hljs generated classes to the rustdoc
      // ones, so that the highlighting adjusts to the theme correctly.
      const highlightJSToRustDoc = [
        ["comment", "comment"],
        ["number", "number"],
        ["symbol", "struct"], // width:
        ["keyword", "kw"],
        ["built_in", "primitive"],
        ["string", "string"],
        ["title", "fnname"], // Foo :=
        ["type", "type"]
      ];

      for ([hljs_class, rustdoc_class] of highlightJSToRustDoc) {
        for (titleElement of document.querySelectorAll(`.hljs-${hljs_class}`)) {
          titleElement.classList.remove(`hljs-${hljs_class}`);
          titleElement.classList.add(rustdoc_class);
        }
      }
    } else {
      // For use with the mdbook Tutorial
      hljs.highlightAll();

      // The Sphinx/my_st generated HTML for code blocks does not use <code> tags, so highlight.js'
      // default selector "pre code" does not match. Let's do it by hand:
      for (block of document.querySelectorAll("div.highlight-60 div.highlight pre, div.highlight-60-no-preview div.highlight pre")) {
        hljs.highlightElement(block)
      }
    }
  });
</script>

        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

                <link rel="icon" href="favicon.svg">
                        <link rel="shortcut icon" href="favicon.png">
                <link rel="stylesheet" href="css/variables.css">
        <link rel="stylesheet" href="css/general.css">
        <link rel="stylesheet" href="css/chrome.css">
                <link rel="stylesheet" href="css/print.css" media="print">
        
        <!-- Fonts -->
        <link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
                <link rel="stylesheet" href="fonts/fonts.css">
        
        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="highlight.css">
        <link rel="stylesheet" href="tomorrow-night.css">
        <link rel="stylesheet" href="ayu-highlight.css">

        <!-- Custom theme stylesheets -->
        
            </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="chapter-item expanded "><a href="getting_started.html"><strong aria-hidden="true">2.</strong> Getting Started</a></li><li class="chapter-item expanded "><a href="memory_tile.html"><strong aria-hidden="true">3.</strong> Memory Tile</a></li><li class="chapter-item expanded "><a href="polishing_the_tile.html"><strong aria-hidden="true">4.</strong> Polishing the Tile</a></li><li class="chapter-item expanded "><a href="from_one_to_multiple_tiles.html"><strong aria-hidden="true">5.</strong> From One To Multiple Tiles</a></li><li class="chapter-item expanded "><a href="creating_the_tiles_from_cpp.html"><strong aria-hidden="true">6.</strong> Creating The Tiles From C++</a></li><li class="chapter-item expanded "><a href="game_logic_in_cpp.html" class="active"><strong aria-hidden="true">7.</strong> Game Logic In C++</a></li><li class="chapter-item expanded "><a href="ideas_for_the_reader.html"><strong aria-hidden="true">8.</strong> Ideas For The Reader</a></li><li class="chapter-item expanded "><a href="conclusion.html"><strong aria-hidden="true">9.</strong> Conclusion</a></li></ol>            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                                                <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                                            </div>

                    <h1 class="menu-title">SixtyFPS Memory Game Tutorial (C++)</h1>

                    <div class="right-buttons">
                                                <a href="print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                                                                        
                    </div>
                </div>

                                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>
                
                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="game-logic-in-c"><a class="header" href="#game-logic-in-c">Game Logic In C++</a></h1>
<p>We'll implement the rules of the game in C++ as well. The general philosophy of SixtyFPS is that merely the user
interface is implemented in the <code>.60</code> 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.</p>
<p>We'll modify the <code>.60</code> markup in the <code>memory.60</code> file to signal to the C++ code when the user clicks on a tile.
Two changes to <span class="hljs-title">MainWindow</span> are needed: We need to add a way for the MainWindow to call to the C++ code that it should
check if a pair of tiles has been solved. And we need to add a property that C++ code can toggle to disable further
tile interaction, to prevent the player from opening more tiles than allowed. No cheating allowed! First, we paste
the callback and property declarations into <span class="hljs-title">MainWindow</span>:</p>
<pre><code class="language-60">    MainWindow := Window {
        width: 326px;
        height: 326px;

        callback check_if_pair_solved(); // Added
        property &lt;bool&gt; disable_tiles; // Added

        property &lt;[TileData]&gt; memory_tiles: [
           { image: @image-url(&quot;icons/at.png&quot;) },
</code></pre>
<p>The last change to the <code>.60</code> 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>:</p>
<pre><code class="language-60">        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 =&gt; {
                // old: tile.image_visible = !tile.image_visible;
                // new:
                if (!root.disable_tiles) {
                    tile.image_visible = !tile.image_visible;
                    root.check_if_pair_solved();
                }
            }
        }
</code></pre>
<p>On the C++ side, we can now add an handler to the <code>check_if_pair_solved</code> callback, that will check if
two tiles are opened. If they match, the <code>solved</code> 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 cannot click anything during this time.</p>
<p>Insert this code before the <code>main_window-&gt;run()</code> call:</p>
<pre><code class="language-cpp">
    auto tiles_model = std::make_shared&lt;sixtyfps::VectorModel&lt;TileData&gt;&gt;(new_tiles);
    main_window-&gt;set_memory_tiles(tiles_model);

    main_window-&gt;on_check_if_pair_solved(
            [main_window_weak = sixtyfps::ComponentWeakHandle(main_window)] {
                auto main_window = *main_window_weak.lock();
                auto tiles_model = main_window-&gt;get_memory_tiles();
                int first_visible_index = -1;
                TileData first_visible_tile;
                for (int i = 0; i &lt; tiles_model-&gt;row_count(); ++i) {
                    auto tile = tiles_model-&gt;row_data(i);
                    if (!tile.image_visible || tile.solved)
                        continue;
                    if (first_visible_index == -1) {
                        first_visible_index = i;
                        first_visible_tile = tile;
                        continue;
                    }
                    bool is_pair_solved = tile == first_visible_tile;
                    if (is_pair_solved) {
                        first_visible_tile.solved = true;
                        tiles_model-&gt;set_row_data(first_visible_index,
                                                  first_visible_tile);
                        tile.solved = true;
                        tiles_model-&gt;set_row_data(i, tile);
                        return;
                    }
                    main_window-&gt;set_disable_tiles(true);

                    sixtyfps::Timer::single_shot(std::chrono::seconds(1),
                        [=]() mutable {
                            main_window-&gt;set_disable_tiles(false);
                            first_visible_tile.image_visible = false;
                            tiles_model-&gt;set_row_data(first_visible_index,
                                                      first_visible_tile);
                            tile.image_visible = false;
                            tiles_model-&gt;set_row_data(i, tile);
                        });
                }
            });

    main_window-&gt;run();
}
</code></pre>
<p>Notice that we take a weak pointer of our <code>main_window</code>. This is very
important because capturing a copy of the <code>main_window</code> itself within the callback handler would result in a circular ownership.
The <code>MainWindow</code> owns the callback handler, which itself owns a reference to the <code>MainWindow</code>, which must be weak
instead of strong to avoid a memory leak.</p>
<p>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.</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                                                    <a rel="prev" href="creating_the_tiles_from_cpp.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>
                        
                                                    <a rel="next" href="ideas_for_the_reader.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>
                        
                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                                    <a rel="prev" href="creating_the_tiles_from_cpp.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                
                                    <a rel="next" href="ideas_for_the_reader.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
                            </nav>

        </div>

        
        
        
                <script type="text/javascript">
            window.playground_copyable = true;
        </script>
        
        
                <script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="searcher.js" type="text/javascript" charset="utf-8"></script>
        
        <script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->
        
        
    </body>
</html>