2020-06-07 14:52:07 +02:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * ScStw Monitor
* * Copyright ( C ) 2020 Itsblue development
* *
* * This program is free software: you can redistribute it and / or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation , either version 3 of the License , or
* * ( at your option ) any later version .
* *
* * This program is distributed in the hope that it will be useful ,
* * but WITHOUT ANY WARRANTY ; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* * GNU General Public License for more details .
* *
* * You should have received a copy of the GNU General Public License
* * along with this program . If not , see < http: //www.gnu.org/licenses/>.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2020-05-22 19:32:57 +02:00
import QtQuick 2.9
import QtQuick . Window 2.2
import QtQuick . Controls 2.2
import Qt . labs . settings 1.0
import QtQuick . Layouts 1.0
import de . itsblue . ScStw 2.0
2020-05-26 17:17:33 +02:00
import de . itsblue . ScStw . Styling 2.0
2020-05-26 17:52:31 +02:00
import de . itsblue . ScStw . Styling . Components 1.0
2020-05-22 19:32:57 +02:00
Window {
id: window
visible: true
2020-06-11 12:28:05 +02:00
width: XscreenWidth
height: XscreenHeight
2020-05-22 19:32:57 +02:00
title: qsTr ( "ScStwMonitor" )
2020-06-11 12:28:05 +02:00
visibility: Window . FullScreen
2020-05-22 19:32:57 +02:00
Page {
id: app
anchors.fill: parent
2020-05-26 17:17:33 +02:00
background: Rectangle {
color: appTheme . theme . colors . background
}
2020-05-22 19:32:57 +02:00
function landscape ( ) {
return app . width > app . height
}
2021-01-05 20:47:17 +01:00
ScStwClient {
id: scStwClient
ipAddress: appSettings . baseStationIp
}
Timer {
running: scStwClient . state !== ScStwClient . Connected
interval: 10
onTriggered: scStwClient . connectToHost ( )
}
ScStwRemoteRace {
id: scStwRemoteRace
autoRefreshTimerText: true
scStwClient: scStwClient
onStateChanged: {
if ( scStwRemoteRace . state !== ScStwRace . IDLE ) {
settingsDialog . close ( )
profilesDialog . close ( )
}
}
2020-05-22 19:32:57 +02:00
}
2020-05-26 17:17:33 +02:00
ScStwAppThemeManager {
id: appTheme
Component.onCompleted: {
2020-06-14 18:16:31 +02:00
appTheme . setTheme ( darkMode ? "Dark" : "Light" )
2020-05-26 17:17:33 +02:00
}
}
2020-05-22 19:32:57 +02:00
Settings {
id: appSettings
2020-06-11 12:28:05 +02:00
property string baseStationIp: "127.0.0.1"
2020-05-22 19:32:57 +02:00
}
Shortcut {
sequences: [ "Ctrl+Q" , StandardKey . Back ]
onActivated: Qt . quit ( )
}
Shortcut {
sequences: [ "F11" , "Esc" ]
onActivated: {
if ( window . visibility === Window . FullScreen ) {
window . visibility = Window . Windowed
}
else {
window . visibility = Window . FullScreen
}
}
}
Loader {
id: mainComponentLoader
anchors.fill: parent
2021-01-05 20:47:17 +01:00
sourceComponent: scStwClient . state === ScStwClient . CONNECTED ? displayComp: loadingComp
2020-05-22 19:32:57 +02:00
}
Component {
id: displayComp
2020-06-14 18:16:31 +02:00
Column {
Rectangle {
id: topBanner
width: parent . width
2021-01-05 20:47:17 +01:00
height: ! showControls && showBanners ? scStwRemoteRace . state === ScStwRace . IDLE ? parent . height * 0.1 : parent . height * 0.05 : 0
2020-06-14 18:16:31 +02:00
color: "black"
Text {
anchors.fill: parent
horizontalAlignment: Text . AlignHCenter
verticalAlignment: Text . AlignVCenter
font.pixelSize: height * 0.5
visible: height > 0
color: "white"
text: qsTr ( "Speed Climbing Stopwatch" )
}
Behavior on height {
NumberAnimation {
duration: 200
easing.type: Easing . Linear
}
}
}
2020-05-22 19:32:57 +02:00
Item {
id: displayItm
2020-06-14 18:16:31 +02:00
width: parent . width
height: parent . height - bottomBanner . height - topBanner . height
2020-05-22 19:32:57 +02:00
Image {
id: bannerImg
anchors {
top: parent . top
left: parent . left
right: parent . right
margins: app . landscape ( ) ? app . height * 0.01 : app . width * 0.1
}
height: app . landscape ( ) ? app . height * 0.25 : app . height * 0.2
visible: showControls
fillMode: Image . PreserveAspectFit
mipmap: true
source: "qrc:/Banner.png"
}
TimerColumn {
anchors.fill: parent
2021-01-05 20:47:17 +01:00
timers: removeDisabledTimers ( scStwRemoteRace . timers )
2020-06-14 18:16:31 +02:00
2020-05-26 17:17:33 +02:00
colors: appTheme . theme . colors
fontName: appTheme . theme . fonts . timers
2021-01-05 20:47:17 +01:00
opacity: ! showControls || [ ScStwRace . IDLE , ScStwRace . STARTING ] . indexOf ( scStwRemoteRace . state ) < 0 ? 1 : 0
2020-05-22 19:32:57 +02:00
2020-06-14 18:16:31 +02:00
function removeDisabledTimers ( timers ) {
var ret = [ ]
for ( var i = 0 ; i < timers . length ; i ++ ) {
if ( timers [ i ] [ "state" ] !== ScStwTimer . DISABLED )
ret . push ( timers [ i ] )
}
return ret
}
2020-05-22 19:32:57 +02:00
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
}
Item {
id: controlsItm
anchors {
left: parent . left
right: parent . right
bottom: parent . bottom
}
visible: showControls
Label {
id: clickHintLabel
property string implicitText: [
"tap anywhere\nto start" ,
"NEXT_START_ACTION" ,
"please wait..." ,
"running\ntap anywhere to stop" ,
"tap anywhere to reset"
2021-01-05 20:47:17 +01:00
] [ scStwRemoteRace . state ]
2020-05-22 19:32:57 +02:00
anchors.fill: parent
verticalAlignment: Text . AlignVCenter
horizontalAlignment: Text . AlignHCenter
fontSizeMode: Text . Fit
font.pixelSize: height * 0.3
2021-01-05 20:47:17 +01:00
color: scStwRemoteRace . state === ScStwRace . STARTING ? appTheme.theme.colors.warning: appTheme . theme . colors . text
2020-05-22 19:32:57 +02:00
2021-01-05 20:47:17 +01:00
text: implicitText === "NEXT_START_ACTION" ? [ "" , "at your \nmarks" , "ready" , "starting..." ] [ scStwRemoteRace . nextStartActionDetails [ ScStwRace . NextStartAction ] + 1 ] : implicitText
2020-05-22 19:32:57 +02:00
Behavior on text {
FadeAnimation {
target: clickHintLabel
fadeDuration: 150
}
}
}
ProgressBar {
id: nextActiondelayProgressBar
anchors {
left: parent . left
right: parent . right
bottom: parent . bottom
}
height: app . landscape ( ) ? app . height * 0.1 : app . width * 0.1
2021-01-05 20:47:17 +01:00
opacity: scStwRemoteRace . nextStartActionDetails [ ScStwRace . NextStartAction ] < 3 && scStwRemoteRace . state === ScStwRace . STARTING ? 1 : 0
2020-05-22 19:32:57 +02:00
2021-01-05 20:47:17 +01:00
value: scStwRemoteRace . nextStartActionDetails [ ScStwRace . NextStartActionDelayProgress ]
2020-05-22 19:32:57 +02:00
background: Rectangle {
implicitWidth: 200
implicitHeight: parent . height
color: "lightgrey"
}
contentItem: Item {
implicitWidth: 200
implicitHeight: parent . height
Rectangle {
width: nextActiondelayProgressBar . visualPosition * parent . width
height: parent . height
color: "grey"
}
}
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
}
MouseArea {
parent: app
anchors.fill: parent
visible: controlsItm . visible
enabled: visible
onClicked: {
2021-01-05 20:47:17 +01:00
switch ( scStwRemoteRace . state ) {
2020-05-22 19:32:57 +02:00
case ScStwRace.IDLE:
// IDLE
2021-01-05 20:47:17 +01:00
scStwRemoteRace . start ( )
2020-05-22 19:32:57 +02:00
break ;
case ScStwRace.STARTING:
// STARTING
2021-01-05 20:47:17 +01:00
scStwRemoteRace . cancel ( )
2020-05-22 19:32:57 +02:00
break ;
case ScStwRace.WAITING:
// WAITING
break ;
case ScStwRace.RUNNING:
// RUNNING
2021-01-05 20:47:17 +01:00
scStwRemoteRace . stop ( )
2020-05-22 19:32:57 +02:00
break ;
case ScStwRace.STOPPED:
// STOPPED
2021-01-05 20:47:17 +01:00
scStwRemoteRace . reset ( )
2020-05-22 19:32:57 +02:00
break ;
}
}
RowLayout {
id: volumeSliderRow
anchors {
left: parent . left
right: parent . right
bottom: parent . bottom
margins: 5
}
height: app . landscape ( ) ? app . height * 0.1 : app . width * 0.12
spacing: 0
2021-01-05 20:47:17 +01:00
opacity: scStwRemoteRace . state === ScStwRace . IDLE ? 1 : 0
2020-05-22 19:32:57 +02:00
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
2020-05-26 17:17:33 +02:00
Icon {
Layout.preferredHeight: parent . height * 0.7
2020-05-22 19:32:57 +02:00
Layout.preferredWidth: height * 0.7
Layout.alignment: Layout . Center
2020-05-26 17:17:33 +02:00
fontName: appTheme . theme . fonts . icons
icon: appTheme . theme . icons . volumeDown
color: appTheme . theme . colors . text
2020-05-22 19:32:57 +02:00
}
Slider {
id: volumeSlider
Layout.fillHeight: true
Layout.fillWidth: true
implicitWidth: 200
value: parseFloat ( backend . scStwClient . readRemoteSetting ( ScStw . SoundVolumeSetting ) )
onPressedChanged: {
if ( ! pressed ) {
volumeSlider . enabled = false
backend . scStwClient . writeRemoteSetting ( ScStw . SoundVolumeSetting , value )
volumeSlider . enabled = true
}
}
leftPadding: width * ( app . landscape ( ) ? 0.02 : 0.05 )
rightPadding: leftPadding
background: Rectangle {
x: volumeSlider . leftPadding
y: volumeSlider . topPadding + volumeSlider . availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: volumeSliderRow . height * 0.1
width: volumeSlider . availableWidth
height: implicitHeight
radius: 2
color: "#bdbebf"
Rectangle {
width: volumeSlider . visualPosition * parent . width
height: parent . height
color: "grey"
radius: 2
}
}
handle: Item {
x: volumeSlider . leftPadding - width * 0.15 + volumeSlider . visualPosition * ( volumeSlider . availableWidth - width * 0.55 )
y: volumeSlider . topPadding + volumeSlider . availableHeight / 2 - height / 2
implicitWidth: volumeSliderRow . height
implicitHeight: implicitWidth
Image {
anchors.fill: parent
fillMode: Image . PreserveAspectFit
source: "qrc:/SpeedHold.png"
}
}
}
2020-05-26 17:17:33 +02:00
Icon {
Layout.preferredHeight: parent . height * 0.7
Layout.preferredWidth: height * 0.7
2020-05-22 19:32:57 +02:00
Layout.alignment: Layout . Center
2020-05-26 17:17:33 +02:00
fontName: appTheme . theme . fonts . icons
icon: appTheme . theme . icons . volumeUp
color: appTheme . theme . colors . text
2020-05-22 19:32:57 +02:00
}
}
}
states: [
State {
2021-01-05 20:47:17 +01:00
when: [ ScStwRace . IDLE , ScStwRace . STARTING ] . indexOf ( scStwRemoteRace . state ) >= 0
2020-05-22 19:32:57 +02:00
name: "big"
PropertyChanges {
target: clickHintLabel
anchors.margins: app . landscape ( ) ? app . height * 0.2 : app . width * 0.1
width: parent . width * 0.7
height: parent . height * 0.7
}
PropertyChanges {
target: controlsItm
height: app . height
}
} ,
State {
2021-01-05 20:47:17 +01:00
when: [ ScStwRace . IDLE , ScStwRace . STARTING ] . indexOf ( scStwRemoteRace . state ) < 0
2020-05-22 19:32:57 +02:00
name: "small"
PropertyChanges {
target: clickHintLabel
anchors.margins: app . landscape ( ) ? app . height * 0.01 : app . width * 0.1
}
PropertyChanges {
target: controlsItm
height: app . landscape ( ) ? app . height * 0.2 : app . height * 0.4
}
}
]
transitions: [
Transition {
from: "*"
to: "*"
PauseAnimation {
duration: 150
}
}
]
}
}
2020-06-14 18:16:31 +02:00
Rectangle {
id: bottomBanner
width: parent . width
2021-01-05 20:47:17 +01:00
height: ! showControls && showBanners ? scStwRemoteRace . state === ScStwRace . IDLE ? parent . height * 0.1 : parent . height * 0.05 : 0
2020-06-14 18:16:31 +02:00
color: "black"
Text {
anchors.fill: parent
horizontalAlignment: Text . AlignHCenter
verticalAlignment: Text . AlignVCenter
font.pixelSize: height * 0.5
visible: height > 0
color: "white"
text: qsTr ( "www.itsblue.de/scstw" )
}
Behavior on height {
NumberAnimation {
duration: 200
easing.type: Easing . Linear
}
}
}
}
2020-05-22 19:32:57 +02:00
}
Component {
id: loadingComp
Item {
id: loadingItm
anchors.fill: parent
2020-05-26 17:17:33 +02:00
BusyIndicator {
2020-05-22 19:32:57 +02:00
id: loadingInd
anchors.centerIn: parent
2021-04-09 18:01:31 +02:00
visible: ! raspi
2020-05-22 19:32:57 +02:00
width: app . landscape ( ) ? parent . height * 0.2 : parent . width * 0.2
height: width
2020-06-13 15:58:24 +02:00
image: appTheme . theme . images . SpeedHold
2020-05-22 19:32:57 +02:00
}
2020-06-14 18:16:31 +02:00
MouseArea {
id: textFieldEnableMa
hoverEnabled: true
anchors.fill: parent
anchors.leftMargin: parent . width * 0.9
}
2020-05-22 19:32:57 +02:00
TextField {
id: ipAddrInputTf
anchors {
bottom: parent . bottom
horizontalCenter: parent . horizontalCenter
}
2021-04-08 19:05:37 +02:00
visible: ! textFieldEnableMa . containsMouse && ! raspi
2020-06-14 18:16:31 +02:00
2021-01-05 20:47:17 +01:00
opacity: scStwClient . state === ScStwClient . CONNECTED ? 0 : 1
2020-05-22 19:32:57 +02:00
text: appSettings . baseStationIp
onEditingFinished: {
appSettings . baseStationIp = text
backend . scStwClient . ipAddress = text
}
}
}
}
}
}