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. 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:

open_curtain value:falsetrue
Left curtain rectangleFill the left half by setting the width width to half the parent's widthWidth of zero makes the rectangle invisible
Right curtain rectangleFill the right half by setting x and width to half of the parent's widthwidth 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 slint! macro with the following:

MemoryTile := Rectangle {
    callback clicked;
    property <bool> open_curtain;
    property <bool> solved;
    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();
        }
    }
}
MainWindow := Window {
    MemoryTile {
        icon: @image-url("icons/bus.png");
        clicked => {
            self.open_curtain = !self.open_curtain;
        }
    }
}

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 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.