From 554575698bc6bc15bd4f2bb36c9f0a7d5898747c Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 Sep 2021 18:00:28 +0200 Subject: [PATCH] More work on the lights pages --- nymea-app/configuredhostsmodel.cpp | 1 + nymea-app/resources.qrc | 2 +- nymea-app/ui/components/ColorPicker.qml | 146 +++++++- .../ui/components/ColorTemperaturePicker.qml | 71 ++-- nymea-app/ui/delegates/InterfaceTile.qml | 2 +- .../devicelistpages/LightThingsListPage.qml | 2 +- nymea-app/ui/devicepages/LightDevicePage.qml | 193 ---------- nymea-app/ui/devicepages/LightThingPage.qml | 338 ++++++++++++++++++ nymea-app/ui/utils/NymeaUtils.qml | 2 +- 9 files changed, 514 insertions(+), 243 deletions(-) delete mode 100644 nymea-app/ui/devicepages/LightDevicePage.qml create mode 100644 nymea-app/ui/devicepages/LightThingPage.qml diff --git a/nymea-app/configuredhostsmodel.cpp b/nymea-app/configuredhostsmodel.cpp index 27b2f005..0a97f0c4 100644 --- a/nymea-app/configuredhostsmodel.cpp +++ b/nymea-app/configuredhostsmodel.cpp @@ -171,6 +171,7 @@ void ConfiguredHostsModel::saveToDisk() QSettings settings; settings.beginGroup("ConfiguredHosts"); settings.remove(""); + settings.setValue("currentIndex", m_currentIndex); for (int i = 0; i < m_list.count(); i++) { settings.beginGroup(QString::number(i)); settings.setValue("uuid", m_list.at(i)->uuid()); diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 6b5f9e70..b18e92d8 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -58,7 +58,7 @@ ui/devicepages/GarageThingPage.qml ui/devicepages/AwningThingPage.qml ui/devicepages/NotificationsDevicePage.qml - ui/devicepages/LightDevicePage.qml + ui/devicepages/LightThingPage.qml ui/devicepages/FingerprintReaderDevicePage.qml ui/devicepages/DeviceLogPage.qml ui/devicelistpages/GenericThingsListPage.qml diff --git a/nymea-app/ui/components/ColorPicker.qml b/nymea-app/ui/components/ColorPicker.qml index ebd4bc07..6d73a2ca 100644 --- a/nymea-app/ui/components/ColorPicker.qml +++ b/nymea-app/ui/components/ColorPicker.qml @@ -8,10 +8,25 @@ Item { 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 { @@ -30,6 +45,29 @@ Item { 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 { @@ -39,19 +77,100 @@ Item { } OpacityMask { anchors.fill: gradient - source: gradient + source: colorizer maskSource: mask } - 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" } + + 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) } } @@ -98,6 +217,13 @@ Item { 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) { diff --git a/nymea-app/ui/components/ColorTemperaturePicker.qml b/nymea-app/ui/components/ColorTemperaturePicker.qml index a5dc60cb..a734ef39 100644 --- a/nymea-app/ui/components/ColorTemperaturePicker.qml +++ b/nymea-app/ui/components/ColorTemperaturePicker.qml @@ -10,9 +10,10 @@ Item { property Thing thing: null - property int orientation: Qt.Horizontal + property int orientation: Qt.Vertical readonly property StateType colorTemperatureStateType: root.thing.thingClass.stateTypes.findByName("colorTemperature") + readonly property State powerState: root.thing.stateByName("power") property int value: thing.stateByName("colorTemperature").value @@ -23,10 +24,12 @@ Item { } Rectangle { + id: background width: Math.min(parent.width, parent.height) anchors.centerIn: parent height: width radius: width / 2 + visible: false gradient: Gradient { GradientStop { position: 0.0; color: "#dfffff" } GradientStop { position: 0.5; color: "#ffffea" } @@ -34,48 +37,44 @@ Item { } } -// Rectangle { -// id: clipRect -// anchors.fill: parent -// radius: Style.cornerRadius -// } + Desaturate { + anchors.fill: background + source: background + desaturation: root.powerState.value === true ? 0 : 1 + Behavior on desaturation { NumberAnimation { duration: Style.animationDuration } } + } -// LinearGradient { -// anchors.fill: parent -// start: root.orientation == Qt.Horizontal ? Qt.point(0, 0) : Qt.point(0, height) -// end: root.orientation == Qt.Horizontal ? Qt.point(width, 0) : Qt.point(0, 0) -// source: clipRect -// gradient: Gradient { -// GradientStop { position: 0.0; color: "#dfffff" } -// GradientStop { position: 0.5; color: "#ffffea" } -// GradientStop { position: 1.0; color: "#ffd649" } -// } -// } + Rectangle { + id: dragHandle + property double valuePercentage: ((actionQueue.pendingValue || root.value) - root.colorTemperatureStateType.minValue) / (root.colorTemperatureStateType.maxValue - root.colorTemperatureStateType.minValue) + width: 20 + height: 20 + radius: height / 2 + color: Style.backgroundColor + border.color: Style.foregroundColor + border.width: 2 + x: (parent.width - width) / 2 + y: parent.height * valuePercentage - (height / 2) -// Rectangle { -// id: dragHandle -// property double valuePercentage: ((actionQueue.pendingValue || root.value) - root.colorTemperatureStateType.minValue) / (root.colorTemperatureStateType.maxValue - root.colorTemperatureStateType.minValue) -// x: orientation == Qt.Horizontal ? valuePercentage * (root.width - dragHandle.width) : 0 -// y: root.orientation === Qt.Vertical ? root.height - dragHandle.height - (valuePercentage * (root.height - dragHandle.height)) : 0 -// height: root.orientation == Qt.Horizontal ? parent.height : 8 -// width: root.orientation == Qt.Horizontal ? 8 : parent.width -// radius: 4 -// color: Qt.tint(Style.backgroundColor, Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, 0.5)) -// } + } -// MouseArea { -// anchors.fill: parent -// onPositionChanged: { -// var minCt = root.colorTemperatureStateType.minValue; -// var maxCt = root.colorTemperatureStateType.maxValue -// var ct; + MouseArea { + anchors.fill: parent + onPositionChanged: { + var minCt = root.colorTemperatureStateType.minValue; + var maxCt = root.colorTemperatureStateType.maxValue + var ct; // if (root.orientation == Qt.Horizontal) { // ct = Math.min(maxCt, Math.max(minCt, (mouseX * (maxCt - minCt) / (width - dragHandle.width)) + minCt)) // } else { + // ct : y = max : height + ct = mouseY * (maxCt - minCt) / height + minCt + ct = Math.min(maxCt, ct) + ct = Math.max(minCt, ct) // ct = Math.min(maxCt, Math.max(minCt, ((height - mouseY) * (maxCt - minCt) / (height - dragHandle.height)) + minCt)) // } -// actionQueue.sendValue(ct); -// } -// } + actionQueue.sendValue(ct); + } + } } diff --git a/nymea-app/ui/delegates/InterfaceTile.qml b/nymea-app/ui/delegates/InterfaceTile.qml index 77392fa6..4f99725b 100644 --- a/nymea-app/ui/delegates/InterfaceTile.qml +++ b/nymea-app/ui/delegates/InterfaceTile.qml @@ -209,7 +209,7 @@ MainPageTile { case "light": var group = engine.thingManager.createGroup(Interfaces.findByName("colorlight"), thingsProxy); print("opening lights page for group", group) - pageStack.push("../devicepages/LightDevicePage.qml", {thing: group}) + pageStack.push("../devicepages/LightThingPage.qml", {thing: group}) } } } diff --git a/nymea-app/ui/devicelistpages/LightThingsListPage.qml b/nymea-app/ui/devicelistpages/LightThingsListPage.qml index 884dbe39..f11ef46c 100644 --- a/nymea-app/ui/devicelistpages/LightThingsListPage.qml +++ b/nymea-app/ui/devicelistpages/LightThingsListPage.qml @@ -107,7 +107,7 @@ ThingsListPageBase { property bool colorInverted: tileColored && NymeaUtils.isDark(Style.foregroundColor) === NymeaUtils.isDark(colorState.value) onClicked: { - if (isEnabled && (colorState || colorTemperatureState)) { + if (isEnabled /*&& (colorState || colorTemperatureState)*/) { root.enterPage(index) } else { itemDelegate.wobble() diff --git a/nymea-app/ui/devicepages/LightDevicePage.qml b/nymea-app/ui/devicepages/LightDevicePage.qml deleted file mode 100644 index 4be900b2..00000000 --- a/nymea-app/ui/devicepages/LightDevicePage.qml +++ /dev/null @@ -1,193 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU version 3. This project 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 -* this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -import QtQuick 2.9 -import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.1 -import Nymea 1.0 -import QtGraphicalEffects 1.0 -import "../components" - -ThingPageBase { - id: root - - readonly property State powerState: thing.stateByName("power") - - readonly property State brightnessState: thing.stateByName("brightness") - readonly property ActionType brightnessActionType: thingClass.actionTypes.findByName("brightness"); - - readonly property State colorState: thing.stateByName("color") - - readonly property StateType ctStateType: thingClass.stateTypes.findByName("colorTemperature") - readonly property State ctState: thing.stateByName("colorTemperature") - readonly property ActionType ctActionType: thingClass.actionTypes.findByName("colorTemperature") - - readonly property int statesCount: (powerState !== null ? 1 : 0) + - (brightnessState !== null ? 1 : 0) + - (ctState !== null ? 1 : 0) + - (colorState !== null ? 1 : 0) - - GridLayout { - anchors.fill: parent - anchors.margins: Style.bigMargins - columns: app.landscape ? root.statesCount : 1 - rowSpacing: Style.bigMargins - columnSpacing: Style.bigMargins - Layout.alignment: Qt.AlignCenter - - GridLayout { - Layout.fillHeight: true - Layout.fillWidth: !app.landscape - columnSpacing: app.margins - rowSpacing: app.margins - Layout.alignment: Qt.AlignHCenter - visible: root.ctStateType !== null - columns: app.landscape ? 1 : 4 - - Repeater { - model: ListModel { - ListElement { name: "activate"; ct: "0"; bri: 100; color: "#00c5ff" } - ListElement { name: "concentrate"; ct: "23"; bri: 100; color: "#3dddff" } - ListElement { name: "reading"; ct: "57"; bri: 100; color: "#f4de00" } - ListElement { name: "relax"; ct: "95" ; bri: 55; color: "#ffaf2a"} - } - delegate: ProgressButton { - Layout.preferredHeight: Style.hugeIconSize - Layout.preferredWidth: Style.hugeIconSize - imageSource: "../images/lighting/" + model.name + ".svg" - longpressEnabled: false -// mode: "normal" -// backgroundColor: model.color - - - onClicked: { - // Translate from % to absolute value in min/max - // % : 100 = abs : (max - min) - print("min,max", root.ctStateType, root.ctStateType.minValue, root.ctStateType.maxValue) - var absoluteCtValue = (model.ct * (root.ctStateType.maxValue - root.ctStateType.minValue) / 100) + root.ctStateType.minValue - var params = []; - var param1 = {}; - param1["paramName"] = root.ctActionType.paramTypes.get(0).name; - param1["value"] = absoluteCtValue; - params.push(param1) - root.thing.executeAction(root.ctActionType.name, params) - params = []; - param1 = {}; - param1["paramName"] = root.brightnessActionType.paramTypes.get(0).name; - param1["value"] = model.bri; - params.push(param1) - root.thing.executeAction(root.brightnessActionType.name, params) - } - } - } - } - - StackLayout { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.maximumHeight: width - currentIndex: selectionTabs.currentIndex - - Repeater { - model: modeModel - delegate: Loader { - sourceComponent: model.comp - } - } - - Component { - id: colorPickerComponent - ColorPicker { - anchors.fill: parent - thing: root.thing - } - } - - Component { - id: colorTemperatureComponent - ColorTemperaturePicker { - anchors.fill: parent - thing: root.thing - orientation: app.landscape ? Qt.Vertical : Qt.Horizontal - } - } - } - - ListModel { - id: modeModel - Component.onCompleted: { - if (root.thing.thingClass.stateTypes.findByName("color") !== null) { - append({modelData: qsTr("Color"), comp: colorPickerComponent}) - } - if (root.thing.thingClass.stateTypes.findByName("colorTemperature") !== null) { - append({modelData: qsTr("Temperature"), comp: colorTemperatureComponent}) - } - } - } - - SelectionTabs { - id: selectionTabs - Layout.fillWidth: true - model: modeModel - visible: modeModel.count > 1 - } - - - GridLayout { - id: basicItems - Layout.fillWidth: !app.landscape - Layout.fillHeight: app.landscape - Layout.alignment: Qt.AlignHCenter - columnSpacing: app.margins - rowSpacing: app.margins - columns: (app.landscape && (root.colorState !== null && root.ctState !== null)) - || (!app.landscape && (root.colorState === null && root.ctState === null)) ? 1 : 2 - - ProgressButton { - imageSource: root.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg" - mode: "normal" - size: Style.bigIconSize - longpressEnabled: false - onClicked: { - root.thing.executeAction("power", [{paramName: "power", value: !root.powerState.value}]) - } - } - - BrightnessSlider { - Layout.fillWidth: orientation == Qt.Horizontal - Layout.fillHeight: orientation == Qt.Vertical - thing: root.thing - orientation: basicItems.columns === 1 ? Qt.Vertical : Qt.Horizontal - visible: root.thing.stateByName("brightness") !== null - } - } - } -} diff --git a/nymea-app/ui/devicepages/LightThingPage.qml b/nymea-app/ui/devicepages/LightThingPage.qml new file mode 100644 index 00000000..2892b23b --- /dev/null +++ b/nymea-app/ui/devicepages/LightThingPage.qml @@ -0,0 +1,338 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project 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 +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 +import QtQuick.Controls.Material 2.1 +import Nymea 1.0 +import QtGraphicalEffects 1.0 +import "../components" +import "../utils" + +ThingPageBase { + id: root + + readonly property State powerState: thing.stateByName("power") + + readonly property State brightnessState: thing.stateByName("brightness") + readonly property ActionType brightnessActionType: thingClass.actionTypes.findByName("brightness"); + + readonly property State colorState: thing.stateByName("color") + + readonly property StateType ctStateType: thingClass.stateTypes.findByName("colorTemperature") + readonly property State ctState: thing.stateByName("colorTemperature") + readonly property ActionType ctActionType: thingClass.actionTypes.findByName("colorTemperature") + + readonly property int statesCount: (powerState !== null ? 1 : 0) + + (brightnessState !== null ? 1 : 0) + + (ctState !== null ? 1 : 0) + + (colorState !== null ? 1 : 0) + + GridLayout { + anchors.fill: parent + anchors.margins: Style.bigMargins + columns: app.landscape ? root.statesCount : 1 + rowSpacing: Style.bigMargins + columnSpacing: Style.bigMargins + Layout.alignment: Qt.AlignCenter + + GridLayout { + Layout.fillHeight: true + Layout.fillWidth: !app.landscape + columnSpacing: app.margins + rowSpacing: app.margins + Layout.alignment: Qt.AlignHCenter + visible: root.ctStateType !== null + columns: app.landscape ? 1 : 4 + + Repeater { + model: ListModel { + ListElement { name: "activate"; ct: "0"; bri: 100; color: "#00c5ff" } + ListElement { name: "concentrate"; ct: "23"; bri: 100; color: "#3dddff" } + ListElement { name: "reading"; ct: "57"; bri: 100; color: "#f4de00" } + ListElement { name: "relax"; ct: "95" ; bri: 55; color: "#ffaf2a"} + } + delegate: ProgressButton { + Layout.preferredHeight: Style.hugeIconSize + Layout.preferredWidth: Style.hugeIconSize + imageSource: "../images/lighting/" + model.name + ".svg" + longpressEnabled: false +// mode: "normal" +// backgroundColor: model.color + + + onClicked: { + // Translate from % to absolute value in min/max + // % : 100 = abs : (max - min) + print("min,max", root.ctStateType, root.ctStateType.minValue, root.ctStateType.maxValue) + var absoluteCtValue = (model.ct * (root.ctStateType.maxValue - root.ctStateType.minValue) / 100) + root.ctStateType.minValue + var params = []; + var param1 = {}; + param1["paramName"] = root.ctActionType.paramTypes.get(0).name; + param1["value"] = absoluteCtValue; + params.push(param1) + root.thing.executeAction(root.ctActionType.name, params) + params = []; + param1 = {}; + param1["paramName"] = root.brightnessActionType.paramTypes.get(0).name; + param1["value"] = model.bri; + params.push(param1) + root.thing.executeAction(root.brightnessActionType.name, params) + } + } + } + } + + ColumnLayout { + spacing: Style.margins + + StackLayout { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.maximumHeight: width + currentIndex: selectionTabs.currentIndex + + Repeater { + model: modeModel + delegate: Loader { + sourceComponent: model.comp + } + } + + Component { + id: colorPickerComponent + ColorPicker { + anchors.fill: parent + thing: root.thing + } + } + + Component { + id: colorTemperatureComponent + ColorTemperaturePicker { + anchors.fill: parent + thing: root.thing + orientation: app.landscape ? Qt.Vertical : Qt.Horizontal + } + } + + Component { + id: brightnessComponent + Item { + id: brightnessController + property Thing thing: root.thing + readonly property State brightnessState: thing ? thing.stateByName("brightness") : null + readonly property State colorState: thing ? thing.stateByName("color") : null + readonly property State powerState: thing ? thing.stateByName("power") : null + + ActionQueue { + id: actionQueue + thing: brightnessController.thing + stateType: thing.thingClass.stateTypes.findByName("brightness") + } + + Rectangle { + id: brightnessCircle + anchors.centerIn: parent + width: Math.min(parent.width, parent.height) + height: width + radius: width / 2 + color: Style.tileBackgroundColor + + } + + ShaderEffect { + anchors.fill: brightnessCircle + property Item source: ShaderEffectSource { + id: shaderSource + anchors.fill: parent + sourceItem: brightnessCircle + hideSource: true + } + property color inColor: Style.tileBackgroundColor + property color outColor: powerState.value === true + ? colorState ? colorState.value : "#ffd649" + : Style.tileOverlayColor + Behavior on outColor { ColorAnimation { duration: Style.animationDuration } } + + property real threshold: 0.1 + property real brightness: 1 - (actionQueue.pendingValue || brightnessState.value) / 100 + + fragmentShader: " + varying highp vec2 qt_TexCoord0; + uniform sampler2D source; + uniform highp vec4 outColor; + uniform highp vec4 inColor; + uniform lowp float threshold; + uniform lowp float qt_Opacity; + uniform lowp float brightness; + void main() { + bool isOn = qt_TexCoord0.y > brightness; + lowp vec4 sourceColor = texture2D(source, qt_TexCoord0); + if (isOn) { + gl_FragColor = mix(vec4(outColor.rgb, 1.0) * sourceColor.a, sourceColor, step(threshold, distance(sourceColor.rgb / sourceColor.a, inColor.rgb))) * qt_Opacity; + } else { + gl_FragColor = sourceColor; + } + }" + + } + + MouseArea { + anchors.fill: brightnessCircle + onMouseYChanged: { + var progress = 1 - mouseY / height + actionQueue.sendValue(progress * 100); + } + } + } + } + + Component { + id: powerComponent + Item { + id: powerController + property Thing thing: root.thing + readonly property State powerState: thing ? thing.stateByName("power") : null + + property color borderColor: "#ffd649" + ActionQueue { + id: actionQueue + thing: powerController.thing + stateType: thing.thingClass.stateTypes.findByName("power") + } + + Rectangle { + id: background + anchors.centerIn: parent + width: Math.min(parent.width, parent.height) + height: width + color: Style.tileBackgroundColor + radius: width / 2 + + ColorIcon { + anchors.centerIn: parent + size: Style.hugeIconSize + name: (actionQueue.pendingValue || powerState.value) === true ? "light-on" : "light-off" + // color: (actionQueue.pendingValue || powerState.value) === true ? Style.accentColor : Style.iconColor + } + + MouseArea { + anchors.fill: parent + onClicked: { + actionQueue.sendValue(!powerState.value) + } + } + } + + RadialGradient { + id: gradient + anchors.fill: background + visible: false + gradient: Gradient{ + GradientStop { position: .45; color: "transparent" } + GradientStop { position: .5; color: Qt.rgba(borderColor.r, borderColor.g, borderColor.b, 1) } + } + } + OpacityMask { + opacity: (actionQueue.pendingValue || powerState.value) === true ? 1 : 0 + anchors.fill: gradient + source: gradient + maskSource: background + Behavior on opacity { NumberAnimation { duration: Style.animationDuration } } + + } + } + } + } + + ListModel { + id: modeModel + Component.onCompleted: { + if (root.colorState) { + append({modelData: qsTr("Color"), comp: colorPickerComponent}) + } + if (root.ctState) { + append({modelData: qsTr("Temperature"), comp: colorTemperatureComponent}) + } + if (root.brightnessState && !root.ctState && !root.colorState) { + append({modelData: qsTr("Brightness"), comp: brightnessComponent}) + } + if (!root.colorState && !root.ctState && !root.brightnessState) { + append({modelData: qsTr("Power"), comp: powerComponent}) + } + } + } + + SelectionTabs { + id: selectionTabs + Layout.fillWidth: true + model: modeModel + visible: modeModel.count > 1 + } + } + + + + GridLayout { + id: basicItems + Layout.fillWidth: !app.landscape + Layout.fillHeight: app.landscape + Layout.alignment: Qt.AlignHCenter + columnSpacing: app.margins + rowSpacing: app.margins + columns: app.landscape ? 1 : 2 + visible: powerButton.visible || brightnessSlider.visible + + ProgressButton { + id: powerButton + imageSource: root.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg" + mode: "normal" + size: Style.bigIconSize + longpressEnabled: false + visible: root.brightnessState || root.ctState || root.colorState + onClicked: { + root.thing.executeAction("power", [{paramName: "power", value: !root.powerState.value}]) + } + } + + BrightnessSlider { + id: brightnessSlider + Layout.fillWidth: orientation == Qt.Horizontal + Layout.fillHeight: orientation == Qt.Vertical + Layout.alignment: Qt.AlignHCenter + thing: root.thing + orientation: basicItems.columns === 1 ? Qt.Vertical : Qt.Horizontal + visible: root.brightnessState && (root.ctState || root.colorState) + } + } + } +} diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml index 05870cad..b2450cf0 100644 --- a/nymea-app/ui/utils/NymeaUtils.qml +++ b/nymea-app/ui/utils/NymeaUtils.qml @@ -38,7 +38,7 @@ Item { } else if (interfaceList.indexOf("garagedoor") >= 0 ) { page = "GarageThingPage.qml"; } else if (interfaceList.indexOf("light") >= 0) { - page = "LightDevicePage.qml"; + page = "LightThingPage.qml"; } else if (interfaceList.indexOf("shutter") >= 0 || interfaceList.indexOf("blind") >= 0) { page = "ShutterDevicePage.qml"; } else if (interfaceList.indexOf("awning") >= 0) {