Compare commits
57 commits
Author | SHA1 | Date | |
---|---|---|---|
bf617ba72c | |||
5694d61c4b | |||
cd0500c40b | |||
12185337b9 | |||
fe453cd5ac | |||
6644d16a92 | |||
f42d8fb8d7 | |||
e6569694e4 | |||
38dbbb3b32 | |||
ce7fc3d367 | |||
8e4e135f9c | |||
c13d108cff | |||
367807b90d | |||
6ca5b81f6d | |||
05bce5e97d | |||
a238a68638 | |||
6d7009b183 | |||
0ca2725bf3 | |||
447c1135dd | |||
38bdf15f40 | |||
0920ee6f77 | |||
a1ea8a4c65 | |||
21c479cd55 | |||
766da23e26 | |||
496d66a521 | |||
43d98bd43e | |||
30471725d0 | |||
88ebde27f2 | |||
4bf2e2b3b3 | |||
d234474233 | |||
78659fe769 | |||
99f49e0798 | |||
455c4044c2 | |||
ce67df3416 | |||
53460b2091 | |||
2f3f0488da | |||
a026912929 | |||
551c6ffb6d | |||
55a5aac4e1 | |||
21b88575fb | |||
fb17ec52cd | |||
6b91988ac5 | |||
75d7212fd9 | |||
3a735e2cf4 | |||
c92362f07a | |||
3492e6c511 | |||
5cfad624a3 | |||
dabb2473b3 | |||
d33748a06d | |||
1cd7ad6649 | |||
d881b203fa | |||
175c2ea264 | |||
484fb8d7a9 | |||
7fbf90b215 | |||
a2e6cdccb9 | |||
f1d6915c52 | |||
f422aecb77 |
30
.drone.yml
Normal file
|
@ -0,0 +1,30 @@
|
|||
kind: pipeline
|
||||
name: default
|
||||
steps:
|
||||
- name: submodules
|
||||
image: alpine/git
|
||||
commands:
|
||||
- git submodule update --init --recursive
|
||||
|
||||
- name: build app
|
||||
image: itsblue.dev/plugins/qt-android:5.15.5-4
|
||||
settings:
|
||||
qmake_arguments: "CONFIG+=release"
|
||||
androiddeployqt_arguments: "--android-platform android-31 --aab"
|
||||
android_keystore_data:
|
||||
from_secret: android-release-keystore
|
||||
android_keystore_alias: "bmca"
|
||||
android_keystore_store_pass:
|
||||
from_secret: android-release-keystore-password
|
||||
|
||||
- name: release
|
||||
image: plugins/gitea-release
|
||||
settings:
|
||||
base_url: https://itsblue.dev
|
||||
api_key:
|
||||
from_secret: gitea-token
|
||||
files:
|
||||
- outputs/apk/release/android-build-release-signed.apk
|
||||
- outputs/bundle/release/android-build-release.aab
|
||||
when:
|
||||
event: tag
|
3
.gitignore
vendored
|
@ -1,2 +1,5 @@
|
|||
*.pro.user*
|
||||
*.DS_Store
|
||||
.bundle
|
||||
vendor
|
||||
outputs
|
||||
|
|
36
CHANGELOG.md
|
@ -3,8 +3,38 @@ 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.8.0] - 2023-06-05
|
||||
### Added
|
||||
- Show both results in speed quali
|
||||
|
||||
# [0.6.0] - UNRELEASED
|
||||
# [0.7.3] - 2023-05-02
|
||||
### Removed
|
||||
- Im-App purchase on android
|
||||
|
||||
# [0.7.1] - 2022-08-22
|
||||
### Fixed
|
||||
- remove unused ACCESS_FINE_LOCATION permission
|
||||
- missing openssl libraries which lead to an error in the https connection
|
||||
|
||||
# [0.7.0] - 2022-08-14
|
||||
### Added
|
||||
- Startnumbers in speed flowchart
|
||||
|
||||
### Fixed
|
||||
- Sharing the poster on Android
|
||||
- Rendering the speed flowchart when starting at 1/2-Final
|
||||
|
||||
### Changed
|
||||
- The qualification rank is now smaller and grayed out in the speed flowchart
|
||||
|
||||
### Removed
|
||||
- URL handler for app.bluerock.dev, it is not needed
|
||||
|
||||
# [0.6.1] - 2021-10-20
|
||||
### Added
|
||||
- Privacy policy link to comply with google play policies
|
||||
|
||||
# [0.6.0] - 2021-08-07
|
||||
### Changed
|
||||
- The subtitle in results and startlists is now the route name instead of the category name
|
||||
|
||||
|
@ -19,6 +49,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
### Fixed
|
||||
- Rare issue with missing background in boulder result rect
|
||||
|
||||
# [0.5.1] - 2021-07-06
|
||||
### Fixed
|
||||
- In-app purchase
|
||||
|
||||
# [0.05] - 2021-06-07
|
||||
### Changed
|
||||
- the boulder result rect doesn't have a background if there is no result now
|
||||
|
|
3
Gemfile
Normal file
|
@ -0,0 +1,3 @@
|
|||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
218
Gemfile.lock
Normal file
|
@ -0,0 +1,218 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.5)
|
||||
rexml
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.612.0)
|
||||
aws-sdk-core (3.131.5)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.525.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.58.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.114.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.4)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.92.4)
|
||||
faraday (1.10.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.208.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.25.0)
|
||||
google-apis-core (>= 0.7, < 2.a)
|
||||
google-apis-core (0.7.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.13.0)
|
||||
google-apis-core (>= 0.7, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.10.0)
|
||||
google-apis-core (>= 0.7, < 2.a)
|
||||
google-apis-storage_v1 (0.18.0)
|
||||
google-apis-core (>= 0.7, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.2.0)
|
||||
google-cloud-storage (1.37.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.1)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.2.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.5)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.6.1)
|
||||
json (2.6.2)
|
||||
jwt (2.4.1)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
optparse (0.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.6.0)
|
||||
public_suffix (4.0.7)
|
||||
rake (13.0.6)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.17.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.7.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.22.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.19
|
|
@ -2,7 +2,7 @@
|
|||
<manifest package="com.itsblue.blueROCK" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default features. -->
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="de.itsblue.blueROCK.MainActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleInstance">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="de.itsblue.blueROCK.MainActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleInstance" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
@ -75,8 +75,6 @@
|
|||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<!-- Accepts URIs that begin with "https://l.bluerock.dev/” -->
|
||||
<data android:scheme="https" android:host="l.bluerock.dev" android:pathPattern=".*"/>
|
||||
<!-- Accepts URIs that begin with "https://app.bluerock.dev” -->
|
||||
<data android:scheme="https" android:host="app.bluerock.dev" android:pathPattern=".*"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
|
@ -87,4 +85,9 @@
|
|||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
</manifest>
|
||||
|
|
|
@ -260,9 +260,9 @@ public class QShareUtils
|
|||
sendIntent.setType(mimeType);
|
||||
|
||||
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
sendIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
return createCustomChooserAndStartActivity(sendIntent, title, requestId, uri);
|
||||
QtNative.activity().startActivity(Intent.createChooser(sendIntent, null));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean viewFile(String filePath, String title, String mimeType, int requestId) {
|
||||
|
|
10
blueROCK.pro
|
@ -1,7 +1,7 @@
|
|||
QT += quick qml quickcontrols2 purchasing
|
||||
QT += quick qml quickcontrols2
|
||||
CONFIG += c++11
|
||||
|
||||
VERSION = 0.6.0
|
||||
VERSION = 0.8.0
|
||||
TARGET = blueROCK
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
|
@ -89,13 +89,15 @@ android {
|
|||
|
||||
ANDROID_VERSION_NAME = $$VERSION
|
||||
ANDROID_VERSION_CODE = $$droidVersionCode($$ANDROID_VERSION_NAME)
|
||||
ANDROID_TARGET_SDK_VERSION = 29
|
||||
ANDROID_TARGET_SDK_VERSION = 31
|
||||
|
||||
include(/home/dorian/Android/Sdk/android_openssl/openssl.pri)
|
||||
ANDROID_HOME = $$(ANDROID_HOME)
|
||||
include($$ANDROID_HOME/android_openssl/openssl.pri)
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||
}
|
||||
|
||||
ios {
|
||||
QT += purchasing
|
||||
OBJECTIVE_SOURCES += sources/shareUtils/ios/iosshareutils.mm \
|
||||
sources/iospermissionutils.mm \
|
||||
sources/shareUtils/ios/docviewcontroller.mm
|
||||
|
|
2
fastlane/Appfile.android
Normal file
|
@ -0,0 +1,2 @@
|
|||
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
|
||||
package_name("com.itsblue.speedclimbing_stopwatch") # e.g. com.krausefx.app
|
38
fastlane/Fastfile
Normal file
|
@ -0,0 +1,38 @@
|
|||
# This file contains the fastlane.tools configuration
|
||||
# You can find the documentation at https://docs.fastlane.tools
|
||||
#
|
||||
# For a list of all available actions, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/actions
|
||||
#
|
||||
# For a list of all available plugins, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/plugins/available-plugins
|
||||
#
|
||||
|
||||
# Uncomment the line if you want fastlane to automatically update itself
|
||||
# update_fastlane
|
||||
|
||||
default_platform(:android)
|
||||
|
||||
platform :android do
|
||||
desc "Runs all the tests"
|
||||
lane :test do
|
||||
gradle(task: "test")
|
||||
end
|
||||
|
||||
desc "Submit a new Beta Build to Crashlytics Beta"
|
||||
lane :beta do
|
||||
gradle(task: "clean assembleRelease")
|
||||
crashlytics
|
||||
|
||||
# sh "your_script.sh"
|
||||
# You can also use other beta testing services here
|
||||
end
|
||||
|
||||
desc "Deploy a new version to the Google Play"
|
||||
lane :deploy do
|
||||
gradle(task: "clean assembleRelease")
|
||||
upload_to_play_store
|
||||
end
|
||||
end
|
|
@ -4,8 +4,7 @@
|
|||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:l.bluerock.dev</string>
|
||||
<string>applinks:app.bluerock.dev</string>
|
||||
<string>applinks:l.bluerock.dev</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
2
qzxing
|
@ -1 +1 @@
|
|||
Subproject commit cfc728583b867e157bd27e8b7c239c05a081e562
|
||||
Subproject commit 6ea2b31e26db9d43db027ba207f5c73dc9d759fc
|
262
resources/qml/Components/BoulderResultRow.qml
Normal file
|
@ -0,0 +1,262 @@
|
|||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
|
||||
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.thisIndex]["boulder"+(index+1)]
|
||||
var numTrys = widgetData[ "participants" ][partDel.thisIndex]["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 || resultData[2] !== -1) {
|
||||
// if there is a result available -> draw background
|
||||
context.fillStyle = "#b7b7b7";
|
||||
}
|
||||
else {
|
||||
context.fillStyle = "transparent";
|
||||
}
|
||||
|
||||
context.fill();
|
||||
|
||||
// outline
|
||||
context.lineWidth = 1;
|
||||
context.strokeStyle = Material.primaryTextColor;
|
||||
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 = Material.primaryTextColor;
|
||||
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 = Material.primaryTextColor;
|
||||
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
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
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
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
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
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
text: boulderResCv.resultData[0]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
97
resources/qml/Components/GeneralResultRow.qml
Normal file
|
@ -0,0 +1,97 @@
|
|||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
|
||||
Row {
|
||||
id: multiGenResRow
|
||||
|
||||
property bool active: (parseInt(widgetData[ "route_order" ]) === -1) && (generalResRep.model > 0)
|
||||
|
||||
height: parent.height
|
||||
width: active ? parent.width - resultLa.width:0
|
||||
|
||||
enabled: active
|
||||
|
||||
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: Material.primaryTextColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
width: 1
|
||||
height: parent.height
|
||||
|
||||
color: Material.primaryTextColor
|
||||
}
|
||||
|
||||
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.thisIndex]["result"+(generalResRep.routes[index][0])] === undefined ?
|
||||
"":
|
||||
widgetData[ "participants" ][partDel.thisIndex]["result"+(generalResRep.routes[index][0])]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -33,6 +33,19 @@ ColoredItemDelegate {
|
|||
NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: widgetData["route_quota"] !== null && parseInt(widgetData["route_quota"]) - 1 === thisIndex
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.bottom
|
||||
}
|
||||
|
||||
height: 3
|
||||
color: Material.primaryTextColor
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: partDelCol
|
||||
|
||||
|
@ -107,370 +120,29 @@ ColoredItemDelegate {
|
|||
Layout.preferredWidth: partDelCol.showSideBySide ? parent.width * 0.5 : parent.width
|
||||
Layout.preferredHeight: partDelCol.showSideBySide ? 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.thisIndex]["boulder"+(index+1)]
|
||||
var numTrys = widgetData[ "participants" ][partDel.thisIndex]["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 || resultData[2] !== -1) {
|
||||
// if there is a result available -> draw background
|
||||
context.fillStyle = "#b7b7b7";
|
||||
}
|
||||
else {
|
||||
context.fillStyle = "transparent";
|
||||
}
|
||||
|
||||
context.fill();
|
||||
|
||||
// outline
|
||||
context.lineWidth = 1;
|
||||
context.strokeStyle = Material.primaryTextColor;
|
||||
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 = Material.primaryTextColor;
|
||||
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 = Material.primaryTextColor;
|
||||
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
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
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
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
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
|
||||
|
||||
color: "#dd000000"
|
||||
|
||||
text: boulderResCv.resultData[0]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
visible: boulderResRow.active || generalResRow.active || speedQualificationResRow.active || resultLa.active
|
||||
|
||||
BoulderResultRow {
|
||||
id: boulderResRow
|
||||
}
|
||||
|
||||
Row {
|
||||
id: multiGenResRow
|
||||
GeneralResultRow {
|
||||
id: generalResRow
|
||||
}
|
||||
|
||||
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: Material.primaryTextColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
width: 1
|
||||
height: parent.height
|
||||
|
||||
color: Material.primaryTextColor
|
||||
}
|
||||
|
||||
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.thisIndex]["result"+(generalResRep.routes[index][0])] === undefined ?
|
||||
"":
|
||||
widgetData[ "participants" ][partDel.thisIndex]["result"+(generalResRep.routes[index][0])]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
SpeedQualificationResultRow {
|
||||
id: speedQualificationResRow
|
||||
}
|
||||
|
||||
Label {
|
||||
id: resultLa
|
||||
|
||||
property bool acitve: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1
|
||||
property bool active: ( parseInt(widgetData[ "route_num_problems" ]) > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1 && !speedQualificationResRow.active
|
||||
|
||||
width: enabled ? parent.width * 0.25:0
|
||||
height: enabled ? parent.height:0
|
||||
|
||||
enabled: ( boulderResRep.model > 0 || widgetData["discipline"] !== "boulder" ) && parseInt(widgetData[ "route_order" ]) > -1
|
||||
enabled: active
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QZXing 3.1
|
||||
import QZXing 3.3
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
Dialog {
|
||||
|
|
289
resources/qml/Components/SpeedFlowChart.js
Normal file
|
@ -0,0 +1,289 @@
|
|||
.pragma library
|
||||
|
||||
/**
|
||||
* Function to extract results from PartiipantFromApi
|
||||
* @param {ParticipantFromApi} fromApi
|
||||
* @return {Result[]}
|
||||
*/
|
||||
function _extractResults(fromApi) {
|
||||
const results = Array(7);
|
||||
|
||||
const existingResults = Object.keys(fromApi).filter(key =>
|
||||
key.match(/result[0-9]/),
|
||||
);
|
||||
|
||||
for (const result of existingResults) {
|
||||
let roundNumber = 0;
|
||||
const match = result.match(/result([0-9])/);
|
||||
if (match !== undefined && match !== null) {
|
||||
roundNumber = parseInt(match[1]);
|
||||
}
|
||||
if (roundNumber < 0) {
|
||||
continue;
|
||||
}
|
||||
results[roundNumber] = {
|
||||
rank: parseInt(fromApi[`result_rank${roundNumber}`] as string),
|
||||
result: fromApi[`result${roundNumber}`] as string,
|
||||
};
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to clean up participants from the api
|
||||
* @param {ParticipantFromApi} fromApi
|
||||
* @return {Participant}
|
||||
*/
|
||||
function participantFromApiParticipant(
|
||||
fromApi,
|
||||
) {
|
||||
const results = _extractResults(fromApi);
|
||||
|
||||
return {
|
||||
id: fromApi.PerId,
|
||||
firstName: fromApi.firstname,
|
||||
lastName: fromApi.lastname,
|
||||
results: results,
|
||||
overallRank: fromApi.result_rank,
|
||||
startNumber: parseInt(fromApi.start_number),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} roundNumber
|
||||
* @param {RouteNames} routeNames
|
||||
* @return {string | undefined}
|
||||
*/
|
||||
function getRoundName(
|
||||
roundNumber,
|
||||
routeNames,
|
||||
) {
|
||||
if (roundNumber < 2 || roundNumber > 6) return undefined;
|
||||
|
||||
return routeNames[roundNumber];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} name
|
||||
* @return {number}
|
||||
*/
|
||||
function getRoundRank(name) {
|
||||
const match = name.match(/1\/([842])/);
|
||||
if (match === undefined || match === null || match.length !== 2) {
|
||||
return 2;
|
||||
}
|
||||
return parseInt(match[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SpeedRoundPair} pair
|
||||
* @param {number} roundIndex
|
||||
*/
|
||||
function computeWinnerOfPair(pair, roundIndex) {
|
||||
if (
|
||||
!(pair.laneA && pair.laneA.participant.results[roundIndex] && pair.laneA.participant.results[roundIndex].rank) ||
|
||||
!(pair.laneB && pair.laneB.participant.results[roundIndex] && pair.laneB.participant.results[roundIndex].rank)
|
||||
)
|
||||
return;
|
||||
|
||||
pair.laneA.result = pair.laneA.participant.results[roundIndex];
|
||||
pair.laneB.result = pair.laneB.participant.results[roundIndex];
|
||||
|
||||
if (pair.winner === undefined) {
|
||||
pair.winner =
|
||||
pair.laneA.participant.results[roundIndex].rank >
|
||||
pair.laneB.participant.results[roundIndex].rank
|
||||
? 'B'
|
||||
: 'A';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SpeedRoundPair} pair
|
||||
* @param {number} roundNumber
|
||||
* @return {Participant | undefined}
|
||||
*/
|
||||
function getWinnerOfPair(
|
||||
pair,
|
||||
roundNumber,
|
||||
) {
|
||||
computeWinnerOfPair(pair, roundNumber);
|
||||
return {
|
||||
['A']: pair.laneA ? pair.laneA.participant : undefined,
|
||||
['B']: pair.laneB ? pair.laneB.participant : undefined,
|
||||
['']: undefined,
|
||||
}[pair.winner ? pair.winner:''];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SpeedRoundPair} pair
|
||||
* @param {number} roundNumber
|
||||
* @return {Participant | undefined}
|
||||
*/
|
||||
function getLooserOfPair(
|
||||
pair,
|
||||
roundNumber,
|
||||
) {
|
||||
computeWinnerOfPair(pair, roundNumber);
|
||||
return {
|
||||
['A']: pair.laneB ? pair.laneB.participant : undefined,
|
||||
['B']: pair.laneA ? pair.laneA.participant : undefined,
|
||||
['']: undefined,
|
||||
}[pair.winner ? pair.winner:''];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} roundIndex index of the new round
|
||||
* @param {string} roundName name of the new round
|
||||
* @param {SpeedRound} previousRound
|
||||
* @param {number} roundRank
|
||||
* @param {boolean} takeLooser
|
||||
* @return {SpeedRound}
|
||||
*/
|
||||
function computeRoundFromPreviousRound(
|
||||
roundIndex,
|
||||
roundName,
|
||||
previousRound,
|
||||
roundRank,
|
||||
takeLooser = false,
|
||||
) {
|
||||
const getAdvancingParticipant = takeLooser
|
||||
? getLooserOfPair
|
||||
: getWinnerOfPair;
|
||||
const nextRoundPairs = new Array(roundRank / 2).fill(0).map((_, i) => {
|
||||
const laneAParticipant = getAdvancingParticipant(
|
||||
previousRound.pairs[i * 2],
|
||||
previousRound.roundIndex,
|
||||
);
|
||||
const laneBParticipant = getAdvancingParticipant(
|
||||
previousRound.pairs[i * 2 + 1],
|
||||
previousRound.roundIndex,
|
||||
);
|
||||
|
||||
return {
|
||||
laneA:
|
||||
laneAParticipant === undefined
|
||||
? undefined
|
||||
: {
|
||||
participant: laneAParticipant,
|
||||
},
|
||||
laneB:
|
||||
laneBParticipant === undefined
|
||||
? undefined
|
||||
: {
|
||||
participant: laneBParticipant,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
pairs: nextRoundPairs,
|
||||
roundIndex: roundIndex,
|
||||
roundName: roundName,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SpeedCompetitionCategoryResult} result The result to process
|
||||
* @return {SpeedFlowchartResult}
|
||||
*/
|
||||
function convertResultsToSpeedFlowchartResult(
|
||||
result,
|
||||
) {
|
||||
const rounds = [];
|
||||
const convertedParticipants = result.participants
|
||||
.map(fromApi => participantFromApiParticipant(fromApi))
|
||||
// sort by qualification result
|
||||
.sort((a, b) => a.results[0].rank - b.results[0].rank);
|
||||
|
||||
const roundIndices = Object.keys(result.route_names)
|
||||
.map(number => parseInt(number))
|
||||
.filter(number => number > 0);
|
||||
|
||||
// process first round
|
||||
const firstRoundName = getRoundName(roundIndices[0], result.route_names);
|
||||
const tmpRoundName = getRoundName(roundIndices[0], result.route_names);
|
||||
const firstRoundRank = getRoundRank(
|
||||
tmpRoundName ? tmpRoundName:'',
|
||||
);
|
||||
|
||||
const getOpponent = (ofRank) => {
|
||||
return convertedParticipants[firstRoundRank * 2 - 1 - ofRank];
|
||||
};
|
||||
|
||||
// Should be:
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7
|
||||
// - 1,16, 8, 9, 4,13, 5,12, 2,15, 7,10, 3,14, 6,11
|
||||
// - 1, 8, 4, 5, 2, 7, 3, 6 for firstRoundNumber=8
|
||||
// - 1, 4, 2, 3 for firstRoundNumber=4
|
||||
// - 1, 2 for firstRoundNumber=2
|
||||
// TODO: come up with a proper alogorithm maybe
|
||||
const ranksOfLaneAInOrder = [
|
||||
[1, 2],
|
||||
[1, 4, 2, 3],
|
||||
[1, 8, 4, 5, 2, 7, 3, 6],
|
||||
][Math.floor(firstRoundRank / 4)];
|
||||
|
||||
const firstRoundPairs = ranksOfLaneAInOrder.map(rank => {
|
||||
return {
|
||||
laneA: { participant: convertedParticipants[rank - 1] },
|
||||
laneB: { participant: getOpponent(rank - 1) },
|
||||
};
|
||||
});
|
||||
|
||||
const firstRound = {
|
||||
pairs: firstRoundPairs,
|
||||
roundIndex: roundIndices[0],
|
||||
roundName: firstRoundName,
|
||||
};
|
||||
|
||||
rounds.push(firstRound);
|
||||
|
||||
// compute following rounds
|
||||
let roundIndex = roundIndices[1];
|
||||
for (let roundRank = firstRoundRank; roundRank > 2; roundRank /= 2) {
|
||||
rounds.push(
|
||||
computeRoundFromPreviousRound(
|
||||
roundIndex,
|
||||
result.route_names[roundIndex] ? result.route_names[roundIndex]:'',
|
||||
rounds[rounds.length - 1],
|
||||
roundRank,
|
||||
),
|
||||
);
|
||||
roundIndex++;
|
||||
}
|
||||
|
||||
// compute final and semi final
|
||||
const semifinalRoundIndex = roundIndex++;
|
||||
const semifinal = computeRoundFromPreviousRound(
|
||||
semifinalRoundIndex,
|
||||
result.route_names[semifinalRoundIndex] ? result.route_names[semifinalRoundIndex]:'',
|
||||
rounds[rounds.length - 1],
|
||||
2,
|
||||
true,
|
||||
);
|
||||
computeWinnerOfPair(semifinal.pairs[0], semifinalRoundIndex);
|
||||
rounds.push(semifinal);
|
||||
|
||||
const finalRoundIndex = roundIndex;
|
||||
const final = computeRoundFromPreviousRound(
|
||||
finalRoundIndex,
|
||||
result.route_names[finalRoundIndex] ? result.route_names[finalRoundIndex]:'',
|
||||
rounds[rounds.length - 2],
|
||||
2,
|
||||
);
|
||||
computeWinnerOfPair(final.pairs[0], finalRoundIndex);
|
||||
rounds.push(final);
|
||||
|
||||
return {
|
||||
rounds: rounds,
|
||||
};
|
||||
}
|
|
@ -22,6 +22,8 @@ import QtQuick.Layouts 1.3
|
|||
import QtQuick.Controls.Material 2.1
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import "SpeedFlowChart.js" as SpeedFlowChart
|
||||
|
||||
Item {
|
||||
id: control
|
||||
|
||||
|
@ -32,8 +34,6 @@ Item {
|
|||
property int refreshes: 0
|
||||
property int roundRefreshes: 1
|
||||
|
||||
property int roundCount: 0
|
||||
|
||||
onFlowchartDataChanged: {
|
||||
prepareData()
|
||||
}
|
||||
|
@ -42,187 +42,26 @@ Item {
|
|||
|
||||
if(!control.enabled || control.flowchartData === undefined || control.flowchartData['route_names'] === undefined)
|
||||
return
|
||||
/*refreshes += 1
|
||||
|
||||
if(refreshes > 2){
|
||||
roundRefreshes += 1
|
||||
}
|
||||
|
||||
console.log("refreshes: " + refreshes + " rounds: " + roundRefreshes)
|
||||
|
||||
// create competition like data (just testing)
|
||||
for(var part in flowchartData['participants']){
|
||||
if(flowchartData['participants'].hasOwnProperty(part)){
|
||||
|
||||
for(var r = 2 + roundRefreshes; r < 7; r++){
|
||||
delete flowchartData['participants'][part]["result"+r]
|
||||
delete flowchartData['participants'][part]["result_rank"+r]
|
||||
}
|
||||
|
||||
if(parseInt(flowchartData['participants'][part]["result_rank0"]) > 14 + refreshes) {
|
||||
delete flowchartData['participants'][part]["result_rank2"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
delete flowchartData['route_names'][2]
|
||||
delete flowchartData['route_names'][3]
|
||||
delete flowchartData['route_names'][4]
|
||||
delete flowchartData['route_names'][5]
|
||||
delete flowchartData['route_names'][6]
|
||||
*/
|
||||
|
||||
//flowchartData['route_names'] = flowchartData['route_names'].slice(0,)
|
||||
|
||||
// array to store the restructured data
|
||||
var allData = []
|
||||
control.allFlowchartData = []
|
||||
|
||||
control.rounds = Object.keys(control.flowchartData['route_names']).length > 2 ? control.flowchartData['route_names']["2"].includes("8") ? 2:1 : 0
|
||||
|
||||
//console.log(JSON.stringify(flowchartData['route_names']))
|
||||
|
||||
for(var round in flowchartData['route_names']){
|
||||
if(flowchartData['route_names'].hasOwnProperty(round) && parseInt(round) >= 0){
|
||||
//console.log(round)
|
||||
|
||||
if(parseInt(round) === 0){
|
||||
// this is the first round
|
||||
|
||||
// find pairs (always wors vs. best (1-16; 1-15; ...)) (they are sorted by the rank of the better athlete (1-2-3-4-5-6-7-8)
|
||||
|
||||
var qualificationResults = []
|
||||
|
||||
for(var x = 0; x < flowchartData['participants'].length; x++){
|
||||
qualificationResults.push(flowchartData['participants'][x])
|
||||
}
|
||||
|
||||
qualificationResults.sort(function(a, b) {
|
||||
return parseInt(a["result_rank0"]) - parseInt(b["result_rank0"]);
|
||||
});
|
||||
|
||||
var nextRoundPairs = []
|
||||
var totalMatches = (parseInt(Object.keys(control.flowchartData['route_names']).length > 2 ? control.flowchartData['route_names']["2"].includes("8") ? 2:1 : 0) + 2)
|
||||
var nextRoundMatches = Math.pow(2, totalMatches-1)
|
||||
|
||||
for(var i = 0; i < nextRoundMatches; i++){
|
||||
nextRoundPairs.push([qualificationResults[i], qualificationResults[nextRoundMatches*2-i-1]])
|
||||
}
|
||||
|
||||
// build second round pairs (sorted by the rank of the better athlete and worst vs. best (1-8; 2-7; ... ))
|
||||
|
||||
var sortedFirstRoundPairs = []
|
||||
|
||||
for(i = 0; i < nextRoundMatches; i += 1){
|
||||
sortedFirstRoundPairs.push([nextRoundPairs.shift(), nextRoundPairs.pop()])
|
||||
}
|
||||
|
||||
// sort these pairs (containing two pairs of athletes) by the rank of the better athlete (1-4;2-3)
|
||||
|
||||
var finalSortedFirstRoundPairs = []
|
||||
|
||||
for(i=0; i < nextRoundMatches/4; i++){
|
||||
finalSortedFirstRoundPairs.push(sortedFirstRoundPairs[i])
|
||||
finalSortedFirstRoundPairs.push(sortedFirstRoundPairs[nextRoundMatches/2-i-1])
|
||||
}
|
||||
|
||||
// convert the list of pairs of pairs of athletes back to a single list of pairs of athletes
|
||||
|
||||
var finalFirstRoundPairs = []
|
||||
|
||||
for(i=0; i < finalSortedFirstRoundPairs.length; i++){
|
||||
finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][0])
|
||||
finalFirstRoundPairs.push(finalSortedFirstRoundPairs[i][1])
|
||||
}
|
||||
|
||||
// push the first round to all data
|
||||
finalFirstRoundPairs.push(2)
|
||||
finalFirstRoundPairs.push(flowchartData['route_names'][2])
|
||||
allData.push(finalFirstRoundPairs)
|
||||
|
||||
}
|
||||
else if(parseInt(round) > 0 ){
|
||||
// this is not the first round
|
||||
|
||||
var nextRound = []
|
||||
|
||||
// only used when the current round is the 1/2 final
|
||||
var smallFinal = []
|
||||
var Final = []
|
||||
|
||||
for(var i = 0; i < allData[allData.length-1].length-2; i+=1){
|
||||
|
||||
var thisPair = allData[allData.length-1][i]
|
||||
var thisWinner
|
||||
var thisLooser
|
||||
var thisWinnerIsFirstOfNewPair = i%2 === 0
|
||||
|
||||
if(thisPair[0] === undefined || thisPair[1] === undefined){
|
||||
continue
|
||||
}
|
||||
|
||||
if(thisWinnerIsFirstOfNewPair){
|
||||
nextRound.push([])
|
||||
}
|
||||
|
||||
//thisPair[0]["result_rank"] = thisPair[0]["result_rank"+round]
|
||||
//thisPair[1]["result_rank"] = thisPair[1]["result_rank"+round]
|
||||
|
||||
if(parseInt(thisPair[0]["result_rank"+round]) < parseInt(thisPair[1]["result_rank"+round])){
|
||||
thisWinner = thisPair[0]
|
||||
thisLooser = thisPair[1]
|
||||
}
|
||||
else if(parseInt(thisPair[0]["result_rank"+round]) > parseInt(thisPair[1]["result_rank"+round])) {
|
||||
thisWinner = thisPair[1]
|
||||
thisLooser = thisPair[0]
|
||||
}
|
||||
else {
|
||||
// no result yet!!
|
||||
//console.log("got no winner yet, rank 0: " + thisPair[0]["result_rank"+round] + " rank 1: " + thisPair[1]["result_rank"+round])
|
||||
continue
|
||||
}
|
||||
|
||||
//console.log(thisWinner['firstname']+" has won in round " + round)
|
||||
|
||||
if(round - control.rounds === 2){
|
||||
// if we are in the 1/2 final
|
||||
|
||||
Final.push(thisWinner)
|
||||
smallFinal.push(thisLooser)
|
||||
}
|
||||
else {
|
||||
nextRound[nextRound.length-1].push(thisWinner)
|
||||
}
|
||||
}
|
||||
|
||||
if(smallFinal.length > 0 && Final.length > 0){
|
||||
|
||||
// Final
|
||||
allData.push([Final, parseInt(round)+2, flowchartData['route_names'][String(parseInt(round)+2)] + " / " + flowchartData['route_names'][String(parseInt(round)+1)] ])
|
||||
// small Final
|
||||
allData.push([smallFinal, parseInt(round)+1])
|
||||
|
||||
//break
|
||||
}
|
||||
else {
|
||||
nextRound.push(parseInt(round) + 1 )
|
||||
nextRound.push(flowchartData['route_names'][parseInt(round) + 1])
|
||||
allData.push(nextRound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control.allFlowchartData = allData
|
||||
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))
|
||||
var flowchartResult = SpeedFlowChart.convertResultsToSpeedFlowchartResult(control.flowchartData)
|
||||
const l = flowchartResult.rounds.length;
|
||||
const dummy = { dummy: true };
|
||||
flowchartResult.rounds[l - 2].pairs = [
|
||||
dummy,
|
||||
...flowchartResult.rounds[l - 1].pairs,
|
||||
...flowchartResult.rounds[l - 2].pairs,
|
||||
];
|
||||
flowchartResult.rounds[l - 2].roundName = `${
|
||||
flowchartResult.rounds[l - 1].roundName
|
||||
} / ${flowchartResult.rounds[l - 2].roundName}`;
|
||||
flowchartResult.rounds.pop();
|
||||
control.allFlowchartData = flowchartResult
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: roundListView
|
||||
|
||||
property int columnWidth: height * 0.3
|
||||
property int columnWidth: height * 0.4
|
||||
property int columnHeight: height
|
||||
|
||||
anchors {
|
||||
|
@ -238,19 +77,18 @@ Item {
|
|||
orientation: ListView.LeftToRight
|
||||
boundsBehavior: ListView.StopAtBounds
|
||||
|
||||
model: control.roundCount
|
||||
model: control.allFlowchartData.rounds.length
|
||||
|
||||
delegate: Item {
|
||||
id: roundItem
|
||||
|
||||
property int thisIndex: index
|
||||
property int thisRound: thisRoundIsValid ? control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-2]:-1
|
||||
property bool thisRoundIsValid: control.allFlowchartData !== undefined
|
||||
&& control.allFlowchartData[roundItem.thisIndex] !== undefined
|
||||
&& control.allFlowchartData[roundItem.thisIndex].length > 2
|
||||
property var thisData: control.allFlowchartData.rounds[thisIndex]
|
||||
|
||||
property bool thisIsLastRound: thisIndex === control.roundCount - 1
|
||||
property bool thisIsSemiFinal: thisIndex === control.roundCount - 2 && rectRep.model === 2
|
||||
property bool thisRoundIsValid: true
|
||||
|
||||
property bool thisIsLastRound: thisIndex === roundListView.model - 1
|
||||
property bool thisIsSemiFinal: thisIndex === roundListView.model - 2
|
||||
|
||||
property int tileSize: (roundItem.height / 8 - roundNameLa.height) * 1.45
|
||||
|
||||
|
@ -272,50 +110,26 @@ Item {
|
|||
font.pixelSize: height * 0.6
|
||||
minimumPixelSize: 1
|
||||
font.bold: true
|
||||
text: roundItem.thisRoundIsValid
|
||||
&& control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] !== undefined ?
|
||||
control.allFlowchartData[roundItem.thisIndex][control.allFlowchartData[roundItem.thisIndex].length-1] :
|
||||
text: roundItem.thisData.roundName ?
|
||||
roundItem.thisData.roundName :
|
||||
"-"
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: rectRep
|
||||
model: Math.max( Math.pow(2, control.roundCount-1) * Math.pow(0.5, (roundItem.thisIndex)), 2)
|
||||
model: roundItem.thisData.pairs.length
|
||||
|
||||
delegate: Item {
|
||||
id: matchItm
|
||||
|
||||
property bool lowerPart: (index%2 > 0)
|
||||
|
||||
property var matchData: roundItem.thisRoundIsValid ?
|
||||
control.allFlowchartData[
|
||||
thisIsSmallFinal ?
|
||||
roundItem.thisIndex+1 :
|
||||
roundItem.thisIndex
|
||||
][ thisIsSmallFinal ? 0:matchItm.thisIndex]:
|
||||
undefined
|
||||
|
||||
property var thisData: roundItem.thisData.pairs[index]
|
||||
property int thisIndex: index
|
||||
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: 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: !roundItem.thisIsLastRound ?
|
||||
(roundItem.height - roundNameLa.height) / rectRep.model - roundCol.spacing :
|
||||
(thisIsFinal ?
|
||||
(roundItem.height - roundNameLa.height) * 0.5 + roundItem.tileSize * 0.5 :
|
||||
(roundItem.height - roundNameLa.height) - (roundItem.height - roundNameLa.height) * 0.5 + roundItem.tileSize * 0.5
|
||||
)
|
||||
|
||||
height: (roundItem.height - roundNameLa.height) / rectRep.model - roundCol.spacing
|
||||
width: roundItem.width
|
||||
|
||||
onMatchDataChanged: {
|
||||
onThisDataChanged: {
|
||||
fadeInPa.start()
|
||||
}
|
||||
|
||||
|
@ -365,6 +179,7 @@ Item {
|
|||
|
||||
RectangularGlow {
|
||||
id: effect
|
||||
visible: matchRect.visible
|
||||
anchors.fill: matchRect
|
||||
glowRadius: 0
|
||||
spread: 0
|
||||
|
@ -381,13 +196,12 @@ Item {
|
|||
bottom: matchItm.thisIsFinal ? parent.bottom:undefined
|
||||
}
|
||||
|
||||
//anchors.verticalCenterOffset: matchItm.lowerPart ? 10:10
|
||||
visible: !matchItm.thisData.dummy
|
||||
|
||||
width: parent.width
|
||||
height: roundItem.tileSize
|
||||
//scale: 0.9
|
||||
|
||||
color: Material.dialogColor
|
||||
border.color: "lightgrey"
|
||||
border.width: 0
|
||||
radius: height * 0.2
|
||||
|
||||
Column {
|
||||
|
@ -401,6 +215,13 @@ Item {
|
|||
model: 2
|
||||
|
||||
delegate: RowLayout {
|
||||
id: laneRow
|
||||
|
||||
property var thisData: index === 0 ? matchItm.thisData.laneA : matchItm.thisData.laneB
|
||||
property var participant: thisData ? thisData.participant : undefined
|
||||
property var result: thisData ? thisData.result : undefined
|
||||
property var lane: index === 0 ? "A":"B"
|
||||
property bool isWinner: matchItm.thisData.winner === laneRow.lane
|
||||
|
||||
height: parent.height / 2 - parent.spacing
|
||||
width: parent.width
|
||||
|
@ -409,53 +230,90 @@ Item {
|
|||
|
||||
Label {
|
||||
Layout.preferredHeight: parent.height
|
||||
Layout.preferredWidth: parent.width * 0.15
|
||||
Layout.preferredWidth: parent.width * 0.08
|
||||
|
||||
height: parent.height
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.pixelSize: height * 0.7
|
||||
fontSizeMode: Text.Fit
|
||||
minimumPixelSize: 1
|
||||
font.pixelSize: height * 0.4
|
||||
font.bold: true
|
||||
opacity: 0.7
|
||||
|
||||
text: matchItm.thisMatchData[index] !== undefined ?
|
||||
text: laneRow.participant ?
|
||||
(
|
||||
parseInt(matchItm.thisMatchData[index]['result_rank0']) < 10 ?
|
||||
matchItm.thisMatchData[index]['result_rank0'] + " ":
|
||||
matchItm.thisMatchData[index]['result_rank0']
|
||||
laneRow.participant.results[0].rank < 10 ?
|
||||
laneRow.participant.results[0].rank + " ":
|
||||
laneRow.participant.results[0].rank
|
||||
):
|
||||
""
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.preferredHeight: parent.height
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: parent.width * 0.6
|
||||
|
||||
|
||||
height: parent.height
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.pixelSize: height * 0.5
|
||||
font.bold: matchItm.winnerIndex === index
|
||||
font.bold: laneRow.isWinner
|
||||
elide: "ElideRight"
|
||||
|
||||
color: matchItm.winnerIndex === index ? Material.color(Material.Green):Material.primaryTextColor
|
||||
color: laneRow.isWinner ? Material.color(Material.Green):Material.primaryTextColor
|
||||
|
||||
text: matchItm.thisMatchData[index] !== undefined ? matchItm.thisMatchData[index]['firstname'] + " " + matchItm.thisMatchData[index]['lastname'] :"-"
|
||||
text: laneRow.participant ? laneRow.participant.firstName + " " + laneRow.participant.lastName :"-"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredHeight: parent.height * 0.8
|
||||
Layout.preferredWidth: parent.width * 0.13
|
||||
|
||||
visible: laneRow.participant && laneRow.participant.startNumber ? true:false
|
||||
radius: height / 2
|
||||
|
||||
border.width: parent.height * 0.03
|
||||
border.color: Material.frameColor
|
||||
color: "transparent"
|
||||
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
|
||||
padding: parent.parent.height * 0.1
|
||||
leftPadding: padding * 2
|
||||
rightPadding: leftPadding
|
||||
|
||||
font.pixelSize: parent.height * 0.5
|
||||
font.bold: true
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
|
||||
text: laneRow.participant ? laneRow.participant.startNumber:""
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.preferredHeight: parent.height
|
||||
Layout.preferredWidth: parent.width * 0.15
|
||||
|
||||
height: parent.height
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: height * 0.5
|
||||
font.bold: laneRow.isWinner
|
||||
|
||||
text: matchItm.thisMatchData[index] !== undefined && matchItm.thisMatchData[index]['result'+matchItm.thisRound] !== undefined ?
|
||||
( parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]) ?
|
||||
(parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]).toFixed(2))
|
||||
: matchItm.thisMatchData[index]['result'+matchItm.thisRound] )
|
||||
: "-"
|
||||
color: laneRow.isWinner ? Material.color(Material.Green):Material.primaryTextColor
|
||||
|
||||
text: laneRow.result ? laneRow.result.result : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -478,7 +336,7 @@ Item {
|
|||
id: blueRockBadgeComponent
|
||||
|
||||
BlueRockBadge {
|
||||
width: roundItem.width
|
||||
width: roundItem.width * 0.8
|
||||
height: width * 0.25
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ Rectangle {
|
|||
|
||||
// always unlock in debug mode
|
||||
//property bool unlocked: QT_DEBUG || appSettings.read("speedBackendPurchase") === "1"
|
||||
property bool unlocked: appSettings.read("speedBackendPurchase") === "1"
|
||||
property bool unlocked: Qt.platform.os !== "iso" || appSettings.read("speedBackendPurchase") === "1"
|
||||
|
||||
Component.onCompleted: {
|
||||
console.warn("unlocked:", appSettings.read("speedBackendPurchase"))
|
||||
|
|
79
resources/qml/Components/SpeedQualificationResultRow.qml
Normal file
|
@ -0,0 +1,79 @@
|
|||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
|
||||
Row {
|
||||
id: control
|
||||
|
||||
property bool active: (parseInt(widgetData[ "route_order" ]) === 0) && (widgetData["discipline"] === "speed") && (thisData["result_l"] !== undefined || thisData["result_r"] !== undefined)
|
||||
property var thisData: widgetData["participants"][partDel.thisIndex]
|
||||
|
||||
height: parent.height
|
||||
width: active ? parent.width - resultLa.width:0
|
||||
|
||||
enabled: active
|
||||
|
||||
Repeater {
|
||||
id: generalResRep
|
||||
|
||||
model: 2
|
||||
|
||||
delegate: Item {
|
||||
id: boulderGenResItm
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
width: parent.width / ( generalResRep.model )
|
||||
height: parent.height
|
||||
|
||||
visible: control.active
|
||||
|
||||
Rectangle {
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
width: 1
|
||||
height: parent.height
|
||||
|
||||
visible: index === 0
|
||||
|
||||
color: Material.primaryTextColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
width: 1
|
||||
height: parent.height
|
||||
|
||||
color: Material.primaryTextColor
|
||||
}
|
||||
|
||||
Label {
|
||||
id: boulderGenResLa
|
||||
|
||||
property string thisKey: "result_"+(["l", "r"][index])
|
||||
|
||||
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
|
||||
font.bold: thisData["result_l"] !== undefined && thisData["result_r"] !== undefined && thisData[thisKey] === thisData["result"]
|
||||
|
||||
text: (control.thisData[thisKey] !== undefined && parseInt(control.thisData[thisKey]) > 0) ? control.thisData[thisKey] : ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QZXing 3.1
|
||||
import QZXing 3.3
|
||||
import QtMultimedia 5.12
|
||||
import QtQuick.Shapes 1.12
|
||||
import QtQuick.Controls.Material 2.12
|
||||
|
|
|
@ -170,7 +170,8 @@ Page {
|
|||
DisclaimerDialog {
|
||||
id: aboutBluerockDisclaimerDialog
|
||||
Material.theme: root.Material.theme
|
||||
title: "blueROCK v" + APP_VERSION + "<br>By <a href=\"https://itsblue.de\">Itsblue Development</a>"
|
||||
//% "privacy policy"
|
||||
title: "blueROCK v" + APP_VERSION + "<br>By <a href=\"https://itsblue.de\">Itsblue Development</a>, <a href=\"https://itsblue.de/apps/bluerock/privacypolicy.html\">" + qsTrId("#privacyPolicy") + "</a>"
|
||||
//% "This app was built using the <a href='https://qt.io'>Qt Framework</a> licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>, the source code can be found <a href='https://itsblue.dev/blueROCK/app'>here</a>.<br><br>Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>."
|
||||
content: qsTrId("#aboutBluerockDisclaimer")
|
||||
}
|
||||
|
|
|
@ -190,17 +190,6 @@ Window {
|
|||
|
||||
Material.theme: appSettings.read("darkTheme") === "true" ? Material.Dark:Material.Light
|
||||
|
||||
Component.onCompleted: {
|
||||
//loadingDl.open()
|
||||
//app.openAthlete() // dorian: 53139 , rustam: 6933 , helen: 53300
|
||||
//openWidget({nation:'GER'})
|
||||
//mainStack.push("Pages/AthleteSearchPage.qml")
|
||||
//openWidget({comp: 11651, cat: 26})
|
||||
//openWidget({person: 6623})
|
||||
//console.log(JSON.stringify(serverConn.getParamsFromUrl("")))
|
||||
//openWidgetFromUrl("https://l.bluerock.dev/?comp=11501&cat=GER_M")
|
||||
}
|
||||
|
||||
FontLoader {
|
||||
id: fa5solid
|
||||
source: "qrc:/fonts/fa5solid.otf"
|
||||
|
|
|
@ -31,5 +31,9 @@
|
|||
<file>Components/SharePopup.qml</file>
|
||||
<file>Pages/QrCodeScanPage.qml</file>
|
||||
<file>Components/MovingLabel.qml</file>
|
||||
<file>Components/SpeedFlowChart.js</file>
|
||||
<file>Components/BoulderResultRow.qml</file>
|
||||
<file>Components/GeneralResultRow.qml</file>
|
||||
<file>Components/SpeedQualificationResultRow.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
BIN
resources/shared/badge.xcf
Normal file
Before Width: | Height: | Size: 146 KiB |
Before Width: | Height: | Size: 257 KiB |
Before Width: | Height: | Size: 227 KiB |
Before Width: | Height: | Size: 265 KiB |
Before Width: | Height: | Size: 212 KiB |
Before Width: | Height: | Size: 217 KiB |
Before Width: | Height: | Size: 214 KiB |
BIN
resources/shared/screenshots/screenshot-1.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
resources/shared/screenshots/screenshot-2.jpg
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
resources/shared/screenshots/screenshot-3.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
resources/shared/screenshots/screenshot-4.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
resources/shared/screenshots/screenshot-5.jpg
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
resources/shared/screenshots/screenshot-6.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
|
@ -127,8 +127,13 @@
|
|||
<source>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>You can find current IFSC results <a href="https://ifsc.results.info">over here</a> and on <a href="https://ifsc-climbing.org">their website</a>.</source>
|
||||
<translation>Leider hat die IFSC den Zugang zu ihren Ergebnissen eingeschränkt <b>und ist nicht mehr bereit, Ergebnisse mit blueROCK zu teilen</b>. Daher ist blueROCK nicht länger in der Lage auf IFSC Ergebnisse zuzugriefen und diese anzuzeigen.<br><br>Aktuelle IFSC Ergebnisse finden sich <a href="https://ifsc.results.info">hier</a> und auf der <a href="https://ifsc-climbing.org">IFSC Webseite</a>.</translation>
|
||||
</message>
|
||||
<message id="#privacyPolicy">
|
||||
<location filename="../qml/Pages/StartPage.qml" line="174"/>
|
||||
<source>privacy policy</source>
|
||||
<translation>Datenschutzerklärung</translation>
|
||||
</message>
|
||||
<message id="#aboutBluerockDisclaimer">
|
||||
<location filename="../qml/Pages/StartPage.qml" line="175"/>
|
||||
<location filename="../qml/Pages/StartPage.qml" line="176"/>
|
||||
<source>This app was built using the <a href='https://qt.io'>Qt Framework</a> licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>, the source code can be found <a href='https://itsblue.dev/blueROCK/app'>here</a>.<br><br>Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>.</source>
|
||||
<oldsource>This app was built using the <a href='https://qt.io'>Qt Framework</a> licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>, the source code can be found <a href='https://itsblue.dev/dorian/blueROCK/'>here</a>.<br><br>Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>.</oldsource>
|
||||
<translation>Diese App wurde unter Verwendung des <a href='https://qt.io'>Qt Frameworks</a> unter der <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 Lizenz</a> erstellt.<br><br>Diese App ist Open-source und lizensiert unter der <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 Lizenz</a>. Der Sourcecode findet sich <a href='https://itsblue.dev/blueROCK/app'>hier</a>.<br><br>Die Ergebnisse und Ranglisten werden von <a href='http://www.digitalROCK.de'>digital ROCK</a> zur Verfügung gestellt.</translation>
|
||||
|
|
|
@ -123,8 +123,13 @@
|
|||
<source>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>You can find current IFSC results <a href="https://ifsc.results.info">over here</a> and on <a href="https://ifsc-climbing.org">their website</a>.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message id="#privacyPolicy">
|
||||
<location filename="../qml/Pages/StartPage.qml" line="174"/>
|
||||
<source>privacy policy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message id="#aboutBluerockDisclaimer">
|
||||
<location filename="../qml/Pages/StartPage.qml" line="175"/>
|
||||
<location filename="../qml/Pages/StartPage.qml" line="176"/>
|
||||
<source>This app was built using the <a href='https://qt.io'>Qt Framework</a> licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>, the source code can be found <a href='https://itsblue.dev/blueROCK/app'>here</a>.<br><br>Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>.</source>
|
||||
<oldsource>This app was built using the <a href='https://qt.io'>Qt Framework</a> licensed under the <a href='https://www.gnu.org/licenses/lgpl-3.0.en.html'>GNU lgplV3 license</a>.<br><br>This app is open source and licensed under the <a href='https://www.gnu.org/licenses/agpl-3.0.en.html'>GNU agplV3 license</a>, the source code can be found <a href='https://itsblue.dev/dorian/blueROCK/'>here</a>.<br><br>Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>.</oldsource>
|
||||
<translation type="unfinished"></translation>
|
||||
|
|
|
@ -33,6 +33,9 @@ int main(int argc, char *argv[])
|
|||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
|
||||
QGuiApplication app(argc, argv);
|
||||
QFont f = app.font();
|
||||
f.setPixelSize(16);
|
||||
app.setFont(f);
|
||||
|
||||
// translation
|
||||
QString localeName = QLocale::system().bcp47Name();
|
||||
|
|