Polishing the Tile

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. 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 moves to the right, sliding the curtain open when animated

To make the tile extensible, replace the hard-coded icon name with an icon property that can be set when instantiating the element.

For the final polish, add a solved property used to animate the color to a shade of green when a player finds a pair.

Replace the code inside the ui/appwindow.slint file with the following:

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;
        x: 0px;
        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 {
    MemoryTile {
        icon: @image-url("icons/bus.png");
        clicked => {
            self.open_curtain = !self.open_curtain;
        }
    }
}

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.

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.