Compare commits
94 commits
internal_m
...
main
Author | SHA1 | Date | |
---|---|---|---|
Dorian Zedler | bf617ba72c | ||
Dorian Zedler | 5694d61c4b | ||
Dorian Zedler | cd0500c40b | ||
Dorian Zedler | 12185337b9 | ||
Dorian Zedler | fe453cd5ac | ||
Dorian Zedler | 6644d16a92 | ||
Dorian Zedler | f42d8fb8d7 | ||
Dorian Zedler | e6569694e4 | ||
Dorian Zedler | 38dbbb3b32 | ||
Dorian Zedler | ce7fc3d367 | ||
Dorian Zedler | 8e4e135f9c | ||
Dorian Zedler | c13d108cff | ||
Dorian Zedler | 367807b90d | ||
Dorian Zedler | 6ca5b81f6d | ||
Dorian Zedler | 05bce5e97d | ||
Dorian Zedler | a238a68638 | ||
Dorian Zedler | 6d7009b183 | ||
Dorian Zedler | 0ca2725bf3 | ||
Dorian Zedler | 447c1135dd | ||
Dorian Zedler | 38bdf15f40 | ||
Dorian Zedler | 0920ee6f77 | ||
Dorian Zedler | a1ea8a4c65 | ||
Dorian Zedler | 21c479cd55 | ||
Dorian Zedler | 766da23e26 | ||
Dorian Zedler | 496d66a521 | ||
Dorian Zedler | 43d98bd43e | ||
Dorian Zedler | 30471725d0 | ||
Dorian Zedler | 88ebde27f2 | ||
Dorian Zedler | 4bf2e2b3b3 | ||
Dorian Zedler | d234474233 | ||
Dorian Zedler | 78659fe769 | ||
Dorian Zedler | 99f49e0798 | ||
Dorian Zedler | 455c4044c2 | ||
Dorian Zedler | ce67df3416 | ||
Dorian Zedler | 53460b2091 | ||
Dorian Zedler | 2f3f0488da | ||
Dorian Zedler | a026912929 | ||
Dorian Zedler | 551c6ffb6d | ||
Dorian Zedler | 55a5aac4e1 | ||
Dorian Zedler | 21b88575fb | ||
Dorian Zedler | fb17ec52cd | ||
Dorian Zedler | 6b91988ac5 | ||
Dorian Zedler | 75d7212fd9 | ||
Dorian Zedler | 3a735e2cf4 | ||
Dorian Zedler | c92362f07a | ||
Dorian Zedler | 3492e6c511 | ||
Dorian Zedler | 5cfad624a3 | ||
Dorian Zedler | dabb2473b3 | ||
Dorian Zedler | d33748a06d | ||
Dorian Zedler | 1cd7ad6649 | ||
Dorian Zedler | d881b203fa | ||
Dorian Zedler | 175c2ea264 | ||
Dorian Zedler | 484fb8d7a9 | ||
Dorian Zedler | 7fbf90b215 | ||
Dorian Zedler | 767556c083 | ||
Dorian Zedler | 9af65bd746 | ||
Dorian Zedler | 1ce24be35b | ||
Dorian Zedler | acd436f000 | ||
Dorian Zedler | 9c979d43ab | ||
Dorian Zedler | 45fd7add57 | ||
Dorian Zedler | 74403dd990 | ||
Dorian Zedler | a2e6cdccb9 | ||
Dorian Zedler | f1d6915c52 | ||
Dorian Zedler | f422aecb77 | ||
Dorian Zedler | 800796fb06 | ||
Dorian Zedler | 7d6b972ef3 | ||
Dorian Zedler | 0d05027086 | ||
Dorian Zedler | d286a87553 | ||
Dorian Zedler | 20ffca4670 | ||
Dorian Zedler | 38ad92207e | ||
Dorian Zedler | cc855bd329 | ||
Dorian Zedler | 5a50ef8cbe | ||
Dorian Zedler | 9ca38f8651 | ||
Dorian Zedler | 6594304deb | ||
Dorian Zedler | 30a29003a9 | ||
Dorian Zedler | 275fff2d22 | ||
Dorian Zedler | 0b8c253564 | ||
Dorian Zedler | 06e76bc287 | ||
Dorian Zedler | 8d1f0173ed | ||
Dorian Zedler | 28400f98e4 | ||
Dorian Zedler | 14bbd77e71 | ||
Dorian Zedler | 2213765a39 | ||
Dorian Zedler | c62af2e172 | ||
Dorian Zedler | 229732762c | ||
Dorian Zedler | 6b87344bf1 | ||
Dorian Zedler | 1d92099d1d | ||
Dorian Zedler | 1dec380277 | ||
Dorian Zedler | 100ac5143b | ||
Dorian Zedler | f6cd1853ae | ||
Dorian Zedler | 883ee82e29 | ||
Dorian Zedler | f4aa3deaec | ||
Dorian Zedler | f9f6dd7f5d | ||
Dorian Zedler | 121589bf29 | ||
Dorian Zedler | 0b055bc2c1 |
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*
|
*.pro.user*
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
|
.bundle
|
||||||
|
vendor
|
||||||
|
outputs
|
||||||
|
|
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "qzxing"]
|
||||||
|
path = qzxing
|
||||||
|
url = https://github.com/ftylitak/qzxing.git
|
66
CHANGELOG.md
|
@ -3,15 +3,75 @@ 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/)
|
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).
|
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.03.1] - UR
|
# [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
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Dark mode
|
||||||
|
- QR-Code scanning
|
||||||
|
- Sharing of every view using either link, QR-Code or a poster
|
||||||
|
- Text which is too large too fit is scrollable now in most places
|
||||||
|
- German translations
|
||||||
|
- URL handler for https://l.bluerock.dev and https://app.bluerock.dev
|
||||||
|
|
||||||
|
### 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
|
### Changed
|
||||||
- the boulder result rect doesn't have a background if there is no result now
|
- the boulder result rect doesn't have a background if there is no result now
|
||||||
- the selected route is kept when changing cats
|
- 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
|
# [0.03.0] - 2019-07-11
|
||||||
### Added
|
### Added
|
||||||
- it is not possible to pin competitions and only show these
|
- it is now possible to pin competitions and only show these
|
||||||
- flowcharts for the speed finals are now available
|
- flowcharts for the speed finals are now available
|
||||||
|
|
||||||
# [0.02.0] - 2019-06-23
|
# [0.02.0] - 2019-06-23
|
||||||
|
@ -21,7 +81,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
|
- event website url is now present in the cat select dialog if available
|
||||||
|
|
||||||
### Changed
|
### 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
|
# [0.01.6] - 2019-06-15
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
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
|
19
README.md
|
@ -1,3 +1,20 @@
|
||||||
# Digital Rock ranking
|
# Digital Rock ranking
|
||||||
|
|
||||||
App to view ranking and calendar data from https://www.digitalrock.de/
|
App to view ranking and calendar data from https://www.digitalrock.de/
|
||||||
|
|
||||||
|
# Init
|
||||||
|
´´´
|
||||||
|
git submodule init
|
||||||
|
git submodule update
|
||||||
|
´´´
|
||||||
|
|
||||||
|
# Poster
|
||||||
|
The poster offsets are (always top left of the element):
|
||||||
|
- Width: 1654
|
||||||
|
- Height: 2339
|
||||||
|
### QR-Code
|
||||||
|
- Cooridnates: 414, 414
|
||||||
|
- Size: 1650x1650
|
||||||
|
### Comp name
|
||||||
|
- Cooridnates: x: 324, y: 2500
|
||||||
|
- Size: 64 per line; 1835 width; 150 max height
|
||||||
|
|
Before Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 150 KiB |
|
@ -1,44 +1,54 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<manifest package="com.itsblue.blueROCKtest" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.03.1" android:versionCode="13" android:installLocation="auto">
|
<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">
|
||||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="blueROCK" android:icon="@drawable/icon">
|
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="blueROCK" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
Remove the comment if you do not require these default 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. -->
|
||||||
|
<!-- %%INSERT_FEATURES -->
|
||||||
|
|
||||||
|
<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" android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Application arguments -->
|
<!-- Application arguments -->
|
||||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||||
<!-- Application arguments -->
|
<!-- Application arguments -->
|
||||||
|
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||||
<meta-data android:name="android.app.lib_name" android:value="blueROCK"/>
|
|
||||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||||
<!-- Deploy Qt libs as part of package -->
|
<!-- Deploy Qt libs as part of package -->
|
||||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
|
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
|
||||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
|
||||||
<!-- Run with local libs -->
|
<!-- Run with local libs -->
|
||||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
|
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||||
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so:lib/libQt5QuickParticles.so"/>
|
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
||||||
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidBearer.jar:jar/QtAndroidExtras.jar"/>
|
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||||
<meta-data android:name="android.app.static_init_classes" android:value=""/>
|
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||||
<!-- Used to specify custom system library path to run with local system libs -->
|
<!-- Used to specify custom system library path to run with local system libs -->
|
||||||
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
||||||
<!-- Messages maps -->
|
<!-- Messages maps -->
|
||||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||||
|
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
||||||
<!-- Messages maps -->
|
<!-- Messages maps -->
|
||||||
|
|
||||||
<!-- Splash screen -->
|
<!-- Splash screen -->
|
||||||
|
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
||||||
|
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
||||||
|
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
||||||
|
are done populating your window with content. -->
|
||||||
|
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
|
||||||
|
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
|
||||||
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
||||||
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
||||||
<!-- Splash screen -->
|
<!-- Splash screen -->
|
||||||
|
|
||||||
<!-- Background running -->
|
<!-- Background running -->
|
||||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||||
application still try to draw after
|
application still try to draw after
|
||||||
|
@ -46,38 +56,38 @@
|
||||||
signal is sent! -->
|
signal is sent! -->
|
||||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||||
<!-- Background running -->
|
<!-- Background running -->
|
||||||
|
|
||||||
<!-- auto screen scale factor -->
|
<!-- auto screen scale factor -->
|
||||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
||||||
<!-- auto screen scale factor -->
|
<!-- auto screen scale factor -->
|
||||||
|
|
||||||
<!-- extract android style -->
|
<!-- extract android style -->
|
||||||
<!-- available android:values :
|
<!-- available android:values :
|
||||||
|
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
||||||
* full - useful QWidget & Quick Controls 1 apps
|
* full - useful QWidget & Quick Controls 1 apps
|
||||||
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
||||||
* none - useful for apps that don't use any of the above Qt modules
|
* none - useful for apps that don't use any of the above Qt modules
|
||||||
-->
|
-->
|
||||||
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
|
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||||
<!-- extract android style -->
|
<!-- extract android style -->
|
||||||
</activity>
|
<!-- Handle shared incoming urls -->
|
||||||
|
<intent-filter android:autoVerify="true">
|
||||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<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=".*"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
<provider android:name="android.support.v4.content.FileProvider" android:authorities="de.itsblue.blueROCK.fileprovider" android:grantUriPermissions="true" android:exported="false">
|
||||||
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/>
|
||||||
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
<!-- 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. -->
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
|
|
||||||
|
|
||||||
<!-- 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. -->
|
|
||||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
|
||||||
|
|
||||||
<uses-permission android:name="com.android.vending.BILLING"/>
|
|
||||||
</manifest>
|
</manifest>
|
83
android/build.gradle
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.6.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||||
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
/*******************************************************
|
||||||
|
* The following variables:
|
||||||
|
* - androidBuildToolsVersion,
|
||||||
|
* - androidCompileSdkVersion
|
||||||
|
* - qt5AndroidDir - holds the path to qt android files
|
||||||
|
* needed to build any Qt application
|
||||||
|
* on Android.
|
||||||
|
*
|
||||||
|
* are defined in gradle.properties file. This file is
|
||||||
|
* updated by QtCreator and androiddeployqt tools.
|
||||||
|
* Changing them manually might break the compilation!
|
||||||
|
*******************************************************/
|
||||||
|
|
||||||
|
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||||
|
|
||||||
|
buildToolsVersion '28.0.3'
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
|
||||||
|
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
|
||||||
|
res.srcDirs = [qt5AndroidDir + '/res', 'res']
|
||||||
|
resources.srcDirs = ['resources']
|
||||||
|
renderscript.srcDirs = ['src']
|
||||||
|
assets.srcDirs = ['assets']
|
||||||
|
jniLibs.srcDirs = ['libs']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.incremental = true
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not compress Qt binary resources file
|
||||||
|
aaptOptions {
|
||||||
|
noCompress 'rcc'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
resConfig "en"
|
||||||
|
minSdkVersion = qtMinSdkVersion
|
||||||
|
targetSdkVersion = qtTargetSdkVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
checkReleaseBuilds false
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
}
|
11
android/gradle.properties
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Project-wide Gradle settings.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m
|
||||||
|
|
||||||
|
# Gradle caching allows reusing the build artifacts from a previous
|
||||||
|
# build with the same inputs. However, over time, the cache size will
|
||||||
|
# grow. Uncomment the following line to enable it.
|
||||||
|
#org.gradle.caching=true
|
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
172
android/gradlew
vendored
Executable file
|
@ -0,0 +1,172 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
84
android/gradlew.bat
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
BIN
android/res/drawable-hdpi/icon.png
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
android/res/drawable-ldpi/icon.png
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
android/res/drawable-mdpi/icon.png
Normal file
After Width: | Height: | Size: 124 KiB |
22
android/res/values/libs.xml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<resources>
|
||||||
|
<array name="qt_sources">
|
||||||
|
<item>https://download.qt.io/ministro/android/qt5/qt-5.14</item>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<!-- The following is handled automatically by the deployment tool. It should
|
||||||
|
not be edited manually. -->
|
||||||
|
|
||||||
|
<array name="bundled_libs">
|
||||||
|
<!-- %%INSERT_EXTRA_LIBS%% -->
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<array name="qt_libs">
|
||||||
|
<!-- %%INSERT_QT_LIBS%% -->
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<array name="load_local_libs">
|
||||||
|
<!-- %%INSERT_LOCAL_LIBS%% -->
|
||||||
|
</array>
|
||||||
|
|
||||||
|
</resources>
|
3
android/res/xml/filepaths.xml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<files-path name="temporaryFiles" path="temporaryFiles/" />
|
||||||
|
</paths>
|
232
android/src/de/itsblue/blueROCK/MainActivity.java
Executable file
|
@ -0,0 +1,232 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke)
|
||||||
|
// this project is based on ideas from
|
||||||
|
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||||
|
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||||
|
// also inspired by:
|
||||||
|
// https://www.androidcode.ninja/android-share-intent-example/
|
||||||
|
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||||
|
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||||
|
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||||
|
// OpenURL in At Android: got ideas from:
|
||||||
|
// https://github.com/BernhardWenzel/open-url-in-qt-android
|
||||||
|
// https://github.com/tobiatesan/android_intents_qt
|
||||||
|
//
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
package de.itsblue.blueROCK;
|
||||||
|
|
||||||
|
import org.qtproject.qt5.android.QtNative;
|
||||||
|
|
||||||
|
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||||
|
import android.os.*;
|
||||||
|
import android.content.*;
|
||||||
|
import android.app.*;
|
||||||
|
|
||||||
|
import java.lang.String;
|
||||||
|
import android.content.Intent;
|
||||||
|
import java.io.File;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import org.ekkescorner.utils.*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class MainActivity extends QtActivity
|
||||||
|
{
|
||||||
|
// native - must be implemented in Cpp via JNI
|
||||||
|
// 'file' scheme or resolved from 'content' scheme:
|
||||||
|
public static native void setFileUrlReceived(String url);
|
||||||
|
//
|
||||||
|
public static native void setOtherUrlReceived(String url, String scheme);
|
||||||
|
// InputStream from 'content' scheme:
|
||||||
|
public static native void setFileReceivedAndSaved(String url);
|
||||||
|
//
|
||||||
|
public static native void fireActivityResult(int requestCode, int resultCode);
|
||||||
|
//
|
||||||
|
public static native boolean checkFileExits(String url);
|
||||||
|
|
||||||
|
public static boolean isIntentPending;
|
||||||
|
public static boolean isInitialized;
|
||||||
|
public static String workingDirPath;
|
||||||
|
|
||||||
|
// Use a custom Chooser without providing own App as share target !
|
||||||
|
// see QShareUtils.java createCustomChooserAndStartActivity()
|
||||||
|
// Selecting your own App as target could cause AndroidOS to call
|
||||||
|
// onCreate() instead of onNewIntent()
|
||||||
|
// and then you are in trouble because we're using 'singleInstance' as LaunchMode
|
||||||
|
// more details: my blog at Qt
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Log.d("ekkescorner", "onCreate QShareActivity");
|
||||||
|
// now we're checking if the App was started from another Android App via Intent
|
||||||
|
Intent theIntent = getIntent();
|
||||||
|
if (theIntent != null){
|
||||||
|
String theAction = theIntent.getAction();
|
||||||
|
if (theAction != null){
|
||||||
|
Log.d("ekkescorner onCreate ", theAction);
|
||||||
|
// QML UI not ready yet
|
||||||
|
// delay processIntent();
|
||||||
|
isIntentPending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // onCreate
|
||||||
|
|
||||||
|
// WIP - trying to find a solution to survive a 2nd onCreate
|
||||||
|
// ongoing discussion in QtMob (Slack)
|
||||||
|
// from other Apps not respecting that you only have a singleInstance
|
||||||
|
// there are problems per ex. sharing a file from Google Files App,
|
||||||
|
// but working well using Xiaomi FileManager App
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
Log.d("ekkescorner", "onDestroy QShareActivity");
|
||||||
|
// super.onDestroy();
|
||||||
|
// System.exit() closes the App before doing onCreate() again
|
||||||
|
// then the App was restarted, but looses context
|
||||||
|
// This works for Samsung My Files
|
||||||
|
// but Google Files doesn't call onDestroy()
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we start Activity with result code
|
||||||
|
// to test JNI with QAndroidActivityResultReceiver you must comment or rename
|
||||||
|
// this method here - otherwise you'll get wrong request or result codes
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
// Check which request we're responding to
|
||||||
|
Log.d("ekkescorner onActivityResult", "requestCode: "+requestCode);
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
Log.d("ekkescorner onActivityResult - resultCode: ", "SUCCESS");
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner onActivityResult - resultCode: ", "CANCEL");
|
||||||
|
}
|
||||||
|
// hint: result comes back too fast for Action SEND
|
||||||
|
// if you want to delete/move the File add a Timer w 500ms delay
|
||||||
|
// see Example App main.qml - delayDeleteTimer
|
||||||
|
// if you want to revoke permissions for older OS
|
||||||
|
// it makes sense also do this after the delay
|
||||||
|
fireActivityResult(requestCode, resultCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are opened from other apps:
|
||||||
|
@Override
|
||||||
|
public void onNewIntent(Intent intent) {
|
||||||
|
Log.d("ekkescorner", "onNewIntent");
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
|
// Intent will be processed, if all is initialized and Qt / QML can handle the event
|
||||||
|
if(isInitialized) {
|
||||||
|
processIntent();
|
||||||
|
} else {
|
||||||
|
isIntentPending = true;
|
||||||
|
}
|
||||||
|
} // onNewIntent
|
||||||
|
|
||||||
|
public void checkPendingIntents(String workingDir) {
|
||||||
|
isInitialized = true;
|
||||||
|
workingDirPath = workingDir;
|
||||||
|
Log.d("ekkescorner", workingDirPath);
|
||||||
|
if(isIntentPending) {
|
||||||
|
isIntentPending = false;
|
||||||
|
Log.d("ekkescorner", "checkPendingIntents: true");
|
||||||
|
processIntent();
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner", "nothingPending");
|
||||||
|
}
|
||||||
|
} // checkPendingIntents
|
||||||
|
|
||||||
|
// process the Intent if Action is SEND or VIEW
|
||||||
|
private void processIntent(){
|
||||||
|
Intent intent = getIntent();
|
||||||
|
|
||||||
|
Uri intentUri;
|
||||||
|
String intentScheme;
|
||||||
|
String intentAction;
|
||||||
|
// we are listening to android.intent.action.SEND or VIEW (see Manifest)
|
||||||
|
if (intent.getAction().equals("android.intent.action.VIEW")){
|
||||||
|
intentAction = "VIEW";
|
||||||
|
intentUri = intent.getData();
|
||||||
|
} else if (intent.getAction().equals("android.intent.action.SEND")){
|
||||||
|
intentAction = "SEND";
|
||||||
|
Bundle bundle = intent.getExtras();
|
||||||
|
intentUri = (Uri)bundle.get(Intent.EXTRA_STREAM);
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner Intent unknown action:", intent.getAction());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d("ekkescorner action:", intentAction);
|
||||||
|
if (intentUri == null){
|
||||||
|
Log.d("ekkescorner Intent URI:", "is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("ekkescorner Intent URI:", intentUri.toString());
|
||||||
|
|
||||||
|
// content or file
|
||||||
|
intentScheme = intentUri.getScheme();
|
||||||
|
if (intentScheme == null){
|
||||||
|
Log.d("ekkescorner Intent URI Scheme:", "is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(intentScheme.equals("file")){
|
||||||
|
// URI as encoded string
|
||||||
|
Log.d("ekkescorner Intent File URI: ", intentUri.toString());
|
||||||
|
setFileUrlReceived(intentUri.toString());
|
||||||
|
// we are done Qt can deal with file scheme
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!intentScheme.equals("content")){
|
||||||
|
Log.d("ekkescorner Intent URI unknown scheme: ", intentScheme);
|
||||||
|
setOtherUrlReceived(intentUri.toString(), intentScheme);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ok - it's a content scheme URI
|
||||||
|
// we will try to resolve the Path to a File URI
|
||||||
|
// if this won't work or if the File cannot be opened,
|
||||||
|
// we'll try to copy the file into our App working dir via InputStream
|
||||||
|
// hopefully in most cases PathResolver will give a path
|
||||||
|
|
||||||
|
// you need the file extension, MimeType or Name from ContentResolver ?
|
||||||
|
// here's HowTo get it:
|
||||||
|
Log.d("ekkescorner Intent Content URI: ", intentUri.toString());
|
||||||
|
ContentResolver cR = this.getContentResolver();
|
||||||
|
MimeTypeMap mime = MimeTypeMap.getSingleton();
|
||||||
|
String fileExtension = mime.getExtensionFromMimeType(cR.getType(intentUri));
|
||||||
|
Log.d("ekkescorner","Intent extension: "+fileExtension);
|
||||||
|
String mimeType = cR.getType(intentUri);
|
||||||
|
Log.d("ekkescorner"," Intent MimeType: "+mimeType);
|
||||||
|
String name = QShareUtils.getContentName(cR, intentUri);
|
||||||
|
if(name != null) {
|
||||||
|
Log.d("ekkescorner Intent Name:", name);
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner Intent Name:", "is NULL");
|
||||||
|
}
|
||||||
|
String filePath;
|
||||||
|
filePath = QSharePathResolver.getRealPathFromURI(this, intentUri);
|
||||||
|
if(filePath == null) {
|
||||||
|
Log.d("ekkescorner QSharePathResolver:", "filePath is NULL");
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner QSharePathResolver:", filePath);
|
||||||
|
// to be safe check if this File Url really can be opened by Qt
|
||||||
|
// there were problems with MS office apps on Android 7
|
||||||
|
if (checkFileExits(filePath)) {
|
||||||
|
setFileUrlReceived(filePath);
|
||||||
|
// we are done Qt can deal with file scheme
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trying the InputStream way:
|
||||||
|
filePath = QShareUtils.createFile(cR, intentUri, workingDirPath);
|
||||||
|
if(filePath == null) {
|
||||||
|
Log.d("ekkescorner Intent FilePath:", "is NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFileReceivedAndSaved(filePath);
|
||||||
|
} // processIntent
|
||||||
|
|
||||||
|
} // class QShareActivity
|
223
android/src/org/ekkescorner/utils/QSharePathResolver.java
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
// from: https://github.com/wkh237/react-native-fetch-blob/blob/master/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java
|
||||||
|
// MIT License, see: https://github.com/wkh237/react-native-fetch-blob/blob/master/LICENSE
|
||||||
|
// original copyright: Copyright (c) 2017 xeiyan@gmail.com
|
||||||
|
// src slightly modified to be used into Qt Projects: (c) 2017 ekke@ekkes-corner.org
|
||||||
|
|
||||||
|
package org.ekkescorner.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.lang.NumberFormatException;
|
||||||
|
|
||||||
|
public class QSharePathResolver {
|
||||||
|
public static String getRealPathFromURI(final Context context, final Uri uri) {
|
||||||
|
|
||||||
|
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
|
||||||
|
// DocumentProvider
|
||||||
|
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
// ExternalStorageProvider
|
||||||
|
if (isExternalStorageDocument(uri)) {
|
||||||
|
Log.d("ekkescorner"," isExternalStorageDocument");
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
if ("primary".equalsIgnoreCase(type)) {
|
||||||
|
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO handle non-primary volumes
|
||||||
|
}
|
||||||
|
// DownloadsProvider
|
||||||
|
else if (isDownloadsDocument(uri)) {
|
||||||
|
Log.d("ekkescorner"," isDownloadsDocument");
|
||||||
|
final String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
Log.d("ekkescorner"," getDocumentId "+id);
|
||||||
|
long longId = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
longId = Long.valueOf(id);
|
||||||
|
}
|
||||||
|
catch(NumberFormatException nfe)
|
||||||
|
{
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
}
|
||||||
|
final Uri contentUri = ContentUris.withAppendedId(
|
||||||
|
Uri.parse("content://downloads/public_downloads"), longId);
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, null, null);
|
||||||
|
}
|
||||||
|
// MediaProvider
|
||||||
|
else if (isMediaDocument(uri)) {
|
||||||
|
Log.d("ekkescorner"," isMediaDocument");
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
Uri contentUri = null;
|
||||||
|
if ("image".equals(type)) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("video".equals(type)) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("audio".equals(type)) {
|
||||||
|
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String selection = "_id=?";
|
||||||
|
final String[] selectionArgs = new String[] {
|
||||||
|
split[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
Log.d("ekkescorner"," is uri.getScheme()");
|
||||||
|
// Return the remote address
|
||||||
|
if (isGooglePhotosUri(uri))
|
||||||
|
return uri.getLastPathSegment();
|
||||||
|
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
}
|
||||||
|
// Other Providers
|
||||||
|
else{
|
||||||
|
Log.d("ekkescorner ","is Other Provider");
|
||||||
|
try {
|
||||||
|
InputStream attachment = context.getContentResolver().openInputStream(uri);
|
||||||
|
if (attachment != null) {
|
||||||
|
String filename = getContentName(context.getContentResolver(), uri);
|
||||||
|
if (filename != null) {
|
||||||
|
File file = new File(context.getCacheDir(), filename);
|
||||||
|
FileOutputStream tmp = new FileOutputStream(file);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while (attachment.read(buffer) > 0) {
|
||||||
|
tmp.write(buffer);
|
||||||
|
}
|
||||||
|
tmp.close();
|
||||||
|
attachment.close();
|
||||||
|
return file.getAbsolutePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO SIGNAL shareError()
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// MediaStore (and general)
|
||||||
|
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
Log.d("ekkescorner ","NOT DocumentsContract.isDocumentUri");
|
||||||
|
Log.d("ekkescorner"," is uri.getScheme()");
|
||||||
|
// Return the remote address
|
||||||
|
if (isGooglePhotosUri(uri))
|
||||||
|
return uri.getLastPathSegment();
|
||||||
|
Log.d("ekkescorner"," return: getDataColumn ");
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
}
|
||||||
|
// File
|
||||||
|
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
Log.d("ekkescorner ","NOT DocumentsContract.isDocumentUri");
|
||||||
|
Log.d("ekkescorner"," is file scheme");
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getContentName(ContentResolver resolver, Uri uri) {
|
||||||
|
Cursor cursor = resolver.query(uri, null, null, null, null);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||||
|
if (nameIndex >= 0) {
|
||||||
|
String name = cursor.getString(nameIndex);
|
||||||
|
cursor.close();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the data column for this Uri. This is useful for
|
||||||
|
* MediaStore Uris, and other file-based ContentProviders.
|
||||||
|
*
|
||||||
|
* @param context The context.
|
||||||
|
* @param uri The Uri to query.
|
||||||
|
* @param selection (Optional) Filter used in the query.
|
||||||
|
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||||
|
* @return The value of the _data column, which is typically a file path.
|
||||||
|
*/
|
||||||
|
public static String getDataColumn(Context context, Uri uri, String selection,
|
||||||
|
String[] selectionArgs) {
|
||||||
|
|
||||||
|
Cursor cursor = null;
|
||||||
|
String result = null;
|
||||||
|
final String column = "_data";
|
||||||
|
final String[] projection = {
|
||||||
|
column
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||||
|
null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
final int index = cursor.getColumnIndexOrThrow(column);
|
||||||
|
result = cursor.getString(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isExternalStorageDocument(Uri uri) {
|
||||||
|
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is DownloadsProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isDownloadsDocument(Uri uri) {
|
||||||
|
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is MediaProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isMediaDocument(Uri uri) {
|
||||||
|
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is Google Photos.
|
||||||
|
*/
|
||||||
|
public static boolean isGooglePhotosUri(Uri uri) {
|
||||||
|
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
396
android/src/org/ekkescorner/utils/QShareUtils.java
Executable file
|
@ -0,0 +1,396 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke)
|
||||||
|
// this project is based on ideas from
|
||||||
|
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||||
|
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||||
|
// also inspired by:
|
||||||
|
// https://www.androidcode.ninja/android-share-intent-example/
|
||||||
|
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||||
|
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||||
|
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||||
|
// https://stackoverflow.com/questions/5734678/custom-filtering-of-intent-chooser-based-on-installed-android-package-name
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
package org.ekkescorner.utils;
|
||||||
|
|
||||||
|
import org.qtproject.qt5.android.QtNative;
|
||||||
|
|
||||||
|
import java.lang.String;
|
||||||
|
import android.content.Intent;
|
||||||
|
import java.io.File;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Collections;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import android.support.v4.content.FileProvider;
|
||||||
|
import android.support.v4.app.ShareCompat;
|
||||||
|
|
||||||
|
public class QShareUtils
|
||||||
|
{
|
||||||
|
// reference Authority as defined in AndroidManifest.xml
|
||||||
|
private static String AUTHORITY="de.itsblue.blueROCK.fileprovider";
|
||||||
|
|
||||||
|
protected QShareUtils()
|
||||||
|
{
|
||||||
|
Log.d("ekkescorner", "QShareUtils()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkMimeTypeView(String mimeType) {
|
||||||
|
if (QtNative.activity() == null)
|
||||||
|
return false;
|
||||||
|
Intent myIntent = new Intent();
|
||||||
|
myIntent.setAction(Intent.ACTION_VIEW);
|
||||||
|
// without an URI resolve always fails
|
||||||
|
// an empty URI allows to resolve the Activity
|
||||||
|
File fileToShare = new File("");
|
||||||
|
Uri uri = Uri.fromFile(fileToShare);
|
||||||
|
myIntent.setDataAndType(uri, mimeType);
|
||||||
|
|
||||||
|
// Verify that the intent will resolve to an activity
|
||||||
|
if (myIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||||
|
Log.d("ekkescorner checkMime ", "YEP - we can go on and View");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner checkMime", "sorry - no App available to View");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkMimeTypeEdit(String mimeType) {
|
||||||
|
if (QtNative.activity() == null)
|
||||||
|
return false;
|
||||||
|
Intent myIntent = new Intent();
|
||||||
|
myIntent.setAction(Intent.ACTION_EDIT);
|
||||||
|
// without an URI resolve always fails
|
||||||
|
// an empty URI allows to resolve the Activity
|
||||||
|
File fileToShare = new File("");
|
||||||
|
Uri uri = Uri.fromFile(fileToShare);
|
||||||
|
myIntent.setDataAndType(uri, mimeType);
|
||||||
|
|
||||||
|
// Verify that the intent will resolve to an activity
|
||||||
|
if (myIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||||
|
Log.d("ekkescorner checkMime ", "YEP - we can go on and Edit");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner checkMime", "sorry - no App available to Edit");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shareText(String text) {
|
||||||
|
if (QtNative.activity() == null)
|
||||||
|
return false;
|
||||||
|
Intent sendIntent = new Intent();
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
|
||||||
|
// Verify that the intent will resolve to an activity
|
||||||
|
if (sendIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||||
|
QtNative.activity().startActivity(sendIntent);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner share", "Intent not resolved");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// thx @oxied and @pooks for the idea: https://stackoverflow.com/a/18835895/135559
|
||||||
|
// theIntent is already configured with all needed properties and flags
|
||||||
|
// so we only have to add the packageName of targeted app
|
||||||
|
public static boolean createCustomChooserAndStartActivity(Intent theIntent, String title, int requestId, Uri uri) {
|
||||||
|
final Context context = QtNative.activity();
|
||||||
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
final boolean isLowerOrEqualsKitKat = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
|
||||||
|
|
||||||
|
// MATCH_DEFAULT_ONLY: Resolution and querying flag. if set, only filters that support the CATEGORY_DEFAULT will be considered for matching.
|
||||||
|
// Check if there is a default app for this type of content.
|
||||||
|
ResolveInfo defaultAppInfo = packageManager.resolveActivity(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
|
if(defaultAppInfo == null) {
|
||||||
|
Log.d("ekkescorner", title+" PackageManager cannot resolve Activity");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// had to remove this check - there can be more Activity names, per ex
|
||||||
|
// com.google.android.apps.docs.editors.kix.quickword.QuickWordDocumentOpenerActivityAlias
|
||||||
|
// if (!defaultAppInfo.activityInfo.name.endsWith("ResolverActivity") && !defaultAppInfo.activityInfo.name.endsWith("EditActivity")) {
|
||||||
|
// Log.d("ekkescorner", title+" defaultAppInfo not Resolver or EditActivity: "+defaultAppInfo.activityInfo.name);
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Retrieve all apps for our intent. Check if there are any apps returned
|
||||||
|
List<ResolveInfo> appInfoList = packageManager.queryIntentActivities(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
|
if (appInfoList.isEmpty()) {
|
||||||
|
Log.d("ekkescorner", title+" appInfoList.isEmpty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log.d("ekkescorner", title+" appInfoList: "+appInfoList.size());
|
||||||
|
|
||||||
|
// Sort in alphabetical order
|
||||||
|
Collections.sort(appInfoList, new Comparator<ResolveInfo>() {
|
||||||
|
@Override
|
||||||
|
public int compare(ResolveInfo first, ResolveInfo second) {
|
||||||
|
String firstName = first.loadLabel(packageManager).toString();
|
||||||
|
String secondName = second.loadLabel(packageManager).toString();
|
||||||
|
return firstName.compareToIgnoreCase(secondName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
List<Intent> targetedIntents = new ArrayList<Intent>();
|
||||||
|
// Filter itself and create intent with the rest of the apps.
|
||||||
|
for (ResolveInfo appInfo : appInfoList) {
|
||||||
|
// get the target PackageName
|
||||||
|
String targetPackageName = appInfo.activityInfo.packageName;
|
||||||
|
// we don't want to share with our own app
|
||||||
|
// in fact sharing with own app with resultCode will crash because doesn't work well with launch mode 'singleInstance'
|
||||||
|
if (targetPackageName.equals(context.getPackageName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if you have a blacklist of apps please exclude them here
|
||||||
|
|
||||||
|
// we create the targeted Intent based on our already configured Intent
|
||||||
|
Intent targetedIntent = new Intent(theIntent);
|
||||||
|
// now add the target packageName so this Intent will only find the one specific App
|
||||||
|
targetedIntent.setPackage(targetPackageName);
|
||||||
|
// collect all these targetedIntents
|
||||||
|
targetedIntents.add(targetedIntent);
|
||||||
|
|
||||||
|
// legacy support and Workaround for Android bug
|
||||||
|
// grantUriPermission needed for KITKAT or older
|
||||||
|
// see https://code.google.com/p/android/issues/detail?id=76683
|
||||||
|
// also: https://stackoverflow.com/questions/18249007/how-to-use-support-fileprovider-for-sharing-content-to-other-apps
|
||||||
|
if(isLowerOrEqualsKitKat) {
|
||||||
|
Log.d("ekkescorner", "legacy support grantUriPermission");
|
||||||
|
context.grantUriPermission(targetPackageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
// attention: you must revoke the permission later, so this only makes sense with getting back a result to know that Intent was done
|
||||||
|
// I always move or delete the file, so I don't revoke permission
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there are apps found for our Intent to avoid that there was only our own removed app before
|
||||||
|
if (targetedIntents.isEmpty()) {
|
||||||
|
Log.d("ekkescorner", title+" targetedIntents.isEmpty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we can create our Intent with custom Chooser
|
||||||
|
// we need all collected targetedIntents as EXTRA_INITIAL_INTENTS
|
||||||
|
// we're using the last targetedIntent as initializing Intent, because
|
||||||
|
// chooser adds its initializing intent to the end of EXTRA_INITIAL_INTENTS :)
|
||||||
|
Intent chooserIntent = Intent.createChooser(targetedIntents.remove(targetedIntents.size() - 1), title);
|
||||||
|
if (targetedIntents.isEmpty()) {
|
||||||
|
Log.d("ekkescorner", title+" only one Intent left for Chooser");
|
||||||
|
} else {
|
||||||
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[] {}));
|
||||||
|
}
|
||||||
|
// Verify that the intent will resolve to an activity
|
||||||
|
if (chooserIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||||
|
if(requestId > 0) {
|
||||||
|
QtNative.activity().startActivityForResult(chooserIntent, requestId);
|
||||||
|
} else {
|
||||||
|
QtNative.activity().startActivity(chooserIntent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log.d("ekkescorner", title+" Chooser Intent not resolved. Should never happen");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I am deleting the files from shared folder when Activity was done or canceled
|
||||||
|
// so probably I don't have to revike FilePermissions for older OS
|
||||||
|
// if you don't delete or move the file: here's what you must done to revoke the access
|
||||||
|
public static void revokeFilePermissions(String filePath) {
|
||||||
|
final Context context = QtNative.activity();
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
||||||
|
File file = new File(filePath);
|
||||||
|
Uri uri = FileProvider.getUriForFile(context, AUTHORITY, file);
|
||||||
|
context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean sendFile(String filePath, String title, String mimeType, int requestId) {
|
||||||
|
if (QtNative.activity() == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// using v4 support library create the Intent from ShareCompat
|
||||||
|
// Intent sendIntent = new Intent();
|
||||||
|
Intent sendIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
|
||||||
|
File imageFileToShare = new File(filePath);
|
||||||
|
|
||||||
|
// Using FileProvider you must get the URI from FileProvider using your AUTHORITY
|
||||||
|
// Uri uri = Uri.fromFile(imageFileToShare);
|
||||||
|
Uri uri;
|
||||||
|
try {
|
||||||
|
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.d("ekkescorner sendFile - cannot be shared: ", filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("ekkescorner sendFile", uri.toString());
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||||
|
|
||||||
|
if(mimeType == null || mimeType.isEmpty()) {
|
||||||
|
// fallback if mimeType not set
|
||||||
|
mimeType = QtNative.activity().getContentResolver().getType(uri);
|
||||||
|
Log.d("ekkescorner sendFile guessed mimeType:", mimeType);
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner sendFile w mimeType:", mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendIntent.setType(mimeType);
|
||||||
|
|
||||||
|
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
|
||||||
|
QtNative.activity().startActivity(Intent.createChooser(sendIntent, null));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean viewFile(String filePath, String title, String mimeType, int requestId) {
|
||||||
|
if (QtNative.activity() == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// using v4 support library create the Intent from ShareCompat
|
||||||
|
// Intent viewIntent = new Intent();
|
||||||
|
Intent viewIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
|
||||||
|
viewIntent.setAction(Intent.ACTION_VIEW);
|
||||||
|
|
||||||
|
File imageFileToShare = new File(filePath);
|
||||||
|
|
||||||
|
// Using FileProvider you must get the URI from FileProvider using your AUTHORITY
|
||||||
|
// Uri uri = Uri.fromFile(imageFileToShare);
|
||||||
|
Uri uri;
|
||||||
|
try {
|
||||||
|
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.d("ekkescorner viewFile - cannot be shared: ", filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// now we got a content URI per ex
|
||||||
|
// content://org.ekkescorner.examples.sharex.fileprovider/my_shared_files/qt-logo.png
|
||||||
|
// from a fileUrl:
|
||||||
|
// /data/user/0/org.ekkescorner.examples.sharex/files/share_example_x_files/qt-logo.png
|
||||||
|
Log.d("ekkescorner viewFile from file path: ", filePath);
|
||||||
|
Log.d("ekkescorner viewFile to content URI: ", uri.toString());
|
||||||
|
|
||||||
|
if(mimeType == null || mimeType.isEmpty()) {
|
||||||
|
// fallback if mimeType not set
|
||||||
|
mimeType = QtNative.activity().getContentResolver().getType(uri);
|
||||||
|
Log.d("ekkescorner viewFile guessed mimeType:", mimeType);
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner viewFile w mimeType:", mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewIntent.setDataAndType(uri, mimeType);
|
||||||
|
|
||||||
|
viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
viewIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
|
||||||
|
return createCustomChooserAndStartActivity(viewIntent, title, requestId, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean editFile(String filePath, String title, String mimeType, int requestId) {
|
||||||
|
if (QtNative.activity() == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// using v4 support library create the Intent from ShareCompat
|
||||||
|
// Intent editIntent = new Intent();
|
||||||
|
Intent editIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
|
||||||
|
editIntent.setAction(Intent.ACTION_EDIT);
|
||||||
|
|
||||||
|
File imageFileToShare = new File(filePath);
|
||||||
|
|
||||||
|
// Using FileProvider you must get the URI from FileProvider using your AUTHORITY
|
||||||
|
// Uri uri = Uri.fromFile(imageFileToShare);
|
||||||
|
Uri uri;
|
||||||
|
try {
|
||||||
|
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.d("ekkescorner editFile - cannot be shared: ", filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log.d("ekkescorner editFile", uri.toString());
|
||||||
|
|
||||||
|
if(mimeType == null || mimeType.isEmpty()) {
|
||||||
|
// fallback if mimeType not set
|
||||||
|
mimeType = QtNative.activity().getContentResolver().getType(uri);
|
||||||
|
Log.d("ekkescorner editFile guessed mimeType:", mimeType);
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner editFile w mimeType:", mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
editIntent.setDataAndType(uri, mimeType);
|
||||||
|
|
||||||
|
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
|
||||||
|
return createCustomChooserAndStartActivity(editIntent, title, requestId, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getContentName(ContentResolver cR, Uri uri) {
|
||||||
|
Cursor cursor = cR.query(uri, null, null, null, null);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
int nameIndex = cursor
|
||||||
|
.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||||
|
if (nameIndex >= 0) {
|
||||||
|
return cursor.getString(nameIndex);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String createFile(ContentResolver cR, Uri uri, String fileLocation) {
|
||||||
|
String filePath = null;
|
||||||
|
try {
|
||||||
|
InputStream iStream = cR.openInputStream(uri);
|
||||||
|
if (iStream != null) {
|
||||||
|
String name = getContentName(cR, uri);
|
||||||
|
if (name != null) {
|
||||||
|
filePath = fileLocation + "/" + name;
|
||||||
|
Log.d("ekkescorner - create File", filePath);
|
||||||
|
File f = new File(filePath);
|
||||||
|
FileOutputStream tmp = new FileOutputStream(f);
|
||||||
|
Log.d("ekkescorner - create File", "new FileOutputStream");
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while (iStream.read(buffer) > 0) {
|
||||||
|
tmp.write(buffer);
|
||||||
|
}
|
||||||
|
tmp.close();
|
||||||
|
iStream.close();
|
||||||
|
return filePath;
|
||||||
|
} // name
|
||||||
|
} // iStream
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return filePath;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return filePath;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
142
blueROCK.pro
|
@ -1,17 +1,8 @@
|
||||||
QT += quick qml quickcontrols2 purchasing widgets
|
QT += quick qml quickcontrols2
|
||||||
CONFIG += c++11
|
CONFIG += c++11
|
||||||
|
|
||||||
VERSION = 0.04
|
VERSION = 0.8.0
|
||||||
|
TARGET = blueROCK
|
||||||
android {
|
|
||||||
QT += androidextras
|
|
||||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-sources
|
|
||||||
}
|
|
||||||
|
|
||||||
ios {
|
|
||||||
QMAKE_ASSET_CATALOGS += resources/shared/Assets.xcassets
|
|
||||||
xcode_product_bundle_identifier_setting.value = "de.itsblue.bluerock"
|
|
||||||
}
|
|
||||||
|
|
||||||
# The following define makes your compiler emit warnings if you use
|
# The following define makes your compiler emit warnings if you use
|
||||||
# any Qt feature that has been marked deprecated (the exact warnings
|
# any Qt feature that has been marked deprecated (the exact warnings
|
||||||
|
@ -24,16 +15,30 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD/headers
|
||||||
|
|
||||||
|
# Add version to define
|
||||||
|
DEFINES += APP_VERSION=\"\\\"$${VERSION}\\\"\"
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
sources/shareUtils/platformshareutils.cpp \
|
||||||
sources/appsettings.cpp \
|
sources/appsettings.cpp \
|
||||||
sources/main.cpp \
|
sources/bluerockbackend.cpp \
|
||||||
sources/serverconn.cpp
|
sources/shareUtils/shareutils.cpp \
|
||||||
|
sources/main.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
headers/appsettings.h \
|
||||||
|
headers/bluerockbackend.h \
|
||||||
|
headers/shareUtils/shareutils.h \
|
||||||
|
headers/shareUtils/platformshareutils.h
|
||||||
|
|
||||||
RESOURCES += resources/qml/qml.qrc \
|
RESOURCES += resources/qml/qml.qrc \
|
||||||
resources/shared/shared.qrc \
|
resources/shared/shared.qrc \
|
||||||
#resources/shared/icons/bluerock/index.theme \
|
resources/translations/translations.qrc
|
||||||
#$$files(resources/shared/icons/*.png, true)
|
|
||||||
|
|
||||||
|
TRANSLATIONS += resources/translations/en.ts \
|
||||||
|
resources/translations/de.ts
|
||||||
|
|
||||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||||
QML_IMPORT_PATH =
|
QML_IMPORT_PATH =
|
||||||
|
@ -46,12 +51,103 @@ qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
!isEmpty(target.path): INSTALLS += target
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
headers/appsettings.h \
|
|
||||||
headers/serverconn.h
|
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
CHANGELOG.md \
|
CHANGELOG.md \
|
||||||
android-sources/AndroidManifest.xml \
|
README.md
|
||||||
resources/shared/icons/bluerock/index.theme \
|
|
||||||
$$files(resources/shared/icons/*.png, true)
|
android {
|
||||||
|
QT += androidextras
|
||||||
|
|
||||||
|
SOURCES += sources/shareUtils/androidshareutils.cpp
|
||||||
|
HEADERS += headers/shareUtils/androidshareutils.h
|
||||||
|
|
||||||
|
OTHER_FILES += android/src/org/ekkescorner/utils/QShareUtils.java \
|
||||||
|
android/src/org/ekkescorner/utils/QSharePathResolver.java \
|
||||||
|
android/src/de/itsblue/blueROCK/MainActivity.java \
|
||||||
|
android/AndroidManifest.xml \
|
||||||
|
android/build.gradle \
|
||||||
|
android/gradle.properties \
|
||||||
|
android/gradle/wrapper/gradle-wrapper.jar \
|
||||||
|
android/gradle/wrapper/gradle-wrapper.properties \
|
||||||
|
android/gradlew \
|
||||||
|
android/gradlew.bat \
|
||||||
|
android/res/values/libs.xml \
|
||||||
|
android/res/xml/filepaths.xml
|
||||||
|
|
||||||
|
defineReplace(droidVersionCode) {
|
||||||
|
segments = $$split(1, ".")
|
||||||
|
for (segment, segments): vCode = "$$first(vCode)$$format_number($$segment, width=3 zeropad)"
|
||||||
|
|
||||||
|
contains(ANDROID_TARGET_ARCH, arm64-v8a): \
|
||||||
|
suffix = 1
|
||||||
|
else:contains(ANDROID_TARGET_ARCH, armeabi-v7a): \
|
||||||
|
suffix = 0
|
||||||
|
# add more cases as needed
|
||||||
|
|
||||||
|
return($$first(vCode)$$first(suffix))
|
||||||
|
}
|
||||||
|
|
||||||
|
ANDROID_VERSION_NAME = $$VERSION
|
||||||
|
ANDROID_VERSION_CODE = $$droidVersionCode($$ANDROID_VERSION_NAME)
|
||||||
|
ANDROID_TARGET_SDK_VERSION = 31
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
HEADERS += headers/shareUtils/ios/iosshareutils.h \
|
||||||
|
headers/iospermissionutils.h \
|
||||||
|
headers/shareUtils/ios/docviewcontroller.h
|
||||||
|
|
||||||
|
OTHER_FILES += ios/Info.plist \
|
||||||
|
ios/blueROCK.entitlements
|
||||||
|
|
||||||
|
QMAKE_INFO_PLIST = ios/Info.plist
|
||||||
|
|
||||||
|
|
||||||
|
#QMAKE_IOS_DEPLOYMENT_TARGET = 12.0
|
||||||
|
|
||||||
|
#disable_warning.name = GCC_WARN_64_TO_32_BIT_CONVERSION
|
||||||
|
#disable_warning.value = NO
|
||||||
|
#QMAKE_MAC_XCODE_SETTINGS += disable_warning
|
||||||
|
|
||||||
|
# see https://bugreports.qt.io/browse/QTCREATORBUG-16968
|
||||||
|
# ios_signature.pri not part of project repo because of private signature details
|
||||||
|
# contains:
|
||||||
|
# QMAKE_XCODE_CODE_SIGN_IDENTITY = "iPhone Developer"
|
||||||
|
# MY_DEVELOPMENT_TEAM.name = DEVELOPMENT_TEAM
|
||||||
|
# MY_DEVELOPMENT_TEAM.value = your team Id from Apple Developer Account
|
||||||
|
# QMAKE_MAC_XCODE_SETTINGS += MY_DEVELOPMENT_TEAM
|
||||||
|
|
||||||
|
#include(ios_signature.pri)
|
||||||
|
|
||||||
|
QMAKE_ASSET_CATALOGS += resources/shared/Assets.xcassets
|
||||||
|
|
||||||
|
MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS
|
||||||
|
MY_ENTITLEMENTS.value = $$PWD/ios/blueROCK.entitlements
|
||||||
|
QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS
|
||||||
|
|
||||||
|
MY_BUNDLE_ID.name = PRODUCT_BUNDLE_IDENTIFIER
|
||||||
|
MY_BUNDLE_ID.value = de.itsblue.bluerock
|
||||||
|
QMAKE_MAC_XCODE_SETTINGS += MY_BUNDLE_ID
|
||||||
|
xcode_product_bundle_identifier_setting.value = "de.itsblue.bluerock"
|
||||||
|
PRODUCT_IDENTIFIER = de.itsblue.bluerock
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG += enable_decoder_qr_code \
|
||||||
|
enable_encoder_qr_code \
|
||||||
|
qzxing_multimedia \
|
||||||
|
qzxing_qml
|
||||||
|
|
||||||
|
include(qzxing/src/QZXing-components.pri)
|
||||||
|
|
||||||
|
# this has to be the last line!
|
||||||
|
ANDROID_ABIS = armeabi-v7a arm64-v8a
|
||||||
|
|
||||||
|
|
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
|
|
@ -10,34 +10,26 @@ class AppSettings : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(NOTIFY themeChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AppSettings(QObject *parent = nullptr);
|
explicit AppSettings(QObject *parent = nullptr);
|
||||||
// This is the Constructor of the AppSettings class
|
// This is the Constructor of the AppSettings class
|
||||||
|
|
||||||
~AppSettings();
|
~AppSettings();
|
||||||
// This is the Destructor of the AppSettings class
|
// This is the Destructor of the AppSettings class
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSettings *settingsManager;
|
QSettings *settingsManager;
|
||||||
// QSettings object which cares about our settings.ini file
|
// QSettings object which cares about our settings.ini file
|
||||||
|
|
||||||
QSettings *themeSettingsManager;
|
|
||||||
// QSettings object which cares about the themes
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void themeChanged();
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
Q_INVOKABLE QString read(const QString &key);
|
Q_INVOKABLE QString read(const QString &key);
|
||||||
// function to read values from the settings file
|
// function to read values from the settings file
|
||||||
|
|
||||||
Q_INVOKABLE void write(const QString &key, const QVariant &value);
|
Q_INVOKABLE void write(const QString &key, const QVariant &value);
|
||||||
// function to write values to the settings file
|
// function to write values to the settings file
|
||||||
|
|
||||||
Q_INVOKABLE void setDefault(const QString &key, const QVariant &defaultValue);
|
Q_INVOKABLE void setDefault(const QString &key, const QVariant &defaultValue);
|
||||||
// function to create a key (/ setting) with a default value if it hasnt been ceated yet
|
// function to create a key (/ setting) with a default value if it hasnt been ceated yet
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // APPSETTINGS_H
|
#endif // APPSETTINGS_H
|
||||||
|
|
81
headers/bluerockbackend.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
blueROCK - for digital rock
|
||||||
|
Copyright (C) 2019 Dorian Zedler
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SERVERCONN_H
|
||||||
|
#define SERVERCONN_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtNetwork>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QBrush>
|
||||||
|
#include <QTextCharFormat>
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include <QPageSize>
|
||||||
|
#include <QPdfWriter>
|
||||||
|
#include <QSizeF>
|
||||||
|
#include "QZXing.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
#include <QtAndroidExtras>
|
||||||
|
#elif defined Q_OS_IOS
|
||||||
|
#include "iospermissionutils.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "shareUtils/shareutils.h"
|
||||||
|
|
||||||
|
class BlueRockBackend : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit BlueRockBackend(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariantMap _senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery());
|
||||||
|
|
||||||
|
ShareUtils* _shareUtils;
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
IosPermissionUtils* _iosPermissionUtils;
|
||||||
|
#endif
|
||||||
|
const QStringList _validBaseDomains = {"digitalrock.de", "bluerock.dev"};
|
||||||
|
bool _pendingIntentsChecked;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
Q_INVOKABLE void openedViaUrl(QString url, QString scheme);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
Q_INVOKABLE QVariant getWidgetData(QVariantMap params);
|
||||||
|
Q_INVOKABLE QVariantMap getParamsFromUrl(QString url);
|
||||||
|
Q_INVOKABLE void shareResultsAsUrl(QString url, QString compName);
|
||||||
|
Q_INVOKABLE void shareResultsAsPoster(QString url, QString compName);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool isCameraPermissionGranted();
|
||||||
|
Q_INVOKABLE bool requestCameraPermission();
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
void onApplicationStateChanged(Qt::ApplicationState applicationState);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERVERCONN_H
|
22
headers/iospermissionutils.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef IOSPERMISSIONUTILS_H
|
||||||
|
#define IOSPERMISSIONUTILS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QEventLoop>
|
||||||
|
|
||||||
|
class IosPermissionUtils : QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
IosPermissionUtils();
|
||||||
|
bool isCameraPermissionGranted();
|
||||||
|
bool requestCameraPermission();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QEventLoop* _responseWaitLoop;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void permissionRequestFinished(bool result);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IOSPERMISSIONUTILS_H
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
blueROCK - for digital rock
|
|
||||||
Copyright (C) 2019 Dorian Zedler
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SERVERCONN_H
|
|
||||||
#define SERVERCONN_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QtNetwork>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QUrl>
|
|
||||||
|
|
||||||
class ServerConn : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ServerConn(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QVariantMap senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery());
|
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
QVariant getWidgetData(QVariantMap params);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SERVERCONN_H
|
|
51
headers/shareUtils/androidshareutils.h
Executable file
|
@ -0,0 +1,51 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||||
|
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
#ifndef ANDROIDSHAREUTILS_H
|
||||||
|
#define ANDROIDSHAREUTILS_H
|
||||||
|
|
||||||
|
#include <QtAndroid>
|
||||||
|
#include <QAndroidActivityResultReceiver>
|
||||||
|
|
||||||
|
#include "shareUtils/platformshareutils.h"
|
||||||
|
|
||||||
|
class AndroidShareUtils : public PlatformShareUtils, public QAndroidActivityResultReceiver
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AndroidShareUtils(QObject* parent = nullptr);
|
||||||
|
bool checkMimeTypeView(const QString &mimeType) override;
|
||||||
|
bool checkMimeTypeEdit(const QString &mimeType) override;
|
||||||
|
virtual QString getTemporaryFileLocationPath() override;
|
||||||
|
void shareText(const QString &text, const QUrl &url) override;
|
||||||
|
void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||||
|
void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||||
|
void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||||
|
|
||||||
|
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override;
|
||||||
|
void onActivityResult(int requestCode, int resultCode);
|
||||||
|
|
||||||
|
void checkPendingIntents(const QString workingDirPath) override;
|
||||||
|
|
||||||
|
static AndroidShareUtils* getInstance();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setFileUrlReceived(const QString &url);
|
||||||
|
void setOtherUrlReceived(const QString &url, const QString &scheme);
|
||||||
|
void setFileReceivedAndSaved(const QString &url);
|
||||||
|
bool checkFileExits(const QString &url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mIsEditMode;
|
||||||
|
qint64 mLastModified;
|
||||||
|
QString mCurrentFilePath;
|
||||||
|
|
||||||
|
static AndroidShareUtils* mInstance;
|
||||||
|
|
||||||
|
void processActivityResult(int requestCode, int resultCode);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // ANDROIDSHAREUTILS_H
|
21
headers/shareUtils/ios/docviewcontroller.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||||
|
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
#ifndef DOCVIEWCONTROLLER_HPP
|
||||||
|
#define DOCVIEWCONTROLLER_HPP
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import "iosshareutils.h"
|
||||||
|
|
||||||
|
@interface DocViewController : UIViewController <UIDocumentInteractionControllerDelegate>
|
||||||
|
|
||||||
|
@property int requestId;
|
||||||
|
|
||||||
|
@property IosShareUtils *mIosShareUtils;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // DOCVIEWCONTROLLER_HPP
|
31
headers/shareUtils/ios/iosshareutils.h
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||||
|
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
#ifndef __IOSSHAREUTILS_H__
|
||||||
|
#define __IOSSHAREUTILS_H__
|
||||||
|
|
||||||
|
#include "headers/shareUtils/platformshareutils.h"
|
||||||
|
|
||||||
|
class IosShareUtils : public PlatformShareUtils
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IosShareUtils(QObject *parent = 0);
|
||||||
|
bool checkMimeTypeView(const QString &mimeType) override;
|
||||||
|
bool checkMimeTypeEdit(const QString &mimeType) override;
|
||||||
|
void shareText(const QString &text, const QUrl &url) override;
|
||||||
|
void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||||
|
void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||||
|
void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
|
||||||
|
|
||||||
|
void handleDocumentPreviewDone(const int &requestId);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void handleFileUrlReceived(const QUrl &url);
|
||||||
|
void handleHttpsUrlReceived(const QUrl &url);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
51
headers/shareUtils/platformshareutils.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke)
|
||||||
|
// this project is based on ideas from
|
||||||
|
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||||
|
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||||
|
// also inspired by:
|
||||||
|
// https://www.androidcode.ninja/android-share-intent-example/
|
||||||
|
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||||
|
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||||
|
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||||
|
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
#ifndef PLATFORMSHAREUTILS_H
|
||||||
|
#define PLATFORMSHAREUTILS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class PlatformShareUtils : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void shareEditDone(int requestCode);
|
||||||
|
void shareFinished(int requestCode);
|
||||||
|
void shareNoAppAvailable(int requestCode);
|
||||||
|
void shareError(int requestCode, QString message);
|
||||||
|
void fileUrlReceived(QString url);
|
||||||
|
void otherUrlReceived(QString url, QString scheme);
|
||||||
|
void fileReceivedAndSaved(QString url);
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlatformShareUtils(QObject *parent = 0);
|
||||||
|
virtual ~PlatformShareUtils();
|
||||||
|
virtual bool checkMimeTypeView(const QString &mimeType);
|
||||||
|
virtual bool checkMimeTypeEdit(const QString &mimeType);
|
||||||
|
virtual QString getTemporaryFileLocationPath();
|
||||||
|
virtual void shareText(const QString &text, const QUrl &url);
|
||||||
|
virtual void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||||
|
virtual void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||||
|
virtual void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||||
|
|
||||||
|
virtual void checkPendingIntents(const QString workingDirPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PLATFORMSHAREUTILS_H
|
63
headers/shareUtils/shareutils.h
Executable file
|
@ -0,0 +1,63 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke)
|
||||||
|
// this project is based on ideas from
|
||||||
|
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||||
|
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||||
|
// also inspired by:
|
||||||
|
// https://www.androidcode.ninja/android-share-intent-example/
|
||||||
|
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||||
|
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||||
|
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
|
||||||
|
// my blog about Qt for mobile: http://j.mp/qt-x
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
#ifndef SHAREUTILS_H
|
||||||
|
#define SHAREUTILS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "shareUtils/platformshareutils.h"
|
||||||
|
|
||||||
|
class ShareUtils : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void shareEditDone(int requestCode);
|
||||||
|
void shareFinished(int requestCode);
|
||||||
|
void shareNoAppAvailable(int requestCode);
|
||||||
|
void shareError(int requestCode, QString message);
|
||||||
|
void fileUrlReceived(QString url);
|
||||||
|
void otherUrlReceived(QString url, QString scheme);
|
||||||
|
void fileReceivedAndSaved(QString url);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onShareEditDone(int requestCode);
|
||||||
|
void onShareFinished(int requestCode);
|
||||||
|
void onShareNoAppAvailable(int requestCode);
|
||||||
|
void onShareError(int requestCode, QString message);
|
||||||
|
void onFileUrlReceived(QString url);
|
||||||
|
void onOtherUrlReceived(QString url, QString scheme);
|
||||||
|
void onFileReceivedAndSaved(QString url);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ShareUtils(QObject *parent = 0);
|
||||||
|
Q_INVOKABLE bool checkMimeTypeView(const QString &mimeType);
|
||||||
|
Q_INVOKABLE bool checkMimeTypeEdit(const QString &mimeType);
|
||||||
|
Q_INVOKABLE QString getTemporaryFileLocationPath();
|
||||||
|
Q_INVOKABLE void shareText(const QString &text, const QUrl &url);
|
||||||
|
Q_INVOKABLE void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||||
|
Q_INVOKABLE void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||||
|
Q_INVOKABLE void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||||
|
Q_INVOKABLE void checkPendingIntents(const QString workingDirPath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PlatformShareUtils* mPlatformShareUtils;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //SHAREUTILS_H
|
45
ios/Info.plist
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${QMAKE_SHORT_VERSION}</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>${QMAKE_PKGINFO_TYPEINFO}</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${QMAKE_FULL_VERSION}</string>
|
||||||
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>https</string>
|
||||||
|
</array>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>${IPHONEOS_DEPLOYMENT_TARGET}</string>
|
||||||
|
<key>NOTE</key>
|
||||||
|
<string>This file was generated by Qt/QMake.</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>blueROCK would like to access the camera.</string>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
10
ios/blueROCK.entitlements
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.developer.associated-domains</key>
|
||||||
|
<array>
|
||||||
|
<string>applinks:l.bluerock.dev</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
1
qzxing
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6ea2b31e26db9d43db027ba207f5c73dc9d759fc
|
21
resources/qml/Components/AlignedButton.qml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property alias horizontalAlignment: label.horizontalAlignment
|
||||||
|
property alias verticalAlignment: label.verticalAlignment
|
||||||
|
|
||||||
|
contentItem: Label {
|
||||||
|
id: label
|
||||||
|
|
||||||
|
text: control.text
|
||||||
|
font: control.font
|
||||||
|
|
||||||
|
color: !control.enabled ? control.Material.hintTextColor :
|
||||||
|
control.flat && control.highlighted ? control.Material.accentColor :
|
||||||
|
control.highlighted ? control.Material.primaryHighlightedTextColor : control.Material.foreground
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ Item {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: toolBar
|
id: toolBar
|
||||||
color: "white"
|
color: Material.background
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
32
resources/qml/Components/BlueRockBadge.qml
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
resources/qml/Components/ColoredItemDelegate.qml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2017 The Qt Company Ltd.
|
||||||
|
** Contact: http://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL3$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at http://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 3 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/lgpl.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 2.0 or later as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.GPL included in
|
||||||
|
** the packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU General Public License version 2.0 requirements will be
|
||||||
|
** met: http://www.gnu.org/licenses/gpl-2.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Controls.Material 2.15
|
||||||
|
import QtQuick.Controls.Material.impl 2.15
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
Material.background: "transparent"
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitHeight: control.Material.delegateHeight
|
||||||
|
|
||||||
|
color: control.highlighted ? control.Material.listHighlightColor : control.Material.background
|
||||||
|
|
||||||
|
Ripple {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
clip: visible
|
||||||
|
pressed: control.pressed
|
||||||
|
anchor: control
|
||||||
|
active: control.down || control.visualFocus || control.hovered
|
||||||
|
color: control.Material.rippleColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
resources/qml/Components/CompetitionCalendarDelegate.qml
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
import QtQuick.Templates 2.12 as T
|
||||||
|
import QtQuick.Controls.impl 2.12
|
||||||
|
import QtQuick.Controls.Material.impl 2.12
|
||||||
|
|
||||||
|
ColoredItemDelegate {
|
||||||
|
id: competitionDel
|
||||||
|
|
||||||
|
property bool over
|
||||||
|
property var thisData: control.widgetData["competitions"][index]
|
||||||
|
|
||||||
|
property string name: thisData["name"]
|
||||||
|
property string date: thisData["date_span"]
|
||||||
|
property var cats: thisData["cats"]
|
||||||
|
property int catId: thisData["cat_id"] === undefined ? 0:thisData["cat_id"]
|
||||||
|
|
||||||
|
property bool thisIsFavored: control.compFavorites.indexOf(parseInt(thisData['WetId'])) >= 0
|
||||||
|
property bool includedByFavorites: control.displayedCompCats.indexOf(-1) >= 0 && thisIsFavored
|
||||||
|
property bool includedByFilter: control.displayedCompCats.indexOf(parseInt(thisData['cat_id'])) >= 0
|
||||||
|
property bool thisIsVisible: includedByFavorites || includedByFilter
|
||||||
|
|
||||||
|
function updateVisibility() {
|
||||||
|
competitionDel.includedByFilter = control.displayedCompCats.indexOf(parseInt(competitionDel.thisData['cat_id'])) >= 0
|
||||||
|
competitionDel.thisIsFavored = control.compFavorites.indexOf(parseInt(thisData['WetId'])) >= 0
|
||||||
|
competitionDel.includedByFavorites = control.displayedCompCats.indexOf(-1) >= 0 && thisIsFavored
|
||||||
|
}
|
||||||
|
|
||||||
|
width: control.width
|
||||||
|
height: thisIsVisible ? compDelCol.height + 10 : 0
|
||||||
|
|
||||||
|
enabled: ((thisData["cats"] !== undefined && thisData["cats"].length > 0) || competitionDel.thisData["homepage"] !== undefined || getCompInfoUrls(index).length > 0) && height > 0
|
||||||
|
//visible: includedByFilter
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
scale: 0.9
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: control
|
||||||
|
function onDisplayedCompCatsChanged() {
|
||||||
|
competitionDel.updateVisibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCompFavoritesChanged() {
|
||||||
|
competitionDel.updateVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onThisDataChanged: {
|
||||||
|
if(thisIsVisible){
|
||||||
|
fadeInPa.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onThisIsVisibleChanged: {
|
||||||
|
if(thisIsVisible){
|
||||||
|
fadeInPa.start()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fadeOutPa.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
control.openComp(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: fadeInPa
|
||||||
|
NumberAnimation { target: competitionDel; property: "opacity"; from: 0; to: 1.0; duration: 400 }
|
||||||
|
NumberAnimation { target: competitionDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: fadeOutPa
|
||||||
|
NumberAnimation { target: competitionDel; property: "opacity"; from: 1; to: 0; duration: 400 }
|
||||||
|
NumberAnimation { target: competitionDel; property: "scale"; from: 1; to: 0.8; duration: 400 }
|
||||||
|
}
|
||||||
|
|
||||||
|
Material.background: control.getCompCatData(catId) === undefined ? app.federalColor:control.getCompCatData(catId)["bgcolor"]
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: compDelCol
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: parent.width * 0.97
|
||||||
|
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
Label {
|
||||||
|
id: nameLa
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
|
||||||
|
text: name
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: bookmarkTb
|
||||||
|
|
||||||
|
text: "\uf005"
|
||||||
|
font.family: competitionDel.thisIsFavored ? fa5solid.name : fa5regular.name
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
control.editFavorites(!competitionDel.thisIsFavored, parseInt(thisData['WetId']))
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.alignment: Layout.Right
|
||||||
|
|
||||||
|
Behavior on font.family {
|
||||||
|
SequentialAnimation {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "scale"
|
||||||
|
target: bookmarkTb
|
||||||
|
duration: 75
|
||||||
|
to: 0.8
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "scale"
|
||||||
|
target: bookmarkTb
|
||||||
|
duration: 75
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: dateLa
|
||||||
|
|
||||||
|
text: date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: bottomLineRa
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 1
|
||||||
|
|
||||||
|
color: "lightgrey"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,21 +64,6 @@ ListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoArea {
|
|
||||||
id: infoArea
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
left: control.left
|
|
||||||
right: control.right
|
|
||||||
top: control.top
|
|
||||||
margins: app.landscape() ? control.width * 0.4:control.width * 0.3
|
|
||||||
topMargin: control.height*( status === 901 ? 0.6:0.5) - height * 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
excludedCodes: [200, 902, 905]
|
|
||||||
errorCode: control.status
|
|
||||||
}
|
|
||||||
|
|
||||||
PullRefresher {
|
PullRefresher {
|
||||||
target: control
|
target: control
|
||||||
|
|
||||||
|
|
59
resources/qml/Components/DisclaimerDialog.qml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Controls.Material 2.15
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
title: ""
|
||||||
|
property string content: ""
|
||||||
|
|
||||||
|
parent: Overlay.overlay
|
||||||
|
|
||||||
|
x: (parent.width - width) * 0.5
|
||||||
|
y: (parent.height - height) * 0.5
|
||||||
|
|
||||||
|
implicitWidth: parent.width * 0.9
|
||||||
|
|
||||||
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||||
|
contentHeight + topPadding + bottomPadding
|
||||||
|
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
|
||||||
|
+ (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
|
||||||
|
|
||||||
|
//width: app.width * 0.8
|
||||||
|
//height: implicitHeight
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
|
||||||
|
standardButtons: Dialog.Close
|
||||||
|
|
||||||
|
header: Label {
|
||||||
|
text: control.title
|
||||||
|
visible: control.title
|
||||||
|
elide: Label.ElideRight
|
||||||
|
padding: 24
|
||||||
|
bottomPadding: 0
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 16
|
||||||
|
background: Rectangle {
|
||||||
|
radius: 2
|
||||||
|
color: control.Material.dialogColor
|
||||||
|
clip: true
|
||||||
|
}
|
||||||
|
onLinkActivated: {
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Label {
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
width: control.parent * 0.8
|
||||||
|
|
||||||
|
text: control.content
|
||||||
|
|
||||||
|
onLinkActivated: {
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,47 +37,25 @@ BusyIndicator {
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
property int currentHeight: 0
|
RotationAnimation {
|
||||||
|
target: blueRockHoldIcon
|
||||||
SequentialAnimation {
|
|
||||||
running: control.running
|
running: control.running
|
||||||
|
duration: control.animationSpeed
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
|
from: 0
|
||||||
NumberAnimation {
|
to: 360
|
||||||
target: item
|
easing.type: Easing.InOutQuad
|
||||||
property: "currentHeight"
|
|
||||||
from: 0
|
|
||||||
to: 800
|
|
||||||
duration: control.animationSpeed
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: blueRockHoldIcon
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
spacing: item.width / 9
|
source: "qrc:/icons/blueRockHold.png"
|
||||||
|
mipmap: true
|
||||||
Repeater {
|
|
||||||
id: repeater
|
|
||||||
model: 5
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
|
|
||||||
property double heightMultiplier: Math.abs( Math.sin( ( ((item.currentHeight/100) + (index*(control.formFactor/repeater.model)))) * (Math.PI/8) ) )
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
width: item.width / 9
|
|
||||||
height: ( heightMultiplier ) * ( item.height - 1 ) + 1
|
|
||||||
|
|
||||||
radius: width * 0.5
|
|
||||||
|
|
||||||
color: control.lineColor
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,18 +19,20 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
import QtQuick.Controls.Material.impl 2.12
|
||||||
|
|
||||||
ToolButton {
|
MouseArea {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property string image
|
property string image
|
||||||
property color backgroundColor: "white"
|
property color backgroundColor: "white"
|
||||||
property color textColor: "black"
|
property color textColor: "black"
|
||||||
property real imageScale: 1
|
property real imageScale: 1.3
|
||||||
property double glowRadius: 0.001
|
property double glowRadius: 0.001
|
||||||
property double glowSpread: 0.2
|
property double glowSpread: 0.2
|
||||||
property bool glowVisible: true
|
property bool glowVisible: true
|
||||||
property double glowScale: 0.75
|
property double glowScale: 0.92
|
||||||
property double glowOpacity: 1
|
property double glowOpacity: 1
|
||||||
|
|
||||||
Behavior on backgroundColor {
|
Behavior on backgroundColor {
|
||||||
|
@ -39,7 +41,7 @@ ToolButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
Item {
|
||||||
id: controlBackgroundContainer
|
id: controlBackgroundContainer
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -65,9 +67,9 @@ ToolButton {
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
radius: height * 0.5
|
radius: height * 0.2
|
||||||
|
|
||||||
color: control.down ? Qt.darker(control.backgroundColor, 1.03) : control.backgroundColor
|
color: Material.dialogColor //control.down ? Qt.darker(control.backgroundColor, 1.03) : control.backgroundColor
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: buttonIcon
|
id: buttonIcon
|
||||||
|
@ -84,22 +86,30 @@ ToolButton {
|
||||||
scale: control.imageScale
|
scale: control.imageScale
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Ripple {
|
||||||
ColorAnimation {
|
id: ripple
|
||||||
duration: 100
|
visible: true
|
||||||
|
clipRadius: controlBackground.radius
|
||||||
|
clip: true
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
pressed: control.pressed
|
||||||
|
anchor: control
|
||||||
|
active: control.pressed || control.visualFocus || control.containsMouse
|
||||||
|
color: control.Material.rippleColor
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Item {
|
||||||
|
width: ripple.width
|
||||||
|
height: ripple.height
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: controlBackground.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
|
||||||
id: conetntText
|
|
||||||
text: qsTr(control.text)
|
|
||||||
anchors.centerIn: parent
|
|
||||||
font: parent.font
|
|
||||||
color: control.textColor
|
|
||||||
opacity: control.enabled ? 1:0.4
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
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])]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
90
resources/qml/Components/MovingLabel.qml
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property alias text: firstLabel.text
|
||||||
|
property alias font: firstLabel.font
|
||||||
|
property alias verticalAlignment: firstLabel.verticalAlignment
|
||||||
|
property int spacing: 30
|
||||||
|
|
||||||
|
property MovingLabel syncWithLabel
|
||||||
|
property int _spacing: syncWithLabel && syncWithLabel._labelWidth > _labelWidth ? (syncWithLabel._labelWidth - _labelWidth + syncWithLabel.spacing) : spacing
|
||||||
|
property alias _labelWidth: firstLabel.width
|
||||||
|
|
||||||
|
signal linkActivated(string link)
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
height: firstLabel.height
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
_resetScroll()
|
||||||
|
if(control.syncWithLabel)
|
||||||
|
control.syncWithLabel._resetScroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
function startScroll(triggerSyncedLabel=true) {
|
||||||
|
if(control.syncWithLabel && triggerSyncedLabel)
|
||||||
|
control.syncWithLabel.startScroll(false)
|
||||||
|
if(control.width < firstLabel.width && !scrollAnimation.running)
|
||||||
|
scrollAnimation.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _resetScroll() {
|
||||||
|
scrollAnimation.stop()
|
||||||
|
firstLabel.anchors.leftMargin = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: firstLabel
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
onLinkActivated: control.onLinkActivated(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: secondLabel
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: firstLabel.right
|
||||||
|
leftMargin: control._spacing
|
||||||
|
verticalCenter: firstLabel.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: scrollAnimation.running
|
||||||
|
|
||||||
|
font: firstLabel.font
|
||||||
|
text: firstLabel.text
|
||||||
|
verticalAlignment: firstLabel.verticalAlignment
|
||||||
|
elide: firstLabel.elide
|
||||||
|
bottomPadding: firstLabel.bottomPadding
|
||||||
|
padding: firstLabel.padding
|
||||||
|
|
||||||
|
onLinkActivated: control.onLinkActivated(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: control.startScroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: scrollAnimation
|
||||||
|
target: firstLabel
|
||||||
|
property: "anchors.leftMargin"
|
||||||
|
from: 0
|
||||||
|
to: -(firstLabel.width + control._spacing)
|
||||||
|
duration: (to / -100) * 1500
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
if(!running)
|
||||||
|
control._resetScroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -32,14 +33,10 @@ Item {
|
||||||
property int postRefreshDelay: 1000 // delay after reload funcion has finished
|
property int postRefreshDelay: 1000 // delay after reload funcion has finished
|
||||||
property int preRefreshDelay: 1000 // delay before reload funcion is called
|
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 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
|
|
||||||
//property color busyIndicatorColor: "pink" // color for the pre-defined busy indicator
|
|
||||||
|
|
||||||
readonly property double dragProgress: Math.min( userPosition / dragOutPosition, 1)
|
readonly property double dragProgress: Math.min( userPosition / dragOutPosition, 1)
|
||||||
|
|
||||||
|
@ -61,7 +58,7 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
color: control.backgroundColor
|
color: Material.dialogColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property Component busyIndicator: BusyIndicator { running: true }
|
property Component busyIndicator: BusyIndicator { running: true }
|
||||||
|
@ -107,7 +104,7 @@ Item {
|
||||||
ctx.reset()
|
ctx.reset()
|
||||||
|
|
||||||
ctx.lineWidth = lineWidth;
|
ctx.lineWidth = lineWidth;
|
||||||
ctx.strokeStyle = control.pullIndicatorColor;
|
ctx.strokeStyle = control.Material.foreground;
|
||||||
|
|
||||||
// middle line
|
// middle line
|
||||||
ctx.moveTo(width/2, topMargin);
|
ctx.moveTo(width/2, topMargin);
|
||||||
|
@ -162,7 +159,7 @@ Item {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: control.target
|
target: control.target
|
||||||
onDragEnded: {
|
function onDragEnded() {
|
||||||
if(userPosition >= control.dragOutPosition * control.dragRefreshPositionMultiplier){
|
if(userPosition >= control.dragOutPosition * control.dragRefreshPositionMultiplier){
|
||||||
control.state = "refreshing"
|
control.state = "refreshing"
|
||||||
preRefreshTimer.start()
|
preRefreshTimer.start()
|
||||||
|
|
158
resources/qml/Components/ResultDelegate.qml
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuick.Controls.Material 2.15
|
||||||
|
|
||||||
|
ColoredItemDelegate {
|
||||||
|
id: partDel
|
||||||
|
|
||||||
|
property int thisIndex: index
|
||||||
|
property var thisData: widgetData[ "participants" ][partDel.thisIndex]
|
||||||
|
|
||||||
|
width: control.width
|
||||||
|
height: partDelCol.showSideBySide ? 40:70
|
||||||
|
|
||||||
|
text: ""
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
scale: 0.9
|
||||||
|
|
||||||
|
onThisDataChanged: {
|
||||||
|
fadeInPa.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
app.openWidget({person:thisData["PerId"]})
|
||||||
|
}
|
||||||
|
|
||||||
|
highlighted: partDel.thisIndex % 2 == 0
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
|
||||||
|
property bool showSideBySide: app.landscape() || app.largeScreen()
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 5
|
||||||
|
|
||||||
|
columns: showSideBySide ? 2:1
|
||||||
|
rows: showSideBySide ? 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: partDelCol.showSideBySide ? parent.width * 0.5 : parent.width
|
||||||
|
Layout.preferredHeight: partDelCol.showSideBySide ? parent.height : parent.height * 0.5
|
||||||
|
|
||||||
|
visible: boulderResRow.active || generalResRow.active || speedQualificationResRow.active || resultLa.active
|
||||||
|
|
||||||
|
BoulderResultRow {
|
||||||
|
id: boulderResRow
|
||||||
|
}
|
||||||
|
|
||||||
|
GeneralResultRow {
|
||||||
|
id: generalResRow
|
||||||
|
}
|
||||||
|
|
||||||
|
SpeedQualificationResultRow {
|
||||||
|
id: speedQualificationResRow
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: resultLa
|
||||||
|
|
||||||
|
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: active
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
font.pixelSize: Math.abs( height * 0.6 )
|
||||||
|
minimumPixelSize: 1
|
||||||
|
|
||||||
|
text: widgetData[ "participants" ][partDel.thisIndex]["result"] === undefined ? "":widgetData[ "participants" ][partDel.thisIndex]["result"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
resources/qml/Components/SelectorPopup.qml
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Controls.Material 2.3
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property var dataObj
|
||||||
|
property string subTitle: ""
|
||||||
|
|
||||||
|
signal selectionFinished(int index, var data)
|
||||||
|
signal linkActivated(string link)
|
||||||
|
|
||||||
|
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: headerLa.height + headerTopSpacerItm.height + (control.subTitle ? headerSubLa.height:0)
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: headerTopSpacerItm
|
||||||
|
height: control.padding
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
|
||||||
|
MovingLabel {
|
||||||
|
id: headerLa
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: selectorPuHeaderCol.width - control.padding * 2
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 16
|
||||||
|
|
||||||
|
text: control.title
|
||||||
|
|
||||||
|
onLinkActivated: control.linkActivated(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: headerSubLa
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: selectorPuHeaderCol.width - control.padding * 2
|
||||||
|
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
topPadding: 5
|
||||||
|
bottomPadding: 0
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 16
|
||||||
|
|
||||||
|
text: control.subTitle
|
||||||
|
|
||||||
|
onLinkActivated: {
|
||||||
|
control.linkActivated(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
resources/qml/Components/SharePopup.qml
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QZXing 3.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property string _shareUrl
|
||||||
|
property string _compName
|
||||||
|
|
||||||
|
parent: Overlay.overlay
|
||||||
|
|
||||||
|
x: (parent.width - width) * 0.5
|
||||||
|
y: (parent.height - height) * 0.5
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
//% "Share these results"
|
||||||
|
title: qsTrId("#shareResultsHeadline")
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
shareComponentLoader.sourceComponent = null
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Loader {
|
||||||
|
id: shareComponentLoader
|
||||||
|
|
||||||
|
asynchronous: false
|
||||||
|
sourceComponent: null
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: shareComponent
|
||||||
|
StackLayout {
|
||||||
|
id: stackLayout
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: menuRow
|
||||||
|
Repeater {
|
||||||
|
id: buttonRepeater
|
||||||
|
property var buttons: [
|
||||||
|
//% "Link"
|
||||||
|
["\uf0c1", qsTrId("#shareByLink"), serverConn.shareResultsAsUrl],
|
||||||
|
//% "QR-Code"
|
||||||
|
["\uf029", qsTrId("#shareByQrCode"), function() {
|
||||||
|
stackLayout.currentIndex = 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
//% "Poster"
|
||||||
|
["\uf1c1", qsTrId("#shareByPoster"), serverConn.shareResultsAsPoster],
|
||||||
|
]
|
||||||
|
|
||||||
|
model: buttons
|
||||||
|
|
||||||
|
delegate: Button {
|
||||||
|
flat: true
|
||||||
|
font.family: fa5solid.name
|
||||||
|
text: "<font size=\"+4\">" + modelData[0] + "</font><br><br> " + modelData[1] + " "
|
||||||
|
onClicked: buttonRepeater.buttons[index][2](_shareUrl, _compName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: qrCodeImage
|
||||||
|
|
||||||
|
property int finalSize: app.landscape() ? app.height * 0.8 : app.width * 0.8
|
||||||
|
property int size: stackLayout.currentIndex === 1 ? finalSize:menuRow.height
|
||||||
|
|
||||||
|
Layout.preferredHeight: size
|
||||||
|
Layout.preferredWidth: size
|
||||||
|
|
||||||
|
sourceSize.width: finalSize
|
||||||
|
sourceSize.height: finalSize
|
||||||
|
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
source: "image://QZXing/encode/" + _shareUrl + "?border=true&correctionLevel=H"
|
||||||
|
|
||||||
|
RectangularGlow {
|
||||||
|
id: effect
|
||||||
|
anchors.fill: blurRockLogoBackgroundRect
|
||||||
|
glowRadius: 0
|
||||||
|
spread: 0
|
||||||
|
opacity: 0.8
|
||||||
|
color: "black"
|
||||||
|
cornerRadius: blurRockLogoBackgroundRect.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: blurRockLogoBackgroundRect
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width * 0.25
|
||||||
|
height: width
|
||||||
|
radius: height * 0.2
|
||||||
|
color: "white"
|
||||||
|
Image {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width * 0.8
|
||||||
|
height: width
|
||||||
|
mipmap: true
|
||||||
|
source: "qrc:/icons/blueRockHold.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on size {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appear(shareUrl, compName) {
|
||||||
|
_shareUrl = shareUrl
|
||||||
|
_compName = compName
|
||||||
|
shareComponentLoader.sourceComponent = shareComponent
|
||||||
|
control.open()
|
||||||
|
}
|
||||||
|
}
|
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,
|
||||||
|
};
|
||||||
|
}
|
|
@ -20,392 +20,326 @@ import QtQuick 2.10
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQuick.Controls.Material 2.1
|
import QtQuick.Controls.Material 2.1
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
ListView {
|
import "SpeedFlowChart.js" as SpeedFlowChart
|
||||||
|
|
||||||
|
Item {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property var flowchartData
|
property var flowchartData
|
||||||
property var allFlowchartData
|
property var allFlowchartData
|
||||||
property int rounds: 0
|
property int rounds: 0
|
||||||
property int tileSize: app.height / 8 * 0.8
|
|
||||||
|
|
||||||
property int refreshes: 0
|
property int refreshes: 0
|
||||||
property int roundRefreshes: 1
|
property int roundRefreshes: 1
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 10
|
|
||||||
|
|
||||||
spacing: app.width * 0.1
|
|
||||||
|
|
||||||
orientation: ListView.LeftToRight
|
|
||||||
boundsBehavior: ListView.StopAtBounds
|
|
||||||
|
|
||||||
onFlowchartDataChanged: {
|
onFlowchartDataChanged: {
|
||||||
prepareData()
|
prepareData()
|
||||||
}
|
}
|
||||||
|
|
||||||
model: 0
|
|
||||||
|
|
||||||
function prepareData() {
|
function prepareData() {
|
||||||
|
|
||||||
if(!control.enabled || control.flowchartData === undefined || control.flowchartData['route_names'] === undefined)
|
if(!control.enabled || control.flowchartData === undefined || control.flowchartData['route_names'] === undefined)
|
||||||
return
|
return
|
||||||
/*refreshes += 1
|
|
||||||
|
|
||||||
if(refreshes > 2){
|
var flowchartResult = SpeedFlowChart.convertResultsToSpeedFlowchartResult(control.flowchartData)
|
||||||
roundRefreshes += 1
|
const l = flowchartResult.rounds.length;
|
||||||
}
|
const dummy = { dummy: true };
|
||||||
|
flowchartResult.rounds[l - 2].pairs = [
|
||||||
console.log("refreshes: " + refreshes + " rounds: " + roundRefreshes)
|
dummy,
|
||||||
|
...flowchartResult.rounds[l - 1].pairs,
|
||||||
// create competition like data (just testing)
|
...flowchartResult.rounds[l - 2].pairs,
|
||||||
for(var part in flowchartData['participants']){
|
];
|
||||||
if(flowchartData['participants'].hasOwnProperty(part)){
|
flowchartResult.rounds[l - 2].roundName = `${
|
||||||
|
flowchartResult.rounds[l - 1].roundName
|
||||||
for(var r = 2 + roundRefreshes; r < 7; r++){
|
} / ${flowchartResult.rounds[l - 2].roundName}`;
|
||||||
delete flowchartData['participants'][part]["result"+r]
|
flowchartResult.rounds.pop();
|
||||||
delete flowchartData['participants'][part]["result_rank"+r]
|
control.allFlowchartData = flowchartResult
|
||||||
}
|
|
||||||
|
|
||||||
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.model = (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 {
|
ListView {
|
||||||
id: roundCol
|
id: roundListView
|
||||||
|
|
||||||
property int thisIndex: index
|
property int columnWidth: height * 0.4
|
||||||
property int thisRound: thisRoundIsValid ? control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-2]:-1
|
property int columnHeight: height
|
||||||
property bool thisRoundIsValid: control.allFlowchartData !== undefined && control.allFlowchartData[roundCol.thisIndex] !== undefined && control.allFlowchartData[roundCol.thisIndex].length > 2
|
|
||||||
property bool thisIsLastRound: thisIndex === control.model - 1
|
|
||||||
|
|
||||||
width: app.width * 0.5
|
anchors {
|
||||||
height: control.height
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
//spacing: app.width * 0.1
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
Label {
|
|
||||||
id: roundNameLa
|
|
||||||
width: parent.width
|
|
||||||
height: control.height * 0.05
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.pixelSize: height * 0.6
|
|
||||||
minimumPixelSize: 1
|
|
||||||
font.bold: true
|
|
||||||
text: roundCol.thisRoundIsValid && control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-1] !== undefined ? control.allFlowchartData[roundCol.thisIndex][control.allFlowchartData[roundCol.thisIndex].length-1] : "-"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
width: Math.min((columnWidth + spacing) * model, control.width)
|
||||||
id: rectRep
|
|
||||||
model: Math.max( Math.pow(2, control.model-1) * Math.pow(0.5, (index)), 2)
|
|
||||||
delegate: Item {
|
|
||||||
id: matchItm
|
|
||||||
|
|
||||||
property bool lowerPart: (index%2 > 0)
|
spacing: app.height * 0.05
|
||||||
|
|
||||||
property var matchData: roundCol.thisRoundIsValid ? control.allFlowchartData[ thisIsSmallFinal ? roundCol.thisIndex+1 : roundCol.thisIndex][ thisIsSmallFinal ? 0:matchItm.thisIndex]:undefined
|
orientation: ListView.LeftToRight
|
||||||
|
boundsBehavior: ListView.StopAtBounds
|
||||||
|
|
||||||
property int thisIndex: index
|
model: control.allFlowchartData.rounds.length
|
||||||
property int thisRound: parseInt(roundCol.thisRound) - (thisIsSmallFinal ? 1:0)
|
|
||||||
property var thisMatchData: thisMatchDataIsValid ? matchData:[]
|
|
||||||
|
|
||||||
property bool thisMatchDataIsValid: (matchData !== undefined && matchData !== null && typeof matchData === "object" && matchData.length > 0)
|
delegate: Item {
|
||||||
property bool thisMatchIsOver: thisMatchDataIsValid && thisMatchData[0]['result_rank'+thisRound] !== undefined && thisMatchData[1]['result_rank'+thisRound] !== undefined
|
id: roundItem
|
||||||
|
|
||||||
property bool thisIsFinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 2
|
property int thisIndex: index
|
||||||
property bool thisIsSmallFinal: roundCol.thisIsLastRound && thisIndex === rectRep.model - 1
|
property var thisData: control.allFlowchartData.rounds[thisIndex]
|
||||||
|
|
||||||
property int winnerIndex: thisMatchIsOver ? (parseInt(thisMatchData[0]['result_rank'+thisRound]) < parseInt(thisMatchData[1]['result_rank'+thisRound]) ? 0:1) : -1
|
property bool thisRoundIsValid: true
|
||||||
|
|
||||||
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 )
|
property bool thisIsLastRound: thisIndex === roundListView.model - 1
|
||||||
width: parent.width
|
property bool thisIsSemiFinal: thisIndex === roundListView.model - 2
|
||||||
|
|
||||||
onMatchDataChanged: {
|
property int tileSize: (roundItem.height / 8 - roundNameLa.height) * 1.45
|
||||||
fadeInPa.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelAnimation {
|
width: roundListView.columnWidth
|
||||||
id: fadeInPa
|
height: roundListView.columnHeight
|
||||||
NumberAnimation { target: matchItm; property: "opacity"; from: 0; to: 1.0; duration: 400 }
|
|
||||||
NumberAnimation { target: matchItm; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Canvas {
|
Column {
|
||||||
id: lineCanvas
|
id: roundCol
|
||||||
width: app.width; height: app.height
|
|
||||||
contextType: "2d"
|
|
||||||
|
|
||||||
visible: !roundCol.thisIsLastRound
|
anchors.fill: parent
|
||||||
|
|
||||||
property int targetX: matchItm.width + control.spacing
|
Label {
|
||||||
property int targetY: matchItm.lowerPart ? 0:matchItm.height
|
id: roundNameLa
|
||||||
|
|
||||||
Path {
|
|
||||||
id: myPath
|
|
||||||
startX: matchRect.x + matchRect.width * matchRect.scale ; startY: matchRect.y + matchRect.height * 0.5
|
|
||||||
|
|
||||||
PathCurve { x: lineCanvas.targetX ; y: lineCanvas.targetY }
|
|
||||||
}
|
|
||||||
|
|
||||||
onPaint: {
|
|
||||||
context.strokeStyle = Qt.rgba(.4,.6,.8);
|
|
||||||
context.path = myPath;
|
|
||||||
context.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: matchRect
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
centerIn: !matchItm.thisIsFinal ? parent:undefined
|
|
||||||
bottom: matchItm.thisIsFinal ? parent.bottom:undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
//anchors.verticalCenterOffset: matchItm.lowerPart ? 10:10
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: control.tileSize
|
height: control.height * 0.05
|
||||||
//scale: 0.9
|
horizontalAlignment: Text.AlignHCenter
|
||||||
color: "white"
|
verticalAlignment: Text.AlignVCenter
|
||||||
border.color: "lightgrey"
|
fontSizeMode: Text.Fit
|
||||||
border.width: 1
|
font.pixelSize: height * 0.6
|
||||||
radius: height * 0.2
|
minimumPixelSize: 1
|
||||||
Material.elevation: 10
|
font.bold: true
|
||||||
|
text: roundItem.thisData.roundName ?
|
||||||
|
roundItem.thisData.roundName :
|
||||||
|
"-"
|
||||||
|
}
|
||||||
|
|
||||||
Grid {
|
Repeater {
|
||||||
columns: app.landscape() ? 2:0
|
id: rectRep
|
||||||
rows: app.landscape() ? 0:2
|
model: roundItem.thisData.pairs.length
|
||||||
|
|
||||||
spacing: app.landscape() ? height * 0.4:0
|
delegate: Item {
|
||||||
|
id: matchItm
|
||||||
|
|
||||||
anchors.fill: parent
|
property bool lowerPart: (index%2 > 0)
|
||||||
anchors.margins: matchRect.radius * 0.5
|
property var thisData: roundItem.thisData.pairs[index]
|
||||||
|
property int thisIndex: index
|
||||||
|
|
||||||
Repeater {
|
height: (roundItem.height - roundNameLa.height) / rectRep.model - roundCol.spacing
|
||||||
// for the two athletes
|
width: roundItem.width
|
||||||
model: 2
|
|
||||||
|
|
||||||
delegate: RowLayout {
|
onThisDataChanged: {
|
||||||
|
fadeInPa.start()
|
||||||
|
}
|
||||||
|
|
||||||
height: app.landscape() ? parent.height:parent.height / 2 - parent.spacing
|
ParallelAnimation {
|
||||||
width: app.landscape() ? parent.width / 2 - parent.spacing : parent.width
|
id: fadeInPa
|
||||||
|
NumberAnimation { target: matchItm; property: "opacity"; from: 0; to: 1.0; duration: 400 }
|
||||||
|
NumberAnimation { target: matchItm; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
|
||||||
|
}
|
||||||
|
|
||||||
spacing: app.landscape() ? height * 0.1:height * 0.1
|
Canvas {
|
||||||
|
id: lineCanvas
|
||||||
|
width: app.width; height: app.height
|
||||||
|
contextType: "2d"
|
||||||
|
|
||||||
Label {
|
visible: !roundItem.thisIsLastRound
|
||||||
Layout.preferredHeight: parent.height
|
|
||||||
|
|
||||||
height: parent.height
|
property int targetYFactor: matchItm.lowerPart ? -1:1
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
Path {
|
||||||
font.pixelSize: height * 0.7
|
id: firstPartPath
|
||||||
fontSizeMode: Text.Fit
|
startX: matchItm.width
|
||||||
minimumPixelSize: 1
|
startY: matchItm.height * 0.5
|
||||||
|
|
||||||
Layout.alignment: Layout.Left
|
PathQuad {
|
||||||
|
id: firstPartPathQuad
|
||||||
text: matchItm.thisMatchData[index] !== undefined ? (parseInt(matchItm.thisMatchData[index]['result_rank0']) < 10 ?matchItm.thisMatchData[index]['result_rank0'] + " ":matchItm.thisMatchData[index]['result_rank0']): ""
|
relativeX: roundListView.spacing * 0.5
|
||||||
|
relativeY: matchItm.height * 0.25 * lineCanvas.targetYFactor
|
||||||
|
relativeControlX: relativeX
|
||||||
|
relativeControlY: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
PathQuad {
|
||||||
Layout.preferredHeight: parent.height
|
relativeX: roundListView.spacing * 0.5
|
||||||
|
relativeY: matchItm.height * 0.25 * lineCanvas.targetYFactor
|
||||||
height: parent.height
|
relativeControlX: 0
|
||||||
|
relativeControlY: relativeY
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
//horizontalAlignment: app.landscape() ? Text.AlignLeft : Text.AlignLeft
|
|
||||||
font.pixelSize: app.landscape() ? height * 0.4 : height * 0.6
|
|
||||||
minimumPixelSize: (app.landscape() ? height * 0.35 : height * 0.5) < 1 ? 1 : (app.landscape() ? height * 0.35 : height * 0.5)
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.bold: matchItm.winnerIndex === index
|
|
||||||
elide: "ElideRight"
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
color: matchItm.winnerIndex === index ? "green":"black"
|
|
||||||
|
|
||||||
text: matchItm.thisMatchData[index] !== undefined ? matchItm.thisMatchData[index]['firstname'] + " " + matchItm.thisMatchData[index]['lastname'] :"-"
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
onPaint: {
|
||||||
Layout.preferredHeight: parent.height
|
context.strokeStyle = "lightgrey"
|
||||||
|
context.lineWidth = matchRect.height * 0.05
|
||||||
|
context.path = firstPartPath;
|
||||||
|
context.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
height: parent.height
|
RectangularGlow {
|
||||||
|
id: effect
|
||||||
|
visible: matchRect.visible
|
||||||
|
anchors.fill: matchRect
|
||||||
|
glowRadius: 0
|
||||||
|
spread: 0
|
||||||
|
opacity: 0.3
|
||||||
|
color: "black"
|
||||||
|
cornerRadius: matchRect.radius
|
||||||
|
}
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
Rectangle {
|
||||||
font.pixelSize: app.landscape() ? height * 0.35 : height * 0.5
|
id: matchRect
|
||||||
|
|
||||||
Layout.alignment: Layout.Right
|
anchors {
|
||||||
|
centerIn: !matchItm.thisIsFinal ? parent:undefined
|
||||||
|
bottom: matchItm.thisIsFinal ? parent.bottom:undefined
|
||||||
|
}
|
||||||
|
|
||||||
text: matchItm.thisMatchData[index] !== undefined && matchItm.thisMatchData[index]['result'+matchItm.thisRound] !== undefined ?
|
visible: !matchItm.thisData.dummy
|
||||||
( parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]) ?
|
|
||||||
(parseFloat(matchItm.thisMatchData[index]['result'+matchItm.thisRound]).toFixed(2))
|
width: parent.width
|
||||||
: matchItm.thisMatchData[index]['result'+matchItm.thisRound] )
|
height: roundItem.tileSize
|
||||||
: "-"
|
|
||||||
|
color: Material.dialogColor
|
||||||
|
radius: height * 0.2
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: matchRect.radius * 0.5
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
// for the two athletes
|
||||||
|
model: 2
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
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
|
||||||
|
|
||||||
|
spacing: height * 0.1
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
Layout.preferredWidth: parent.width * 0.08
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
font.pixelSize: height * 0.4
|
||||||
|
font.bold: true
|
||||||
|
opacity: 0.7
|
||||||
|
|
||||||
|
text: laneRow.participant ?
|
||||||
|
(
|
||||||
|
laneRow.participant.results[0].rank < 10 ?
|
||||||
|
laneRow.participant.results[0].rank + " ":
|
||||||
|
laneRow.participant.results[0].rank
|
||||||
|
):
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
Layout.maximumWidth: parent.width * 0.6
|
||||||
|
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
font.pixelSize: height * 0.5
|
||||||
|
font.bold: laneRow.isWinner
|
||||||
|
elide: "ElideRight"
|
||||||
|
|
||||||
|
color: laneRow.isWinner ? Material.color(Material.Green):Material.primaryTextColor
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
color: laneRow.isWinner ? Material.color(Material.Green):Material.primaryTextColor
|
||||||
|
|
||||||
|
text: laneRow.result ? laneRow.result.result : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 * 0.8
|
||||||
|
height: width * 0.25
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
131
resources/qml/Components/SpeedFlowChartLocker.qml
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtPurchasing 1.12
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: speedFlowChartProduct
|
||||||
|
|
||||||
|
function onPurchaseFailed() {
|
||||||
|
//% "Purchase failed"
|
||||||
|
purchaseBt.text = qsTrId("#purchaseFailed")
|
||||||
|
purchaseBt.enabled = false
|
||||||
|
buttonTextResetTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: buttonTextResetTimer
|
||||||
|
interval: 2000
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
purchaseBt.text = (speedFlowChartProduct.status === Product.Registered
|
||||||
|
//% "Buy now for"
|
||||||
|
? qsTrId("#buyNowFor") + " " + speedFlowChartProduct.price
|
||||||
|
//% "This item is currently unavailable"
|
||||||
|
: qsTrId("#itemIsUnavailable"))
|
||||||
|
purchaseBt.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: lockedLayout
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: parent.height * 0.02
|
||||||
|
bottomMargin: parent.height * 0.05
|
||||||
|
rightMargin: parent.width * 0.1 + 20
|
||||||
|
leftMargin: parent.width * 0.1 + 20
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: lockedLayout.height * 0.01
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: lockedLayout.height * 0.03
|
||||||
|
width: lockedLayout.width
|
||||||
|
//% "This is a premium feature."
|
||||||
|
text: qsTrId("#thisIsAPremiumFeature")
|
||||||
|
|
||||||
|
font.pixelSize: height
|
||||||
|
font.bold: true
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
minimumPixelSize: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
SwipeGallery {
|
||||||
|
property string platformIcons: Qt.platform.os === "osx" || Qt.platform.os === "iso" ? "ios":"android"
|
||||||
|
|
||||||
|
height: lockedLayout.height * 0.85
|
||||||
|
width: lockedLayout.width
|
||||||
|
|
||||||
|
images: [
|
||||||
|
"qrc:/screenshots/SpeedFlowchartDemo/" + platformIcons + "/1.jpeg",
|
||||||
|
"qrc:/screenshots/SpeedFlowchartDemo/" + platformIcons + "/2.jpeg",
|
||||||
|
"qrc:/screenshots/SpeedFlowchartDemo/" + platformIcons + "/3.jpeg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: purchaseBt
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: lockedLayout.height * 0.07
|
||||||
|
enabled: speedFlowChartProduct.status === Product.Registered
|
||||||
|
text: speedFlowChartProduct.status === Product.Registered
|
||||||
|
? "\uf218 "+ qsTrId("#buyNowFor") + " " + speedFlowChartProduct.price
|
||||||
|
: qsTrId("#itemIsUnavailable")
|
||||||
|
font.family: fa5solid.name
|
||||||
|
font.pixelSize: height * 0.4
|
||||||
|
font.capitalization: Font.MixedCase
|
||||||
|
onClicked: speedFlowChartProduct.purchase()
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: bottomRow
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: lockedLayout.height * 0.065
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: restorePurchaseButton
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
Layout.preferredHeight: bottomRow.height
|
||||||
|
//visible: speedFlowChartProduct.status === Product.Registered
|
||||||
|
|
||||||
|
flat: true
|
||||||
|
font.pixelSize: height * 0.4
|
||||||
|
font.capitalization: Font.MixedCase
|
||||||
|
//% "Restore purchase"
|
||||||
|
text: qsTrId("#restorePurchase")
|
||||||
|
|
||||||
|
onClicked: inAppProductStore.restorePurchases()
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: contactSupportButton
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
Layout.preferredHeight: bottomRow.height
|
||||||
|
|
||||||
|
flat: true
|
||||||
|
font.pixelSize: height * 0.4
|
||||||
|
font.capitalization: Font.MixedCase
|
||||||
|
//% "contact support"
|
||||||
|
text: qsTrId("#contact support")
|
||||||
|
|
||||||
|
onClicked: Qt.openUrlExternally("mailto:contact@itsblue.de")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
resources/qml/Components/SpeedFlowChartPopup.qml
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
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: QT_DEBUG || appSettings.read("speedBackendPurchase") === "1"
|
||||||
|
property bool unlocked: Qt.platform.os !== "iso" || appSettings.read("speedBackendPurchase") === "1"
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.warn("unlocked:", appSettings.read("speedBackendPurchase"))
|
||||||
|
}
|
||||||
|
|
||||||
|
state: "hidden"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
color: Material.background
|
||||||
|
|
||||||
|
onStateChanged: reloadState()
|
||||||
|
onUnlockedChanged: reloadState()
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
if(control.state === "hidden"){
|
||||||
|
control.state = "visible"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
control.state = "hidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVisible() {
|
||||||
|
return control.state === "visible"
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadState() {
|
||||||
|
if(state === "visible" && unlocked) {
|
||||||
|
speedFlowChartLoader.sourceComponent = speedFlowChartComponent
|
||||||
|
}
|
||||||
|
else if(state === "visible" && !unlocked) {
|
||||||
|
speedFlowChartLoader.sourceComponent = speedFlowChartLockerComponent
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
speedFlowChartLoader.sourceComponent = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: speedFlowChartProduct
|
||||||
|
function onPurchaseRestored() {
|
||||||
|
control.unlocked = appSettings.read("speedBackendPurchase") === "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPurchaseSucceeded() {
|
||||||
|
control.unlocked = appSettings.read("speedBackendPurchase") === "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
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] : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 1
|
anchors.margins: 1
|
||||||
anchors.bottomMargin: indicator.height
|
anchors.bottomMargin: indicator.height
|
||||||
|
spacing: width * 0.2
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: control.images.length
|
model: control.images.length
|
||||||
|
|
298
resources/qml/Pages/QrCodeScanPage.qml
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QZXing 3.3
|
||||||
|
import QtMultimedia 5.12
|
||||||
|
import QtQuick.Shapes 1.12
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property string _statusText: ""
|
||||||
|
property string _statusColor: Material.primaryTextColor
|
||||||
|
property bool _freezeScanning: false
|
||||||
|
signal headerComponentChanged()
|
||||||
|
|
||||||
|
//% "Scan QR-Code"
|
||||||
|
title: qsTrId("#scanQrCode")
|
||||||
|
|
||||||
|
onFocusChanged: focus ? open() : close()
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
_setDefaultStatusText()
|
||||||
|
control._freezeScanning = false
|
||||||
|
if(serverConn.isCameraPermissionGranted())
|
||||||
|
cameraLoader.sourceComponent = cameraComponent
|
||||||
|
else
|
||||||
|
cameraLoader.sourceComponent = noPermissionComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
cameraLoader.sourceComponent = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setDefaultStatusText() {
|
||||||
|
//% "Place the Code in the center"
|
||||||
|
_statusText = qsTrId("#placeQrCodeInCenter")
|
||||||
|
_statusColor = Material.primaryTextColor
|
||||||
|
}
|
||||||
|
|
||||||
|
function _handleTag(tag) {
|
||||||
|
if(control._freezeScanning)
|
||||||
|
return
|
||||||
|
|
||||||
|
control._freezeScanning = true
|
||||||
|
|
||||||
|
//% "Plase wait"
|
||||||
|
control._statusText = qsTrId("#pleaseWait") + "..."
|
||||||
|
|
||||||
|
if(app.openWidgetFromUrl(tag))
|
||||||
|
control.close()
|
||||||
|
else {
|
||||||
|
//% "Invalid QR-Code"
|
||||||
|
control._statusText = qsTrId("#invalidQrCode")
|
||||||
|
control._statusColor = Material.color(Material.Red)
|
||||||
|
statusTextResetTimer.start()
|
||||||
|
control._freezeScanning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _requestCameraPermission() {
|
||||||
|
var permissionGranted = serverConn.requestCameraPermission()
|
||||||
|
|
||||||
|
if(permissionGranted)
|
||||||
|
cameraLoader.sourceComponent = cameraComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Loader {
|
||||||
|
id: cameraLoader
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
//asynchronous: true
|
||||||
|
sourceComponent: null
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: cameraComponent
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Camera {
|
||||||
|
id: camera
|
||||||
|
captureMode: Camera.CaptureStillImage
|
||||||
|
imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceAuto
|
||||||
|
|
||||||
|
focus {
|
||||||
|
focusMode: Camera.FocusContinuous
|
||||||
|
focusPointMode: Camera.FocusPointCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyBusyIndicator {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoOutput {
|
||||||
|
id: videoOutput
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
fillMode: VideoOutput.PreserveAspectCrop
|
||||||
|
|
||||||
|
source: camera
|
||||||
|
filters: [ zxingFilter ]
|
||||||
|
focus : visible // to receive focus and capture key events when visible
|
||||||
|
|
||||||
|
autoOrientation: true
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (camera.lockStatus !== Camera.Unlocked)
|
||||||
|
camera.unlock();
|
||||||
|
|
||||||
|
camera.searchAndLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: app.landscape() ? focusIndicatorRect.left : parent.right
|
||||||
|
bottom: app.landscape() ? parent.bottom : focusIndicatorRect.top
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: focusIndicatorRect.opacity
|
||||||
|
color: focusIndicatorRect.border.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: focusIndicatorRect
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: Math.min(parent.height, parent.width)
|
||||||
|
height: width
|
||||||
|
|
||||||
|
border.width: width * 0.1
|
||||||
|
border.color: "#000000"
|
||||||
|
|
||||||
|
opacity: 0.5
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
bottom: focusIndicatorRect.bottom
|
||||||
|
bottomMargin: height * 0.5
|
||||||
|
horizontalCenter: focusIndicatorRect.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
width: (focusIndicatorRect.width - focusIndicatorRect.border.width * 2) * 0.8
|
||||||
|
height: focusIndicatorRect.border.width
|
||||||
|
|
||||||
|
radius: height * 0.3
|
||||||
|
|
||||||
|
color: Material.backgroundColor
|
||||||
|
|
||||||
|
Material.elevation: 10
|
||||||
|
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: height * 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
color: control._statusColor
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.5
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
minimumPixelSize: height * 0.2
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
text: control._statusText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
top: app.landscape() ? parent.top : focusIndicatorRect.bottom
|
||||||
|
left: app.landscape() ? focusIndicatorRect.right : parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: focusIndicatorRect.opacity
|
||||||
|
color: focusIndicatorRect.border.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: noPermissionComponent
|
||||||
|
ColumnLayout {
|
||||||
|
//anchors.fill: parent
|
||||||
|
//anchors.margins: app.landscape() ? app.height * 0.1 : app.width * 0.1
|
||||||
|
|
||||||
|
property int columnWidth: control.width * 0.9
|
||||||
|
|
||||||
|
spacing: height * 0.02
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: noPermissionIcon
|
||||||
|
|
||||||
|
Layout.preferredWidth: parent.columnWidth
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
|
font.pixelSize: app.landscape() ? parent.height * 0.25:parent.height * 0.15
|
||||||
|
font.family: fa5solid.name
|
||||||
|
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
text: "\uf3ed"
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: noPermissionText
|
||||||
|
|
||||||
|
Layout.preferredWidth: parent.columnWidth
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: noPermissionIcon.height * 0.2
|
||||||
|
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
//% "Camera access required"
|
||||||
|
text: qsTrId("#cameraPermissionDenied")
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: noPermissionDetailText
|
||||||
|
|
||||||
|
Layout.preferredWidth: parent.columnWidth
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
|
font.pixelSize: noPermissionText.font.pixelSize * 0.7
|
||||||
|
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
//% "blueROCK needs to access your camera in order to scan QR-Codes. It will never record or store any photos or videos."
|
||||||
|
text: qsTrId("#cameraPermissionDeniedDetails")
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: grantPermissionButton
|
||||||
|
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
|
//% "Allow access"
|
||||||
|
text: qsTrId("#allowAccess")
|
||||||
|
|
||||||
|
onClicked: control._requestCameraPermission()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QZXingFilter {
|
||||||
|
id: zxingFilter
|
||||||
|
|
||||||
|
decoder {
|
||||||
|
onTagFound: control._handleTag(tag)
|
||||||
|
|
||||||
|
enabledDecoders: QZXing.DecoderFormat_QR_CODE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: statusTextResetTimer
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
interval: 3000
|
||||||
|
onTriggered: _setDefaultStatusText()
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,8 +16,10 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
|
||||||
|
@ -25,54 +27,41 @@ Page {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
title: "start"
|
title: "start"
|
||||||
property Component headerComponent: null
|
|
||||||
|
|
||||||
Label {
|
signal headerComponentChanged()
|
||||||
|
|
||||||
|
BlueRockBadge {
|
||||||
|
id: headerBadge
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
top: parent.top
|
top: parent.top
|
||||||
topMargin: root.height * 0.03
|
topMargin: root.height * 0.03
|
||||||
}
|
}
|
||||||
|
|
||||||
font.pixelSize: anchors.topMargin
|
height: app.landscape() ? menuGr.buttonSize * 0.2:menuGr.buttonSize * 0.3
|
||||||
font.bold: true
|
|
||||||
|
|
||||||
text: "blueROCK"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Grid {
|
GridLayout {
|
||||||
id: menuGr
|
id: menuGr
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
rows: app.landscape() ? 1:3
|
rows: app.landscape() ? 1:2
|
||||||
columns: app.landscape() ? 3:1
|
columns: app.landscape() ? 2:1
|
||||||
|
|
||||||
spacing: !app.landscape() ? parent.height * 0.08:parent.width * 0.1
|
rowSpacing: app.landscape() ? parent.width * 0.1:headerBadge.anchors.topMargin
|
||||||
|
columnSpacing: rowSpacing
|
||||||
|
|
||||||
property int buttonSize: app.landscape() ? parent.width * 0.2:parent.height * 0.2
|
property int buttonSize: app.landscape() ? parent.width * 0.2:parent.height * 0.19
|
||||||
|
|
||||||
FancyButton {
|
|
||||||
id: ifscBt
|
|
||||||
|
|
||||||
height: menuGr.buttonSize
|
|
||||||
width: height
|
|
||||||
|
|
||||||
image: "qrc:/icons/ifsc.png"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
app.openWidget({nation:"ICC"})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FancyButton {
|
FancyButton {
|
||||||
id: davBt
|
id: davBt
|
||||||
|
|
||||||
height: menuGr.buttonSize
|
Layout.preferredHeight: menuGr.buttonSize
|
||||||
width: height
|
Layout.preferredWidth: menuGr.buttonSize
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
image: "qrc:/icons/dav.png"
|
image: Material.theme === Material.Dark ? "qrc:/icons/dav-dark.png":"qrc:/icons/dav.png"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
app.openWidget({nation:"GER"})
|
app.openWidget({nation:"GER"})
|
||||||
|
@ -83,37 +72,107 @@ Page {
|
||||||
FancyButton {
|
FancyButton {
|
||||||
id: sacBt
|
id: sacBt
|
||||||
|
|
||||||
height: menuGr.buttonSize
|
Layout.preferredHeight: menuGr.buttonSize
|
||||||
width: height
|
Layout.preferredWidth: menuGr.buttonSize
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
image: "qrc:/icons/sac.png"
|
image: Material.theme === Material.Dark ? "qrc:/icons/sac-dark.png":"qrc:/icons/sac.png"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
app.openWidget({nation:"SUI"})
|
app.openWidget({nation:"SUI"})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Grid {
|
||||||
|
id: footerMenu
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
bottomMargin: root.height * 0.03
|
margins: headerBadge.anchors.topMargin
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
width: parent.width * 0.9
|
width: app.landscape() ? childrenRect.width : parent.width * 0.8
|
||||||
height: anchors.bottomMargin
|
height: app.landscape() ? headerBadge.height : Math.min(headerBadge.height * 2, width * 0.27)
|
||||||
|
|
||||||
wrapMode: "Wrap"
|
columnSpacing: height * 0.1
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
text: "Resultservice and rankings provided by <a href='http://www.digitalROCK.de'>digital ROCK</a>."
|
columns: app.landscape() ? 4:2
|
||||||
|
rows: app.landscape() ? 1:2
|
||||||
|
|
||||||
onLinkActivated: {
|
Repeater {
|
||||||
Qt.openUrlExternally(link)
|
id: buttonRepeater
|
||||||
|
property var buttons: [
|
||||||
|
//% "IFSC results"
|
||||||
|
["\uf059", qsTrId("#ifscResults"), ifscDisclaimerDialog.open],
|
||||||
|
[
|
||||||
|
"\uf042",
|
||||||
|
Material.theme === Material.Light ?
|
||||||
|
//% "Dark mode"
|
||||||
|
qsTrId("#darkMode"):
|
||||||
|
//% "Light mode"
|
||||||
|
qsTrId("#lightMode"),
|
||||||
|
app.toggleDarkMode
|
||||||
|
],
|
||||||
|
//% "About blueROCK"
|
||||||
|
["\uf05a", qsTrId("#aboutBluerock"), aboutBluerockDisclaimerDialog.open],
|
||||||
|
["\uf029", qsTrId("#scanQrCode"), function(){
|
||||||
|
mainStack.push("qrc:/Pages/QrCodeScanPage.qml")
|
||||||
|
}],
|
||||||
|
]
|
||||||
|
|
||||||
|
model: buttons
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
|
||||||
|
width: app.landscape() ? footerMenuButton.implicitWidth : footerMenu.width * 0.5 - (footerMenu.columnSpacing / 2)
|
||||||
|
height: app.landscape() ? footerMenu.height : footerMenu.height * 0.5 - (footerMenu.rowSpacing / 2)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: footerMenuButton
|
||||||
|
|
||||||
|
property bool isLeft: index % 2 === 0
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
right: isLeft && !app.landscape() ? parent.right : undefined
|
||||||
|
left: isLeft && !app.landscape() ? undefined : parent.left
|
||||||
|
centerIn: app.landscape() ? parent : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
flat: true
|
||||||
|
|
||||||
|
font.family: fa5solid.name
|
||||||
|
font.pixelSize: height * 0.4
|
||||||
|
font.capitalization: Font.MixedCase
|
||||||
|
//horizontalAlignment: isLeft ? Text.AlignRight : Text.AlignLeft
|
||||||
|
|
||||||
|
text: isLeft && !app.landscape() ? modelData[1] + " " + modelData[0] : modelData[0] + " " + modelData[1]
|
||||||
|
|
||||||
|
onClicked: buttonRepeater.buttons[index][2]()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DisclaimerDialog {
|
||||||
|
id: ifscDisclaimerDialog
|
||||||
|
Material.theme: root.Material.theme
|
||||||
|
//% "Where are the IFSC results?"
|
||||||
|
title: qsTrId("#ifscDisclaimerTitle")
|
||||||
|
//% "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>."
|
||||||
|
content: qsTrId("#ifscDisclaimer")
|
||||||
|
}
|
||||||
|
|
||||||
|
DisclaimerDialog {
|
||||||
|
id: aboutBluerockDisclaimerDialog
|
||||||
|
Material.theme: root.Material.theme
|
||||||
|
//% "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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,9 @@ Page {
|
||||||
Result,
|
Result,
|
||||||
|
|
||||||
Ranking,
|
Ranking,
|
||||||
Aggregated // not yet implemented
|
Aggregated, // not yet implemented
|
||||||
|
|
||||||
|
Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
title: widgetLd.item !== null && widgetLd.item.hasOwnProperty('title') ? widgetLd.item['title']:""
|
title: widgetLd.item !== null && widgetLd.item.hasOwnProperty('title') ? widgetLd.item['title']:""
|
||||||
|
@ -46,6 +48,10 @@ Page {
|
||||||
property bool titleIsPageTitle: widgetLd.item !== null && widgetLd.item.hasOwnProperty('titleIsPageTitle') ? widgetLd.item['titleIsPageTitle']:false
|
property bool titleIsPageTitle: widgetLd.item !== null && widgetLd.item.hasOwnProperty('titleIsPageTitle') ? widgetLd.item['titleIsPageTitle']:false
|
||||||
property Component headerComponent: widgetLd.item !== null && widgetLd.item.hasOwnProperty('headerComponent') ? widgetLd.item['headerComponent']:null
|
property Component headerComponent: widgetLd.item !== null && widgetLd.item.hasOwnProperty('headerComponent') ? widgetLd.item['headerComponent']:null
|
||||||
|
|
||||||
|
function onBackRequested() {
|
||||||
|
return !widgetLd.item.hasOwnProperty('onBackRequested') || widgetLd.item.onBackRequested()
|
||||||
|
}
|
||||||
|
|
||||||
property var params
|
property var params
|
||||||
|
|
||||||
property int status: -1
|
property int status: -1
|
||||||
|
@ -68,7 +74,6 @@ Page {
|
||||||
// route: (int) round
|
// route: (int) round
|
||||||
// type: ('','starters', 'nat_team_ranking', 'sektionenwertung', 'regionalzentren'),
|
// type: ('','starters', 'nat_team_ranking', 'sektionenwertung', 'regionalzentren'),
|
||||||
//}
|
//}
|
||||||
|
|
||||||
var ret = serverConn.getWidgetData(params)
|
var ret = serverConn.getWidgetData(params)
|
||||||
|
|
||||||
root.status = ret["status"]
|
root.status = ret["status"]
|
||||||
|
@ -76,7 +81,11 @@ Page {
|
||||||
if(ret["status"] === 200){
|
if(ret["status"] === 200){
|
||||||
root.widgetData = ret["data"]
|
root.widgetData = ret["data"]
|
||||||
root.widgetType = checkWidgetType(params, root.widgetData)
|
root.widgetType = checkWidgetType(params, root.widgetData)
|
||||||
if(widgetLd.load()){
|
if(widgetType === WidgetPage.WidgetType.Invalid) {
|
||||||
|
root.ready = false
|
||||||
|
root.status = 906
|
||||||
|
}
|
||||||
|
else if(widgetLd.load()){
|
||||||
root.ready = true
|
root.ready = true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -84,7 +93,13 @@ Page {
|
||||||
root.ready = false
|
root.ready = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(ret["status"] === 404 && [WidgetPage.WidgetType.Registration, WidgetPage.WidgetType.Startlist, WidgetPage.WidgetType.Result].includes(root.widgetType) && root.params["route"] !== "") {
|
else if(ret["status"] === 404 &&
|
||||||
|
[
|
||||||
|
WidgetPage.WidgetType.Registration,
|
||||||
|
WidgetPage.WidgetType.Startlist,
|
||||||
|
WidgetPage.WidgetType.Result
|
||||||
|
].includes(root.widgetType) &&
|
||||||
|
root.params["route"] !== "") {
|
||||||
// if we get a 404 and have startlist, results or registration, the route was not found -> remove round and try again
|
// if we get a 404 and have startlist, results or registration, the route was not found -> remove round and try again
|
||||||
root.params["route"] = ""
|
root.params["route"] = ""
|
||||||
loadData(root.params)
|
loadData(root.params)
|
||||||
|
@ -114,6 +129,10 @@ Page {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function areParamsValid() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function checkWidgetType(params, widgetData){
|
function checkWidgetType(params, widgetData){
|
||||||
var widgetType
|
var widgetType
|
||||||
|
|
||||||
|
@ -160,10 +179,25 @@ Page {
|
||||||
// aggregated
|
// aggregated
|
||||||
widgetType = WidgetPage.WidgetType.Aggregated
|
widgetType = WidgetPage.WidgetType.Aggregated
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
widgetType = WidgetPage.WidgetType.Invalid
|
||||||
|
}
|
||||||
|
|
||||||
return widgetType
|
return widgetType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encodeQueryData(data) {
|
||||||
|
const ret = [];
|
||||||
|
for (let d in data)
|
||||||
|
ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
|
||||||
|
return ret.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareWidget(compName) {
|
||||||
|
var url = "https://l.bluerock.dev/?" + encodeQueryData(params)
|
||||||
|
sharePu.appear(url, compName)
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: widgetLd
|
id: widgetLd
|
||||||
|
|
||||||
|
@ -197,9 +231,6 @@ Page {
|
||||||
delete(widgetLd.sourceComponent)
|
delete(widgetLd.sourceComponent)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFile(widgetType) {
|
function getFile(widgetType) {
|
||||||
|
@ -241,107 +272,19 @@ Page {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog {
|
SelectorPopup {
|
||||||
id: selectorPu
|
id: selectorPu
|
||||||
|
|
||||||
property var dataObj
|
Material.theme: root.Material.theme
|
||||||
property string subTitle: ""
|
|
||||||
|
|
||||||
signal selectionFinished(int index, var data)
|
contentItem: ListView {
|
||||||
|
|
||||||
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 {
|
|
||||||
id: selectorLv
|
id: selectorLv
|
||||||
|
|
||||||
property int delegateHeight: 50
|
property int delegateHeight: 50
|
||||||
|
spacing: 10
|
||||||
|
clip: true
|
||||||
|
|
||||||
anchors.fill: parent
|
implicitHeight: model === 0 ? 0:(delegateHeight + spacing) * model
|
||||||
|
|
||||||
implicitWidth: parent.width
|
|
||||||
implicitHeight: root.height * 0.7 < ( (delegateHeight + spacing) * model ) ? root.height * 0.7 : (delegateHeight + spacing) * model + 100
|
|
||||||
|
|
||||||
model: selectorPu.dataObj !== undefined ? selectorPu.dataObj.length:0
|
model: selectorPu.dataObj !== undefined ? selectorPu.dataObj.length:0
|
||||||
|
|
||||||
|
@ -354,13 +297,12 @@ Page {
|
||||||
leftMargin: 3
|
leftMargin: 3
|
||||||
bottom: selectorLv.bottom
|
bottom: selectorLv.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Button {
|
delegate: Button {
|
||||||
id: catBt
|
id: catBt
|
||||||
|
|
||||||
width: parent.width
|
width: selectorLv.width
|
||||||
height: text !== "" ? selectorLv.delegateHeight:0
|
height: text !== "" ? selectorLv.delegateHeight:0
|
||||||
|
|
||||||
flat: true
|
flat: true
|
||||||
|
@ -373,32 +315,11 @@ 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharePopup {
|
||||||
|
id: sharePu
|
||||||
|
|
||||||
|
Material.theme: root.Material.theme
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
|
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
|
||||||
|
@ -27,12 +28,12 @@ DataListView {
|
||||||
|
|
||||||
property bool ready
|
property bool ready
|
||||||
|
|
||||||
property string title: (params.nation === "ICC" ? "IFSC":params.nation === "GER" ? "DAV":"SAC") + " " + qsTr("calendar") + " " + control.year
|
//% "calendar"
|
||||||
|
property string title: (params.nation === "ICC" ? "IFSC":params.nation === "GER" ? "DAV":"SAC") + " " + qsTrId("#calendar") + " " + control.year
|
||||||
|
|
||||||
property Component headerComponent: RowLayout {
|
property Component headerComponent: RowLayout {
|
||||||
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
//width: 10//childrenRect.width
|
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
|
@ -42,7 +43,9 @@ DataListView {
|
||||||
control.changeYear()
|
control.changeYear()
|
||||||
}
|
}
|
||||||
|
|
||||||
icon.name: "year"
|
text: "\uf133"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
|
@ -61,16 +64,19 @@ DataListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compCats.push( {"text": qsTr("Pinned"), "data": {"sort_rank":0, "cat_id":[-1]}} )
|
//% "Favorites"
|
||||||
|
compCats.push( {"text": qsTrId("#favorites"), "data": {"sort_rank":0, "cat_id":[-1]}} )
|
||||||
|
|
||||||
compCats.sort(function(a, b) {
|
compCats.sort(function(a, b) {
|
||||||
return a['data']['sort_rank'] - b['data']['sort_rank'];
|
return a['data']['sort_rank'] - b['data']['sort_rank'];
|
||||||
});
|
});
|
||||||
|
|
||||||
filterSelectPu.appear(compCats, qsTr("Select Filters"), "")
|
//% "Select filters"
|
||||||
|
filterSelectPu.appear(compCats, qsTrId("#selectFilters"), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
icon.name: "filter"
|
text: "\uf0b0"
|
||||||
|
font.family: fa5solid.name
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
|
@ -80,7 +86,8 @@ DataListView {
|
||||||
control.openCup()
|
control.openCup()
|
||||||
}
|
}
|
||||||
|
|
||||||
icon.name: "cup"
|
text: "\uf091"
|
||||||
|
font.family: fa5solid.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,10 +113,15 @@ DataListView {
|
||||||
initFilters()
|
initFilters()
|
||||||
initFavorites()
|
initFavorites()
|
||||||
|
|
||||||
if(model){
|
if(model && widgetData["competitions"].length > 0){
|
||||||
control.status = 200
|
control.status = 200
|
||||||
control.ready = true
|
control.ready = true
|
||||||
}
|
}
|
||||||
|
else if(widgetData["competitions"].length === 0) {
|
||||||
|
control.status = 404
|
||||||
|
control.ready = false
|
||||||
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
control.ready = false
|
control.ready = false
|
||||||
control.status = 901
|
control.status = 901
|
||||||
|
@ -151,7 +163,7 @@ DataListView {
|
||||||
|
|
||||||
if(endDate.getTime() < new Date().getTime()){
|
if(endDate.getTime() < new Date().getTime()){
|
||||||
// end date is already over -> move the list view down!
|
// 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!")
|
//console.log("moving down!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,13 +205,18 @@ DataListView {
|
||||||
var infoUrls = getCompInfoUrls(compIndex)
|
var infoUrls = getCompInfoUrls(compIndex)
|
||||||
var infosheet = "";
|
var infosheet = "";
|
||||||
if(infoUrls.length >= 1)
|
if(infoUrls.length >= 1)
|
||||||
infosheet += ("<a href='" + getCompInfoUrls(compIndex)[0] + "'>" + qsTr('infosheet') + "</a>")
|
//% "Infosheet"
|
||||||
|
infosheet += ("<a href='" + getCompInfoUrls(compIndex)[0] + "'>" + qsTrId("#infosheet") + "</a>")
|
||||||
if(infoUrls.length === 2)
|
if(infoUrls.length === 2)
|
||||||
infosheet += (", <a href='" + getCompInfoUrls(compIndex)[1] + "'>" + qsTr('further infos') + "</a>")
|
//% "Further infos"
|
||||||
|
infosheet += (", <a href='" + getCompInfoUrls(compIndex)[1] + "'>" + qsTrId("#furtherInfos") + "</a>")
|
||||||
|
|
||||||
console.log("Infosheet: " + infosheet)
|
console.log("Infosheet: " + infosheet)
|
||||||
|
|
||||||
var eventWebsite = control.widgetData["competitions"][compIndex]["homepage"] !== undefined ? ("<a href='" + control.widgetData["competitions"][compIndex]["homepage"] + "'>" + qsTr('Event Website') + "</a>"):""
|
|
||||||
|
var eventWebsite = control.widgetData["competitions"][compIndex]["homepage"] !== undefined ?
|
||||||
|
//% "Event website"
|
||||||
|
("<a href='" + control.widgetData["competitions"][compIndex]["homepage"] + "'>" + qsTrId("#eventWebsite") + "</a>"):""
|
||||||
|
|
||||||
selector.appear(selectOptions, control.widgetData["competitions"][compIndex]['name'], eventWebsite + ((eventWebsite !== "" && infosheet !== "") ? ", ":"") + infosheet )
|
selector.appear(selectOptions, control.widgetData["competitions"][compIndex]['name'], eventWebsite + ((eventWebsite !== "" && infosheet !== "") ? ", ":"") + infosheet )
|
||||||
}
|
}
|
||||||
|
@ -215,7 +232,8 @@ DataListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selector.appear(selectOptions, qsTr("select year"))
|
//% "Select year"
|
||||||
|
selector.appear(selectOptions, qsTrId("#selectYear"))
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCup(state, data) {
|
function openCup(state, data) {
|
||||||
|
@ -227,7 +245,8 @@ DataListView {
|
||||||
|
|
||||||
if(state === undefined){
|
if(state === undefined){
|
||||||
// opened for the first time -> select cup
|
// opened for the first time -> select cup
|
||||||
selectTitle = qsTr("select cup")
|
//% "Select cup"
|
||||||
|
selectTitle = qsTrId("#selectCup")
|
||||||
|
|
||||||
cups.sort(function(a, b) {
|
cups.sort(function(a, b) {
|
||||||
return parseInt(b["SerId"]) - parseInt(a["SerId"]);
|
return parseInt(b["SerId"]) - parseInt(a["SerId"]);
|
||||||
|
@ -255,7 +274,8 @@ DataListView {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
selectTitle = cup['name'] + ": " + qsTr("select category")
|
//% "Select category"
|
||||||
|
selectTitle = cup['name'] + ": " + qsTrId("#selectCategory")
|
||||||
|
|
||||||
// build a list with all cat in the cup out of the cat keys (rkey) given in the cup.cats
|
// build a list with all cat in the cup out of the cat keys (rkey) given in the cup.cats
|
||||||
for(prop in cup['cats']){
|
for(prop in cup['cats']){
|
||||||
|
@ -369,9 +389,8 @@ DataListView {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: parent.selector
|
target: parent.selector
|
||||||
onSelectionFinished: {
|
function onSelectionFinished(index, data) {
|
||||||
if(data.comp !== undefined){
|
if(data.comp !== undefined){
|
||||||
//console.log(data.status)
|
|
||||||
app.openWidget({comp: data.comp, cat: data.cat, type:data.status === 4 ? 'starters':''})
|
app.openWidget({comp: data.comp, cat: data.cat, type:data.status === 4 ? 'starters':''})
|
||||||
}
|
}
|
||||||
else if(data.year !== undefined){
|
else if(data.year !== undefined){
|
||||||
|
@ -385,6 +404,10 @@ DataListView {
|
||||||
app.openWidget({cup: data.cup, cat: data.cat})
|
app.openWidget({cup: data.cup, cat: data.cat})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onLinkActivated(link) {
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header: Item {
|
header: Item {
|
||||||
|
@ -399,281 +422,22 @@ DataListView {
|
||||||
height: 10
|
height: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: CompetitionCalendarDelegate {
|
||||||
id: competitionDel
|
|
||||||
|
|
||||||
property bool over
|
|
||||||
property var thisData: control.widgetData["competitions"][index]
|
|
||||||
|
|
||||||
property string name: thisData["name"]
|
|
||||||
property string date: thisData["date_span"]
|
|
||||||
property var cats: thisData["cats"]
|
|
||||||
property int catId: thisData["cat_id"] === undefined ? 0:thisData["cat_id"]
|
|
||||||
|
|
||||||
property bool thisIsFavored: control.compFavorites.indexOf(parseInt(thisData['WetId'])) >= 0
|
|
||||||
property bool includedByFavorites: control.displayedCompCats.indexOf(-1) >= 0 && thisIsFavored
|
|
||||||
property bool includedByFilter: control.displayedCompCats.indexOf(parseInt(thisData['cat_id'])) >= 0
|
|
||||||
property bool thisIsVisible: includedByFavorites || includedByFilter
|
|
||||||
|
|
||||||
function updateVisibility() {
|
|
||||||
competitionDel.includedByFilter = control.displayedCompCats.indexOf(parseInt(competitionDel.thisData['cat_id'])) >= 0
|
|
||||||
competitionDel.thisIsFavored = control.compFavorites.indexOf(parseInt(thisData['WetId'])) >= 0
|
|
||||||
competitionDel.includedByFavorites = control.displayedCompCats.indexOf(-1) >= 0 && thisIsFavored
|
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: thisIsVisible ? compDelCol.height + 10 : 0
|
|
||||||
|
|
||||||
enabled: ((thisData["cats"] !== undefined && thisData["cats"].length > 0) || competitionDel.thisData["homepage"] !== undefined || getCompInfoUrls(index).length > 0) && height > 0
|
|
||||||
//visible: includedByFilter
|
|
||||||
|
|
||||||
opacity: 0
|
|
||||||
scale: 0.9
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: control
|
|
||||||
onDisplayedCompCatsChanged: {
|
|
||||||
competitionDel.updateVisibility()
|
|
||||||
}
|
|
||||||
|
|
||||||
onCompFavoritesChanged: {
|
|
||||||
competitionDel.updateVisibility()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onThisDataChanged: {
|
|
||||||
if(thisIsVisible){
|
|
||||||
fadeInPa.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onThisIsVisibleChanged: {
|
|
||||||
if(thisIsVisible){
|
|
||||||
fadeInPa.start()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fadeOutPa.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on height {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 400
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
control.openComp(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
id: fadeInPa
|
|
||||||
NumberAnimation { target: competitionDel; property: "opacity"; from: 0; to: 1.0; duration: 400 }
|
|
||||||
NumberAnimation { target: competitionDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
id: fadeOutPa
|
|
||||||
NumberAnimation { target: competitionDel; property: "opacity"; from: 1; to: 0; duration: 400 }
|
|
||||||
NumberAnimation { target: competitionDel; property: "scale"; from: 1; to: 0.8; duration: 400 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: delBackroundRect
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
opacity: 0.5
|
|
||||||
|
|
||||||
color: control.getCompCatData(catId) === undefined ? "white":control.getCompCatData(catId)["bgcolor"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: compDelCol
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
width: parent.width * 0.97
|
|
||||||
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
width: parent.width
|
|
||||||
Label {
|
|
||||||
id: nameLa
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
font.bold: true
|
|
||||||
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
|
|
||||||
text: name
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolButton {
|
|
||||||
id: bookmarkTb
|
|
||||||
icon.name: competitionDel.thisIsFavored ? "pinFilled":"pin"
|
|
||||||
onClicked: {
|
|
||||||
control.editFavorites(!competitionDel.thisIsFavored, parseInt(thisData['WetId']))
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.alignment: Layout.Right
|
|
||||||
|
|
||||||
Behavior on icon.name {
|
|
||||||
SequentialAnimation {
|
|
||||||
NumberAnimation {
|
|
||||||
property: "scale"
|
|
||||||
target: bookmarkTb
|
|
||||||
duration: 75
|
|
||||||
to: 0.8
|
|
||||||
}
|
|
||||||
NumberAnimation {
|
|
||||||
property: "scale"
|
|
||||||
target: bookmarkTb
|
|
||||||
duration: 75
|
|
||||||
to: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: dateLa
|
|
||||||
|
|
||||||
color: "grey"
|
|
||||||
|
|
||||||
text: date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: bottomLineRa
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
bottom: parent.bottom
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
|
|
||||||
height: 1
|
|
||||||
|
|
||||||
color: "lightgrey"
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
section.property: "month"
|
SelectorPopup {
|
||||||
section.delegate: ItemDelegate {
|
|
||||||
id: name
|
|
||||||
background: Rectangle {
|
|
||||||
color: "red"
|
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
text: section
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialog {
|
|
||||||
id: filterSelectPu
|
id: filterSelectPu
|
||||||
|
|
||||||
property var dataObj
|
Material.theme: control.Material.theme
|
||||||
property string subTitle: ""
|
|
||||||
|
|
||||||
signal selectionFinished(int index, var data)
|
contentItem: ListView {
|
||||||
|
|
||||||
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 {
|
|
||||||
id: selectorLv
|
id: selectorLv
|
||||||
|
|
||||||
property int delegateHeight: 50
|
property int delegateHeight: 50
|
||||||
|
spacing: 10
|
||||||
|
clip: true
|
||||||
|
|
||||||
anchors.fill: parent
|
implicitHeight: model === 0 ? 0:(delegateHeight + spacing) * model
|
||||||
|
|
||||||
implicitWidth: parent.width
|
|
||||||
implicitHeight: root.height * 0.7 < ( (delegateHeight + spacing) * model ) ? root.height * 0.7 : (delegateHeight + spacing) * model + 100
|
|
||||||
|
|
||||||
model: filterSelectPu.dataObj !== undefined ? filterSelectPu.dataObj.length:0
|
model: filterSelectPu.dataObj !== undefined ? filterSelectPu.dataObj.length:0
|
||||||
|
|
||||||
|
@ -686,13 +450,12 @@ DataListView {
|
||||||
leftMargin: 3
|
leftMargin: 3
|
||||||
bottom: selectorLv.bottom
|
bottom: selectorLv.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: CheckDelegate {
|
delegate: CheckDelegate {
|
||||||
id: catBt
|
id: catBt
|
||||||
|
|
||||||
width: parent.width
|
width: selectorLv.width
|
||||||
height: text !== "" ? selectorLv.delegateHeight:0
|
height: text !== "" ? selectorLv.delegateHeight:0
|
||||||
|
|
||||||
//flat: true
|
//flat: true
|
||||||
|
@ -705,7 +468,7 @@ DataListView {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: control
|
target: control
|
||||||
onDisplayedCompCatsChanged: {
|
function onDisplayedCompCatsChanged() {
|
||||||
//console.log("filters changed")
|
//console.log("filters changed")
|
||||||
//competitionDel.visible = control.displayedCompCats.indexOf(parseInt(competitionDel.thisData['cat_id'])) >= 0
|
//competitionDel.visible = control.displayedCompCats.indexOf(parseInt(competitionDel.thisData['cat_id'])) >= 0
|
||||||
checked = getCheckedState()
|
checked = getCheckedState()
|
||||||
|
@ -733,31 +496,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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
import QtQuick.Controls.Material 2.3
|
import QtQuick.Controls.Material 2.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
|
||||||
|
@ -34,6 +35,15 @@ Page {
|
||||||
|
|
||||||
property var widgetData: currentWidgetData
|
property var widgetData: currentWidgetData
|
||||||
|
|
||||||
|
property Component headerComponent: ToolButton {
|
||||||
|
id: shareToolBt
|
||||||
|
|
||||||
|
onClicked: shareWidget(control.title)
|
||||||
|
|
||||||
|
text: "\uf1e0"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
control.ready = true
|
control.ready = true
|
||||||
control.status = 200
|
control.status = 200
|
||||||
|
@ -43,25 +53,11 @@ Page {
|
||||||
id: mainSv
|
id: mainSv
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 10
|
anchors.margins: 20
|
||||||
anchors.rightMargin: 14
|
|
||||||
|
|
||||||
contentWidth: parent.width - anchors.leftMargin - anchors.rightMargin
|
contentWidth: parent.width - anchors.margins * 2
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: mainSv.top
|
|
||||||
left: mainSv.right
|
|
||||||
margins: 10
|
|
||||||
leftMargin: 3
|
|
||||||
bottom: mainSv.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
width: 8
|
|
||||||
|
|
||||||
active: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: mainCol
|
id: mainCol
|
||||||
|
@ -72,25 +68,61 @@ Page {
|
||||||
height: control.height * 0.3
|
height: control.height * 0.3
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
Image {
|
spacing: photoContainerItem.width * 0.1
|
||||||
id: photo
|
|
||||||
|
|
||||||
property bool ready: false
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: photoContainerItem
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
height: parent.height * 0.9
|
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 {
|
||||||
asynchronous: true
|
id: photo
|
||||||
|
|
||||||
|
property bool ready: photo.status === Image.Ready
|
||||||
|
|
||||||
FancyBusyIndicator {
|
|
||||||
height: width
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
opacity: photo.status === Image.Loading
|
height: parent.height * 0.9
|
||||||
|
width: height * 0.7
|
||||||
|
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
mipmap: true
|
||||||
|
|
||||||
|
source: widgetData["photo"] === undefined ?
|
||||||
|
"":
|
||||||
|
widgetData["photo"].replace("https", "http")
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
FancyBusyIndicator {
|
||||||
|
height: width
|
||||||
|
anchors.centerIn: parent
|
||||||
|
opacity: photo.status === Image.Loading
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Item {
|
||||||
|
width: photo.width
|
||||||
|
height: photo.height
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: photo.width
|
||||||
|
height: photo.height
|
||||||
|
radius: photo.width * 0.2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +131,7 @@ Page {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
height: parent.height * 0.9
|
height: parent.height * 0.9
|
||||||
width: parent.width - photo.width
|
width: parent.width - photoContainerItem.width * 1.1
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|
||||||
|
@ -167,7 +199,8 @@ Page {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
text: qsTr("age") + ": " + widgetData["age"]
|
//% "Age"
|
||||||
|
text: qsTrId("#age") + ": " + widgetData["age"]
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
@ -183,7 +216,8 @@ Page {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
text: qsTr("year of birth") + ": " + widgetData["birthdate"]
|
//% "Year of birth"
|
||||||
|
text: qsTrId("#yearOfBirth") + ": " + widgetData["birthdate"]
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
@ -199,7 +233,8 @@ Page {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
text: qsTr("city") + ": " + widgetData["city"]
|
//% "City"
|
||||||
|
text: qsTrId("#city") + ": " + widgetData["city"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +249,9 @@ Page {
|
||||||
|
|
||||||
wrapMode: Label.Wrap
|
wrapMode: Label.Wrap
|
||||||
|
|
||||||
text: widgetData["freetext"] === undefined ? "":widgetData["freetext"]
|
text: widgetData["freetext"] === undefined ?
|
||||||
|
"":
|
||||||
|
(widgetData["freetext"] + "<br>")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,9 +261,9 @@ Page {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
height: 1
|
height: 1
|
||||||
width: parent.width * 0.9
|
width: parent.width
|
||||||
|
|
||||||
color: "black"
|
color: Material.foreground
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +275,11 @@ Page {
|
||||||
|
|
||||||
flat: true
|
flat: true
|
||||||
|
|
||||||
text: bestResultsRep.showAllResults ? qsTr("show best results"):qsTr("show all results")
|
text: bestResultsRep.showAllResults ?
|
||||||
|
//% "Show best results"
|
||||||
|
qsTrId("#showBestResults"):
|
||||||
|
//% "Show all results"
|
||||||
|
qsTrId("#showAllResults")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
bestResultsRep.showAllResults = !bestResultsRep.showAllResults
|
bestResultsRep.showAllResults = !bestResultsRep.showAllResults
|
||||||
|
@ -307,6 +348,7 @@ Page {
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 50
|
height: 50
|
||||||
|
spacing: width * 0.05
|
||||||
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
scale: 0.9
|
scale: 0.9
|
||||||
|
@ -324,11 +366,11 @@ Page {
|
||||||
Label {
|
Label {
|
||||||
id: bestResultRankLa
|
id: bestResultRankLa
|
||||||
|
|
||||||
width: parent.width * 0.2
|
width: parent.width * 0.1
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
minimumPixelSize: 1
|
minimumPixelSize: 1
|
||||||
|
@ -345,7 +387,7 @@ Page {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
minimumPixelSize: height * 0.3
|
minimumPixelSize: height * 0.3
|
||||||
|
@ -370,7 +412,7 @@ Page {
|
||||||
scale: 0.8
|
scale: 0.8
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignRight
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
minimumPixelSize: 1
|
minimumPixelSize: 1
|
||||||
|
|
|
@ -27,15 +27,25 @@ DataListView {
|
||||||
property bool ready
|
property bool ready
|
||||||
|
|
||||||
property string title: control.widgetData['comp_name']
|
property string title: control.widgetData['comp_name']
|
||||||
property string subTitle: qsTr("(Ranking)") + " after " + control.widgetData['route_name']
|
//% "(Ranking)"
|
||||||
|
property string subTitle: qsTrId("#rankingHeadline") + " after " + control.widgetData['route_name']
|
||||||
property bool titleIsPageTitle: true
|
property bool titleIsPageTitle: true
|
||||||
|
|
||||||
property var widgetData: currentWidgetData
|
property var widgetData: currentWidgetData
|
||||||
signal closeAll()
|
signal closeAll()
|
||||||
|
|
||||||
|
property Component headerComponent: ToolButton {
|
||||||
|
id: shareToolBt
|
||||||
|
|
||||||
|
onClicked: shareWidget(control.title)
|
||||||
|
|
||||||
|
text: "\uf1e0"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: selector
|
target: selector
|
||||||
onSelectionFinished: {
|
function onSelectionFinished(index, data) {
|
||||||
if(data.cat !== undefined){
|
if(data.cat !== undefined){
|
||||||
updateData({cat: data.cat}, true)
|
updateData({cat: data.cat}, true)
|
||||||
}
|
}
|
||||||
|
@ -64,12 +74,12 @@ DataListView {
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: partDel
|
id: partDel
|
||||||
|
|
||||||
property int ind: index
|
property int thisIndex: index
|
||||||
property var thisData: widgetData[ "participants" ][index]
|
property var thisData: widgetData[ "participants" ][index]
|
||||||
|
|
||||||
state: "closed"
|
state: "closed"
|
||||||
|
|
||||||
width: parent.width
|
width: control.width
|
||||||
height: 70
|
height: 70
|
||||||
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
@ -80,7 +90,7 @@ DataListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressAndHold: {
|
onPressAndHold: {
|
||||||
app.openWidget({person:thisData["PerId"]})
|
app.openWidget({person:thisData["PerId"]})
|
||||||
}
|
}
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
|
@ -91,6 +101,8 @@ DataListView {
|
||||||
|
|
||||||
text: ""
|
text: ""
|
||||||
|
|
||||||
|
highlighted: partDel.thisIndex % 2 == 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if(state === "closed"){
|
if(state === "closed"){
|
||||||
// close all other delegates
|
// close all other delegates
|
||||||
|
@ -105,21 +117,11 @@ DataListView {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: control
|
target: control
|
||||||
onCloseAll: {
|
function onCloseAll() {
|
||||||
partDel.state = "closed"
|
partDel.state = "closed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
width: partDel.width
|
|
||||||
|
|
||||||
color: partDel.ind % 2 == 0 ? "white":"lightgrey"
|
|
||||||
|
|
||||||
opacity: 0.2
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: partDelCol
|
id: partDelCol
|
||||||
|
|
||||||
|
@ -221,7 +223,7 @@ DataListView {
|
||||||
font.pixelSize: Math.abs( height * 0.6 )
|
font.pixelSize: Math.abs( height * 0.6 )
|
||||||
minimumPixelSize: 1
|
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 +335,7 @@ DataListView {
|
||||||
|
|
||||||
for(var prop in obj) {
|
for(var prop in obj) {
|
||||||
// go through the whole array and search for data keys
|
// 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", " - ")])
|
resultData.unshift([prop, obj[prop].replace("\n", " - ")])
|
||||||
//console.log("found " + obj[prop] + " at index " + prop)
|
//console.log("found " + obj[prop] + " at index " + prop)
|
||||||
}
|
}
|
||||||
|
@ -382,7 +384,7 @@ DataListView {
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
minimumPixelSize: 1
|
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 {
|
Behavior on height {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
|
||||||
|
@ -30,17 +31,34 @@ DataListView {
|
||||||
property string subTitle: getSubtitle()
|
property string subTitle: getSubtitle()
|
||||||
property bool titleIsPageTitle: true
|
property bool titleIsPageTitle: true
|
||||||
|
|
||||||
property Component headerComponent: ToolButton {
|
property Component headerComponent: RowLayout {
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: shareToolBt
|
||||||
|
|
||||||
|
onClicked: shareWidget(control.title)
|
||||||
|
|
||||||
|
text: "\uf1e0"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
id: moreToolBt
|
id: moreToolBt
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
control.changeCat()
|
control.changeCat()
|
||||||
}
|
}
|
||||||
|
|
||||||
icon.name: "menu"
|
text: "\uf142"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var widgetData: currentWidgetData
|
property var widgetData: currentWidgetData
|
||||||
|
property var athletes
|
||||||
|
|
||||||
function getSubtitle() {
|
function getSubtitle() {
|
||||||
var titleString
|
var titleString
|
||||||
|
@ -55,18 +73,16 @@ DataListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var addition = "(Registration) "
|
//% "(Registration)"
|
||||||
|
var addition = qsTrId("#registrationHeadline")
|
||||||
|
|
||||||
if(titleString !== undefined){
|
if(titleString)
|
||||||
return addition + " " + titleString
|
return addition + " " + titleString
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getText(index){
|
function getText(athleteData){
|
||||||
|
|
||||||
var fedName // federation name
|
var fedName // federation name
|
||||||
|
|
||||||
|
@ -75,21 +91,20 @@ DataListView {
|
||||||
|
|
||||||
for(var i = 0; i < widgetData["federations"].length; i ++ ){
|
for(var i = 0; i < widgetData["federations"].length; i ++ ){
|
||||||
//console.log("checking " + i + ": cat: " + parseInt(widgetData["categorys"][i]["GrpId"]) + " searched cat: " + root.catId)
|
//console.log("checking " + i + ": cat: " + parseInt(widgetData["categorys"][i]["GrpId"]) + " searched cat: " + root.catId)
|
||||||
if(widgetData["federations"][i]["fed_id"] === widgetData[ 'athletes' ][index]["reg_fed_id"]){
|
if(widgetData["federations"][i]["fed_id"] === athleteData["reg_fed_id"]){
|
||||||
fedName = widgetData["federations"][i]["shortcut"]
|
fedName = widgetData["federations"][i]["shortcut"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// an international competition -> get nation
|
// an international competition -> get nation
|
||||||
|
fedName = athleteData["nation"]
|
||||||
fedName = widgetData[ 'athletes' ][index]["nation"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return widgetData[ "athletes" ][index]["firstname"] + " " + widgetData[ "athletes" ][index]["lastname"] + " (" + fedName + ")"
|
return athleteData["firstname"] + " " + athleteData["lastname"] + " (" + fedName + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeCat(){
|
function changeCat() {
|
||||||
var cats = control.widgetData["categorys"]
|
var cats = control.widgetData["categorys"]
|
||||||
|
|
||||||
cats.sort(function(a, b) {
|
cats.sort(function(a, b) {
|
||||||
|
@ -106,24 +121,43 @@ DataListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selector.appear(selectOptions, qsTr("select cat"))
|
selector.appear(selectOptions, qsTrId("#selectCategory"),
|
||||||
|
//% "Show results"
|
||||||
|
"<a href=\"blank\">" + qsTrId("#showResults") + "</a>")
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterAthletes(athletes) {
|
||||||
|
if(!params.cat) {
|
||||||
|
params.cat = control.widgetData["categorys"][0]["GrpId"]
|
||||||
|
}
|
||||||
|
|
||||||
|
var filtered = athletes.filter(function(athlete){
|
||||||
|
var res = String(athlete.cat).toLowerCase() === String(params.cat).toLowerCase()
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: selector
|
target: selector
|
||||||
onSelectionFinished: {
|
function onSelectionFinished(index, data) {
|
||||||
if(data.cat !== undefined){
|
if(data.cat !== undefined){
|
||||||
updateData({cat: data.cat}, true)
|
updateData({cat: data.cat}, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onLinkActivated(link) {
|
||||||
|
selector.close()
|
||||||
|
var tmpParams = params
|
||||||
|
tmpParams.type = ""
|
||||||
|
app.openWidget(tmpParams)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status: model === 0 ? 901:200
|
|
||||||
|
|
||||||
model: widgetData[ 'athletes' ] === undefined ? 0:widgetData[ 'athletes' ].length
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if(model > 0){
|
athletes = filterAthletes(widgetData["athletes"])
|
||||||
|
if(athletes.length > 0){
|
||||||
control.ready = true
|
control.ready = true
|
||||||
control.status = 200
|
control.status = 200
|
||||||
}
|
}
|
||||||
|
@ -137,17 +171,24 @@ DataListView {
|
||||||
updateData({}, false)
|
updateData({}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onWidgetDataChanged: {
|
||||||
|
athletes = []
|
||||||
|
athletes = filterAthletes(widgetData["athletes"])
|
||||||
|
}
|
||||||
|
|
||||||
|
model: athletes
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: partDel
|
id: partDel
|
||||||
|
|
||||||
property var thisData: widgetData[ "athletes" ][index]
|
property int thisIndex: index
|
||||||
|
property var thisData: modelData
|
||||||
width: parent.width
|
|
||||||
height: parseInt(thisData.cat) === parseInt(params.cat) ? undefined:0
|
|
||||||
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
scale: 0.9
|
scale: 0.9
|
||||||
|
|
||||||
|
width: control.width
|
||||||
|
|
||||||
onThisDataChanged: {
|
onThisDataChanged: {
|
||||||
fadeInPa.start()
|
fadeInPa.start()
|
||||||
}
|
}
|
||||||
|
@ -164,20 +205,20 @@ DataListView {
|
||||||
|
|
||||||
text: ""
|
text: ""
|
||||||
|
|
||||||
|
highlighted: partDel.thisIndex % 2 == 0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: parent.width * 0.05
|
anchors.leftMargin: parent.width * 0.05
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.bold: true
|
font.bold: true
|
||||||
font.pixelSize: Math.abs( height * 0.35 )
|
font.pixelSize: Math.abs( height * 0.3 )
|
||||||
minimumPixelSize: height * 0.3
|
|
||||||
|
|
||||||
elide: "ElideRight"
|
elide: "ElideRight"
|
||||||
|
|
||||||
text: control.getText(index)
|
text: control.getText(partDel.thisData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.4
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick.Controls.Material 2.3
|
import QtQuick.Controls.Material 2.3
|
||||||
//import QtLocation 5.13
|
import QtPurchasing 1.12
|
||||||
|
|
||||||
import QtPurchasing 1.12
|
|
||||||
|
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
|
||||||
|
@ -37,42 +35,44 @@ DataListView {
|
||||||
property bool titleIsPageTitle: true
|
property bool titleIsPageTitle: true
|
||||||
|
|
||||||
property Component headerComponent: RowLayout {
|
property Component headerComponent: RowLayout {
|
||||||
id: headerComponent
|
|
||||||
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
ToolButton {
|
layoutDirection: Qt.RightToLeft
|
||||||
id: flowToolBt
|
|
||||||
|
|
||||||
visible: speedFlowChart.enabled
|
|
||||||
|
|
||||||
enabled: control.widgetData['route_order'] === "-1" && Object.keys(control.widgetData['route_names']).length > 2
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if(speedFlowChartBackgroundRect.state === "hidden"){
|
|
||||||
speedFlowChartBackgroundRect.state ="visible"
|
|
||||||
control.positionViewAtBeginning()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
speedFlowChartBackgroundRect.state = "hidden"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
icon.name: "flowchart"
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
id: moreToolBt
|
id: moreToolBt
|
||||||
|
|
||||||
Layout.alignment: Layout.Right
|
onClicked: control.changeCat()
|
||||||
|
|
||||||
|
text: "\uf142"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: flowToolBt
|
||||||
|
|
||||||
|
visible: speedFlowChartPopup.enabled
|
||||||
|
|
||||||
|
enabled: control.widgetData['route_order'] === "-1" && Object.keys(control.widgetData['route_names']).length > 2
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
control.changeCat()
|
control.positionViewAtBeginning()
|
||||||
|
speedFlowChartPopup.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
icon.name: "menu"
|
text: "\uf0e8"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: shareToolBt
|
||||||
|
|
||||||
|
onClicked: shareWidget(control.title)
|
||||||
|
|
||||||
|
text: "\uf1e0"
|
||||||
|
font.family: fa5solid.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,36 +105,34 @@ DataListView {
|
||||||
console.log("widget data changed")
|
console.log("widget data changed")
|
||||||
|
|
||||||
if(control.widgetData['discipline'] === 'speed' && control.widgetData['route_order'] === "-1" && Object.keys(control.widgetData['route_names']).length > 2){
|
if(control.widgetData['discipline'] === 'speed' && control.widgetData['route_order'] === "-1" && Object.keys(control.widgetData['route_names']).length > 2){
|
||||||
speedFlowChart.flowchartData = ({})
|
speedFlowChartPopup.flowchartData = ({})
|
||||||
speedFlowChart.enabled = true
|
speedFlowChartPopup.flowchartData = control.widgetData
|
||||||
speedFlowChart.flowchartData = control.widgetData
|
speedFlowChartPopup.enabled = true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
speedFlowChart.enabled = false
|
speedFlowChartPopup.flowchartData = ({})
|
||||||
speedFlowChart.flowchartData = ({})
|
speedFlowChartPopup.enabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onBackRequested() {
|
||||||
|
if(!speedFlowChartPopup.isVisible())
|
||||||
|
return true
|
||||||
|
|
||||||
|
speedFlowChartPopup.toggle()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function getSubtitle() {
|
function getSubtitle() {
|
||||||
var titleString
|
var titleString = control.widgetData["route_name"]
|
||||||
|
|
||||||
for(var i = 0; i < control.widgetData["categorys"].length; i ++ ){
|
//% "(Results)"
|
||||||
//console.log("checking " + i + ": cat: " + parseInt(control.widgetData["categorys"][i]["GrpId"]) + " searched cat: " + params.cat)
|
var addition = qsTrId("#resultsHeadline")
|
||||||
if(parseInt(control.widgetData["categorys"][i]["GrpId"]) === parseInt(params.cat)){
|
|
||||||
titleString = control.widgetData["categorys"][i]["name"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var addition = qsTr("(Results)")
|
if(titleString)
|
||||||
|
|
||||||
if(titleString !== undefined){
|
|
||||||
return addition + " " + titleString
|
return addition + " " + titleString
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeRoute(route) {
|
function changeRoute(route) {
|
||||||
|
@ -158,12 +156,12 @@ DataListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selector.appear(selectOptions, qsTr("select cat"))
|
selector.appear(selectOptions, qsTrId("#selectCategory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: selector
|
target: selector
|
||||||
onSelectionFinished: {
|
function onSelectionFinished(index, data) {
|
||||||
if(data.cat !== undefined){
|
if(data.cat !== undefined){
|
||||||
updateData({cat: data.cat}, true)
|
updateData({cat: data.cat}, true)
|
||||||
}
|
}
|
||||||
|
@ -171,454 +169,13 @@ DataListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
onContentYChanged: {
|
onContentYChanged: {
|
||||||
if(contentY > 0 && speedFlowChartBackgroundRect.state === "visible"){
|
if(contentY > 0 && speedFlowChartPopup.state === "visible"){
|
||||||
control.positionViewAtBeginning()
|
control.positionViewAtBeginning()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ResultDelegate {
|
||||||
id: partDel
|
enabled: speedFlowChartPopup.state === "hidden"
|
||||||
|
|
||||||
property int ind: index
|
|
||||||
property var thisData: widgetData[ "participants" ][partDel.ind]
|
|
||||||
|
|
||||||
enabled: speedFlowChartBackgroundRect.state === "hidden"
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: 70
|
|
||||||
|
|
||||||
text: ""
|
|
||||||
|
|
||||||
opacity: 0
|
|
||||||
scale: 0.9
|
|
||||||
|
|
||||||
onThisDataChanged: {
|
|
||||||
fadeInPa.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
app.openWidget({person:thisData["PerId"]})
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelAnimation {
|
|
||||||
id: fadeInPa
|
|
||||||
NumberAnimation { target: partDel; property: "opacity"; from: 0; to: 1.0; duration: 400 }
|
|
||||||
NumberAnimation { target: partDel; property: "scale"; from: 0.8; to: 1.0; duration: 400 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
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){
|
|
||||||
var resultString = widgetData[ "participants" ][partDel.ind]["boulder"+(index+1)]
|
|
||||||
|
|
||||||
var resultList = []
|
|
||||||
|
|
||||||
if( resultString !== undefined){
|
|
||||||
resultString = resultString.replace("t", "")
|
|
||||||
resultString = resultString.replace("z", "")
|
|
||||||
resultString = resultString.replace("b", "")
|
|
||||||
resultList = resultString.split(" ")
|
|
||||||
|
|
||||||
while (resultList.length < 2){
|
|
||||||
resultList.unshift(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resultList = [-1,-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultList
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
|
||||||
id: boulderResItm
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
width: parent.width / ( boulderResRep.model )
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
Canvas {
|
|
||||||
id: boulderResCv
|
|
||||||
|
|
||||||
property var resultData: boulderResRep.getDataForIcon(index)
|
|
||||||
|
|
||||||
onResultDataChanged: {
|
|
||||||
boulderResCv.requestPaint()
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
height: parent.height > parent.width ? parent.width * 0.9:parent.height * 0.9
|
|
||||||
width: height
|
|
||||||
|
|
||||||
onPaint: {
|
|
||||||
var width = 24//boulderResCv.width * 0.9
|
|
||||||
var height = width
|
|
||||||
|
|
||||||
var radius = width * 0.3
|
|
||||||
|
|
||||||
var offsetX = width * 0.05
|
|
||||||
var offsetY = height * 0.05
|
|
||||||
|
|
||||||
//console.log("drawing result rect with width: " + width + " and height: " + height)
|
|
||||||
|
|
||||||
var context = getContext("2d");
|
|
||||||
|
|
||||||
// clear all remainings from other routes
|
|
||||||
context.clearRect(0, 0, width, height);
|
|
||||||
|
|
||||||
context.beginPath();
|
|
||||||
|
|
||||||
context.moveTo(0 + offsetX + radius, 0 + offsetY);
|
|
||||||
|
|
||||||
// top line
|
|
||||||
context.lineTo(width - radius + offsetX, 0 + offsetY);
|
|
||||||
// top right corner
|
|
||||||
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 0);
|
|
||||||
// right line
|
|
||||||
context.lineTo(width + offsetX, height - radius + offsetY);
|
|
||||||
// bottom right corner
|
|
||||||
context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI);
|
|
||||||
// bottom line
|
|
||||||
context.lineTo(0 + radius + offsetX, height + offsetY);
|
|
||||||
// bottom left corner
|
|
||||||
context.arc(radius + offsetY, height - radius + offsetY, radius, 0.5 * Math.PI, Math.PI);
|
|
||||||
// left line
|
|
||||||
context.lineTo(0 + offsetX, radius + offsetY);
|
|
||||||
// top left corner
|
|
||||||
context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI);
|
|
||||||
|
|
||||||
// fill
|
|
||||||
if(resultData[0] !== -1) {
|
|
||||||
// if there is a result available -> draw background
|
|
||||||
context.fillStyle = "#b7b7b7";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
context.fillStyle = "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
context.fill();
|
|
||||||
|
|
||||||
// outline
|
|
||||||
context.lineWidth = 1;
|
|
||||||
context.strokeStyle = '#424242';
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
if(resultData[1] > 0){
|
|
||||||
|
|
||||||
// the first triangle
|
|
||||||
context.beginPath();
|
|
||||||
|
|
||||||
// top right corner
|
|
||||||
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.75 * Math.PI, 0);
|
|
||||||
|
|
||||||
// right line
|
|
||||||
context.lineTo(width + offsetX, height - radius + offsetY);
|
|
||||||
|
|
||||||
// bottom right corner
|
|
||||||
context.arc(width-radius + offsetX, height - radius + offsetY, radius, 0, 0.5 * Math.PI);
|
|
||||||
|
|
||||||
// bottom line
|
|
||||||
context.lineTo(0 + radius + offsetX, height + offsetY);
|
|
||||||
// bottom left corner
|
|
||||||
context.arc(radius + offsetX, height - radius + offsetY, radius, 0.5 * Math.PI, 0.75 * Math.PI);
|
|
||||||
context.closePath();
|
|
||||||
|
|
||||||
context.fillStyle = "#44ed38";
|
|
||||||
context.fill();
|
|
||||||
|
|
||||||
// outline
|
|
||||||
context.lineWidth = 1;
|
|
||||||
context.strokeStyle = '#424242';
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
|
|
||||||
if(resultData[0] > 0){
|
|
||||||
// the second triangle
|
|
||||||
context.beginPath();
|
|
||||||
// bottom left corner
|
|
||||||
context.arc(radius + offsetX, height - radius + offsetY, radius, 0.75 * Math.PI, 1 * Math.PI);
|
|
||||||
// left line
|
|
||||||
context.lineTo(0 + offsetX, radius + offsetY);
|
|
||||||
// top left corner
|
|
||||||
context.arc(radius + offsetX, radius + offsetY, radius, Math.PI, 1.5 * Math.PI);
|
|
||||||
// top line
|
|
||||||
context.lineTo(width - radius + offsetX, 0 + offsetY);
|
|
||||||
// top right corner
|
|
||||||
context.arc(width-radius + offsetX, radius + offsetY, radius, 1.5 * Math.PI, 1.75 * Math.PI);
|
|
||||||
|
|
||||||
context.closePath();
|
|
||||||
|
|
||||||
context.fillStyle = "#44ed38";
|
|
||||||
context.fill();
|
|
||||||
|
|
||||||
// outline
|
|
||||||
context.lineWidth = 1;
|
|
||||||
context.strokeStyle = '#424242';
|
|
||||||
context.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: boulderResZoneLa
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
bottom: parent.bottom
|
|
||||||
margins: boulderResCv.height * 0.05
|
|
||||||
}
|
|
||||||
|
|
||||||
height: parent.height / 2
|
|
||||||
width: parent.width / 2
|
|
||||||
|
|
||||||
visible: parseInt(text) > 0
|
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.pixelSize: height
|
|
||||||
minimumPixelSize: 1
|
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
text: boulderResCv.resultData[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: boulderResTopLa
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
top: parent.top
|
|
||||||
margins: boulderResCv.height * 0.05
|
|
||||||
}
|
|
||||||
|
|
||||||
height: parent.height / 2
|
|
||||||
width: parent.width / 2
|
|
||||||
|
|
||||||
visible: parseInt(text) > 0
|
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.pixelSize: height
|
|
||||||
minimumPixelSize: 1
|
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
text: boulderResCv.resultData[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: multiGenResRow
|
|
||||||
|
|
||||||
property bool active: ((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"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: ItemDelegate {
|
footer: ItemDelegate {
|
||||||
|
@ -640,7 +197,7 @@ DataListView {
|
||||||
property var tabs: getTabs()
|
property var tabs: getTabs()
|
||||||
property var tabIndexes: []
|
property var tabIndexes: []
|
||||||
|
|
||||||
enabled: speedFlowChartBackgroundRect.state === "hidden"
|
enabled: speedFlowChartPopup.state === "hidden"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
@ -720,195 +277,13 @@ DataListView {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
SpeedFlowChartPopup {
|
||||||
id: speedFlowChartBackgroundRect
|
id: speedFlowChartPopup
|
||||||
|
|
||||||
state: "hidden"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
left: parent.right
|
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
color: Material.background
|
|
||||||
|
|
||||||
SpeedFlowChart {
|
|
||||||
id: speedFlowChart
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
flowchartData: ({})
|
|
||||||
|
|
||||||
onEnabledChanged: {
|
|
||||||
if(!enabled){
|
|
||||||
speedFlowChartBackgroundRect.state = 'hidden'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: speedFlowChartLockedOverlay
|
|
||||||
|
|
||||||
state: appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked"
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: -20
|
|
||||||
|
|
||||||
color: "white"
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: speedFlowChartProduct
|
|
||||||
onPurchaseRestored: {
|
|
||||||
speedFlowChartLockedOverlay.state = appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked"
|
|
||||||
}
|
|
||||||
|
|
||||||
onPurchaseSucceeded: {
|
|
||||||
speedFlowChartLockedOverlay.state = appSettings.read("speedBackendPurchase") === "1" ? "unlocked":"locked"
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PullRefresher {
|
PullRefresher {
|
||||||
|
// has to be placed here again,
|
||||||
|
// to be on top of the speed flowchart
|
||||||
target: control
|
target: control
|
||||||
|
|
||||||
postRefreshDelay: 0
|
postRefreshDelay: 0
|
||||||
|
@ -921,4 +296,5 @@ DataListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
|
||||||
|
@ -28,32 +29,45 @@ DataListView {
|
||||||
property bool ready
|
property bool ready
|
||||||
|
|
||||||
property string title: control.widgetData['comp_name']
|
property string title: control.widgetData['comp_name']
|
||||||
property string subTitle: qsTr("(Startlist)") + " " + control.widgetData['route_name'] //getSubtitle()
|
property string subTitle: getSubtitle()
|
||||||
property bool titleIsPageTitle: true
|
property bool titleIsPageTitle: true
|
||||||
|
|
||||||
property Component headerComponent: ToolButton {
|
property Component headerComponent: RowLayout {
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: shareToolBt
|
||||||
|
|
||||||
|
onClicked: shareWidget(control.title)
|
||||||
|
|
||||||
|
text: "\uf1e0"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
id: moreToolBt
|
id: moreToolBt
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
control.changeCat()
|
control.changeCat()
|
||||||
}
|
}
|
||||||
|
|
||||||
icon.name: "menu"
|
text: "\uf142"
|
||||||
|
font.family: fa5solid.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubtitle() {
|
function getSubtitle() {
|
||||||
var titleString
|
var titleString = control.widgetData["route_name"]
|
||||||
|
|
||||||
for(var i = 0; i < control.widgetData["categorys"].length; i ++ ){
|
//% "(Startlist)"
|
||||||
//console.log("checking " + i + ": cat: " + parseInt(control.widgetData["categorys"][i]["GrpId"]) + " searched cat: " + params.cat)
|
var addition = qsTrId("#startlistHeadline")
|
||||||
if(parseInt(control.widgetData["categorys"][i]["GrpId"]) === parseInt(params.cat)){
|
|
||||||
titleString = control.widgetData["categorys"][i]["name"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var addition = qsTr("(Startlist)")
|
|
||||||
return addition + " " + titleString
|
|
||||||
|
|
||||||
|
if(titleString)
|
||||||
|
return addition + " " + titleString
|
||||||
|
else
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeRoute(route) {
|
function changeRoute(route) {
|
||||||
|
@ -77,12 +91,12 @@ DataListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selector.appear(selectOptions, qsTr("select cat"))
|
selector.appear(selectOptions, qsTrId("#selectCategory"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: selector
|
target: selector
|
||||||
onSelectionFinished: {
|
function onSelectionFinished(index, data) {
|
||||||
if(data.cat !== undefined){
|
if(data.cat !== undefined){
|
||||||
updateData({cat: data.cat}, true)
|
updateData({cat: data.cat}, true)
|
||||||
}
|
}
|
||||||
|
@ -113,9 +127,11 @@ DataListView {
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
id: partDel
|
id: partDel
|
||||||
|
|
||||||
|
property int thisIndex: index
|
||||||
property var thisData: widgetData[ "participants" ][index]
|
property var thisData: widgetData[ "participants" ][index]
|
||||||
|
|
||||||
width: parent.width
|
width: control.width
|
||||||
|
height: 50
|
||||||
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
scale: 0.9
|
scale: 0.9
|
||||||
|
@ -136,6 +152,8 @@ DataListView {
|
||||||
|
|
||||||
text: ""
|
text: ""
|
||||||
|
|
||||||
|
highlighted: partDel.thisIndex % 2 == 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: partDelFirstRow
|
id: partDelFirstRow
|
||||||
|
|
||||||
|
@ -145,7 +163,7 @@ DataListView {
|
||||||
rightMargin: parent.width * 0.05
|
rightMargin: parent.width * 0.05
|
||||||
}
|
}
|
||||||
|
|
||||||
spacing: width * 0.1
|
spacing: width * 0.05
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
@ -153,7 +171,7 @@ DataListView {
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
font.bold: true
|
font.bold: true
|
||||||
font.pixelSize: Math.abs( height * 0.6 )
|
font.pixelSize: Math.abs( height * 0.4 )
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
@ -162,7 +180,7 @@ DataListView {
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
width: parent.width * 0.4
|
width: parent.width * 0.45
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
font.bold: true
|
font.bold: true
|
||||||
|
@ -178,10 +196,8 @@ DataListView {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
width: parent.width * 0.3
|
width: parent.width * 0.3
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.bold: false
|
font.bold: false
|
||||||
font.pixelSize: Math.abs( height * 0.4 )
|
font.pixelSize: height * 0.3
|
||||||
minimumPixelSize: height * 0.3
|
|
||||||
|
|
||||||
elide: "ElideRight"
|
elide: "ElideRight"
|
||||||
|
|
||||||
|
|
|
@ -20,31 +20,37 @@ import QtQuick 2.9
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Material 2.12
|
||||||
import QtPurchasing 1.12
|
import QtPurchasing 1.12
|
||||||
|
|
||||||
import com.itsblue.digitalRockRanking 1.0
|
import de.itsblue.blueROCK 1.0
|
||||||
|
|
||||||
import "./Pages"
|
import "./Pages"
|
||||||
import "./Components"
|
import "./Components"
|
||||||
|
|
||||||
|
import "./Widgets"
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
visible: true
|
visible: true
|
||||||
width: 540
|
width: 540
|
||||||
height: 960
|
height: 960
|
||||||
title: qsTr("blueROCK")
|
title: "blueROCK"
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: app
|
id: app
|
||||||
|
|
||||||
property int errorCode: -1
|
property int errorCode: -1
|
||||||
|
|
||||||
|
property var colorShade: Material.theme === Material.Light ? Material.Shade300 : Material.Shade700
|
||||||
|
property color nationalAdultsColor: Material.color(Material.Green, colorShade)
|
||||||
|
property color nationalYouthColor: Material.color(Material.LightGreen, colorShade)
|
||||||
|
property color federalColor: Material.color(Material.Grey, colorShade)
|
||||||
|
|
||||||
// comp cats source:
|
// 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/dav_calendar.php
|
||||||
// - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/sac_calendar.php
|
// - https://github.com/ralfbecker/ranking/blob/master/sitemgr/digitalrock/sac_calendar.php
|
||||||
|
|
||||||
property var compCats: {
|
property var compCats: {
|
||||||
|
|
||||||
// --- ICC ---
|
// --- ICC ---
|
||||||
|
|
||||||
/*'int' : {
|
/*'int' : {
|
||||||
|
@ -109,36 +115,10 @@ Window {
|
||||||
|
|
||||||
// --- GER ---
|
// --- GER ---
|
||||||
|
|
||||||
/*'ger_boulder' : {
|
|
||||||
'label' : 'Bouldern',
|
|
||||||
'nation' : 'GER',
|
|
||||||
'wettk_reg' : '^[0-9]{2,2}_B+.*',
|
|
||||||
'serie_reg' : '^[0-9]{2,2}_BC',
|
|
||||||
'bgcolor' : '#FFDBA8', //'#f59d30'
|
|
||||||
'cat_id' : [59]
|
|
||||||
},
|
|
||||||
'ger' : {
|
|
||||||
'label' : 'Lead',
|
|
||||||
'nation' : 'GER',
|
|
||||||
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}[^WLJ]+.*',
|
|
||||||
'serie_reg' : '^[0-9]{2,2}_DC',
|
|
||||||
'bgcolor' : '#A8F0A8', //'#69b9a9'
|
|
||||||
'cat_id' : [57]
|
|
||||||
},
|
|
||||||
'ger_speed' : {
|
|
||||||
'label' : 'Speed',
|
|
||||||
'nation' : 'GER',
|
|
||||||
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}[^WLJ]+.*',
|
|
||||||
'serie_reg' : '',
|
|
||||||
'rang_title': '',
|
|
||||||
'bgcolor' : '#A8F0A8', //'#e72e5d'
|
|
||||||
'cat_id' : [60]
|
|
||||||
},*/
|
|
||||||
|
|
||||||
'ger_meisterschaft' : {
|
'ger_meisterschaft' : {
|
||||||
'label' : 'Deutsche Meisterschaft',
|
'label' : 'Deutsche Meisterschaft',
|
||||||
'nation' : 'GER',
|
'nation' : 'GER',
|
||||||
'bgcolor' : '#A8F0A8',
|
'bgcolor' : app.nationalAdultsColor,//'#A8F0A8',
|
||||||
'sort_rank': 1,
|
'sort_rank': 1,
|
||||||
'cat_id' : [57, 59, 60]
|
'cat_id' : [57, 59, 60]
|
||||||
},
|
},
|
||||||
|
@ -148,8 +128,8 @@ Window {
|
||||||
'nation' : 'GER',
|
'nation' : 'GER',
|
||||||
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}[^WL]+.*',
|
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}[^WL]+.*',
|
||||||
'serie_reg' : '^[0-9]{2,2}_JC',
|
'serie_reg' : '^[0-9]{2,2}_JC',
|
||||||
// 'rang_title': 'Deutsche Jugend RANGLISTE',
|
// 'rang_title': 'Deutsche Jugend RANGLISTE',
|
||||||
'bgcolor' : '#D8FFD8',
|
'bgcolor' : app.nationalYouthColor,//'#D8FFD8',
|
||||||
'sort_rank': 2,
|
'sort_rank': 2,
|
||||||
'cat_id' : [58]
|
'cat_id' : [58]
|
||||||
},
|
},
|
||||||
|
@ -159,7 +139,7 @@ Window {
|
||||||
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
|
'wettk_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
|
||||||
'serie_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
|
'serie_reg' : '^[0-9]{2,2}[_J]{1,1}LM.*',
|
||||||
'rang_title': '',
|
'rang_title': '',
|
||||||
'bgcolor' : '#F0F0F0',
|
'bgcolor' : app.federalColor, //'#F0F0F0',
|
||||||
'sort_rank': 3,
|
'sort_rank': 3,
|
||||||
'cat_id' : [61,56]
|
'cat_id' : [61,56]
|
||||||
},
|
},
|
||||||
|
@ -172,7 +152,7 @@ Window {
|
||||||
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
|
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
|
||||||
'serie_reg' : '.*',
|
'serie_reg' : '.*',
|
||||||
'rang_title': 'SWISS RANKING',
|
'rang_title': 'SWISS RANKING',
|
||||||
'bgcolor' : '#A8F0A8',
|
'bgcolor' : app.nationalAdultsColor, //'#A8F0A8',
|
||||||
'sort_rank': 1,
|
'sort_rank': 1,
|
||||||
'cat_id' : [62,63]
|
'cat_id' : [62,63]
|
||||||
},
|
},
|
||||||
|
@ -182,7 +162,7 @@ Window {
|
||||||
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
|
'wettk_reg' : '^[0-9]{2,2}_[^R].*',
|
||||||
'serie_reg' : '.*',
|
'serie_reg' : '.*',
|
||||||
'rang_title': 'SWISS RANKING',
|
'rang_title': 'SWISS RANKING',
|
||||||
'bgcolor' : '#D8FFD8',
|
'bgcolor' : app.nationalYouthColor, //'#D8FFD8',
|
||||||
'sort_rank': 2,
|
'sort_rank': 2,
|
||||||
'cat_id' : [65]
|
'cat_id' : [65]
|
||||||
},
|
},
|
||||||
|
@ -191,16 +171,16 @@ Window {
|
||||||
'nation' : 'SUI',
|
'nation' : 'SUI',
|
||||||
'wettk_reg' : '^[0-9]{2,2}_RG_.*',
|
'wettk_reg' : '^[0-9]{2,2}_RG_.*',
|
||||||
'rang_title': '',
|
'rang_title': '',
|
||||||
'bgcolor' : '#F0F0F0',
|
'bgcolor' : app.federalColor, //'#F0F0F0',
|
||||||
'sort_rank': 3,
|
'sort_rank': 3,
|
||||||
'cat_id' : [64]
|
'cat_id' : [64]
|
||||||
},
|
},
|
||||||
'sui_ice' : {
|
'sui_ice' : {
|
||||||
'label' : 'Iceclimbing',
|
'label' : 'Iceclimbing',
|
||||||
'nation' : 'SUI',
|
'nation' : 'SUI',
|
||||||
'wettk_reg' : '^[0-9]{2,2}_RC_.*',
|
'wparams["valid"]ettk_reg' : '^[0-9]{2,2}_RC_.*',
|
||||||
'rang_title': '',
|
'rang_title': '',
|
||||||
'bgcolor' : '#F0F0F0',
|
'bgcolor' : app.federalColor, //'#F0F0F0',
|
||||||
'sort_rank': 4,
|
'sort_rank': 4,
|
||||||
'cat_id' : [84]
|
'cat_id' : [84]
|
||||||
}
|
}
|
||||||
|
@ -208,24 +188,30 @@ Window {
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Component.onCompleted: {
|
Material.theme: appSettings.read("darkTheme") === "true" ? Material.Dark:Material.Light
|
||||||
//app.openAthlete(53139) // dorian: 53139 , rustam: 6933 , helen: 53300
|
|
||||||
//openWidget({nation:'GER'})
|
FontLoader {
|
||||||
//mainStack.push("Pages/AthleteSearchPage.qml")
|
id: fa5solid
|
||||||
|
source: "qrc:/fonts/fa5solid.otf"
|
||||||
|
}
|
||||||
|
|
||||||
|
FontLoader {
|
||||||
|
id: fa5regular
|
||||||
|
source: "qrc:/fonts/fa5regular.otf"
|
||||||
}
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequences: ["Esc", "Back"]
|
sequences: ["Esc", "Back"]
|
||||||
enabled: mainStack.depth > 1
|
enabled: mainStack.depth > 1
|
||||||
onActivated: {
|
onActivated: app.goBack()
|
||||||
if(!mainStack.currentItem.locked){
|
|
||||||
mainStack.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerConn {
|
BlueRockBackend {
|
||||||
id: serverConn
|
id: serverConn
|
||||||
|
|
||||||
|
onOpenedViaUrl: {
|
||||||
|
app.openWidgetFromUrl(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppSettings {
|
AppSettings {
|
||||||
|
@ -235,8 +221,6 @@ Window {
|
||||||
StackView {
|
StackView {
|
||||||
id: mainStack
|
id: mainStack
|
||||||
|
|
||||||
//enabled: !loadingDl.opened
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: toolBar.bottom
|
top: toolBar.bottom
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
@ -313,13 +297,10 @@ Window {
|
||||||
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
onClicked: {
|
onClicked: app.goBack()
|
||||||
if(!mainStack.currentItem.locked){
|
|
||||||
mainStack.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
icon.name: "back"
|
text: "\uf053"
|
||||||
|
font.family: fa5solid.name
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -329,20 +310,16 @@ Window {
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
width: parent.width - extraComponentLoader.width - toolButton.width - 3 * parent.spacing
|
width: parent.width - extraComponentLoader.width - toolButton.width - 3 * parent.spacing
|
||||||
|
|
||||||
Label {
|
MovingLabel {
|
||||||
id: toolBarTitleLa
|
id: toolBarTitleLa
|
||||||
|
|
||||||
|
syncWithLabel: toolBarSubTitleLa
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
scale: 1
|
scale: 1
|
||||||
|
|
||||||
elide: "ElideRight"
|
|
||||||
|
|
||||||
font.bold: true
|
font.bold: true
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
color: "black"
|
|
||||||
|
|
||||||
text: getText()
|
text: getText()
|
||||||
|
|
||||||
function getText(){
|
function getText(){
|
||||||
|
@ -371,18 +348,16 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
MovingLabel {
|
||||||
id: toolBarSubTitleLa
|
id: toolBarSubTitleLa
|
||||||
|
|
||||||
width: parent.width
|
visible: text !== ""
|
||||||
height: text !== "" ? undefined:0
|
|
||||||
|
|
||||||
elide: "ElideRight"
|
syncWithLabel: toolBarTitleLa
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
font.bold: false
|
font.bold: false
|
||||||
|
|
||||||
color: "black"
|
|
||||||
|
|
||||||
text: getText()
|
text: getText()
|
||||||
|
|
||||||
function getText(){
|
function getText(){
|
||||||
|
@ -392,7 +367,7 @@ Window {
|
||||||
titleString = mainStack.currentItem.subTitle
|
titleString = mainStack.currentItem.subTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
return(titleString)
|
return titleString
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on text {
|
Behavior on text {
|
||||||
|
@ -411,28 +386,20 @@ Window {
|
||||||
|
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
Layout.alignment: Layout.Right
|
onItemChanged: {
|
||||||
|
if(item === null) {
|
||||||
onStatusChanged: {
|
|
||||||
//console.log("loader status changed: " + status)
|
|
||||||
|
|
||||||
if(status == 0){
|
|
||||||
extraComponentLoader.Layout.preferredWidth = 0
|
extraComponentLoader.Layout.preferredWidth = 0
|
||||||
}
|
return
|
||||||
else {
|
|
||||||
extraComponentLoader.item.width = extraComponentLoader.item.implicitWidth
|
|
||||||
extraComponentLoader.Layout.preferredWidth = extraComponentLoader.item.width
|
|
||||||
widthChangedCon.target = extraComponentLoader.item
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log("set loader width to: " + extraComponentLoader.Layout.preferredWidth + " for a item width of: " + extraComponentLoader.item.implicitWidth)
|
extraComponentLoader.item.width = extraComponentLoader.item.implicitWidth
|
||||||
|
extraComponentLoader.Layout.preferredWidth = extraComponentLoader.item.width
|
||||||
|
widthChangedCon.target = extraComponentLoader.item
|
||||||
}
|
}
|
||||||
|
|
||||||
//sourceComponent: mainStack.currentItem.headerComponent
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: mainStack
|
target: mainStack
|
||||||
onCurrentItemChanged: {
|
function onCurrentItemChanged() {
|
||||||
secondCon.target = mainStack.currentItem
|
secondCon.target = mainStack.currentItem
|
||||||
disappearNa.start()
|
disappearNa.start()
|
||||||
}
|
}
|
||||||
|
@ -440,14 +407,15 @@ Window {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
id: secondCon
|
id: secondCon
|
||||||
onHeaderComponentChanged: {
|
target: null
|
||||||
|
function onHeaderComponentChanged() {
|
||||||
disappearNa.start()
|
disappearNa.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
id: widthChangedCon
|
id: widthChangedCon
|
||||||
onImplicitWidthChanged:{
|
function onImplicitWidthChanged() {
|
||||||
extraComponentLoader.item.width = extraComponentLoader.item.implicitWidth
|
extraComponentLoader.item.width = extraComponentLoader.item.implicitWidth
|
||||||
extraComponentLoader.Layout.preferredWidth = extraComponentLoader.item.implicitWidth
|
extraComponentLoader.Layout.preferredWidth = extraComponentLoader.item.implicitWidth
|
||||||
}
|
}
|
||||||
|
@ -543,7 +511,7 @@ Window {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog {
|
Popup {
|
||||||
id: loadingDl
|
id: loadingDl
|
||||||
|
|
||||||
x: ( app.width - width ) / 2
|
x: ( app.width - width ) / 2
|
||||||
|
@ -553,6 +521,9 @@ Window {
|
||||||
closePolicy: Dialog.NoAutoClose
|
closePolicy: Dialog.NoAutoClose
|
||||||
|
|
||||||
contentItem: Column {
|
contentItem: Column {
|
||||||
|
|
||||||
|
spacing: 50
|
||||||
|
|
||||||
FancyBusyIndicator {
|
FancyBusyIndicator {
|
||||||
running: true
|
running: true
|
||||||
}
|
}
|
||||||
|
@ -562,16 +533,30 @@ Window {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
font.bold: true
|
font.bold: true
|
||||||
|
color: "white"
|
||||||
|
|
||||||
text: "loading..."
|
//% "Loading"
|
||||||
|
text: qsTrId("#loading") + "..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Store {
|
Store {
|
||||||
|
id: inAppProductStore
|
||||||
|
|
||||||
Product {
|
Product {
|
||||||
id: speedFlowChartProduct
|
id: speedFlowChartProduct
|
||||||
identifier: "speed_flowchart"
|
identifier: "speed_flowchart"
|
||||||
|
//identifier: "android.test.purchased"
|
||||||
|
//identifier: "android.test.canceled"
|
||||||
|
//identifier: "android.test.refunded"
|
||||||
|
|
||||||
type: Product.Unlockable
|
type: Product.Unlockable
|
||||||
|
|
||||||
onPurchaseRestored: {
|
onPurchaseRestored: {
|
||||||
|
@ -588,28 +573,58 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function landscape(){
|
function landscape() {
|
||||||
return app.height < app.width
|
return app.height < app.width
|
||||||
}
|
}
|
||||||
|
|
||||||
function openWidget(params){
|
function toggleDarkMode() {
|
||||||
|
var dark = app.Material.theme === Material.Light
|
||||||
|
|
||||||
|
app.Material.theme = dark ? Material.Dark : Material.Light
|
||||||
|
appSettings.write("darkTheme", dark)
|
||||||
|
}
|
||||||
|
|
||||||
|
function largeScreen() {
|
||||||
|
return Math.min(app.width, app.height) > 750
|
||||||
|
}
|
||||||
|
|
||||||
|
function openWidget(params) {
|
||||||
loadingDl.open()
|
loadingDl.open()
|
||||||
|
|
||||||
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
|
console.log("Opening widget: ", JSON.stringify(params))
|
||||||
app.errorCode = calComp.status
|
|
||||||
|
|
||||||
if(calComp.ready){
|
var result = false
|
||||||
mainStack.push(calComp)
|
|
||||||
}
|
if(Object.keys(params).length) {
|
||||||
else {
|
var calComp = Qt.createComponent("qrc:/Pages/WidgetPage.qml").createObject(null, {"params": params})
|
||||||
delete(calComp)
|
app.errorCode = calComp.status
|
||||||
|
|
||||||
|
if(calComp.ready) {
|
||||||
|
mainStack.push(calComp)
|
||||||
|
result = true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete(calComp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingDl.close()
|
loadingDl.close()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultString(string, defaultString){
|
function openWidgetFromUrl(url) {
|
||||||
if(string === undefined || string === null){
|
var result = serverConn.getParamsFromUrl(url)
|
||||||
|
|
||||||
|
if(result["valid"]) {
|
||||||
|
openWidget(result["params"])
|
||||||
|
return app.errorCode !== 906
|
||||||
|
}
|
||||||
|
|
||||||
|
return result["valid"]
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultString(string, defaultString) {
|
||||||
|
if(string === undefined || string === null) {
|
||||||
return defaultString
|
return defaultString
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -617,6 +632,11 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
if(!mainStack.currentItem.hasOwnProperty('onBackRequested') || mainStack.currentItem.onBackRequested())
|
||||||
|
mainStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
function getErrorInfo(errorCode) {
|
function getErrorInfo(errorCode) {
|
||||||
|
|
||||||
var infoLevel
|
var infoLevel
|
||||||
|
@ -625,66 +645,36 @@ Window {
|
||||||
// 2 - error
|
// 2 - error
|
||||||
|
|
||||||
var errorString
|
var errorString
|
||||||
var errorDescription
|
|
||||||
|
|
||||||
switch(errorCode) {
|
switch(errorCode) {
|
||||||
case 0:
|
case 0:
|
||||||
infoLevel = 2
|
infoLevel = 2
|
||||||
errorString = "No connection to server"
|
//% "No connection to server"
|
||||||
errorDescription = "Please check your internet connection and try again."
|
errorString = qsTrId("#noConnectionError")
|
||||||
break
|
break
|
||||||
case 200:
|
case 404:
|
||||||
infoLevel = 0
|
|
||||||
errorString = "Success"
|
|
||||||
errorDescription = "The request was successfull"
|
|
||||||
break
|
|
||||||
case 401:
|
|
||||||
infoLevel = 2
|
infoLevel = 2
|
||||||
errorString = "Authentication required"
|
//% "Not found"
|
||||||
errorDescription = "The server asked for user credentinals, please chack them and try again"
|
errorString = qsTrId("#notFoundError")
|
||||||
break
|
|
||||||
case 500:
|
|
||||||
infoLevel = 2
|
|
||||||
errorString = "Internal server error"
|
|
||||||
errorDescription = "The server was unable to process this request, this is probaply the servers fault. Please try again later."
|
|
||||||
break
|
|
||||||
case 900:
|
|
||||||
infoLevel = 2
|
|
||||||
errorString = "Internal error"
|
|
||||||
errorDescription = "Something went wron internally, this is probaply an inssue in the program code"
|
|
||||||
break
|
break
|
||||||
case 901:
|
case 901:
|
||||||
infoLevel = 1
|
infoLevel = 1
|
||||||
errorString = "No Data"
|
//% "No Data"
|
||||||
errorDescription = "There is currently no data available. Please try again later."
|
errorString = qsTrId("#noDataError")
|
||||||
break
|
break
|
||||||
case 902:
|
case 906:
|
||||||
infoLevel = 1
|
|
||||||
errorString = "Cached (old) data"
|
|
||||||
errorDescription = "Es konnte keine Verbindung zum Server hergestellt werden, aber es sind noch alte Daten gespeichert."
|
|
||||||
break
|
|
||||||
case 903:
|
|
||||||
infoLevel = 1
|
|
||||||
errorString = "Ungültiger Aufruf"
|
|
||||||
errorDescription = "Die aufgerufene Funktion ist momentan nicht verfügbar, bitte versuche es später erneut."
|
|
||||||
break
|
|
||||||
case 904:
|
|
||||||
infoLevel = 2
|
infoLevel = 2
|
||||||
errorString = "Incompatible API"
|
//% "Invalid Request"
|
||||||
errorDescription = "Please make shure that you are using the latest version of this app and try again."
|
errorString = qsTrId("#invalidRequestError")
|
||||||
break
|
errorDescription = "Invalid Request"
|
||||||
case 905:
|
|
||||||
infoLevel = 1
|
|
||||||
errorString = "Loading..."
|
|
||||||
errorDescription = "Please wait while we're loading some data"
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
infoLevel = 2
|
infoLevel = 2
|
||||||
errorString = "Unexpected error ("+errorCode+")"
|
//% "Unexpected error"
|
||||||
errorDescription = "Unexpected error while getting data from the server. Please try again later."
|
errorString = qsTrId("#unexpectedError") + " ("+errorCode+")"
|
||||||
}
|
}
|
||||||
|
|
||||||
return([infoLevel, errorString, errorDescription])
|
return([infoLevel, errorString])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,21 @@
|
||||||
<file>Pages/AthleteSearchPage.qml</file>
|
<file>Pages/AthleteSearchPage.qml</file>
|
||||||
<file>Components/SpeedFlowChart.qml</file>
|
<file>Components/SpeedFlowChart.qml</file>
|
||||||
<file>Components/SwipeGallery.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>
|
||||||
|
<file>Components/DisclaimerDialog.qml</file>
|
||||||
|
<file>Components/ColoredItemDelegate.qml</file>
|
||||||
|
<file>Components/AlignedButton.qml</file>
|
||||||
|
<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>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
BIN
resources/shared/PosterTemplate.png
Normal file
After Width: | Height: | Size: 235 KiB |
BIN
resources/shared/PosterTemplate.xcf
Normal file
BIN
resources/shared/badge.xcf
Normal file
BIN
resources/shared/blueRockHold.xcf
Normal file
BIN
resources/shared/fonts/OpenSans-Light.ttf
Normal file
BIN
resources/shared/fonts/fa5regular.otf
Normal file
BIN
resources/shared/fonts/fa5solid.otf
Normal file
BIN
resources/shared/icons/blueRockHold.png
Normal file
After Width: | Height: | Size: 240 KiB |
Before Width: | Height: | Size: 220 B |
Before Width: | Height: | Size: 487 B |
Before Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 715 B |
Before Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 123 B |
Before Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 369 B |
Before Width: | Height: | Size: 123 B |
Before Width: | Height: | Size: 606 B |
Before Width: | Height: | Size: 501 B |
Before Width: | Height: | Size: 843 B |