C
Qt Quick Ultralite Thermostat Demo
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
import QtQuick 2.15
import QtQuickUltralite.Extras 2.0
import Thermo 1.0
ThermoView {
id: root
property int maxTemp: Units.fahrenheitToTemperatureUnit(90)
property int minTemp: Units.fahrenheitToTemperatureUnit(50)
property int prevTemp: GlobalState.selectedRoom.temperature
property int smallestAngle: 25
property alias thermoOn: powerBtn.checked
function _setTemperatureImpl(t: real, stopAnimation: bool) {
GlobalState.selectedRoom.temperature = Math.min(Math.max(Math.round(t), minTemp), maxTemp);
tens.stopAnimation = stopAnimation;
ones.stopAnimation = stopAnimation;
tens.reversed = GlobalState.selectedRoom.temperature < prevTemp;
ones.reversed = GlobalState.selectedRoom.temperature < prevTemp;
prevTemp = GlobalState.selectedRoom.temperature;
}
function setTemperature(t: real) {
_setTemperatureImpl(t, false);
}
function setTemperatureWithoutAnimation(t: real) {
_setTemperatureImpl(t, true);
}
Item {
anchors.horizontalCenter: parent.horizontalCenter
height: width
width: wheelImg.width
y: Theme.thermoY
Image {
id: wheelImg
source: root.thermoOn ? "jog.png" : "jog-off.png"
}
Row {
id: thermoText
anchors.centerIn: parent
clip: true
height: Theme.roomViewTempHeight
width: Theme.roomViewTempWidth
AnimatedDigit {
id: tens
stopAnimation: true
value: GlobalState.selectedRoom.temperature / 10
visible: root.thermoOn
}
AnimatedDigit {
id: ones
stopAnimation: true
value: GlobalState.selectedRoom.temperature % 10
visible: root.thermoOn
}
}
Image {
id: digitMaskTop
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: thermoText.top
source: "digitMaskTop.png"
}
Image {
id: digitMaskBottom
anchors.bottom: thermoText.bottom
anchors.horizontalCenter: parent.horizontalCenter
source: "digitMaskBottom.png"
}
Text {
anchors.left: thermoText.right
anchors.top: thermoText.top
anchors.topMargin: Theme.isBig ? 13 : 11
color: "#3d464d"
font.pixelSize: Theme.wheelTempUnitSize
text: Units.temperatureSymbol
visible: root.thermoOn
}
MouseArea {
height: parent.height / 2
visible: root.thermoOn
width: parent.width
onClicked: {
root.setTemperature(GlobalState.selectedRoom.temperature + 1);
}
Image {
anchors.centerIn: parent
source: parent.pressed ? "pressed-bg-up.png" : ""
}
ColorizedImage {
anchors.centerIn: parent
anchors.verticalCenterOffset: Theme.tempControlOffset
color: parent.pressed ? ColorStyle.blue : ColorStyle.greyMedium3
source: "temp-up-pressed.png"
}
}
MouseArea {
anchors.bottom: parent.bottom
height: parent.height / 2
visible: root.thermoOn
width: parent.width
onClicked: {
root.setTemperature(GlobalState.selectedRoom.temperature - 1);
}
Image {
anchors.centerIn: parent
source: parent.pressed ? "pressed-bg-down.png" : ""
}
ColorizedImage {
anchors.centerIn: parent
anchors.verticalCenterOffset: -Theme.tempControlOffset
color: parent.pressed ? ColorStyle.blue : ColorStyle.greyMedium3
source: "temp-down-pressed.png"
}
}
Image {
id: thermoHandle
property real angle: (90 + root.smallestAngle + (GlobalState.selectedRoom.temperature - root.minTemp) / (root.maxTemp - root.minTemp) * (360 - 2 * root.smallestAngle)) * 2 * Math.PI / 360
source: "images/inner-circle.png"
visible: root.thermoOn
x: parent.width / 2 - width / 2 + Theme.wheelSize * Math.cos(angle)
y: parent.height / 2 - height / 2 + Theme.wheelSize * Math.sin(angle)
z: 10
MouseArea {
property real pressedX: 0
property real pressedY: 0
anchors.centerIn: parent
height: Theme.thermoHandleSize
width: Theme.thermoHandleSize
onPositionChanged: {
var px = mouse.x - pressedX + x + width / 2 + parent.x - parent.parent.width / 2;
var py = mouse.y - pressedY + y + height / 2 + parent.y - parent.parent.height / 2;
var d = Math.sqrt(px * px + py * py);
if (d < Theme.wheelSize * 0.6)
return; // too far from the position
var angle = Math.atan2(px / d, py / d) * 360 / (Math.PI * 2);
angle = (360 - angle);
while (angle > 360)
angle -= 360;
var temperature = root.minTemp + (angle - root.smallestAngle) * (root.maxTemp - root.minTemp) / (360 - 2 * root.smallestAngle);
root.setTemperatureWithoutAnimation(temperature);
}
onPressed: {
pressedX = mouse.x;
pressedY = mouse.y;
}
Image {
anchors.centerIn: parent
source: "thermo-handle.png"
visible: parent.pressed
}
}
}
}
Text {
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.isBig ? 36 : 12
anchors.horizontalCenter: parent.horizontalCenter
color: GlobalState.selectedRoom.status == Room.Off ? ColorStyle.greyDark1 : ColorStyle.greyDark4
font.pixelSize: Theme.wheelStatusTextSize
text: {
switch (GlobalState.selectedRoom.status) {
//% "Heating"
case Room.Heating:
return qsTrId("id-heating");
//% "Cooling"
case Room.Cooling:
return qsTrId("id-cooling");
//% "Off"
default:
return qsTrId("id-off");
}
}
visible: root.thermoOn || Theme.isBig
}
ColorizedImage {
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.isBig ? 15 : 0
anchors.horizontalCenter: parent.horizontalCenter
color: GlobalState.selectedRoom.status == Room.Heating ? ColorStyle.pinkyRed : ColorStyle.blue
source: "status-small.png"
visible: root.thermoOn
}
Column {
anchors.left: parent.left
anchors.leftMargin: Theme.roomButtonMargin
spacing: Theme.roomButtonSpacing
y: Theme.roomButtonsY
RoomControlButton {
id: autoBtn
checked: GlobalState.selectedRoom.auto_
enabled: root.thermoOn
icon.source: "auto-on.png"
onCheckedChanged: {
if (checked) {
dryerBtn.checked = fanBtn.checked = ecoBtn.checked = streamerBtn.checked = false;
}
GlobalState.selectedRoom.auto_ = checked;
}
}
RoomControlButton {
id: dryerBtn
checked: GlobalState.selectedRoom.dryer
enabled: root.thermoOn
icon.source: "dryer-on.png"
onCheckedChanged: {
if (checked) {
autoBtn.checked = false;
}
GlobalState.selectedRoom.dryer = checked;
}
}
FanControlButton {
id: fanBtn
currentRoom: GlobalState.selectedRoom
enabled: root.thermoOn
}
}
Column {
anchors.right: parent.right
anchors.rightMargin: Theme.roomButtonMargin
spacing: Theme.roomButtonSpacing
y: Theme.roomButtonsY
RoomControlButton {
id: powerBtn
checked: GlobalState.selectedRoom.power
icon.source: "power-on.png"
pulsing: !checked
onCheckedChanged: {
GlobalState.selectedRoom.power = checked;
}
}
RoomControlButton {
id: ecoBtn
checked: GlobalState.selectedRoom.eco
enabled: root.thermoOn
icon.source: "eco-on.png"
onCheckedChanged: {
if (checked) {
autoBtn.checked = false;
}
GlobalState.selectedRoom.eco = checked;
}
}
RoomControlButton {
id: streamerBtn
checked: GlobalState.selectedRoom.streamer
enabled: root.thermoOn
icon.source: "streamer-on.png"
onCheckedChanged: {
if (checked) {
autoBtn.checked = false;
}
GlobalState.selectedRoom.streamer = checked;
}
}
}
}