212 lines
7.1 KiB
QML
212 lines
7.1 KiB
QML
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright (C) 2013 - 2024, nymea GmbH
|
|
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
|
*
|
|
* This file is part of nymea-app.
|
|
*
|
|
* nymea-app 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.
|
|
*
|
|
* nymea-app 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 nymea-app. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import Nymea
|
|
import QtQuick.Layouts
|
|
|
|
import "../utils"
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property Thing thing: null
|
|
property string stateName: ""
|
|
property StateType stateType: thing ? thing.thingClass.stateTypes.findByName(stateName) : null
|
|
|
|
property color color: Style.accentColor
|
|
property bool on: true
|
|
property real precision: 1
|
|
property real linePrecision: 1
|
|
|
|
readonly property State progressState: thing ? thing.states.getState(stateType.id) : null
|
|
readonly property State powerState: thing ? thing.stateByName("power") : null
|
|
|
|
property int startAngle: 135
|
|
property int maxAngle: 270
|
|
readonly property int steps: canvas.roundToPrecision(root.progressState.maxValue - root.progressState.minValue) / root.precision + 1
|
|
readonly property int lineSteps: canvas.roundToPrecision(root.progressState.maxValue - root.progressState.minValue, root.linePrecision) / root.linePrecision + 1
|
|
readonly property double stepSize: (root.progressState.maxValue - root.progressState.minValue) / steps
|
|
readonly property double anglePerStep: maxAngle / steps
|
|
readonly property double lineAnglePerStep: maxAngle / lineSteps
|
|
|
|
|
|
ActionQueue {
|
|
id: actionQueue
|
|
thing: root.thing
|
|
stateType: root.stateType
|
|
onPendingValueChanged: canvas.requestPaint()
|
|
}
|
|
|
|
ActionQueue {
|
|
id: powerActionQueue
|
|
thing: root.thing
|
|
stateName: "power"
|
|
}
|
|
|
|
Connections {
|
|
target: root.progressState
|
|
function onValueChanged() {
|
|
canvas.requestPaint()
|
|
}
|
|
}
|
|
|
|
|
|
Canvas {
|
|
id: canvas
|
|
anchors.centerIn: root
|
|
width: Math.min(root.width, root.height)
|
|
height: width
|
|
|
|
property color effectColor: root.on ? root.color : Style.iconColor
|
|
Behavior on effectColor { ColorAnimation { duration: Style.animationDuration } }
|
|
onEffectColorChanged: requestPaint()
|
|
|
|
function roundToPrecision(value, precision) {
|
|
var effectivePrecision = precision === undefined ? root.precision : precision;
|
|
var tmp = Math.round(value / effectivePrecision) * effectivePrecision;
|
|
return tmp;
|
|
}
|
|
|
|
onPaint: {
|
|
var ctx = getContext("2d");
|
|
ctx.reset();
|
|
|
|
var center = { x: canvas.width / 2, y: canvas.height / 2 };
|
|
|
|
// Step lines
|
|
var currentValue = actionQueue.pendingValue || root.progressState.value
|
|
var currentStepValue;
|
|
if (root.progressState) {
|
|
currentStepValue = roundToPrecision(currentValue - root.progressState.minValue, root.linePrecision)
|
|
}
|
|
|
|
// print("* current step", currentStepValue, root.lineSteps, currentValue)
|
|
|
|
for (var step = 0; step < lineSteps; step += 1) {
|
|
var stepValue = step * root.linePrecision
|
|
var angle = step * lineAnglePerStep + startAngle;
|
|
var innerRadius = canvas.width * 0.4
|
|
var outerRadius = canvas.width * 0.5
|
|
|
|
if (stepValue <= currentStepValue) {
|
|
ctx.strokeStyle = canvas.effectColor
|
|
innerRadius = canvas.width * 0.38
|
|
ctx.lineWidth = 4;
|
|
} else {
|
|
ctx.strokeStyle = Style.tileOverlayColor;
|
|
ctx.lineWidth = 1;
|
|
}
|
|
|
|
ctx.beginPath();
|
|
// rotate
|
|
//convert to radians
|
|
var rad = angle * Math.PI/180;
|
|
var c = Math.cos(rad);
|
|
var s = Math.sin(rad);
|
|
var innerPointX = center.x + (innerRadius * c);
|
|
var innerPointY = center.y + (innerRadius * s);
|
|
var outerPointX = center.x + (outerRadius * c);
|
|
var outerPointY = center.x + (outerRadius * s);
|
|
|
|
ctx.moveTo(innerPointX, innerPointY);
|
|
ctx.lineTo(outerPointX, outerPointY);
|
|
ctx.stroke();
|
|
ctx.closePath();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
MouseArea {
|
|
anchors.fill: canvas
|
|
|
|
property bool dragging: false
|
|
property double lastAngle
|
|
property double angleDiff: 0
|
|
|
|
onPressed: {
|
|
angleDiff = 0
|
|
lastAngle = calculateAngle(mouseX, mouseY)
|
|
}
|
|
|
|
onReleased: {
|
|
if (!dragging && root.powerState) {
|
|
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
|
|
powerActionQueue.sendValue(!root.powerState.value)
|
|
}
|
|
dragging = false
|
|
}
|
|
|
|
onPositionChanged: {
|
|
var angle = calculateAngle(mouseX, mouseY)
|
|
var tmpDiff = angle - lastAngle
|
|
if (tmpDiff > 300) {
|
|
tmpDiff -= 360
|
|
}
|
|
if (tmpDiff < -300) {
|
|
tmpDiff += 360
|
|
}
|
|
|
|
lastAngle = angle;
|
|
|
|
angleDiff += tmpDiff
|
|
if (Math.abs(angleDiff) > 1) {
|
|
dragging = true
|
|
}
|
|
|
|
var valueDiff = angleDiff / root.anglePerStep * root.stepSize
|
|
valueDiff = canvas.roundToPrecision(valueDiff)
|
|
if (Math.abs(valueDiff) > 0) {
|
|
var currentValue = actionQueue.pendingValue || root.progressState.value
|
|
var newValue = currentValue + valueDiff
|
|
newValue = Math.min(root.progressState.maxValue, Math.max(root.progressState.minValue, newValue))
|
|
print("newValue", newValue)
|
|
if (currentValue !== newValue) {
|
|
actionQueue.sendValue(newValue)
|
|
}
|
|
var steps = Math.round(valueDiff / root.stepSize)
|
|
angleDiff -= steps * root.anglePerStep
|
|
}
|
|
}
|
|
|
|
function calculateAngle(mouseX, mouseY) {
|
|
// transform coords to center of dial
|
|
mouseX -= canvas.width / 2
|
|
mouseY -= canvas.height / 2
|
|
|
|
var rad = Math.atan(mouseY / mouseX);
|
|
var angle = rad * 180 / Math.PI
|
|
|
|
angle += 90;
|
|
|
|
if (mouseX < 0 && mouseY >= 0) angle = 180 + angle;
|
|
if (mouseX < 0 && mouseY < 0) angle = 180 + angle;
|
|
|
|
return angle;
|
|
}
|
|
}
|
|
}
|