/* Speed climbing reaction timer Copyright (C) 2019 Itsblue Development | Dorian Zedler This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import QtQuick 2.9 import QtQuick.Window 2.2 import QtQuick.Controls 2.4 import QtMultimedia 5.0 import QtGraphicalEffects 1.0 import QtQuick.Controls.Styles 1.4 import com.itsblue.SpeedClimbingStartTrainer 2.0 import "./Components" Window { visible: true width: 640 height: 480 title: qsTr("Hello World") Page { id: app state: "IDLE" anchors.fill: parent property double startTime: -1 property double stopTime: -1 property double reactionTime: 0 property int highscore: settings.read("highscore") AppSettings { id: settings } AppTheme { id: theme } Item { id: soundsItm SoundEffect { id: readySe source: "qrc:/sounds/ready_1.wav" muted: app.state === "WAITING" onPlayingChanged: { if(!playing && app.state === "RUNNING"){ startSe.play() } } } SoundEffect { id: startSe source: "qrc:/sounds/OFFICAL_IFSC_STARTIGNAL.wav" muted: app.state === "WAITING" onPlayingChanged: { if(!playing){ app.startTime = new Date().getTime() console.log("offset: ", new Date().getTime() - app.startTime) if(app.state === "WAITING"){ app.reactionTime = app.stopTime - app.startTime app.state = "STOPPED" } } } } SoundEffect { id: failedSe source: "qrc:/sounds/false.wav" } } Rectangle { anchors.fill: parent color: theme.style.backgroundColor Behavior on color { ColorAnimation { duration: 200 } } } Item { id: topContainerItm anchors { top: parent.top left: parent.left right: app.landscape() ? startBt.left:parent.right bottom: app.landscape() ? parent.bottom:startBt.top bottomMargin: app.landscape() ? undefined:parent.height * 0.1 rightMargin: app.landscape() ? parent.width * 0.05:0 } Text { id: topLa anchors.centerIn: parent width: parent.width * 0.8 text: "" color: theme.style.textColor fontSizeMode: Text.Fit verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pixelSize: app.landscape() ? parent.width * 0.1 : parent.height * 0.4 minimumPixelSize: 0 Behavior on text { FadeAnimation{ target: topLa } } } Text { id: highscoreLa anchors { top: topLa.bottom horizontalCenter: parent.horizontalCenter } width: topLa.width * 0.5 height: topLa.height visible: text !== "highscore: -1" text: "" color: theme.style.textColor fontSizeMode: Text.Fit verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pixelSize: app.landscape() ? parent.width * 0.1 : parent.height * 0.4 minimumPixelSize: 0 Behavior on text { FadeAnimation{ target: highscoreLa } } } Switch { anchors { top: topLa.bottom horizontalCenter: parent.horizontalCenter horizontalCenterOffset: width * 0.1 } checked: settings.read("theme") === "1" scale: 0.8 opacity: app.state === "IDLE" ? 1:0 onCheckedChanged: { if(checked){ settings.write("theme", 1) } else { settings.write("theme", 0) } theme.refreshTheme() } indicator: Rectangle { property bool checked: parent.checked property bool down: parent.down height: parent.height width: height * 2 radius: height * 0.5 color: parent.checked ? "#17a81a" : "transparent" border.color: parent.checked ? "#17a81a" : "#cccccc" Behavior on color{ ColorAnimation{ duration: 200 } } Rectangle { x: parent.checked ? parent.width - width : 0 width: parent.height height: parent.height radius: height * 0.5 color: parent.down ? theme.style.buttonPressedColor:theme.style.backgroundColor //"#cccccc" : "#ffffff" border.color: parent.checked ? (parent.down ? "#17a81a" : "#21be2b") : "#999999" Behavior on x{ NumberAnimation { property: "x" duration: 200 easing.type: Easing.InOutQuad } } } } Behavior on opacity { NumberAnimation { duration: 200 } } } } FancyButton { id: startBt text: "start" property int size: app.landscape() ? parent.width * 0.5:parent.height * 0.5 backgroundColor: theme.style.buttonColor textColor: theme.style.textColor anchors { bottom: parent.bottom right: parent.right bottomMargin: app.landscape() ? parent.height * 0.5 - startBt.height * 0.5:parent.height * 0.1 rightMargin: app.landscape() ? parent.width * 0.05:parent.width * 0.5 - startBt.width * 0.5 } font.pixelSize: height * 0.15 height: app.landscape() ? (size > parent.height * 0.9 ? parent.height * 0.9:size) : (size > parent.width * 0.9 ? parent.width * 0.9:size) width: height onPressed: { app.start() } onReleased: { app.stop() } } function landscape() { return app.height < app.width } function start() { if(app.state !== "IDLE"){ app.reset() return } app.state = "RUNNING" readySe.play() } function stop() { app.stopTime = new Date().getTime() if(app.state !== "RUNNING"){ return } if(startSe.playing){ app.state = "WAITING" failedSe.play() } else if(readySe.playing){ app.state = "IDLE" } else { app.state = "STOPPED" app.reactionTime = app.stopTime - app.startTime app.refreshHighscore() } } function refreshHighscore(){ if(app.reactionTime > 0 && ( app.reactionTime < parseInt(settings.read("highscore")) || settings.read("highscore") === "-1" )){ settings.write("highscore", app.reactionTime) app.highscore = app.reactionTime } } function reset() { app.state = "IDLE" } states: [ State { name: "IDLE" PropertyChanges { target: topLa text: "Hold down start to start" } }, State { name: "RUNNING" PropertyChanges { target: topLa text: "release to stop" } }, State { name: "STOPPED" PropertyChanges { target: topLa text: "Your reaction time: " + app.reactionTime } PropertyChanges { target: startBt text: "reset" } PropertyChanges { target: highscoreLa text: "highscore: " + app.highscore } }, State { name: "WAITING" PropertyChanges { target: topLa text: "please wait..." } PropertyChanges { target: startBt enabled: false text: "waiting..." } } ] } }