Many small and big improvements, see change log for details

This commit is contained in:
Dorian Zedler 2021-06-06 18:34:27 +02:00
parent 121589bf29
commit f9f6dd7f5d
Signed by: dorian
GPG key ID: 989DE36109AFA354
20 changed files with 1281 additions and 1191 deletions

View file

@ -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

View file

@ -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

View file

@ -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"
}
}

View file

@ -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

View file

@ -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: "<html>(<a href=\"" + (partDel.thisData["fed_url"] === undefined ? "":partDel.thisData["fed_url"]).toString() + "\">" + (widgetData[ "display_athlete" ] === "nation" ? partDel.thisData["nation"] : partDel.thisData["federation"]) + "</a>)</html>"
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"]
}
}
}
}

View file

@ -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
}
}
}

View file

@ -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,22 +215,52 @@ 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 columnWidth: height * 0.3
property int columnHeight: height
anchors {
top: parent.top
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
width: Math.min((columnWidth + spacing) * model, control.width)
spacing: app.height * 0.05
orientation: ListView.LeftToRight
boundsBehavior: ListView.StopAtBounds
model: control.roundCount
delegate: Item {
id: roundItem
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 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
width: app.width * 0.5
height: control.height
property bool thisIsLastRound: thisIndex === control.roundCount - 1
property bool thisIsSemiFinal: thisIndex === control.roundCount - 2 && rectRep.model === 2
//spacing: app.width * 0.1
property int tileSize: (roundItem.height / 8 - roundNameLa.height) * 1.45
width: roundListView.columnWidth
height: roundListView.columnHeight
Column {
id: roundCol
anchors.fill: parent
Label {
id: roundNameLa
@ -250,33 +272,48 @@ ListView {
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] : "-"
text: roundItem.thisRoundIsValid
&& control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] !== undefined ?
control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] :
"-"
}
Repeater {
id: rectRep
model: Math.max( Math.pow(2, control.model-1) * Math.pow(0.5, (index)), 2)
model: Math.max( Math.pow(2, control.roundCount-1) * Math.pow(0.5, (roundItem.thisIndex)), 2)
delegate: Item {
id: matchItm
property bool lowerPart: (index%2 > 0)
property var matchData: roundCol.thisRoundIsValid ? control.allFlowchartData[ thisIsSmallFinal ? roundCol.thisIndex+1 : roundCol.thisIndex][ thisIsSmallFinal ? 0:matchItm.thisIndex]:undefined
property var matchData: roundItem.thisRoundIsValid ?
control.allFlowchartData[
thisIsSmallFinal ?
roundItem.thisIndex+1 :
roundItem.thisIndex
][ thisIsSmallFinal ? 0:matchItm.thisIndex]:
undefined
property int thisIndex: index
property int thisRound: parseInt(roundCol.thisRound) - (thisIsSmallFinal ? 1:0)
property int thisRound: parseInt(roundItem.thisRound) - (thisIsSmallFinal ? 1:0)
property var thisMatchData: thisMatchDataIsValid ? matchData:[]
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
property bool thisIsFinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 2
property bool thisIsSmallFinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 1
property bool thisIsFinal: roundItem.thisIsLastRound && thisIndex === rectRep.model - 2
property bool thisIsSmallFinal: roundItem.thisIsLastRound && thisIndex === rectRep.model - 1
property int winnerIndex: thisMatchIsOver ? (parseInt(thisMatchData[0]['result_rank'+thisRound]) < parseInt(thisMatchData[1]['result_rank'+thisRound]) ? 0:1) : -1
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
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
)
width: roundItem.width
onMatchDataChanged: {
fadeInPa.start()
@ -293,25 +330,49 @@ ListView {
width: app.width; height: app.height
contextType: "2d"
visible: !roundCol.thisIsLastRound
visible: !roundItem.thisIsLastRound
property int targetX: matchItm.width + control.spacing
property int targetY: matchItm.lowerPart ? 0:matchItm.height
property int targetYFactor: matchItm.lowerPart ? -1:1
Path {
id: myPath
startX: matchRect.x + matchRect.width * matchRect.scale ; startY: matchRect.y + matchRect.height * 0.5
id: firstPartPath
startX: matchItm.width
startY: matchItm.height * 0.5
PathCurve { x: lineCanvas.targetX ; y: lineCanvas.targetY }
PathQuad {
id: firstPartPathQuad
relativeX: roundListView.spacing * 0.5
relativeY: matchItm.height * 0.25 * lineCanvas.targetYFactor
relativeControlX: relativeX
relativeControlY: 0
}
PathQuad {
relativeX: roundListView.spacing * 0.5
relativeY: matchItm.height * 0.25 * lineCanvas.targetYFactor
relativeControlX: 0
relativeControlY: relativeY
}
}
onPaint: {
context.strokeStyle = Qt.rgba(.4,.6,.8);
context.path = myPath;
context.strokeStyle = "lightgrey"
context.lineWidth = matchRect.height * 0.05
context.path = firstPartPath;
context.stroke();
}
}
RectangularGlow {
id: effect
anchors.fill: matchRect
glowRadius: 0
spread: 0
opacity: 0.3
color: "black"
cornerRadius: matchRect.radius
}
Rectangle {
id: matchRect
@ -322,19 +383,15 @@ ListView {
//anchors.verticalCenterOffset: matchItm.lowerPart ? 10:10
width: parent.width
height: control.tileSize
height: roundItem.tileSize
//scale: 0.9
color: "white"
border.color: "lightgrey"
border.width: 1
border.width: 0
radius: height * 0.2
Material.elevation: 10
Grid {
columns: app.landscape() ? 2:0
rows: app.landscape() ? 0:2
spacing: app.landscape() ? height * 0.4:0
Column {
spacing: 0
anchors.fill: parent
anchors.margins: matchRect.radius * 0.5
@ -345,42 +402,42 @@ ListView {
delegate: RowLayout {
height: app.landscape() ? parent.height:parent.height / 2 - parent.spacing
width: app.landscape() ? parent.width / 2 - parent.spacing : parent.width
height: parent.height / 2 - parent.spacing
width: parent.width
spacing: app.landscape() ? height * 0.1:height * 0.1
spacing: height * 0.1
Label {
Layout.preferredHeight: parent.height
height: parent.height
Layout.preferredWidth: parent.width * 0.15
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.pixelSize: height * 0.7
fontSizeMode: Text.Fit
minimumPixelSize: 1
Layout.alignment: Layout.Left
text: matchItm.thisMatchData[index] !== undefined ? (parseInt(matchItm.thisMatchData[index]['result_rank0']) < 10 ?matchItm.thisMatchData[index]['result_rank0'] + " ":matchItm.thisMatchData[index]['result_rank0']): ""
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.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
horizontalAlignment: Text.AlignLeft
font.pixelSize: height * 0.5
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'] :"-"
@ -392,9 +449,7 @@ ListView {
height: parent.height
verticalAlignment: Text.AlignVCenter
font.pixelSize: app.landscape() ? height * 0.35 : height * 0.5
Layout.alignment: Layout.Right
font.pixelSize: height * 0.5
text: matchItm.thisMatchData[index] !== undefined && matchItm.thisMatchData[index]['result'+matchItm.thisRound] !== undefined ?
( parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]) ?
@ -408,6 +463,27 @@ ListView {
}
}
}
}
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
}
}
}
}
}
}

View file

@ -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()
}
}
}

View file

@ -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
}
}
]
}

View file

@ -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 <b>is not willing to share results with blueROCK anymore</b>. " +
"Because of this, blueROCK is no longer able to access and display IFSC results.<br><br>" +

View file

@ -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
}
}
}
}

View file

@ -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
}
}
}
}

View file

@ -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,19 +59,40 @@ 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")
Image {
id: photo
property bool ready: photo.status === Image.Ready
anchors.centerIn: parent
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 {
@ -92,6 +100,21 @@ Page {
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
}
}
}
}
}
Column {
@ -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"] + "<br>")
}
@ -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

View file

@ -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 {

View file

@ -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"

View file

@ -21,8 +21,6 @@ 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 "../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: "<html>(<a href=\"" + (partDel.thisData["fed_url"] === undefined ? "":partDel.thisData["fed_url"]).toString() + "\">" + (widgetData[ "display_athlete" ] === "nation" ? partDel.thisData["nation"] : partDel.thisData["federation"]) + "</a>)</html>"
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 {
}
}
}

View file

@ -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"

View file

@ -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

View file

@ -20,5 +20,10 @@
<file>Components/SpeedFlowChart.qml</file>
<file>Components/SwipeGallery.qml</file>
<file>Components/CompetitionCalendarDelegate.qml</file>
<file>Components/SelectorPopup.qml</file>
<file>Components/ResultDelegate.qml</file>
<file>Components/SpeedFlowChartLocker.qml</file>
<file>Components/SpeedFlowChartPopup.qml</file>
<file>Components/BlueRockBadge.qml</file>
</qresource>
</RCC>

View file

@ -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<AppSettings>("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;