split profile dialog up into multiple files
This commit is contained in:
parent
a3f0f13ff3
commit
9bb64027b4
10 changed files with 869 additions and 743 deletions
Binary file not shown.
|
@ -1,741 +0,0 @@
|
||||||
/*
|
|
||||||
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
|
||||||
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
|
||||||
|
|
||||||
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, version 3 of the License.
|
|
||||||
|
|
||||||
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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.9
|
|
||||||
import QtMultimedia 5.8
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
import QtQuick.Controls 2.4
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import com.itsblue.speedclimbingstopwatch 1.0
|
|
||||||
import "./components"
|
|
||||||
|
|
||||||
|
|
||||||
Popup {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
modal: true
|
|
||||||
dim: false
|
|
||||||
|
|
||||||
opacity: 0
|
|
||||||
|
|
||||||
enter: Transition {
|
|
||||||
NumberAnimation { properties: "opacity"; to: 1; duration: 300; easing.type: Easing.InOutQuad }
|
|
||||||
NumberAnimation { properties: "scale"; from: 0.9; to: 1; duration: 300; easing.type: Easing.InOutQuad }
|
|
||||||
}
|
|
||||||
|
|
||||||
exit: Transition {
|
|
||||||
NumberAnimation { properties: "opacity"; to: 0; duration: 300; easing.type: Easing.InOutQuad }
|
|
||||||
NumberAnimation { properties: "scale"; from: 1; to: 0.9; duration: 300; easing.type: Easing.InOutQuad }
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
RectangularGlow {
|
|
||||||
id: backgroundEffect
|
|
||||||
glowRadius: 7
|
|
||||||
spread: 0.02
|
|
||||||
color: "black"
|
|
||||||
opacity: 0.18
|
|
||||||
anchors.fill: backgroundRect
|
|
||||||
cornerRadius: backgroundRect.radius
|
|
||||||
scale: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: backgroundRect
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: width * 0.1
|
|
||||||
color: appTheme.style.viewColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StackView {
|
|
||||||
id: profiles_stack
|
|
||||||
property int text_pixelSize: headlineUnderline.width * 0.08
|
|
||||||
//initialItem: profileListComp
|
|
||||||
width: headlineUnderline.width
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: topContainerItm.bottom
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: ( parent.width - headlineUnderline.width ) / 2
|
|
||||||
topMargin: headlineUnderline.anchors.topMargin * 1.2
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: topContainerItm.height * 0.3
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {duration: 200}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
profiles_stack.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
onOpened: {
|
|
||||||
profiles_stack.init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCurrentItemChanged: {
|
|
||||||
currentItem.opened()
|
|
||||||
}
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
if(profiles_stack.depth === 0){
|
|
||||||
profiles_stack.openAthletes()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
profiles_stack.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
|
|
||||||
|
|
||||||
RemoteDataListView {
|
|
||||||
id: profileList
|
|
||||||
|
|
||||||
property int currentAthlete: -1
|
|
||||||
property string title: "profiles"
|
|
||||||
property string secondButt: "add"
|
|
||||||
|
|
||||||
signal opened()
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
profileList.loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
//anchors.fill: parent
|
|
||||||
//anchors.topMargin: topContainerItm.height * 0.1
|
|
||||||
|
|
||||||
loadData: function () {
|
|
||||||
status = 905
|
|
||||||
//listData = {}
|
|
||||||
var retData = speedBackend.getAthletes()
|
|
||||||
|
|
||||||
if(retData === undefined){
|
|
||||||
status = 500
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
listData = retData["allAthletes"]
|
|
||||||
currentAthlete = retData["activeAthlete"]
|
|
||||||
status = listData.lenght !== false ? 200:0
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: SwipeDelegate {
|
|
||||||
id: swipeDelegate
|
|
||||||
|
|
||||||
property bool active: profileList.currentAthlete === profileList.listData[index]["id"]
|
|
||||||
|
|
||||||
text: profileList.listData[index]["fullName"]
|
|
||||||
width: profileList.width - (swipeDelegate.x)
|
|
||||||
height: profileList.height / 5
|
|
||||||
|
|
||||||
font.pixelSize: profiles_stack.text_pixelSize
|
|
||||||
|
|
||||||
function remove() {
|
|
||||||
removeAnim.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
profiles_stack.openResults(profileList.listData[index]["userName"])
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Text {
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: swipeDelegate.width * 0.05
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: swipeDelegate.rightPadding
|
|
||||||
}
|
|
||||||
|
|
||||||
text: swipeDelegate.text
|
|
||||||
color: appTheme.style.textColor
|
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
|
|
||||||
font.pixelSize: swipeDelegate.height * 0.4
|
|
||||||
|
|
||||||
minimumPixelSize: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: pressed ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
|
|
||||||
ColorAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: control
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: 7
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
color: control.down ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width * 0.65
|
|
||||||
height: width
|
|
||||||
anchors.centerIn: parent
|
|
||||||
radius: control.checked ? width * 0.2:0
|
|
||||||
color: control.down ? "#17a81a" : "#21be2b"
|
|
||||||
opacity: control.checked ? 1:0
|
|
||||||
scale: control.checked ? 0.9:0
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on radius {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: "grey"
|
|
||||||
height: 1
|
|
||||||
width: parent.width * 0.9
|
|
||||||
visible: index > 0
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
top: parent.top
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
id: removeAnim
|
|
||||||
target: swipeDelegate
|
|
||||||
property: "height"
|
|
||||||
to: 0
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
onStopped: profileModel.model.remove(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
swipe.transition: Transition {
|
|
||||||
SmoothedAnimation { velocity: 3; easing.type: Easing.InOutCubic }
|
|
||||||
}
|
|
||||||
|
|
||||||
swipe.left: Row {
|
|
||||||
anchors.left: parent.left
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: deleteLabel
|
|
||||||
text: qsTr("Delete")
|
|
||||||
color: appTheme.style.textColor
|
|
||||||
verticalAlignment: Label.AlignVCenter
|
|
||||||
padding: 12
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
SwipeDelegate.onClicked: {
|
|
||||||
profileList.status = 905
|
|
||||||
if(speedBackend.deleteAthlete(profileList.listData[index]["userName"])){
|
|
||||||
profileList.loadData()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
profileList.status = 200
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----Option to add a profile-----*/
|
|
||||||
Component {
|
|
||||||
id: addProfileComp
|
|
||||||
Column {
|
|
||||||
property string title: "add profile"
|
|
||||||
property string secondButt: "ok"
|
|
||||||
property string newProfileName: ""
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: head_add
|
|
||||||
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if(speedBackend.createAthlete(userNameTf.text, fullNameTf.text)){
|
|
||||||
profiles_stack.get(profiles_stack.depth - 2 ).opened()
|
|
||||||
profiles_stack.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: fullNameTf
|
|
||||||
width: parent.width
|
|
||||||
placeholderText: "full name"
|
|
||||||
onTextChanged: {
|
|
||||||
parent.newProfileName = text
|
|
||||||
}
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: userNameTf
|
|
||||||
width: parent.width
|
|
||||||
placeholderText: "username"
|
|
||||||
onTextChanged: {
|
|
||||||
parent.newProfileName = text
|
|
||||||
}
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Result View ---
|
|
||||||
Component {
|
|
||||||
id: resultViewComp
|
|
||||||
RemoteDataListView {
|
|
||||||
id: resultView
|
|
||||||
|
|
||||||
property string userName
|
|
||||||
property string title: userName
|
|
||||||
property string secondButt: "none"
|
|
||||||
|
|
||||||
signal opened()
|
|
||||||
|
|
||||||
anchors.margins: 10
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
onOpened: {
|
|
||||||
loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData: function () {
|
|
||||||
status = 905
|
|
||||||
listData = {}
|
|
||||||
listData = speedBackend.getResults(userName)
|
|
||||||
status = listData.lenght !== false ? 200:0
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: SmoothItemDelegate {
|
|
||||||
id: resultDel
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: resultView.height / 4
|
|
||||||
|
|
||||||
backgroundRect.radius: 0
|
|
||||||
|
|
||||||
function getDateText(){
|
|
||||||
var date = new Date(listData[index]["timestamp"]*1000).toLocaleString(Qt.locale(), "dddd, dd.MMM HH:mm")
|
|
||||||
return date
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: "grey"
|
|
||||||
height: 1
|
|
||||||
width: parent.width * 0.9
|
|
||||||
visible: index > 0
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
top: parent.top
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: parent.width * 0.05
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: dateLa
|
|
||||||
|
|
||||||
height: parent.height / parent.children.length
|
|
||||||
|
|
||||||
font.pixelSize: height * 0.8
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
|
||||||
|
|
||||||
text: resultDel.getDateText()
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: resultLa
|
|
||||||
|
|
||||||
height: parent.height / parent.children.length
|
|
||||||
|
|
||||||
font.pixelSize: height * 0.8
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
|
||||||
|
|
||||||
text: qsTr("result: ") + (listData[index]["result"] / 1000).toFixed(3) + " s"
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: reactionTimeLa
|
|
||||||
|
|
||||||
height: parent.height / parent.children.length
|
|
||||||
|
|
||||||
font.pixelSize: height * 0.8
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
|
||||||
|
|
||||||
text: qsTr("reaction time: ") + listData[index]["reactionTime"].toFixed(0) + " ms"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----Custom animations-----*/
|
|
||||||
property int animationDuration: 200
|
|
||||||
pushEnter: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 0
|
|
||||||
to: 1
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
|
|
||||||
/*NumberAnimation {
|
|
||||||
property: "x"
|
|
||||||
from: width * 0.1
|
|
||||||
to: 0
|
|
||||||
duration: 300
|
|
||||||
}*/
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
property: "scale"
|
|
||||||
from: 1.1
|
|
||||||
to: 1
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pushExit: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 1
|
|
||||||
to: 0
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
|
|
||||||
/*NumberAnimation {
|
|
||||||
property: "x"
|
|
||||||
to: -width * 0.1
|
|
||||||
from: 0
|
|
||||||
duration: 300
|
|
||||||
}*/
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
property: "scale"
|
|
||||||
from: 1
|
|
||||||
to: 0.9
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
popExit: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 1
|
|
||||||
to: 0
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
|
|
||||||
/*NumberAnimation {
|
|
||||||
property: "x"
|
|
||||||
to: width * 0.1
|
|
||||||
from: 0
|
|
||||||
duration: 300
|
|
||||||
}*/
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
property: "scale"
|
|
||||||
from: 1
|
|
||||||
to: 1.1
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
popEnter: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "opacity"
|
|
||||||
from: 0
|
|
||||||
to: 1
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
|
|
||||||
/*NumberAnimation {
|
|
||||||
property: "x"
|
|
||||||
from: -width * 0.1
|
|
||||||
to: 0
|
|
||||||
duration: 300
|
|
||||||
}*/
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
property: "scale"
|
|
||||||
from: 0.9
|
|
||||||
to: 1
|
|
||||||
duration: profiles_stack.animationDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: topContainerItm
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
height: parent.height * 0.15
|
|
||||||
width: backgroundRect.width
|
|
||||||
|
|
||||||
RectangularGlow {
|
|
||||||
id: headerUnderlineEffect
|
|
||||||
glowRadius: 7
|
|
||||||
spread: 0.02
|
|
||||||
color: "black"
|
|
||||||
opacity: 0.18
|
|
||||||
anchors.fill: headlineUnderline
|
|
||||||
scale: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: headlineUnderline
|
|
||||||
height: 1
|
|
||||||
width: parent.width
|
|
||||||
color: "grey"
|
|
||||||
anchors {
|
|
||||||
bottom: parent.bottom
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Canvas {
|
|
||||||
|
|
||||||
id: headerBackground
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
property color color: appTheme.style.viewColor
|
|
||||||
|
|
||||||
onPaint: {
|
|
||||||
var ctx = getContext("2d");
|
|
||||||
|
|
||||||
var topMargin = backgroundRect.radius
|
|
||||||
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.fillStyle = headerBackground.color
|
|
||||||
ctx.moveTo(width, topMargin);
|
|
||||||
//
|
|
||||||
//ctx.lineTo(width, topMargin);
|
|
||||||
ctx.lineTo(width, height);
|
|
||||||
ctx.lineTo(0, height);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: head_text
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width * 0.8
|
|
||||||
height: parent.height * 0.8
|
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.pixelSize: headlineUnderline.width * 0.1
|
|
||||||
minimumPixelSize: 0
|
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
|
||||||
|
|
||||||
text: profiles_stack.currentItem.title
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FancyButton {
|
|
||||||
id: head_back
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: -height * 0.3
|
|
||||||
top:parent.top
|
|
||||||
topMargin: anchors.leftMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
height: topContainerItm.height * 0.8
|
|
||||||
width: height
|
|
||||||
|
|
||||||
glowOpacity: Math.pow( root.opacity, 100 )
|
|
||||||
|
|
||||||
backgroundColor: appTheme.style.buttonColor
|
|
||||||
|
|
||||||
image: appTheme.style.backIcon
|
|
||||||
|
|
||||||
onClicked: profiles_stack.depth > 1 ? profiles_stack.pop():root.close()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FancyButton {
|
|
||||||
id: head_add
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
rightMargin: -height * 0.3
|
|
||||||
top:parent.top
|
|
||||||
topMargin: anchors.rightMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
height: topContainerItm.height * 0.8
|
|
||||||
width: height
|
|
||||||
|
|
||||||
opacity: root.opacity < 1 ? root.opacity : ["ok", "add"].indexOf(profiles_stack.currentItem.secondButt) >= 0 ? 1:0
|
|
||||||
|
|
||||||
glowOpacity: opacity < 1 ? Math.pow( opacity, 100 ) : Math.pow( opacity, 100 )
|
|
||||||
|
|
||||||
backgroundColor: appTheme.style.buttonColor
|
|
||||||
|
|
||||||
image: appTheme.style.confirmIcon
|
|
||||||
imageScale: profiles_stack.currentItem.secondButt === "ok" ? 1:0
|
|
||||||
|
|
||||||
Label {
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
topMargin: parent.height/2 - height*0.55
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: parent.width/2 - width/2
|
|
||||||
}
|
|
||||||
opacity: profiles_stack.currentItem.secondButt === "add" ? 1:0
|
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
|
||||||
|
|
||||||
text: "+"
|
|
||||||
font.pixelSize: parent.height * 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
switch(profiles_stack.currentItem.secondButt){
|
|
||||||
case "add":
|
|
||||||
profiles_stack.push(addProfileComp)
|
|
||||||
break
|
|
||||||
case "ok":
|
|
||||||
//speedBackend.createAthlete(fullNameTf.text, userNameTf.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
enabled: root.opacity === 1
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
67
qml/ProfilesDialog/AddProfilePage.qml
Normal file
67
qml/ProfilesDialog/AddProfilePage.qml
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
||||||
|
Copyright (C) 2018 - 2019 Itsblue Development - Dorian Zeder
|
||||||
|
|
||||||
|
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, version 3 of the License.
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtMultimedia 5.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import com.itsblue.speedclimbingstopwatch 1.0
|
||||||
|
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
property string title: "add profile"
|
||||||
|
property string secondButt: "ok"
|
||||||
|
property string newProfileName: ""
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: head_add
|
||||||
|
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if(speedBackend.createAthlete(userNameTf.text, fullNameTf.text)){
|
||||||
|
profilesStack.get(profilesStack.depth - 2 ).opened()
|
||||||
|
profilesStack.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: fullNameTf
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: "full name"
|
||||||
|
onTextChanged: {
|
||||||
|
parent.newProfileName = text
|
||||||
|
}
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: userNameTf
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: "username"
|
||||||
|
onTextChanged: {
|
||||||
|
parent.newProfileName = text
|
||||||
|
}
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
237
qml/ProfilesDialog/ProfileListPage.qml
Normal file
237
qml/ProfilesDialog/ProfileListPage.qml
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
||||||
|
Copyright (C) 2018 - 2019 Itsblue Development - Dorian Zeder
|
||||||
|
|
||||||
|
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, version 3 of the License.
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtMultimedia 5.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import com.itsblue.speedclimbingstopwatch 1.0
|
||||||
|
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
|
||||||
|
RemoteDataListView {
|
||||||
|
id: profileList
|
||||||
|
|
||||||
|
property int currentAthlete: -1
|
||||||
|
property string title: "profiles"
|
||||||
|
property string secondButt: "add"
|
||||||
|
|
||||||
|
signal opened()
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
profileList.loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
//anchors.fill: parent
|
||||||
|
//anchors.topMargin: topContainerItm.height * 0.1
|
||||||
|
|
||||||
|
loadData: function () {
|
||||||
|
status = 905
|
||||||
|
//listData = {}
|
||||||
|
var retData = speedBackend.getAthletes()
|
||||||
|
|
||||||
|
if(retData === undefined){
|
||||||
|
status = 500
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
listData = retData["allAthletes"]
|
||||||
|
currentAthlete = retData["activeAthlete"]
|
||||||
|
status = listData.lenght !== false ? 200:0
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: SwipeDelegate {
|
||||||
|
id: swipeDelegate
|
||||||
|
|
||||||
|
property bool active: profileList.currentAthlete === profileList.listData[index]["id"]
|
||||||
|
|
||||||
|
text: profileList.listData[index]["fullName"]
|
||||||
|
width: profileList.width - (swipeDelegate.x)
|
||||||
|
height: profileList.height / 5
|
||||||
|
|
||||||
|
font.pixelSize: profilesStack.text_pixelSize
|
||||||
|
|
||||||
|
function remove() {
|
||||||
|
removeAnim.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
profilesStack.openResults(profileList.listData[index]["userName"])
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: swipeDelegate.width * 0.05
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: swipeDelegate.rightPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
text: swipeDelegate.text
|
||||||
|
color: appTheme.style.textColor
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
|
font.pixelSize: swipeDelegate.height * 0.4
|
||||||
|
|
||||||
|
minimumPixelSize: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: pressed ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 7
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
color: control.down ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width * 0.65
|
||||||
|
height: width
|
||||||
|
anchors.centerIn: parent
|
||||||
|
radius: control.checked ? width * 0.2:0
|
||||||
|
color: control.down ? "#17a81a" : "#21be2b"
|
||||||
|
opacity: control.checked ? 1:0
|
||||||
|
scale: control.checked ? 0.9:0
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on radius {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "grey"
|
||||||
|
height: 1
|
||||||
|
width: parent.width * 0.9
|
||||||
|
visible: index > 0
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: removeAnim
|
||||||
|
target: swipeDelegate
|
||||||
|
property: "height"
|
||||||
|
to: 0
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
onStopped: profileModel.model.remove(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
swipe.transition: Transition {
|
||||||
|
SmoothedAnimation { velocity: 3; easing.type: Easing.InOutCubic }
|
||||||
|
}
|
||||||
|
|
||||||
|
swipe.left: Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: deleteLabel
|
||||||
|
text: qsTr("Delete")
|
||||||
|
color: appTheme.style.textColor
|
||||||
|
verticalAlignment: Label.AlignVCenter
|
||||||
|
padding: 12
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
SwipeDelegate.onClicked: {
|
||||||
|
profileList.status = 905
|
||||||
|
if(speedBackend.deleteAthlete(profileList.listData[index]["userName"])){
|
||||||
|
profileList.loadData()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
profileList.status = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
263
qml/ProfilesDialog/ProfilesDialog.qml
Normal file
263
qml/ProfilesDialog/ProfilesDialog.qml
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
||||||
|
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
||||||
|
|
||||||
|
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, version 3 of the License.
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtMultimedia 5.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import com.itsblue.speedclimbingstopwatch 1.0
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
dim: false
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
enter: Transition {
|
||||||
|
NumberAnimation { properties: "opacity"; to: 1; duration: 300; easing.type: Easing.InOutQuad }
|
||||||
|
NumberAnimation { properties: "scale"; from: 0.9; to: 1; duration: 300; easing.type: Easing.InOutQuad }
|
||||||
|
}
|
||||||
|
|
||||||
|
exit: Transition {
|
||||||
|
NumberAnimation { properties: "opacity"; to: 0; duration: 300; easing.type: Easing.InOutQuad }
|
||||||
|
NumberAnimation { properties: "scale"; from: 1; to: 0.9; duration: 300; easing.type: Easing.InOutQuad }
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
RectangularGlow {
|
||||||
|
id: backgroundEffect
|
||||||
|
glowRadius: 7
|
||||||
|
spread: 0.02
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.18
|
||||||
|
anchors.fill: backgroundRect
|
||||||
|
cornerRadius: backgroundRect.radius
|
||||||
|
scale: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: backgroundRect
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: width * 0.1
|
||||||
|
color: appTheme.style.viewColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilesStack {
|
||||||
|
id: profilesStack
|
||||||
|
|
||||||
|
width: headlineUnderline.width
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: topContainerItm.bottom
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: ( parent.width - width ) / 2
|
||||||
|
topMargin: headlineUnderline.anchors.topMargin * 1.2
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: topContainerItm.height * 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {duration: 200}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
profilesStack.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
onOpened: {
|
||||||
|
profilesStack.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: topContainerItm
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
height: parent.height * 0.15
|
||||||
|
width: backgroundRect.width
|
||||||
|
|
||||||
|
RectangularGlow {
|
||||||
|
id: headerUnderlineEffect
|
||||||
|
glowRadius: 7
|
||||||
|
spread: 0.02
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.18
|
||||||
|
anchors.fill: headlineUnderline
|
||||||
|
scale: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: headlineUnderline
|
||||||
|
height: 1
|
||||||
|
width: parent.width
|
||||||
|
color: "grey"
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
|
||||||
|
id: headerBackground
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property color color: appTheme.style.viewColor
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
|
||||||
|
var topMargin = backgroundRect.radius
|
||||||
|
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillStyle = headerBackground.color
|
||||||
|
ctx.moveTo(width, topMargin);
|
||||||
|
//
|
||||||
|
//ctx.lineTo(width, topMargin);
|
||||||
|
ctx.lineTo(width, height);
|
||||||
|
ctx.lineTo(0, height);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: head_text
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
width: parent.width * 0.8
|
||||||
|
height: parent.height * 0.8
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
font.pixelSize: headlineUnderline.width * 0.1
|
||||||
|
minimumPixelSize: 0
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
color: appTheme.style.textColor
|
||||||
|
|
||||||
|
text: profilesStack.currentItem.title
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyButton {
|
||||||
|
id: head_back
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: -height * 0.3
|
||||||
|
top:parent.top
|
||||||
|
topMargin: anchors.leftMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
height: topContainerItm.height * 0.8
|
||||||
|
width: height
|
||||||
|
|
||||||
|
glowOpacity: Math.pow( root.opacity, 100 )
|
||||||
|
|
||||||
|
backgroundColor: appTheme.style.buttonColor
|
||||||
|
|
||||||
|
image: appTheme.style.backIcon
|
||||||
|
|
||||||
|
onClicked: profilesStack.depth > 1 ? profilesStack.pop():root.close()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyButton {
|
||||||
|
id: head_add
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: -height * 0.3
|
||||||
|
top:parent.top
|
||||||
|
topMargin: anchors.rightMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
height: topContainerItm.height * 0.8
|
||||||
|
width: height
|
||||||
|
|
||||||
|
opacity: root.opacity < 1 ? root.opacity : ["ok", "add"].indexOf(profilesStack.currentItem.secondButt) >= 0 ? 1:0
|
||||||
|
|
||||||
|
glowOpacity: opacity < 1 ? Math.pow( opacity, 100 ) : Math.pow( opacity, 100 )
|
||||||
|
|
||||||
|
backgroundColor: appTheme.style.buttonColor
|
||||||
|
|
||||||
|
image: appTheme.style.confirmIcon
|
||||||
|
imageScale: profilesStack.currentItem.secondButt === "ok" ? 1:0
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: parent.height/2 - height*0.55
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: parent.width/2 - width/2
|
||||||
|
}
|
||||||
|
opacity: profilesStack.currentItem.secondButt === "add" ? 1:0
|
||||||
|
|
||||||
|
color: appTheme.style.textColor
|
||||||
|
|
||||||
|
text: "+"
|
||||||
|
font.pixelSize: parent.height * 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
switch(profilesStack.currentItem.secondButt){
|
||||||
|
case "add":
|
||||||
|
profilesStack.push(addProfileComp)
|
||||||
|
break
|
||||||
|
case "ok":
|
||||||
|
//speedBackend.createAthlete(fullNameTf.text, userNameTf.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
enabled: root.opacity === 1
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
175
qml/ProfilesDialog/ProfilesStack.qml
Normal file
175
qml/ProfilesDialog/ProfilesStack.qml
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
||||||
|
Copyright (C) 2018 - 2019 Itsblue Development - Dorian Zeder
|
||||||
|
|
||||||
|
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, version 3 of the License.
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtMultimedia 5.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import com.itsblue.speedclimbingstopwatch 1.0
|
||||||
|
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: profilesStack
|
||||||
|
property int text_pixelSize: width * 0.08
|
||||||
|
//initialItem: profileListComp
|
||||||
|
|
||||||
|
onCurrentItemChanged: {
|
||||||
|
currentItem.opened()
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
if(profilesStack.depth === 0){
|
||||||
|
profilesStack.openAthletes()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
profilesStack.currentItem.opened()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAthletes() {
|
||||||
|
var athsComp = profileListComp.createObject(null, {})
|
||||||
|
profilesStack.push(athsComp)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openResults( userName ){
|
||||||
|
var resComp = resultViewComp.createObject(null, {"userName": userName})
|
||||||
|
profilesStack.push(resComp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----List of all profiles-----*/
|
||||||
|
Component {
|
||||||
|
id: profileListComp
|
||||||
|
|
||||||
|
ProfileListPage {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----Option to add a profile-----*/
|
||||||
|
Component {
|
||||||
|
id: addProfileComp
|
||||||
|
|
||||||
|
AddProfilePage {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Result View ---
|
||||||
|
Component {
|
||||||
|
id: resultViewComp
|
||||||
|
ResultListPage {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----Custom animations-----*/
|
||||||
|
property int animationDuration: 200
|
||||||
|
pushEnter: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
/*NumberAnimation {
|
||||||
|
property: "x"
|
||||||
|
from: width * 0.1
|
||||||
|
to: 0
|
||||||
|
duration: 300
|
||||||
|
}*/
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
property: "scale"
|
||||||
|
from: 1.1
|
||||||
|
to: 1
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushExit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
/*NumberAnimation {
|
||||||
|
property: "x"
|
||||||
|
to: -width * 0.1
|
||||||
|
from: 0
|
||||||
|
duration: 300
|
||||||
|
}*/
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
property: "scale"
|
||||||
|
from: 1
|
||||||
|
to: 0.9
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
popExit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
/*NumberAnimation {
|
||||||
|
property: "x"
|
||||||
|
to: width * 0.1
|
||||||
|
from: 0
|
||||||
|
duration: 300
|
||||||
|
}*/
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
property: "scale"
|
||||||
|
from: 1
|
||||||
|
to: 1.1
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popEnter: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
/*NumberAnimation {
|
||||||
|
property: "x"
|
||||||
|
from: -width * 0.1
|
||||||
|
to: 0
|
||||||
|
duration: 300
|
||||||
|
}*/
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
property: "scale"
|
||||||
|
from: 0.9
|
||||||
|
to: 1
|
||||||
|
duration: profilesStack.animationDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
qml/ProfilesDialog/ResultListPage.qml
Normal file
120
qml/ProfilesDialog/ResultListPage.qml
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
||||||
|
Copyright (C) 2018 - 2019 Itsblue Development - Dorian Zeder
|
||||||
|
|
||||||
|
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, version 3 of the License.
|
||||||
|
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtMultimedia 5.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import com.itsblue.speedclimbingstopwatch 1.0
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
RemoteDataListView {
|
||||||
|
id: resultView
|
||||||
|
|
||||||
|
property string userName
|
||||||
|
property string title: userName
|
||||||
|
property string secondButt: "none"
|
||||||
|
|
||||||
|
signal opened()
|
||||||
|
|
||||||
|
anchors.margins: 10
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData: function () {
|
||||||
|
status = 905
|
||||||
|
listData = {}
|
||||||
|
listData = speedBackend.getResults(userName)
|
||||||
|
status = listData.lenght !== false ? 200:0
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: SmoothItemDelegate {
|
||||||
|
id: resultDel
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: resultView.height / 4
|
||||||
|
|
||||||
|
backgroundRect.radius: 0
|
||||||
|
|
||||||
|
function getDateText(){
|
||||||
|
var date = new Date(listData[index]["timestamp"]*1000).toLocaleString(Qt.locale(), "dddd, dd.MMM HH:mm")
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "grey"
|
||||||
|
height: 1
|
||||||
|
width: parent.width * 0.9
|
||||||
|
visible: index > 0
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: parent.width * 0.05
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: dateLa
|
||||||
|
|
||||||
|
height: parent.height / parent.children.length
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.8
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
|
color: appTheme.style.textColor
|
||||||
|
|
||||||
|
text: resultDel.getDateText()
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: resultLa
|
||||||
|
|
||||||
|
height: parent.height / parent.children.length
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.8
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
|
color: appTheme.style.textColor
|
||||||
|
|
||||||
|
text: qsTr("result: ") + (listData[index]["result"] / 1000).toFixed(3) + " s"
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: reactionTimeLa
|
||||||
|
|
||||||
|
height: parent.height / parent.children.length
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.8
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
|
color: appTheme.style.textColor
|
||||||
|
|
||||||
|
text: qsTr("reaction time: ") + listData[index]["reactionTime"].toFixed(0) + " ms"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import QtQuick.Controls 2.2
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import "."
|
import "."
|
||||||
import "./components"
|
import "./components"
|
||||||
|
import "./ProfilesDialog"
|
||||||
//import QtQuick.Layouts 1.11
|
//import QtQuick.Layouts 1.11
|
||||||
|
|
||||||
import com.itsblue.speedclimbingstopwatch 2.0
|
import com.itsblue.speedclimbingstopwatch 2.0
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>main.qml</file>
|
<file>main.qml</file>
|
||||||
<file>ProfilesDialog.qml</file>
|
|
||||||
<file>SettingsDialog.qml</file>
|
<file>SettingsDialog.qml</file>
|
||||||
<file>components/ProgressCircle.qml</file>
|
<file>components/ProgressCircle.qml</file>
|
||||||
<file>components/ConnectionDelegate.qml</file>
|
<file>components/ConnectionDelegate.qml</file>
|
||||||
|
@ -16,5 +15,10 @@
|
||||||
<file>components/SmoothSliderDelegate.qml</file>
|
<file>components/SmoothSliderDelegate.qml</file>
|
||||||
<file>components/RemoteDataListView.qml</file>
|
<file>components/RemoteDataListView.qml</file>
|
||||||
<file>components/FancyBusyIndicator.qml</file>
|
<file>components/FancyBusyIndicator.qml</file>
|
||||||
|
<file>ProfilesDialog/ProfilesDialog.qml</file>
|
||||||
|
<file>ProfilesDialog/ProfilesStack.qml</file>
|
||||||
|
<file>ProfilesDialog/ProfileListPage.qml</file>
|
||||||
|
<file>ProfilesDialog/AddProfilePage.qml</file>
|
||||||
|
<file>ProfilesDialog/ResultListPage.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -459,7 +459,7 @@ QVariant ClimbingRace::getAthletes() {
|
||||||
|
|
||||||
QVariantMap tmpAthletes = reply["data"].toMap();
|
QVariantMap tmpAthletes = reply["data"].toMap();
|
||||||
|
|
||||||
qDebug() << tmpAthletes;
|
//qDebug() << tmpAthletes;
|
||||||
|
|
||||||
return tmpAthletes;
|
return tmpAthletes;
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue