/* 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.4 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Controls.Material 2.3 import "../Components" DataListView { id: control property bool ready property string title: control.widgetData['comp_name'] property string subTitle: getSubtitle() property bool titleIsPageTitle: true property Component headerComponent: RowLayout { height: parent.height width: 100//childrenRect.width spacing: 0 ToolButton { id: flowToolBt visible: control.widgetData['discipline'] === 'speed' onClicked: { if(speedFlowChartBackgroundRect.state === "hidden"){ speedFlowChartBackgroundRect.state ="visible" } else { speedFlowChartBackgroundRect.state = "hidden" } } icon.name: "flowchart" } ToolButton { id: moreToolBt onClicked: { control.changeCat() } icon.name: "menu" } } property var widgetData: currentWidgetData Component.onCompleted: { if(model > 0){ control.ready = true control.status = 200 } else { control.ready = false control.status = 901 } if(!control.ready) return //model = widgetData[ "participants" ] === undefined ? 0:widgetData[ "participants" ].length //routeSelectTb.setCurrentIndex(routeSelectTb.getIndex(parseInt(control.widgetData['route_order']))) } model: widgetData[ "participants" ] === undefined ? 0:widgetData[ "participants" ].length onRefresh: { updateData({}, false) } function getSubtitle() { var titleString for(var i = 0; i < control.widgetData["categorys"].length; i ++ ){ //console.log("checking " + i + ": cat: " + parseInt(control.widgetData["categorys"][i]["GrpId"]) + " searched cat: " + params.cat) if(parseInt(control.widgetData["categorys"][i]["GrpId"]) === parseInt(params.cat)){ titleString = control.widgetData["categorys"][i]["name"] } } var addition = qsTr("(Results)") if(titleString !== undefined){ return addition + " " + titleString } else { return "" } } function changeRoute(route) { updateData({route:route}, true) } function changeCat(){ var cats = control.widgetData["categorys"] cats.sort(function(a, b) { return parseInt(a["GrpId"]) - parseInt(b["GrpId"]); }); var selectOptions = [] for(var prop in cats){ if (cats.hasOwnProperty(prop) && parseInt(cats[prop]["GrpId"]) !== parseInt(params.cat)) { // append all cats and ignore the current one //console.log("found cat: ", cats[prop]['name']) selectOptions.push({text: cats[prop]['name'], data:{cat: cats[prop]['GrpId']}}) } } selector.appear(selectOptions, qsTr("select cat")) } Connections { target: selector onSelectionFinished: { if(data.cat !== undefined){ updateData({cat: data.cat, route:""}, true) } } } delegate: ItemDelegate { id: partDel property int ind: index property var thisData: widgetData[ "participants" ][partDel.ind] width: parent.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 { 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 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.enabled || multiGenResRow.enabled || resultLa.enabled ? parent.height / 2 : 0 Row { id: multiResRow height: parent.height width: enabled ? 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) } 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"); 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 context.fillStyle = "#b7b7b7"; 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: 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: text !== "0" fontSizeMode: Text.Fit font.pixelSize: height minimumPixelSize: 1 verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: boulderResCv.resultData[0] } } } } } Row { id: multiGenResRow height: parent.height width: enabled ? 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: enabled 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 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"] } } } } footer: ItemDelegate { height: routeSelectTb.height } RectangularGlow { id: toolBarEffect glowRadius: 3 spread: 0.2 color: "black" opacity: 0.3 anchors.fill: routeSelectTb } TabBar { id: routeSelectTb property var tabs: getTabs() property var tabIndexes: [] anchors { left: parent.left right: parent.right bottom: parent.bottom } height: tabs.length > 1 ? 50:0 width: parent.width position: TabBar.Footer currentIndex: getIndex(parseInt(control.widgetData['route_order'])) function getTabs() { var obj = control.widgetData["route_names"] var buttonData = [] for(var prop in obj) { // go through the whole array and search for data keys if (obj.hasOwnProperty(prop)) { buttonData.push([prop, obj[prop]]) //console.log("found " + obj[prop] + " at index " + prop) } } buttonData.sort(function(a, b) { return a[0] - b[0]; }); return buttonData } function getIndex(routeNumber) { //console.log("getting index for route number: " + routeNumber) if(tabs === undefined){ //return } for(var i = 0; i < tabs.length; i++){ //console.log(tabs[i]) if(parseInt(tabs[i][0]) === routeNumber){ //console.log("found index: " + i) return i } } return 0 } Repeater { id: routeSelectButtonRep model: routeSelectTb.tabs.length onModelChanged: { routeSelectTb.setCurrentIndex(routeSelectTb.getIndex(params.route)) } delegate: TabButton { text: routeSelectTb.tabs[index][1] width: Math.max(150, routeSelectTb.width / routeSelectButtonRep.model) //text.length * font.pixelSize onClicked: { //console.log("changing to index: " + index + " (" + routeSelectTb.tabs[index][0] + ", " + routeSelectTb.tabs[index][1] + ")") if(routeSelectTb.getIndex(parseInt(control.widgetData['route_order'])) !== index){ control.changeRoute(routeSelectTb.tabs[index][0]) routeSelectTb.setCurrentIndex(routeSelectTb.getIndex(parseInt(control.widgetData['route_order']))) } } } } } Rectangle { id: speedFlowChartBackgroundRect state: "hidden" anchors.fill: parent color: Material.background SpeedFlowChart { anchors.fill: parent flowchartData: control.widgetData; //participants: control.widgetData['participants'].slice() //route_names: control.widgetData['route_names'] rounds: control.widgetData['route_names'][2].includes("8") ? 2:1 } states: [ State { name: "hidden" PropertyChanges { target: speedFlowChartBackgroundRect opacity: 0 scale: 0.9 height: 0 } }, State { name: "visible" PropertyChanges { target: speedFlowChartBackgroundRect opacity: 1 scale: 1 height: parent.height } } ] transitions: [ Transition { NumberAnimation { properties: "opacity,scale" duration: 200 } } ] } }