From f9f6dd7f5de951a451f35cc63813efc12b3cd01f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sun, 6 Jun 2021 18:34:27 +0200 Subject: [PATCH] Many small and big improvements, see change log for details --- CHANGELOG.md | 13 +- blueROCK.pro | 4 +- resources/qml/Components/BlueRockBadge.qml | 32 + resources/qml/Components/PullRefresher.qml | 4 +- resources/qml/Components/ResultDelegate.qml | 484 ++++++++++++ resources/qml/Components/SelectorPopup.qml | 139 ++++ resources/qml/Components/SpeedFlowChart.qml | 376 +++++---- .../qml/Components/SpeedFlowChartLocker.qml | 101 +++ .../qml/Components/SpeedFlowChartPopup.qml | 116 +++ resources/qml/Pages/StartPage.qml | 33 +- resources/qml/Pages/WidgetPage.qml | 126 +--- resources/qml/Widgets/CalendarWidget.qml | 137 +--- resources/qml/Widgets/ProfileWidget.qml | 98 ++- resources/qml/Widgets/RankingWidget.qml | 16 +- resources/qml/Widgets/RegistrationWidget.qml | 29 +- resources/qml/Widgets/ResultWidget.qml | 714 +----------------- resources/qml/Widgets/StartlistWidget.qml | 24 +- resources/qml/main.qml | 13 +- resources/qml/qml.qrc | 5 + sources/main.cpp | 8 +- 20 files changed, 1281 insertions(+), 1191 deletions(-) create mode 100644 resources/qml/Components/BlueRockBadge.qml create mode 100644 resources/qml/Components/ResultDelegate.qml create mode 100644 resources/qml/Components/SelectorPopup.qml create mode 100644 resources/qml/Components/SpeedFlowChartLocker.qml create mode 100644 resources/qml/Components/SpeedFlowChartPopup.qml diff --git a/CHANGELOG.md b/CHANGELOG.md index 00b9c7a..f4bf88b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -# [0.03.1] - UR +# [0.05] - UR ### Changed - the boulder result rect doesn't have a background if there is no result now - the selected route is kept when changing cats +- the boulder result rect now shows the number of tries when there is not zone or top yet +- the speed flowchart now blocks the back key from closing the competition and instead closes itself +- redesigned start page +- added disclaimer regarding IFSC results +- some internal refactoring +- the calendar now scrolls less far down +- improoved layout in landscape mode +- some design changes in profile page and speed flowchart +- added second link for "further infos" in calendar # [0.03.0] - 2019-07-11 ### Added @@ -21,7 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - event website url is now present in the cat select dialog if available ### Changed -- competitons are clickable if there is a infosheet or event website url available event when they don't have any categories +- competitons are clickable if there is a infosheet or event website url available even when they don't have any categories # [0.01.6] - 2019-06-15 ### Fixed diff --git a/blueROCK.pro b/blueROCK.pro index b5292dd..120941a 100644 --- a/blueROCK.pro +++ b/blueROCK.pro @@ -50,9 +50,7 @@ DISTFILES += \ android-sources/gradle/wrapper/gradle-wrapper.properties \ android-sources/gradlew \ android-sources/gradlew.bat \ - android-sources/res/values/libs.xml \ - resources/shared/icons/bluerock/index.theme \ - $$files(resources/shared/icons/*.png, true) + android-sources/res/values/libs.xml android { QT += androidextras diff --git a/resources/qml/Components/BlueRockBadge.qml b/resources/qml/Components/BlueRockBadge.qml new file mode 100644 index 0000000..0fce454 --- /dev/null +++ b/resources/qml/Components/BlueRockBadge.qml @@ -0,0 +1,32 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.0 + +RowLayout { + id: control + + Image { + Layout.preferredHeight: parent.height + Layout.preferredWidth: height + Layout.alignment: Layout.Center + + fillMode: Image.PreserveAspectFit + mipmap: true + + source: "qrc:/icons/blueRockHold.png" + } + + Label { + Layout.preferredHeight: parent.height + Layout.fillWidth: true + Layout.alignment: Layout.Center + + fontSizeMode: Text.Fit + font.pixelSize: parent.height * 0.6 + font.bold: true + + verticalAlignment: Text.AlignVCenter + + text: "blueROCK" + } +} diff --git a/resources/qml/Components/PullRefresher.qml b/resources/qml/Components/PullRefresher.qml index 1e72f89..9004143 100644 --- a/resources/qml/Components/PullRefresher.qml +++ b/resources/qml/Components/PullRefresher.qml @@ -32,10 +32,10 @@ Item { property int postRefreshDelay: 1000 // delay after reload funcion has finished property int preRefreshDelay: 1000 // delay before reload funcion is called - property int refreshPosition: height * 1.2 // position of the item when refreshing + property int refreshPosition: height * 1.7 // position of the item when refreshing property int dragOutPosition: height * 1.8 // maximum drag out - property double dragRefreshPositionMultiplier: 0.5 // position of the item when starting to refresh + property double dragRefreshPositionMultiplier: 0.6 // position of the item when starting to refresh property color backgroundColor: "white" // color for the pre-defined background property color pullIndicatorColor: "black" // color for the pre-defined pull indicator diff --git a/resources/qml/Components/ResultDelegate.qml b/resources/qml/Components/ResultDelegate.qml new file mode 100644 index 0000000..4d77a0d --- /dev/null +++ b/resources/qml/Components/ResultDelegate.qml @@ -0,0 +1,484 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +ItemDelegate { + id: partDel + + property int ind: index + property var thisData: widgetData[ "participants" ][partDel.ind] + + width: control.width + height: app.landscape() ? 40:70 + + text: "" + + opacity: 0 + scale: 0.9 + + onThisDataChanged: { + fadeInPa.start() + } + + onClicked: { + app.openWidget({person:thisData["PerId"]}) + } + + ParallelAnimation { + id: fadeInPa + NumberAnimation { target: partDel; property: "opacity"; from: 0; to: 1.0; duration: 400 } + NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 } + } + + Rectangle { + id: partDelBackgroundRect + anchors.fill: parent + + width: partDel.width + + color: partDel.ind % 2 == 0 ? "white":"lightgrey" + + opacity: 0.2 + } + + GridLayout { + id: partDelCol + + anchors.fill: parent + anchors.margins: 5 + + columns: app.landscape() ? 2:1 + rows: app.landscape()? 1:2 + + columnSpacing: height * 0.2 + rowSpacing: 0 + + Row { + id: partDelFirstRow + + Layout.fillWidth: true + Layout.fillHeight: true + + Label { + height: parent.height + width: text === "" ? parent.width * 0.08:parent.width * 0.1 + + fontSizeMode: Text.Fit + font.bold: true + font.pixelSize: Math.abs( partDelSecondRow.height > 0 ? height * 0.6:height * 0.4 ) + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: partDel.thisData["result_rank"] === undefined ? "":partDel.thisData["result_rank"] + } + + Label { + height: parent.height + width: parent.width * 0.5 + + fontSizeMode: Text.Fit + font.bold: true + font.pixelSize: Math.abs( partDelSecondRow.height > 0 ? height * 0.6:height * 0.4 ) + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + + text: partDel.thisData["firstname"] + " " + partDel.thisData["lastname"] + (partDel.thisData["start_number"] !== undefined ? (" (" + partDel.thisData["start_number"] + ")"):"") + } + + Label { + height: parent.height + width: parent.width * 0.4 + + fontSizeMode: Text.Fit + font.bold: false + font.pixelSize: Math.abs( partDelSecondRow.height > 0 ? height * 0.4:height * 0.3 ) + minimumPixelSize: height * 0.3 < 1 ? 1:height * 0.3 + + elide: "ElideRight" + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: "(" + (widgetData[ "display_athlete" ] === "nation" ? partDel.thisData["nation"] : partDel.thisData["federation"]) + ")" + + onLinkActivated: { + Qt.openUrlExternally(link) + } + } + } + + Row { + id: partDelSecondRow + + Layout.preferredWidth: app.landscape() ? parent.width * 0.5 : parent.width + Layout.preferredHeight: app.landscape() ? parent.height : parent.height * 0.5 + + visible: multiResRow.active || multiGenResRow.active || resultLa.acitve + + Row { + id: multiResRow + + property bool active: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0 + + height: parent.height + width: active ? parent.width * 0.75:0 + + enabled: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0 + + Repeater { + id: boulderResRep + model: parseInt(widgetData[ "route_num_problems" ]) + + function getDataForIcon(index){ + // TODO: clean + var resultString = widgetData[ "participants" ][partDel.ind]["boulder"+(index+1)] + var numTrys = widgetData[ "participants" ][partDel.ind]["try"+(index+1)] + + var resultList = [] + + if (resultString !== undefined) { + resultString = resultString.replace("t", "") + resultString = resultString.replace("z", "") + resultString = resultString.replace("b", "") + resultList = resultString.split(" ") + + while (resultList.length < 2){ + resultList.unshift(0) + } + } + else { + resultList = [-1,-1] + } + + if (numTrys !== undefined) { + resultList.push(numTrys) + } + else { + resultList.push(-1) + } + + return resultList + } + + delegate: Item { + id: boulderResItm + + anchors.verticalCenter: parent.verticalCenter + + width: parent.width / ( boulderResRep.model ) + height: parent.height + + Canvas { + id: boulderResCv + + property var resultData: boulderResRep.getDataForIcon(index) + + onResultDataChanged: { + boulderResCv.requestPaint() + } + + anchors.centerIn: parent + + height: parent.height > parent.width ? parent.width * 0.9:parent.height * 0.9 + width: height + + onPaint: { + var width = 24//boulderResCv.width * 0.9 + var height = width + + var radius = width * 0.3 + + var offsetX = width * 0.05 + var offsetY = height * 0.05 + + //console.log("drawing result rect with width: " + width + " and height: " + height) + + var context = getContext("2d"); + + // clear all remainings from other routes + context.clearRect(0, 0, width, height); + + context.beginPath(); + + context.moveTo(0 + offsetX + radius, 0 + offsetY); + + // top line + context.lineTo(width - radius + offsetX, 0 + offsetY); + // top right corner + context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 0); + // right line + context.lineTo(width + offsetX, height - radius + offsetY); + // bottom right corner + context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI); + // bottom line + context.lineTo(0 + radius + offsetX, height + offsetY); + // bottom left corner + context.arc(radius + offsetY, height - radius + offsetY, radius, 0.5 * Math.PI, Math.PI); + // left line + context.lineTo(0 + offsetX, radius + offsetY); + // top left corner + context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI); + + // fill + if(resultData[0] !== -1) { + // if there is a result available -> draw background + context.fillStyle = "#b7b7b7"; + } + else { + context.fillStyle = "transparent"; + } + + context.fill(); + + // outline + context.lineWidth = 1; + context.strokeStyle = '#424242'; + context.stroke(); + + if(resultData[1] > 0){ + + // the first triangle + context.beginPath(); + + // top right corner + context.arc(width-radius + offsetX, radius + offsetY, radius, 1.75 * Math.PI, 0); + + // right line + context.lineTo(width + offsetX, height - radius + offsetY); + + // bottom right corner + context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI); + + // bottom line + context.lineTo(0 + radius + offsetX, height + offsetY); + // bottom left corner + context.arc(radius + offsetX, height - radius + offsetY, radius, 0.5 * Math.PI, 0.75 * Math.PI); + context.closePath(); + + context.fillStyle = "#44ed38"; + context.fill(); + + // outline + context.lineWidth = 1; + context.strokeStyle = '#424242'; + context.stroke(); + + + if(resultData[0] > 0){ + // the second triangle + context.beginPath(); + // bottom left corner + context.arc(radius + offsetX, height - radius + offsetY, radius, 0.75 * Math.PI, 1 * Math.PI); + // left line + context.lineTo(0 + offsetX, radius + offsetY); + // top left corner + context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI); + // top line + context.lineTo(width - radius + offsetX, 0 + offsetY); + // top right corner + context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 1.75 * Math.PI); + + context.closePath(); + + context.fillStyle = "#44ed38"; + context.fill(); + + // outline + context.lineWidth = 1; + context.strokeStyle = '#424242'; + context.stroke(); + } + } + + } + + Label { + id: boulderResTrysLa + + anchors.centerIn: parent + + height: parent.height / 2 + width: parent.width / 2 + + visible: !boulderResZoneLa.visible && boulderResCv.resultData[2] > 0 + + fontSizeMode: Text.Fit + font.pixelSize: height + minimumPixelSize: 1 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: boulderResCv.resultData[2] + + } + + Label { + id: boulderResZoneLa + + anchors { + right: parent.right + bottom: parent.bottom + margins: boulderResCv.height * 0.05 + } + + height: parent.height / 2 + width: parent.width / 2 + + visible: parseInt(text) > 0 + + fontSizeMode: Text.Fit + font.pixelSize: height + minimumPixelSize: 1 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: boulderResCv.resultData[1] + } + + Label { + id: boulderResTopLa + + anchors { + left: parent.left + top: parent.top + margins: boulderResCv.height * 0.05 + } + + height: parent.height / 2 + width: parent.width / 2 + + visible: parseInt(text) > 0 + + fontSizeMode: Text.Fit + font.pixelSize: height + minimumPixelSize: 1 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: boulderResCv.resultData[0] + } + } + + } + + } + + } + + Row { + id: multiGenResRow + + property bool active: ((parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)) ? true:false + + height: parent.height + width: active ? parent.width - resultLa.width:0 + + enabled: ((parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)) ? true:false + + Repeater { + id: generalResRep + + property var routes: getRoutes() + model: routes.length + + function getRoutes() { + + var obj = widgetData["route_names"] + var routes = [] + + for(var prop in obj) { + // go through the whole array and search for data keys + if (obj.hasOwnProperty(prop) && prop > -1) { + routes.push([prop, obj[prop]]) + //console.log("found " + obj[prop] + " at index " + prop) + } + } + + routes.sort(function(a, b) { + return a[0] - b[0]; + }); + + return routes + } + + delegate: Item { + id: boulderGenResItm + + anchors.verticalCenter: parent.verticalCenter + + width: parent.width / ( generalResRep.model ) + height: parent.height + + visible: multiGenResRow.active + + Rectangle { + + anchors { + left: parent.left + } + + width: 1 + height: parent.height + + visible: index === 0 + + color: "grey" + } + + Rectangle { + anchors { + right: parent.right + } + + width: 1 + height: parent.height + + color: "grey" + } + + Label { + id: boulderGenResLa + anchors.centerIn: parent + + height: parent.height + width: parent.width * 0.9 + + fontSizeMode: Text.Fit + font.pixelSize: Math.abs( height * 0.6 ) + minimumPixelSize: 1 + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + text: widgetData[ "participants" ][partDel.ind]["result"+(generalResRep.routes[index][0])] === undefined ? "":widgetData[ "participants" ][partDel.ind]["result"+(generalResRep.routes[index][0])] + } + + } + + } + } + + Label { + id: resultLa + + property bool acitve: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 + + width: enabled ? parent.width * 0.25:0 + height: enabled ? parent.height:0 + + enabled: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + + fontSizeMode: Text.Fit + font.pixelSize: Math.abs( height * 0.6 ) + minimumPixelSize: 1 + + text: widgetData[ "participants" ][partDel.ind]["result"] === undefined ? "":widgetData[ "participants" ][partDel.ind]["result"] + } + } + } + } diff --git a/resources/qml/Components/SelectorPopup.qml b/resources/qml/Components/SelectorPopup.qml new file mode 100644 index 0000000..f9dcd4d --- /dev/null +++ b/resources/qml/Components/SelectorPopup.qml @@ -0,0 +1,139 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.3 + +Dialog { + id: control + + property var dataObj + property string subTitle: "" + property int implicitY: parent.height - implicitHeight + + signal selectionFinished(int index, var data) + + parent: Overlay.overlay + + x: 0 + y: parent.height - implicitHeight + + opacity: 1 + + width: parent.width + implicitWidth: width + contentHeight: Math.min(parent.height * 0.7, implicitContentHeight) + implicitHeight: contentHeight + topPadding + bottomPadding + header.height + padding: 30 + + modal: true + focus: true + + title: "" + + header: Column { + id: selectorPuHeaderCol + + width: control.width + height: headerSubLa.text !== "" && headerLa.text !== "" ? 73 : 40 + + Label { + id: headerLa + + visible: control.title + + width: selectorPuHeaderCol.width + + elide: "ElideRight" + padding: control.padding + bottomPadding: 0 + font.bold: true + font.pixelSize: 16 + + text: control.title + + onLinkActivated: { + console.log("Opening " + link) + Qt.openUrlExternally(link) + } + } + + Label { + id: headerSubLa + + visible: control.subTitle + + width: selectorPuHeaderCol.width + + elide: "ElideRight" + padding: control.padding + topPadding: 5 + bottomPadding: 0 + font.bold: true + font.pixelSize: 16 + + text: control.subTitle + + onLinkActivated: { + console.log("Opening " + link) + Qt.openUrlExternally(link) + } + + } + } + + background: Item { + Rectangle { + id: backgroundRect + anchors { + fill: parent + bottomMargin: -radius + } + + radius: control.leftPadding + + color: control.Material.dialogColor + } + } + + function appear(dataObj, title, subTitle) { + + if(dataObj.length > 0){ + control.dataObj = dataObj + } + else { + control.dataObj = undefined + } + + control.title = title + control.subTitle = subTitle === undefined ? "":subTitle + control.open() + } + + enter: Transition { + NumberAnimation { + property: "opacity"; + from: 0 + to: 1.0 + easing.type: Easing.Linear + } + + NumberAnimation { + property: "y" + from: control.parent.height - control.implicitHeight * 0.7 + to: control.parent.height - control.implicitHeight + } + } + + exit: Transition { + NumberAnimation { + property: "opacity"; + from: 1 + to: 0 + } + + NumberAnimation { + property: "y" + from: control.parent.height - control.implicitHeight + to: control.parent.height - control.implicitHeight * 0.7 + } + } +} diff --git a/resources/qml/Components/SpeedFlowChart.qml b/resources/qml/Components/SpeedFlowChart.qml index 499fd54..520852b 100644 --- a/resources/qml/Components/SpeedFlowChart.qml +++ b/resources/qml/Components/SpeedFlowChart.qml @@ -20,32 +20,24 @@ import QtQuick 2.10 import QtQuick.Controls 2.4 import QtQuick.Layouts 1.3 import QtQuick.Controls.Material 2.1 +import QtGraphicalEffects 1.0 -ListView { +Item { id: control property var flowchartData property var allFlowchartData property int rounds: 0 - property int tileSize: app.height / 8 * 0.8 property int refreshes: 0 property int roundRefreshes: 1 - anchors.fill: parent - anchors.margins: 10 - - spacing: app.width * 0.1 - - orientation: ListView.LeftToRight - boundsBehavior: ListView.StopAtBounds + property int roundCount: 0 onFlowchartDataChanged: { prepareData() } - model: 0 - function prepareData() { if(!control.enabled || control.flowchartData === undefined || control.flowchartData['route_names'] === undefined) @@ -223,189 +215,273 @@ ListView { } control.allFlowchartData = allData - control.model = (parseInt(Object.keys(control.flowchartData['route_names']).length > 2 ? control.flowchartData['route_names']["2"].includes("8") ? 2:1 : 0) + 2) + control.roundCount = (parseInt(Object.keys(control.flowchartData['route_names']).length > 2 ? control.flowchartData['route_names']["2"].includes("8") ? 2:1 : 0) + 2) //console.log(JSON.stringify(allData)) } - delegate: Column { - id: roundCol + ListView { + id: roundListView - property int thisIndex: index - property int thisRound: thisRoundIsValid ? control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-2]:-1 - property bool thisRoundIsValid: control.allFlowchartData !== undefined && control.allFlowchartData[roundCol.thisIndex] !== undefined && control.allFlowchartData[roundCol.thisIndex].length > 2 - property bool thisIsLastRound: thisIndex === control.model - 1 + property int columnWidth: height * 0.3 + property int columnHeight: height - width: app.width * 0.5 - height: control.height - - //spacing: app.width * 0.1 - - Label { - id: roundNameLa - width: parent.width - height: control.height * 0.05 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - fontSizeMode: Text.Fit - font.pixelSize: height * 0.6 - minimumPixelSize: 1 - font.bold: true - text: roundCol.thisRoundIsValid && control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-1] !== undefined ? control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-1] : "-" + anchors { + top: parent.top + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter } - Repeater { - id: rectRep - model: Math.max( Math.pow(2, control.model-1) * Math.pow(0.5, (index)), 2) - delegate: Item { - id: matchItm + width: Math.min((columnWidth + spacing) * model, control.width) - property bool lowerPart: (index%2 > 0) + spacing: app.height * 0.05 - property var matchData: roundCol.thisRoundIsValid ? control.allFlowchartData[ thisIsSmallFinal ? roundCol.thisIndex+1 : roundCol.thisIndex][ thisIsSmallFinal ? 0:matchItm.thisIndex]:undefined + orientation: ListView.LeftToRight + boundsBehavior: ListView.StopAtBounds - property int thisIndex: index - property int thisRound: parseInt(roundCol.thisRound) - (thisIsSmallFinal ? 1:0) - property var thisMatchData: thisMatchDataIsValid ? matchData:[] + model: control.roundCount - property bool thisMatchDataIsValid: (matchData !== undefined && matchData !== null && typeof matchData === "object" && matchData.length > 0) - property bool thisMatchIsOver: thisMatchDataIsValid && thisMatchData[0]['result_rank'+thisRound] !== undefined && thisMatchData[1]['result_rank'+thisRound] !== undefined + delegate: Item { + id: roundItem - property bool thisIsFinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 2 - property bool thisIsSmallFinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 1 + property int thisIndex: index + property int thisRound: thisRoundIsValid ? control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-2]:-1 + property bool thisRoundIsValid: control.allFlowchartData !== undefined + && control.allFlowchartData[roundItem.thisIndex] !== undefined + && control.allFlowchartData[roundItem.thisIndex].length > 2 - property int winnerIndex: thisMatchIsOver ? (parseInt(thisMatchData[0]['result_rank'+thisRound]) < parseInt(thisMatchData[1]['result_rank'+thisRound]) ? 0:1) : -1 + property bool thisIsLastRound: thisIndex === control.roundCount - 1 + property bool thisIsSemiFinal: thisIndex === control.roundCount - 2 && rectRep.model === 2 - height: !roundCol.thisIsLastRound ? (roundCol.height - roundNameLa.height) / rectRep.model - roundCol.spacing : (thisIsFinal ? (roundCol.height - roundNameLa.height) * 0.5 + control.tileSize * 0.5 : (roundCol.height - roundNameLa.height) - (roundCol.height - roundNameLa.height) * 0.5 + control.tileSize * 0.5 ) - width: parent.width + property int tileSize: (roundItem.height / 8 - roundNameLa.height) * 1.45 - onMatchDataChanged: { - fadeInPa.start() - } + width: roundListView.columnWidth + height: roundListView.columnHeight - ParallelAnimation { - id: fadeInPa - NumberAnimation { target: matchItm; property: "opacity"; from: 0; to: 1.0; duration: 400 } - NumberAnimation { target: matchItm; property: "scale"; from: 0.8; to: 1.0; duration: 400 } - } + Column { + id: roundCol - Canvas { - id: lineCanvas - width: app.width; height: app.height - contextType: "2d" + anchors.fill: parent - visible: !roundCol.thisIsLastRound - - property int targetX: matchItm.width + control.spacing - property int targetY: matchItm.lowerPart ? 0:matchItm.height - - Path { - id: myPath - startX: matchRect.x + matchRect.width * matchRect.scale ; startY: matchRect.y + matchRect.height * 0.5 - - PathCurve { x: lineCanvas.targetX ; y: lineCanvas.targetY } - } - - onPaint: { - context.strokeStyle = Qt.rgba(.4,.6,.8); - context.path = myPath; - context.stroke(); - } - } - - Rectangle { - id: matchRect - - anchors { - centerIn: !matchItm.thisIsFinal ? parent:undefined - bottom: matchItm.thisIsFinal ? parent.bottom:undefined - } - - //anchors.verticalCenterOffset: matchItm.lowerPart ? 10:10 + Label { + id: roundNameLa width: parent.width - height: control.tileSize - //scale: 0.9 - color: "white" - border.color: "lightgrey" - border.width: 1 - radius: height * 0.2 - Material.elevation: 10 + height: control.height * 0.05 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + fontSizeMode: Text.Fit + font.pixelSize: height * 0.6 + minimumPixelSize: 1 + font.bold: true + text: roundItem.thisRoundIsValid + && control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] !== undefined ? + control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] : + "-" + } - Grid { - columns: app.landscape() ? 2:0 - rows: app.landscape() ? 0:2 + Repeater { + id: rectRep + model: Math.max( Math.pow(2, control.roundCount-1) * Math.pow(0.5, (roundItem.thisIndex)), 2) + delegate: Item { + id: matchItm - spacing: app.landscape() ? height * 0.4:0 + property bool lowerPart: (index%2 > 0) - anchors.fill: parent - anchors.margins: matchRect.radius * 0.5 + property var matchData: roundItem.thisRoundIsValid ? + control.allFlowchartData[ + thisIsSmallFinal ? + roundItem.thisIndex+1 : + roundItem.thisIndex + ][ thisIsSmallFinal ? 0:matchItm.thisIndex]: + undefined - Repeater { - // for the two athletes - model: 2 + property int thisIndex: index + property int thisRound: parseInt(roundItem.thisRound) - (thisIsSmallFinal ? 1:0) + property var thisMatchData: thisMatchDataIsValid ? matchData:[] - delegate: RowLayout { + property bool thisMatchDataIsValid: (matchData !== undefined && matchData !== null && typeof matchData === "object" && matchData.length > 0) + property bool thisMatchIsOver: thisMatchDataIsValid && thisMatchData[0]['result_rank'+thisRound] !== undefined && thisMatchData[1]['result_rank'+thisRound] !== undefined - height: app.landscape() ? parent.height:parent.height / 2 - parent.spacing - width: app.landscape() ? parent.width / 2 - parent.spacing : parent.width + property bool thisIsFinal: roundItem.thisIsLastRound && thisIndex === rectRep.model - 2 + property bool thisIsSmallFinal: roundItem.thisIsLastRound && thisIndex === rectRep.model - 1 - spacing: app.landscape() ? height * 0.1:height * 0.1 + property int winnerIndex: thisMatchIsOver ? (parseInt(thisMatchData[0]['result_rank'+thisRound]) < parseInt(thisMatchData[1]['result_rank'+thisRound]) ? 0:1) : -1 - Label { - Layout.preferredHeight: parent.height + height: !roundItem.thisIsLastRound ? + (roundItem.height - roundNameLa.height) / rectRep.model - roundCol.spacing : + (thisIsFinal ? + (roundItem.height - roundNameLa.height) * 0.5 + roundItem.tileSize * 0.5 : + (roundItem.height - roundNameLa.height) - (roundItem.height - roundNameLa.height) * 0.5 + roundItem.tileSize * 0.5 + ) - height: parent.height + width: roundItem.width - verticalAlignment: Text.AlignVCenter - font.pixelSize: height * 0.7 - fontSizeMode: Text.Fit - minimumPixelSize: 1 + onMatchDataChanged: { + fadeInPa.start() + } - Layout.alignment: Layout.Left + ParallelAnimation { + id: fadeInPa + NumberAnimation { target: matchItm; property: "opacity"; from: 0; to: 1.0; duration: 400 } + NumberAnimation { target: matchItm; property: "scale"; from: 0.8; to: 1.0; duration: 400 } + } - text: matchItm.thisMatchData[index] !== undefined ? (parseInt(matchItm.thisMatchData[index]['result_rank0']) < 10 ?matchItm.thisMatchData[index]['result_rank0'] + " ":matchItm.thisMatchData[index]['result_rank0']): "" + Canvas { + id: lineCanvas + width: app.width; height: app.height + contextType: "2d" + + visible: !roundItem.thisIsLastRound + + property int targetYFactor: matchItm.lowerPart ? -1:1 + + Path { + id: firstPartPath + startX: matchItm.width + startY: matchItm.height * 0.5 + + PathQuad { + id: firstPartPathQuad + relativeX: roundListView.spacing * 0.5 + relativeY: matchItm.height * 0.25 * lineCanvas.targetYFactor + relativeControlX: relativeX + relativeControlY: 0 } - Label { - Layout.preferredHeight: parent.height - - height: parent.height - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - //horizontalAlignment: app.landscape() ? Text.AlignLeft : Text.AlignLeft - font.pixelSize: app.landscape() ? height * 0.4 : height * 0.6 - minimumPixelSize: (app.landscape() ? height * 0.35 : height * 0.5) < 1 ? 1 : (app.landscape() ? height * 0.35 : height * 0.5) - fontSizeMode: Text.Fit - font.bold: matchItm.winnerIndex === index - elide: "ElideRight" - - Layout.fillWidth: true - - color: matchItm.winnerIndex === index ? "green":"black" - - text: matchItm.thisMatchData[index] !== undefined ? matchItm.thisMatchData[index]['firstname'] + " " + matchItm.thisMatchData[index]['lastname'] :"-" + PathQuad { + relativeX: roundListView.spacing * 0.5 + relativeY: matchItm.height * 0.25 * lineCanvas.targetYFactor + relativeControlX: 0 + relativeControlY: relativeY } + } - Label { - Layout.preferredHeight: parent.height + onPaint: { + context.strokeStyle = "lightgrey" + context.lineWidth = matchRect.height * 0.05 + context.path = firstPartPath; + context.stroke(); + } + } - height: parent.height + RectangularGlow { + id: effect + anchors.fill: matchRect + glowRadius: 0 + spread: 0 + opacity: 0.3 + color: "black" + cornerRadius: matchRect.radius + } - verticalAlignment: Text.AlignVCenter - font.pixelSize: app.landscape() ? height * 0.35 : height * 0.5 + Rectangle { + id: matchRect - Layout.alignment: Layout.Right + anchors { + centerIn: !matchItm.thisIsFinal ? parent:undefined + bottom: matchItm.thisIsFinal ? parent.bottom:undefined + } - text: matchItm.thisMatchData[index] !== undefined && matchItm.thisMatchData[index]['result'+matchItm.thisRound] !== undefined ? - ( parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]) ? - (parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]).toFixed(2)) - : matchItm.thisMatchData[index]['result'+matchItm.thisRound] ) - : "-" + //anchors.verticalCenterOffset: matchItm.lowerPart ? 10:10 + width: parent.width + height: roundItem.tileSize + //scale: 0.9 + color: "white" + border.color: "lightgrey" + border.width: 0 + radius: height * 0.2 + + Column { + spacing: 0 + + anchors.fill: parent + anchors.margins: matchRect.radius * 0.5 + + Repeater { + // for the two athletes + model: 2 + + delegate: RowLayout { + + height: parent.height / 2 - parent.spacing + width: parent.width + + spacing: height * 0.1 + + Label { + Layout.preferredHeight: parent.height + Layout.preferredWidth: parent.width * 0.15 + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.pixelSize: height * 0.7 + fontSizeMode: Text.Fit + minimumPixelSize: 1 + + text: matchItm.thisMatchData[index] !== undefined ? + ( + parseInt(matchItm.thisMatchData[index]['result_rank0']) < 10 ? + matchItm.thisMatchData[index]['result_rank0'] + " ": + matchItm.thisMatchData[index]['result_rank0'] + ): + "" + } + + Label { + Layout.preferredHeight: parent.height + Layout.fillWidth: true + + height: parent.height + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.pixelSize: height * 0.5 + font.bold: matchItm.winnerIndex === index + elide: "ElideRight" + + color: matchItm.winnerIndex === index ? "green":"black" + + text: matchItm.thisMatchData[index] !== undefined ? matchItm.thisMatchData[index]['firstname'] + " " + matchItm.thisMatchData[index]['lastname'] :"-" + } + + Label { + Layout.preferredHeight: parent.height + + height: parent.height + + verticalAlignment: Text.AlignVCenter + font.pixelSize: height * 0.5 + + text: matchItm.thisMatchData[index] !== undefined && matchItm.thisMatchData[index]['result'+matchItm.thisRound] !== undefined ? + ( parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]) ? + (parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]).toFixed(2)) + : matchItm.thisMatchData[index]['result'+matchItm.thisRound] ) + : "-" + } + } } } } } } + + } + + Loader { + id: blueRockBadgeLoader + + x: (parent.width - width) / 2 + y: (parent.height - roundNameLa.height - height) / 2 + roundNameLa.height + + sourceComponent: roundItem.thisIsSemiFinal ? blueRockBadgeComponent:undefined + + Component { + id: blueRockBadgeComponent + + BlueRockBadge { + width: roundItem.width + height: width * 0.25 + } + } } } } diff --git a/resources/qml/Components/SpeedFlowChartLocker.qml b/resources/qml/Components/SpeedFlowChartLocker.qml new file mode 100644 index 0000000..b72ea2d --- /dev/null +++ b/resources/qml/Components/SpeedFlowChartLocker.qml @@ -0,0 +1,101 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtPurchasing 1.12 + +Rectangle { + id: speedFlowChartLockedOverlay + + anchors.fill: parent + anchors.margins: -20 + + color: "white" + + Connections { + target: speedFlowChartProduct + + function onPurchaseFailed() { + purchaseBt.text = qsTr("Purchase failed") + purchaseBt.enabled = false + buttonTextResetTimer.start() + } + } + + Timer { + id: buttonTextResetTimer + interval: 2000 + running: false + repeat: false + onTriggered: { + purchaseBt.text = (speedFlowChartProduct.status === Product.Registered + ? "Buy now for " + speedFlowChartProduct.price + : qsTr("this item is currently unavailable")) + purchaseBt.enabled = true + } + } + + ColumnLayout { + id: lockedLayout + + anchors { + fill: parent + topMargin: parent.height * 0.05 + bottomMargin: parent.height * 0.1 + rightMargin: parent.width * 0.1 + 20 + leftMargin: parent.width * 0.1 + 20 + } + + //spacing: parent.height * 0.05 + + Image { + id: name + + Layout.alignment: Layout.Center + Layout.preferredHeight: height + Layout.preferredWidth: width + + width: lockedLayout.height * 0.1 + height: width + + mipmap: true + + source: "qrc:/icons/lock.png" + } + + Text { + Layout.fillWidth: true + + height: parent.height * 0.05 + + text: qsTr("This is a premium feature.") + + font.bold: true + font.pixelSize: parent.height * 0.05 + fontSizeMode: Text.Fit + + minimumPixelSize: 1 + + horizontalAlignment: Text.AlignHCenter + + } + + SwipeGallery { + Layout.fillHeight: true + Layout.fillWidth: true + + images: ["qrc:/screenshots/SpeedFlowchartDemo/1.png","qrc:/screenshots/SpeedFlowchartDemo/2.png","qrc:/screenshots/SpeedFlowchartDemo/3.png"] + } + + Button { + id: purchaseBt + Layout.alignment: Layout.Center + enabled: speedFlowChartProduct.status === Product.Registered + text: speedFlowChartProduct.status === Product.Registered + ? "Buy now for " + speedFlowChartProduct.price + : qsTr("this item is currently unavailable") + icon.name: "buy" + onClicked: speedFlowChartProduct.purchase() + } + + } +} diff --git a/resources/qml/Components/SpeedFlowChartPopup.qml b/resources/qml/Components/SpeedFlowChartPopup.qml new file mode 100644 index 0000000..fcd8297 --- /dev/null +++ b/resources/qml/Components/SpeedFlowChartPopup.qml @@ -0,0 +1,116 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Material 2.15 + +Rectangle { + id: control + + property var flowchartData: ({}) + + // always unlock in debug mode + property bool unlocked: appSettings.read("speedBackendPurchase") === "1" || QT_DEBUG + + state: "hidden" + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.right + } + + width: parent.width + height: parent.height + + color: Material.background + + onStateChanged: { + if(state === "visible" && unlocked) { + speedFlowChartLoader.sourceComponent = speedFlowChartComponent + } + else if(state === "visible" && !unlocked) { + speedFlowChartLoader.sourceComponent = speedFlowChartLockerComponent + } + else { + speedFlowChartLoader.sourceComponent = undefined + } + } + + function toggle() { + if(control.state === "hidden"){ + control.state = "visible" + } + else { + control.state = "hidden" + } + } + + function isVisible() { + return control.state === "visible" + } + + Connections { + target: speedFlowChartProduct + function onPurchaseRestored() { + control.unlocked = appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked" + } + + function onPurchaseSucceeded() { + control.unlocked = appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked" + } + } + + Loader { + id: speedFlowChartLoader + + anchors.fill: parent + + Component { + id: speedFlowChartComponent + SpeedFlowChart { + anchors.fill: parent + anchors.margins: 10 + + flowchartData: control.flowchartData + } + } + + Component { + id: speedFlowChartLockerComponent + + SpeedFlowChartLocker { + + } + + } + } + + states: [ + State { + name: "hidden" + PropertyChanges { + target: control + opacity: 0 + anchors.leftMargin: 0 + } + }, + + State { + name: "visible" + PropertyChanges { + target: control + opacity: 1 + anchors.leftMargin: -parent.width + } + } + ] + + transitions: [ + Transition { + NumberAnimation { + properties: "opacity,scale,anchors.leftMargin" + duration: 200 + easing.type: Easing.InOutQuad + } + } + ] +} diff --git a/resources/qml/Pages/StartPage.qml b/resources/qml/Pages/StartPage.qml index faa3c70..0d3ce8e 100644 --- a/resources/qml/Pages/StartPage.qml +++ b/resources/qml/Pages/StartPage.qml @@ -29,9 +29,8 @@ Page { signal headerComponentChanged() - RowLayout { - id: headerLayout - + BlueRockBadge { + id: headerBadge anchors { horizontalCenter: parent.horizontalCenter top: parent.top @@ -40,28 +39,6 @@ Page { height: menuGr.buttonSize * 0.3 spacing: anchors.topMargin * 0.5 - - Image { - Layout.preferredHeight: parent.height - Layout.preferredWidth: height - - fillMode: Image.PreserveAspectFit - mipmap: true - - source: "qrc:/icons/blueRockHold.png" - } - - Label { - Layout.preferredHeight: parent.height - Layout.fillWidth: true - - font.pixelSize: parent.height * 0.6 - font.bold: true - - verticalAlignment: Text.AlignVCenter - - text: "blueROCK" - } } Grid { @@ -125,7 +102,7 @@ Page { anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom - bottomMargin: headerLayout.anchors.topMargin + bottomMargin: headerBadge.anchors.topMargin } width: parent.width * 0.9 @@ -161,6 +138,10 @@ Page { contentItem: Label { wrapMode: Text.Wrap + + width: ifscDisclaimerDialog.width * 0.8 + height: implicitHeight + text: "Unfortunately, the IFSC has restricted the access to their data and is not willing to share results with blueROCK anymore. " + "Because of this, blueROCK is no longer able to access and display IFSC results.

" + diff --git a/resources/qml/Pages/WidgetPage.qml b/resources/qml/Pages/WidgetPage.qml index 5e56bb9..5f01e5b 100644 --- a/resources/qml/Pages/WidgetPage.qml +++ b/resources/qml/Pages/WidgetPage.qml @@ -245,107 +245,16 @@ Page { } - Dialog { + SelectorPopup { id: selectorPu - property var dataObj - property string subTitle: "" - - signal selectionFinished(int index, var data) - - x: 0 //root.width / 2 - width / 2 - y: root.height - selectorPu.height * 0.7//root.height - height //root.height / 2 - height / 2 - - opacity: 0 - - width: root.width - height: selectorLv.implicitHeight - - modal: true - focus: true - - title: "" - - header: Column { - id: selectorPuHeaderCol - - width: parent.width - - Label { - id: headerLa - - visible: selectorPu.title - - width: parent.width - - elide: "ElideRight" - padding: 24 - bottomPadding: 0 - font.bold: true - font.pixelSize: 16 - background: Rectangle { - radius: 2 - color: selectorPu.Material.dialogColor - clip: true - } - - text: selectorPu.title - - onLinkActivated: { - Qt.openUrlExternally(link) - } - - } - Label { - id: headerSubLa - - visible: selectorPu.subTitle - - width: parent.width - - elide: "ElideRight" - padding: 24 - topPadding: 5 - bottomPadding: 0 - font.bold: true - font.pixelSize: 16 - background: Rectangle { - radius: 2 - color: selectorPu.Material.dialogColor - clip: true - } - - text: selectorPu.subTitle - - onLinkActivated: { - Qt.openUrlExternally(link) - } - - } - } - - function appear(dataObj, title, subTitle) { - if(dataObj.length > 0){ - selectorPu.dataObj = dataObj - } - else { - selectorPu.dataObj = undefined - } - selectorPu.title = title - selectorPu.subTitle = subTitle === undefined ? "":subTitle - selectorPu.open() - - } - - ListView { + contentItem: ListView { id: selectorLv property int delegateHeight: 50 + spacing: 10 - anchors.fill: parent - - implicitWidth: parent.width - implicitHeight: root.height * 0.7 < ( (delegateHeight + spacing) * model ) ? root.height * 0.7 : (delegateHeight + spacing) * model + 100 + implicitHeight: model === 0 ? 0:(delegateHeight + spacing) * model model: selectorPu.dataObj !== undefined ? selectorPu.dataObj.length:0 @@ -377,32 +286,5 @@ Page { } } } - - enter: Transition { - NumberAnimation { - property: "opacity"; - //from: 0.0; - to: 1.0 - } - NumberAnimation { - property: "y" - //from: root.height - selectorPu.height * 0.7 - to: root.height - selectorPu.height - } - } - - exit: Transition { - NumberAnimation { - property: "opacity"; - //from: 1.0; - to: 0.0 - } - NumberAnimation { - property: "y" - //from: root.height - selectorPu.height - to: root.height - selectorPu.height * 0.7 - } - } } - } diff --git a/resources/qml/Widgets/CalendarWidget.qml b/resources/qml/Widgets/CalendarWidget.qml index f569c88..f9785c1 100644 --- a/resources/qml/Widgets/CalendarWidget.qml +++ b/resources/qml/Widgets/CalendarWidget.qml @@ -150,7 +150,7 @@ DataListView { if(endDate.getTime() < new Date().getTime()){ // end date is already over -> move the list view down! - control.positionViewAtIndex(i, ListView.Top) + control.positionViewAtIndex(i - 2, ListView.Top) //console.log("moving down!") } } @@ -400,114 +400,18 @@ DataListView { delegate: CompetitionCalendarDelegate { } - section.property: "month" - section.delegate: ItemDelegate { - id: name - background: Rectangle { - color: "red" - } - - width: parent.width - text: section - } - - Dialog { + SelectorPopup { id: filterSelectPu - property var dataObj - property string subTitle: "" - - signal selectionFinished(int index, var data) - - x: 0 - control.anchors.leftMargin //root.width / 2 - width / 2 - y: root.height - filterSelectPu.height * 0.7//root.height - height //root.height / 2 - height / 2 - - opacity: 0 - - width: control.width + control.anchors.leftMargin + control.anchors.rightMargin - height: selectorLv.implicitHeight - - modal: true - focus: true - - title: "" - - function appear(dataObj, title, subTitle) { - if(dataObj.length > 0){ - filterSelectPu.dataObj = dataObj - filterSelectPu.title = title - filterSelectPu.subTitle = subTitle === undefined ? "":subTitle - filterSelectPu.open() - } - } - - header: Column { - id: filterSelectPuHeaderCol - - width: parent.width - - Label { - id: headerLa - - visible: filterSelectPu.title - - width: parent.width - - elide: "ElideRight" - padding: 24 - bottomPadding: 0 - font.bold: true - font.pixelSize: 16 - background: Rectangle { - radius: 2 - //color: filterSelectPu.Material.dialogColor - clip: true - } - - text: filterSelectPu.title - - onLinkActivated: { - Qt.openUrlExternally(link) - } - - } - Label { - id: headerSubLa - - visible: filterSelectPu.subTitle - - width: parent.width - - elide: "ElideRight" - padding: 24 - topPadding: 5 - bottomPadding: 0 - font.bold: true - font.pixelSize: 16 - background: Rectangle { - radius: 2 - //color: filterSelectPu.Material.dialogColor - clip: true - } - - text: filterSelectPu.subTitle - - onLinkActivated: { - Qt.openUrlExternally(link) - } - - } - } - - ListView { + contentItem: ListView { id: selectorLv property int delegateHeight: 50 + spacing: 10 - anchors.fill: parent + clip: true - implicitWidth: parent.width - implicitHeight: root.height * 0.7 < ( (delegateHeight + spacing) * model ) ? root.height * 0.7 : (delegateHeight + spacing) * model + 100 + implicitHeight: model === 0 ? 0:(delegateHeight + spacing) * model model: filterSelectPu.dataObj !== undefined ? filterSelectPu.dataObj.length:0 @@ -520,13 +424,12 @@ DataListView { leftMargin: 3 bottom: selectorLv.bottom } - } delegate: CheckDelegate { id: catBt - width: parent.width + width: selectorLv.width height: text !== "" ? selectorLv.delegateHeight:0 //flat: true @@ -567,31 +470,5 @@ DataListView { } } } - - enter: Transition { - NumberAnimation { - property: "opacity"; - //from: 0.0; - to: 1.0 - } - NumberAnimation { - property: "y" - //from: root.height - filterSelectPu.height * 0.7 - to: root.height - filterSelectPu.height - } - } - - exit: Transition { - NumberAnimation { - property: "opacity"; - //from: 1.0; - to: 0.0 - } - NumberAnimation { - property: "y" - //from: root.height - filterSelectPu.height - to: root.height - filterSelectPu.height * 0.7 - } - } } } diff --git a/resources/qml/Widgets/ProfileWidget.qml b/resources/qml/Widgets/ProfileWidget.qml index fb928b8..83526ea 100644 --- a/resources/qml/Widgets/ProfileWidget.qml +++ b/resources/qml/Widgets/ProfileWidget.qml @@ -19,6 +19,7 @@ import QtQuick 2.9 import QtQuick.Controls 2.4 import QtQuick.Controls.Material 2.3 +import QtGraphicalEffects 1.0 import "../Components" @@ -43,25 +44,11 @@ Page { id: mainSv anchors.fill: parent - anchors.margins: 10 - anchors.rightMargin: 14 + anchors.margins: 20 - contentWidth: parent.width - anchors.leftMargin - anchors.rightMargin + contentWidth: parent.width - anchors.margins * 2 - ScrollBar.vertical: ScrollBar { - - anchors { - top: mainSv.top - left: mainSv.right - margins: 10 - leftMargin: 3 - bottom: mainSv.bottom - } - - width: 8 - - active: true - } + ScrollBar.vertical.policy: ScrollBar.AlwaysOff Column { id: mainCol @@ -72,25 +59,61 @@ Page { height: control.height * 0.3 width: parent.width - Image { - id: photo - - property bool ready: false + spacing: photoContainerItem.width * 0.1 + Item { + id: photoContainerItem anchors.verticalCenter: parent.verticalCenter height: parent.height * 0.9 - width: status === Image.Null || status === Image.Error ? 0:parent.width * 0.5 + width: photo.status === Image.Null || photo.status === Image.Error ? 0:parent.width * 0.4 - fillMode: Image.PreserveAspectFit + RectangularGlow { + id: effect + visible: photo.ready + anchors.fill: photo + glowRadius: 1 + opacity: 0.4 + color: "black" + cornerRadius: photo.width * 0.2 + } - source: widgetData["photo"] === undefined ? "":widgetData["photo"].replace("https", "http").replace("www.digitalrock.de", "egw.ifsc-climbing.org") - asynchronous: true + Image { + id: photo + + property bool ready: photo.status === Image.Ready - FancyBusyIndicator { - height: width anchors.centerIn: parent - opacity: photo.status === Image.Loading + height: parent.height * 0.9 + width: height * 0.7 + + fillMode: Image.PreserveAspectCrop + mipmap: true + + source: widgetData["photo"] === undefined ? + "": + widgetData["photo"].replace("https", "http") + asynchronous: true + + FancyBusyIndicator { + height: width + anchors.centerIn: parent + opacity: photo.status === Image.Loading + } + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Item { + width: photo.width + height: photo.height + Rectangle { + anchors.centerIn: parent + width: photo.width + height: photo.height + radius: photo.width * 0.2 + } + } + } } } @@ -99,7 +122,7 @@ Page { anchors.verticalCenter: parent.verticalCenter height: parent.height * 0.9 - width: parent.width - photo.width + width: parent.width - photoContainerItem.width * 1.1 Label { @@ -214,7 +237,9 @@ Page { wrapMode: Label.Wrap - text: widgetData["freetext"] === undefined ? "":widgetData["freetext"] + text: widgetData["freetext"] === undefined ? + "": + (widgetData["freetext"] + "
") } @@ -224,7 +249,7 @@ Page { anchors.horizontalCenter: parent.horizontalCenter height: 1 - width: parent.width * 0.9 + width: parent.width color: "black" @@ -307,6 +332,7 @@ Page { width: parent.width height: 50 + spacing: 0 opacity: 0 scale: 0.9 @@ -324,11 +350,11 @@ Page { Label { id: bestResultRankLa - width: parent.width * 0.2 + width: parent.width * 0.1 height: parent.height verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignLeft fontSizeMode: Text.Fit minimumPixelSize: 1 @@ -341,11 +367,11 @@ Page { Label { id: bestResultCompLa - width: parent.width * 0.6 + width: parent.width * 0.7 height: parent.height verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignLeft fontSizeMode: Text.Fit minimumPixelSize: height * 0.3 @@ -370,7 +396,7 @@ Page { scale: 0.8 verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignRight fontSizeMode: Text.Fit minimumPixelSize: 1 diff --git a/resources/qml/Widgets/RankingWidget.qml b/resources/qml/Widgets/RankingWidget.qml index 6892306..7a3acd8 100644 --- a/resources/qml/Widgets/RankingWidget.qml +++ b/resources/qml/Widgets/RankingWidget.qml @@ -35,7 +35,7 @@ DataListView { Connections { target: selector - onSelectionFinished: { + function onSelectionFinished(index, data) { if(data.cat !== undefined){ updateData({cat: data.cat}, true) } @@ -64,12 +64,12 @@ DataListView { delegate: ItemDelegate { id: partDel - property int ind: index + property int thisIndex: index property var thisData: widgetData[ "participants" ][index] state: "closed" - width: parent.width + width: control.width height: 70 opacity: 0 @@ -105,7 +105,7 @@ DataListView { Connections { target: control - onCloseAll: { + function onCloseAll() { partDel.state = "closed" } } @@ -115,7 +115,7 @@ DataListView { width: partDel.width - color: partDel.ind % 2 == 0 ? "white":"lightgrey" + color: partDel.thisIndex % 2 == 0 ? "white":"lightgrey" opacity: 0.2 } @@ -221,7 +221,7 @@ DataListView { font.pixelSize: Math.abs( height * 0.6 ) minimumPixelSize: 1 - text: widgetData[ "participants" ][partDel.ind]["points"] === undefined ? "":"points: "+widgetData[ "participants" ][partDel.ind]["points"] + text: widgetData[ "participants" ][partDel.thisIndex]["points"] === undefined ? "":"points: "+widgetData[ "participants" ][partDel.thisIndex]["points"] } } } @@ -333,7 +333,7 @@ DataListView { for(var prop in obj) { // go through the whole array and search for data keys - if (obj.hasOwnProperty(prop) && control.widgetData["participants"][partDel.ind]["result" + prop.replace(" ", "")] !== undefined ) { + if (obj.hasOwnProperty(prop) && control.widgetData["participants"][partDel.thisIndex]["result" + prop.replace(" ", "")] !== undefined ) { resultData.unshift([prop, obj[prop].replace("\n", " - ")]) //console.log("found " + obj[prop] + " at index " + prop) } @@ -382,7 +382,7 @@ DataListView { horizontalAlignment: Text.AlignLeft minimumPixelSize: 1 - text: control.widgetData["participants"][partDel.ind]["result"+detailResultRowRep.compResults[index][0].replace(" ", "")] === undefined ? "":control.widgetData["participants"][partDel.ind]["result"+detailResultRowRep.compResults[index][0].replace(" ", "")].replace("\n", " ") + text: control.widgetData["participants"][partDel.thisIndex]["result"+detailResultRowRep.compResults[index][0].replace(" ", "")] === undefined ? "":control.widgetData["participants"][partDel.thisIndex]["result"+detailResultRowRep.compResults[index][0].replace(" ", "")].replace("\n", " ") } Behavior on height { diff --git a/resources/qml/Widgets/RegistrationWidget.qml b/resources/qml/Widgets/RegistrationWidget.qml index 4ca9569..106d27e 100644 --- a/resources/qml/Widgets/RegistrationWidget.qml +++ b/resources/qml/Widgets/RegistrationWidget.qml @@ -31,13 +31,13 @@ DataListView { property bool titleIsPageTitle: true property Component headerComponent: ToolButton { - id: moreToolBt + id: moreToolBt - onClicked: { - control.changeCat() - } + onClicked: { + control.changeCat() + } - icon.name: "menu" + icon.name: "menu" } property var widgetData: currentWidgetData @@ -111,7 +111,7 @@ DataListView { Connections { target: selector - onSelectionFinished: { + function onSelectionFinished(index, data) { if(data.cat !== undefined){ updateData({cat: data.cat}, true) } @@ -140,10 +140,11 @@ DataListView { delegate: ItemDelegate { id: partDel + property int thisIndex: index property var thisData: widgetData[ "athletes" ][index] width: parent.width - height: parseInt(thisData.cat) === parseInt(params.cat) ? undefined:0 + height: parseInt(thisData.cat) === parseInt(params.cat) ? 50:0 opacity: 0 scale: 0.9 @@ -164,16 +165,24 @@ DataListView { text: "" + Rectangle { + anchors.fill: parent + + width: partDel.width + + color: partDel.thisIndex % 2 == 0 ? "white":"lightgrey" + + opacity: 0.2 + } + Label { anchors.fill: parent anchors.leftMargin: parent.width * 0.05 verticalAlignment: Text.AlignVCenter - fontSizeMode: Text.Fit font.bold: true - font.pixelSize: Math.abs( height * 0.35 ) - minimumPixelSize: height * 0.3 + font.pixelSize: Math.abs( height * 0.3 ) elide: "ElideRight" diff --git a/resources/qml/Widgets/ResultWidget.qml b/resources/qml/Widgets/ResultWidget.qml index 01101b6..b44c2f4 100644 --- a/resources/qml/Widgets/ResultWidget.qml +++ b/resources/qml/Widgets/ResultWidget.qml @@ -21,9 +21,7 @@ import QtQuick.Controls 2.4 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Controls.Material 2.3 - //import QtLocation 5.13 - - import QtPurchasing 1.12 +import QtPurchasing 1.12 import "../Components" @@ -46,9 +44,7 @@ DataListView { ToolButton { id: moreToolBt - onClicked: { - control.changeCat() - } + onClicked: control.changeCat() icon.name: "menu" } @@ -56,11 +52,15 @@ DataListView { ToolButton { id: flowToolBt - visible: speedFlowChart.enabled + visible: speedFlowChartPopup.enabled enabled: control.widgetData['route_order'] === "-1" && Object.keys(control.widgetData['route_names']).length > 2 - onClicked: speedFlowChartBackgroundRect.toggle() + onClicked: { + control.positionViewAtBeginning() + speedFlowChartPopup.toggle() + } + icon.name: "flowchart" } } @@ -94,21 +94,21 @@ DataListView { console.log("widget data changed") if(control.widgetData['discipline'] === 'speed' && control.widgetData['route_order'] === "-1" && Object.keys(control.widgetData['route_names']).length > 2){ - speedFlowChart.flowchartData = ({}) - speedFlowChart.enabled = true - speedFlowChart.flowchartData = control.widgetData + speedFlowChartPopup.flowchartData = ({}) + speedFlowChartPopup.flowchartData = control.widgetData + speedFlowChartPopup.enabled = true } else { - speedFlowChart.enabled = false - speedFlowChart.flowchartData = ({}) + speedFlowChartPopup.flowchartData = ({}) + speedFlowChartPopup.enabled = false } } function onBackRequested() { - if(!speedFlowChartBackgroundRect.isVisible()) + if(!speedFlowChartPopup.isVisible()) return true - speedFlowChartBackgroundRect.toggle() + speedFlowChartPopup.toggle() return false } @@ -168,484 +168,13 @@ DataListView { } onContentYChanged: { - if(contentY > 0 && speedFlowChartBackgroundRect.state === "visible"){ + if(contentY > 0 && speedFlowChartPopup.state === "visible"){ control.positionViewAtBeginning() } } - delegate: ItemDelegate { - id: partDel - - property int ind: index - property var thisData: widgetData[ "participants" ][partDel.ind] - - enabled: speedFlowChartBackgroundRect.state === "hidden" - - width: control.width - height: 70 - - text: "" - - opacity: 0 - scale: 0.9 - - onThisDataChanged: { - fadeInPa.start() - } - - onClicked: { - app.openWidget({person:thisData["PerId"]}) - } - - ParallelAnimation { - id: fadeInPa - NumberAnimation { target: partDel; property: "opacity"; from: 0; to: 1.0; duration: 400 } - NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 } - } - - Rectangle { - id: partDelBackgroundRect - anchors.fill: parent - - width: partDel.width - - color: partDel.ind % 2 == 0 ? "white":"lightgrey" - - opacity: 0.2 - } - - Column { - id: partDelCol - - anchors.fill: parent - anchors.margins: 5 - - Row { - id: partDelFirstRow - - width: parent.width - height: parent.height - partDelSecondRow.height - - Label { - height: parent.height - width: text === "" ? parent.width * 0.08:parent.width * 0.1 - - fontSizeMode: Text.Fit - font.bold: true - font.pixelSize: Math.abs( partDelSecondRow.height > 0 ? height * 0.6:height * 0.4 ) - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - text: partDel.thisData["result_rank"] === undefined ? "":partDel.thisData["result_rank"] - } - - Label { - height: parent.height - width: parent.width * 0.5 - - fontSizeMode: Text.Fit - font.bold: true - font.pixelSize: Math.abs( partDelSecondRow.height > 0 ? height * 0.6:height * 0.4 ) - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - - text: partDel.thisData["firstname"] + " " + partDel.thisData["lastname"] + (partDel.thisData["start_number"] !== undefined ? (" (" + partDel.thisData["start_number"] + ")"):"") - } - - Label { - height: parent.height - width: parent.width * 0.4 - - fontSizeMode: Text.Fit - font.bold: false - font.pixelSize: Math.abs( partDelSecondRow.height > 0 ? height * 0.4:height * 0.3 ) - minimumPixelSize: height * 0.3 < 1 ? 1:height * 0.3 - - elide: "ElideRight" - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - text: "(" + (widgetData[ "display_athlete" ] === "nation" ? partDel.thisData["nation"] : partDel.thisData["federation"]) + ")" - - onLinkActivated: { - Qt.openUrlExternally(link) - } - } - } - - Row { - id: partDelSecondRow - - width: parent.width - height: multiResRow.active || multiGenResRow.active || resultLa.acitve ? parent.height / 2 : 0 - - Row { - id: multiResRow - - property bool active: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0 - - height: parent.height - width: active ? parent.width * 0.75:0 - - enabled: parseInt(widgetData[ "route_order" ]) > -1 && boulderResRep.model > 0 - - Repeater { - id: boulderResRep - model: parseInt(widgetData[ "route_num_problems" ]) - - function getDataForIcon(index){ - // TODO: clean - var resultString = widgetData[ "participants" ][partDel.ind]["boulder"+(index+1)] - var numTrys = widgetData[ "participants" ][partDel.ind]["try"+(index+1)] - - var resultList = [] - - if (resultString !== undefined) { - resultString = resultString.replace("t", "") - resultString = resultString.replace("z", "") - resultString = resultString.replace("b", "") - resultList = resultString.split(" ") - - while (resultList.length < 2){ - resultList.unshift(0) - } - } - else { - resultList = [-1,-1] - } - - if (numTrys !== undefined) { - resultList.push(numTrys) - } - else { - resultList.push(-1) - } - - return resultList - } - - delegate: Item { - id: boulderResItm - - anchors.verticalCenter: parent.verticalCenter - - width: parent.width / ( boulderResRep.model ) - height: parent.height - - Canvas { - id: boulderResCv - - property var resultData: boulderResRep.getDataForIcon(index) - - onResultDataChanged: { - boulderResCv.requestPaint() - } - - anchors.centerIn: parent - - height: parent.height > parent.width ? parent.width * 0.9:parent.height * 0.9 - width: height - - onPaint: { - var width = 24//boulderResCv.width * 0.9 - var height = width - - var radius = width * 0.3 - - var offsetX = width * 0.05 - var offsetY = height * 0.05 - - //console.log("drawing result rect with width: " + width + " and height: " + height) - - var context = getContext("2d"); - - // clear all remainings from other routes - context.clearRect(0, 0, width, height); - - context.beginPath(); - - context.moveTo(0 + offsetX + radius, 0 + offsetY); - - // top line - context.lineTo(width - radius + offsetX, 0 + offsetY); - // top right corner - context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 0); - // right line - context.lineTo(width + offsetX, height - radius + offsetY); - // bottom right corner - context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI); - // bottom line - context.lineTo(0 + radius + offsetX, height + offsetY); - // bottom left corner - context.arc(radius + offsetY, height - radius + offsetY, radius, 0.5 * Math.PI, Math.PI); - // left line - context.lineTo(0 + offsetX, radius + offsetY); - // top left corner - context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI); - - // fill - if(resultData[0] !== -1) { - // if there is a result available -> draw background - context.fillStyle = "#b7b7b7"; - } - else { - context.fillStyle = "transparent"; - } - - context.fill(); - - // outline - context.lineWidth = 1; - context.strokeStyle = '#424242'; - context.stroke(); - - if(resultData[1] > 0){ - - // the first triangle - context.beginPath(); - - // top right corner - context.arc(width-radius + offsetX, radius + offsetY, radius, 1.75 * Math.PI, 0); - - // right line - context.lineTo(width + offsetX, height - radius + offsetY); - - // bottom right corner - context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI); - - // bottom line - context.lineTo(0 + radius + offsetX, height + offsetY); - // bottom left corner - context.arc(radius + offsetX, height - radius + offsetY, radius, 0.5 * Math.PI, 0.75 * Math.PI); - context.closePath(); - - context.fillStyle = "#44ed38"; - context.fill(); - - // outline - context.lineWidth = 1; - context.strokeStyle = '#424242'; - context.stroke(); - - - if(resultData[0] > 0){ - // the second triangle - context.beginPath(); - // bottom left corner - context.arc(radius + offsetX, height - radius + offsetY, radius, 0.75 * Math.PI, 1 * Math.PI); - // left line - context.lineTo(0 + offsetX, radius + offsetY); - // top left corner - context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI); - // top line - context.lineTo(width - radius + offsetX, 0 + offsetY); - // top right corner - context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 1.75 * Math.PI); - - context.closePath(); - - context.fillStyle = "#44ed38"; - context.fill(); - - // outline - context.lineWidth = 1; - context.strokeStyle = '#424242'; - context.stroke(); - } - } - - } - - Label { - id: boulderResTrysLa - - anchors.centerIn: parent - - height: parent.height / 2 - width: parent.width / 2 - - visible: !boulderResZoneLa.visible - - fontSizeMode: Text.Fit - font.pixelSize: height - minimumPixelSize: 1 - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - text: boulderResCv.resultData[2] - - } - - Label { - id: boulderResZoneLa - - anchors { - right: parent.right - bottom: parent.bottom - margins: boulderResCv.height * 0.05 - } - - height: parent.height / 2 - width: parent.width / 2 - - visible: parseInt(text) > 0 - - fontSizeMode: Text.Fit - font.pixelSize: height - minimumPixelSize: 1 - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - text: boulderResCv.resultData[1] - } - - Label { - id: boulderResTopLa - - anchors { - left: parent.left - top: parent.top - margins: boulderResCv.height * 0.05 - } - - height: parent.height / 2 - width: parent.width / 2 - - visible: parseInt(text) > 0 - - fontSizeMode: Text.Fit - font.pixelSize: height - minimumPixelSize: 1 - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - text: boulderResCv.resultData[0] - } - } - - } - - } - - } - - Row { - id: multiGenResRow - - property bool active: ((parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)) ? true:false - - height: parent.height - width: active ? parent.width - resultLa.width:0 - - enabled: ((parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)) ? true:false - - Repeater { - id: generalResRep - - property var routes: getRoutes() - model: routes.length - - function getRoutes() { - - var obj = widgetData["route_names"] - var routes = [] - - for(var prop in obj) { - // go through the whole array and search for data keys - if (obj.hasOwnProperty(prop) && prop > -1) { - routes.push([prop, obj[prop]]) - //console.log("found " + obj[prop] + " at index " + prop) - } - } - - routes.sort(function(a, b) { - return a[0] - b[0]; - }); - - return routes - } - - delegate: Item { - id: boulderGenResItm - - anchors.verticalCenter: parent.verticalCenter - - width: parent.width / ( generalResRep.model ) - height: parent.height - - visible: multiGenResRow.active - - Rectangle { - - anchors { - left: parent.left - } - - width: 1 - height: parent.height - - visible: index === 0 - - color: "grey" - } - - Rectangle { - anchors { - right: parent.right - } - - width: 1 - height: parent.height - - color: "grey" - } - - Label { - id: boulderGenResLa - anchors.centerIn: parent - - height: parent.height - width: parent.width * 0.9 - - fontSizeMode: Text.Fit - font.pixelSize: Math.abs( height * 0.6 ) - minimumPixelSize: 1 - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - text: widgetData[ "participants" ][partDel.ind]["result"+(generalResRep.routes[index][0])] === undefined ? "":widgetData[ "participants" ][partDel.ind]["result"+(generalResRep.routes[index][0])] - } - - } - - } - } - - Label { - id: resultLa - - property bool acitve: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 - - width: enabled ? parent.width * 0.25:0 - height: enabled ? parent.height:0 - - enabled: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - fontSizeMode: Text.Fit - font.pixelSize: Math.abs( height * 0.6 ) - minimumPixelSize: 1 - - text: widgetData[ "participants" ][partDel.ind]["result"] === undefined ? "":widgetData[ "participants" ][partDel.ind]["result"] - } - } - } + delegate: ResultDelegate { + enabled: speedFlowChartPopup.state === "hidden" } footer: ItemDelegate { @@ -667,7 +196,7 @@ DataListView { property var tabs: getTabs() property var tabIndexes: [] - enabled: speedFlowChartBackgroundRect.state === "hidden" + enabled: speedFlowChartPopup.state === "hidden" anchors { left: parent.left @@ -747,209 +276,13 @@ DataListView { } - Rectangle { - id: speedFlowChartBackgroundRect - - state: "hidden" - - anchors { - top: parent.top - bottom: parent.bottom - left: parent.right - } - - width: parent.width - height: parent.height - - color: Material.background - - function toggle() { - if(speedFlowChartBackgroundRect.state === "hidden"){ - speedFlowChartBackgroundRect.state = "visible" - control.positionViewAtBeginning() - } - else { - speedFlowChartBackgroundRect.state = "hidden" - } - } - - function isVisible() { - return speedFlowChartBackgroundRect.state === "visible" - } - - SpeedFlowChart { - id: speedFlowChart - - anchors.fill: parent - - enabled: false - - flowchartData: ({}) - - onEnabledChanged: { - if(!enabled){ - speedFlowChartBackgroundRect.state = 'hidden' - } - } - - Rectangle { - id: speedFlowChartLockedOverlay - - state: appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked" - - anchors.fill: parent - anchors.margins: -20 - - color: "white" - - Connections { - target: speedFlowChartProduct - function onPurchaseRestored() { - speedFlowChartLockedOverlay.state = appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked" - } - - function onPurchaseSucceeded() { - speedFlowChartLockedOverlay.state = appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked" - } - - function onPurchaseFailed() { - purchaseBt.text = qsTr("Purchase failed") - purchaseBt.enabled = false - buttonTextResetTimer.start() - } - } - - Timer { - id: buttonTextResetTimer - interval: 2000 - running: false - repeat: false - onTriggered: { - purchaseBt.text = (speedFlowChartProduct.status === Product.Registered - ? "Buy now for " + speedFlowChartProduct.price - : qsTr("this item is currently unavailable")) - purchaseBt.enabled = true - } - } - - ColumnLayout { - id: lockedLayout - - anchors { - fill: parent - topMargin: parent.height * 0.05 - bottomMargin: parent.height * 0.1 - rightMargin: parent.width * 0.1 + 20 - leftMargin: parent.width * 0.1 + 20 - } - - //spacing: parent.height * 0.05 - - Image { - id: name - - Layout.alignment: Layout.Center - Layout.preferredHeight: height - Layout.preferredWidth: width - - width: lockedLayout.height * 0.1 - height: width - - mipmap: true - - source: "qrc:/icons/lock.png" - } - - Text { - Layout.fillWidth: true - - height: parent.height * 0.05 - - text: qsTr("This is a premium feature.") - - font.bold: true - font.pixelSize: parent.height * 0.05 - fontSizeMode: Text.Fit - - minimumPixelSize: 1 - - horizontalAlignment: Text.AlignHCenter - - } - - SwipeGallery { - Layout.fillHeight: true - Layout.fillWidth: true - - images: ["qrc:/screenshots/SpeedFlowchartDemo/1.png","qrc:/screenshots/SpeedFlowchartDemo/2.png","qrc:/screenshots/SpeedFlowchartDemo/3.png"] - } - - Button { - id: purchaseBt - Layout.alignment: Layout.Center - enabled: speedFlowChartProduct.status === Product.Registered - text: speedFlowChartProduct.status === Product.Registered - ? "Buy now for " + speedFlowChartProduct.price - : qsTr("this item is currently unavailable") - icon.name: "buy" - //display: AbstractButton.TextBesideIcon - onClicked: speedFlowChartProduct.purchase() - } - - } - - states: [ - State { - name: "unlocked" - PropertyChanges { - target: speedFlowChartLockedOverlay - visible: false - } - }, - State { - name: "locked" - PropertyChanges { - target: speedFlowChartLockedOverlay - visible: true - } - } - ] - } - - } - - states: [ - State { - name: "hidden" - PropertyChanges { - target: speedFlowChartBackgroundRect - opacity: 0 - anchors.leftMargin: 0 - } - }, - - State { - name: "visible" - PropertyChanges { - target: speedFlowChartBackgroundRect - opacity: 1 - anchors.leftMargin: -parent.width - } - } - ] - - transitions: [ - Transition { - NumberAnimation { - properties: "opacity,scale,anchors.leftMargin" - duration: 200 - easing.type: Easing.InOutQuad - } - } - ] + SpeedFlowChartPopup { + id: speedFlowChartPopup } PullRefresher { + // has to be placed here again, + // to be on top of the speed flowchart target: control postRefreshDelay: 0 @@ -962,4 +295,5 @@ DataListView { } } + } diff --git a/resources/qml/Widgets/StartlistWidget.qml b/resources/qml/Widgets/StartlistWidget.qml index 4f9b6b8..90959d1 100644 --- a/resources/qml/Widgets/StartlistWidget.qml +++ b/resources/qml/Widgets/StartlistWidget.qml @@ -82,7 +82,7 @@ DataListView { Connections { target: selector - onSelectionFinished: { + function onSelectionFinished(index, data) { if(data.cat !== undefined){ updateData({cat: data.cat}, true) } @@ -113,9 +113,11 @@ DataListView { delegate: ItemDelegate { id: partDel + property int thisIndex: index property var thisData: widgetData[ "participants" ][index] width: parent.width + height: 50 opacity: 0 scale: 0.9 @@ -136,6 +138,16 @@ DataListView { text: "" + Rectangle { + anchors.fill: parent + + width: partDel.width + + color: partDel.thisIndex % 2 == 0 ? "white":"lightgrey" + + opacity: 0.2 + } + Row { id: partDelFirstRow @@ -145,7 +157,7 @@ DataListView { rightMargin: parent.width * 0.05 } - spacing: width * 0.1 + spacing: width * 0.05 Label { height: parent.height @@ -153,7 +165,7 @@ DataListView { fontSizeMode: Text.Fit font.bold: true - font.pixelSize: Math.abs( height * 0.6 ) + font.pixelSize: Math.abs( height * 0.4 ) verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter @@ -162,7 +174,7 @@ DataListView { Label { height: parent.height - width: parent.width * 0.4 + width: parent.width * 0.45 fontSizeMode: Text.Fit font.bold: true @@ -178,10 +190,8 @@ DataListView { height: parent.height width: parent.width * 0.3 - fontSizeMode: Text.Fit font.bold: false - font.pixelSize: Math.abs( height * 0.4 ) - minimumPixelSize: height * 0.3 + font.pixelSize: height * 0.3 elide: "ElideRight" diff --git a/resources/qml/main.qml b/resources/qml/main.qml index 488d0e2..e0f650f 100644 --- a/resources/qml/main.qml +++ b/resources/qml/main.qml @@ -20,6 +20,7 @@ import QtQuick 2.9 import QtQuick.Window 2.2 import QtQuick.Controls 2.4 import QtQuick.Layouts 1.3 +import QtQuick.Controls.Material 2.12 import QtPurchasing 1.12 import com.itsblue.digitalRockRanking 1.0 @@ -39,7 +40,6 @@ Window { property int errorCode: -1 // comp cats source: - // - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/icc_calendar.php // - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/dav_calendar.php // - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/sac_calendar.php @@ -120,12 +120,15 @@ Window { anchors.fill: parent + //Material.theme: Material.Dark + Component.onCompleted: { //loadingDl.open() - //app.openAthlete(53139) // dorian: 53139 , rustam: 6933 , helen: 53300 + //app.openAthlete() // dorian: 53139 , rustam: 6933 , helen: 53300 //openWidget({nation:'GER'}) //mainStack.push("Pages/AthleteSearchPage.qml") - //openWidget({comp: 11651, cat: 25}) + //openWidget({comp: 11651, cat: 26}) + //openWidget({person: 6623}) } Shortcut { @@ -315,8 +318,10 @@ Window { height: parent.height onItemChanged: { - if(item === null) + if(item === null) { + extraComponentLoader.Layout.preferredWidth = 0 return + } extraComponentLoader.item.width = extraComponentLoader.item.implicitWidth extraComponentLoader.Layout.preferredWidth = extraComponentLoader.item.width diff --git a/resources/qml/qml.qrc b/resources/qml/qml.qrc index e19b2bf..2305758 100644 --- a/resources/qml/qml.qrc +++ b/resources/qml/qml.qrc @@ -20,5 +20,10 @@ Components/SpeedFlowChart.qml Components/SwipeGallery.qml Components/CompetitionCalendarDelegate.qml + Components/SelectorPopup.qml + Components/ResultDelegate.qml + Components/SpeedFlowChartLocker.qml + Components/SpeedFlowChartPopup.qml + Components/BlueRockBadge.qml diff --git a/sources/main.cpp b/sources/main.cpp index df5e095..193c314 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -33,7 +33,6 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); - qDebug() << QStyleFactory::keys(); QQuickStyle::setStyle("Material"); QIcon::setFallbackSearchPaths(QIcon::fallbackSearchPaths() << ":/resources/shared/icons"); QIcon::setThemeName("bluerock"); @@ -42,6 +41,13 @@ int main(int argc, char *argv[]) qmlRegisterType("com.itsblue.digitalRockRanking", 1, 0, "AppSettings"); QQmlApplicationEngine engine; + +#ifdef QT_DEBUG + engine.rootContext()->setContextProperty("QT_DEBUG", true); +#else + engine.rootContext()->setContextProperty("QT_DEBUG", false); +#endif + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1;