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
Item {
id: root
height: Theme.appHeight
width: Theme.appWidth
property bool onScreen: false
property bool offScreen: false
property real baseOp: 0.0
property real stripesSideMargin: 4
property real stripesBottomMargin: 40
property real stripesIconsMargin: 30
property real needleOffset: 18
property real secondNeedleOffset: 20
property real arrowAnimationMs: 30
property real updateInterval: 1000 // One second
property int introDuration: 100
SequentialAnimation {
id: intro
NumberAnimation { target: watchIndicators; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
NumberAnimation { target: stripesBackground; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
ParallelAnimation {
NumberAnimation { target: batteryIcon; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
NumberAnimation { target: stepsIcon; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
NumberAnimation { target: heartBeat; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
NumberAnimation { target: calendar; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
}
ParallelAnimation {
NumberAnimation { target: secondArrow; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
NumberAnimation { target: centerDot; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: introDuration }
}
}
Timer {
running: true
repeat: true
interval: updateInterval
onTriggered: WatchModel.update()
}
Image {
id: watchIndicators
anchors.centerIn: parent
source: "images/shield/dots-all.png"
opacity: baseOp
}
/*
* Health content
*/
Image {
id: stripesBackground
source: "images/shield/small-oval-alt.png"
anchors.centerIn: parent
opacity: baseOp
}
Image {
id: batteryIcon
source: "images/shield/battery-icon.png"
anchors.verticalCenter: stripesBackground.verticalCenter
anchors.left: stripesBackground.left
anchors.leftMargin: root.stripesIconsMargin
opacity: baseOp
}
Image {
id: stepsIcon
source: "images/shield/steps-icon.png"
anchors.verticalCenter: stripesBackground.verticalCenter
anchors.right: stripesBackground.right
anchors.rightMargin: root.stripesIconsMargin
opacity: baseOp
}
Row {
id: heartBeat
opacity: baseOp
spacing: 10
anchors.top: parent.top
anchors.topMargin: parent.height / 4
anchors.horizontalCenter: parent.horizontalCenter
Image {
anchors.verticalCenter: parent.verticalCenter
source: "images/shield/heart.png"
}
Text {
anchors.verticalCenter: parent.verticalCenter
text: HealthModel.HeartRate
font.pixelSize: 34
font.family: Theme.fontFamily
font.weight: Font.Light
color: Theme.whiteColor
}
}
GradientShape {
id: batteryBar
anchors.fill: parent
SequentialAnimation {
running: true
loops: Animation.Infinite
NumberAnimation {
target: batteryBar
property: "extent"
from: 1.0
to: 0.0
duration: 30000
}
NumberAnimation {
target: batteryBar
property: "extent"
from: 0.0
to: 1.0
duration: 30000
}
}
}
GradientShape {
id: healthBar
anchors.fill: parent
flip: true
SequentialAnimation {
running: true
loops: Animation.Infinite
NumberAnimation {
target: healthBar
property: "extent"
from: 0.0
to: 1.0
duration: 45000
}
}
}
/*
* Watch arrows
*/
Image {
id: minuteArrow
source: "images/shield/hand-minute.png"
anchors.top: parent.top
anchors.topMargin: parent.height / 2 - height + needleOffset
anchors.horizontalCenter: parent.horizontalCenter
transform: Rotation {
angle: WatchModel.MinuteAngle
origin.x: minuteArrow.width / 2
origin.y: minuteArrow.height - needleOffset
}
}
Image {
id: hourArrow
source: "images/shield/hand-hour.png"
anchors.top: parent.top
anchors.topMargin: parent.height / 2 - height + needleOffset
anchors.horizontalCenter: parent.horizontalCenter
transform: Rotation {
angle: WatchModel.HourAngle
origin.x: hourArrow.width / 2
origin.y: hourArrow.height - needleOffset
}
}
Image {
id: secondArrow
source: "images/shield/hand-second.png"
anchors.top: parent.top
anchors.topMargin: parent.height / 2 - height + secondNeedleOffset
anchors.horizontalCenter: parent.horizontalCenter
opacity: baseOp
transform: Rotation {
angle: WatchModel.SecondAngle
origin.x: secondArrow.width / 2
origin.y: secondArrow.height - secondNeedleOffset
}
}
Image {
id: centerDot
source: "images/shield/hands-middle-dot.png"
anchors.centerIn: parent
opacity: baseOp
}
/*
* Calendar text
*/
Column {
id: calendar
opacity: baseOp
anchors.bottom: parent.bottom
anchors.bottomMargin: parent.height / 4
anchors.horizontalCenter: parent.horizontalCenter
StaticText {
anchors.horizontalCenter: parent.horizontalCenter
text: "Friday"
font.pixelSize: 26
font.family: Theme.fontFamily
font.weight: Font.Light
color: Theme.whiteColor
}
StaticText {
anchors.horizontalCenter: parent.horizontalCenter
text: "16.08"
font.pixelSize: 26
font.family: Theme.fontFamily
font.weight: Font.Light
color: Theme.whiteColor
}
}
onOnScreenChanged: {
console.log("Screen changed")
if (onScreen) {
intro.start()
}
}
onOffScreenChanged: {
if (offScreen) {
resetIntro()
}
}
function resetIntro() {
intro.stop()
watchIndicators.opacity = baseOp
secondArrow.opacity = baseOp
stripesBackground.opacity = baseOp
batteryIcon.opacity = baseOp
stepsIcon.opacity = baseOp
heartBeat.opacity = baseOp
calendar.opacity = baseOp
}
}