C
Qt Quick Ultralite swipe_game Demo
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
import QtQuick 2.0
import StyleModule 1.0
import SwipeModule 1.0
/*
This view displays information about the current game status and
controls the flow of the game.
*/
BaseView {
id: root
signal gameOver()
signal gameAborted()
// start/stop the timers which control the flow if the view is (not) visible
onVisibleChanged: {
if (visible) {
restartTimer.restart()
} else {
responseTimer.stop()
}
}
MouseArea {
id: swipeArea
anchors.fill: parent
property int startX: 0
property int startY: 0
property bool swipeFinished: false
onPressed: {
startX = mouseX
startY = mouseY
swipeFinished = false
}
function guessedGreater(swipe : Swipe.Direction) : bool
{
return (swipe == Swipe.Direction.Right) || (swipe == Swipe.Direction.Up)
}
onPositionChanged: {
if (swipeFinished) {
return
}
var swipe = Swipe.detectDirection(mouseX, mouseY, startX, startY, Style.swipeThreshold)
if ((Globals.isNumberMode() && Swipe.isHorizontal(swipe)) ||
(!Globals.isNumberMode() && Swipe.isVertical(swipe))) {
swipeFinished = true
Globals.checkAnswer(guessedGreater(swipe))
internal.checkStatus()
}
}
}
Row {
id: failRow
anchors {
top: parent.top
topMargin: Style.marginBig
horizontalCenter: parent.horizontalCenter
}
spacing: Style.lineSize
Repeater {
model: Globals.tries
delegate: Rectangle {
height: Style.buttonHeight
width: height
radius: height * 0.5
color: Style.colorLines
Rectangle {
anchors {
fill: parent
margins: Style.lineSize
}
radius: height * 0.5
color: index < Globals.fails ? Style.colorWarning : Style.colorButtonBackground
}
}
}
}
Text {
id: currentValue
anchors.centerIn: parent
font: Globals.isNumberMode() ? Style.textFontBig : Style.textFontDefault
color: Style.colorText
text: Globals.isNumberMode() ? Globals.gameCurrentNumber : Globals.gameCurrentCountry
}
Text {
id: score
anchors {
bottom: stopButton.top
bottomMargin: Style.marginDefault
horizontalCenter: parent.horizontalCenter
}
font: Style.textFontDefault
color: Style.colorText
text: "Score: " + Globals.score
}
Button {
id: stopButton
anchors {
bottom: parent.bottom
bottomMargin: Style.marginDefault
horizontalCenter: parent.horizontalCenter
}
font: Style.textFontDefault
text: "Stop"
onClicked: {
root.gameAborted()
}
}
QtObject {
id: internal
function checkStatus() {
if (Globals.fails >= Globals.tries) {
root.gameOver()
} else {
Globals.requestNewValue()
restartTimer.restart()
}
}
}
Timer {
id: responseTimer
property int steps: 12 // aim for 12 steps to visualize the timeout indicators
property int currentStep: 0
property int angle: currentStep * 360/steps
interval: Globals.time / steps
repeat: true
onTriggered: {
currentStep += 1
if (currentStep >= steps) {
stop()
Globals.addFail()
internal.checkStatus()
}
}
}
// restarting a timer while its onTriggered block is still active doesn't work.
// It even crashes when the timer is set to repeat and restart is called while onTriggered is still active.
// Use a second timer to restart the responseTimer with a small delay
Timer {
id: restartTimer
interval: 10
onTriggered: {
responseTimer.currentStep = 0
responseTimer.restart()
}
}
// the timeoutIndicator displays a line which grows circular around the frame
// to indicate how much time is left to respond to the current game value.
Item {
id: timeoutIndicator
anchors.fill: parent
Item {
id: rightArcClipper
anchors {
left: parent.horizontalCenter
right: parent.right
top: parent.top
bottom: parent.bottom
}
clip: true
visible: responseTimer.running
Image {
id: rightArc
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.left
}
width: timeoutIndicator.width
height: width
source: "timebar.svg"
rotation: Math.min(180, responseTimer.angle)
}
}
Item {
id: leftArcClipper
anchors {
left: parent.left
right: parent.horizontalCenter
top: parent.top
bottom: parent.bottom
}
clip: true
visible: responseTimer.running
Image {
id: leftArc
width: timeoutIndicator.width
height: width
source: "timebar.svg"
rotation: Math.max(180, responseTimer.angle)
}
}
}
}