C
Qt Quick Ultralite Watch Demo
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
import QtQuick 2.15
import QtQuickUltralite.Extras 2.0
import Watch 1.0
Rectangle {
id: root
height: Theme.appHeight
width: Theme.appWidth
color: Theme.backgroundColor
property bool onScreen: false
property bool offScreen: false
readonly property int heartbeatDuration: 130
readonly property int heartImgWidth: 39
readonly property int heartImgHeight: 34
readonly property int introDuration: 200
readonly property int linesIntroDuration: 50
readonly property int introDurationLong: 250
readonly property real startOp: 0.0
readonly property int startWidth: 0
readonly property real startTopMargin: height / 2 - heartRate.height / 2
SequentialAnimation {
id: intro
NumberAnimation { target: heartRate; property: "margin"; to: 25; easing.type: Easing.InOutCubic; duration: introDurationLong }
NumberAnimation { target: horizontalLines; property: "stOpacity"; to: 0.3; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: horizontalLines; property: "ndOpacity"; to: 0.5; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: horizontalLines; property: "rdOpacity"; to: 0.6; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: horizontalLines; property: "foOpacity"; to: 0.6; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: horizontalLines; property: "fvOpacity"; to: 0.6; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: verticalLines; property: "stOpacity"; to: 1.0; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: verticalLines; property: "ndOpacity"; to: 1.0; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: verticalLines; property: "rdOpacity"; to: 1.0; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: verticalLines; property: "foOpacity"; to: 1.0; easing.type: Easing.OutCubic; duration: linesIntroDuration }
NumberAnimation { target: graphMask; property: "width"; to: graph.width; duration: 2*introDuration }
ParallelAnimation {
NumberAnimation { target: graphPoint; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
NumberAnimation { target: minHeader; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
}
NumberAnimation { target: maxHeader; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
}
SequentialAnimation {
id: heartbeat
loops: 2
ParallelAnimation {
NumberAnimation { target: heartImg; property: "height"; to: heartImgHeight * 1.2; easing.type: Easing.InCubic; duration: heartbeatDuration }
NumberAnimation { target: heartImg; property: "width"; to: heartImgWidth * 1.2; easing.type: Easing.InCubic; duration: heartbeatDuration }
}
ParallelAnimation {
NumberAnimation { target: heartImg; property: "height"; to: heartImgHeight; easing.type: Easing.InCubic; duration: heartbeatDuration }
NumberAnimation { target: heartImg; property: "width"; to: heartImgWidth; easing.type: Easing.InCubic; duration: heartbeatDuration }
}
}
Image {
id: heartImg
anchors.right: heartRate.left
anchors.rightMargin: 15
anchors.verticalCenter: heartRate.verticalCenter
source: "images/health/heart.png"
Behavior on width {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic;
}
}
Behavior on height {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic;
}
}
}
Text {
anchors.top: parent.top
anchors.topMargin: margin
anchors.horizontalCenter: parent.horizontalCenter
property int margin: startTopMargin
id: heartRate
text: HealthModel.HeartRate
font.pixelSize: 61
font.family: Theme.fontFamily
font.weight: Font.Medium
color: Theme.whiteColor
}
Column {
id: horizontalLines
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 38
property real stOpacity: startOp
property real ndOpacity: startOp
property real rdOpacity: startOp
property real foOpacity: startOp
property real fvOpacity: startOp
Repeater {
model: [
{ value: "200" },
{ value: "150" },
{ value: "100" },
{ value: "50" },
{ value: "" }
]
delegate: Item {
opacity: getHorizontalStripeOp(index)
Image {
id: horizontalImg
source: "images/health/horizontal-line.png"
}
Text {
anchors.left: horizontalImg.right
anchors.leftMargin: 10
anchors.verticalCenter: horizontalImg.verticalCenter
text: modelData.value
font.pixelSize: 17
font.family: Theme.fontFamily
font.weight: Font.Medium
color: Theme.grayColor
}
}
}
}
Row {
id: verticalLines
anchors.left: parent.left
anchors.leftMargin: 60
anchors.bottom: horizontalLines.bottom
spacing: 70
property real stOpacity: startOp
property real ndOpacity: startOp
property real rdOpacity: startOp
property real foOpacity: startOp
Repeater {
model: [
{ hour: "10:00" },
{ hour: "12:00" },
{ hour: "14:00" },
{ hour: "16:00" }
]
delegate: Item {
width: verticalImg.width
height: verticalImg.height
opacity: getVerticalStripeOp(index)
Image {
anchors.centerIn: parent
id: verticalImg
source: "images/health/vertical-line.png"
}
Text {
anchors.top: verticalImg.bottom
anchors.topMargin: 10
anchors.horizontalCenter: verticalImg.horizontalCenter
text: modelData.hour
font.pixelSize: 14
font.family: Theme.fontFamily
font.weight: Font.Medium
color: Theme.grayColor
}
}
}
}
/*
* Graph images
*/
Item {
id: graphMask
width:startWidth
height:graph.height
clip: true
anchors.bottom: horizontalLines.bottom
anchors.left: horizontalLines.left
Image {
id: graph
source: "images/health/graph.png"
}
}
Image {
id: graphPoint
source: "images/health/graph-point.png"
anchors.bottom: graphMask.bottom
anchors.bottomMargin: 50
anchors.right: graphMask.right
anchors.rightMargin: 7
opacity: startOp
}
/*
* Minimal heart rate container
*/
StaticText {
anchors.top: horizontalLines.bottom
anchors.topMargin: 40
anchors.left: parent.left
anchors.leftMargin: 120
opacity: startOp
id: minHeader
text: "MIN"
font.pixelSize: 14
font.family: Theme.fontFamily
font.weight: Font.Medium
color: Theme.grayColor
}
Text {
anchors.top: minHeader.bottom
anchors.horizontalCenter: minHeader.horizontalCenter
opacity: minHeader.opacity
text: HealthModel.MinHeartRate
font.pixelSize: 30
font.family: Theme.fontFamily
font.weight: Font.Medium
color: Theme.whiteColor
}
/*
* Maximal heart rate container
*/
StaticText {
anchors.top: horizontalLines.bottom
anchors.topMargin: 40
anchors.right: parent.right
anchors.rightMargin: 120
opacity: startOp
id: maxHeader
text: "MAX"
font.pixelSize: 14
font.family: Theme.fontFamily
font.weight: Font.Medium
color: Theme.grayColor
}
Text {
anchors.top: maxHeader.bottom
anchors.horizontalCenter: maxHeader.horizontalCenter
opacity: maxHeader.opacity
text: HealthModel.MaxHeartRate
font.pixelSize: 30
font.family: Theme.fontFamily
font.weight: Font.Medium
color: Theme.whiteColor
}
Timer {
id: degreeTimer
interval: 1000
running: true
repeat: true
onTriggered: {
HealthModel.update()
heartbeat.start()
}
}
onOnScreenChanged: {
if (onScreen) {
intro.start()
}
}
onOffScreenChanged: {
if (offScreen) {
resetIntro()
}
}
function resetIntro() {
intro.stop()
heartRate.margin = startTopMargin
horizontalLines.stOpacity = startOp
horizontalLines.ndOpacity = startOp
horizontalLines.rdOpacity = startOp
horizontalLines.foOpacity = startOp
horizontalLines.fvOpacity = startOp
verticalLines.stOpacity = startOp
verticalLines.ndOpacity = startOp
verticalLines.rdOpacity = startOp
verticalLines.foOpacity = startOp
graphMask.width = startWidth
graphPoint.opacity = startOp
minHeader.opacity = startOp
maxHeader.opacity = startOp
}
function getHorizontalStripeOp(index : int) : real {
switch(index) {
case 0:
return horizontalLines.stOpacity
case 1:
return horizontalLines.ndOpacity
case 2:
return horizontalLines.rdOpacity
case 3:
return horizontalLines.foOpacity
case 4:
return horizontalLines.fvOpacity
}
}
function getVerticalStripeOp(index : int) : real {
switch(index) {
case 0:
return verticalLines.stOpacity
case 1:
return verticalLines.ndOpacity
case 2:
return verticalLines.rdOpacity
case 3:
return verticalLines.foOpacity
}
}
}