diff --git a/headers/climbingrace.h b/headers/climbingrace.h index 19edd77..23f7419 100644 --- a/headers/climbingrace.h +++ b/headers/climbingrace.h @@ -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(); }; diff --git a/qml/ProfilesDialog.qml b/qml/ProfilesDialog.qml index 3904058..4345c18 100644 --- a/qml/ProfilesDialog.qml +++ b/qml/ProfilesDialog.qml @@ -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 diff --git a/qml/SettingsDialog.qml b/qml/SettingsDialog.qml index 4affffb..4487de4 100644 --- a/qml/SettingsDialog.qml +++ b/qml/SettingsDialog.qml @@ -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 + } } } diff --git a/qml/components/FancyBusyIndicator.qml b/qml/components/FancyBusyIndicator.qml new file mode 100644 index 0000000..32d8421 --- /dev/null +++ b/qml/components/FancyBusyIndicator.qml @@ -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" + } + } + } + } + } +} diff --git a/qml/components/RemoteDataListView.qml b/qml/components/RemoteDataListView.qml new file mode 100644 index 0000000..d9c8ccc --- /dev/null +++ b/qml/components/RemoteDataListView.qml @@ -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 + } +} diff --git a/qml/qml.qrc b/qml/qml.qrc index 6a1ba34..1043b1e 100644 --- a/qml/qml.qrc +++ b/qml/qml.qrc @@ -14,5 +14,7 @@ components/SmoothSwitchDelegate.qml components/InputDelegate.qml components/SmoothSliderDelegate.qml + components/RemoteDataListView.qml + components/FancyBusyIndicator.qml diff --git a/sources/climbingrace.cpp b/sources/climbingrace.cpp index bd195be..6e42c0f 100644 --- a/sources/climbingrace.cpp +++ b/sources/climbingrace.cpp @@ -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 ---