/**************************************************************************** ** 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 . ****************************************************************************/ 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 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 } ScStwMonitorBackend { id: backend scStwClient.ipAddress: appSettings.baseStationIp } ScStwAppThemeManager { id: appTheme Component.onCompleted: { appTheme.setTheme("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: 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 timers: backend.race.timers colors: appTheme.theme.colors fontName: appTheme.theme.fonts.timers 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 ? appTheme.theme.colors.warning:appTheme.theme.colors.text 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 } } 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(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 BusyIndicator { 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 } } } } } }