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
/*
This component provides circular gesture recognition.
*/
MouseArea {
id: root
// NOTE: overwrite the Style and Globals value assignments if you want to use the component outside of this project
// determines which distance a touch coordinate needs to have from the center to count as valid
property int circleMinDistance: Style.swipeCircleMinDistance
// determines the angle a circular gesture needs to cover to be recognized as a swipe
property int arcThreshold: Style.swipeArcThreshold
// the image used for visual feedback. Set to empty string if you want to try this component outside of this project
property string highlightSource: "mask_circle_highlight_right.svg"
// can be activated in the config view as a guide for users
property bool showTouchArea: Globals.showTouchAreas
signal clockwiseTriggered()
signal counterClockwiseTriggered()
implicitWidth: Style.appSize
implicitHeight: Style.appSize
onPressed: {
var distanceFromCenter = internal.calculateDistanceFromCenter(mouseX, mouseY)
// reject touch coordinates outside a circular strip
if (distanceFromCenter >= internal.radius || distanceFromCenter < root.circleMinDistance) {
mouse.accepted = false
} else {
internal.startAngle = internal.calculateAngle(mouseX, mouseY)
internal.currentAngle = internal.startAngle
highlight.visible = true
}
}
onPositionChanged: {
var distanceFromCenter = internal.calculateDistanceFromCenter(mouseX, mouseY)
if (distanceFromCenter >= internal.radius || distanceFromCenter < root.circleMinDistance) {
highlight.visible = false
// reset the startAngle if the touch coordinates move outside the desired area
internal.startAngle = internal.calculateAngle(mouseX, mouseY)
} else {
internal.currentAngle = internal.calculateAngle(mouseX, mouseY)
var arcLength = internal.currentAngle - internal.startAngle
// take care to handle the transition between 180 and -180 properly, since Math.atan2 used
// to calculate the angle returns angles from -180 to 180
if (arcLength < -180) {
arcLength += 360
} else if (arcLength > 180) {
arcLength -= 360
}
// trigger a signal if the angle between the start point and the current point exceeds a given threshold
if (arcLength >= root.arcThreshold) {
root.clockwiseTriggered()
internal.startAngle = internal.currentAngle
} else if (arcLength <= -root.arcThreshold) {
root.counterClockwiseTriggered()
internal.startAngle = internal.currentAngle
}
highlight.visible = true
}
}
// can be activated in the config view as a guide for users
Rectangle {
id: areaVisualizer
anchors.fill: parent
radius: width * 0.5
color: "magenta"
opacity: 0.4
visible: root.showTouchArea
}
Rectangle {
anchors.centerIn: areaVisualizer
width: 2 * root.circleMinDistance
height: width
radius: width * 0.5
color: "#1b1d1e"
visible: root.showTouchArea
}
onReleased: {
highlight.visible = false
}
Image {
id: highlight
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.horizontalCenter
height: parent.height
width: parent.width/2
source: root.highlightSource
transform: Rotation {
origin.x: 0
origin.y: height/2
angle: internal.currentAngle
}
// don't use a binding to the pressed property of the MouseArea to show the highlight since
// we explicitly don't want to show the highlight for touch coordinates outside a specific range
visible: false
}
QtObject {
id: internal
property int startAngle: 0
property int currentAngle: 0
readonly property real radius: root.width * 0.5
// returns the angle calculated from the given x,y coordinates
function calculateAngle(x : int, y : int) : int {
return radianToDegree(Math.atan2(y - radius, x - radius))
}
// converts a given radian value to degrees
function radianToDegree(radian : real) : int {
return radian * 180/Math.PI
}
// returns the distance the given x,y coordinates have to the center
function calculateDistanceFromCenter(x : int, y : int) : int {
return Math.sqrt((x - radius) * (x - radius) + (y - radius) * (y - radius))
}
}
}