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 provides a list of highscores. Each time the game is completed,
a new entry is added to the list.
Scrolling is deliberately deactivated on the list itself. Instead it can
be scrolled using areas at the sides of the list.
*/
BaseView {
id: root
property int swipeAreaSize: Globals.useSmallTouchAreas ? Style.swipeAreaSizeSmall : Style.swipeAreaSizeDefault
function addHighscore() {
highscoreModel.addEntry(Globals.time, Globals.tries, Globals.score)
}
signal swipeTriggered()
clip: true
MouseArea {
id: scrollArea
property int startY: 0
property int startContentY: 0
anchors {
top: contentFlickable.top
bottom: contentFlickable.bottom
left: parent.left
right: parent.right
}
onPressed: {
startY = mouseY
startContentY = contentFlickable.contentY
}
onPositionChanged: {
contentFlickable.contentY = Math.max(0, Math.min(startContentY - mouseY + startY, contentFlickable.contentHeight - contentFlickable.height))
}
MouseArea {
id: blockArea
anchors {
fill: parent
leftMargin: root.swipeAreaSize
rightMargin: root.swipeAreaSize
}
}
// can be activated in the config view as a guide for users
Rectangle {
id: leftAreaVisualizer
anchors {
left: parent.left
right: blockArea.left
top: parent.top
bottom: parent.bottom
}
color: "magenta"
opacity: 0.4
visible: Globals.showTouchAreas
}
// can be activated in the config view as a guide for users
Rectangle {
id: rightAreaVisualizer
anchors {
left: blockArea.right
right: parent.right
top: parent.top
bottom: parent.bottom
}
color: "magenta"
opacity: 0.4
visible: Globals.showTouchAreas
}
}
HighscoreModel {
id: highscoreModel
}
Flickable {
id: contentFlickable
property int hypothenuse: root.height * 0.5
anchors {
left: parent.left
right: parent.right
margins: root.swipeAreaSize
verticalCenter: parent.verticalCenter
}
// with the width set by the swipeAreaSize value the height is calulated so its top and bottom hit the circular frame
height: 2 * Math.sqrt((hypothenuse * hypothenuse) - ((hypothenuse - root.swipeAreaSize) * (hypothenuse - root.swipeAreaSize)))
contentHeight: Math.max(contentColumn.height, contentFlickable.height)
contentWidth: 0
interactive: false
Column {
id: contentColumn
function addZeros(score : int) : string {
if (score < 10) {
return "00" + score
} else if (score < 100) {
return "0" + score
}
return score
}
function formatTime(time : int) : string {
if (time % 1000 === 0) {
return time/1000 + ".0"
}
return time/1000
}
width: contentFlickable.width
spacing: Style.listSpacing
Text {
id: header
anchors.horizontalCenter: parent.horizontalCenter
font: Style.textFontSmall
color: Style.colorText
text: "Highscore"
}
Repeater {
id: contentRepeater
model: highscoreModel
delegate: Text {
anchors.horizontalCenter: parent.horizontalCenter
font: Style.textFontSmall
color: Style.colorText
text: contentColumn.addZeros(index + 1) + ": " +
contentColumn.formatTime(model.time) + "s, " + model.tries + " tries: " +
contentColumn.addZeros(model.score)
}
}
}
}
ScrollIndicator {
id: leftIndicator
anchors {
top: contentFlickable.top
bottom: contentFlickable.bottom
left: contentFlickable.left
}
flickable: contentFlickable
showIndicator: scrollArea.pressed
}
ScrollIndicator {
id: rightIndicator
anchors {
verticalCenter: parent.verticalCenter
right: contentFlickable.right
}
height: leftIndicator.height
flickable: contentFlickable
showIndicator: scrollArea.pressed
}
// used for navigation between views
DirectionalSwipeArea {
id: downArea
anchors.fill: parent
direction: Swipe.Direction.Down
onTriggered: {
root.swipeTriggered()
}
}
// a mask is required to hide the flickable's content outside the circular frame, because due to the circular
// layout of the view clipping alone woulnd't suffice.
Image {
id: circleMask
anchors.fill: parent
source: "mask_circle.svg"
}
}