monitor/resources/qml/main.qml

554 lines
18 KiB
QML
Executable File

/****************************************************************************
** 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/>.
****************************************************************************/
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
import de.itsblue.ScStw.Styling 2.0
import de.itsblue.ScStw.Styling.Components 1.0
Window {
id: window
visible: true
width: XscreenWidth
height: XscreenHeight
title: qsTr("ScStwMonitor")
visibility: Window.FullScreen
Page {
id: app
anchors.fill: parent
background: Rectangle {
color: appTheme.theme.colors.background
}
function landscape() {
return app.width > app.height
}
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()
}
}
}
ScStwAppThemeManager {
id: appTheme
Component.onCompleted: {
appTheme.setTheme(darkMode ? "Dark":"Light")
}
}
Settings {
id: appSettings
property string baseStationIp: "127.0.0.1"
}
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
sourceComponent: scStwClient.state === ScStwClient.CONNECTED ? displayComp:loadingComp
}
Component {
id: displayComp
Column {
Rectangle {
id: topBanner
width: parent.width
height: !showControls && showBanners ? scStwRemoteRace.state === ScStwRace.IDLE ? parent.height * 0.1:parent.height * 0.05 : 0
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
}
}
}
Item {
id: displayItm
width: parent.width
height: parent.height - bottomBanner.height - topBanner.height
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
timers: removeDisabledTimers(scStwRemoteRace.timers)
colors: appTheme.theme.colors
fontName: appTheme.theme.fonts.timers
opacity: !showControls || [ScStwRace.IDLE,ScStwRace.STARTING].indexOf(scStwRemoteRace.state) < 0 ? 1:0
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
}
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"
][scStwRemoteRace.state]
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
fontSizeMode: Text.Fit
font.pixelSize: height * 0.3
color: scStwRemoteRace.state === ScStwRace.STARTING ? appTheme.theme.colors.warning:appTheme.theme.colors.text
text: implicitText === "NEXT_START_ACTION" ? ["", "at your \nmarks", "ready", "starting..."][scStwRemoteRace.nextStartActionDetails[ScStwRace.NextStartAction]+1]:implicitText
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
opacity: scStwRemoteRace.nextStartActionDetails[ScStwRace.NextStartAction] < 3 && scStwRemoteRace.state === ScStwRace.STARTING ? 1:0
value: scStwRemoteRace.nextStartActionDetails[ScStwRace.NextStartActionDelayProgress]
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: {
switch (scStwRemoteRace.state) {
case ScStwRace.IDLE:
// IDLE
scStwRemoteRace.start()
break;
case ScStwRace.STARTING:
// STARTING
scStwRemoteRace.cancel()
break;
case ScStwRace.WAITING:
// WAITING
break;
case ScStwRace.RUNNING:
// RUNNING
scStwRemoteRace.stop()
break;
case ScStwRace.STOPPED:
// STOPPED
scStwRemoteRace.reset()
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
opacity: scStwRemoteRace.state === ScStwRace.IDLE ? 1:0
visible: opacity > 0
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
Icon {
Layout.preferredHeight: parent.height * 0.7
Layout.preferredWidth: height * 0.7
Layout.alignment: Layout.Center
fontName: appTheme.theme.fonts.icons
icon: appTheme.theme.icons.volumeDown
color: appTheme.theme.colors.text
}
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"
}
}
}
Icon {
Layout.preferredHeight: parent.height * 0.7
Layout.preferredWidth: height * 0.7
Layout.alignment: Layout.Center
fontName: appTheme.theme.fonts.icons
icon: appTheme.theme.icons.volumeUp
color: appTheme.theme.colors.text
}
}
}
states: [
State {
when: [ScStwRace.IDLE,ScStwRace.STARTING].indexOf(scStwRemoteRace.state) >= 0
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 {
when: [ScStwRace.IDLE,ScStwRace.STARTING].indexOf(scStwRemoteRace.state) < 0
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
}
}
]
}
}
Rectangle {
id: bottomBanner
width: parent.width
height: !showControls && showBanners ? scStwRemoteRace.state === ScStwRace.IDLE ? parent.height * 0.1:parent.height * 0.05 : 0
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
}
}
}
}
}
Component {
id: loadingComp
Item {
id: loadingItm
anchors.fill: parent
BusyIndicator {
id: loadingInd
anchors.centerIn: parent
width: app.landscape() ? parent.height * 0.2 : parent.width *0.2
height: width
image: appTheme.theme.images.SpeedHold
}
MouseArea {
id: textFieldEnableMa
hoverEnabled: true
anchors.fill: parent
anchors.leftMargin: parent.width * 0.9
}
TextField {
id: ipAddrInputTf
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
visible: !textFieldEnableMa.containsMouse && !raspi
opacity: scStwClient.state === ScStwClient.CONNECTED ? 0:1
text: appSettings.baseStationIp
onEditingFinished: {
appSettings.baseStationIp = text
backend.scStwClient.ipAddress = text
}
}
}
}
}
}