418 lines
14 KiB
QML
418 lines
14 KiB
QML
|
import QtQuick 2.9
|
||
|
import QtQuick.Window 2.2
|
||
|
import QtQuick.Controls 2.2
|
||
|
import QtGraphicalEffects 1.0
|
||
|
import Qt.labs.settings 1.0
|
||
|
import QtQuick.Layouts 1.0
|
||
|
|
||
|
import de.itsblue.ScStw 2.0
|
||
|
import de.itsblue.ScStwMonitor 2.0
|
||
|
|
||
|
Window {
|
||
|
id: window
|
||
|
visible: true
|
||
|
width: XscreenWidth / 2
|
||
|
height: XscreenHeight / 2
|
||
|
title: qsTr("ScStwMonitor")
|
||
|
|
||
|
//visibility: Window.FullScreen
|
||
|
|
||
|
Page {
|
||
|
id: app
|
||
|
anchors.fill: parent
|
||
|
|
||
|
function landscape() {
|
||
|
return app.width > app.height
|
||
|
}
|
||
|
|
||
|
ScStwMonitorBackend {
|
||
|
id: backend
|
||
|
scStwClient.ipAddress: appSettings.baseStationIp
|
||
|
}
|
||
|
|
||
|
Settings {
|
||
|
id: appSettings
|
||
|
property string baseStationIp: "192.168.4.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
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FontLoader {
|
||
|
id: timerFont
|
||
|
source:"qrc:///fonts/PTMono-Regular.ttf"
|
||
|
}
|
||
|
|
||
|
Loader {
|
||
|
id: mainComponentLoader
|
||
|
|
||
|
anchors.fill: parent
|
||
|
|
||
|
sourceComponent: backend.scStwClient.state === ScStwClient.CONNECTED ? displayComp:loadingComp
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: displayComp
|
||
|
|
||
|
Item {
|
||
|
id: displayItm
|
||
|
|
||
|
anchors.fill: parent
|
||
|
|
||
|
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
|
||
|
|
||
|
opacity: !showControls || [ScStwRace.IDLE,ScStwRace.STARTING].indexOf(backend.race.state) < 0 ? 1:0
|
||
|
|
||
|
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"
|
||
|
][backend.race.state]
|
||
|
|
||
|
anchors.fill: parent
|
||
|
|
||
|
verticalAlignment: Text.AlignVCenter
|
||
|
horizontalAlignment: Text.AlignHCenter
|
||
|
|
||
|
fontSizeMode: Text.Fit
|
||
|
|
||
|
font.pixelSize: height * 0.3
|
||
|
|
||
|
color: backend.race.state === ScStwRace.STARTING ? "#e0b928":"grey"
|
||
|
|
||
|
text: implicitText === "NEXT_START_ACTION" ? ["", "at your \nmarks", "ready", "starting..."][backend.race.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: backend.race.nextStartActionDetails[ScStwRace.NextStartAction] < 3 && backend.race.state === ScStwRace.STARTING ? 1:0
|
||
|
|
||
|
value: backend.race.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 (backend.race.state) {
|
||
|
case ScStwRace.IDLE:
|
||
|
// IDLE
|
||
|
backend.race.start()
|
||
|
break;
|
||
|
case ScStwRace.STARTING:
|
||
|
// STARTING
|
||
|
backend.race.cancel()
|
||
|
break;
|
||
|
case ScStwRace.WAITING:
|
||
|
// WAITING
|
||
|
break;
|
||
|
case ScStwRace.RUNNING:
|
||
|
// RUNNING
|
||
|
backend.race.stop()
|
||
|
break;
|
||
|
case ScStwRace.STOPPED:
|
||
|
// STOPPED
|
||
|
backend.race.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: backend.race.state === ScStwRace.IDLE ? 1:0
|
||
|
visible: opacity > 0
|
||
|
|
||
|
Behavior on opacity {
|
||
|
NumberAnimation {
|
||
|
duration: 200
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Image {
|
||
|
Layout.preferredHeight: parent.height * 0.5
|
||
|
Layout.preferredWidth: height * 0.7
|
||
|
Layout.alignment: Layout.Center
|
||
|
|
||
|
mipmap: true
|
||
|
|
||
|
fillMode: Image.PreserveAspectFit
|
||
|
source: "qrc:/VolumeLow.png"
|
||
|
}
|
||
|
|
||
|
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"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Image {
|
||
|
Layout.preferredHeight: parent.height * 0.5
|
||
|
Layout.preferredWidth: height
|
||
|
Layout.alignment: Layout.Center
|
||
|
|
||
|
mipmap: true
|
||
|
fillMode: Image.PreserveAspectFit
|
||
|
source: "qrc:/VolumeHigh.png"
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
states: [
|
||
|
State {
|
||
|
when: [ScStwRace.IDLE,ScStwRace.STARTING].indexOf(backend.race.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(backend.race.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
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Component {
|
||
|
id: loadingComp
|
||
|
|
||
|
Item {
|
||
|
id: loadingItm
|
||
|
|
||
|
anchors.fill: parent
|
||
|
|
||
|
FancyBusyIndicator {
|
||
|
id: loadingInd
|
||
|
anchors.centerIn: parent
|
||
|
|
||
|
width: app.landscape() ? parent.height * 0.2 : parent.width *0.2
|
||
|
height: width
|
||
|
}
|
||
|
|
||
|
TextField {
|
||
|
id: ipAddrInputTf
|
||
|
|
||
|
anchors {
|
||
|
bottom: parent.bottom
|
||
|
horizontalCenter: parent.horizontalCenter
|
||
|
}
|
||
|
|
||
|
opacity: backend.scStwClient.state === ScStwClient.CONNECTED ? 0:1
|
||
|
|
||
|
text: appSettings.baseStationIp
|
||
|
|
||
|
onEditingFinished: {
|
||
|
appSettings.baseStationIp = text
|
||
|
backend.scStwClient.ipAddress = text
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|