Library migration #36
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "shared-libraries"]
|
||||||
|
path = shared-libraries
|
||||||
|
url = https://git.itsblue.de/ScStw/shared-libraries.git
|
52
CHANGELOG
|
@ -1,52 +0,0 @@
|
||||||
# Changelog
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [1.1]
|
|
||||||
### Added
|
|
||||||
- connecting to and controlling a ScStw Basestation is now possible
|
|
||||||
- creating profiles and saving times is now possible (only when connected to a ScStw Basestation)
|
|
||||||
- dark mode
|
|
||||||
### Changed
|
|
||||||
- new, more modern style
|
|
||||||
- new start tone which is compliant with the IFSC norm
|
|
||||||
- new and improved codebase
|
|
||||||
|
|
||||||
|
|
||||||
## [0.04] - 2018-08-11
|
|
||||||
### Added
|
|
||||||
- buzzer icon in the upper left corner indicating that the buzzer is connected
|
|
||||||
### Fixed
|
|
||||||
- start seqnece continues in a buggy way when cancel is being pressed while 'at your marks' or 'ready'
|
|
||||||
- bug that made the start sequence freeze if a delay of zero or lower or a non valid number was set as delay
|
|
||||||
### Changed
|
|
||||||
- increased the size of the back buttons in settings / profiles dialog
|
|
||||||
|
|
||||||
## [0.03 - BETA] - 2018-07-29
|
|
||||||
### Added
|
|
||||||
- cancel button during start sequence
|
|
||||||
- new screen in landscape mode
|
|
||||||
- buttons for settings and profiles
|
|
||||||
- the screen stays on now
|
|
||||||
- the volume csontrols control the media volume directly
|
|
||||||
- settings dialog
|
|
||||||
- capabilitie to connect to a Buzzer via Wifi
|
|
||||||
- it is now possible to setup an automatic start sequence that spells the command
|
|
||||||
'at your marks' and 'ready' with a customizable delay before them
|
|
||||||
### Fixed
|
|
||||||
- bug that made a Button freeze when it was pressed and the screen rotated at the same time
|
|
||||||
|
|
||||||
## [0.02] - 2018-07-18
|
|
||||||
### Fixed
|
|
||||||
- negative time when the stopping starts
|
|
||||||
- removed delay between the end of the startton an the begin of the stopping
|
|
||||||
### Changed
|
|
||||||
- slowed down animations
|
|
||||||
### Added
|
|
||||||
- animation for the text "click start to start" between STOPPED and IDLE to
|
|
||||||
prevent it from getting out of the screen
|
|
||||||
|
|
||||||
## [0.01]
|
|
||||||
### Initial Release
|
|
38
ScStwApp.pro
|
@ -18,26 +18,20 @@ 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
|
||||||
|
|
||||||
TARGET = speedclimbing_stw
|
TARGET = ScStwApp
|
||||||
|
|
||||||
|
# include submodules
|
||||||
|
CONFIG += ScStwLibraries_QML ScStwLibraries_Styling ScStwLibraries_ClientLibs
|
||||||
|
include($$PWD/shared-libraries/ScStwLibraries/ScStwLibraries.pri)
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
sources/scstwappbackend.cpp \
|
||||||
sources/main.cpp \
|
sources/main.cpp \
|
||||||
sources/sqlstoragemodel.cpp \
|
sources/scstwappsettings.cpp
|
||||||
sources/sqlprofilemodel.cpp \
|
|
||||||
sources/appsettings.cpp \
|
|
||||||
sources/baseconn.cpp \
|
|
||||||
sources/speedtimer.cpp \
|
|
||||||
sources/climbingrace.cpp \
|
|
||||||
sources/apptheme.cpp
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
headers/sqlstoragemodel.h \
|
headers/scstwappbackend.h \
|
||||||
headers/sqlprofilemodel.h \
|
headers/scstwappsettings.h
|
||||||
headers/appsettings.h \
|
|
||||||
headers/baseconn.h \
|
|
||||||
headers/speedtimer.h \
|
|
||||||
headers/climbingrace.h \
|
|
||||||
headers/apptheme.h
|
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
resources/shared/shared.qrc \
|
resources/shared/shared.qrc \
|
||||||
|
@ -59,10 +53,18 @@ else: unix:!android: target.path = /home/pi/$${TARGET}/bin
|
||||||
!isEmpty(target.path): INSTALLS += target
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
android-sources/AndroidManifest.xml \
|
|
||||||
CHANGELOG \
|
CHANGELOG \
|
||||||
android-sources/src/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/src/de/itsblue/scstw/MainActivity.java
|
||||||
|
|
||||||
android {
|
android {
|
||||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-sources
|
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||||
|
ANDROID_ABIS = armeabi-v7a arm64-v8a
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,54 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<manifest package="com.itsblue.speedclimbing_stopwatch" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
|
<manifest package="com.itsblue.speedclimbing_stwtest" 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="speedclimbing stw" 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="speedclimbing stw" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
Remove the comment if you do not require these default permissions. -->
|
||||||
|
<!-- %%INSERT_PERMISSIONS -->
|
||||||
|
|
||||||
|
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||||
|
Remove the comment if you do not require these default features. -->
|
||||||
|
<!-- %%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="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
||||||
<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="speedclimbing stw"/>
|
|
||||||
<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:plugins/mediaservice/libqtmedia_android.so:lib/libQt5MultimediaQuick.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/QtMultimedia.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="org.qtproject.qt5.android.multimedia.QtMultimediaUtils"/>
|
<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,42 +56,21 @@
|
||||||
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>
|
</activity>
|
||||||
|
|
||||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
|
||||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
|
||||||
|
|
||||||
<!-- 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.ACCESS_NETWORK_STATE"/>
|
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 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:name="android.hardware.camera" android:required="false"/>
|
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
|
||||||
<uses-feature android:name="android.hardware.microphone" android:required="false"/>
|
|
||||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -22,11 +22,9 @@
|
||||||
<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="speedclimbing_stw"/>
|
<meta-data android:name="android.app.lib_name" android:value="speedclimbing_stw"/>
|
||||||
<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"/>
|
||||||
|
@ -49,12 +47,10 @@
|
||||||
<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"/>
|
||||||
<!-- Messages maps -->
|
<!-- Messages maps -->
|
||||||
|
|
||||||
<!-- Splash screen -->
|
<!-- Splash screen -->
|
||||||
<!-- 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
|
||||||
|
@ -62,11 +58,9 @@
|
||||||
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 :
|
||||||
* full - useful QWidget & Quick Controls 1 apps
|
* full - useful QWidget & Quick Controls 1 apps
|
||||||
|
@ -75,14 +69,11 @@
|
||||||
-->
|
-->
|
||||||
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
|
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
|
||||||
<!-- extract android style -->
|
<!-- extract android style -->
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28"/>
|
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="29"/>
|
||||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||||
|
|
||||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
77
android/build.gradle
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
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'])
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 29
|
||||||
|
}
|
||||||
|
}
|
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
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 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>
|
|
@ -15,12 +15,13 @@
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.itsblue.speedclimbing_stopwatch;
|
package de.itsblue.scstw;
|
||||||
|
|
||||||
public class MainActivity extends org.qtproject.qt5.android.bindings.QtActivity {
|
public class MainActivity extends org.qtproject.qt5.android.bindings.QtActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(android.os.Bundle savedInstanceState){
|
public void onCreate(android.os.Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
this.getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
this.getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
//this.getWindow().setVolumeControlStream(android.view.AudioManager.STREAM_MUSIC);
|
this.setVolumeControlStream(android.media.AudioManager.STREAM_MUSIC);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
#ifndef APPSETTINGS_H
|
|
||||||
#define APPSETTINGS_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
|
|
||||||
class AppSettings : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit AppSettings(QObject *parent = nullptr);
|
|
||||||
~AppSettings();
|
|
||||||
|
|
||||||
Q_INVOKABLE QString loadSetting(const QString &key);
|
|
||||||
Q_INVOKABLE void writeSetting(const QString &key, const QVariant &variant);
|
|
||||||
Q_INVOKABLE void setDefaultSetting(const QString &key, const QVariant &defaultVariant);
|
|
||||||
|
|
||||||
QSettings *settingsManager;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
};
|
|
||||||
extern AppSettings * pGlobalAppSettings;
|
|
||||||
|
|
||||||
#endif // APPSETTINGS_H
|
|
|
@ -1,31 +0,0 @@
|
||||||
#ifndef APPTHEME_H
|
|
||||||
#define APPTHEME_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QVariant>
|
|
||||||
#include "appsettings.h"
|
|
||||||
|
|
||||||
class AppTheme : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QVariant style READ getStyle NOTIFY styleChanged)
|
|
||||||
public:
|
|
||||||
explicit AppTheme(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QVariant lightTheme;
|
|
||||||
QVariant darkTheme;
|
|
||||||
|
|
||||||
QVariant * currentTheme;
|
|
||||||
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void styleChanged();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
QVariant getStyle();
|
|
||||||
Q_INVOKABLE void changeTheme();
|
|
||||||
Q_INVOKABLE void refreshTheme();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // APPTHEME_H
|
|
|
@ -1,153 +0,0 @@
|
||||||
#ifndef BASECONN_H
|
|
||||||
#define BASECONN_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QTcpSocket>
|
|
||||||
#include <QDataStream>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QSemaphore>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QtConcurrent/QtConcurrent>
|
|
||||||
#include <string.h>
|
|
||||||
#include <QByteArray>
|
|
||||||
|
|
||||||
#include "headers/appsettings.h"
|
|
||||||
#include "headers/speedtimer.h"
|
|
||||||
|
|
||||||
class BaseConn : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit BaseConn(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
// values for the socket connection
|
|
||||||
int connection_progress;
|
|
||||||
QString ip;
|
|
||||||
ushort port = 3563;
|
|
||||||
int errors;
|
|
||||||
int errors_until_disconnect = 4;
|
|
||||||
|
|
||||||
QVariant connections;
|
|
||||||
|
|
||||||
QString latestReadReply;
|
|
||||||
|
|
||||||
//---general status values---//
|
|
||||||
|
|
||||||
// some meta data of the base
|
|
||||||
QString firmwareVersion;
|
|
||||||
bool firmwareUpToDate;
|
|
||||||
double timeOffset;
|
|
||||||
|
|
||||||
|
|
||||||
// the current state
|
|
||||||
QString state;
|
|
||||||
// can be:
|
|
||||||
// - 'disconnected'
|
|
||||||
// - 'connecting'
|
|
||||||
// - 'connected'
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
QDateTime *date;
|
|
||||||
//to get the current time
|
|
||||||
|
|
||||||
QTcpSocket *socket;
|
|
||||||
//socket for communication with the extention
|
|
||||||
|
|
||||||
QTimer *timeoutTimer;
|
|
||||||
|
|
||||||
QString readBuffer;
|
|
||||||
|
|
||||||
int nextConnectionId;
|
|
||||||
|
|
||||||
struct waitingRequest {
|
|
||||||
int id;
|
|
||||||
QEventLoop * loop;
|
|
||||||
QJsonObject reply;
|
|
||||||
};
|
|
||||||
|
|
||||||
QList<waitingRequest> waitingRequests;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void stateChanged();
|
|
||||||
//is emitted, when the connection state changes
|
|
||||||
|
|
||||||
void progressChanged();
|
|
||||||
//is emmited during the connection process when the progress changes
|
|
||||||
|
|
||||||
void gotUnexpectedReply(QString reply);
|
|
||||||
|
|
||||||
void gotUpdate(QVariant data);
|
|
||||||
|
|
||||||
void connectionsChanged();
|
|
||||||
|
|
||||||
void connectionSlotReleased();
|
|
||||||
|
|
||||||
void nextRemoteActionChanged();
|
|
||||||
|
|
||||||
void nextRemoteActionDelayProgChanged();
|
|
||||||
|
|
||||||
void gotError(QString error);
|
|
||||||
|
|
||||||
void propertiesChanged();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
void connectToHost();
|
|
||||||
//function to connect to the base station
|
|
||||||
|
|
||||||
void connectionTimeout();
|
|
||||||
|
|
||||||
bool init();
|
|
||||||
void deInit();
|
|
||||||
|
|
||||||
void closeConnection();
|
|
||||||
|
|
||||||
void gotError(QAbstractSocket::SocketError err);
|
|
||||||
|
|
||||||
// --- socket communication handling ---
|
|
||||||
|
|
||||||
QVariantMap sendCommand(int header, QJsonValue data = "", bool useTerminationKeys = true, int timeout = 3000);
|
|
||||||
|
|
||||||
// --- updater functions ---
|
|
||||||
|
|
||||||
bool updateTime();
|
|
||||||
bool updateFirmware();
|
|
||||||
bool isFirmwareUpToDate();
|
|
||||||
|
|
||||||
// --- helper functions ---
|
|
||||||
|
|
||||||
int writeRemoteSetting(QString key, QString value);
|
|
||||||
|
|
||||||
bool refreshConnections();
|
|
||||||
|
|
||||||
void setConnections(QVariantList connections);
|
|
||||||
|
|
||||||
|
|
||||||
// functions for the qml adapter
|
|
||||||
QString getIP() const;
|
|
||||||
void setIP(const QString &ipAdress);
|
|
||||||
|
|
||||||
QString getState() const;
|
|
||||||
void setState(QString newState);
|
|
||||||
|
|
||||||
int getProgress() const;
|
|
||||||
|
|
||||||
QVariant getConnections();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void readyRead();
|
|
||||||
|
|
||||||
void processSocketMessage(QString message);
|
|
||||||
|
|
||||||
void socketReplyRecieved(QString reply);
|
|
||||||
|
|
||||||
void socketStateChanged(QAbstractSocket::SocketState socketState);
|
|
||||||
};
|
|
||||||
extern BaseConn * pGlobalBaseConn;
|
|
||||||
|
|
||||||
#endif // BASECONN_H
|
|
|
@ -1,79 +0,0 @@
|
||||||
#ifndef BUZZERCONN_H
|
|
||||||
#define BUZZERCONN_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QtNetwork>
|
|
||||||
#include <QAuthenticator>
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QtDebug>
|
|
||||||
|
|
||||||
typedef struct strReturnData{
|
|
||||||
int status_code;
|
|
||||||
QString text;
|
|
||||||
}ReturnData_t;
|
|
||||||
|
|
||||||
class BuzzerConn : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit BuzzerConn(QObject *parent = nullptr, QString ip = "http://192.168.4.1", int port = 80);
|
|
||||||
double offset;
|
|
||||||
QList<double> latest_offsets;
|
|
||||||
double latest_button_pressed;
|
|
||||||
double starttime;
|
|
||||||
bool connected;
|
|
||||||
int connection_progress;
|
|
||||||
QString ip;
|
|
||||||
int port;
|
|
||||||
int errors;
|
|
||||||
int errors_until_disconnect = 4;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
QNetworkAccessManager *networkManager;
|
|
||||||
QNetworkAccessManager *reloadNetworkManager;
|
|
||||||
QDateTime *date;
|
|
||||||
QTcpSocket *socket;
|
|
||||||
QStringList pending_commands;
|
|
||||||
//QSemaphore dataPipe(1);
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
ReturnData_t senddata(QNetworkAccessManager * NetMan, QUrl serviceUrl, int timeout);
|
|
||||||
//function to communicate with the buzzer
|
|
||||||
Q_INVOKABLE signed long sendCommand(QString command, int timeout);
|
|
||||||
//function to send commands to the sensor
|
|
||||||
//Can be:
|
|
||||||
//command - return
|
|
||||||
//GET_TIMESTAMP - timestamp of the sensor
|
|
||||||
//GET_LASTPRESSED - timestamp of the sensor when it was triggered the last time
|
|
||||||
//
|
|
||||||
//error codes:
|
|
||||||
//(-1) : timeout
|
|
||||||
//(-2) : invalid data was recieved
|
|
||||||
Q_INVOKABLE QList<double> gettimes(int timeout);
|
|
||||||
//function to get the times from the buzzer as a list with the normal network manager
|
|
||||||
Q_INVOKABLE bool connect();
|
|
||||||
//function to connect to buzzer
|
|
||||||
Q_INVOKABLE bool calcoffset(QList<double> times);
|
|
||||||
//function that calculates the average time offset between the buzzer and the device
|
|
||||||
Q_INVOKABLE bool buzzer_triggered();
|
|
||||||
//function that checks ih the buzzer has been pushed since the last call of this function
|
|
||||||
Q_INVOKABLE bool start();
|
|
||||||
//syncs the buzzer and the base to make a start possible
|
|
||||||
Q_INVOKABLE double get(QString key);
|
|
||||||
//can return some things (offset, lastpressed, currtime, connection_progress, connected)
|
|
||||||
Q_INVOKABLE QString test();
|
|
||||||
Q_INVOKABLE bool refresh();
|
|
||||||
//refreshed the connection to the buzzer
|
|
||||||
Q_INVOKABLE void appendCommand(QString command);
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BUZZERCONN_H
|
|
|
@ -1,120 +0,0 @@
|
||||||
#ifndef CLIMBINGRACE_H
|
|
||||||
#define CLIMBINGRACE_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QSound>
|
|
||||||
#include <QSoundEffect>
|
|
||||||
#include <QMediaPlayer>
|
|
||||||
#include "headers/baseconn.h"
|
|
||||||
#include "headers/appsettings.h"
|
|
||||||
#include "headers/speedtimer.h"
|
|
||||||
|
|
||||||
class ClimbingRace : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(int state READ getState NOTIFY stateChanged)
|
|
||||||
Q_PROPERTY(int mode READ getMode NOTIFY modeChanged)
|
|
||||||
Q_PROPERTY(QVariant timers READ getTimerTextList NOTIFY timerTextChanged)
|
|
||||||
Q_PROPERTY(QString baseStationState READ getBaseStationState NOTIFY baseStationStateChanged)
|
|
||||||
Q_PROPERTY(QVariant baseStationConnections READ getBaseStationConnections NOTIFY baseStationConnectionsChanged)
|
|
||||||
Q_PROPERTY(double nextStartActionDelayProgress READ getNextStartActionDelayProgress NOTIFY nextStartActionDelayProgressChanged)
|
|
||||||
Q_PROPERTY(int nextStartAction READ getNextStartAction NOTIFY nextStartActionChanged)
|
|
||||||
Q_PROPERTY(QVariantMap baseStationProperties READ getBaseStationProperties NOTIFY baseStationPropertiesChanged)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ClimbingRace(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
enum raceState { IDLE, STARTING, WAITING, RUNNING, STOPPED };
|
|
||||||
raceState state;
|
|
||||||
|
|
||||||
enum raceMode { LOCAL, REMOTE };
|
|
||||||
raceMode mode;
|
|
||||||
|
|
||||||
enum NextStartAction { AtYourMarks, Ready, Start, None };
|
|
||||||
|
|
||||||
private:
|
|
||||||
AppSettings * appSettings;
|
|
||||||
BaseConn * baseConn;
|
|
||||||
|
|
||||||
QMediaPlayer * player;
|
|
||||||
|
|
||||||
QTimer * timerTextRefreshTimer;
|
|
||||||
QTimer * nextStartActionTimer;
|
|
||||||
|
|
||||||
QDateTime *date;
|
|
||||||
|
|
||||||
QList<SpeedTimer *> speedTimers;
|
|
||||||
|
|
||||||
NextStartAction nextStartAction;
|
|
||||||
|
|
||||||
double nextStartActionDelayProgress;
|
|
||||||
// only used in remote mode:
|
|
||||||
double nextStartActionDelayStartedAt;
|
|
||||||
double nextStartActionTotalDelay;
|
|
||||||
|
|
||||||
// helper vars
|
|
||||||
QVariantList qmlTimers;
|
|
||||||
const QStringList remoteSettings = {"ready_en", "ready_delay", "at_marks_en", "at_marks_delay"};
|
|
||||||
const QStringList remoteOnlySettings = {"soundVolume"};
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
// helper functions
|
|
||||||
void playSoundsAndStartRace();
|
|
||||||
bool playSound(QString path);
|
|
||||||
void setState(raceState newState);
|
|
||||||
void refreshMode();
|
|
||||||
void refreshTimerText();
|
|
||||||
|
|
||||||
bool refreshRemoteTimers(QVariantList timers);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void nextStartActionChanged();
|
|
||||||
void nextStartActionDelayProgressChanged();
|
|
||||||
|
|
||||||
void stateChanged(int state);
|
|
||||||
void modeChanged();
|
|
||||||
void timerTextChanged();
|
|
||||||
void baseStationStateChanged();
|
|
||||||
void baseStationConnectionsChanged();
|
|
||||||
void baseStationPropertiesChanged();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
Q_INVOKABLE int startRace();
|
|
||||||
Q_INVOKABLE int stopRace(int type);
|
|
||||||
Q_INVOKABLE int resetRace();
|
|
||||||
|
|
||||||
// base station sync
|
|
||||||
void handleBaseStationUpdate(QVariant data);
|
|
||||||
Q_INVOKABLE bool pairConnectedUsbExtensions();
|
|
||||||
|
|
||||||
// functions for qml
|
|
||||||
Q_INVOKABLE int getState();
|
|
||||||
Q_INVOKABLE int getMode();
|
|
||||||
Q_INVOKABLE QVariant getTimerTextList();
|
|
||||||
Q_INVOKABLE double getNextStartActionDelayProgress();
|
|
||||||
Q_INVOKABLE int getNextStartAction();
|
|
||||||
|
|
||||||
Q_INVOKABLE void writeSetting(QString key, QVariant value);
|
|
||||||
Q_INVOKABLE QString readSetting(QString key);
|
|
||||||
|
|
||||||
Q_INVOKABLE void connectBaseStation();
|
|
||||||
Q_INVOKABLE void disconnectBaseStation();
|
|
||||||
Q_INVOKABLE QString getBaseStationState();
|
|
||||||
Q_INVOKABLE QVariant getBaseStationConnections();
|
|
||||||
Q_INVOKABLE QVariantMap getBaseStationProperties();
|
|
||||||
|
|
||||||
Q_INVOKABLE bool updateBasestationFirmware();
|
|
||||||
Q_INVOKABLE bool updateBasestationTime();
|
|
||||||
|
|
||||||
// athlete management
|
|
||||||
Q_INVOKABLE QVariant getAthletes();
|
|
||||||
Q_INVOKABLE bool createAthlete( QString userName, QString fullName );
|
|
||||||
Q_INVOKABLE bool deleteAthlete( QString userName );
|
|
||||||
Q_INVOKABLE bool selectAthlete( QString userName, int timerId );
|
|
||||||
Q_INVOKABLE QVariant getResults( QString userName );
|
|
||||||
|
|
||||||
Q_INVOKABLE bool reloadBaseStationIpAdress();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLIMBINGRACE_H
|
|
42
headers/scstwappbackend.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef SCSTWAPPBACKEND_H
|
||||||
|
#define SCSTWAPPBACKEND_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <scstwclient.h>
|
||||||
|
#include <scstwrace.h>
|
||||||
|
#include <ScStw.hpp>
|
||||||
|
#include <scstwremoterace.h>
|
||||||
|
|
||||||
|
#include "headers/scstwappsettings.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ScStwAppBackend : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(ScStwClient *scStwClient READ getScStwClient WRITE setScStwClient NOTIFY scStwClientChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ScStwAppBackend(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScStwClient * scStwClient;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// functions for qml
|
||||||
|
Q_INVOKABLE ScStwClient *getScStwClient();
|
||||||
|
Q_INVOKABLE void setScStwClient(ScStwClient *client);
|
||||||
|
|
||||||
|
// athlete management
|
||||||
|
Q_INVOKABLE QVariant getAthletes();
|
||||||
|
Q_INVOKABLE bool createAthlete( QString userName, QString fullName );
|
||||||
|
Q_INVOKABLE bool deleteAthlete( QString userName );
|
||||||
|
Q_INVOKABLE bool selectAthlete( QString userName, int timerId );
|
||||||
|
Q_INVOKABLE QVariant getResults( QString userName );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void scStwClientChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SCSTWAPPBACKEND_H
|
58
headers/scstwappsettings.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef APPSETTINGS_H
|
||||||
|
#define APPSETTINGS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include "scstwremotesettings.h"
|
||||||
|
|
||||||
|
class ScStwAppSettings : public ScStwRemoteSettings
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ScStwAppSettings(QObject *parent = nullptr);
|
||||||
|
explicit ScStwAppSettings(ScStwClient * scStwClient, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
enum AppInternalSetting {
|
||||||
|
InvalidSetting = -1,
|
||||||
|
AppThemeSetting,
|
||||||
|
BaseStationIpSetting
|
||||||
|
};
|
||||||
|
Q_ENUM(AppInternalSetting)
|
||||||
|
|
||||||
|
enum KeyLevelEnum {
|
||||||
|
KeyLevel = 1
|
||||||
|
};
|
||||||
|
Q_ENUM(KeyLevelEnum)
|
||||||
|
|
||||||
|
using ScStwSettings::readSetting;
|
||||||
|
Q_INVOKABLE QVariant readSetting(AppInternalSetting key);
|
||||||
|
using ScStwSettings::writeSetting;
|
||||||
|
Q_INVOKABLE bool writeSetting(AppInternalSetting key, QVariant value);
|
||||||
|
using ScStwSettings::setDefaultSetting;
|
||||||
|
Q_INVOKABLE bool setDefaultSetting(AppInternalSetting key, QVariant defaultValue);
|
||||||
|
|
||||||
|
static QString keyToString(int key) {
|
||||||
|
return QMetaEnum::fromType<AppInternalSetting>().valueToKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariant::Type keyToType(int key) {
|
||||||
|
QMap<AppInternalSetting, QVariant::Type> types = {
|
||||||
|
{AppThemeSetting, QVariant::String},
|
||||||
|
{BaseStationIpSetting, QVariant::String}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(types.contains(AppInternalSetting(key)))
|
||||||
|
return types[AppInternalSetting(key)];
|
||||||
|
|
||||||
|
return QVariant::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
};
|
||||||
|
extern ScStwAppSettings * pGlobalAppSettings;
|
||||||
|
|
||||||
|
#endif // APPSETTINGS_H
|
|
@ -1,48 +0,0 @@
|
||||||
#ifndef SPEEDTIMER_H
|
|
||||||
#define SPEEDTIMER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
class SpeedTimer : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit SpeedTimer(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
enum timerState { IDLE, STARTING, WAITING, RUNNING, WON, LOST, FAILED, CANCELLED, DISABLED };
|
|
||||||
timerState state;
|
|
||||||
|
|
||||||
// variables for capturing the time
|
|
||||||
double startTime;
|
|
||||||
double stopTime;
|
|
||||||
double stoppedTime;
|
|
||||||
double reactionTime;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void stateChanged(timerState newState);
|
|
||||||
void startCanceled(bool falseStart);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
bool start(bool force = false);
|
|
||||||
bool stop(int type, bool force = false);
|
|
||||||
bool reset(bool force = false);
|
|
||||||
|
|
||||||
void setState(timerState newState);
|
|
||||||
QString getState();
|
|
||||||
double getCurrTime();
|
|
||||||
QString getText();
|
|
||||||
|
|
||||||
//helper functions
|
|
||||||
|
|
||||||
void delay(int mSecs);
|
|
||||||
|
|
||||||
timerState stateFromString(QString state);
|
|
||||||
private:
|
|
||||||
QDateTime *date;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SPEEDTIMER_H
|
|
|
@ -1,29 +0,0 @@
|
||||||
#ifndef SQLPROFILEMODEL_H
|
|
||||||
#define SQLPROFILEMODEL_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QSqlError>
|
|
||||||
#include <QSqlQuery>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QSqlRecord>
|
|
||||||
#include <QSqlTableModel>
|
|
||||||
|
|
||||||
class SqlProfileModel : public QSqlTableModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit SqlProfileModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
|
|
||||||
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
|
|
||||||
Q_INVOKABLE bool append(QString name);
|
|
||||||
Q_INVOKABLE void remove(int row);
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SQLPROFILEMODEL_H
|
|
|
@ -1,26 +0,0 @@
|
||||||
#ifndef SQLSTORAGEMODEL_H
|
|
||||||
#define SQLSTORAGEMODEL_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QSqlError>
|
|
||||||
#include <QSqlQuery>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QSqlRecord>
|
|
||||||
#include <QSqlTableModel>
|
|
||||||
|
|
||||||
class SqlStorageModel : public QSqlTableModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SqlStorageModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
|
|
||||||
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SQLSTORAGEMODEL_H
|
|
99
resources/qml/MainPage/BottomToolBar.qml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
|
||||||
|
import "../components"
|
||||||
|
import "../components/layout"
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
|
||||||
|
ToolBar {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property bool connectedToBase: speedBackend.scStwClient.state === ScStwClient.CONNECTED
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: bottomContentLayout
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: app.landscape() ? 0:parent.width * 0.1
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: app.landscape() ? 0:parent.width * 0.1
|
||||||
|
top: parent.top
|
||||||
|
topMargin: app.landscape() ? parent.height * 0.1:0
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: app.landscape() ? parent.height * 0.1:0
|
||||||
|
}
|
||||||
|
|
||||||
|
columns: app.landscape() ? 1:2
|
||||||
|
rows: app.landscape() ? 2:1
|
||||||
|
|
||||||
|
FancyButton {
|
||||||
|
id: settingsButt
|
||||||
|
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
Layout.preferredHeight: app.landscape() ? width:control.height * 0.8
|
||||||
|
Layout.preferredWidth: app.landscape() ? control.width * 0.8:height
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
settingsDialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
image: appTheme.theme.images.settIcon
|
||||||
|
|
||||||
|
backgroundColor: parent.pressed ? appTheme.theme.colors.buttonPressed:appTheme.theme.colors.button
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FancyButton {
|
||||||
|
id: profilesButt
|
||||||
|
|
||||||
|
property double size
|
||||||
|
|
||||||
|
state: control.connectedToBase ? "visible":"hidden"
|
||||||
|
visible: size > 0
|
||||||
|
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
Layout.preferredWidth: app.landscape() ? control.width * size:control.height * size
|
||||||
|
Layout.preferredHeight: width
|
||||||
|
|
||||||
|
image: appTheme.theme.images.profilesIcon
|
||||||
|
|
||||||
|
backgroundColor: parent.pressed ? appTheme.theme.colors.buttonPressed:appTheme.theme.colors.button
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
profilesDialog.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "hidden"
|
||||||
|
PropertyChanges {
|
||||||
|
target: profilesButt
|
||||||
|
size: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "visible"
|
||||||
|
PropertyChanges {
|
||||||
|
target: profilesButt
|
||||||
|
size: 0.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
properties: "size"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
395
resources/qml/MainPage/CenterContent.qml
Normal file
|
@ -0,0 +1,395 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
|
||||||
|
import "../components"
|
||||||
|
import "../components/layout"
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: centerLayout
|
||||||
|
|
||||||
|
//anchors.centerIn: parent
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
columns: app.landscape() ? 3:1
|
||||||
|
rows: app.landscape() ? 1:3
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: centerExtraContentTop
|
||||||
|
|
||||||
|
property double size
|
||||||
|
|
||||||
|
Layout.preferredHeight: app.landscape() ? centerLayout.height * 0.6:Math.min(centerLayout.width * size, centerLayout.height * size)
|
||||||
|
Layout.preferredWidth: app.landscape() ? Math.min(centerLayout.width * size, centerLayout.height * size):centerLayout.width * 0.8
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
|
|
||||||
|
Behavior on size {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 800
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: centerExtraContentTopStack
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 1
|
||||||
|
|
||||||
|
property QtObject newItem: emptyComp
|
||||||
|
|
||||||
|
onNewItemChanged: {
|
||||||
|
centerExtraContentTopStack.replace(newItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceEnter: Transition {
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 400
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceExit: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: waitingDetailsComp
|
||||||
|
|
||||||
|
Column {
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: stateIndicatorRow
|
||||||
|
|
||||||
|
property int delegateWidth: width / stateIndicatorRepeater.model - (spacing * (stateIndicatorRepeater.model - 1) / stateIndicatorRepeater.model)
|
||||||
|
property int delegateHeight: height
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height * 0.9
|
||||||
|
|
||||||
|
spacing: width * 0.1
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: stateIndicatorRepeater
|
||||||
|
|
||||||
|
model: scStwRemoteRace.timers.length
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: timerStatusColumn
|
||||||
|
|
||||||
|
property var thisTimer: scStwRemoteRace.timers[index]
|
||||||
|
|
||||||
|
width: parent.delegateWidth
|
||||||
|
height: parent.delegateHeight
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.preferredWidth: parent.width
|
||||||
|
Layout.preferredHeight: parent.height * 0.1
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.8
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
minimumPointSize: 1
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
|
text: "Lane " + timerStatusColumn.thisTimer["letter"]
|
||||||
|
}
|
||||||
|
|
||||||
|
StateIndicator {
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
backgroundColor: appTheme.theme.colors.background
|
||||||
|
successColor: appTheme.theme.colors.success
|
||||||
|
warningColor: appTheme.theme.colors.warning
|
||||||
|
|
||||||
|
state: timerStatusColumn.thisTimer["readyState"] === ScStwTimer.IsReady ?
|
||||||
|
"success":timerStatusColumn.thisTimer["readyState"] === ScStwTimer.IsDisabled ?
|
||||||
|
"unknown":"warn"
|
||||||
|
|
||||||
|
indicatorSize: 0.8
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var disabled = timerStatusColumn.thisTimer["state"] !== ScStwTimer.DISABLED
|
||||||
|
console.log("setting timer to disabled: " + disabled)
|
||||||
|
scStwRemoteRace.setTimerDisabled(timerStatusColumn.thisTimer["id"], disabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height * 0.1
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.6
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
minimumPointSize: 1
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
|
text: "Tap on a lane to disable it"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: incidentDetailsComp
|
||||||
|
|
||||||
|
ExtensionOverview {
|
||||||
|
opacity: 0
|
||||||
|
backgroundColor: appTheme.theme.colors.background
|
||||||
|
delegateHeight: centerExtraContentTopStack.height / 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: emptyComp
|
||||||
|
Item {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MainActionButton {
|
||||||
|
id: mainActionButton
|
||||||
|
|
||||||
|
property double size
|
||||||
|
startProgress: scStwRemoteRace.currentStartDelay["progress"]
|
||||||
|
|
||||||
|
Layout.alignment: Layout.Center
|
||||||
|
|
||||||
|
Layout.preferredHeight: app.landscape() ? width:Math.min(centerLayout.width * size, centerLayout.height * size)
|
||||||
|
Layout.preferredWidth: app.landscape() ? Math.min(centerLayout.width * size, centerLayout.height * size):height
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if(progressControlActivated && progress < 1.0)
|
||||||
|
return
|
||||||
|
|
||||||
|
var ret;
|
||||||
|
|
||||||
|
switch(scStwRemoteRace.state) {
|
||||||
|
case ScStwRace.IDLE:
|
||||||
|
ret = scStwRemoteRace.start()
|
||||||
|
break;
|
||||||
|
case ScStwRace.WAITING:
|
||||||
|
if(!scStwRemoteRace.readySoundEnabled && scStwRemoteRace.isReadyForNextState)
|
||||||
|
ret = scStwRemoteRace.start()
|
||||||
|
else
|
||||||
|
ret = scStwRemoteRace.cancel()
|
||||||
|
break;
|
||||||
|
case ScStwRace.PREPAIRING:
|
||||||
|
case ScStwRace.STARTING:
|
||||||
|
ret = scStwRemoteRace.cancel()
|
||||||
|
break;
|
||||||
|
case ScStwRace.RUNNING:
|
||||||
|
ret = scStwRemoteRace.stop()
|
||||||
|
break;
|
||||||
|
case ScStwRace.STOPPED:
|
||||||
|
case ScStwRace.INCIDENT:
|
||||||
|
ret = scStwRemoteRace.reset()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ret !== 200)
|
||||||
|
console.log("Error executing main button action: " + ret)
|
||||||
|
|
||||||
|
progress = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on size {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 800
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: centerExtraContentBottom
|
||||||
|
|
||||||
|
property double size
|
||||||
|
|
||||||
|
Layout.preferredHeight: app.landscape() ? centerLayout.height:Math.min(centerLayout.width * size, centerLayout.height * size)
|
||||||
|
Layout.preferredWidth: app.landscape() ? Math.min(centerLayout.width * size, centerLayout.height * size):centerLayout.width
|
||||||
|
|
||||||
|
Behavior on size {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: ScStwRace.IDLE
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainActionButton
|
||||||
|
size: 0.9
|
||||||
|
text: "start"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerLayout
|
||||||
|
|
||||||
|
height: app.landscape() ? control.height : Math.max(control.height, app.height * 0.4)
|
||||||
|
width: app.landscape() ? Math.max(control.width, app.width * 0.4) : control.width
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.PREPAIRING
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainActionButton
|
||||||
|
size: 0.9
|
||||||
|
text: "cancel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.WAITING
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainActionButton
|
||||||
|
size: scStwRemoteRace.competitionMode ? 0.3:0.9
|
||||||
|
text: scStwRemoteRace.readySoundEnabled ? "cancel": scStwRemoteRace.isReadyForNextState ? "ready":"cancel"
|
||||||
|
progressControlActivated: scStwRemoteRace.competitionMode && !scStwRemoteRace.readySoundEnabled && !scStwRemoteRace.isReadyForNextState
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerExtraContentTop
|
||||||
|
|
||||||
|
size: scStwRemoteRace.competitionMode ? 0.7:0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerExtraContentBottom
|
||||||
|
|
||||||
|
size: scStwRemoteRace.competitionMode ? 0.05:0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerExtraContentTopStack
|
||||||
|
|
||||||
|
newItem: scStwRemoteRace.competitionMode ? waitingDetailsComp:emptyComp
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.STARTING
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainActionButton
|
||||||
|
size: 0.9
|
||||||
|
text: "cancel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.RUNNING
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainActionButton
|
||||||
|
size: 0.9
|
||||||
|
text: scStwRemoteRace.competitionMode ? "cancel":"stop"
|
||||||
|
progressControlActivated: scStwRemoteRace.competitionMode
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.STOPPED
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainActionButton
|
||||||
|
|
||||||
|
size: 0.5
|
||||||
|
|
||||||
|
text: "reset"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerLayout
|
||||||
|
|
||||||
|
height: app.landscape() ? control.height : Math.max(control.height, app.height * 0.4)
|
||||||
|
width: app.landscape() ? Math.max(control.width, app.width * 0.4) : control.width
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.INCIDENT
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: mainActionButton
|
||||||
|
|
||||||
|
size: 0.5
|
||||||
|
|
||||||
|
text: "reset"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerExtraContentTop
|
||||||
|
|
||||||
|
size: scStwRemoteRace.competitionMode ? 0.7:0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerExtraContentBottom
|
||||||
|
|
||||||
|
size: scStwRemoteRace.competitionMode ? 0.05:0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: centerExtraContentTopStack
|
||||||
|
|
||||||
|
newItem: scStwRemoteRace.competitionMode ? incidentDetailsComp:emptyComp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
307
resources/qml/MainPage/TopToolBar.qml
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.9
|
||||||
|
|
||||||
|
import "../components"
|
||||||
|
import "../components/layout"
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
|
||||||
|
ToolBar {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property string statusText
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: topContentLoader
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Behavior on state {
|
||||||
|
FadeAnimation {
|
||||||
|
target: topContentLoader
|
||||||
|
fadeDuration: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: raceStatusLabelComponent
|
||||||
|
Text {
|
||||||
|
id: raceStatusLabel
|
||||||
|
|
||||||
|
padding: app.landscape() ? width * 0.2 : height * 0.25
|
||||||
|
|
||||||
|
text: control.statusText
|
||||||
|
|
||||||
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
font.pixelSize: height
|
||||||
|
|
||||||
|
minimumPixelSize: 1
|
||||||
|
|
||||||
|
Behavior on text {
|
||||||
|
FadeAnimation {
|
||||||
|
target: raceStatusLabel
|
||||||
|
fadeDuration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: timerColumnComponent
|
||||||
|
TimerColumn {
|
||||||
|
id: timerColumn
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: app.landscape() ? 0:parent.height * 0.1
|
||||||
|
anchors.bottomMargin: app.landscape() ? 0:parent.height * 0.1
|
||||||
|
|
||||||
|
timers: scStwRemoteRace.timers
|
||||||
|
colors: appTheme.theme.colors
|
||||||
|
fontName: appTheme.theme.fonts.timers
|
||||||
|
showTimerLetter: true
|
||||||
|
|
||||||
|
|
||||||
|
// make text smaller for much better performance
|
||||||
|
textScale: app.landscape() ? 0.7 : 0.5
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: connectionIconContainer
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: app.landscape() ? parent.height * 0.8:0
|
||||||
|
rightMargin: app.landscape() ? 0:parent.width * 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
opacity: scStwRemoteRace.state === ScStwRace.IDLE ? 1:0
|
||||||
|
|
||||||
|
ConnectionIcon {
|
||||||
|
id: baseConnConnIcon
|
||||||
|
|
||||||
|
function clientStateToString(state) {
|
||||||
|
switch(state) {
|
||||||
|
case ScStwClient.DISCONNECTED:
|
||||||
|
return "disconnected"
|
||||||
|
case ScStwClient.CONNECTING:
|
||||||
|
return "connecting"
|
||||||
|
case ScStwClient.INITIALISING:
|
||||||
|
return "connecting"
|
||||||
|
case ScStwClient.CONNECTED:
|
||||||
|
return "connected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status: clientStateToString(speedBackend.scStwClient.state)
|
||||||
|
|
||||||
|
source: appTheme.theme.images.baseStationIcon
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 10
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
height: !app.landscape()? parent.height*0.4:parent.width*0.4
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: extensionStatusRow
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 10
|
||||||
|
left: baseConnConnIcon.right
|
||||||
|
leftMargin: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
height: baseConnConnIcon.height * 0.4
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: extensionStatusRep
|
||||||
|
|
||||||
|
model: Object.keys(speedBackend.scStwClient.extensions)
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: statusRect
|
||||||
|
|
||||||
|
property string thisLetter: modelData
|
||||||
|
property var thisExtensions: speedBackend.scStwClient.extensions[modelData]
|
||||||
|
property int thisLaneState: getLaneState(thisExtensions)
|
||||||
|
|
||||||
|
width: height
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
radius: width * 0.1
|
||||||
|
|
||||||
|
color: appTheme.theme.colors.background
|
||||||
|
border.color: [appTheme.theme.colors.success, appTheme.theme.colors.warning, appTheme.theme.colors.error][thisLaneState]
|
||||||
|
border.width: width * 0.08
|
||||||
|
|
||||||
|
onThisExtensionsChanged: {
|
||||||
|
thisLaneState = getLaneState(thisExtensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLaneState(extensions) {
|
||||||
|
var batteryWarning = false;
|
||||||
|
for(var i = 0; i < extensions.length; i++) {
|
||||||
|
if(extensions[i]["state"] !== ScStw.ExtensionConnected || extensions[i]["batteryState"] === ScStw.BatteryCritical)
|
||||||
|
return 2
|
||||||
|
if(extensions[i]["batteryState"] === ScStw.BatteryWarning)
|
||||||
|
batteryWarning = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return batteryWarning ? 1:0
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
text: parent.thisLetter
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
font.pixelSize: height * 0.7
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
color: statusRect.border.color
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: ScStwRace.IDLE
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
statusText: app.landscape() ? "Press\nstart":"Press start"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: topContentLoader
|
||||||
|
sourceComponent: raceStatusLabelComponent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.PREPAIRING
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
statusText: "At your\nmarks"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: topContentLoader
|
||||||
|
sourceComponent: raceStatusLabelComponent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.WAITING
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
statusText: "Ready"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: topContentLoader
|
||||||
|
sourceComponent: raceStatusLabelComponent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.STARTING
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
statusText: "Starting"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: topContentLoader
|
||||||
|
sourceComponent: raceStatusLabelComponent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.RUNNING
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: topContentLoader
|
||||||
|
sourceComponent: timerColumnComponent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.STOPPED
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: topContentLoader
|
||||||
|
sourceComponent: timerColumnComponent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: ScStwRace.INCIDENT
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
statusText: app.landscape() ? "Technical\nincident!":"Technical incident!"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: topContentLoader
|
||||||
|
sourceComponent: raceStatusLabelComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,11 @@ 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 QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import com.itsblue.speedclimbingstopwatch 1.0
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,11 @@ 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 QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import com.itsblue.speedclimbingstopwatch 1.0
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
|
@ -91,7 +95,7 @@ RemoteDataListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
text: swipeDelegate.text
|
text: swipeDelegate.text
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
@ -104,7 +108,7 @@ RemoteDataListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: pressed ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
color: pressed ? appTheme.theme.colors.delegatePressed : appTheme.theme.colors.delegateBackground
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
|
|
||||||
|
@ -128,22 +132,22 @@ RemoteDataListView {
|
||||||
Repeater {
|
Repeater {
|
||||||
id: athleteSelectBoxRep
|
id: athleteSelectBoxRep
|
||||||
|
|
||||||
model: speedBackend.timers.length
|
model: scStwRemoteRace.timers.length
|
||||||
|
|
||||||
delegate: CheckBox {
|
delegate: CheckBox {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property bool active: speedBackend.timers[index]["id"] === profileList.listData[swipeDelegate.thisIndex]["active"]
|
property bool active: scStwRemoteRace.timers[index]["id"] === profileList.listData[swipeDelegate.thisIndex]["active"]
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
height: parent.height * 0.6
|
height: parent.height * 0.6
|
||||||
|
|
||||||
enabled: speedBackend.timers[index]["state"] !== "DISABLED"
|
enabled: scStwRemoteRace.timers[index]["state"] !== ScStwTimer.DISABLED
|
||||||
checked: control.active
|
checked: control.active
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
if(checked && !control.active && speedBackend.selectAthlete(profileList.listData[swipeDelegate.thisIndex]["userName"], speedBackend.timers[index]["id"])){
|
if(checked && !control.active && speedBackend.selectAthlete(profileList.listData[swipeDelegate.thisIndex]["userName"], scStwRemoteRace.timers[index]["id"])){
|
||||||
profileList.loadData()
|
profileList.loadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +164,7 @@ RemoteDataListView {
|
||||||
|
|
||||||
radius: width * 0.2
|
radius: width * 0.2
|
||||||
border.color: control.enabled ? control.down ? "#17a81a" : "#21be2b" : "grey"
|
border.color: control.enabled ? control.down ? "#17a81a" : "#21be2b" : "grey"
|
||||||
color: control.down ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
color: control.down ? appTheme.theme.colors.delegatePressed : appTheme.theme.colors.delegateBackground
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width * 0.65
|
width: parent.width * 0.65
|
||||||
|
@ -233,7 +237,7 @@ RemoteDataListView {
|
||||||
Label {
|
Label {
|
||||||
id: deleteLabel
|
id: deleteLabel
|
||||||
text: qsTr("Delete")
|
text: qsTr("Delete")
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
verticalAlignment: Label.AlignVCenter
|
verticalAlignment: Label.AlignVCenter
|
||||||
padding: 12
|
padding: 12
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
|
@ -21,7 +21,12 @@ 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 QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import com.itsblue.speedclimbingstopwatch 1.0
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +64,7 @@ Popup {
|
||||||
id: backgroundRect
|
id: backgroundRect
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: width * 0.1
|
radius: width * 0.1
|
||||||
color: appTheme.style.viewColor
|
color: appTheme.theme.colors.view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +92,7 @@ Popup {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root
|
target: root
|
||||||
onOpened: {
|
function onOpened() {
|
||||||
profilesStack.init()
|
profilesStack.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +137,7 @@ Popup {
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
property color color: appTheme.style.viewColor
|
property color color: appTheme.theme.colors.view
|
||||||
|
|
||||||
onPaint: {
|
onPaint: {
|
||||||
var ctx = getContext("2d");
|
var ctx = getContext("2d");
|
||||||
|
@ -173,7 +178,7 @@ Popup {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
text: profilesStack.currentItem.title
|
text: profilesStack.currentItem.title
|
||||||
|
|
||||||
|
@ -196,9 +201,9 @@ Popup {
|
||||||
|
|
||||||
glowOpacity: Math.pow( root.opacity, 100 )
|
glowOpacity: Math.pow( root.opacity, 100 )
|
||||||
|
|
||||||
backgroundColor: appTheme.style.buttonColor
|
backgroundColor: appTheme.theme.colors.button
|
||||||
|
|
||||||
image: appTheme.style.backIcon
|
image: appTheme.theme.images.backIcon
|
||||||
|
|
||||||
onClicked: profilesStack.depth > 1 ? profilesStack.pop():root.close()
|
onClicked: profilesStack.depth > 1 ? profilesStack.pop():root.close()
|
||||||
|
|
||||||
|
@ -221,9 +226,9 @@ Popup {
|
||||||
|
|
||||||
glowOpacity: opacity < 1 ? Math.pow( opacity, 100 ) : Math.pow( opacity, 100 )
|
glowOpacity: opacity < 1 ? Math.pow( opacity, 100 ) : Math.pow( opacity, 100 )
|
||||||
|
|
||||||
backgroundColor: appTheme.style.buttonColor
|
backgroundColor: appTheme.theme.colors.button
|
||||||
|
|
||||||
image: appTheme.style.confirmIcon
|
image: appTheme.theme.images.confirmIcon
|
||||||
imageScale: profilesStack.currentItem.secondButt === "ok" ? 1:0
|
imageScale: profilesStack.currentItem.secondButt === "ok" ? 1:0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
@ -235,7 +240,7 @@ Popup {
|
||||||
}
|
}
|
||||||
opacity: profilesStack.currentItem.secondButt === "add" ? 1:0
|
opacity: profilesStack.currentItem.secondButt === "add" ? 1:0
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
text: "+"
|
text: "+"
|
||||||
font.pixelSize: parent.height * 0.8
|
font.pixelSize: parent.height * 0.8
|
||||||
|
|
|
@ -21,7 +21,11 @@ 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 QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import com.itsblue.speedclimbingstopwatch 1.0
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,12 @@ 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 QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import com.itsblue.speedclimbingstopwatch 1.0
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
RemoteDataListView {
|
RemoteDataListView {
|
||||||
|
@ -83,7 +88,7 @@ RemoteDataListView {
|
||||||
font.pixelSize: height * 0.8
|
font.pixelSize: height * 0.8
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
text: resultDel.getDateText()
|
text: resultDel.getDateText()
|
||||||
}
|
}
|
||||||
|
@ -96,7 +101,7 @@ RemoteDataListView {
|
||||||
font.pixelSize: height * 0.8
|
font.pixelSize: height * 0.8
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
text: qsTr("result: ") + (listData[index]["result"] / 1000).toFixed(3) + " s"
|
text: qsTr("result: ") + (listData[index]["result"] / 1000).toFixed(3) + " s"
|
||||||
}
|
}
|
||||||
|
@ -109,7 +114,7 @@ RemoteDataListView {
|
||||||
font.pixelSize: height * 0.8
|
font.pixelSize: height * 0.8
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
text: qsTr("reaction time: ") + listData[index]["reactionTime"].toFixed(0) + " ms"
|
text: qsTr("reaction time: ") + listData[index]["reactionTime"].toFixed(0) + " ms"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import QtQuick 2.9
|
|
||||||
import QtMultimedia 5.8
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
import QtQuick.Controls 2.2
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import QtQuick.Controls.Styles 1.4
|
|
||||||
import "../components"
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: control
|
|
||||||
|
|
||||||
property string title: qsTr("connections")
|
|
||||||
property var parentObj
|
|
||||||
|
|
||||||
spacing: parentObj.rowSpacing
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
|
|
||||||
model: speedBackend.baseStationConnections.length
|
|
||||||
delegate: ConnectionDelegate {
|
|
||||||
|
|
||||||
opacity: 1
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: parentObj.delegateHeight
|
|
||||||
|
|
||||||
text: speedBackend.baseStationConnections[index]["name"]
|
|
||||||
status: {'status': speedBackend.baseStationConnections[index]["state"], 'progress': speedBackend.baseStationConnections[index]["progress"]}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.12
|
||||||
import QtMultimedia 5.8
|
import QtMultimedia 5.8
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
|
@ -6,6 +6,13 @@ import QtQuick.Layouts 1.3
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
import QtQuick.Templates 2.12 as T
|
import QtQuick.Templates 2.12 as T
|
||||||
|
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -13,60 +20,72 @@ Column {
|
||||||
|
|
||||||
property string title: qsTr("base station")
|
property string title: qsTr("base station")
|
||||||
|
|
||||||
property bool baseConnected: speedBackend.baseStationState === "connected"
|
property bool baseConnected: speedBackend.scStwClient.state === ScStwClient.CONNECTED
|
||||||
property var parentObj
|
property var parentObj
|
||||||
|
|
||||||
opacity: 0
|
opacity: 0
|
||||||
|
|
||||||
function doFirmwareUpdate() {
|
|
||||||
doFirmwareUpdateTimer.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: doFirmwareUpdateTimer
|
|
||||||
|
|
||||||
interval: 10
|
|
||||||
repeat: false
|
|
||||||
running: false
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
busyDl.open()
|
|
||||||
var ret = speedBackend.updateBasestationFirmware()
|
|
||||||
busyDl.displayMessageAndClose(ret ? "OK":"error", ret ? "#6bd43b":"#e03b2f" )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionDelegate {
|
ConnectionDelegate {
|
||||||
id: connectToBaseDel
|
id: connectToBaseDel
|
||||||
|
|
||||||
|
function clientStateToString(state) {
|
||||||
|
switch(state) {
|
||||||
|
case ScStwClient.DISCONNECTED:
|
||||||
|
return "disconnected"
|
||||||
|
case ScStwClient.CONNECTING:
|
||||||
|
return "connecting"
|
||||||
|
case ScStwClient.INITIALISING:
|
||||||
|
return "connecting"
|
||||||
|
case ScStwClient.CONNECTED:
|
||||||
|
return "connected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
text: status.status === "connected" ? qsTr("disconnect"): status.status === "disconnected" ? qsTr("connect"):qsTr("connecting...")
|
text: status.status === "connected" ? qsTr("disconnect"): status.status === "disconnected" ? qsTr("connect"):qsTr("connecting...")
|
||||||
|
|
||||||
status: { "status": speedBackend.baseStationState, "progress": 100 }
|
status: { "status": clientStateToString(speedBackend.scStwClient.state), "progress": 100 }
|
||||||
connect: speedBackend.connectBaseStation
|
connect: speedBackend.scStwClient.connectToHost
|
||||||
disconnect: speedBackend.disconnectBaseStation
|
disconnect: speedBackend.scStwClient.closeConnection
|
||||||
type: "baseStation"
|
type: "baseStation"
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height * 0.05
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 2
|
||||||
|
color: appTheme.theme.colors.line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackView {
|
||||||
id: baseStationOptionsLd
|
id: baseStationOptionsLd
|
||||||
|
|
||||||
property alias parentComp: control
|
width: parent.width
|
||||||
property alias baseConnected: control.baseConnected
|
height: parent.height
|
||||||
|
|
||||||
onBaseConnectedChanged: {
|
property alias parentComp: control
|
||||||
disappearAnim.start()
|
property Component sourceComponent: control.baseConnected ? baseStationConnectedOptionsComp : baseStationDisconnectedOptionsComp
|
||||||
|
|
||||||
|
onSourceComponentChanged: {
|
||||||
|
baseStationOptionsLd.replace(sourceComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
baseStationOptionsLd.sourceComponent = control.baseConnected ? baseStationConnectedOptionsComp : baseStationDisconnectedOptionsComp
|
baseStationOptionsLd.replace(sourceComponent)
|
||||||
item.opacity = 1
|
|
||||||
item.scale = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceComponent: null
|
replaceExit: Transition {
|
||||||
|
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
id: disappearAnim
|
id: disappearAnim
|
||||||
|
|
||||||
|
@ -74,24 +93,17 @@ Column {
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
to: 0
|
to: 0
|
||||||
duration: 100
|
duration: 100
|
||||||
target: baseStationOptionsLd.item
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
property: "scale"
|
property: "scale"
|
||||||
to: 0.95
|
to: 0.95
|
||||||
duration: 100
|
duration: 100
|
||||||
target: baseStationOptionsLd.item
|
|
||||||
}
|
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if(!running) {
|
|
||||||
baseStationOptionsLd.sourceComponent = control.baseConnected ? baseStationConnectedOptionsComp : baseStationDisconnectedOptionsComp
|
|
||||||
appearAnim.start()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replaceEnter: Transition {
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
id: appearAnim
|
id: appearAnim
|
||||||
|
|
||||||
|
@ -100,7 +112,6 @@ Column {
|
||||||
from: 0
|
from: 0
|
||||||
to: 1
|
to: 1
|
||||||
duration: 100
|
duration: 100
|
||||||
target: baseStationOptionsLd.item
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
@ -108,10 +119,9 @@ Column {
|
||||||
from: 0.95
|
from: 0.95
|
||||||
to: 1
|
to: 1
|
||||||
duration: 100
|
duration: 100
|
||||||
target: baseStationOptionsLd.item
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -120,8 +130,6 @@ Column {
|
||||||
Column {
|
Column {
|
||||||
id: baseStationDisconnectedOptions
|
id: baseStationDisconnectedOptions
|
||||||
|
|
||||||
width: parentComp.width
|
|
||||||
|
|
||||||
opacity: 0 // opacity and scale are adjusted by baseStationOptionsLd
|
opacity: 0 // opacity and scale are adjusted by baseStationOptionsLd
|
||||||
scale: 0.95
|
scale: 0.95
|
||||||
|
|
||||||
|
@ -131,12 +139,11 @@ Column {
|
||||||
text: qsTr("IP")
|
text: qsTr("IP")
|
||||||
|
|
||||||
inputHint: "IP"
|
inputHint: "IP"
|
||||||
inputText: speedBackend.readSetting("baseStationIpAdress")
|
inputText: scStwAppSettings.readSetting(ScStwAppSettings.BaseStationIpSetting)
|
||||||
inputTextFieldWidth: width * 0.7
|
inputTextFieldWidth: width * 0.7
|
||||||
|
|
||||||
onInputTextChanged: {
|
onInputTextChanged: {
|
||||||
speedBackend.writeSetting("baseStationIpAdress", inputText)
|
scStwAppSettings.writeSetting(ScStwAppSettings.BaseStationIpSetting, inputText)
|
||||||
speedBackend.reloadBaseStationIpAdress()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -172,258 +179,77 @@ Column {
|
||||||
Component {
|
Component {
|
||||||
id: baseStationConnectedOptionsComp
|
id: baseStationConnectedOptionsComp
|
||||||
|
|
||||||
ScrollView{
|
|
||||||
id: flickable
|
|
||||||
contentHeight: baseStationConnectedOptions.childrenRect.height
|
|
||||||
contentWidth: -1
|
|
||||||
width: parentComp.width
|
|
||||||
height: control.height - baseStationOptionsLd.y
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
opacity: 0 // opacity and scale are adjusted by baseStationOptionsLd
|
|
||||||
scale: 0.95
|
|
||||||
|
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
parent: flickable.parent
|
|
||||||
anchors.top: flickable.top
|
|
||||||
anchors.left: flickable.right
|
|
||||||
anchors.bottom: flickable.bottom
|
|
||||||
policy: ScrollBar.AlwaysOn
|
|
||||||
interactive: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: baseStationConnectedOptions
|
id: baseStationConnectedOptions
|
||||||
|
|
||||||
width: parentComp.width
|
width: parentComp.width
|
||||||
|
|
||||||
SmoothSliderDelegate {
|
Label {
|
||||||
id: baseStationVolumeDel
|
id: headerLabel
|
||||||
text: qsTr("volume")
|
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
|
|
||||||
sliderValue: 0
|
visible: true
|
||||||
|
|
||||||
onSliderFinished: {
|
|
||||||
enabled = false
|
|
||||||
speedBackend.writeSetting("soundVolume", sliderValue)
|
|
||||||
enabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
var val = speedBackend.readSetting("soundVolume")
|
|
||||||
if(val !== "false"){
|
|
||||||
sliderValue = parseFloat(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NextPageDelegate {
|
|
||||||
id: baseStationConnectionsDel
|
|
||||||
text: qsTr("connected extensions")
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: parentObj.delegateHeight
|
|
||||||
|
|
||||||
visible: height > 5
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
parentObj.push(baseStationConnections)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SmoothItemDelegate {
|
|
||||||
id: connectUsbExtensionsDel
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: parentObj.delegateHeight
|
|
||||||
|
|
||||||
text: "pair extensions"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
busyDl.open()
|
|
||||||
var ret = speedBackend.pairConnectedUsbExtensions()
|
|
||||||
busyDl.displayMessageAndClose(ret ? "OK":"error", ret ? "#6bd43b":"#e03b2f" )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SmoothItemDelegate {
|
|
||||||
id: baseStationUpdateDel
|
|
||||||
|
|
||||||
// 0: hidden 1: update firmware 2: sync time
|
|
||||||
property int mode: speedBackend.baseStationProperties["firmware"]["upToDate"] ?
|
|
||||||
(Math.abs(parseInt(speedBackend.baseStationProperties["timeOffset"])) > 10000 ? 2:0)
|
|
||||||
:1
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: mode > 0 ? parentObj.delegateHeight:0
|
|
||||||
|
|
||||||
visible: height > 5
|
|
||||||
|
|
||||||
text: mode === 2 ? qsTr("sync time"):qsTr("update firmware")
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if(mode === 1) {
|
|
||||||
control.doFirmwareUpdate()
|
|
||||||
}
|
|
||||||
else if(mode === 2){
|
|
||||||
busyDl.open()
|
|
||||||
var ret = speedBackend.updateBasestationTime()
|
|
||||||
busyDl.displayMessageAndClose(ret ? "OK":"error", ret ? "#6bd43b":"#e03b2f" )
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.left: parent.left
|
|
||||||
width: parent.width
|
|
||||||
height: 1
|
|
||||||
color: appTheme.style.lineColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.left: parent.left
|
|
||||||
width: parent.width
|
|
||||||
height: parentObj.delegateHeight * 0.5
|
|
||||||
|
|
||||||
|
|
||||||
Label {
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
left: parent.left
|
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width * 0.5
|
|
||||||
height: parent.height * 0.5
|
|
||||||
|
|
||||||
|
|
||||||
verticalAlignment: Text.AlignTop
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.pixelSize: height
|
|
||||||
|
|
||||||
minimumPixelSize: 1
|
|
||||||
|
|
||||||
color: appTheme.style.lineColor
|
|
||||||
text: "version: " + speedBackend.baseStationProperties["firmware"]["version"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
|
|
||||||
property var date: new Date(new Date().getTime() + parseInt(speedBackend.baseStationProperties["timeOffset"]))
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width * 0.5
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
verticalAlignment: Text.AlignTop
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
|
|
||||||
minimumPixelSize: 1
|
|
||||||
font.pixelSize: height
|
|
||||||
|
|
||||||
color: appTheme.style.lineColor
|
|
||||||
text: date.toLocaleDateString() + "\n" + date.toLocaleTimeString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Popup {
|
|
||||||
id: busyDl
|
|
||||||
|
|
||||||
property string message: ""
|
|
||||||
property color messageColor: "transparent"
|
|
||||||
|
|
||||||
x: Math.round((parent.width - width) / 2)
|
|
||||||
y: Math.round((parent.height - height) / 2)
|
|
||||||
|
|
||||||
width: app.width
|
|
||||||
height: app.height
|
|
||||||
|
|
||||||
modal: true
|
|
||||||
dim: true
|
|
||||||
|
|
||||||
closePolicy: Dialog.NoAutoClose
|
|
||||||
|
|
||||||
function displayMessageAndClose(message, messageColor) {
|
|
||||||
busyDl.message = message
|
|
||||||
busyDl.messageColor = messageColor
|
|
||||||
|
|
||||||
closeDelayTimer.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: closeDelayTimer
|
|
||||||
|
|
||||||
interval: 1000
|
|
||||||
repeat: false
|
|
||||||
running: false
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
busyDl.close()
|
|
||||||
busyDl.message = ""
|
|
||||||
busyDl.messageColor = "transparent"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
FancyBusyIndicator {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
opacity: busyDl.message === "" ? 1:0
|
|
||||||
|
|
||||||
lineColor: "white"
|
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation { duration: 150 } }
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
width: parent.width * 0.5
|
|
||||||
height: parent.height * 0.5
|
|
||||||
|
|
||||||
opacity: busyDl.message === "" ? 0:1
|
|
||||||
|
|
||||||
color: busyDl.messageColor
|
|
||||||
text: busyDl.message
|
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
|
||||||
font.pixelSize: height
|
|
||||||
minimumPixelSize: 1
|
|
||||||
|
|
||||||
font.bold: true
|
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation { duration: 150 } }
|
fontSizeMode: Text.Fit
|
||||||
|
font.pixelSize: height
|
||||||
|
|
||||||
|
minimumPixelSize: 1
|
||||||
|
|
||||||
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
|
text: "ScStwBaseStation"
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: firmwareVersionLabel
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parentObj.delegateHeight * 0.3
|
||||||
|
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
font.pixelSize: height
|
||||||
|
|
||||||
|
minimumPixelSize: 1
|
||||||
|
|
||||||
|
color: appTheme.theme.colors.line
|
||||||
|
|
||||||
|
text: "Firmware: " + speedBackend.scStwClient.getFirmwareVersion() + " API: " + speedBackend.scStwClient.getApiVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
NextPageDelegate {
|
||||||
|
text: qsTr("settings")
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parentObj.delegateHeight * 0.8
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.5
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
parentObj.push(baseStationSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T.Overlay.modal: Rectangle {
|
NextPageDelegate {
|
||||||
id: modalRect
|
text: qsTr("extensions")
|
||||||
color: "#80404040"
|
|
||||||
Behavior on opacity { NumberAnimation { duration: 150 } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parentObj.delegateHeight * 0.8
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.5
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
parentObj.push(extensions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtMultimedia 5.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: baseStationSettings
|
||||||
|
|
||||||
|
property var parentObj
|
||||||
|
property string title: "Bs settings"
|
||||||
|
|
||||||
|
width: parentObj.width
|
||||||
|
|
||||||
|
SmoothSliderDelegate {
|
||||||
|
id: baseStationVolumeDel
|
||||||
|
|
||||||
|
property ScStwSetting setting: scStwAppSettings.getSetting(ScStwSettings.SoundVolumeSetting, ScStwSettings.KeyLevel)
|
||||||
|
|
||||||
|
text: qsTr("volume")
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parentObj.delegateHeight
|
||||||
|
|
||||||
|
font.pixelSize: height * 0.5
|
||||||
|
|
||||||
|
sliderValue: setting.readonlyValue
|
||||||
|
|
||||||
|
onSliderFinished: {
|
||||||
|
enabled = false
|
||||||
|
setting.value = sliderValue
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SmoothSwitchDelegate {
|
||||||
|
property ScStwSetting setting: scStwAppSettings.getSetting(ScStwSettings.CompetitionModeSetting, ScStwSettings.KeyLevel)
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parentObj.delegateHeight
|
||||||
|
|
||||||
|
checked: setting.readonlyValue
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
enabled = false
|
||||||
|
setting.value = checked
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
text: qsTr("competition mode")
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,8 +42,8 @@ Popup {
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
color: appTheme.style.viewColor
|
color: appTheme.theme.colors.view
|
||||||
border.color: appTheme.style.lineColor
|
border.color: appTheme.theme.colors.line
|
||||||
border.width: 0
|
border.width: 0
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
|
@ -76,7 +76,7 @@ Popup {
|
||||||
height: header.height
|
height: header.height
|
||||||
width: header.width
|
width: header.width
|
||||||
|
|
||||||
property color color: appTheme.style.viewColor
|
property color color: appTheme.theme.colors.view
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
|
@ -130,7 +130,7 @@ Popup {
|
||||||
|
|
||||||
text: options_stack.currentItem.title
|
text: options_stack.currentItem.title
|
||||||
font.pixelSize: headlineUnderline.width * 0.1
|
font.pixelSize: headlineUnderline.width * 0.1
|
||||||
color: enabled ? appTheme.style.textColor:appTheme.style.disabledTextColor
|
color: enabled ? appTheme.theme.colors.text:appTheme.theme.colors.disabledText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ Popup {
|
||||||
id: headlineUnderline
|
id: headlineUnderline
|
||||||
height: 1
|
height: 1
|
||||||
width: parent.width
|
width: parent.width
|
||||||
color: appTheme.style.lineColor
|
color: appTheme.theme.colors.line
|
||||||
visible: false
|
visible: false
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
|
@ -166,7 +166,7 @@ Popup {
|
||||||
|
|
||||||
glowOpacity: Math.pow( root.opacity, 100 )
|
glowOpacity: Math.pow( root.opacity, 100 )
|
||||||
|
|
||||||
image: appTheme.style.backIcon
|
image: appTheme.theme.images.backIcon
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
options_stack.depth > 1 ? options_stack.pop():root.close()
|
options_stack.depth > 1 ? options_stack.pop():root.close()
|
||||||
|
|
|
@ -47,14 +47,22 @@ StackView {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----Page to view devices that core connected to the pase startion-----*/
|
|
||||||
Component {
|
Component {
|
||||||
id: baseStationConnections
|
id: extensions
|
||||||
|
|
||||||
SettingsBaseStationConnectionsPage {
|
ExtensionOverview {
|
||||||
parentObj: control
|
property string title: qsTr("extensions")
|
||||||
|
delegateHeight: control.delegateHeight
|
||||||
|
backgroundColor: appTheme.theme.colors.view
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: baseStationSettings
|
||||||
|
|
||||||
|
SettingsBaseStationSettingsPage {
|
||||||
|
parentObj: control
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----Custom animations-----*/
|
/*-----Custom animations-----*/
|
||||||
|
|
|
@ -5,6 +5,12 @@ import QtQuick.Controls 2.2
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -17,37 +23,38 @@ Column {
|
||||||
|
|
||||||
function updateSetting(key, val, del){
|
function updateSetting(key, val, del){
|
||||||
del.busy = true
|
del.busy = true
|
||||||
speedBackend.writeSetting(key, val)
|
scStwAppSettings.writeSetting(key, val, ScStwSettings.KeyLevel)
|
||||||
del.busy = false
|
del.busy = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSetting(key, del){
|
function loadSetting(key, del){
|
||||||
return speedBackend.readSetting(key)
|
return scStwAppSettings.readSetting(key, ScStwSettings.KeyLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
SmoothSwitchDelegate {
|
SmoothSwitchDelegate {
|
||||||
id: ready_del
|
id: ready_del
|
||||||
|
|
||||||
property bool busy: false
|
property ScStwSetting setting: scStwAppSettings.getSetting(ScStwSettings.ReadySoundEnableSetting, ScStwSettings.KeyLevel)
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
|
|
||||||
enabled: !busy
|
checked: setting.value
|
||||||
|
|
||||||
text: qsTr("say 'ready'")
|
|
||||||
|
|
||||||
checked: parent.loadSetting("ready_en", ready_del) === "true"
|
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
parent.updateSetting("ready_en", checked, ready_del)
|
enabled = false
|
||||||
|
setting.value = checked
|
||||||
|
enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
text: qsTr("say 'ready'")
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDelegate {
|
InputDelegate {
|
||||||
id: ready_delay_del
|
id: ready_delay_del
|
||||||
|
|
||||||
property bool busy: false
|
property bool busy: false
|
||||||
|
property ScStwSetting setting: scStwAppSettings.getSetting(ScStwSettings.ReadySoundDelaySetting, ScStwSettings.KeyLevel)
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
|
@ -58,29 +65,32 @@ Column {
|
||||||
inputHint: qsTr("time")
|
inputHint: qsTr("time")
|
||||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||||
|
|
||||||
inputText: control.loadSetting("ready_delay", ready_delay_del)
|
inputText: setting.value
|
||||||
|
|
||||||
onInputFinished: {
|
onInputFinished: {
|
||||||
control.updateSetting("ready_delay", inputText, ready_delay_del)
|
console.log("input finished")
|
||||||
|
busy = true
|
||||||
|
setting.value = inputText
|
||||||
|
busy = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SmoothSwitchDelegate {
|
SmoothSwitchDelegate {
|
||||||
id: at_marks_del
|
id: at_marks_del
|
||||||
|
|
||||||
property bool busy: false
|
property ScStwSetting setting: scStwAppSettings.getSetting(ScStwSettings.AtYourMarksSoundEnableSetting, ScStwSettings.KeyLevel)
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
|
|
||||||
enabled: !busy
|
|
||||||
|
|
||||||
text: qsTr("say 'at your marks'")
|
text: qsTr("say 'at your marks'")
|
||||||
|
|
||||||
checked: control.loadSetting("at_marks_en", ready_del) === "true"
|
checked: setting.value
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
parent.updateSetting("at_marks_en",at_marks_del.checked, at_marks_del)
|
enabled = false
|
||||||
|
setting.value = checked
|
||||||
|
enabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +98,7 @@ Column {
|
||||||
id: at_marks_delay_del
|
id: at_marks_delay_del
|
||||||
|
|
||||||
property bool busy: false
|
property bool busy: false
|
||||||
|
property ScStwSetting setting: scStwAppSettings.getSetting(ScStwSettings.AtYourMarksSoundDelaySetting, ScStwSettings.KeyLevel)
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
|
@ -98,10 +109,12 @@ Column {
|
||||||
|
|
||||||
enabled: !busy && at_marks_del.checked
|
enabled: !busy && at_marks_del.checked
|
||||||
|
|
||||||
inputText: control.loadSetting("at_marks_delay", at_marks_delay_del)
|
inputText: setting.value
|
||||||
|
|
||||||
onInputFinished: {
|
onInputFinished: {
|
||||||
control.updateSetting("at_marks_delay", inputText, at_marks_delay_del)
|
busy = true
|
||||||
|
setting.setValue(inputText)
|
||||||
|
busy = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,12 @@ import QtQuick.Controls 2.2
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
import de.itsblue.ScStwApp 2.0
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -49,11 +55,10 @@ Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parentObj.delegateHeight
|
height: parentObj.delegateHeight
|
||||||
|
|
||||||
checked: speedBackend.readSetting("theme") === "Dark"
|
checked: appTheme.setting.value === "Dark"
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
speedBackend.writeSetting("theme", checked ? "Dark":"Light")
|
appTheme.setting.setValue(checked ? "Dark":"Light")
|
||||||
appTheme.refreshTheme()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
250
resources/qml/components/BatteryIndicator.qml
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property color chargingColor: "green"
|
||||||
|
property color fineColor: "green"
|
||||||
|
property color warningColor: "orange"
|
||||||
|
property color criticalColor: "red"
|
||||||
|
property color unknownColor: "grey"
|
||||||
|
property color notChargingColor: "red"
|
||||||
|
property color backgroundColor: "white"
|
||||||
|
property double indicatorSize: 1
|
||||||
|
|
||||||
|
height: app.height * 0.9
|
||||||
|
width: height * 0.8
|
||||||
|
|
||||||
|
color: backgroundColor
|
||||||
|
|
||||||
|
border.width: Math.min(width * 0.1, height * 0.1) * control.indicatorSize
|
||||||
|
border.color: "green"
|
||||||
|
radius: border.width
|
||||||
|
|
||||||
|
state: "charging"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: outerRect
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
right: parent.left
|
||||||
|
rightMargin: parent.border.width * 0.25
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
width: parent.border.width
|
||||||
|
height: parent.height * 0.5
|
||||||
|
radius: width * 0.5
|
||||||
|
|
||||||
|
color: parent.border.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: innerRect
|
||||||
|
|
||||||
|
property double amountFilled: 1
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
|
||||||
|
margins: parent.border.width * 1.5
|
||||||
|
}
|
||||||
|
|
||||||
|
width: (parent.width - parent.border.width * 3) * amountFilled
|
||||||
|
|
||||||
|
radius: control.radius * 0.5
|
||||||
|
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
color: control.border.color
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: chargingRect
|
||||||
|
|
||||||
|
property double amountFilled: 0
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
width: parent.width * amountFilled
|
||||||
|
|
||||||
|
radius: innerRect.radius
|
||||||
|
|
||||||
|
color: Qt.darker(innerRect.color, 1.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: questionMarkText
|
||||||
|
visible: false
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
height: parent.height * 0.7
|
||||||
|
width: parent.width * 0.7
|
||||||
|
|
||||||
|
color: outerRect.color
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: height
|
||||||
|
|
||||||
|
text: "?"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: chargingAnimation
|
||||||
|
|
||||||
|
running: false
|
||||||
|
|
||||||
|
loops: Animation.Infinite
|
||||||
|
alwaysRunToEnd: true
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: chargingRect
|
||||||
|
|
||||||
|
properties: "amountFilled"
|
||||||
|
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
|
||||||
|
duration: 3000
|
||||||
|
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: chargingRect
|
||||||
|
|
||||||
|
properties: "opacity"
|
||||||
|
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
|
||||||
|
duration: 3000
|
||||||
|
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "charging"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.chargingColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: innerRect
|
||||||
|
amountFilled: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: chargingAnimation
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "notCharging"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.notChargingColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: innerRect
|
||||||
|
amountFilled: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: questionMarkText
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "fine"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.fineColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: innerRect
|
||||||
|
amountFilled: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "warning"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.warningColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: innerRect
|
||||||
|
amountFilled: 0.3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "critical"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.criticalColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: innerRect
|
||||||
|
amountFilled: 0.1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "unknown"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.unknownColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: innerRect
|
||||||
|
amountFilled: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: questionMarkText
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "amountFilled"
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ColorAnimation {
|
||||||
|
properties: "color,border.color"
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
|
@ -6,27 +6,31 @@ SmoothItemDelegate {
|
||||||
|
|
||||||
property var status
|
property var status
|
||||||
property string oldState: ""
|
property string oldState: ""
|
||||||
property var connect
|
property var connect: null
|
||||||
property var disconnect
|
property var disconnect
|
||||||
|
property string batteryState: "unknown"
|
||||||
|
|
||||||
property string type
|
property string type
|
||||||
|
|
||||||
text: qsTr(type)
|
text: qsTr(type)
|
||||||
|
|
||||||
enabled: (status.status === "disconnected" && control.connect !== undefined) || ( status.status === "connected" && control.disconnect !== undefined )
|
enabled: (status.status === "disconnected" && control.connect !== undefined) || ( status.status === "connected" && control.disconnect !== undefined )
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if(status.status === "disconnected"){
|
if(connect == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(status.status === "disconnected")
|
||||||
connect()
|
connect()
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
disconnect()
|
disconnect()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if(oldState !== status.status) {
|
if(oldState !== status.status) {
|
||||||
if(status.status === "disconnected" && oldState === "connecting") {
|
if(status.status === "disconnected" && oldState === "connecting") {
|
||||||
statusIndicator.color_override = appTheme.style.errorColor
|
statusIndicator.color_override = "error"
|
||||||
shortDelay.start()
|
shortDelay.start()
|
||||||
}
|
}
|
||||||
oldState = status.status
|
oldState = status.status
|
||||||
|
@ -43,57 +47,59 @@ SmoothItemDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Row {
|
||||||
id: statusItem
|
|
||||||
anchors {
|
anchors {
|
||||||
right: parent.right
|
right: parent.right
|
||||||
rightMargin: ( height / control.height / 2 ) * height
|
rightMargin: ( height / control.height / 2 ) * height
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spacing: height * 0.4
|
||||||
|
|
||||||
|
BatteryIndicator {
|
||||||
|
id: batteryIndicator
|
||||||
|
|
||||||
height: control.height * 0.4
|
height: control.height * 0.4
|
||||||
width: height
|
width: height
|
||||||
|
|
||||||
Rectangle {
|
opacity: control.batteryState === "unknown" ? 0:1
|
||||||
|
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
chargingColor: fineColor
|
||||||
|
fineColor: appTheme.theme.colors.success
|
||||||
|
warningColor: appTheme.theme.colors.warning
|
||||||
|
criticalColor: appTheme.theme.colors.error
|
||||||
|
notChargingColor: warningColor
|
||||||
|
|
||||||
|
state: control.batteryState
|
||||||
|
|
||||||
|
indicatorSize: 0.8
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateIndicator {
|
||||||
id: statusIndicator
|
id: statusIndicator
|
||||||
property string color_override: ""
|
property string color_override: ""
|
||||||
anchors.fill: parent
|
|
||||||
color: color_override === "" ? status.status === "connected" ? appTheme.style.successColor:"transparent":color_override
|
|
||||||
opacity: status.status === "connecting" ? 0:1
|
|
||||||
radius: height * 0.5
|
|
||||||
border.color: appTheme.style.lineColor
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Behavior on color {
|
height: control.height * 0.4
|
||||||
ColorAnimation {
|
width: height
|
||||||
duration: 200
|
|
||||||
|
state: color_override === "" ? status.status === "connected" ? "success": status.status === "connecting" ? "working":"unknown":color_override
|
||||||
|
|
||||||
|
indicatorSize: 0.8
|
||||||
|
|
||||||
|
radius: border.width * 2
|
||||||
|
|
||||||
|
backgroundColor: appTheme.theme.colors.background
|
||||||
|
successColor: appTheme.theme.colors.success
|
||||||
|
errorColor: appTheme.theme.colors.error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 800
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressCircle {
|
|
||||||
id: prog
|
|
||||||
anchors.fill: parent
|
|
||||||
opacity: status.status === "connecting" ? 1:0
|
|
||||||
lineWidth: 1
|
|
||||||
|
|
||||||
arcBegin: 0
|
|
||||||
arcEnd: 360 * ( status.progress / 100 )
|
|
||||||
colorCircle: appTheme.style.lineColor
|
|
||||||
onColorCircleChanged: prog.repaint()
|
|
||||||
onArcEndChanged: prog.repaint()
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
110
resources/qml/components/ExtensionOverview.qml
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtMultimedia 5.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import "../components"
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property int delegateHeight: 40
|
||||||
|
property color backgroundColor: "transparent"
|
||||||
|
|
||||||
|
spacing: control.delegateHeight * 0.3
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
model: Object.keys(speedBackend.scStwClient.extensions)
|
||||||
|
|
||||||
|
header: Rectangle {
|
||||||
|
// for top spacing
|
||||||
|
width: parent.width
|
||||||
|
height: control.spacing
|
||||||
|
color: control.backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: laneContainerRect
|
||||||
|
|
||||||
|
property string thisLane: modelData
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
width: parent.width * 0.98
|
||||||
|
height: control.delegateHeight * extensionsList.model
|
||||||
|
|
||||||
|
radius: width * 0.05
|
||||||
|
|
||||||
|
border.width: 1
|
||||||
|
border.color: appTheme.theme.colors.line
|
||||||
|
|
||||||
|
color: control.backgroundColor
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: laneLabelRect
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: parent.radius * 1
|
||||||
|
verticalCenter: parent.top
|
||||||
|
}
|
||||||
|
|
||||||
|
height: control.delegateHeight * 0.5
|
||||||
|
width: laneLabel.font.pixelSize * 4
|
||||||
|
|
||||||
|
color: control.backgroundColor
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: laneLabel
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
leftPadding: laneContainerRect.width * 0.02
|
||||||
|
rightPadding: laneContainerRect.width * 0.02
|
||||||
|
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
font.pixelSize: height * 0.5
|
||||||
|
|
||||||
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
|
text: "Lane " + laneContainerRect.thisLane
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: extensionsList
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: width * 0.04
|
||||||
|
|
||||||
|
interactive: false
|
||||||
|
|
||||||
|
model: speedBackend.scStwClient.extensions[parent.thisLane].length
|
||||||
|
|
||||||
|
delegate: ConnectionDelegate {
|
||||||
|
property var thisExtension: speedBackend.scStwClient.extensions[laneContainerRect.thisLane][index]
|
||||||
|
property var stateTranslations: ["disconnected", "connecting", "connecting", "connected"]
|
||||||
|
property var batteryStateStrings: ["critical", "warning", "fine", "charging", "notCharging"]
|
||||||
|
color: control.backgroundColor
|
||||||
|
|
||||||
|
batteryState: thisExtension["batteryState"] === -1 ? "unknown":batteryStateStrings[thisExtension["batteryState"]]
|
||||||
|
|
||||||
|
height: extensionsList.height / extensionsList.model
|
||||||
|
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
text: thisExtension["type"] === 0 ? "StartPad":"TopPad" // TODO: make dynamic with ScStw::extensionTypeToString()
|
||||||
|
status: {'status': stateTranslations[thisExtension["state"]]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
|
||||||
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, version 3 of the License.
|
|
||||||
|
|
||||||
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 Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.0
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
id: root
|
|
||||||
property QtObject target
|
|
||||||
property string fadeProperty: "scale"
|
|
||||||
property int fadeDuration: 150
|
|
||||||
property int fadeDuration_in: fadeDuration
|
|
||||||
property int fadeDuration_out: fadeDuration
|
|
||||||
property alias outValue: outAnimation.to
|
|
||||||
property alias inValue: inAnimation.to
|
|
||||||
property alias outEasingType: outAnimation.easing.type
|
|
||||||
property alias inEasingType: inAnimation.easing.type
|
|
||||||
property string easingType: "Quad"
|
|
||||||
ParallelAnimation {
|
|
||||||
NumberAnimation { // in the default case, fade scale to 0
|
|
||||||
id: outAnimation
|
|
||||||
target: root.target
|
|
||||||
property: "scale"
|
|
||||||
duration: root.fadeDuration_in
|
|
||||||
to: 0.9
|
|
||||||
easing.type: Easing["In"+root.easingType]
|
|
||||||
}
|
|
||||||
NumberAnimation { // in the default case, fade scale to 0
|
|
||||||
id: outAnimation2
|
|
||||||
target: root.target
|
|
||||||
property: "opacity"
|
|
||||||
duration: root.fadeDuration_in
|
|
||||||
to: 0
|
|
||||||
easing.type: Easing["In"+root.easingType]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PropertyAction { } // actually change the property targeted by the Behavior between the 2 other animations
|
|
||||||
ParallelAnimation {
|
|
||||||
NumberAnimation { // in the default case, fade scale back to 1
|
|
||||||
id: inAnimation
|
|
||||||
target: root.target
|
|
||||||
property: root.fadeProperty
|
|
||||||
duration: root.fadeDuration_out
|
|
||||||
to: 1
|
|
||||||
easing.type: Easing["Out"+root.easingType]
|
|
||||||
}
|
|
||||||
NumberAnimation { // in the default case, fade scale to 0
|
|
||||||
id: inAnimation2
|
|
||||||
target: root.target
|
|
||||||
property: "opacity"
|
|
||||||
duration: root.fadeDuration_in
|
|
||||||
to: 1
|
|
||||||
easing.type: Easing["In"+root.easingType]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
import QtQuick 2.3
|
|
||||||
import QtQuick.Controls 2.4
|
|
||||||
import QtQuick.Controls.Styles 1.2
|
|
||||||
|
|
||||||
BusyIndicator {
|
|
||||||
id: control
|
|
||||||
|
|
||||||
property double animationSpeed: 1000
|
|
||||||
property double formFactor: 4.5
|
|
||||||
|
|
||||||
property color lineColor: "#21be2b"
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
implicitWidth: 64
|
|
||||||
implicitHeight: 64
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: item
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
property int currentHeight: 0
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
running: control.running
|
|
||||||
loops: Animation.Infinite
|
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
target: item
|
|
||||||
property: "currentHeight"
|
|
||||||
from: 0
|
|
||||||
to: 800
|
|
||||||
duration: control.animationSpeed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
spacing: item.width / 9
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,15 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.2
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property string image
|
property string image
|
||||||
property color backgroundColor: appTheme.style.buttonColor
|
property color backgroundColor: appTheme.theme.colors.button
|
||||||
property real imageScale: 1
|
property real imageScale: 1
|
||||||
property double glowRadius: 0.001
|
property double glowRadius: 0.001
|
||||||
property double glowSpread: 0.2
|
property double glowSpread: 0.2
|
||||||
|
@ -14,16 +17,16 @@ Button {
|
||||||
property double glowScale: 0.75
|
property double glowScale: 0.75
|
||||||
property double glowOpacity: Math.pow( control.opacity, 100 )
|
property double glowOpacity: Math.pow( control.opacity, 100 )
|
||||||
|
|
||||||
|
Behavior on text {
|
||||||
|
//animate a text change
|
||||||
//scale: control.pressed ? 0.8:1
|
enabled: true
|
||||||
|
FadeAnimation {
|
||||||
Behavior on scale {
|
target: text
|
||||||
PropertyAnimation {
|
|
||||||
duration: 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contentItem: Item {}
|
||||||
|
|
||||||
background: Item {
|
background: Item {
|
||||||
id: controlBackgroundContainer
|
id: controlBackgroundContainer
|
||||||
|
|
||||||
|
@ -56,6 +59,26 @@ Button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: text
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.verticalCenterOffset: -height * 0.05
|
||||||
|
height: parent.height * 0.6
|
||||||
|
width: parent.width * 0.6
|
||||||
|
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
font.pixelSize: Math.max(parent.height * 0.16, parent.width * 0.16)
|
||||||
|
font.family: "Helvetica"
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
text: control.text
|
||||||
|
|
||||||
|
color: enabled ? appTheme.theme.colors.text:appTheme.theme.colors.disabledText
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: buttonIcon
|
id: buttonIcon
|
||||||
source: control.image
|
source: control.image
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.2
|
||||||
|
|
||||||
SmoothItemDelegate {
|
SmoothItemDelegate {
|
||||||
id: control
|
id: control
|
||||||
|
@ -37,7 +37,7 @@ SmoothItemDelegate {
|
||||||
|
|
||||||
inputMethodHints: control.inputMethodHints
|
inputMethodHints: control.inputMethodHints
|
||||||
|
|
||||||
palette.text: appTheme.style.textColor
|
palette.text: appTheme.theme.colors.text
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
control.inputText = text
|
control.inputText = text
|
||||||
|
|
129
resources/qml/components/MainActionButton.qml
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 2.9
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
import de.itsblue.ScStw 2.0
|
||||||
|
import de.itsblue.ScStw.Styling 2.0
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
|
||||||
|
DelayButton {
|
||||||
|
id : control
|
||||||
|
|
||||||
|
text: "start"
|
||||||
|
property color backgroundColor: appTheme.theme.colors.button
|
||||||
|
property bool progressControlActivated: false
|
||||||
|
property double startProgress
|
||||||
|
property double oldStartProgress: -1
|
||||||
|
|
||||||
|
delay: progressControlActivated ? 2000:0
|
||||||
|
|
||||||
|
onStartProgressChanged: {
|
||||||
|
if(startProgress > oldStartProgress)
|
||||||
|
oldStartProgress = startProgress
|
||||||
|
else {
|
||||||
|
startProgressAnimation.from = oldStartProgress
|
||||||
|
startProgressAnimation.to = startProgress
|
||||||
|
startProgressAnimation.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: labelText
|
||||||
|
text: control.text
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.pixelSize: parent.height * 0.16
|
||||||
|
font.family: "Helvetica"
|
||||||
|
color: enabled ? appTheme.theme.colors.text:appTheme.theme.colors.disabledText
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: startProgressAnimation
|
||||||
|
|
||||||
|
to: 0
|
||||||
|
target: control
|
||||||
|
properties: "oldStartProgress"
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on text {
|
||||||
|
//animate a text change
|
||||||
|
enabled: true
|
||||||
|
FadeAnimation {
|
||||||
|
target: labelText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
RectangularGlow {
|
||||||
|
glowRadius: 0.001
|
||||||
|
spread: 0.2
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
cornerRadius: background.radius
|
||||||
|
anchors.fill: background
|
||||||
|
scale: 0.75
|
||||||
|
opacity: Math.pow( control.opacity, 100 )
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: background
|
||||||
|
implicitWidth: 100
|
||||||
|
implicitHeight: 100
|
||||||
|
color: control.down ? Qt.darker(control.backgroundColor, 1.2) : control.backgroundColor
|
||||||
|
radius: size / 2
|
||||||
|
|
||||||
|
readonly property real size: Math.min(control.width, control.height)
|
||||||
|
width: size
|
||||||
|
height: size
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: canvas
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: control
|
||||||
|
function onProgressChanged() {
|
||||||
|
canvas.requestPaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOldStartProgressChanged() {
|
||||||
|
canvas.requestPaint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var progress
|
||||||
|
var showHoldProgress = ((control.oldStartProgress <= 0 || control.oldStartProgress === 1) && control.progressControlActivated)
|
||||||
|
|
||||||
|
if(showHoldProgress)
|
||||||
|
progress = control.progress
|
||||||
|
else
|
||||||
|
progress = control.oldStartProgress < 0 ? 0:1-control.oldStartProgress
|
||||||
|
|
||||||
|
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
ctx.strokeStyle = showHoldProgress ? appTheme.theme.colors.error:"grey"
|
||||||
|
ctx.lineWidth = parent.width * 0.02
|
||||||
|
ctx.beginPath()
|
||||||
|
var startAngle = Math.PI * 1.5
|
||||||
|
var endAngle = startAngle + progress * Math.PI * 2
|
||||||
|
ctx.arc(width / 2, height / 2, width / 2 - ctx.lineWidth / 2 - 2, startAngle, endAngle)
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ SmoothItemDelegate {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: forwardImage
|
id: forwardImage
|
||||||
source: appTheme.style.backIcon
|
source: appTheme.theme.images.backIcon
|
||||||
rotation: 180
|
rotation: 180
|
||||||
height: control.height * 0.4
|
height: control.height * 0.4
|
||||||
width: height
|
width: height
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
|
|
||||||
|
import de.itsblue.ScStw.Styling.Components 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
|
@ -73,8 +75,12 @@ Item {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FancyBusyIndicator {
|
BusyIndicator {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: app.landscape() ? parent.height * 0.1:parent.width * 0.1
|
||||||
|
height: width
|
||||||
|
|
||||||
opacity: !(status === 200 || status === 902) ? 1:0
|
opacity: !(status === 200 || status === 902) ? 1:0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
|
|
@ -4,8 +4,9 @@ import QtQuick.Controls 2.2
|
||||||
ItemDelegate {
|
ItemDelegate {
|
||||||
id: control
|
id: control
|
||||||
text: ""
|
text: ""
|
||||||
property color textColor: appTheme.style.textColor
|
property color textColor: appTheme.theme.colors.text
|
||||||
property alias backgroundRect: backgroundRect
|
property alias backgroundRect: backgroundRect
|
||||||
|
property color color: appTheme.theme.colors.delegateBackground
|
||||||
|
|
||||||
font.pixelSize: height * 0.4
|
font.pixelSize: height * 0.4
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ ItemDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
text: control.text
|
text: control.text
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
@ -42,7 +43,7 @@ ItemDelegate {
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: backgroundRect
|
id: backgroundRect
|
||||||
color: control.down ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
color: control.down ? appTheme.theme.colors.delegatePressed : appTheme.theme.colors.delegateBackground
|
||||||
|
|
||||||
radius: height * 0.3
|
radius: height * 0.3
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.2
|
||||||
|
|
||||||
SwitchDelegate {
|
SwitchDelegate {
|
||||||
id: control
|
id: control
|
||||||
|
@ -24,7 +24,7 @@ SwitchDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
text: control.text
|
text: control.text
|
||||||
color: appTheme.style.textColor
|
color: appTheme.theme.colors.text
|
||||||
|
|
||||||
fontSizeMode: Text.Fit
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ SwitchDelegate {
|
||||||
width: parent.height
|
width: parent.height
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: height * 0.5
|
radius: height * 0.5
|
||||||
color: parent.down ? appTheme.style.buttonPressedColor:appTheme.style.backgroundColor
|
color: parent.down ? appTheme.theme.colors.buttonPressed:appTheme.theme.colors.background
|
||||||
border.color: parent.checked ? (parent.down ? "#17a81a" : "#21be2b") : "#999999"
|
border.color: parent.checked ? (parent.down ? "#17a81a" : "#21be2b") : "#999999"
|
||||||
Behavior on x{
|
Behavior on x{
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
@ -74,7 +74,7 @@ SwitchDelegate {
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
opacity: enabled ? 1 : 0.3
|
opacity: enabled ? 1 : 0.3
|
||||||
color: control.down ? appTheme.style.delegatePressedColor : appTheme.style.delegateBackgroundColor
|
color: control.down ? appTheme.theme.colors.delegatePressed : appTheme.theme.colors.delegateBackground
|
||||||
|
|
||||||
radius: height * 0.3
|
radius: height * 0.3
|
||||||
|
|
||||||
|
|
316
resources/qml/components/StateIndicator.qml
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property color successColor: "green"
|
||||||
|
property color warningColor: "orange"
|
||||||
|
property color errorColor: "red"
|
||||||
|
property color unknownColor: "grey"
|
||||||
|
property color backgroundColor: "white"
|
||||||
|
property color workingColor: "grey"
|
||||||
|
property double indicatorSize: 1
|
||||||
|
|
||||||
|
height: app.height * 0.9
|
||||||
|
width: height * 0.8
|
||||||
|
|
||||||
|
color: backgroundColor
|
||||||
|
|
||||||
|
border.width: Math.min(width * 0.1, height * 0.1) * control.indicatorSize
|
||||||
|
border.color: "green"
|
||||||
|
radius: border.width * 0.5
|
||||||
|
|
||||||
|
state: "success"
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: higherRect
|
||||||
|
|
||||||
|
property int totalWidth: Math.abs(Math.sin(rotation * Math.PI / 180) * (height))
|
||||||
|
property int totalHeight: Math.abs(Math.cos(rotation * Math.PI / 180) * (height))
|
||||||
|
|
||||||
|
radius: width * 0.5
|
||||||
|
border.width: 0
|
||||||
|
border.color: control.workingColor
|
||||||
|
|
||||||
|
color: "green"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: lowerRect
|
||||||
|
|
||||||
|
property int totalWidth: Math.abs(Math.sin(rotation * Math.PI / 180) * (height))
|
||||||
|
property int totalHeight: Math.abs(Math.cos(rotation * Math.PI / 180) * (height))
|
||||||
|
|
||||||
|
radius: width * 0.5
|
||||||
|
border.width: 0
|
||||||
|
border.color: control.workingColor
|
||||||
|
|
||||||
|
color: "green"
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
|
id: workingAnimation
|
||||||
|
|
||||||
|
property int from: lowerRect.height
|
||||||
|
property int to: Math.max(control.width * 1.1, control.height * 1.1)
|
||||||
|
|
||||||
|
property bool shouldBeRunning: false
|
||||||
|
|
||||||
|
running: false
|
||||||
|
|
||||||
|
loops: Animation.Infinite
|
||||||
|
|
||||||
|
onShouldBeRunningChanged: {
|
||||||
|
pauseAnimation.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: higherRect
|
||||||
|
|
||||||
|
properties: "height"
|
||||||
|
|
||||||
|
from: workingAnimation.from
|
||||||
|
to: workingAnimation.to
|
||||||
|
|
||||||
|
duration: 1000
|
||||||
|
|
||||||
|
easing.type: Easing.InQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: higherRect
|
||||||
|
|
||||||
|
properties: "opacity"
|
||||||
|
|
||||||
|
from: 1
|
||||||
|
to: 0.3
|
||||||
|
|
||||||
|
duration: 1000
|
||||||
|
|
||||||
|
easing.type: Easing.InQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PauseAnimation {
|
||||||
|
id: pauseAnimation
|
||||||
|
duration: 400
|
||||||
|
onStopped: {
|
||||||
|
workingAnimation.running = workingAnimation.shouldBeRunning
|
||||||
|
higherRect.opacity = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "working"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.workingColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: higherRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
width: height
|
||||||
|
height: Math.min(parent.width * 0.1, parent.height * 0.1) * control.indicatorSize
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
border.color: control.workingColor
|
||||||
|
border.width: control.border.width
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: lowerRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
width: height
|
||||||
|
height: Math.min(parent.width * 0.2, parent.height * 0.2) * control.indicatorSize
|
||||||
|
|
||||||
|
color: control.workingColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: workingAnimation
|
||||||
|
|
||||||
|
shouldBeRunning: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "unknown"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.unknownColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: higherRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
width: control.border.width
|
||||||
|
height: Math.min(parent.width * 0.7, parent.height * 0.7) * control.indicatorSize
|
||||||
|
|
||||||
|
color: control.unknownColor
|
||||||
|
|
||||||
|
rotation: 90
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: lowerRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
width: control.border.width
|
||||||
|
height: Math.min(parent.width * 0.7, parent.height * 0.7) * control.indicatorSize
|
||||||
|
|
||||||
|
color: control.unknownColor
|
||||||
|
|
||||||
|
rotation: -90
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
id: errorState
|
||||||
|
name: "error"
|
||||||
|
|
||||||
|
property int distance: control.height * 0.1
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.errorColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: higherRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: control.border.width + parent.height * 0.15
|
||||||
|
|
||||||
|
width: control.border.width
|
||||||
|
height: parent.height * 0.4 * control.indicatorSize
|
||||||
|
|
||||||
|
color: control.errorColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: lowerRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: higherRect.y + higherRect.height + errorState.distance
|
||||||
|
|
||||||
|
width: control.border.width * 1.3
|
||||||
|
height: width
|
||||||
|
|
||||||
|
color: control.errorColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
name: "warn"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.warningColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: higherRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
width: control.border.width
|
||||||
|
height: Math.min(parent.width * 0.7, parent.height * 0.7) * control.indicatorSize
|
||||||
|
|
||||||
|
color: control.warningColor
|
||||||
|
|
||||||
|
rotation: 45
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: lowerRect
|
||||||
|
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
width: control.border.width
|
||||||
|
height: Math.min(parent.width * 0.7, parent.height * 0.7) * control.indicatorSize
|
||||||
|
|
||||||
|
color: control.warningColor
|
||||||
|
|
||||||
|
rotation: -45
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
State {
|
||||||
|
id: tickState
|
||||||
|
|
||||||
|
property int bottomX: ((control.width) / 2 - (higherRect.totalWidth - (lowerRect.totalWidth + higherRect.totalWidth) / 2 )) * 1.055
|
||||||
|
property int bottomY: ((control.height + higherRect.totalHeight) / 2 - lowerRect.radius) * 1.05
|
||||||
|
|
||||||
|
name: "success"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
border.color: control.successColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: higherRect
|
||||||
|
|
||||||
|
x: tickState.bottomX - width / 2 + (Math.sin(rotation * Math.PI / 180) * (height / 2 - radius))
|
||||||
|
y: tickState.bottomY - height / 2 - (Math.cos(rotation * Math.PI / 180) * (height / 2 - radius))
|
||||||
|
|
||||||
|
width: control.border.width
|
||||||
|
height: Math.min(parent.width * 0.6, parent.height * 0.6) * control.indicatorSize
|
||||||
|
|
||||||
|
rotation: 40
|
||||||
|
|
||||||
|
color: control.successColor
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: lowerRect
|
||||||
|
|
||||||
|
x: tickState.bottomX - width / 2 + (Math.sin(rotation * Math.PI / 180) * (height / 2 - radius))
|
||||||
|
y: tickState.bottomY - height / 2 - (Math.cos(rotation * Math.PI / 180) *(height / 2 - radius))
|
||||||
|
|
||||||
|
width: control.border.width
|
||||||
|
height: Math.min(parent.width * 0.3, parent.height * 0.3) * control.indicatorSize
|
||||||
|
|
||||||
|
rotation: -40
|
||||||
|
|
||||||
|
color: control.successColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "height,width,rotation,x,y"
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ColorAnimation {
|
||||||
|
properties: "color,border.color"
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
48
resources/qml/components/layout/ToolBar.qml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property double size: sizes[sizeMode]
|
||||||
|
property string sizeMode: "small"
|
||||||
|
property var sizes: {
|
||||||
|
"tiny": 0,
|
||||||
|
"small": 0.15,
|
||||||
|
"medium": 0.25,
|
||||||
|
"large": 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.preferredHeight: app.landscape() ? app.height : app.height * size
|
||||||
|
Layout.preferredWidth: app.landscape() ? app.width * size : app.width
|
||||||
|
|
||||||
|
Behavior on size {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 700
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RectangularGlow {
|
||||||
|
glowRadius: 7
|
||||||
|
spread: 0.02
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.18
|
||||||
|
anchors.fill: headerBackgroundRectangle
|
||||||
|
scale: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: headerBackgroundRectangle
|
||||||
|
anchors.fill: parent
|
||||||
|
color: appTheme.theme.colors.menu
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@
|
||||||
<file>main.qml</file>
|
<file>main.qml</file>
|
||||||
<file>components/ProgressCircle.qml</file>
|
<file>components/ProgressCircle.qml</file>
|
||||||
<file>components/ConnectionDelegate.qml</file>
|
<file>components/ConnectionDelegate.qml</file>
|
||||||
<file>components/FadeAnimation.qml</file>
|
|
||||||
<file>components/ConnectionIcon.qml</file>
|
<file>components/ConnectionIcon.qml</file>
|
||||||
<file>components/NextPageDelegate.qml</file>
|
<file>components/NextPageDelegate.qml</file>
|
||||||
<file>components/FancyButton.qml</file>
|
<file>components/FancyButton.qml</file>
|
||||||
|
@ -12,7 +11,6 @@
|
||||||
<file>components/InputDelegate.qml</file>
|
<file>components/InputDelegate.qml</file>
|
||||||
<file>components/SmoothSliderDelegate.qml</file>
|
<file>components/SmoothSliderDelegate.qml</file>
|
||||||
<file>components/RemoteDataListView.qml</file>
|
<file>components/RemoteDataListView.qml</file>
|
||||||
<file>components/FancyBusyIndicator.qml</file>
|
|
||||||
<file>ProfilesDialog/ProfilesDialog.qml</file>
|
<file>ProfilesDialog/ProfilesDialog.qml</file>
|
||||||
<file>ProfilesDialog/ProfilesStack.qml</file>
|
<file>ProfilesDialog/ProfilesStack.qml</file>
|
||||||
<file>ProfilesDialog/ProfileListPage.qml</file>
|
<file>ProfilesDialog/ProfileListPage.qml</file>
|
||||||
|
@ -23,6 +21,14 @@
|
||||||
<file>SettingsDialog/StartPage.qml</file>
|
<file>SettingsDialog/StartPage.qml</file>
|
||||||
<file>SettingsDialog/SettingsStartSequencePage.qml</file>
|
<file>SettingsDialog/SettingsStartSequencePage.qml</file>
|
||||||
<file>SettingsDialog/SettingsBaseStationPage.qml</file>
|
<file>SettingsDialog/SettingsBaseStationPage.qml</file>
|
||||||
<file>SettingsDialog/SettingsBaseStationConnectionsPage.qml</file>
|
<file>components/layout/ToolBar.qml</file>
|
||||||
|
<file>MainPage/TopToolBar.qml</file>
|
||||||
|
<file>MainPage/CenterContent.qml</file>
|
||||||
|
<file>MainPage/BottomToolBar.qml</file>
|
||||||
|
<file>components/MainActionButton.qml</file>
|
||||||
|
<file>components/StateIndicator.qml</file>
|
||||||
|
<file>components/BatteryIndicator.qml</file>
|
||||||
|
<file>components/ExtensionOverview.qml</file>
|
||||||
|
<file>SettingsDialog/SettingsBaseStationSettingsPage.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 40 KiB |
|
@ -25,6 +25,5 @@
|
||||||
<file>graphics/icons/user_black.png</file>
|
<file>graphics/icons/user_black.png</file>
|
||||||
<file>graphics/icons/ok.png</file>
|
<file>graphics/icons/ok.png</file>
|
||||||
<file>sounds/IFSC_STARTSIGNAL_SINE.wav</file>
|
<file>sounds/IFSC_STARTSIGNAL_SINE.wav</file>
|
||||||
<file>ScStwBasestation.sb64</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Before Width: | Height: | Size: 59 KiB |
1
shared-libraries
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 37bea9105f1c7e0c8f1a209da3d5043c61abfb44
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
|
||||||
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, version 3 of the License.
|
|
||||||
|
|
||||||
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 Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "headers/appsettings.h"
|
|
||||||
|
|
||||||
AppSettings * pGlobalAppSettings = nullptr;
|
|
||||||
|
|
||||||
AppSettings::AppSettings(QObject* parent)
|
|
||||||
:QObject(parent)
|
|
||||||
{
|
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
|
||||||
|
|
||||||
this->settingsManager = new QSettings(path+"/settings.ini", QSettings::IniFormat, this);
|
|
||||||
|
|
||||||
this->setDefaultSetting("ready_en", "false");
|
|
||||||
this->setDefaultSetting("ready_delay", 0);
|
|
||||||
this->setDefaultSetting("at_marks_en", "false");
|
|
||||||
this->setDefaultSetting("at_marks_delay", 0);
|
|
||||||
|
|
||||||
this->setDefaultSetting("theme", "Light");
|
|
||||||
|
|
||||||
this->setDefaultSetting("baseStationIpAdress", "192.168.4.1");
|
|
||||||
|
|
||||||
pGlobalAppSettings = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AppSettings::loadSetting(const QString &key)
|
|
||||||
{
|
|
||||||
this->settingsManager->beginGroup("AppSettings");
|
|
||||||
QString value = this->settingsManager->value(key , false).toString();
|
|
||||||
this->settingsManager->endGroup();
|
|
||||||
return(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppSettings::writeSetting(const QString &key, const QVariant &variant)
|
|
||||||
{
|
|
||||||
this->settingsManager->beginGroup("AppSettings");
|
|
||||||
this->settingsManager->setValue(key , variant);
|
|
||||||
this->settingsManager->endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppSettings::setDefaultSetting(const QString &key, const QVariant &defaultVariant)
|
|
||||||
{
|
|
||||||
QString value = this->loadSetting(key);
|
|
||||||
if(value == "false"){
|
|
||||||
this->writeSetting(key, defaultVariant);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
AppSettings::~AppSettings()
|
|
||||||
{
|
|
||||||
delete settingsManager;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
#include "headers/apptheme.h"
|
|
||||||
|
|
||||||
AppTheme::AppTheme(QObject *parent) : QObject(parent)
|
|
||||||
{
|
|
||||||
|
|
||||||
QVariantMap tmpDarkTheme = {
|
|
||||||
{"backgroundColor", "#2d3037"},
|
|
||||||
|
|
||||||
{"buttonColor", "#202227"},
|
|
||||||
{"buttonPressedColor", "#41454f"},
|
|
||||||
{"buttonBorderColor", "grey"},
|
|
||||||
{"disabledButtonColor", "#555555"},
|
|
||||||
|
|
||||||
{"viewColor", "#202227"},
|
|
||||||
{"menuColor", "#292b32"},
|
|
||||||
|
|
||||||
{"delegate1Color", "#202227"},
|
|
||||||
{"delegate2Color", "#202227"},
|
|
||||||
{"delegateBackgroundColor", "#202227"},
|
|
||||||
{"delegatePressedColor", "#41454f"},
|
|
||||||
|
|
||||||
{"textColor", "#ffffff"},
|
|
||||||
{"textDarkColor", "#232323"},
|
|
||||||
{"disabledTextColor", "#777777"},
|
|
||||||
|
|
||||||
{"sliderColor", "#6ccaf2"},
|
|
||||||
|
|
||||||
{"successColor", "#6bd43b"},
|
|
||||||
{"errorColor", "#e03b2f"},
|
|
||||||
|
|
||||||
{"lineColor", "grey"},
|
|
||||||
|
|
||||||
{"backIcon", "qrc:/graphics/icons/back.png"},
|
|
||||||
{"settIcon", "qrc:/graphics/icons/settings.png"},
|
|
||||||
{"buzzerIcon", "qrc:/graphics/icons/buzzer.png"},
|
|
||||||
{"startpadIcon", "qrc:/graphics/icons/startpad.png"},
|
|
||||||
{"baseStationIcon", "qrc:/graphics/icons/BaseStation.png"},
|
|
||||||
{"profilesIcon", "qrc:/graphics/icons/user.png"},
|
|
||||||
{"confirmIcon", "qrc:/graphics/icons/ok.png"}
|
|
||||||
|
|
||||||
};
|
|
||||||
this->darkTheme = tmpDarkTheme;
|
|
||||||
|
|
||||||
QVariantMap tmpLightTheme = {
|
|
||||||
{"backgroundColor", "white"},
|
|
||||||
|
|
||||||
{"buttonColor", "white"},
|
|
||||||
{"buttonPressedColor", "lightgrey"},
|
|
||||||
{"buttonBorderColor", "grey"},
|
|
||||||
{"disabledButtonColor", "#d5d5d5"},
|
|
||||||
|
|
||||||
{"viewColor", "white"},
|
|
||||||
{"menuColor", "#f8f8f8"},
|
|
||||||
|
|
||||||
{"delegate1Color", "#202227"},
|
|
||||||
{"delegate2Color", "#202227"},
|
|
||||||
{"delegateBackgroundColor", "white"},
|
|
||||||
{"delegatePressedColor", "#dddedf"},
|
|
||||||
|
|
||||||
{"textColor", "black"},
|
|
||||||
{"textDarkColor", "#232323"},
|
|
||||||
{"disabledTextColor", "grey"},
|
|
||||||
|
|
||||||
{"sliderColor", "#6ccaf2"},
|
|
||||||
|
|
||||||
{"successColor", "#60de26"},
|
|
||||||
{"errorColor", "#ff0000"},
|
|
||||||
|
|
||||||
{"lineColor", "grey"},
|
|
||||||
|
|
||||||
{"backIcon", "qrc:/graphics/icons/back_black.png"},
|
|
||||||
{"settIcon", "qrc:/graphics/icons/settings_black.png"},
|
|
||||||
{"buzzerIcon", "qrc:/graphics/icons/buzzer_black.png"},
|
|
||||||
{"startpadIcon", "qrc:/graphics/icons/startpad_black.png"},
|
|
||||||
{"baseStationIcon", "qrc:/graphics/icons/BaseStation_black.png"},
|
|
||||||
{"profilesIcon", "qrc:/graphics/icons/user_black.png"},
|
|
||||||
{"confirmIcon", "qrc:/graphics/icons/ok_black.png"}
|
|
||||||
|
|
||||||
};
|
|
||||||
this->lightTheme = tmpLightTheme;
|
|
||||||
|
|
||||||
QString currentThemeString = pGlobalAppSettings->loadSetting("theme");
|
|
||||||
|
|
||||||
if(currentThemeString == "Light"){
|
|
||||||
this->currentTheme = &this->lightTheme;
|
|
||||||
}
|
|
||||||
else if (currentThemeString == "Dark") {
|
|
||||||
this->currentTheme = &this->darkTheme;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->currentTheme = &this->lightTheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant AppTheme::getStyle() {
|
|
||||||
return *this->currentTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppTheme::changeTheme() {
|
|
||||||
QString currentThemeString = pGlobalAppSettings->loadSetting("theme");
|
|
||||||
QString newThemeString = "Light";
|
|
||||||
|
|
||||||
if(currentThemeString == "Light"){
|
|
||||||
this->currentTheme = &this->darkTheme;
|
|
||||||
newThemeString = "Dark";
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (currentThemeString == "Dark") {
|
|
||||||
this->currentTheme = &this->lightTheme;
|
|
||||||
newThemeString = "Light";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->currentTheme = &this->lightTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
pGlobalAppSettings->writeSetting("theme", newThemeString);
|
|
||||||
|
|
||||||
emit this->styleChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppTheme::refreshTheme() {
|
|
||||||
QString currentThemeString = pGlobalAppSettings->loadSetting("theme");
|
|
||||||
|
|
||||||
if(currentThemeString == "Light"){
|
|
||||||
this->currentTheme = &this->lightTheme;
|
|
||||||
}
|
|
||||||
else if (currentThemeString == "Dark") {
|
|
||||||
this->currentTheme = &this->darkTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit this->styleChanged();
|
|
||||||
}
|
|
|
@ -1,460 +0,0 @@
|
||||||
#include "headers/baseconn.h"
|
|
||||||
|
|
||||||
BaseConn * pGlobalBaseConn = nullptr;
|
|
||||||
|
|
||||||
BaseConn::BaseConn(QObject *parent) : QObject(parent)
|
|
||||||
{
|
|
||||||
pGlobalBaseConn = this;
|
|
||||||
|
|
||||||
socket = new QTcpSocket(this);
|
|
||||||
|
|
||||||
this->timeoutTimer = new QTimer(this);
|
|
||||||
this->timeoutTimer->setSingleShot(true);
|
|
||||||
|
|
||||||
this->state = "disconnected";
|
|
||||||
|
|
||||||
connect(this->socket, SIGNAL(error(QAbstractSocket::SocketError)),
|
|
||||||
this, SLOT(gotError(QAbstractSocket::SocketError)));
|
|
||||||
|
|
||||||
connect(this->socket, &QAbstractSocket::stateChanged, this, &BaseConn::socketStateChanged);
|
|
||||||
|
|
||||||
this->nextConnectionId = 1;
|
|
||||||
this->connections = QVariantList({});
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::connectToHost() {
|
|
||||||
qDebug() << "connecting";
|
|
||||||
setState("connecting");
|
|
||||||
this->connection_progress = 0;
|
|
||||||
|
|
||||||
connect(this->timeoutTimer, SIGNAL(timeout()), this, SLOT(connectionTimeout()));
|
|
||||||
|
|
||||||
//connect
|
|
||||||
this->socket->connectToHost(this->ip, this->port);
|
|
||||||
|
|
||||||
timeoutTimer->start(3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::connectionTimeout() {
|
|
||||||
this->socket->abort();
|
|
||||||
disconnect(this->timeoutTimer, SIGNAL(timeout()), this, SLOT(connectionTimeout()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseConn::init() {
|
|
||||||
disconnect(this->timeoutTimer, SIGNAL(timeout()), this, SLOT(connectionTimeout()));
|
|
||||||
this->timeoutTimer->stop();
|
|
||||||
|
|
||||||
connect(this->socket, &QTcpSocket::readyRead, this, &BaseConn::readyRead);
|
|
||||||
this->connection_progress = 50;
|
|
||||||
|
|
||||||
this->setState("connected");
|
|
||||||
|
|
||||||
// init remote session
|
|
||||||
QJsonArray updateSubs = {"onRaceStateChanged", "onTimersChanged", "onExtensionConnectionsChanged", "onNextStartActionChanged"};
|
|
||||||
QJsonObject sessionParams = {{"updateSubs", updateSubs}, {"init", true}, {"usingTerminationKeys", true}};
|
|
||||||
|
|
||||||
QVariantMap initResponse = this->sendCommand(1, sessionParams, false);
|
|
||||||
|
|
||||||
if(initResponse["status"] != 200) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->firmwareVersion = initResponse["data"].toMap()["version"].toString();
|
|
||||||
this->timeOffset = initResponse["data"].toMap()["time"].toDouble() - this->date->currentMSecsSinceEpoch();
|
|
||||||
this->firmwareUpToDate = this->isFirmwareUpToDate();
|
|
||||||
|
|
||||||
emit this->propertiesChanged();
|
|
||||||
|
|
||||||
qDebug() << "[INFO][BaseStation] Init done! firmware: version: " << this->firmwareVersion << " up-to-date: " << this->firmwareUpToDate << " time offset: " << this->timeOffset;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::deInit() {
|
|
||||||
this->connections.clear();
|
|
||||||
emit this->connectionsChanged();
|
|
||||||
this->setState("disconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::closeConnection()
|
|
||||||
{
|
|
||||||
this->connections = QVariantList({});
|
|
||||||
emit this->connectionsChanged();
|
|
||||||
|
|
||||||
qDebug() << "closing connection";
|
|
||||||
switch (socket->state())
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
socket->disconnectFromHost();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
socket->abort();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
socket->abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
setState("disconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::gotError(QAbstractSocket::SocketError err)
|
|
||||||
{
|
|
||||||
//qDebug() << "got error";
|
|
||||||
QString strError = "unknown";
|
|
||||||
switch (err)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
strError = "Connection was refused";
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
strError = "Remote host closed the connection";
|
|
||||||
this->closeConnection();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
strError = "Host address was not found";
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
strError = "Connection timed out";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
strError = "Unknown error";
|
|
||||||
}
|
|
||||||
|
|
||||||
emit gotError(strError);
|
|
||||||
qDebug() << "got socket error: " << strError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------
|
|
||||||
// --- socket communication handling ---
|
|
||||||
// -------------------------------------
|
|
||||||
|
|
||||||
void BaseConn::socketStateChanged(QAbstractSocket::SocketState socketState) {
|
|
||||||
switch (socketState) {
|
|
||||||
case QAbstractSocket::UnconnectedState:
|
|
||||||
{
|
|
||||||
this->deInit();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QAbstractSocket::ConnectedState:
|
|
||||||
{
|
|
||||||
if(!this->init()) {
|
|
||||||
this->closeConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
//qDebug() << "+ --- UNKNOWN SOCKET STATE: " << socketState;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap BaseConn::sendCommand(int header, QJsonValue data, bool useTerminationKeys, int timeout) {
|
|
||||||
if(this->state != "connected"){
|
|
||||||
return {{"status", 910}, {"data", "not connected"}};
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate id and witing requests entry
|
|
||||||
int thisId = nextConnectionId;
|
|
||||||
//qDebug() << "sending command: " << header << " with data: " << data << " and id: " << thisId;
|
|
||||||
nextConnectionId ++;
|
|
||||||
|
|
||||||
QEventLoop *loop = new QEventLoop(this);
|
|
||||||
QTimer *timer = new QTimer(this);
|
|
||||||
QJsonObject reply;
|
|
||||||
|
|
||||||
this->waitingRequests.append({thisId, loop, reply});
|
|
||||||
|
|
||||||
QJsonObject requestObj;
|
|
||||||
requestObj.insert("id", thisId);
|
|
||||||
requestObj.insert("header", header);
|
|
||||||
requestObj.insert("data", data);
|
|
||||||
|
|
||||||
QString jsonRequest = QJsonDocument(requestObj).toJson();
|
|
||||||
|
|
||||||
timer->setSingleShot(true);
|
|
||||||
// quit the loop when the timer times out
|
|
||||||
loop->connect(timer, SIGNAL(timeout()), loop, SLOT(quit()));
|
|
||||||
// quit the loop when the connection was established
|
|
||||||
// loop.connect(this, &BaseConn::gotReply, &loop, &QEventLoop::quit);
|
|
||||||
// start the timer before starting to connect
|
|
||||||
timer->start(timeout);
|
|
||||||
|
|
||||||
//write data
|
|
||||||
if(useTerminationKeys) {
|
|
||||||
socket->write("<message>" + jsonRequest.toLatin1() + "</message>");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
socket->write(jsonRequest.toLatin1());
|
|
||||||
}
|
|
||||||
|
|
||||||
//wait for an answer to finish (programm gets stuck in here)
|
|
||||||
loop->exec();
|
|
||||||
|
|
||||||
bool replyFound = false;
|
|
||||||
|
|
||||||
// find reply and delete the request from waiting list
|
|
||||||
for(int i = 0; i<this->waitingRequests.length(); i++){
|
|
||||||
if(this->waitingRequests[i].id == thisId){
|
|
||||||
// request was found
|
|
||||||
replyFound = true;
|
|
||||||
// delete event loop
|
|
||||||
if(this->waitingRequests[i].loop != nullptr) {
|
|
||||||
delete this->waitingRequests[i].loop;
|
|
||||||
}
|
|
||||||
// store reply
|
|
||||||
reply = this->waitingRequests[i].reply;
|
|
||||||
// remove reply from waiting list
|
|
||||||
this->waitingRequests.removeAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!replyFound) {
|
|
||||||
// some internal error occured
|
|
||||||
return {{"status", 900}, {"data", ""}};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(timer->remainingTime() == -1){
|
|
||||||
//the time has been triggered -> timeout
|
|
||||||
return {{"status", 911}, {"data", ""}};
|
|
||||||
}
|
|
||||||
|
|
||||||
delete timer;
|
|
||||||
return {{"status", reply.value("header").toInt()}, {"data", reply.value("data").toVariant()}};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::readyRead() {
|
|
||||||
|
|
||||||
//qDebug() << "ready to ready " << socket->bytesAvailable() << " bytes" ;
|
|
||||||
QString reply = socket->readAll();
|
|
||||||
|
|
||||||
//qWarning() << "socket read: " << reply;
|
|
||||||
|
|
||||||
processSocketMessage(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::processSocketMessage(QString message) {
|
|
||||||
QString startKey = "<message>";
|
|
||||||
QString endKey = "</message>";
|
|
||||||
|
|
||||||
//qWarning() << "... processing message now ... : " << message;
|
|
||||||
|
|
||||||
if(message == ""){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((message.startsWith(startKey) && message.endsWith(endKey)) && (message.count(startKey) == 1 && message.count(endKey) == 1)){
|
|
||||||
// non-split message ( e.g.: <message>123456789</message>
|
|
||||||
}
|
|
||||||
else if(!message.contains(endKey) && (!this->readBuffer.isEmpty() || message.startsWith(startKey))){
|
|
||||||
// begin of a split message ( e.g.: <message>123 )
|
|
||||||
// or middle of a split message ( e.g.: 456 )
|
|
||||||
//qWarning() << "this is a begin or middle of split a message";
|
|
||||||
this->readBuffer += message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(!message.contains(startKey) && message.endsWith(endKey)) {
|
|
||||||
// end of a split message ( e.g.: 789</message> )
|
|
||||||
|
|
||||||
if(!this->readBuffer.isEmpty()){
|
|
||||||
message = readBuffer + message;
|
|
||||||
readBuffer.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if((message.count(startKey) > 1 || message.count(endKey) > 1) || (message.contains(endKey) && !message.endsWith(endKey) && message.contains(startKey) && !message.startsWith(startKey))) {
|
|
||||||
// multiple messages in one packet ( e.g.: <message>123456789</message><message>987654321</message> )
|
|
||||||
// or multiple message fragments in one message ( e.g.: 56789</message><message>987654321</message> or 56789</message><message>98765 )
|
|
||||||
//qDebug() << "detected multiple messages";
|
|
||||||
|
|
||||||
int startOfSecondMessage = message.lastIndexOf(startKey);
|
|
||||||
// process first part of message
|
|
||||||
QString firstMessage = message.left(startOfSecondMessage);
|
|
||||||
this->processSocketMessage(firstMessage);
|
|
||||||
// process second part of message
|
|
||||||
QString secondMessage = message.right(message.length() - startOfSecondMessage);
|
|
||||||
this->processSocketMessage(secondMessage);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// invalid message
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//qWarning() << "... done processing, message: " << message;
|
|
||||||
this->socketReplyRecieved(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::socketReplyRecieved(QString reply) {
|
|
||||||
reply.replace("<message>", "");
|
|
||||||
reply.replace("</message>", "");
|
|
||||||
|
|
||||||
int id = 0;
|
|
||||||
|
|
||||||
QJsonDocument jsonReply = QJsonDocument::fromJson(reply.toUtf8());
|
|
||||||
QJsonObject replyObj = jsonReply.object();
|
|
||||||
|
|
||||||
if(!replyObj.isEmpty()){
|
|
||||||
id = replyObj.value("id").toInt();
|
|
||||||
|
|
||||||
if(id == -1) {
|
|
||||||
// this message is an update!!
|
|
||||||
emit this->gotUpdate(replyObj.toVariantMap());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this message is the reply to a command!
|
|
||||||
for(int i = 0; i < this->waitingRequests.length(); i++){
|
|
||||||
if(this->waitingRequests[i].id == id){
|
|
||||||
this->waitingRequests[i].reply = replyObj;
|
|
||||||
if(this->waitingRequests[i].loop != nullptr){
|
|
||||||
this->waitingRequests[i].loop->quit();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
latestReadReply = reply;
|
|
||||||
emit gotUnexpectedReply(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------
|
|
||||||
// --- updater functions ---
|
|
||||||
// -------------------------
|
|
||||||
|
|
||||||
bool BaseConn::updateTime() {
|
|
||||||
if(abs(this->timeOffset) < 10000) {
|
|
||||||
// the time is already up-to-date
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap ret = this->sendCommand(5001, this->date->currentSecsSinceEpoch());
|
|
||||||
qDebug() << ret;
|
|
||||||
|
|
||||||
return ret["status"].toInt() == 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseConn::updateFirmware() {
|
|
||||||
QString file = ":/ScStwBasestation.sb64";
|
|
||||||
QFile f(file);
|
|
||||||
if (!f.open(QFile::ReadOnly)) return false;
|
|
||||||
QString fileContents = f.readAll();
|
|
||||||
|
|
||||||
if(this->firmwareUpToDate) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap ret = this->sendCommand(5000, fileContents, true, 15000);
|
|
||||||
|
|
||||||
return ret["status"].toInt() == 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseConn::isFirmwareUpToDate() {
|
|
||||||
QString file = ":/ScStwBasestation.sb64";
|
|
||||||
QFile f(file);
|
|
||||||
if (!f.open(QFile::ReadOnly)) return false;
|
|
||||||
QString fileContents = f.readAll();
|
|
||||||
|
|
||||||
QString newFirmwareVersion = fileContents.split("<VER>")[1].split("</VER>")[0];
|
|
||||||
int newFirmwareVersionMajor = newFirmwareVersion.split(".")[0].toInt();
|
|
||||||
int newFirmwareVersionMinor = newFirmwareVersion.split(".")[1].toInt();
|
|
||||||
int newFirmwareVersionPatch = newFirmwareVersion.split(".")[2].toInt();
|
|
||||||
|
|
||||||
QString currentFirmwareVersion = this->firmwareVersion;
|
|
||||||
int currentFirmwareVersionMajor = currentFirmwareVersion.split(".")[0].toInt();
|
|
||||||
int currentFirmwareVersionMinor = currentFirmwareVersion.split(".")[1].toInt();
|
|
||||||
int currentFirmwareVersionPatch = currentFirmwareVersion.split(".")[2].toInt();
|
|
||||||
|
|
||||||
return newFirmwareVersionMajor < currentFirmwareVersionMajor || newFirmwareVersionMinor < currentFirmwareVersionMinor || newFirmwareVersionPatch <= currentFirmwareVersionPatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------
|
|
||||||
// --- helper functions ---
|
|
||||||
// ------------------------
|
|
||||||
|
|
||||||
int BaseConn::writeRemoteSetting(QString key, QString value) {
|
|
||||||
QJsonArray requestData;
|
|
||||||
requestData.append(key);
|
|
||||||
requestData.append(value);
|
|
||||||
return this->sendCommand(3000, requestData)["status"].toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::setIP(const QString &ipAdress){
|
|
||||||
this->ip = ipAdress;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BaseConn::getIP() const
|
|
||||||
{
|
|
||||||
return(this->ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BaseConn::getState() const
|
|
||||||
{
|
|
||||||
return(this->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::setState(QString newState){
|
|
||||||
if(this->state != newState) {
|
|
||||||
qDebug() << "+--- BaseConn state changed: " << newState;
|
|
||||||
this->state = newState;
|
|
||||||
emit stateChanged();
|
|
||||||
if(this->state == "disconnected") {
|
|
||||||
this->deInit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int BaseConn::getProgress() const
|
|
||||||
{
|
|
||||||
return(connection_progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseConn::refreshConnections() {
|
|
||||||
QVariantMap reply = this->sendCommand(2006);
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
if(reply["status"] == 910){
|
|
||||||
this->connections = QVariantList({});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
qDebug() << "+ --- error refreshing connections: " << reply["status"];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantList tmpConnections = reply["data"].toList();
|
|
||||||
|
|
||||||
if(this->connections != reply["data"].toList()){
|
|
||||||
this->connections = reply["data"].toList();
|
|
||||||
emit this->connectionsChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant BaseConn::getConnections() {
|
|
||||||
return(connections);
|
|
||||||
/*
|
|
||||||
"id": "id of the extention (int)",
|
|
||||||
"type": "type of the extention (can be: 'STARTPAD', 'TOPPAD')",
|
|
||||||
"name": "name of the extention",
|
|
||||||
"ip": "ip-adress of he extention (string)",
|
|
||||||
"state": "state of the extention (can be: 'disconnected', 'connecting', 'connected')"
|
|
||||||
*/
|
|
||||||
//QVariantMap conn = {{"id",0}, {"type","STARTPAD"}, {"name", "startpad1"}, {"ip", "192.168.4.11"}, {"state", "connected"}};
|
|
||||||
//QVariantMap conn1 = {{"id",0}, {"type","TOPPAD"}, {"name", "buzzer1"}, {"ip", "192.168.4.10"}, {"state", "connected"}};
|
|
||||||
//QVariantList conns = {conn, conn1};
|
|
||||||
//return conns;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseConn::setConnections(QVariantList connections) {
|
|
||||||
if(this->connections != connections){
|
|
||||||
this->connections = connections;
|
|
||||||
emit this->connectionsChanged();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,367 +0,0 @@
|
||||||
/*
|
|
||||||
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
|
||||||
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, version 3 of the License.
|
|
||||||
|
|
||||||
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 Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "headers/buzzerconn.h"
|
|
||||||
|
|
||||||
BuzzerConn::BuzzerConn(QObject *parent, QString ip, int port) : QObject(parent)
|
|
||||||
{
|
|
||||||
this->networkManager = new QNetworkAccessManager();
|
|
||||||
this->reloadNetworkManager = new QNetworkAccessManager();
|
|
||||||
|
|
||||||
this->socket = new QTcpSocket();
|
|
||||||
|
|
||||||
this->date = new QDateTime;
|
|
||||||
this->latest_button_pressed = 0;
|
|
||||||
this->connected = false;
|
|
||||||
|
|
||||||
this->ip = ip;
|
|
||||||
this->port = port;
|
|
||||||
// "http://192.168.4.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BuzzerConn::connect()
|
|
||||||
{
|
|
||||||
qDebug() << "connecting...";
|
|
||||||
|
|
||||||
//wait until the request has finished
|
|
||||||
QEventLoop loop;
|
|
||||||
QTimer timer;
|
|
||||||
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
||||||
loop.connect(this->socket, SIGNAL(connected()), &loop, SLOT(quit()));
|
|
||||||
|
|
||||||
timer.start(3000);
|
|
||||||
this->socket->connectToHost(this->ip, this->port);
|
|
||||||
loop.exec();
|
|
||||||
timer.stop();
|
|
||||||
|
|
||||||
if(timer.remainingTime() == 0){
|
|
||||||
//the time has been triggered -> timeout
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QList<double> times = gettimes(2000);
|
|
||||||
qDebug() << times[0];
|
|
||||||
if(times[0] == 200.0){
|
|
||||||
this->latest_button_pressed = times[2];
|
|
||||||
for(int i=0;i<=100;i++){
|
|
||||||
this->connection_progress = i;
|
|
||||||
if(!calcoffset(this->gettimes(1000))){
|
|
||||||
this->connection_progress = 100;
|
|
||||||
this->connected = false;
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->connected = true;
|
|
||||||
return(true);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
this->connected = false;
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BuzzerConn::calcoffset(QList<double> times)
|
|
||||||
{
|
|
||||||
if(times.length() != 3){
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
if(times[0] == 200.0){
|
|
||||||
this->latest_button_pressed = times[2];
|
|
||||||
double offset = date->currentMSecsSinceEpoch() - times[1];
|
|
||||||
if(this->latest_offsets.length()>=100){
|
|
||||||
this->latest_offsets.removeFirst();
|
|
||||||
}
|
|
||||||
this->latest_offsets.append(offset);
|
|
||||||
|
|
||||||
double mem = 0;
|
|
||||||
for(int i=0;i<latest_offsets.length();i++){
|
|
||||||
mem += latest_offsets[i];
|
|
||||||
}
|
|
||||||
this->offset = mem / double(latest_offsets.length());
|
|
||||||
qDebug("%20f", this->offset);
|
|
||||||
return(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//this->connected = false;
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<double> BuzzerConn::gettimes(int timeout)
|
|
||||||
{
|
|
||||||
// QList<double> times;
|
|
||||||
// ReturnData_t ret = senddata(this->networkManager, QUrl(this->ip), timeout);
|
|
||||||
// times.append(double(ret.status_code));
|
|
||||||
|
|
||||||
// if(ret.status_code == 200){
|
|
||||||
// ret.text.replace("\n","");
|
|
||||||
// ret.text.replace("\r","");
|
|
||||||
// QStringList times_cache = ret.text.split("<br>");
|
|
||||||
// times.append(times_cache[0].toDouble());
|
|
||||||
// times.append(times_cache[1].toDouble());
|
|
||||||
|
|
||||||
// return(times);
|
|
||||||
// }
|
|
||||||
// else{
|
|
||||||
// return(times);
|
|
||||||
// }
|
|
||||||
|
|
||||||
QList<double> times;
|
|
||||||
signed long ret;
|
|
||||||
ret = this->sendCommand("GET_TIMESTAMP", timeout);
|
|
||||||
if(ret >= 0){
|
|
||||||
times.append(double(200));
|
|
||||||
times.append(double(ret));
|
|
||||||
ret = this->sendCommand("GET_LASTPRESSED", timeout);
|
|
||||||
if(ret >= 0){
|
|
||||||
times.append(double(ret));
|
|
||||||
return(times);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
times[0] = ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
times.append(ret);
|
|
||||||
}
|
|
||||||
return(times);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BuzzerConn::buzzer_triggered()
|
|
||||||
{
|
|
||||||
|
|
||||||
if(!this->connected){
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pending_commands.length() > 0){
|
|
||||||
QString command = this->pending_commands.first();
|
|
||||||
|
|
||||||
signed long retval = this->sendCommand(command, 800);
|
|
||||||
if(retval > 0){
|
|
||||||
this->pending_commands.removeFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<double> times = this->gettimes(1000);
|
|
||||||
if(times[0] == 200.0){
|
|
||||||
if(times[2] > this->latest_button_pressed){
|
|
||||||
this->latest_button_pressed = times[2];
|
|
||||||
|
|
||||||
return(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
//this->connected = false;
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BuzzerConn::start()
|
|
||||||
{
|
|
||||||
if(!this->connected){
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
QList<double> times = this->gettimes(1000);
|
|
||||||
if(times[0] == 200.0 && this->connected){
|
|
||||||
this->latest_button_pressed = times[2];
|
|
||||||
return(true);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
this->connected = false;
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double BuzzerConn::get(QString key)
|
|
||||||
{
|
|
||||||
if(key == "offset"){
|
|
||||||
return(this->offset);
|
|
||||||
}
|
|
||||||
else if (key == "lastpressed") {
|
|
||||||
return(this->latest_button_pressed);
|
|
||||||
}
|
|
||||||
else if( key == "currtime") {
|
|
||||||
return(this->date->currentMSecsSinceEpoch());
|
|
||||||
}
|
|
||||||
else if( key == "connection_progress") {
|
|
||||||
return(this->connection_progress);
|
|
||||||
}
|
|
||||||
else if( key == "connected") {
|
|
||||||
if(this->connected){
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BuzzerConn::test()
|
|
||||||
{
|
|
||||||
ReturnData_t ret = this->senddata(this->networkManager, QUrl("http://www.google.de"), 500);
|
|
||||||
return(ret.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BuzzerConn::refresh()
|
|
||||||
{
|
|
||||||
if(!this->connected){
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
// QList<double> times;
|
|
||||||
// ReturnData_t ret = senddata(this->reloadNetworkManager, QUrl(this->ip), 1000);
|
|
||||||
// times.append(double(ret.status_code));
|
|
||||||
|
|
||||||
// if(ret.status_code == 200){
|
|
||||||
// ret.text.replace("\n","");
|
|
||||||
// ret.text.replace("\r","");
|
|
||||||
// QStringList times_cache = ret.text.split("<br>");
|
|
||||||
// times.append(times_cache[0].toDouble());
|
|
||||||
// times.append(times_cache[1].toDouble());
|
|
||||||
// calcoffset(times);
|
|
||||||
// return(true);
|
|
||||||
// }
|
|
||||||
// else{
|
|
||||||
// //this->connected = false;
|
|
||||||
// return(false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(pending_commands.length() > 0){
|
|
||||||
QString command = this->pending_commands.first();
|
|
||||||
|
|
||||||
signed long retval = this->sendCommand(command, 800);
|
|
||||||
if(retval > 0){
|
|
||||||
this->pending_commands.removeFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//refresh the times
|
|
||||||
QList<double> ret = this->gettimes(800);
|
|
||||||
if(ret[0] >= 0){
|
|
||||||
this->errors = 0;
|
|
||||||
return(this->calcoffset(ret));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->errors ++;
|
|
||||||
if(this->errors > errors_until_disconnect){
|
|
||||||
this->socket->disconnectFromHost();
|
|
||||||
this->connected = false;
|
|
||||||
}
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnData_t BuzzerConn::senddata(QNetworkAccessManager * NetMan, QUrl serviceUrl, int timeout)
|
|
||||||
{
|
|
||||||
|
|
||||||
ReturnData_t ret; //this is a custom type to store the returned data
|
|
||||||
// Call the webservice
|
|
||||||
|
|
||||||
QNetworkRequest request(serviceUrl);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader,
|
|
||||||
"application/x-www-form-urlencoded");
|
|
||||||
|
|
||||||
//send a POST request with the given url and data to the server
|
|
||||||
QUrlQuery pdata;
|
|
||||||
QNetworkReply* reply;
|
|
||||||
|
|
||||||
//wait until the request has finished
|
|
||||||
QEventLoop loop;
|
|
||||||
QTimer timer;
|
|
||||||
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
||||||
loop.connect(NetMan, SIGNAL(finished(QNetworkReply*)), SLOT(quit()));
|
|
||||||
|
|
||||||
timer.start(timeout);
|
|
||||||
reply = NetMan->post(request, pdata.toString(QUrl::FullyEncoded).toUtf8());
|
|
||||||
loop.exec();
|
|
||||||
timer.stop();
|
|
||||||
|
|
||||||
//get the status code
|
|
||||||
QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
|
||||||
|
|
||||||
ret.status_code = status_code.toInt();
|
|
||||||
if(ret.status_code == 0){ //if the statuscode is zero, the connecion to the server was not possible
|
|
||||||
ret.status_code = 444;
|
|
||||||
}
|
|
||||||
//get the full text response
|
|
||||||
ret.text = QString::fromUtf8(reply->readAll());
|
|
||||||
|
|
||||||
//return the data
|
|
||||||
return(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
signed long BuzzerConn::sendCommand(QString command, int timeout){
|
|
||||||
|
|
||||||
//if there is any data in the storage, clear it
|
|
||||||
if(this->socket->bytesAvailable() > 0){
|
|
||||||
this->socket->readAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
//send request to the socket server
|
|
||||||
QByteArray arrBlock;
|
|
||||||
QDataStream out(&arrBlock, QIODevice::WriteOnly);
|
|
||||||
//out.setVersion(QDataStream::Qt_5_10);
|
|
||||||
out << quint16(0) << command;
|
|
||||||
|
|
||||||
out.device()->seek(0);
|
|
||||||
out << quint16(arrBlock.size() - sizeof(quint16));
|
|
||||||
|
|
||||||
this->socket->write(arrBlock);
|
|
||||||
|
|
||||||
//now wait for the server of the sensor to answer
|
|
||||||
QEventLoop loop;
|
|
||||||
QTimer timer;
|
|
||||||
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
||||||
loop.connect(socket, SIGNAL(readyRead()), &loop, SLOT(quit()));
|
|
||||||
timer.start(timeout);
|
|
||||||
loop.exec();
|
|
||||||
|
|
||||||
//loop finished
|
|
||||||
timer.stop();
|
|
||||||
if(timer.remainingTime() == 0){
|
|
||||||
//the time has been triggered -> timeout
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if the data is not 4 bites long if is invalid -> clear and terminate
|
|
||||||
if(this->socket->bytesAvailable() != 4){
|
|
||||||
this->socket->readAll();
|
|
||||||
return(-2);
|
|
||||||
}
|
|
||||||
long data = 0;
|
|
||||||
this->socket->read((char*)&data,4);
|
|
||||||
|
|
||||||
qDebug() << data;
|
|
||||||
qDebug() << this->socket->bytesAvailable();
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuzzerConn::appendCommand(QString command){
|
|
||||||
this->pending_commands.append(command);
|
|
||||||
}
|
|
|
@ -1,649 +0,0 @@
|
||||||
#include "headers/climbingrace.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* manages:
|
|
||||||
* - global state
|
|
||||||
* - timers
|
|
||||||
* - sounds
|
|
||||||
* - next start action
|
|
||||||
* - next start action delay progress
|
|
||||||
* - settings (remote and local)
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClimbingRace::ClimbingRace(QObject *parent) : QObject(parent)
|
|
||||||
{
|
|
||||||
this->state = IDLE;
|
|
||||||
this->mode = LOCAL;
|
|
||||||
|
|
||||||
this->appSettings = new AppSettings(this);
|
|
||||||
this->baseConn = new BaseConn(this);
|
|
||||||
|
|
||||||
this->baseConn->setIP(pGlobalAppSettings->loadSetting("baseStationIpAdress"));
|
|
||||||
connect(this->baseConn, &BaseConn::stateChanged, this, &ClimbingRace::baseStationStateChanged);
|
|
||||||
connect(this->baseConn, &BaseConn::stateChanged, this, &ClimbingRace::refreshMode);
|
|
||||||
connect(this->baseConn, &BaseConn::connectionsChanged, this, &ClimbingRace::baseStationConnectionsChanged);
|
|
||||||
connect(this->baseConn, &BaseConn::gotUpdate, this, &ClimbingRace::handleBaseStationUpdate);
|
|
||||||
connect(this->baseConn, &BaseConn::propertiesChanged, this, &ClimbingRace::baseStationPropertiesChanged);
|
|
||||||
|
|
||||||
this->speedTimers.append( new SpeedTimer(this) );
|
|
||||||
|
|
||||||
this->player = new QMediaPlayer;
|
|
||||||
this->date = new QDateTime;
|
|
||||||
|
|
||||||
this->nextStartActionTimer = new QTimer(this);
|
|
||||||
nextStartActionTimer->setSingleShot(true);
|
|
||||||
|
|
||||||
this->timerTextRefreshTimer = new QTimer(this);
|
|
||||||
this->timerTextRefreshTimer->setInterval(1);
|
|
||||||
this->timerTextRefreshTimer->setSingleShot(true);
|
|
||||||
this->timerTextRefreshTimer->connect(this->timerTextRefreshTimer, &QTimer::timeout, this, &ClimbingRace::refreshTimerText);
|
|
||||||
this->refreshTimerText();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------
|
|
||||||
// --- Main Functionality ---
|
|
||||||
// --------------------------
|
|
||||||
|
|
||||||
int ClimbingRace::startRace() {
|
|
||||||
|
|
||||||
if(this->state != IDLE) {
|
|
||||||
return 904;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "+ --- starting race";
|
|
||||||
|
|
||||||
int returnCode = 900;
|
|
||||||
|
|
||||||
switch (this->mode) {
|
|
||||||
case LOCAL:
|
|
||||||
{
|
|
||||||
|
|
||||||
this->setState(STARTING);
|
|
||||||
|
|
||||||
this->nextStartAction = None;
|
|
||||||
this->playSoundsAndStartRace();
|
|
||||||
|
|
||||||
returnCode = 200;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REMOTE:
|
|
||||||
{
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(1000);
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
returnCode = reply["status"].toInt();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
returnCode = 200;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ClimbingRace::stopRace(int type) {
|
|
||||||
|
|
||||||
if(this->state != RUNNING && this->state != STARTING) {
|
|
||||||
return 904;
|
|
||||||
}
|
|
||||||
|
|
||||||
// type can be:
|
|
||||||
// 0: stopp
|
|
||||||
// 1: cancel
|
|
||||||
// 2: fail (fase start)
|
|
||||||
|
|
||||||
qDebug() << "+ --- stopping race";
|
|
||||||
|
|
||||||
int returnCode = 900;
|
|
||||||
|
|
||||||
switch (this->mode) {
|
|
||||||
case LOCAL:
|
|
||||||
{
|
|
||||||
|
|
||||||
if(type == 1){
|
|
||||||
this->nextStartActionTimer->stop();
|
|
||||||
this->player->stop();
|
|
||||||
this->nextStartAction = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
returnCode = this->speedTimers[0]->stop(type) ? 200:904;
|
|
||||||
|
|
||||||
if(returnCode == 200) {
|
|
||||||
this->setState(STOPPED);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REMOTE:
|
|
||||||
{
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(1001);
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
returnCode = reply["status"].toInt();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
returnCode = 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ClimbingRace::resetRace() {
|
|
||||||
|
|
||||||
if(this->state != STOPPED) {
|
|
||||||
return 904;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "+ --- resetting race";
|
|
||||||
|
|
||||||
int returnCode = 900;
|
|
||||||
|
|
||||||
|
|
||||||
switch (this->mode) {
|
|
||||||
case LOCAL:
|
|
||||||
{
|
|
||||||
returnCode = this->speedTimers[0]->reset() ? 200:904;
|
|
||||||
|
|
||||||
if(returnCode == 200){
|
|
||||||
this->setState(IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REMOTE:
|
|
||||||
{
|
|
||||||
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(1002);
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
returnCode = reply["status"].toInt();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
returnCode = 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------
|
|
||||||
// --- Base Station sync ---
|
|
||||||
// -------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief ClimbingRace::handleBaseStationUpdate
|
|
||||||
*
|
|
||||||
* Function to handle a update, sent by the base station, which indicates
|
|
||||||
* that some remote value (like a state) has changed
|
|
||||||
*
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
void ClimbingRace::handleBaseStationUpdate(QVariant data) {
|
|
||||||
//qDebug() << "got update: " << data;
|
|
||||||
int header = data.toMap()["header"].toInt();
|
|
||||||
switch (header) {
|
|
||||||
case 9000:
|
|
||||||
{
|
|
||||||
// the remote race state has changed
|
|
||||||
this->setState( raceState( data.toMap()["data"].toInt() ) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 9001:
|
|
||||||
{
|
|
||||||
// the remote timers have changed
|
|
||||||
this->refreshRemoteTimers(data.toMap()["data"].toList());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 9002:
|
|
||||||
{
|
|
||||||
// the extension connections have changed
|
|
||||||
this->baseConn->setConnections(data.toMap()["data"].toList());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 9003:
|
|
||||||
{
|
|
||||||
// the next start action has changed
|
|
||||||
this->nextStartActionTotalDelay = data.toMap()["data"].toMap()["nextActionDelay"].toDouble();
|
|
||||||
this->nextStartActionDelayStartedAt = this->date->currentMSecsSinceEpoch() - (this->nextStartActionTotalDelay * data.toMap()["data"].toMap()["nextActionDelayProg"].toDouble());
|
|
||||||
this->nextStartAction = NextStartAction( data.toMap()["data"].toMap()["nextAction"].toInt() );
|
|
||||||
|
|
||||||
emit this->nextStartActionChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::refreshRemoteTimers(QVariantList timers) {
|
|
||||||
|
|
||||||
if(timers.length() != speedTimers.length()){
|
|
||||||
// local timers are out of sync
|
|
||||||
|
|
||||||
// delete all current timers
|
|
||||||
foreach(SpeedTimer * locTimer, this->speedTimers){
|
|
||||||
delete locTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
speedTimers.clear();
|
|
||||||
|
|
||||||
foreach(QVariant remTimer, timers){
|
|
||||||
// create a local timer for each remote timer
|
|
||||||
this->speedTimers.append(new SpeedTimer(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(QVariant remTimer, timers){
|
|
||||||
int currId = remTimer.toMap()["id"].toInt();
|
|
||||||
speedTimers[currId]->startTime = this->date->currentMSecsSinceEpoch() - remTimer.toMap()["currTime"].toDouble();
|
|
||||||
speedTimers[currId]->stoppedTime = remTimer.toMap()["currTime"].toDouble();
|
|
||||||
speedTimers[currId]->reactionTime = remTimer.toMap()["reactTime"].toDouble();
|
|
||||||
|
|
||||||
speedTimers[currId]->setState(SpeedTimer::timerState(remTimer.toMap()["state"].toInt()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------
|
|
||||||
// --- helper functions ---
|
|
||||||
// ------------------------
|
|
||||||
|
|
||||||
void ClimbingRace::playSoundsAndStartRace() {
|
|
||||||
qDebug() << "next Action: " << nextStartAction;
|
|
||||||
|
|
||||||
nextStartActionTimer->disconnect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace()));
|
|
||||||
|
|
||||||
switch (this->nextStartAction) {
|
|
||||||
case AtYourMarks:
|
|
||||||
{
|
|
||||||
if(!playSound("qrc:/sounds/at_marks_1.wav")){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(appSettings->loadSetting("ready_en") == "true"){
|
|
||||||
nextStartAction = Ready;
|
|
||||||
nextStartActionTimer->setInterval(appSettings->loadSetting("ready_delay").toInt() <= 0 ? 1:appSettings->loadSetting("ready_delay").toInt());
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
nextStartAction = Start;
|
|
||||||
nextStartActionTimer->setInterval(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Ready:
|
|
||||||
{
|
|
||||||
if(!playSound("qrc:/sounds/ready_1.wav")){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nextStartAction = Start;
|
|
||||||
nextStartActionTimer->setInterval(1);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Start:
|
|
||||||
{
|
|
||||||
if(!playSound("qrc:/sounds/IFSC_STARTSIGNAL_SINE.wav")){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nextStartAction = None;
|
|
||||||
nextStartActionTimer->disconnect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace()));
|
|
||||||
|
|
||||||
this->setState(RUNNING);
|
|
||||||
speedTimers[0]->start();
|
|
||||||
|
|
||||||
emit this->nextStartActionChanged();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case None:
|
|
||||||
{
|
|
||||||
this->speedTimers[0]->setState(SpeedTimer::STARTING);
|
|
||||||
if(appSettings->loadSetting("at_marks_en") == "true"){
|
|
||||||
nextStartAction = AtYourMarks;
|
|
||||||
nextStartActionTimer->setInterval(appSettings->loadSetting("at_marks_delay").toInt() <= 0 ? 1:appSettings->loadSetting("at_marks_delay").toInt());
|
|
||||||
}
|
|
||||||
else if(appSettings->loadSetting("ready_en") == "true"){
|
|
||||||
nextStartAction = Ready;
|
|
||||||
nextStartActionTimer->setInterval(appSettings->loadSetting("ready_delay").toInt() <= 0 ? 1:appSettings->loadSetting("ready_delay").toInt());
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
nextStartAction = Start;
|
|
||||||
nextStartActionTimer->setInterval(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit this->nextStartActionChanged();
|
|
||||||
|
|
||||||
nextStartActionTimer->connect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace()));
|
|
||||||
nextStartActionTimer->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::playSound(QString path) {
|
|
||||||
|
|
||||||
player->setMedia(QUrl(path));
|
|
||||||
player->setVolume(50);
|
|
||||||
player->play();
|
|
||||||
|
|
||||||
QTimer timer;
|
|
||||||
timer.setInterval(1);
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
|
|
||||||
QEventLoop loop;
|
|
||||||
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
||||||
|
|
||||||
while (player->mediaStatus() == QMediaPlayer::LoadingMedia || player->mediaStatus() == QMediaPlayer::BufferingMedia || player->mediaStatus() == QMediaPlayer::BufferedMedia) {
|
|
||||||
timer.start();
|
|
||||||
loop.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(player->mediaStatus() == QMediaPlayer::EndOfMedia){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClimbingRace::setState(raceState newState) {
|
|
||||||
|
|
||||||
if(newState != this->state) {
|
|
||||||
this->state = newState;
|
|
||||||
this->stateChanged(newState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClimbingRace::refreshMode() {
|
|
||||||
raceMode newMode;
|
|
||||||
if(this->baseConn->state == "connected"){
|
|
||||||
newMode = REMOTE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newMode = LOCAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this->mode != newMode){
|
|
||||||
|
|
||||||
if(newMode == LOCAL){
|
|
||||||
// if the new mode is local -> connection to base station has been lost
|
|
||||||
|
|
||||||
// reset race
|
|
||||||
// reset state
|
|
||||||
this->setState(IDLE);
|
|
||||||
|
|
||||||
// reset timers
|
|
||||||
// go back to one timer
|
|
||||||
for (int i = 0;i<this->speedTimers.length();i++) {
|
|
||||||
delete this->speedTimers[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
this->speedTimers.clear();
|
|
||||||
|
|
||||||
this->speedTimers.append(new SpeedTimer);
|
|
||||||
|
|
||||||
// reset base conn
|
|
||||||
// clear extensions
|
|
||||||
this->baseConn->connections.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->mode = newMode;
|
|
||||||
emit this->modeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClimbingRace::refreshTimerText() {
|
|
||||||
|
|
||||||
// --- refresh timer text ---
|
|
||||||
|
|
||||||
QVariantList newTimerTextList;
|
|
||||||
|
|
||||||
foreach(SpeedTimer * timer, this->speedTimers){
|
|
||||||
QVariantMap timerMap = {{"text",timer->getText()}, {"reacttime", timer->reactionTime}, {"state", timer->getState()}, {"id", this->speedTimers.indexOf(timer)}};
|
|
||||||
newTimerTextList.append(timerMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(newTimerTextList != this->qmlTimers){
|
|
||||||
this->qmlTimers = newTimerTextList;
|
|
||||||
emit timerTextChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- refresh next start action delay progress ---
|
|
||||||
double nextStartActionRemainingDelay = 0;
|
|
||||||
|
|
||||||
switch (this->mode) {
|
|
||||||
case LOCAL: {
|
|
||||||
|
|
||||||
// get remaining and total next start action delay time
|
|
||||||
if(nextStartAction == 0){
|
|
||||||
this->nextStartActionTotalDelay = appSettings->loadSetting("at_marks_delay").toDouble();
|
|
||||||
}
|
|
||||||
else if (nextStartAction == 1) {
|
|
||||||
this->nextStartActionTotalDelay = appSettings->loadSetting("ready_delay").toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
nextStartActionRemainingDelay = this->nextStartActionTimer->remainingTime();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REMOTE: {
|
|
||||||
|
|
||||||
// calculate remaining next start action delay time
|
|
||||||
nextStartActionRemainingDelay = this->nextStartActionTotalDelay - ( this->date->currentMSecsSinceEpoch() - this->nextStartActionDelayStartedAt );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate next start action delay progress
|
|
||||||
if(nextStartActionRemainingDelay > 0){
|
|
||||||
this->nextStartActionDelayProgress = nextStartActionRemainingDelay / this->nextStartActionTotalDelay;
|
|
||||||
emit this->nextStartActionDelayProgressChanged();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->nextStartActionDelayProgress = 0;
|
|
||||||
emit this->nextStartActionDelayProgressChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*if (this->mode == REMOTE && this->state == IDLE) {
|
|
||||||
this->nextStartActionDelayProgress = 0;
|
|
||||||
emit this->nextStartActionDelayProgressChanged();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
this->timerTextRefreshTimer->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::pairConnectedUsbExtensions() {
|
|
||||||
QVariantMap ret = this->baseConn->sendCommand(5002, "", 10000);
|
|
||||||
qDebug() << ret;
|
|
||||||
return ret["status"] == 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - athlete management -
|
|
||||||
|
|
||||||
QVariant ClimbingRace::getAthletes() {
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(4003);
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
qDebug() << "+ --- error getting athletes: " << reply["status"];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap tmpAthletes = reply["data"].toMap();
|
|
||||||
|
|
||||||
//qDebug() << tmpAthletes;
|
|
||||||
|
|
||||||
return tmpAthletes;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::createAthlete(QString userName, QString fullName) {
|
|
||||||
|
|
||||||
QVariant requestData = QVariantMap({{"fullName", fullName}, {"userName", userName}});
|
|
||||||
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(4001, requestData.toJsonValue());
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
qDebug() << "+ --- error creating athlete: " << reply["status"];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::deleteAthlete( QString userName ){
|
|
||||||
|
|
||||||
QVariant requestData = QVariantMap({{"userName", userName}});
|
|
||||||
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(4002, requestData.toJsonValue());
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
qDebug() << "+ --- error deleting athlete: " << reply["status"];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::selectAthlete( QString userName, int timerId ){
|
|
||||||
|
|
||||||
QVariant requestData = QVariantMap({{"userName", userName}, {"timerId", timerId}});
|
|
||||||
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(4000, requestData.toJsonValue());
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
qDebug() << "+ --- error selecting athlete: " << reply["status"];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ClimbingRace::getResults( QString userName ){
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(4004, userName);
|
|
||||||
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
//handle Error!!
|
|
||||||
qDebug() << "+ --- error getting results: " << reply["status"];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantList tmpAthletes = reply["data"].toList();
|
|
||||||
|
|
||||||
//qDebug() << tmpAthletes;
|
|
||||||
|
|
||||||
return tmpAthletes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------
|
|
||||||
// --- functions for qml ---
|
|
||||||
// -------------------------
|
|
||||||
|
|
||||||
int ClimbingRace::getState() {
|
|
||||||
return this->state;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ClimbingRace::getMode() {
|
|
||||||
return this->mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ClimbingRace::getTimerTextList() {
|
|
||||||
return this->qmlTimers;
|
|
||||||
}
|
|
||||||
|
|
||||||
double ClimbingRace::getNextStartActionDelayProgress() {
|
|
||||||
return this->nextStartActionDelayProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ClimbingRace::getNextStartAction() {
|
|
||||||
return this->nextStartAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClimbingRace::writeSetting(QString key, QVariant value) {
|
|
||||||
this->refreshMode();
|
|
||||||
|
|
||||||
if(this->mode == REMOTE && ( this->remoteSettings.contains(key) || this->remoteOnlySettings.contains(key) ) ){
|
|
||||||
this->baseConn->writeRemoteSetting(key, value.toString());
|
|
||||||
}
|
|
||||||
else if(!this->remoteOnlySettings.contains(key)){
|
|
||||||
this->appSettings->writeSetting(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ClimbingRace::readSetting(QString key) {
|
|
||||||
this->refreshMode();
|
|
||||||
|
|
||||||
if(this->mode == REMOTE && ( this->remoteSettings.contains(key) || this->remoteOnlySettings.contains(key) )){
|
|
||||||
QVariantMap reply = this->baseConn->sendCommand(3001, key);
|
|
||||||
if(reply["status"] != 200){
|
|
||||||
return "false";
|
|
||||||
}
|
|
||||||
return reply["data"].toString();
|
|
||||||
}
|
|
||||||
else if(!this->remoteOnlySettings.contains(key)){
|
|
||||||
return this->appSettings->loadSetting(key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "false";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClimbingRace::connectBaseStation() {
|
|
||||||
this->reloadBaseStationIpAdress();
|
|
||||||
this->baseConn->connectToHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClimbingRace::disconnectBaseStation() {
|
|
||||||
this->baseConn->closeConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ClimbingRace::getBaseStationState() {
|
|
||||||
return this->baseConn->getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ClimbingRace::getBaseStationConnections() {
|
|
||||||
return baseConn->getConnections();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap ClimbingRace::getBaseStationProperties() {
|
|
||||||
QVariantMap firmware = {{"version", this->baseConn->firmwareVersion}, {"upToDate", this->baseConn->firmwareUpToDate}};
|
|
||||||
return {{"firmware", firmware}, {"timeOffset", this->baseConn->timeOffset}};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::updateBasestationFirmware() {
|
|
||||||
return this->baseConn->updateFirmware();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::updateBasestationTime() {
|
|
||||||
return this->baseConn->updateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClimbingRace::reloadBaseStationIpAdress() {
|
|
||||||
if(this->baseConn->state == "disconnected"){
|
|
||||||
this->baseConn->setIP(pGlobalAppSettings->loadSetting("baseStationIpAdress"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QQmlEngine>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -35,6 +36,8 @@
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QRect>
|
||||||
|
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
#include <QtCore/QCommandLineOption>
|
#include <QtCore/QCommandLineOption>
|
||||||
|
@ -44,94 +47,38 @@
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QtQml/QQmlContext>
|
#include <QtQml/QQmlContext>
|
||||||
//#include <QtWebView/QtWebView>
|
|
||||||
#ifdef Q_OS_ANDROID
|
#include "headers/scstwappsettings.h"
|
||||||
#include <QtAndroidExtras>
|
//#include "headers/speedtimer.h"
|
||||||
#endif
|
//#include "headers/climbingrace.h"
|
||||||
#include "headers/sqlstoragemodel.h"
|
#include "headers/scstwappbackend.h"
|
||||||
#include "headers/sqlprofilemodel.h"
|
#include <scstwlibraries.h>
|
||||||
#include "headers/appsettings.h"
|
|
||||||
#include "headers/baseconn.h"
|
|
||||||
#include "headers/speedtimer.h"
|
|
||||||
#include "headers/climbingrace.h"
|
|
||||||
#include "headers/apptheme.h"
|
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
|
||||||
static void connectToDatabase()
|
|
||||||
{
|
|
||||||
QSqlDatabase database = QSqlDatabase::database();
|
|
||||||
if (!database.isValid()) {
|
|
||||||
database = QSqlDatabase::addDatabase("QSQLITE");
|
|
||||||
if (!database.isValid())
|
|
||||||
qFatal("Cannot add database: %s", qPrintable(database.lastError().text()));
|
|
||||||
}
|
|
||||||
|
|
||||||
const QDir writeDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
|
||||||
|
|
||||||
if (!writeDir.mkpath("."))
|
|
||||||
qFatal("Failed to create writable directory at %s", qPrintable(writeDir.absolutePath()));
|
|
||||||
|
|
||||||
// Ensure that we have a writable location on all devices.
|
|
||||||
const QString fileName = writeDir.absolutePath() + "/chat-database.sqlite3";
|
|
||||||
//QFile::remove(fileName);
|
|
||||||
// When using the SQLite driver, open() will create the SQLite database if it doesn't exist.
|
|
||||||
database.setDatabaseName(fileName);
|
|
||||||
if (!database.open()) {
|
|
||||||
QFile::remove(fileName);
|
|
||||||
qFatal("Cannot open database: %s", qPrintable(database.lastError().text()));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
// setup speed backend
|
||||||
//set the standard volume to media
|
qmlRegisterType<ScStwAppBackend>("de.itsblue.ScStwApp", 2, 0, "SpeedBackend");
|
||||||
QAndroidJniObject jactivity=QtAndroid::androidActivity();
|
qmlRegisterType<ScStwAppSettings>("de.itsblue.ScStwApp", 2, 0, "ScStwAppSettings");
|
||||||
if(jactivity.isValid())
|
|
||||||
jactivity.callMethod<void>("setVolumeControlStream","(I)V",3);
|
|
||||||
|
|
||||||
//set statusbar color
|
qRegisterMetaType<ScStwAppSettings::AppInternalSetting>("ScStwAppSettings::BaseStationSetting");
|
||||||
QtAndroid::runOnAndroidThread([=]()
|
|
||||||
{
|
|
||||||
QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;");
|
|
||||||
window.callMethod<void>("addFlags", "(I)V", 0x80000000);
|
|
||||||
window.callMethod<void>("clearFlags", "(I)V", 0x04000000);
|
|
||||||
//window.callMethod<void>("setStatusBarColor", "(I)V", 0x202227); // Desired statusbar color
|
|
||||||
//QAndroidJniObject decorView = window.callObjectMethod("getDecorView", "()Landroid/view/View;");
|
|
||||||
//decorView.callMethod<void>("setSystemUiVisibility", "(I)V", 0x00002000);
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
connectToDatabase();
|
|
||||||
|
|
||||||
AppSettings * pAppSettings = new AppSettings();
|
|
||||||
|
|
||||||
//setup the sql storage model as a qml model
|
|
||||||
qmlRegisterType<SqlProfileModel>("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlProfileModel");
|
|
||||||
qmlRegisterType<SqlStorageModel>("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlStorageModel");
|
|
||||||
|
|
||||||
// setup speed backend and App themes
|
|
||||||
qmlRegisterType<ClimbingRace>("com.itsblue.speedclimbingstopwatch", 2, 0, "SpeedBackend");
|
|
||||||
qmlRegisterType<AppTheme>("com.itsblue.speedclimbingstopwatch", 2, 0, "AppTheme");
|
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
|
||||||
|
|
||||||
QQmlContext *context = engine.rootContext();
|
ScStwLibraries::init();
|
||||||
|
ScStwLibraries::initStyling(&engine);
|
||||||
|
|
||||||
// stup app settings
|
const QUrl url(QStringLiteral("qrc:/main.qml"));
|
||||||
context->setContextProperty("_cppAppSettings", pAppSettings);
|
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
|
||||||
|
&app, [url](QObject *obj, const QUrl &objUrl) {
|
||||||
|
if (!obj && url == objUrl)
|
||||||
|
QCoreApplication::exit(-1);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
engine.load(url);
|
||||||
|
|
||||||
if (engine.rootObjects().isEmpty())
|
return app.exec();
|
||||||
return -1;
|
|
||||||
|
|
||||||
int iRet = 0;
|
|
||||||
iRet = app.exec();
|
|
||||||
|
|
||||||
return iRet;
|
|
||||||
}
|
}
|
||||||
|
|
106
sources/scstwappbackend.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include "../headers/scstwappbackend.h"
|
||||||
|
|
||||||
|
ScStwAppBackend::ScStwAppBackend(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------
|
||||||
|
// --- helper functions ---
|
||||||
|
// ------------------------
|
||||||
|
|
||||||
|
void ScStwAppBackend::setScStwClient(ScStwClient *client) {
|
||||||
|
if(client != this->scStwClient) {
|
||||||
|
this->scStwClient = client;
|
||||||
|
emit this->scStwClientChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - athlete management -
|
||||||
|
|
||||||
|
// TODO: move to client
|
||||||
|
QVariant ScStwAppBackend::getAthletes() {
|
||||||
|
QVariantMap reply = this->scStwClient->sendCommand(4003);
|
||||||
|
|
||||||
|
if(reply["status"] != 200){
|
||||||
|
//handle Error!!
|
||||||
|
qDebug() << "+ --- error getting athletes: " << reply["status"];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap tmpAthletes = reply["data"].toMap();
|
||||||
|
|
||||||
|
//qDebug() << tmpAthletes;
|
||||||
|
|
||||||
|
return tmpAthletes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScStwAppBackend::createAthlete(QString userName, QString fullName) {
|
||||||
|
|
||||||
|
QVariant requestData = QVariantMap({{"fullName", fullName}, {"userName", userName}});
|
||||||
|
|
||||||
|
QVariantMap reply = this->scStwClient->sendCommand(4001, requestData.toJsonValue());
|
||||||
|
|
||||||
|
if(reply["status"] != 200){
|
||||||
|
//handle Error!!
|
||||||
|
qDebug() << "+ --- error creating athlete: " << reply["status"];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScStwAppBackend::deleteAthlete( QString userName ){
|
||||||
|
|
||||||
|
QVariant requestData = QVariantMap({{"userName", userName}});
|
||||||
|
|
||||||
|
QVariantMap reply = this->scStwClient->sendCommand(4002, requestData.toJsonValue());
|
||||||
|
|
||||||
|
if(reply["status"] != 200){
|
||||||
|
//handle Error!!
|
||||||
|
qDebug() << "+ --- error deleting athlete: " << reply["status"];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScStwAppBackend::selectAthlete( QString userName, int timerId ){
|
||||||
|
|
||||||
|
QVariant requestData = QVariantMap({{"userName", userName}, {"timerId", timerId}});
|
||||||
|
|
||||||
|
QVariantMap reply = this->scStwClient->sendCommand(4000, requestData.toJsonValue());
|
||||||
|
|
||||||
|
if(reply["status"] != 200){
|
||||||
|
//handle Error!!
|
||||||
|
qDebug() << "+ --- error selecting athlete: " << reply["status"];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScStwAppBackend::getResults( QString userName ){
|
||||||
|
QVariantMap reply = this->scStwClient->sendCommand(4004, userName);
|
||||||
|
|
||||||
|
if(reply["status"] != 200){
|
||||||
|
//handle Error!!
|
||||||
|
qDebug() << "+ --- error getting results: " << reply["status"];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList tmpAthletes = reply["data"].toList();
|
||||||
|
|
||||||
|
//qDebug() << tmpAthletes;
|
||||||
|
|
||||||
|
return tmpAthletes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// --- functions for qml ---
|
||||||
|
// -------------------------
|
||||||
|
|
||||||
|
ScStwClient* ScStwAppBackend::getScStwClient() {
|
||||||
|
return this->scStwClient;
|
||||||
|
}
|
46
sources/scstwappsettings.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
||||||
|
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, version 3 of the License.
|
||||||
|
|
||||||
|
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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "headers/scstwappsettings.h"
|
||||||
|
|
||||||
|
ScStwAppSettings * pGlobalAppSettings = nullptr;
|
||||||
|
|
||||||
|
ScStwAppSettings::ScStwAppSettings(QObject *parent) : ScStwAppSettings (nullptr, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ScStwAppSettings::ScStwAppSettings(ScStwClient * client, QObject* parent)
|
||||||
|
:ScStwRemoteSettings(client, parent)
|
||||||
|
{
|
||||||
|
this->registerKeyLevelConverters(ScStwAppSettings::KeyLevel, &ScStwAppSettings::keyToString, &ScStwAppSettings::keyToType);
|
||||||
|
|
||||||
|
this->setDefaultSetting(ScStwAppSettings::AppThemeSetting, "Light");
|
||||||
|
this->setDefaultSetting(ScStwAppSettings::BaseStationIpSetting, "192.168.4.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScStwAppSettings::readSetting(AppInternalSetting key) {
|
||||||
|
return this->readSetting(key, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScStwAppSettings::writeSetting(AppInternalSetting key, QVariant value) {
|
||||||
|
return this->writeSetting(key, 1, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScStwAppSettings::setDefaultSetting(AppInternalSetting key, QVariant defaultValue) {
|
||||||
|
return this->setDefaultSetting(key, 1, defaultValue);
|
||||||
|
}
|
||||||
|
|
|
@ -1,211 +0,0 @@
|
||||||
#include "headers/speedtimer.h"
|
|
||||||
|
|
||||||
SpeedTimer::SpeedTimer(QObject *parent) : QObject(parent)
|
|
||||||
{
|
|
||||||
|
|
||||||
this->date = new QDateTime;
|
|
||||||
|
|
||||||
this->startTime = 0;
|
|
||||||
this->stopTime = 0;
|
|
||||||
this->stoppedTime = 0;
|
|
||||||
this->reactionTime = 0;
|
|
||||||
this->state = IDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpeedTimer::start(bool force) {
|
|
||||||
if(this->state != STARTING && !force){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
qDebug() << "starting timer";
|
|
||||||
if(!force){
|
|
||||||
this->stopTime = 0;
|
|
||||||
this->stoppedTime = 0;
|
|
||||||
this->reactionTime = 0;
|
|
||||||
this->startTime = this->date->currentMSecsSinceEpoch();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->setState(RUNNING);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpeedTimer::stop(int type, bool force) {
|
|
||||||
|
|
||||||
// type can be:
|
|
||||||
// 0: stopped
|
|
||||||
// 1: cancelled
|
|
||||||
// 2: failed (fase start)
|
|
||||||
|
|
||||||
if( ( this->state != SpeedTimer::STARTING && this->state != SpeedTimer::RUNNING && this->state ) && !force ){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//qDebug() << "Stopping: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << stoppedTime << " reactionTime: " << reactionTime;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
this->stopTime = this->date->currentMSecsSinceEpoch();
|
|
||||||
this->stoppedTime = this->stopTime - this->startTime;
|
|
||||||
this->setState(WON);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
this->stoppedTime = 0;
|
|
||||||
this->setState(CANCELLED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
this->stoppedTime = this->reactionTime;
|
|
||||||
this->setState(FAILED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "Stopped: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << stoppedTime << " reactionTime: " << reactionTime;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
//this->startPad->appendCommand("SET_LED_STARTING");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpeedTimer::reset(bool force){
|
|
||||||
if( ( this->state < WON ) && !force){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->startTime = 0;
|
|
||||||
this->stopTime = 0;
|
|
||||||
this->stoppedTime = 0;
|
|
||||||
this->reactionTime = 0;
|
|
||||||
this->setState(IDLE);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
//this->startPad->appendCommand("SET_LED_STARTING");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpeedTimer::setState(timerState newState){
|
|
||||||
if(this->state != newState){
|
|
||||||
this->state = newState;
|
|
||||||
qDebug() << "+--- timer state changed: " << newState;
|
|
||||||
emit this->stateChanged(newState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SpeedTimer::getState(){
|
|
||||||
switch(state){
|
|
||||||
case IDLE:
|
|
||||||
return "IDLE";
|
|
||||||
case STARTING:
|
|
||||||
return "STARTING";
|
|
||||||
case WAITING:
|
|
||||||
return "WAITING";
|
|
||||||
case RUNNING:
|
|
||||||
return "RUNNING";
|
|
||||||
case WON:
|
|
||||||
return "WON";
|
|
||||||
case LOST:
|
|
||||||
return "LOST";
|
|
||||||
case FAILED:
|
|
||||||
return "FAILED";
|
|
||||||
case CANCELLED:
|
|
||||||
return "CANCELLED";
|
|
||||||
case DISABLED:
|
|
||||||
return "DISABLED";
|
|
||||||
}
|
|
||||||
return "ERROR";
|
|
||||||
}
|
|
||||||
|
|
||||||
double SpeedTimer::getCurrTime() {
|
|
||||||
double currTime;
|
|
||||||
if(this->state == RUNNING && this->startTime > 0){
|
|
||||||
currTime = this->date->currentMSecsSinceEpoch() - this->startTime;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
currTime = this->stoppedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(currTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SpeedTimer::getText() {
|
|
||||||
//qDebug() << this->getState();
|
|
||||||
QString newText;
|
|
||||||
switch (this->state) {
|
|
||||||
case SpeedTimer::IDLE:
|
|
||||||
newText = "0.000 sec";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::STARTING:
|
|
||||||
newText = "0.000 sec";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::WAITING:
|
|
||||||
newText = "please wait...";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::RUNNING:
|
|
||||||
newText = QString::number( this->getCurrTime() / 1000.0, 'f', 3 ) + " sec";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::WON:
|
|
||||||
newText = QString::number( this->stoppedTime / 1000.0, 'f', 3 ) + " sec";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::LOST:
|
|
||||||
newText = QString::number( this->stoppedTime / 1000.0, 'f', 3 ) + " sec";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::FAILED:
|
|
||||||
newText = "false start";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::CANCELLED:
|
|
||||||
newText = "cancelled";
|
|
||||||
break;
|
|
||||||
case SpeedTimer::DISABLED:
|
|
||||||
newText = "---";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newText;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpeedTimer::delay(int mSecs){
|
|
||||||
QEventLoop loop;
|
|
||||||
QTimer timer;
|
|
||||||
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
// quit the loop when the timer times out
|
|
||||||
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
||||||
//quit the loop when the connection was established
|
|
||||||
// start the timer before starting to connect
|
|
||||||
timer.start(mSecs);
|
|
||||||
//connect
|
|
||||||
|
|
||||||
//wait for the connection to finish (programm gets stuck in here)
|
|
||||||
loop.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeedTimer::timerState SpeedTimer::stateFromString(QString state){
|
|
||||||
|
|
||||||
if(state == "IDLE"){
|
|
||||||
return IDLE;
|
|
||||||
}
|
|
||||||
else if (state == "STARTING") {
|
|
||||||
return STARTING;
|
|
||||||
}
|
|
||||||
else if (state == "RUNNING") {
|
|
||||||
return RUNNING;
|
|
||||||
}
|
|
||||||
else if (state == "WON") {
|
|
||||||
return WON;
|
|
||||||
}
|
|
||||||
else if (state == "LOST") {
|
|
||||||
return LOST;
|
|
||||||
}
|
|
||||||
else if (state == "FAILED") {
|
|
||||||
return FAILED;
|
|
||||||
}
|
|
||||||
else if(state == "DISABLED") {
|
|
||||||
return DISABLED;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return CANCELLED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
|
||||||
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, version 3 of the License.
|
|
||||||
|
|
||||||
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 Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "headers/sqlprofilemodel.h"
|
|
||||||
|
|
||||||
static void createTable()
|
|
||||||
{
|
|
||||||
if (QSqlDatabase::database().tables().contains(QStringLiteral("Contacts"))) {
|
|
||||||
// The table already exists; we don't need to do anything.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSqlQuery query;
|
|
||||||
|
|
||||||
//creat eth etable to store the profiles
|
|
||||||
if (!query.exec(
|
|
||||||
"CREATE TABLE IF NOT EXISTS `profiles` ( "
|
|
||||||
" `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,"
|
|
||||||
" `name` TEXT NOT NULL "
|
|
||||||
" );")) {
|
|
||||||
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
|
|
||||||
}
|
|
||||||
|
|
||||||
//create the table to store the times
|
|
||||||
if (!query.exec(
|
|
||||||
"CREATE TABLE IF NOT EXISTS `times` ("
|
|
||||||
" `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,"
|
|
||||||
" `profileid` INTEGER NOT NULL,"
|
|
||||||
" `time` INTEGER NOT NULL, "
|
|
||||||
" `timestamp` INTEGER NOT NULL"
|
|
||||||
" );")) {
|
|
||||||
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SqlProfileModel::SqlProfileModel(QObject *parent) : QSqlTableModel(parent)
|
|
||||||
{
|
|
||||||
qDebug("ProfileModel constructor");
|
|
||||||
createTable();
|
|
||||||
setTable("profiles");
|
|
||||||
setEditStrategy(QSqlTableModel::OnManualSubmit);
|
|
||||||
select();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant SqlProfileModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (role < Qt::UserRole)
|
|
||||||
return QSqlTableModel::data(index, role);
|
|
||||||
|
|
||||||
const QSqlRecord sqlRecord = record(index.row());
|
|
||||||
return sqlRecord.value(role - Qt::UserRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> SqlProfileModel::roleNames() const
|
|
||||||
{
|
|
||||||
QHash<int, QByteArray> names;
|
|
||||||
names[Qt::UserRole + 0] = "id";
|
|
||||||
names[Qt::UserRole + 1] = "name";
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SqlProfileModel::append(QString name)
|
|
||||||
{
|
|
||||||
qDebug() << name;
|
|
||||||
QSqlRecord newRecord = record();
|
|
||||||
newRecord.setValue("name", name);
|
|
||||||
|
|
||||||
if (!insertRecord(rowCount(), newRecord)) {
|
|
||||||
qWarning() << "Failed to add profile:" << lastError().text();
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
submitAll();
|
|
||||||
return(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SqlProfileModel::remove(int row)
|
|
||||||
{
|
|
||||||
removeRows(row, 1);
|
|
||||||
submitAll();
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
|
||||||
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published
|
|
||||||
by the Free Software Foundation, version 3 of the License.
|
|
||||||
|
|
||||||
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 Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "headers/sqlstoragemodel.h"
|
|
||||||
|
|
||||||
SqlStorageModel::SqlStorageModel(QObject *parent) : QSqlTableModel(parent)
|
|
||||||
{
|
|
||||||
qDebug("ProfileModel constructor");
|
|
||||||
setTable("times");
|
|
||||||
select();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant SqlStorageModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (role < Qt::UserRole)
|
|
||||||
return QSqlTableModel::data(index, role);
|
|
||||||
|
|
||||||
const QSqlRecord sqlRecord = record(index.row());
|
|
||||||
return sqlRecord.value(role - Qt::UserRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> SqlStorageModel::roleNames() const
|
|
||||||
{
|
|
||||||
QHash<int, QByteArray> names;
|
|
||||||
names[Qt::UserRole + 0] = "id";
|
|
||||||
names[Qt::UserRole + 1] = "name";
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
|