/* blueROCK - for digital rock Copyright (C) 2019 Dorian Zedler This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import QtQuick 2.9 import QtQuick.Controls 2.5 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Controls.Material 2.3 //import QtLocation 5.13 import QtPurchasing 1.12 import "../Components" BRWidgetPage { id: control ready: true title: data.name titleIsPageTitle: true subTitle: data.currentCategory.currentRound.name + " " + data.currentCategory.name + " " + qsTr("(Results)") headerComponent: RowLayout { id: headerComponent height: parent.height spacing: 0 ToolButton { id: flowToolBt visible: false // TOODO: speedFlowChart.enabled enabled: true // TODO control.data.currentCategory.currentRound.getId() === -1 && Object.keys(control.widgetData['route_names']).length > 2 onClicked: { if(speedFlowChartBackgroundRect.state === "hidden"){ speedFlowChartBackgroundRect.state ="visible" control.positionViewAtBeginning() } else { speedFlowChartBackgroundRect.state = "hidden" } } icon.name: "flowchart" } ToolButton { id: moreToolBt onClicked: { control.changeCategory() } icon.name: "menu" } } onLoadingFinished: { return; if(resultLv.model.length > 0){ control.ready = true control.status = 200 } else { control.ready = false control.status = 901 } if(!control.ready) return } onSelectionFinished: { if(data.task === "category"){ loadingDl.open() control.data.currentCategory = data.category control.data.load() loadingDl.close() } else if(data.task === "season"){ loadingDl.open() control.data.currentSeason = data.season loadingDl.close() } else if(data.task === "cup1"){ control.openCup(data) } else if(data.task === "cup2"){ app.openWidget({cup: data.cup, cat: data.cat}) } } function changeCategory(){ var selectOptions = [] var categories = control.data.categories for(var i = 0; i < categories.length; i++){ if(categories[i] === control.data.currentCategory) continue selectOptions.push({text: categories[i].name, data:{task: "category", category: categories[i]}}) } selector.appear(selectOptions, qsTr("select cat")) } function changeRound(round) { loadingDl.open() control.data.currentCategory.currentRound = round control.data.load() loadingDl.close() } DataListView { id: resultLv anchors { top: parent.top topMargin: 0 left: parent.left right: parent.right bottom: routeSelectTb.top } model: control.data.results onRefresh: { control.data.load() } /* TODO onWidgetDataChanged: { 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 } else { speedFlowChart.enabled = false speedFlowChart.flowchartData = ({}) } }*/ onContentYChanged: { if(contentY > 0 && speedFlowChartBackgroundRect.state === "visible"){ resultLv.positionViewAtBeginning() } } header: Item {} delegate: ItemDelegate { id: partDel property int ind: index property var thisData: modelData enabled: speedFlowChartBackgroundRect.state === "hidden" width: resultLv.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: height * 0.6//Math.abs( partDelSecondRow.height > 0 ? height * 0.6:height * 0.4 ) verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: partDel.thisData.rank } Label { height: parent.height width: parent.width * 0.5 fontSizeMode: Text.Fit font.bold: true font.pixelSize: height * 0.6//Math.abs( partDelSecondRow.height > 0 ? height * 0.6:height * 0.4 ) verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft text: partDel.thisData.athlete.firstName + " " + partDel.thisData.athlete.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: height * 0.6 //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: "(" + partDel.thisData.athlete.federation + ")" onLinkActivated: { Qt.openUrlExternally(link) } } } Row { id: partDelSecondRow width: parent.width height: resultLa.acitve ? parent.height / 2 : 0 // TODO multiResRow.active || multiGenResRow.active || resultLa.acitve ? parent.height / 2 : 0 /* Row { id: multiResRow property bool active: false // TODO 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){ var resultString = widgetData[ "participants" ][partDel.ind]["boulder"+(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] } 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: 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: false // TODO ((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: true // TODO ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 width: enabled ? parent.width * 0.75:0 height: enabled ? parent.height:0 enabled: true //( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft fontSizeMode: Text.Fit font.pixelSize: Math.abs( height * 0.6 ) minimumPixelSize: 1 text: partDel.thisData.details.string() } } } } PullRefresher { target: resultLv postRefreshDelay: 0 busyIndicator: FancyBusyIndicator {} refreshPosition: height * 1.3 onRefreshRequested: { resultLv.refresh() } } } RectangularGlow { id: toolBarEffect glowRadius: 3 spread: 0.2 color: "black" opacity: 0.3 anchors.fill: routeSelectTb } TabBar { id: routeSelectTb property var tabs: control.data.currentCategory.rounds property var tabIndexes: [] enabled: speedFlowChartBackgroundRect.state === "hidden" anchors { left: parent.left right: parent.right bottom: parent.bottom } height: tabs.length > 1 ? 50:0 width: parent.width position: TabBar.Footer currentIndex: tabs.indexOf(control.data.currentCategory.currentRound) onTabsChanged: { console.log("tabs: " + tabs + " rounds: " + control.data.currentCategory.rounds) } Repeater { id: routeSelectButtonRep model: routeSelectTb.tabs onModelChanged: { //routeSelectTb.setCurrentIndex(routeSelectTb.tabs.indexOf(control.data.currentCategory.currentRound)) } delegate: TabButton { text: modelData.name width: Math.max(150, routeSelectTb.width / routeSelectButtonRep.model.length) //text.length * font.pixelSize onClicked: { control.changeRound(modelData) return; } } } } Rectangle { id: speedFlowChartBackgroundRect state: "hidden" anchors { top: control.top bottom: control.bottom left: control.right } width: control.width height: control.height color: Material.background 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 } } ] } }