From ee20ace8caf6066f2099e5e7d60d373f3f0dedbc Mon Sep 17 00:00:00 2001 From: Ivan Solovev Date: Fri, 24 Mar 2023 15:10:22 +0100 Subject: HeartRate Game: QML revamp This patch implements proper QML module registration for both CMake and qmake. It also tries to address all qmllint warnings, which results in a major refactoring, because the current approach with Loaders provides a lot of unqualified access warnings. The new approach uses StackLayout to switch between different pages. Task-number: QTBUG-111972 Change-Id: Icc0122deebe63af16dd53c47690f2dc9fb88c919 Reviewed-by: Juha Vuolle Reviewed-by: Qt CI Bot (cherry picked from commit 007bf2bcf10007b02dd5fe09c0fc4be24229af14) Reviewed-by: Qt Cherry-pick Bot --- examples/bluetooth/heartrate-game/App.qml | 98 ++++++++++ .../heartrate-game/BluetoothAlarmDialog.qml | 75 ++++++++ examples/bluetooth/heartrate-game/BottomLine.qml | 12 ++ examples/bluetooth/heartrate-game/CMakeLists.txt | 74 +++---- examples/bluetooth/heartrate-game/Connect.qml | 159 ++++++++++++++++ examples/bluetooth/heartrate-game/GameButton.qml | 39 ++++ examples/bluetooth/heartrate-game/GamePage.qml | 36 ++++ examples/bluetooth/heartrate-game/GameSettings.qml | 51 +++++ examples/bluetooth/heartrate-game/Main.qml | 71 +++++++ examples/bluetooth/heartrate-game/Measure.qml | 212 +++++++++++++++++++++ examples/bluetooth/heartrate-game/SplashScreen.qml | 30 +++ examples/bluetooth/heartrate-game/Stats.qml | 55 ++++++ examples/bluetooth/heartrate-game/StatsLabel.qml | 34 ++++ examples/bluetooth/heartrate-game/TitleBar.qml | 54 ++++++ examples/bluetooth/heartrate-game/devicehandler.h | 5 +- .../bluetooth/heartrate-game/heartrate-game.pro | 32 +++- examples/bluetooth/heartrate-game/images.qrc | 7 - .../heartrate-game/images/bt_off_to_on.png | Bin 0 -> 6143 bytes examples/bluetooth/heartrate-game/images/heart.png | Bin 0 -> 2664 bytes examples/bluetooth/heartrate-game/images/logo.png | Bin 0 -> 31915 bytes examples/bluetooth/heartrate-game/main.cpp | 6 +- examples/bluetooth/heartrate-game/qml.qrc | 18 -- examples/bluetooth/heartrate-game/qml/App.qml | 83 -------- .../heartrate-game/qml/BluetoothAlarmDialog.qml | 75 -------- .../bluetooth/heartrate-game/qml/BottomLine.qml | 12 -- examples/bluetooth/heartrate-game/qml/Connect.qml | 141 -------------- .../bluetooth/heartrate-game/qml/GameButton.qml | 41 ---- examples/bluetooth/heartrate-game/qml/GamePage.qml | 46 ----- .../bluetooth/heartrate-game/qml/GameSettings.qml | 54 ------ examples/bluetooth/heartrate-game/qml/Measure.qml | 197 ------------------- .../bluetooth/heartrate-game/qml/SplashScreen.qml | 43 ----- examples/bluetooth/heartrate-game/qml/Stats.qml | 52 ----- .../bluetooth/heartrate-game/qml/StatsLabel.qml | 35 ---- examples/bluetooth/heartrate-game/qml/TitleBar.qml | 50 ----- .../heartrate-game/qml/images/bt_off_to_on.png | Bin 6143 -> 0 bytes .../bluetooth/heartrate-game/qml/images/heart.png | Bin 2664 -> 0 bytes .../bluetooth/heartrate-game/qml/images/logo.png | Bin 31915 -> 0 bytes examples/bluetooth/heartrate-game/qml/main.qml | 63 ------ examples/bluetooth/heartrate-game/qml/qmldir | 1 - examples/bluetooth/heartrate-game/qmldir | 15 ++ 40 files changed, 999 insertions(+), 977 deletions(-) create mode 100644 examples/bluetooth/heartrate-game/App.qml create mode 100644 examples/bluetooth/heartrate-game/BluetoothAlarmDialog.qml create mode 100644 examples/bluetooth/heartrate-game/BottomLine.qml create mode 100644 examples/bluetooth/heartrate-game/Connect.qml create mode 100644 examples/bluetooth/heartrate-game/GameButton.qml create mode 100644 examples/bluetooth/heartrate-game/GamePage.qml create mode 100644 examples/bluetooth/heartrate-game/GameSettings.qml create mode 100644 examples/bluetooth/heartrate-game/Main.qml create mode 100644 examples/bluetooth/heartrate-game/Measure.qml create mode 100644 examples/bluetooth/heartrate-game/SplashScreen.qml create mode 100644 examples/bluetooth/heartrate-game/Stats.qml create mode 100644 examples/bluetooth/heartrate-game/StatsLabel.qml create mode 100644 examples/bluetooth/heartrate-game/TitleBar.qml delete mode 100644 examples/bluetooth/heartrate-game/images.qrc create mode 100644 examples/bluetooth/heartrate-game/images/bt_off_to_on.png create mode 100644 examples/bluetooth/heartrate-game/images/heart.png create mode 100644 examples/bluetooth/heartrate-game/images/logo.png delete mode 100644 examples/bluetooth/heartrate-game/qml.qrc delete mode 100644 examples/bluetooth/heartrate-game/qml/App.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/BottomLine.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/Connect.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/GameButton.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/GamePage.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/GameSettings.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/Measure.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/SplashScreen.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/Stats.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/StatsLabel.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/TitleBar.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.png delete mode 100644 examples/bluetooth/heartrate-game/qml/images/heart.png delete mode 100644 examples/bluetooth/heartrate-game/qml/images/logo.png delete mode 100644 examples/bluetooth/heartrate-game/qml/main.qml delete mode 100644 examples/bluetooth/heartrate-game/qml/qmldir create mode 100644 examples/bluetooth/heartrate-game/qmldir diff --git a/examples/bluetooth/heartrate-game/App.qml b/examples/bluetooth/heartrate-game/App.qml new file mode 100644 index 00000000..98633c98 --- /dev/null +++ b/examples/bluetooth/heartrate-game/App.qml @@ -0,0 +1,98 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts +import HeartRateGame + +Item { + id: app + + required property ConnectionHandler connectionHandler + required property DeviceFinder deviceFinder + required property DeviceHandler deviceHandler + + anchors.fill: parent + opacity: 0.0 + + Behavior on opacity { + NumberAnimation { + duration: 500 + } + } + + property int __currentIndex: 0 + + TitleBar { + id: titleBar + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + currentIndex: app.__currentIndex + + onTitleClicked: (index) => { + if (index < app.__currentIndex) + app.__currentIndex = index + } + } + + StackLayout { + id: pageStack + anchors.left: parent.left + anchors.right: parent.right + anchors.top: titleBar.bottom + anchors.bottom: parent.bottom + currentIndex: app.__currentIndex + + Connect { + connectionHandler: app.connectionHandler + deviceFinder: app.deviceFinder + deviceHandler: app.deviceHandler + + onShowMeasurePage: app.__currentIndex = 1 + } + Measure { + id: measurePage + deviceHandler: app.deviceHandler + + onShowStatsPage: app.__currentIndex = 2 + } + Stats { + deviceHandler: app.deviceHandler + } + + onCurrentIndexChanged: { + if (currentIndex === 0) + measurePage.close() + } + } + + BluetoothAlarmDialog { + id: btAlarmDialog + anchors.fill: parent + visible: !app.connectionHandler.alive + } + + Keys.onReleased: (event) => { + switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + { + if (app.__currentIndex > 0) { + app.__currentIndex = app.__currentIndex - 1 + event.accepted = true + } else { + Qt.quit() + } + break + } + default: + break + } + } + + Component.onCompleted: { + forceActiveFocus() + app.opacity = 1.0 + } +} diff --git a/examples/bluetooth/heartrate-game/BluetoothAlarmDialog.qml b/examples/bluetooth/heartrate-game/BluetoothAlarmDialog.qml new file mode 100644 index 00000000..93f4a3cd --- /dev/null +++ b/examples/bluetooth/heartrate-game/BluetoothAlarmDialog.qml @@ -0,0 +1,75 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: root + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.9 + } + + MouseArea { + id: eventEater + } + + Rectangle { + id: dialogFrame + + anchors.centerIn: parent + width: parent.width * 0.8 + height: parent.height * 0.6 + border.color: "#454545" + color: GameSettings.backgroundColor + radius: width * 0.05 + + Item { + id: dialogContainer + anchors.fill: parent + anchors.margins: parent.width*0.05 + + Image { + id: offOnImage + anchors.left: quitButton.left + anchors.right: quitButton.right + anchors.top: parent.top + height: GameSettings.heightForWidth(width, sourceSize) + source: "images/bt_off_to_on.png" + } + + Text { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: offOnImage.bottom + anchors.bottom: quitButton.top + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + font.pixelSize: GameSettings.mediumFontSize + color: GameSettings.textColor + text: qsTr("This application cannot be used without Bluetooth. Please switch Bluetooth ON to continue.") + } + + GameButton { + id: quitButton + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + width: dialogContainer.width * 0.6 + height: GameSettings.buttonHeight + onClicked: Qt.quit() + + Text { + anchors.centerIn: parent + color: GameSettings.textColor + font.pixelSize: GameSettings.bigFontSize + text: qsTr("Quit") + } + } + } + } +} + diff --git a/examples/bluetooth/heartrate-game/BottomLine.qml b/examples/bluetooth/heartrate-game/BottomLine.qml new file mode 100644 index 00000000..caebc307 --- /dev/null +++ b/examples/bluetooth/heartrate-game/BottomLine.qml @@ -0,0 +1,12 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + width: parent.width * 0.85 + height: parent.height * 0.05 + radius: height*0.5 +} diff --git a/examples/bluetooth/heartrate-game/CMakeLists.txt b/examples/bluetooth/heartrate-game/CMakeLists.txt index 0d2a7c84..ac680cc2 100644 --- a/examples/bluetooth/heartrate-game/CMakeLists.txt +++ b/examples/bluetooth/heartrate-game/CMakeLists.txt @@ -15,11 +15,6 @@ find_package(Qt6 REQUIRED COMPONENTS Bluetooth Core Gui Qml Quick) qt_standard_project_setup(REQUIRES 6.5) qt_add_executable(heartrate-game - bluetoothbaseclass.cpp bluetoothbaseclass.h - connectionhandler.cpp connectionhandler.h - devicefinder.cpp devicefinder.h - devicehandler.cpp devicehandler.h - deviceinfo.cpp deviceinfo.h heartrate-global.h main.cpp ) @@ -37,10 +32,37 @@ target_link_libraries(heartrate-game PRIVATE Qt::Quick ) +set_source_files_properties(GameSettings.qml PROPERTIES + QT_QML_SINGLETON_TYPE TRUE +) + qt_add_qml_module(heartrate-game - URI Shared + URI HeartRateGame VERSION 1.0 - NO_RESOURCE_TARGET_PATH + SOURCES + bluetoothbaseclass.cpp bluetoothbaseclass.h + connectionhandler.cpp connectionhandler.h + devicefinder.cpp devicefinder.h + devicehandler.cpp devicehandler.h + deviceinfo.cpp deviceinfo.h + QML_FILES + App.qml + BluetoothAlarmDialog.qml + BottomLine.qml + Connect.qml + GameButton.qml + GamePage.qml + GameSettings.qml + Measure.qml + SplashScreen.qml + Stats.qml + StatsLabel.qml + TitleBar.qml + Main.qml + RESOURCES + images/bt_off_to_on.png + images/heart.png + images/logo.png ) if (APPLE) @@ -57,44 +79,6 @@ if (APPLE) endif() endif() -# Resources: -set(qml_resource_files - "qml/App.qml" - "qml/BluetoothAlarmDialog.qml" - "qml/BottomLine.qml" - "qml/Connect.qml" - "qml/GameButton.qml" - "qml/GamePage.qml" - "qml/GameSettings.qml" - "qml/Measure.qml" - "qml/SplashScreen.qml" - "qml/Stats.qml" - "qml/StatsLabel.qml" - "qml/TitleBar.qml" - "qml/main.qml" - "qml/qmldir" -) - -qt6_add_resources(heartrate-game "qml" - PREFIX - "/" - FILES - ${qml_resource_files} -) - -set(images_resource_files - "qml/images/bt_off_to_on.png" - "qml/images/heart.png" - "qml/images/logo.png" -) - -qt6_add_resources(heartrate-game "images" - PREFIX - "/" - FILES - ${images_resource_files} -) - install(TARGETS heartrate-game RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" diff --git a/examples/bluetooth/heartrate-game/Connect.qml b/examples/bluetooth/heartrate-game/Connect.qml new file mode 100644 index 00000000..8582411d --- /dev/null +++ b/examples/bluetooth/heartrate-game/Connect.qml @@ -0,0 +1,159 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +pragma ComponentBehavior: Bound +import QtQuick +import HeartRateGame + +GamePage { + id: connectPage + + required property ConnectionHandler connectionHandler + required property DeviceFinder deviceFinder + required property DeviceHandler deviceHandler + + signal showMeasurePage + + errorMessage: deviceFinder.error + infoMessage: deviceFinder.info + + Rectangle { + id: viewContainer + anchors.top: parent.top + // only BlueZ platform has address type selection + anchors.bottom: connectPage.connectionHandler.requiresAddressType ? addressTypeButton.top + : searchButton.top + anchors.topMargin: GameSettings.fieldMargin + connectPage.messageHeight + anchors.bottomMargin: GameSettings.fieldMargin + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - GameSettings.fieldMargin * 2 + color: GameSettings.viewColor + radius: GameSettings.buttonRadius + + Text { + id: title + width: parent.width + height: GameSettings.fieldHeight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: GameSettings.textColor + font.pixelSize: GameSettings.mediumFontSize + text: qsTr("FOUND DEVICES") + + BottomLine { + height: 1 + width: parent.width + color: "#898989" + } + } + + ListView { + id: devices + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.top: title.bottom + model: connectPage.deviceFinder.devices + clip: true + + delegate: Rectangle { + id: box + + required property int index + required property var modelData + + height: GameSettings.fieldHeight * 1.2 + width: devices.width + color: index % 2 === 0 ? GameSettings.delegate1Color : GameSettings.delegate2Color + + MouseArea { + anchors.fill: parent + onClicked: { + connectPage.deviceFinder.connectToService(box.modelData.deviceAddress) + connectPage.showMeasurePage() + } + } + + Text { + id: device + font.pixelSize: GameSettings.smallFontSize + text: box.modelData.deviceName + anchors.top: parent.top + anchors.topMargin: parent.height * 0.1 + anchors.leftMargin: parent.height * 0.1 + anchors.left: parent.left + color: GameSettings.textColor + } + + Text { + id: deviceAddress + font.pixelSize: GameSettings.smallFontSize + text: box.modelData.deviceAddress + anchors.bottom: parent.bottom + anchors.bottomMargin: parent.height * 0.1 + anchors.rightMargin: parent.height * 0.1 + anchors.right: parent.right + color: Qt.darker(GameSettings.textColor) + } + } + } + } + + GameButton { + id: addressTypeButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: searchButton.top + anchors.bottomMargin: GameSettings.fieldMargin * 0.5 + width: viewContainer.width + height: GameSettings.fieldHeight + visible: connectPage.connectionHandler.requiresAddressType // only required on BlueZ + state: "public" + onClicked: state === "public" ? state = "random" : state = "public" + + states: [ + State { + name: "public" + PropertyChanges { + addressTypeText.text: qsTr("Public Address") + } + PropertyChanges { + connectPage.deviceHandler.addressType: DeviceHandler.PublicAddress + } + }, + State { + name: "random" + PropertyChanges { + addressTypeText.text: qsTr("Random Address") + } + PropertyChanges { + connectPage.deviceHandler.addressType: DeviceHandler.RandomAddress + } + } + ] + + Text { + id: addressTypeText + anchors.centerIn: parent + font.pixelSize: GameSettings.tinyFontSize + color: GameSettings.textColor + } + } + + GameButton { + id: searchButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: GameSettings.fieldMargin + width: viewContainer.width + height: GameSettings.fieldHeight + enabled: !connectPage.deviceFinder.scanning + onClicked: connectPage.deviceFinder.startSearch() + + Text { + anchors.centerIn: parent + font.pixelSize: GameSettings.tinyFontSize + text: qsTr("START SEARCH") + color: searchButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor + } + } +} diff --git a/examples/bluetooth/heartrate-game/GameButton.qml b/examples/bluetooth/heartrate-game/GameButton.qml new file mode 100644 index 00000000..8e876010 --- /dev/null +++ b/examples/bluetooth/heartrate-game/GameButton.qml @@ -0,0 +1,39 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + id: button + color: baseColor + onEnabledChanged: checkColor() + radius: GameSettings.buttonRadius + + property color baseColor: GameSettings.buttonColor + property color pressedColor: GameSettings.buttonPressedColor + property color disabledColor: GameSettings.disabledButtonColor + + signal clicked + + function checkColor() { + if (!button.enabled) { + button.color = disabledColor + } else { + if (mouseArea.containsPress) + button.color = pressedColor + else + button.color = baseColor + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onPressed: button.checkColor() + onReleased: button.checkColor() + onClicked: { + button.checkColor() + button.clicked() + } + } +} diff --git a/examples/bluetooth/heartrate-game/GamePage.qml b/examples/bluetooth/heartrate-game/GamePage.qml new file mode 100644 index 00000000..249f9418 --- /dev/null +++ b/examples/bluetooth/heartrate-game/GamePage.qml @@ -0,0 +1,36 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + id: page + + property string errorMessage: "" + property string infoMessage: "" + property real messageHeight: msg.height + property bool hasError: errorMessage != "" + property bool hasInfo: infoMessage != "" + + Rectangle { + id: msg + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: GameSettings.fieldHeight + color: page.hasError ? GameSettings.errorColor : GameSettings.infoColor + visible: page.hasError || page.hasInfo + + Text { + id: error + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + minimumPixelSize: 5 + font.pixelSize: GameSettings.smallFontSize + fontSizeMode: Text.Fit + color: GameSettings.textColor + text: page.hasError ? page.errorMessage : page.infoMessage + } + } +} diff --git a/examples/bluetooth/heartrate-game/GameSettings.qml b/examples/bluetooth/heartrate-game/GameSettings.qml new file mode 100644 index 00000000..0fe85460 --- /dev/null +++ b/examples/bluetooth/heartrate-game/GameSettings.qml @@ -0,0 +1,51 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +pragma Singleton +import QtQuick + +Item { + property int wHeight + property int wWidth + + // Colors + readonly property color backgroundColor: "#2d3037" + readonly property color buttonColor: "#202227" + readonly property color buttonPressedColor: "#6ccaf2" + readonly property color disabledButtonColor: "#555555" + readonly property color viewColor: "#202227" + readonly property color delegate1Color: Qt.darker(viewColor, 1.2) + readonly property color delegate2Color: Qt.lighter(viewColor, 1.2) + readonly property color textColor: "#ffffff" + readonly property color textDarkColor: "#232323" + readonly property color disabledTextColor: "#777777" + readonly property color sliderColor: "#6ccaf2" + readonly property color errorColor: "#ba3f62" + readonly property color infoColor: "#3fba62" + + // Font sizes + property real microFontSize: hugeFontSize * 0.2 + property real tinyFontSize: hugeFontSize * 0.4 + property real smallTinyFontSize: hugeFontSize * 0.5 + property real smallFontSize: hugeFontSize * 0.6 + property real mediumFontSize: hugeFontSize * 0.7 + property real bigFontSize: hugeFontSize * 0.8 + property real largeFontSize: hugeFontSize * 0.9 + property real hugeFontSize: (wWidth + wHeight) * 0.03 + property real giganticFontSize: (wWidth + wHeight) * 0.04 + + // Some other values + property real fieldHeight: wHeight * 0.08 + property real fieldMargin: fieldHeight * 0.5 + property real buttonHeight: wHeight * 0.08 + property real buttonRadius: buttonHeight * 0.1 + + // Some help functions + function widthForHeight(h, ss) { + return h / ss.height * ss.width + } + + function heightForWidth(w, ss) { + return w / ss.width * ss.height + } +} diff --git a/examples/bluetooth/heartrate-game/Main.qml b/examples/bluetooth/heartrate-game/Main.qml new file mode 100644 index 00000000..e26f9b00 --- /dev/null +++ b/examples/bluetooth/heartrate-game/Main.qml @@ -0,0 +1,71 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Window +import HeartRateGame + +Window { + id: wroot + visible: true + width: 720 * .7 + height: 1240 * .7 + title: qsTr("HeartRateGame") + color: GameSettings.backgroundColor + + required property ConnectionHandler connectionHandler + required property DeviceFinder deviceFinder + required property DeviceHandler deviceHandler + + Component.onCompleted: { + GameSettings.wWidth = Qt.binding(function () { + return width + }) + GameSettings.wHeight = Qt.binding(function () { + return height + }) + } + + Loader { + id: splashLoader + anchors.fill: parent + asynchronous: false + visible: true + + sourceComponent: SplashScreen { + appIsReady: appLoader.status === Loader.Ready + onReadyChanged: { + if (ready) { + appLoader.visible = true + splashLoader.visible = false + splashLoader.active = false + } + } + } + + onStatusChanged: { + if (status === Loader.Ready) + appLoader.active = true + } + } + + Loader { + id: appLoader + anchors.fill: parent + active: false + asynchronous: true + visible: false + + sourceComponent: App { + connectionHandler: wroot.connectionHandler + deviceFinder: wroot.deviceFinder + deviceHandler: wroot.deviceHandler + } + + onStatusChanged: { + if (status === Loader.Error) + Qt.quit() + } + } +} diff --git a/examples/bluetooth/heartrate-game/Measure.qml b/examples/bluetooth/heartrate-game/Measure.qml new file mode 100644 index 00000000..48e84e76 --- /dev/null +++ b/examples/bluetooth/heartrate-game/Measure.qml @@ -0,0 +1,212 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import HeartRateGame + +GamePage { + id: measurePage + + required property DeviceHandler deviceHandler + + errorMessage: deviceHandler.error + infoMessage: deviceHandler.info + + property real __timeCounter: 0 + property real __maxTimeCount: 60 + property string relaxText: qsTr("Relax!\nWhen you are ready, press Start. You have %1s time to increase heartrate so much as possible.\nGood luck!").arg(__maxTimeCount) + + signal showStatsPage + + function close() { + deviceHandler.stopMeasurement() + deviceHandler.disconnectService() + } + + function start() { + if (!deviceHandler.measuring) { + __timeCounter = 0 + deviceHandler.startMeasurement() + } + } + + function stop() { + if (deviceHandler.measuring) + deviceHandler.stopMeasurement() + + measurePage.showStatsPage() + } + + Timer { + id: measureTimer + interval: 1000 + running: measurePage.deviceHandler.measuring + repeat: true + onTriggered: { + measurePage.__timeCounter++ + if (measurePage.__timeCounter >= measurePage.__maxTimeCount) + measurePage.stop() + } + } + + Column { + anchors.centerIn: parent + spacing: GameSettings.fieldHeight * 0.5 + + Rectangle { + id: circle + anchors.horizontalCenter: parent.horizontalCenter + width: Math.min(measurePage.width, measurePage.height - GameSettings.fieldHeight * 4) + - 2 * GameSettings.fieldMargin + height: width + radius: width * 0.5 + color: GameSettings.viewColor + + Text { + id: hintText + anchors.centerIn: parent + anchors.verticalCenterOffset: -parent.height * 0.1 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width * 0.8 + height: parent.height * 0.6 + wrapMode: Text.WordWrap + text: measurePage.relaxText + visible: !measurePage.deviceHandler.measuring + color: GameSettings.textColor + fontSizeMode: Text.Fit + minimumPixelSize: 10 + font.pixelSize: GameSettings.mediumFontSize + } + + Text { + id: text + anchors.centerIn: parent + anchors.verticalCenterOffset: -parent.height * 0.15 + font.pixelSize: parent.width * 0.45 + text: measurePage.deviceHandler.hr + visible: measurePage.deviceHandler.measuring + color: GameSettings.textColor + } + + Item { + id: minMaxContainer + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width * 0.7 + height: parent.height * 0.15 + anchors.bottom: parent.bottom + anchors.bottomMargin: parent.height * 0.16 + visible: measurePage.deviceHandler.measuring + + Text { + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + text: measurePage.deviceHandler.minHR + color: GameSettings.textColor + font.pixelSize: GameSettings.hugeFontSize + + Text { + anchors.left: parent.left + anchors.bottom: parent.top + font.pixelSize: parent.font.pixelSize * 0.8 + color: parent.color + text: "MIN" + } + } + + Text { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + text: measurePage.deviceHandler.maxHR + color: GameSettings.textColor + font.pixelSize: GameSettings.hugeFontSize + + Text { + anchors.right: parent.right + anchors.bottom: parent.top + font.pixelSize: parent.font.pixelSize * 0.8 + color: parent.color + text: "MAX" + } + } + } + + Image { + id: heart + anchors.horizontalCenter: minMaxContainer.horizontalCenter + anchors.verticalCenter: minMaxContainer.bottom + width: parent.width * 0.2 + height: width + source: "images/heart.png" + smooth: true + antialiasing: true + + SequentialAnimation { + id: heartAnim + running: measurePage.deviceHandler.alive + loops: Animation.Infinite + alwaysRunToEnd: true + PropertyAnimation { + target: heart + property: "scale" + to: 1.2 + duration: 500 + easing.type: Easing.InQuad + } + PropertyAnimation { + target: heart + property: "scale" + to: 1.0 + duration: 500 + easing.type: Easing.OutQuad + } + } + } + } + + Rectangle { + id: timeSlider + color: GameSettings.viewColor + anchors.horizontalCenter: parent.horizontalCenter + width: circle.width + height: GameSettings.fieldHeight + radius: GameSettings.buttonRadius + + Rectangle { + height: parent.height + radius: parent.radius + color: GameSettings.sliderColor + width: Math.min( + 1.0, + measurePage.__timeCounter / measurePage.__maxTimeCount) * parent.width + } + + Text { + anchors.centerIn: parent + color: "gray" + text: (measurePage.__maxTimeCount - measurePage.__timeCounter).toFixed(0) + " s" + font.pixelSize: GameSettings.bigFontSize + } + } + } + + GameButton { + id: startButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: GameSettings.fieldMargin + width: circle.width + height: GameSettings.fieldHeight + enabled: !measurePage.deviceHandler.measuring + radius: GameSettings.buttonRadius + + onClicked: measurePage.start() + + Text { + anchors.centerIn: parent + font.pixelSize: GameSettings.tinyFontSize + text: qsTr("START") + color: startButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor + } + } +} diff --git a/examples/bluetooth/heartrate-game/SplashScreen.qml b/examples/bluetooth/heartrate-game/SplashScreen.qml new file mode 100644 index 00000000..2f9ac1b3 --- /dev/null +++ b/examples/bluetooth/heartrate-game/SplashScreen.qml @@ -0,0 +1,30 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import HeartRateGame + +Item { + id: root + + property bool appIsReady: false + property bool splashIsReady: false + property bool ready: appIsReady && splashIsReady + + anchors.fill: parent + + Image { + anchors.centerIn: parent + width: Math.min(parent.height, parent.width) * 0.6 + height: GameSettings.heightForWidth(width, sourceSize) + source: "images/logo.png" + } + + Timer { + id: splashTimer + interval: 1000 + onTriggered: splashIsReady = true + } + + Component.onCompleted: splashTimer.start() +} diff --git a/examples/bluetooth/heartrate-game/Stats.qml b/examples/bluetooth/heartrate-game/Stats.qml new file mode 100644 index 00000000..22cdd536 --- /dev/null +++ b/examples/bluetooth/heartrate-game/Stats.qml @@ -0,0 +1,55 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import HeartRateGame + +GamePage { + id: statsPage + + required property DeviceHandler deviceHandler + + Column { + anchors.centerIn: parent + width: parent.width + + Text { + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: GameSettings.hugeFontSize + color: GameSettings.textColor + text: qsTr("RESULT") + } + + Text { + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: GameSettings.giganticFontSize * 3 + color: GameSettings.textColor + text: (statsPage.deviceHandler.maxHR - statsPage.deviceHandler.minHR).toFixed(0) + } + + Item { + height: GameSettings.fieldHeight + width: 1 + } + + StatsLabel { + title: qsTr("MIN") + value: statsPage.deviceHandler.minHR.toFixed(0) + } + + StatsLabel { + title: qsTr("MAX") + value: statsPage.deviceHandler.maxHR.toFixed(0) + } + + StatsLabel { + title: qsTr("AVG") + value: statsPage.deviceHandler.average.toFixed(1) + } + + StatsLabel { + title: qsTr("CALORIES") + value: statsPage.deviceHandler.calories.toFixed(3) + } + } +} diff --git a/examples/bluetooth/heartrate-game/StatsLabel.qml b/examples/bluetooth/heartrate-game/StatsLabel.qml new file mode 100644 index 00000000..0ea4249a --- /dev/null +++ b/examples/bluetooth/heartrate-game/StatsLabel.qml @@ -0,0 +1,34 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Item { + height: GameSettings.fieldHeight + width: parent.width + + property alias title: leftText.text + property alias value: rightText.text + + Text { + id: leftText + anchors.left: parent.left + height: parent.height + width: parent.width * 0.45 + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + font.pixelSize: GameSettings.mediumFontSize + color: GameSettings.textColor + } + + Text { + id: rightText + anchors.right: parent.right + height: parent.height + width: parent.width * 0.45 + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + font.pixelSize: GameSettings.mediumFontSize + color: GameSettings.textColor + } +} diff --git a/examples/bluetooth/heartrate-game/TitleBar.qml b/examples/bluetooth/heartrate-game/TitleBar.qml new file mode 100644 index 00000000..016a4435 --- /dev/null +++ b/examples/bluetooth/heartrate-game/TitleBar.qml @@ -0,0 +1,54 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +pragma ComponentBehavior: Bound +import QtQuick + +Rectangle { + id: titleBar + + property var __titles: ["CONNECT", "MEASURE", "STATS"] + property int currentIndex: 0 + + signal titleClicked(int index) + + height: GameSettings.fieldHeight + color: GameSettings.viewColor + + Repeater { + model: 3 + Text { + id: caption + required property int index + width: titleBar.width / 3 + height: titleBar.height + x: index * width + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: titleBar.__titles[index] + font.pixelSize: GameSettings.tinyFontSize + color: titleBar.currentIndex === index ? GameSettings.textColor + : GameSettings.disabledTextColor + + MouseArea { + anchors.fill: parent + onClicked: titleBar.titleClicked(caption.index) + } + } + } + + Item { + anchors.bottom: parent.bottom + width: parent.width / 3 + height: parent.height + x: titleBar.currentIndex * width + + BottomLine {} + + Behavior on x { + NumberAnimation { + duration: 200 + } + } + } +} diff --git a/examples/bluetooth/heartrate-game/devicehandler.h b/examples/bluetooth/heartrate-game/devicehandler.h index fd670f43..eb155e5b 100644 --- a/examples/bluetooth/heartrate-game/devicehandler.h +++ b/examples/bluetooth/heartrate-game/devicehandler.h @@ -13,7 +13,7 @@ #include #include -#include +#include class DeviceInfo; @@ -31,8 +31,7 @@ class DeviceHandler : public BluetoothBaseClass Q_PROPERTY(float calories READ calories NOTIFY statsChanged) Q_PROPERTY(AddressType addressType READ addressType WRITE setAddressType) - QML_NAMED_ELEMENT(AddressType) - QML_UNCREATABLE("Enum is not a type") + QML_ELEMENT public: enum class AddressType { diff --git a/examples/bluetooth/heartrate-game/heartrate-game.pro b/examples/bluetooth/heartrate-game/heartrate-game.pro index f55053b4..65be3382 100644 --- a/examples/bluetooth/heartrate-game/heartrate-game.pro +++ b/examples/bluetooth/heartrate-game/heartrate-game.pro @@ -2,10 +2,9 @@ TEMPLATE = app TARGET = heartrate-game QT += qml quick bluetooth -CONFIG += c++11 CONFIG += qmltypes -QML_IMPORT_NAME = Shared +QML_IMPORT_NAME = HeartRateGame QML_IMPORT_MAJOR_VERSION = 1 HEADERS += \ @@ -23,14 +22,31 @@ SOURCES += main.cpp \ devicehandler.cpp \ bluetoothbaseclass.cpp +qml_resources.files = \ + qmldir \ + App.qml \ + BluetoothAlarmDialog.qml \ + BottomLine.qml \ + Connect.qml \ + GameButton.qml \ + GamePage.qml \ + GameSettings.qml \ + Measure.qml \ + SplashScreen.qml \ + Stats.qml \ + StatsLabel.qml \ + TitleBar.qml \ + Main.qml \ + images/bt_off_to_on.png \ + images/heart.png \ + images/logo.png + +qml_resources.prefix = /qt/qml/HeartRateGame + +RESOURCES = qml_resources + ios: QMAKE_INFO_PLIST = Info.plist macos: QMAKE_INFO_PLIST = ../shared/Info.qmake.macos.plist -RESOURCES += qml.qrc \ - images.qrc - -# Additional import path used to resolve QML modules in Qt Creator's code model -QML_IMPORT_PATH = - target.path = $$[QT_INSTALL_EXAMPLES]/bluetooth/heartrate-game INSTALLS += target diff --git a/examples/bluetooth/heartrate-game/images.qrc b/examples/bluetooth/heartrate-game/images.qrc deleted file mode 100644 index 38058de0..00000000 --- a/examples/bluetooth/heartrate-game/images.qrc +++ /dev/null @@ -1,7 +0,0 @@ - - - qml/images/logo.png - qml/images/bt_off_to_on.png - qml/images/heart.png - - diff --git a/examples/bluetooth/heartrate-game/images/bt_off_to_on.png b/examples/bluetooth/heartrate-game/images/bt_off_to_on.png new file mode 100644 index 00000000..5ea1f3f0 Binary files /dev/null and b/examples/bluetooth/heartrate-game/images/bt_off_to_on.png differ diff --git a/examples/bluetooth/heartrate-game/images/heart.png b/examples/bluetooth/heartrate-game/images/heart.png new file mode 100644 index 00000000..f2b3c0a3 Binary files /dev/null and b/examples/bluetooth/heartrate-game/images/heart.png differ diff --git a/examples/bluetooth/heartrate-game/images/logo.png b/examples/bluetooth/heartrate-game/images/logo.png new file mode 100644 index 00000000..ea0af7e0 Binary files /dev/null and b/examples/bluetooth/heartrate-game/images/logo.png differ diff --git a/examples/bluetooth/heartrate-game/main.cpp b/examples/bluetooth/heartrate-game/main.cpp index 11573512..50f891e1 100644 --- a/examples/bluetooth/heartrate-game/main.cpp +++ b/examples/bluetooth/heartrate-game/main.cpp @@ -48,9 +48,9 @@ int main(int argc, char *argv[]) {u"deviceHandler"_s, QVariant::fromValue(&deviceHandler)} }); - engine.load(QUrl(u"qrc:/qml/main.qml"_s)); - if (engine.rootObjects().isEmpty()) - return -1; + QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, + []() { QCoreApplication::exit(1); }, Qt::QueuedConnection); + engine.loadFromModule("HeartRateGame", "Main"); return app.exec(); } diff --git a/examples/bluetooth/heartrate-game/qml.qrc b/examples/bluetooth/heartrate-game/qml.qrc deleted file mode 100644 index bab96355..00000000 --- a/examples/bluetooth/heartrate-game/qml.qrc +++ /dev/null @@ -1,18 +0,0 @@ - - - qml/BluetoothAlarmDialog.qml - qml/main.qml - qml/SplashScreen.qml - qml/GameSettings.qml - qml/App.qml - qml/TitleBar.qml - qml/Connect.qml - qml/Measure.qml - qml/Stats.qml - qml/GameButton.qml - qml/GamePage.qml - qml/BottomLine.qml - qml/StatsLabel.qml - qml/qmldir - - diff --git a/examples/bluetooth/heartrate-game/qml/App.qml b/examples/bluetooth/heartrate-game/qml/App.qml deleted file mode 100644 index 1eb53202..00000000 --- a/examples/bluetooth/heartrate-game/qml/App.qml +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick - -Item { - id: app - anchors.fill: parent - opacity: 0.0 - - Behavior on opacity { NumberAnimation { duration: 500 } } - - property var lastPages: [] - property int __currentIndex: 0 - - function init() - { - opacity = 1.0 - showPage("Connect.qml") - } - - function prevPage() - { - lastPages.pop() - pageLoader.setSource(lastPages[lastPages.length-1]) - __currentIndex = lastPages.length-1; - } - - function showPage(name) - { - lastPages.push(name) - pageLoader.setSource(name) - __currentIndex = lastPages.length-1; - } - - TitleBar { - id: titleBar - currentIndex: __currentIndex - - onTitleClicked: (index) => { - if (index < __currentIndex) - pageLoader.item.close() - } - } - - Loader { - id: pageLoader - anchors.left: parent.left - anchors.right: parent.right - anchors.top: titleBar.bottom - anchors.bottom: parent.bottom - - onStatusChanged: { - if (status === Loader.Ready) - { - pageLoader.item.init(); - pageLoader.item.forceActiveFocus() - } - } - } - - Keys.onReleased: (event) => { - switch (event.key) { - case Qt.Key_Escape: - case Qt.Key_Back: { - if (__currentIndex > 0) { - pageLoader.item.close() - event.accepted = true - } else { - Qt.quit() - } - break; - } - default: break; - } - } - - BluetoothAlarmDialog { - id: btAlarmDialog - anchors.fill: parent - visible: !connectionHandler.alive - } -} diff --git a/examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml b/examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml deleted file mode 100644 index 93f4a3cd..00000000 --- a/examples/bluetooth/heartrate-game/qml/BluetoothAlarmDialog.qml +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick - -Item { - id: root - anchors.fill: parent - - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.9 - } - - MouseArea { - id: eventEater - } - - Rectangle { - id: dialogFrame - - anchors.centerIn: parent - width: parent.width * 0.8 - height: parent.height * 0.6 - border.color: "#454545" - color: GameSettings.backgroundColor - radius: width * 0.05 - - Item { - id: dialogContainer - anchors.fill: parent - anchors.margins: parent.width*0.05 - - Image { - id: offOnImage - anchors.left: quitButton.left - anchors.right: quitButton.right - anchors.top: parent.top - height: GameSettings.heightForWidth(width, sourceSize) - source: "images/bt_off_to_on.png" - } - - Text { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: offOnImage.bottom - anchors.bottom: quitButton.top - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - font.pixelSize: GameSettings.mediumFontSize - color: GameSettings.textColor - text: qsTr("This application cannot be used without Bluetooth. Please switch Bluetooth ON to continue.") - } - - GameButton { - id: quitButton - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - width: dialogContainer.width * 0.6 - height: GameSettings.buttonHeight - onClicked: Qt.quit() - - Text { - anchors.centerIn: parent - color: GameSettings.textColor - font.pixelSize: GameSettings.bigFontSize - text: qsTr("Quit") - } - } - } - } -} - diff --git a/examples/bluetooth/heartrate-game/qml/BottomLine.qml b/examples/bluetooth/heartrate-game/qml/BottomLine.qml deleted file mode 100644 index caebc307..00000000 --- a/examples/bluetooth/heartrate-game/qml/BottomLine.qml +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick - -Rectangle { - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - width: parent.width * 0.85 - height: parent.height * 0.05 - radius: height*0.5 -} diff --git a/examples/bluetooth/heartrate-game/qml/Connect.qml b/examples/bluetooth/heartrate-game/qml/Connect.qml deleted file mode 100644 index 40f25310..00000000 --- a/examples/bluetooth/heartrate-game/qml/Connect.qml +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import Shared - -GamePage { - - errorMessage: deviceFinder.error - infoMessage: deviceFinder.info - - Rectangle { - id: viewContainer - anchors.top: parent.top - anchors.bottom: - // only BlueZ platform has address type selection - connectionHandler.requiresAddressType ? addressTypeButton.top : searchButton.top - anchors.topMargin: GameSettings.fieldMargin + messageHeight - anchors.bottomMargin: GameSettings.fieldMargin - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - GameSettings.fieldMargin*2 - color: GameSettings.viewColor - radius: GameSettings.buttonRadius - - - Text { - id: title - width: parent.width - height: GameSettings.fieldHeight - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: GameSettings.textColor - font.pixelSize: GameSettings.mediumFontSize - text: qsTr("FOUND DEVICES") - - BottomLine { - height: 1; - width: parent.width - color: "#898989" - } - } - - - ListView { - id: devices - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.top: title.bottom - model: deviceFinder.devices - clip: true - - delegate: Rectangle { - id: box - height:GameSettings.fieldHeight * 1.2 - width: devices.width - color: index % 2 === 0 ? GameSettings.delegate1Color : GameSettings.delegate2Color - - MouseArea { - anchors.fill: parent - onClicked: { - deviceFinder.connectToService(modelData.deviceAddress); - app.showPage("Measure.qml") - } - } - - Text { - id: device - font.pixelSize: GameSettings.smallFontSize - text: modelData.deviceName - anchors.top: parent.top - anchors.topMargin: parent.height * 0.1 - anchors.leftMargin: parent.height * 0.1 - anchors.left: parent.left - color: GameSettings.textColor - } - - Text { - id: deviceAddress - font.pixelSize: GameSettings.smallFontSize - text: modelData.deviceAddress - anchors.bottom: parent.bottom - anchors.bottomMargin: parent.height * 0.1 - anchors.rightMargin: parent.height * 0.1 - anchors.right: parent.right - color: Qt.darker(GameSettings.textColor) - } - } - } - } - - GameButton { - id: addressTypeButton - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: searchButton.top - anchors.bottomMargin: GameSettings.fieldMargin*0.5 - width: viewContainer.width - height: GameSettings.fieldHeight - visible: connectionHandler.requiresAddressType // only required on BlueZ - state: "public" - onClicked: state == "public" ? state = "random" : state = "public" - - states: [ - State { - name: "public" - PropertyChanges { target: addressTypeText; text: qsTr("Public Address") } - PropertyChanges { target: deviceHandler; addressType: AddressType.PublicAddress } - }, - State { - name: "random" - PropertyChanges { target: addressTypeText; text: qsTr("Random Address") } - PropertyChanges { target: deviceHandler; addressType: AddressType.RandomAddress } - } - ] - - Text { - id: addressTypeText - anchors.centerIn: parent - font.pixelSize: GameSettings.tinyFontSize - color: GameSettings.textColor - } - } - - GameButton { - id: searchButton - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: GameSettings.fieldMargin - width: viewContainer.width - height: GameSettings.fieldHeight - enabled: !deviceFinder.scanning - onClicked: deviceFinder.startSearch() - - Text { - anchors.centerIn: parent - font.pixelSize: GameSettings.tinyFontSize - text: qsTr("START SEARCH") - color: searchButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor - } - } -} diff --git a/examples/bluetooth/heartrate-game/qml/GameButton.qml b/examples/bluetooth/heartrate-game/qml/GameButton.qml deleted file mode 100644 index 3ce9d66f..00000000 --- a/examples/bluetooth/heartrate-game/qml/GameButton.qml +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import "." - -Rectangle { - id: button - color: baseColor - onEnabledChanged: checkColor() - radius: GameSettings.buttonRadius - - property color baseColor: GameSettings.buttonColor - property color pressedColor: GameSettings.buttonPressedColor - property color disabledColor: GameSettings.disabledButtonColor - - signal clicked() - - function checkColor() - { - if (!button.enabled) { - button.color = disabledColor - } else { - if (mouseArea.containsPress) - button.color = pressedColor - else - button.color = baseColor - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - onPressed: checkColor() - onReleased: checkColor() - onClicked: { - checkColor() - button.clicked() - } - } -} diff --git a/examples/bluetooth/heartrate-game/qml/GamePage.qml b/examples/bluetooth/heartrate-game/qml/GamePage.qml deleted file mode 100644 index 25a5bb3d..00000000 --- a/examples/bluetooth/heartrate-game/qml/GamePage.qml +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import "." - -Item { - anchors.fill: parent - - property string errorMessage: "" - property string infoMessage: "" - property real messageHeight: msg.height - property bool hasError: errorMessage != "" - property bool hasInfo: infoMessage != "" - - function init() - { - } - - function close() - { - app.prevPage() - } - - Rectangle { - id: msg - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: GameSettings.fieldHeight - color: hasError ? GameSettings.errorColor : GameSettings.infoColor - visible: hasError || hasInfo - - Text { - id: error - anchors.fill: parent - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - minimumPixelSize: 5 - font.pixelSize: GameSettings.smallFontSize - fontSizeMode: Text.Fit - color: GameSettings.textColor - text: hasError ? errorMessage : infoMessage - } - } -} diff --git a/examples/bluetooth/heartrate-game/qml/GameSettings.qml b/examples/bluetooth/heartrate-game/qml/GameSettings.qml deleted file mode 100644 index f265b73c..00000000 --- a/examples/bluetooth/heartrate-game/qml/GameSettings.qml +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -pragma Singleton -import QtQuick - -Item { - property int wHeight - property int wWidth - - // Colors - readonly property color backgroundColor: "#2d3037" - readonly property color buttonColor: "#202227" - readonly property color buttonPressedColor: "#6ccaf2" - readonly property color disabledButtonColor: "#555555" - readonly property color viewColor: "#202227" - readonly property color delegate1Color: Qt.darker(viewColor, 1.2) - readonly property color delegate2Color: Qt.lighter(viewColor, 1.2) - readonly property color textColor: "#ffffff" - readonly property color textDarkColor: "#232323" - readonly property color disabledTextColor: "#777777" - readonly property color sliderColor: "#6ccaf2" - readonly property color errorColor: "#ba3f62" - readonly property color infoColor: "#3fba62" - - // Font sizes - property real microFontSize: hugeFontSize * 0.2 - property real tinyFontSize: hugeFontSize * 0.4 - property real smallTinyFontSize: hugeFontSize * 0.5 - property real smallFontSize: hugeFontSize * 0.6 - property real mediumFontSize: hugeFontSize * 0.7 - property real bigFontSize: hugeFontSize * 0.8 - property real largeFontSize: hugeFontSize * 0.9 - property real hugeFontSize: (wWidth + wHeight) * 0.03 - property real giganticFontSize: (wWidth + wHeight) * 0.04 - - // Some other values - property real fieldHeight: wHeight * 0.08 - property real fieldMargin: fieldHeight * 0.5 - property real buttonHeight: wHeight * 0.08 - property real buttonRadius: buttonHeight * 0.1 - - // Some help functions - function widthForHeight(h, ss) - { - return h/ss.height * ss.width; - } - - function heightForWidth(w, ss) - { - return w/ss.width * ss.height; - } - -} diff --git a/examples/bluetooth/heartrate-game/qml/Measure.qml b/examples/bluetooth/heartrate-game/qml/Measure.qml deleted file mode 100644 index c434d511..00000000 --- a/examples/bluetooth/heartrate-game/qml/Measure.qml +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick - -GamePage { - id: measurePage - - errorMessage: deviceHandler.error - infoMessage: deviceHandler.info - - property real __timeCounter: 0; - property real __maxTimeCount: 60 - property string relaxText: qsTr("Relax!\nWhen you are ready, press Start. You have %1s time to increase heartrate so much as possible.\nGood luck!").arg(__maxTimeCount) - - function close() - { - deviceHandler.stopMeasurement(); - deviceHandler.disconnectService(); - app.prevPage(); - } - - function start() - { - if (!deviceHandler.measuring) { - __timeCounter = 0; - deviceHandler.startMeasurement() - } - } - - function stop() - { - if (deviceHandler.measuring) { - deviceHandler.stopMeasurement() - } - - app.showPage("Stats.qml") - } - - Timer { - id: measureTimer - interval: 1000 - running: deviceHandler.measuring - repeat: true - onTriggered: { - __timeCounter++; - if (__timeCounter >= __maxTimeCount) - measurePage.stop() - } - } - - Column { - anchors.centerIn: parent - spacing: GameSettings.fieldHeight * 0.5 - - Rectangle { - id: circle - anchors.horizontalCenter: parent.horizontalCenter - width: Math.min(measurePage.width, measurePage.height-GameSettings.fieldHeight*4) - 2*GameSettings.fieldMargin - height: width - radius: width*0.5 - color: GameSettings.viewColor - - Text { - id: hintText - anchors.centerIn: parent - anchors.verticalCenterOffset: -parent.height*0.1 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - width: parent.width * 0.8 - height: parent.height * 0.6 - wrapMode: Text.WordWrap - text: measurePage.relaxText - visible: !deviceHandler.measuring - color: GameSettings.textColor - fontSizeMode: Text.Fit - minimumPixelSize: 10 - font.pixelSize: GameSettings.mediumFontSize - } - - Text { - id: text - anchors.centerIn: parent - anchors.verticalCenterOffset: -parent.height*0.15 - font.pixelSize: parent.width * 0.45 - text: deviceHandler.hr - visible: deviceHandler.measuring - color: GameSettings.textColor - } - - Item { - id: minMaxContainer - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width*0.7 - height: parent.height * 0.15 - anchors.bottom: parent.bottom - anchors.bottomMargin: parent.height*0.16 - visible: deviceHandler.measuring - - Text { - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - text: deviceHandler.minHR - color: GameSettings.textColor - font.pixelSize: GameSettings.hugeFontSize - - Text { - anchors.left: parent.left - anchors.bottom: parent.top - font.pixelSize: parent.font.pixelSize*0.8 - color: parent.color - text: "MIN" - } - } - - Text { - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - text: deviceHandler.maxHR - color: GameSettings.textColor - font.pixelSize: GameSettings.hugeFontSize - - Text { - anchors.right: parent.right - anchors.bottom: parent.top - font.pixelSize: parent.font.pixelSize*0.8 - color: parent.color - text: "MAX" - } - } - } - - Image { - id: heart - anchors.horizontalCenter: minMaxContainer.horizontalCenter - anchors.verticalCenter: minMaxContainer.bottom - width: parent.width * 0.2 - height: width - source: "images/heart.png" - smooth: true - antialiasing: true - - SequentialAnimation{ - id: heartAnim - running: deviceHandler.alive - loops: Animation.Infinite - alwaysRunToEnd: true - PropertyAnimation { target: heart; property: "scale"; to: 1.2; duration: 500; easing.type: Easing.InQuad } - PropertyAnimation { target: heart; property: "scale"; to: 1.0; duration: 500; easing.type: Easing.OutQuad } - } - } - } - - Rectangle { - id: timeSlider - color: GameSettings.viewColor - anchors.horizontalCenter: parent.horizontalCenter - width: circle.width - height: GameSettings.fieldHeight - radius: GameSettings.buttonRadius - - Rectangle { - height: parent.height - radius: parent.radius - color: GameSettings.sliderColor - width: Math.min(1.0,__timeCounter / __maxTimeCount) * parent.width - } - - Text { - anchors.centerIn: parent - color: "gray" - text: (__maxTimeCount - __timeCounter).toFixed(0) + " s" - font.pixelSize: GameSettings.bigFontSize - } - } - } - - GameButton { - id: startButton - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: GameSettings.fieldMargin - width: circle.width - height: GameSettings.fieldHeight - enabled: !deviceHandler.measuring - radius: GameSettings.buttonRadius - - onClicked: start() - - Text { - anchors.centerIn: parent - font.pixelSize: GameSettings.tinyFontSize - text: qsTr("START") - color: startButton.enabled ? GameSettings.textColor : GameSettings.disabledTextColor - } - } -} diff --git a/examples/bluetooth/heartrate-game/qml/SplashScreen.qml b/examples/bluetooth/heartrate-game/qml/SplashScreen.qml deleted file mode 100644 index 23f71f08..00000000 --- a/examples/bluetooth/heartrate-game/qml/SplashScreen.qml +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import "." - -Item { - id: root - anchors.fill: parent - - property bool appIsReady: false - property bool splashIsReady: false - - property bool ready: appIsReady && splashIsReady - onReadyChanged: if (ready) readyToGo(); - - signal readyToGo() - - function appReady() - { - appIsReady = true - } - - function errorInLoadingApp() - { - Qt.quit() - } - - Image { - anchors.centerIn: parent - width: Math.min(parent.height, parent.width)*0.6 - height: GameSettings.heightForWidth(width, sourceSize) - source: "images/logo.png" - } - - Timer { - id: splashTimer - interval: 1000 - onTriggered: splashIsReady = true - } - - Component.onCompleted: splashTimer.start() -} diff --git a/examples/bluetooth/heartrate-game/qml/Stats.qml b/examples/bluetooth/heartrate-game/qml/Stats.qml deleted file mode 100644 index b818e85e..00000000 --- a/examples/bluetooth/heartrate-game/qml/Stats.qml +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick - -GamePage { - - Column { - anchors.centerIn: parent - width: parent.width - - Text { - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: GameSettings.hugeFontSize - color: GameSettings.textColor - text: qsTr("RESULT") - } - - Text { - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: GameSettings.giganticFontSize*3 - color: GameSettings.textColor - text: (deviceHandler.maxHR - deviceHandler.minHR).toFixed(0) - } - - Item { - height: GameSettings.fieldHeight - width: 1 - } - - StatsLabel { - title: qsTr("MIN") - value: deviceHandler.minHR.toFixed(0) - } - - StatsLabel { - title: qsTr("MAX") - value: deviceHandler.maxHR.toFixed(0) - } - - StatsLabel { - title: qsTr("AVG") - value: deviceHandler.average.toFixed(1) - } - - - StatsLabel { - title: qsTr("CALORIES") - value: deviceHandler.calories.toFixed(3) - } - } -} diff --git a/examples/bluetooth/heartrate-game/qml/StatsLabel.qml b/examples/bluetooth/heartrate-game/qml/StatsLabel.qml deleted file mode 100644 index cd5cda5b..00000000 --- a/examples/bluetooth/heartrate-game/qml/StatsLabel.qml +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import "." - -Item { - height: GameSettings.fieldHeight - width: parent.width - - property alias title: leftText.text - property alias value: rightText.text - - Text { - id: leftText - anchors.left: parent.left - height: parent.height - width: parent.width * 0.45 - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - font.pixelSize: GameSettings.mediumFontSize - color: GameSettings.textColor - } - - Text { - id: rightText - anchors.right: parent.right - height: parent.height - width: parent.width * 0.45 - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - font.pixelSize: GameSettings.mediumFontSize - color: GameSettings.textColor - } -} diff --git a/examples/bluetooth/heartrate-game/qml/TitleBar.qml b/examples/bluetooth/heartrate-game/qml/TitleBar.qml deleted file mode 100644 index b7de77c4..00000000 --- a/examples/bluetooth/heartrate-game/qml/TitleBar.qml +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick - -Rectangle { - id: titleBar - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: GameSettings.fieldHeight - color: GameSettings.viewColor - - property var __titles: ["CONNECT", "MEASURE", "STATS"] - property int currentIndex: 0 - - signal titleClicked(int index) - - Repeater { - model: 3 - Text { - width: titleBar.width / 3 - height: titleBar.height - x: index * width - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - text: __titles[index] - font.pixelSize: GameSettings.tinyFontSize - color: titleBar.currentIndex === index ? GameSettings.textColor : GameSettings.disabledTextColor - - MouseArea { - anchors.fill: parent - onClicked: titleClicked(index) - } - } - } - - - Item { - anchors.bottom: parent.bottom - width: parent.width / 3 - height: parent.height - x: currentIndex * width - - BottomLine{} - - Behavior on x { NumberAnimation { duration: 200 } } - } - -} diff --git a/examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.png b/examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.png deleted file mode 100644 index 5ea1f3f0..00000000 Binary files a/examples/bluetooth/heartrate-game/qml/images/bt_off_to_on.png and /dev/null differ diff --git a/examples/bluetooth/heartrate-game/qml/images/heart.png b/examples/bluetooth/heartrate-game/qml/images/heart.png deleted file mode 100644 index f2b3c0a3..00000000 Binary files a/examples/bluetooth/heartrate-game/qml/images/heart.png and /dev/null differ diff --git a/examples/bluetooth/heartrate-game/qml/images/logo.png b/examples/bluetooth/heartrate-game/qml/images/logo.png deleted file mode 100644 index ea0af7e0..00000000 Binary files a/examples/bluetooth/heartrate-game/qml/images/logo.png and /dev/null differ diff --git a/examples/bluetooth/heartrate-game/qml/main.qml b/examples/bluetooth/heartrate-game/qml/main.qml deleted file mode 100644 index 44d824fa..00000000 --- a/examples/bluetooth/heartrate-game/qml/main.qml +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import QtQuick.Window -import "." -import Shared - -Window { - id: wroot - visible: true - width: 720 * .7 - height: 1240 * .7 - title: qsTr("HeartRateGame") - color: GameSettings.backgroundColor - - required property ConnectionHandler connectionHandler - required property DeviceFinder deviceFinder - required property AddressType deviceHandler - - Component.onCompleted: { - GameSettings.wWidth = Qt.binding(function() {return width}) - GameSettings.wHeight = Qt.binding(function() {return height}) - } - - Loader { - id: splashLoader - anchors.fill: parent - source: "SplashScreen.qml" - asynchronous: false - visible: true - - onStatusChanged: { - if (status === Loader.Ready) { - appLoader.setSource("App.qml"); - } - } - } - - Connections { - target: splashLoader.item - function onReadyToGo() { - appLoader.visible = true - appLoader.item.init() - splashLoader.visible = false - splashLoader.setSource("") - appLoader.item.forceActiveFocus(); - } - } - - Loader { - id: appLoader - anchors.fill: parent - visible: false - asynchronous: true - onStatusChanged: { - if (status === Loader.Ready) - splashLoader.item.appReady() - if (status === Loader.Error) - splashLoader.item.errorInLoadingApp(); - } - } -} diff --git a/examples/bluetooth/heartrate-game/qml/qmldir b/examples/bluetooth/heartrate-game/qml/qmldir deleted file mode 100644 index 5e0d2b54..00000000 --- a/examples/bluetooth/heartrate-game/qml/qmldir +++ /dev/null @@ -1 +0,0 @@ -singleton GameSettings 1.0 GameSettings.qml diff --git a/examples/bluetooth/heartrate-game/qmldir b/examples/bluetooth/heartrate-game/qmldir new file mode 100644 index 00000000..161e6bab --- /dev/null +++ b/examples/bluetooth/heartrate-game/qmldir @@ -0,0 +1,15 @@ +module HeartRateGame +prefer :/qt/qml/HeartRateGame/ +App 1.0 App.qml +BluetoothAlarmDialog 1.0 BluetoothAlarmDialog.qml +BottomLine 1.0 BottomLine.qml +Connect 1.0 Connect.qml +GameButton 1.0 GameButton.qml +GamePage 1.0 GamePage.qml +singleton GameSettings 1.0 GameSettings.qml +Measure 1.0 Measure.qml +SplashScreen 1.0 SplashScreen.qml +Stats 1.0 Stats.qml +StatsLabel 1.0 StatsLabel.qml +TitleBar 1.0 TitleBar.qml +Main 1.0 Main.qml -- cgit v1.2.3