This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.

277 lines
9.5 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 2.9
import QtGraphicalEffects 1.0
import Nymea 1.0
import "../utils"
Item {
id: root
property Thing thing: null
readonly property State colorState: thing ? thing.stateByName("color") : null
readonly property State powerState: thing ? thing.stateByName("power") : null
Connections {
target: colorState
onValueChanged: {
if (actionQueue.pendingValue === null) {
actionQueue.useStoredPoint = false
}
}
}
ActionQueue {
id: actionQueue
thing: root.thing
stateType: thing.thingClass.stateTypes.findByName("color")
property bool useStoredPoint: false
property point storedPoint: Qt.point(0, 0)
}
ConicalGradient {
id: gradient
anchors.centerIn: parent
width: Math.min(400, Math.min(parent.width, parent.height))
height: width
visible: false
gradient: Gradient{
id: g
GradientStop { position: 0.000; color: Qt.rgba(1, 0, 0, 1) }
GradientStop { position: 0.167; color: Qt.rgba(1, 1, 0, 1) }
GradientStop { position: 0.333; color: Qt.rgba(0, 1, 0, 1) }
GradientStop { position: 0.500; color: Qt.rgba(0, 1, 1, 1) }
GradientStop { position: 0.667; color: Qt.rgba(0, 0, 1, 1) }
GradientStop { position: 0.833; color: Qt.rgba(1, 0, 1, 1) }
GradientStop { position: 1.000; color: Qt.rgba(1, 0, 0, 1) }
}
onWidthChanged: dragHandle.updatePoint()
onHeightChanged: dragHandle.updatePoint()
RadialGradient {
anchors.fill: gradient
gradient: Gradient{
GradientStop { position: 0.05; color: Qt.rgba(1, 1, 1, 1) }
GradientStop { position: 0.10; color: Qt.rgba(1, 1, 1, .9) }
GradientStop { position: 0.20; color: Qt.rgba(1, 1, 1, .7) }
GradientStop { position: 0.30; color: Qt.rgba(1, 1, 1, .5) }
GradientStop { position: 0.40; color: Qt.rgba(1, 1, 1, .3) }
GradientStop { position: 0.50; color: "transparent" }
}
}
}
Desaturate {
id: colorizer
anchors.fill: gradient
source: gradient
desaturation: root.powerState.value === true ? 0 : 1
Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } }
visible: false
}
Rectangle {
id: mask
anchors.fill: gradient
radius: width / 2
}
OpacityMask {
anchors.fill: gradient
source: colorizer
maskSource: mask
}
Rectangle {
id: dragHandle
width: 20
height: 20
radius: height / 2
color: Style.backgroundColor
border.color: Style.foregroundColor
border.width: 2
x: point.x + gradient.width / 2 + gradient.x - width / 2
y: point.y + gradient.height / 2 + gradient.y - height / 2
property color shownColor: root.colorState ? actionQueue.pendingValue || root.colorState.value : "white`"
onShownColorChanged: updatePoint()
// Component.onCompleted: updatePoint()
property point point: Qt.point(0,0);
function updatePoint() {
if (actionQueue.useStoredPoint) {
point = actionQueue.storedPoint
return
}
// print("current color:", shownColor.r, shownColor.g, shownColor.b)
var whitePart = Math.min(Math.min(shownColor.r, shownColor.g), shownColor.b)
var stopIndex = 0
var progressInStop = 0
if (shownColor.r === 1) {
if (shownColor.g > shownColor.b) {
stopIndex = 0
progressInStop = shownColor.g - whitePart
} else {
stopIndex = 5
progressInStop = 1 - shownColor.b + whitePart
}
}
if (shownColor.g === 1) {
if (shownColor.r > shownColor.b) {
stopIndex = 1
progressInStop = 1 - shownColor.r + whitePart
} else {
stopIndex = 2
progressInStop = shownColor.b - whitePart
}
}
if (shownColor.b === 1) {
if (shownColor.r > shownColor.g) {
stopIndex = 4
progressInStop = shownColor.r - whitePart
} else {
stopIndex = 3
progressInStop = 1-shownColor.g + whitePart
}
}
var stopBefore = g.stops[stopIndex]
var stopAfter = g.stops[stopIndex+1]
// print("stopIndex", stopIndex)
// print("stopBefore:", stopBefore.color.r, stopBefore.color.g, stopBefore.color.b)
// print("stopAfter:", stopAfter.color.r, stopAfter.color.g, stopAfter.color.b)
// print("progressInStop", progressInStop)
// print("beforePosition", stopBefore.position)
var positionInGradient = stopBefore.position + (stopAfter.position - stopBefore.position) * progressInStop
// print("positionInGradient", positionInGradient)
var degrees = 360 * positionInGradient;
degrees -= 90;
var radian = degrees * 0.0174532925
var radius = gradient.height * 0.9 / 2 * (1-whitePart)
var x = radius * Math.cos(radian)
var y = radius * Math.sin(radian)
// print("degrees", degrees)
// print("radius", radius)
// print("Setting point to", x, y)
point = Qt.point(x, y)
}
}
MouseArea {
id: mouseArea
anchors.fill: gradient
onPositionChanged: {
var angle = calculateAngle(mouseX, mouseY)
var position = angle / 360;
var stopBefore = null;
var stopAfter = null;
for (var i = 0; i < g.stops.length; i++) {
var stop = g.stops[i];
if (stop.position < position) {
stopBefore = stop;
continue
}
stopAfter = stop;
break;
}
var colorBefore = stopBefore.color
var colorAfter = stopAfter.color
// p : 1 = pis : (a - b)
var positionInStop = (position - stopBefore.position) / (stopAfter.position - stopBefore.position);
var dr = stopAfter.color.r - stopBefore.color.r;
var dg = stopAfter.color.g - stopBefore.color.g;
var db = stopAfter.color.b - stopBefore.color.b;
var distanceFromCenter = calculateDistance(mouseX, mouseY)
// Reduce width a bit to keep outside circle for full color intensity
var positionFromCenter = Math.min(distanceFromCenter / (width * 0.9 / 2), 1)
// Invert it, The further we're away, the less impact this should have
positionFromCenter = 1 - positionFromCenter;
print("pos", positionFromCenter)
var color = Qt.rgba((stopBefore.color.r + dr * positionInStop) + positionFromCenter,
(stopBefore.color.g + dg * positionInStop) + positionFromCenter,
(stopBefore.color.b + db * positionInStop) + positionFromCenter,
1)
actionQueue.sendValue(color);
// Store the coordinates (limited to the circle) as the above calculation is lossy so we can't precicely
// calcuate the position from the color but we don't want the drag handle jumping while dragging.
var rad = (angle - 90) / 180 * Math.PI
var radius = Math.min(distanceFromCenter, width * 0.9 / 2)
actionQueue.storedPoint = Qt.point(radius * Math.cos(rad), radius * Math.sin(rad))
actionQueue.useStoredPoint = true
}
function calculateAngle(mouseX, mouseY) {
// transform coords to center of dial
mouseX -= mouseArea.width / 2
mouseY -= mouseArea.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;
}
function calculateDistance(mouseX, mouseY) {
mouseX -= mouseArea.width / 2
mouseY -= mouseArea.height / 2
return Math.abs(Math.sqrt(Math.pow(mouseX, 2) + Math.pow(mouseY, 2)))
}
}
}