- added ability to view results of athlete
- improved design of athlete view and stack push / pop animations
This commit is contained in:
parent
6b819d3298
commit
33ae172038
7 changed files with 400 additions and 46 deletions
|
@ -101,6 +101,7 @@ public slots:
|
|||
Q_INVOKABLE bool createAthlete( QString userName, QString fullName );
|
||||
Q_INVOKABLE bool deleteAthlete( QString userName );
|
||||
Q_INVOKABLE bool selectAthlete( QString userName );
|
||||
Q_INVOKABLE QVariant getResults( QString userName );
|
||||
|
||||
Q_INVOKABLE bool reloadBaseStationIpAdress();
|
||||
};
|
||||
|
|
|
@ -68,7 +68,7 @@ Popup {
|
|||
StackView {
|
||||
id: profiles_stack
|
||||
property int text_pixelSize: headlineUnderline.width * 0.08
|
||||
initialItem: profileListComp
|
||||
//initialItem: profileListComp
|
||||
width: headlineUnderline.width
|
||||
|
||||
anchors {
|
||||
|
@ -84,44 +84,48 @@ Popup {
|
|||
NumberAnimation {duration: 200}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
onOpened: {
|
||||
profiles_stack.openAthletes()
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentItemChanged: {
|
||||
currentItem.opened()
|
||||
}
|
||||
|
||||
function openAthletes() {
|
||||
var athsComp = profileListComp.createObject(null, {})
|
||||
profiles_stack.push(athsComp)
|
||||
}
|
||||
|
||||
function openResults( userName ){
|
||||
var resComp = resultViewComp.createObject(null, {"userName": userName})
|
||||
profiles_stack.push(resComp)
|
||||
}
|
||||
|
||||
/*-----List of all profiles-----*/
|
||||
Component {
|
||||
id: profileListComp
|
||||
|
||||
|
||||
ListView {
|
||||
RemoteDataListView {
|
||||
id: profileList
|
||||
|
||||
property string title: "profiles"
|
||||
property string secondButt: "add"
|
||||
property var listData: speedBackend.getAthletes()
|
||||
|
||||
function loadData() {
|
||||
profileList.enabled = false
|
||||
signal opened()
|
||||
|
||||
onOpened: {
|
||||
loadData()
|
||||
}
|
||||
|
||||
loadData: function () {
|
||||
status = 905
|
||||
listData = {}
|
||||
listData = speedBackend.getAthletes()
|
||||
profileList.enabled = true
|
||||
}
|
||||
|
||||
model: listData.length
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
onOpened: {
|
||||
profileList.loadData()
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
opacity: profileList.count <= 0 ? 1:0
|
||||
text: "add a profile by clicking +"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: parent.width*0.06
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
status = listData.lenght !== false ? 200:0
|
||||
}
|
||||
|
||||
delegate: SwipeDelegate {
|
||||
|
@ -141,14 +145,53 @@ Popup {
|
|||
}
|
||||
|
||||
onClicked: {
|
||||
if(speedBackend.selectAthlete(profileList.listData[index]["userName"])){
|
||||
profileList.loadData()
|
||||
}
|
||||
profiles_stack.openResults(profileList.listData[index]["userName"])
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Qt.darker( pressed ? Qt.darker("white", 1.1):"white", swipeDelegate.active ? 1.1:0 )
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: control
|
||||
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
height: parent.height * 0.6
|
||||
|
||||
checked: swipeDelegate.active
|
||||
|
||||
onCheckedChanged: {
|
||||
if(checked && !swipeDelegate.active && speedBackend.selectAthlete(profileList.listData[index]["userName"])){
|
||||
profileList.loadData()
|
||||
}
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 26
|
||||
implicitHeight: 26
|
||||
|
||||
height: parent.height
|
||||
width: height
|
||||
|
||||
x: control.leftPadding
|
||||
y: parent.height / 2 - height / 2
|
||||
|
||||
radius: width * 0.2
|
||||
border.color: control.down ? "#17a81a" : "#21be2b"
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * 0.65
|
||||
height: width
|
||||
anchors.centerIn: parent
|
||||
radius: width * 0.2
|
||||
color: control.down ? "#17a81a" : "#21be2b"
|
||||
visible: control.checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -188,9 +231,12 @@ Popup {
|
|||
height: parent.height
|
||||
|
||||
SwipeDelegate.onClicked: {
|
||||
profileList.status = 905
|
||||
if(speedBackend.deleteAthlete(profileList.listData[index]["userName"])){
|
||||
profileList.loadData()
|
||||
return
|
||||
}
|
||||
profileList.status = 200
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
|
@ -199,10 +245,7 @@ Popup {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*-----Option to add a profile-----*/
|
||||
|
@ -220,6 +263,7 @@ Popup {
|
|||
|
||||
onClicked: {
|
||||
if(speedBackend.createAthlete(userNameTf.text, fullNameTf.text)){
|
||||
profiles_stack.get(profiles_stack.depth - 2 ).loadData()
|
||||
profiles_stack.pop()
|
||||
}
|
||||
}
|
||||
|
@ -248,25 +292,99 @@ Popup {
|
|||
}
|
||||
}
|
||||
|
||||
/*-----Custom animations-----*/
|
||||
// --- Result View ---
|
||||
Component {
|
||||
id: resultViewComp
|
||||
RemoteDataListView {
|
||||
id: resultView
|
||||
|
||||
property string userName
|
||||
property string title: userName
|
||||
property string secondButt: "none"
|
||||
|
||||
signal opened()
|
||||
|
||||
onOpened: {
|
||||
loadData()
|
||||
}
|
||||
|
||||
loadData: function () {
|
||||
status = 905
|
||||
listData = {}
|
||||
listData = speedBackend.getResults(userName)
|
||||
status = listData.lenght !== false ? 200:0
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: resultDel
|
||||
|
||||
width: parent.width
|
||||
|
||||
font.pixelSize: profiles_stack.text_pixelSize
|
||||
|
||||
text: "result: " + (listData[index]["result"] / 1000).toFixed(3) + " sec \nreaction time: " + listData[index]["reactionTime"].toFixed(0) + " ms"
|
||||
|
||||
Rectangle {
|
||||
color: "grey"
|
||||
height: 1
|
||||
width: parent.width * 0.9
|
||||
visible: index > 0
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
||||
font.pixelSize: 10
|
||||
|
||||
text: " " + getText()
|
||||
|
||||
function getText(){
|
||||
var date = new Date(listData[index]["timestamp"]*1000).toLocaleDateString(Qt.locale("de_DE"))
|
||||
return date
|
||||
console.log(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----Custom animations-----*/
|
||||
pushEnter: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: 200
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
from: width * 0.1
|
||||
to: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
pushExit: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: 200
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
to: -width * 0.1
|
||||
from: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
|
||||
popExit: Transition {
|
||||
|
@ -274,18 +392,30 @@ Popup {
|
|||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: 200
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
to: width * 0.1
|
||||
from: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
popEnter: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: 200
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
from: -width * 0.1
|
||||
to: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,14 +463,21 @@ Popup {
|
|||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
|
||||
var topMargin = backgroundRect.radius
|
||||
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = headerBackground.color
|
||||
ctx.moveTo(0, 0);
|
||||
//ctx.arc(centreX, centreY, root.width / 2, 1 * Math.PI, 2*Math.PI, false);
|
||||
ctx.lineTo(width, 0);
|
||||
ctx.moveTo(width, topMargin);
|
||||
//
|
||||
//ctx.lineTo(width, topMargin);
|
||||
ctx.lineTo(width, height);
|
||||
ctx.lineTo(0, height);
|
||||
ctx.lineTo(0, 0);
|
||||
ctx.lineTo(0, topMargin)
|
||||
|
||||
ctx.arc(topMargin, topMargin, topMargin, 1 * Math.PI, 1.5*Math.PI, false);
|
||||
ctx.lineTo(width-topMargin, 0)
|
||||
ctx.arc(width-topMargin, topMargin, topMargin, 1.5*Math.PI, 0, false)
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
@ -408,6 +545,8 @@ Popup {
|
|||
height: parent.height * 0.1
|
||||
width:height
|
||||
|
||||
opacity: profiles_stack.currentItem.secondButt !== "none" ? 1:0
|
||||
|
||||
glowOpacity: Math.pow( root.opacity, 100 )
|
||||
|
||||
backgroundColor: appTheme.style.buttonColor
|
||||
|
|
|
@ -520,6 +520,13 @@ Popup {
|
|||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
from: width * 0.1
|
||||
to: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
pushExit: Transition {
|
||||
NumberAnimation {
|
||||
|
@ -529,6 +536,13 @@ Popup {
|
|||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
to: -width * 0.1
|
||||
from: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
|
||||
popExit: Transition {
|
||||
|
@ -539,6 +553,12 @@ Popup {
|
|||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
to: width * 0.1
|
||||
from: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
popEnter: Transition {
|
||||
NumberAnimation {
|
||||
|
@ -548,6 +568,12 @@ Popup {
|
|||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
property: "x"
|
||||
from: -width * 0.1
|
||||
to: 0
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
95
qml/components/FancyBusyIndicator.qml
Normal file
95
qml/components/FancyBusyIndicator.qml
Normal file
|
@ -0,0 +1,95 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
|
||||
BusyIndicator {
|
||||
id: control
|
||||
|
||||
property double animationSpeed: 0.5
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: 64
|
||||
implicitHeight: 64
|
||||
|
||||
Item {
|
||||
id: item
|
||||
|
||||
x: parent.width / 2 - 32
|
||||
y: parent.height / 2 - 32
|
||||
|
||||
width: 64
|
||||
height: 64
|
||||
|
||||
opacity: control.running ? 1 : 0
|
||||
|
||||
property int currentHeight: 0
|
||||
|
||||
onCurrentHeightChanged: {
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: 250
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
loops: Animation.Infinite
|
||||
|
||||
running: true
|
||||
|
||||
NumberAnimation {
|
||||
target: item
|
||||
|
||||
duration: 2000 * 1/control.animationSpeed
|
||||
|
||||
to: 1000
|
||||
|
||||
properties: "currentHeight"
|
||||
|
||||
easing.type: Easing.InOutQuad
|
||||
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
target: item
|
||||
|
||||
duration: 2000 * 1/control.animationSpeed
|
||||
|
||||
to: 0
|
||||
|
||||
properties: "currentHeight"
|
||||
|
||||
easing.type: Easing.InOutQuad
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: item.width / 9
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: 5
|
||||
|
||||
Rectangle {
|
||||
|
||||
property double heightMultiplier: Math.abs( Math.sin(( (item.currentHeight + (index*20))*0.01) * (Math.PI/2) ) )
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
width: item.width / 9
|
||||
height: ( heightMultiplier ) * ( item.height - 1 ) + 1
|
||||
|
||||
radius: width * 0.5
|
||||
|
||||
color: "#21be2b"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
qml/components/RemoteDataListView.qml
Normal file
77
qml/components/RemoteDataListView.qml
Normal file
|
@ -0,0 +1,77 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.4
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
property var loadData
|
||||
property var listData: ({})
|
||||
property Component delegate
|
||||
|
||||
property int status: -1
|
||||
|
||||
signal refresh()
|
||||
|
||||
Component.onCompleted: {
|
||||
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
model: control.listData.length
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
enabled: status === 200 || status === 902
|
||||
opacity: enabled ? 1:0
|
||||
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
parent: listView.parent
|
||||
|
||||
anchors {
|
||||
top: listView.top
|
||||
left: listView.right
|
||||
margins: 10
|
||||
leftMargin: 3
|
||||
bottom: listView.bottom
|
||||
}
|
||||
|
||||
width: 8
|
||||
|
||||
visible: listView.model > 0
|
||||
|
||||
active: true
|
||||
}
|
||||
|
||||
delegate: control.delegate
|
||||
|
||||
onContentYChanged: {
|
||||
/*
|
||||
if(contentY < -listView.height * 0.3 && control.status !== 905){
|
||||
contentY = 0
|
||||
control.refresh()
|
||||
}*/
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on contentY {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FancyBusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
opacity: listView.opacity === 1 ? 0:1
|
||||
}
|
||||
}
|
|
@ -14,5 +14,7 @@
|
|||
<file>components/SmoothSwitchDelegate.qml</file>
|
||||
<file>components/InputDelegate.qml</file>
|
||||
<file>components/SmoothSliderDelegate.qml</file>
|
||||
<file>components/RemoteDataListView.qml</file>
|
||||
<file>components/FancyBusyIndicator.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -459,7 +459,7 @@ QVariant ClimbingRace::getAthletes() {
|
|||
|
||||
QVariantList tmpAthletes = reply["data"].toList();
|
||||
|
||||
qDebug() << tmpAthletes;
|
||||
//qDebug() << tmpAthletes;
|
||||
|
||||
return tmpAthletes;
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ bool ClimbingRace::selectAthlete( QString userName){
|
|||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
qDebug() << "+ --- error deleting athlete: " << reply["status"];
|
||||
qDebug() << "+ --- error selecting athlete: " << reply["status"];
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -511,7 +511,21 @@ bool ClimbingRace::selectAthlete( QString userName){
|
|||
|
||||
}
|
||||
|
||||
QVariant ClimbingRace::getResults( QString userName ){
|
||||
QVariantMap reply = this->baseConn->sendCommand(4004, userName);
|
||||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
qDebug() << "+ --- error getting results: " << reply["status"];
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantList tmpAthletes = reply["data"].toList();
|
||||
|
||||
//qDebug() << tmpAthletes;
|
||||
|
||||
return tmpAthletes;
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// --- functions for qml ---
|
||||
|
|
Reference in a new issue