diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 471c4d2e..14678866 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -234,5 +234,7 @@ ui/images/sensors/moisture.svg ui/images/sensors/pressure.svg ui/images/sensors/temperature.svg + ui/components/MainPageTile.qml + ui/images/configure.svg diff --git a/nymea-app/styles/dark/ApplicationWindow.qml b/nymea-app/styles/dark/ApplicationWindow.qml index cea9b461..2b501ab5 100644 --- a/nymea-app/styles/dark/ApplicationWindow.qml +++ b/nymea-app/styles/dark/ApplicationWindow.qml @@ -10,4 +10,9 @@ ApplicationWindow { Material.theme: Material.Dark Material.accent: guhAccent Material.primary: Material.background + + function interfaceToColor(name) { + return "khaki" + } + } diff --git a/nymea-app/ui/MainPage.qml b/nymea-app/ui/MainPage.qml index 9038673e..c7fe8202 100644 --- a/nymea-app/ui/MainPage.qml +++ b/nymea-app/ui/MainPage.qml @@ -81,6 +81,7 @@ Page { id: favoritesViewComponent FavoritesView { id: favoritesView + objectName: "favorites" width: swipeView.width height: swipeView.height property string title: qsTr("My favorites") @@ -201,6 +202,7 @@ Page { id: tabEntryComponent MainPageTabButton { property int pageIndex: 0 +// height: tabBar.height onClicked: settings.currentMainViewIndex = pageIndex alignment: app.landscape ? Qt.Horizontal : Qt.Vertical } diff --git a/nymea-app/ui/components/FancyHeader.qml b/nymea-app/ui/components/FancyHeader.qml index 4aebfd81..9c3e7b0f 100644 --- a/nymea-app/ui/components/FancyHeader.qml +++ b/nymea-app/ui/components/FancyHeader.qml @@ -32,7 +32,7 @@ ToolBar { verticalAlignment: Text.AlignVCenter font.pixelSize: app.mediumFont elide: Text.ElideRight - text: root.title.toLowerCase(); + text: root.title } HeaderButton { diff --git a/nymea-app/ui/components/GuhHeader.qml b/nymea-app/ui/components/GuhHeader.qml index 63c3daf2..751025d1 100644 --- a/nymea-app/ui/components/GuhHeader.qml +++ b/nymea-app/ui/components/GuhHeader.qml @@ -40,7 +40,7 @@ ToolBar { verticalAlignment: Text.AlignVCenter font.pixelSize: app.mediumFont elide: Text.ElideRight - text: root.text.toLowerCase(); + text: root.text } } } diff --git a/nymea-app/ui/components/MainPageTabButton.qml b/nymea-app/ui/components/MainPageTabButton.qml index 5a216242..a0d67798 100644 --- a/nymea-app/ui/components/MainPageTabButton.qml +++ b/nymea-app/ui/components/MainPageTabButton.qml @@ -8,6 +8,12 @@ TabButton { property string iconSource property var alignment: Qt.Vertical + Rectangle { + anchors.fill: parent + color: Material.foreground + opacity: 0.05 + } + contentItem: GridLayout { columns: root.alignment === Qt.Vertical ? 1 : 2 ColorIcon { diff --git a/nymea-app/ui/components/MainPageTile.qml b/nymea-app/ui/components/MainPageTile.qml new file mode 100644 index 00000000..9b31d484 --- /dev/null +++ b/nymea-app/ui/components/MainPageTile.qml @@ -0,0 +1,108 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Controls.Material 2.2 +import QtQuick.Layouts 1.3 + +Item { + id: root + + property alias iconName: colorIcon.name + property alias fallbackIconName: fallbackIcon.name + property alias iconColor: colorIcon.color + property string text + property bool disconnected: false + property bool batteryCritical: false + + property alias contentItem: innerContent.children + + signal clicked(); + signal pressAndHold(); + + Pane { + anchors.fill: parent + anchors.margins: app.margins / 2 + Material.elevation: 1 + padding: 0 + + contentItem: ItemDelegate { + padding: 0; topPadding: 0; bottomPadding: 0 + onClicked: root.clicked() + onPressAndHold: root.pressAndHold() + + contentItem: ColumnLayout { + spacing: 0 + ColumnLayout { + Layout.fillHeight: true + Layout.margins: app.margins + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ColorIcon { + id: colorIcon + anchors.centerIn: parent + height: app.iconSize * 1.3 + width: height + ColorIcon { + id: fallbackIcon + anchors.fill: parent + color: root.iconColor + visible: parent.status === Image.Error + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Label { + id: label + anchors.centerIn: parent + width: parent.width + text: root.text.toUpperCase() + font.pixelSize: app.smallFont + font.letterSpacing: 1 + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 2 + elide: Text.ElideRight + } + } + + } + MouseArea { + id: innerContent + Layout.fillWidth: true + Layout.preferredHeight: app.iconSize + app.margins * 2 + visible: root.contentItem.length > 1 + + Rectangle { + anchors.fill: parent + color: Material.foreground + opacity: 0.05 + } + } + } + } + } + + Row { + id: quickAlertPane + anchors { top: parent.top; right: parent.right; margins: app.margins } + ColorIcon { + height: app.iconSize / 2 + width: height + name: "../images/dialog-warning-symbolic.svg" + color: "red" + visible: root.disconnected + } + ColorIcon { + height: app.iconSize / 2 + width: height + name: "../images/battery/battery-010.svg" + visible: root.batteryCritical + } + } +} + diff --git a/nymea-app/ui/images/configure.svg b/nymea-app/ui/images/configure.svg new file mode 100644 index 00000000..00655d2f --- /dev/null +++ b/nymea-app/ui/images/configure.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/nymea-app/ui/mainviews/DevicesPageDelegate.qml b/nymea-app/ui/mainviews/DevicesPageDelegate.qml index 8dd61738..e7c0c91a 100644 --- a/nymea-app/ui/mainviews/DevicesPageDelegate.qml +++ b/nymea-app/ui/mainviews/DevicesPageDelegate.qml @@ -5,257 +5,134 @@ import QtQuick.Controls.Material 2.2 import Nymea 1.0 import "../components" -Item { +MainPageTile { id: root - Pane { - anchors.fill: parent - anchors.margins: app.margins / 2 - Material.elevation: 1 + text: interfaceToString(model.name).toUpperCase() + iconName: interfaceToIcon(model.name) + iconColor: app.guhAccent + disconnected: devicesSubProxyConnectables.count > 0 + batteryCritical: devicesSubProxyBattery.count > 0 - Column { - width: parent.width - anchors.centerIn: parent - anchors.verticalCenterOffset: -app.iconSize - spacing: app.margins - ColorIcon { - height: app.iconSize * 1.3 - width: height - color: app.guhAccent - anchors.horizontalCenter: parent.horizontalCenter - name: interfaceToIcon(model.name) - } - - Label { - text: interfaceToString(model.name).toUpperCase() - font.pixelSize: app.extraSmallFont - font.bold: true - font.letterSpacing: 1 - width: parent.width - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - } + onClicked: { + var page; + switch (model.name) { + case "light": + page = "LightsDeviceListPage.qml" + break; + default: + page = "GenericDeviceListPage.qml" } - - MouseArea { - anchors.fill: parent - onClicked: { - var page; - switch (model.name) { - case "light": - page = "LightsDeviceListPage.qml" - break; - default: - page = "GenericDeviceListPage.qml" - } - if (model.name === "uncategorized") { - pageStack.push(Qt.resolvedUrl("../devicelistpages/" + page), {hiddenInterfaces: app.supportedInterfaces}) - } else { - pageStack.push(Qt.resolvedUrl("../devicelistpages/" + page), {shownInterfaces: [model.name]}) - } - } - } - - DevicesProxy { - id: devicesProxy - devices: Engine.deviceManager.devices - shownInterfaces: [model.name] + if (model.name === "uncategorized") { + pageStack.push(Qt.resolvedUrl("../devicelistpages/" + page), {hiddenInterfaces: app.supportedInterfaces}) + } else { + pageStack.push(Qt.resolvedUrl("../devicelistpages/" + page), {shownInterfaces: [model.name]}) } } - Row { - id: quickAlertPane - anchors { top: parent.top; right: parent.right; margins: app.margins } - DevicesProxy { - id: devicesSubProxyConnectables - devices: devicesProxy - filterDisconnected: true - } - ColorIcon { - height: app.iconSize / 2 - width: height - name: "../images/dialog-warning-symbolic.svg" - color: "red" - visible: devicesSubProxyConnectables.count > 0 - } - DevicesProxy { - id: devicesSubProxyBattery - devices: devicesProxy - filterBatteryCritical: true - } - ColorIcon { - height: app.iconSize / 2 - width: height - name: "../images/battery/battery-010.svg" - visible: devicesSubProxyBattery.count > 0 - } + DevicesProxy { + id: devicesProxy + devices: Engine.deviceManager.devices + shownInterfaces: [model.name] } + DevicesProxy { + id: devicesSubProxyConnectables + devices: devicesProxy + filterDisconnected: true + } + DevicesProxy { + id: devicesSubProxyBattery + devices: devicesProxy + filterBatteryCritical: true + } - Item { - id: inlineControlPane - anchors { left: parent.left; bottom: parent.bottom; right: parent.right; margins: app.margins / 2 } - height: app.iconSize + app.margins * 2 - Rectangle { - anchors.fill: parent - // color: app.guhAccent - color: "black" - opacity: .05 + contentItem: Loader { + id: inlineControlLoader + anchors { + fill: parent + leftMargin: app.margins / 2 + rightMargin: app.margins / 2 } - Loader { - id: inlineControlLoader - anchors { - fill: parent - leftMargin: app.margins - rightMargin: app.margins - topMargin: app.margins / 2 - bottomMargin: app.margins / 2 - } - sourceComponent: { - switch (model.name) { - case "sensor": - case "weather": - return labelComponent; + sourceComponent: { + switch (model.name) { + case "sensor": + case "weather": + return labelComponent; - case "light": - case "media": - case "garagegate": - case "blind": - case "extendedblind": - case "shutter": - case "extendedshutter": - case "awning": - case "extendedawning": - return buttonComponent - default: - console.warn("DevicesPageDelegate, inlineControl: Unhandled interface", model.name) - } + case "light": + case "media": + case "garagegate": + case "blind": + case "extendedblind": + case "shutter": + case "extendedshutter": + case "awning": + case "extendedawning": + return buttonComponent + default: + console.warn("DevicesPageDelegate, inlineControl: Unhandled interface", model.name) } } } Component { id: buttonComponent - MouseArea { - onClicked: { - switch (model.name) { - case "light": - if (devicesProxy.count == 1) { - var device = devicesProxy.get(0); - var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); - var stateType = deviceClass.stateTypes.findByName("power") - var actionType = deviceClass.actionTypes.findByName("power") - var params = []; - var param1 = {}; - param1["paramTypeId"] = actionType.paramTypes.get(0).id; - param1["value"] = !device.states.getState(stateType.id).value; - params.push(param1) - Engine.deviceManager.executeAction(device.id, actionType.id, params) - } else { + ColumnLayout { + spacing: 0 + + Label { + id: label + Layout.fillWidth: true + text: { + switch (model.name) { + case "media": + return devicesProxy.get(0).name; + case "light": + var count = 0; for (var i = 0; i < devicesProxy.count; i++) { var device = devicesProxy.get(i); var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); - var actionType = deviceClass.actionTypes.findByName("power"); - - var params = []; - var param1 = {}; - param1["paramTypeId"] = actionType.paramTypes.get(0).id; - param1["value"] = false; - params.push(param1) - Engine.deviceManager.executeAction(device.id, actionType.id, params) + var stateType = deviceClass.stateTypes.findByName("power") + if (device.states.getState(stateType.id).value === true) { + count++; + } } + return count === 0 ? qsTr("All off") : qsTr("%1 on").arg(count) + case "garagegate": + var count = 0; + for (var i = 0; i < devicesProxy.count; i++) { + var device = devicesProxy.get(i); + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var stateType = deviceClass.stateTypes.findByName("state"); + if (device.states.getState(stateType.id).value !== "closed") { + count++; + } + } + return count === 0 ? qsTr("All closed") : qsTr("%1 open").arg(count) + case "blind": + case "extendedblind": + case "awning": + case "extendedawning": + case "shutter": + case "extendedshutter": + return qsTr("%1 installed").arg(devicesProxy.count) } - break; - case "media": - var device = devicesProxy.get(0) - var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); - var stateType = deviceClass.stateTypes.findByName("playbackStatus"); - var state = device.states.getState(stateType.id) - - var actionName - switch (state.value) { - case "Playing": - actionName = "pause"; - break; - case "Paused": - actionName = "play"; - break; - } - var actionTypeId = deviceClass.actionTypes.findByName(actionName).id; - - print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes) - - Engine.deviceManager.executeAction(device.id, actionTypeId) - case "garagegate": - case "shutter": - case "extendedshutter": - case "blind": - case "extendedblind": - case "awning": - case "extendedawning": - case "simpleclosable": - for (var i = 0; i < devicesProxy.count; i++) { - var device = devicesProxy.get(i); - var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); - var actionType = deviceClass.actionTypes.findByName("close"); - Engine.deviceManager.executeAction(device.id, actionType.id) - } - - default: - console.warn("DevicesPageDelegate, inlineButtonControl clicked: Unhandled interface", model.name) + console.warn("DevicesPageDelegate, inlineButtonControl: Unhandled interface", model.name) } + font.pixelSize: app.smallFont + elide: Text.ElideRight } + ItemDelegate { + Layout.preferredHeight: app.iconSize + Layout.preferredWidth: height + Layout.alignment: Qt.AlignRight - ColumnLayout { - anchors.fill: parent - - Label { - id: label - Layout.fillWidth: true - text: { - switch (model.name) { - case "media": - return devicesProxy.get(0).name; - case "light": - var count = 0; - for (var i = 0; i < devicesProxy.count; i++) { - var device = devicesProxy.get(i); - var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); - var stateType = deviceClass.stateTypes.findByName("power") - if (device.states.getState(stateType.id).value === true) { - count++; - } - } - return count === 0 ? qsTr("All off") : qsTr("%1 on").arg(count) - case "garagegate": - var count = 0; - for (var i = 0; i < devicesProxy.count; i++) { - var device = devicesProxy.get(i); - var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); - var stateType = deviceClass.stateTypes.findByName("state"); - if (device.states.getState(stateType.id).value !== "closed") { - count++; - } - } - return count === 0 ? qsTr("All closed") : qsTr("%1 open").arg(count) - case "blind": - case "extendedblind": - case "awning": - case "extendedawning": - case "shutter": - case "extendedshutter": - return qsTr("%1 installed").arg(devicesProxy.count) - } - console.warn("DevicesPageDelegate, inlineButtonControl: Unhandled interface", model.name) - } - font.pixelSize: app.smallFont - elide: Text.ElideRight - } ColorIcon { id: icon - width: app.largeFont + width: app.iconSize height: width color: app.guhAccent - Layout.alignment: Qt.AlignRight + name: { switch (model.name) { case "media": @@ -281,13 +158,86 @@ Item { } } } + + onClicked: { + switch (model.name) { + case "light": + if (devicesProxy.count == 1) { + var device = devicesProxy.get(0); + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var stateType = deviceClass.stateTypes.findByName("power") + var actionType = deviceClass.actionTypes.findByName("power") + var params = []; + var param1 = {}; + param1["paramTypeId"] = actionType.paramTypes.get(0).id; + param1["value"] = !device.states.getState(stateType.id).value; + params.push(param1) + Engine.deviceManager.executeAction(device.id, actionType.id, params) + } else { + for (var i = 0; i < devicesProxy.count; i++) { + var device = devicesProxy.get(i); + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var actionType = deviceClass.actionTypes.findByName("power"); + + var params = []; + var param1 = {}; + param1["paramTypeId"] = actionType.paramTypes.get(0).id; + param1["value"] = false; + params.push(param1) + Engine.deviceManager.executeAction(device.id, actionType.id, params) + } + } + break; + case "media": + var device = devicesProxy.get(0) + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var stateType = deviceClass.stateTypes.findByName("playbackStatus"); + var state = device.states.getState(stateType.id) + + var actionName + switch (state.value) { + case "Playing": + actionName = "pause"; + break; + case "Paused": + actionName = "play"; + break; + } + var actionTypeId = deviceClass.actionTypes.findByName(actionName).id; + + print("executing", device, device.id, actionTypeId, actionName, deviceClass.actionTypes) + + Engine.deviceManager.executeAction(device.id, actionTypeId) + case "garagegate": + case "shutter": + case "extendedshutter": + case "blind": + case "extendedblind": + case "awning": + case "extendedawning": + case "simpleclosable": + for (var i = 0; i < devicesProxy.count; i++) { + var device = devicesProxy.get(i); + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var actionType = deviceClass.actionTypes.findByName("close"); + Engine.deviceManager.executeAction(device.id, actionType.id) + } + + default: + console.warn("DevicesPageDelegate, inlineButtonControl clicked: Unhandled interface", model.name) + } + } + } + } } Component { id: labelComponent ColumnLayout { + spacing: 0 + property var device: devicesProxy.get(0) property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null property var state: deviceClass ? device.states.getState(deviceClass.stateTypes.findByName("temperature").id) : null diff --git a/nymea-app/ui/mainviews/FavoritesView.qml b/nymea-app/ui/mainviews/FavoritesView.qml index 02982c2c..3ae73926 100644 --- a/nymea-app/ui/mainviews/FavoritesView.qml +++ b/nymea-app/ui/mainviews/FavoritesView.qml @@ -8,6 +8,7 @@ import "../components" Item { id: root + property bool editMode: false readonly property int count: tagsProxy.count TagsProxyModel { @@ -22,48 +23,123 @@ Item { readonly property int minTileWidth: 180 readonly property int minTileHeight: 240 readonly property int tilesPerRow: root.width / minTileWidth + cellWidth: gridView.width / tilesPerRow cellHeight: cellWidth model: tagsProxy - delegate: favoritesDelegateComponent + delegate: MainPageTile { + id: delegateRoot + width: gridView.cellWidth + height: gridView.cellHeight + text: device.name.toUpperCase() + iconName: app.interfacesToIcon(deviceClass.interfaces) + iconColor: app.guhAccent + visible: !fakeDragItem.visible || fakeDragItem.deviceId !== device.id + batteryCritical: batteryCriticalState && batteryCriticalState.value === true + disconnected: connectedState && connectedState.value === false + + property var modelIndex: index + + property string deviceId: model.deviceId + property string ruleId: model.ruleId + readonly property var device: Engine.deviceManager.devices.getDevice(deviceId) + readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null + readonly property var connectedState: deviceClass.interfaces.indexOf("connectable") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("connected").id) : null + readonly property var batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null + + onClicked: pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(deviceClass.interfaces)), {device: device}) + + onPressAndHold: root.editMode = true + + contentItem: Loader { + id: loader + anchors.fill: parent + sourceComponent: { + if (delegateRoot.deviceClass.interfaces.indexOf("closable") >= 0) { + return closableComponent; + } + if (delegateRoot.deviceClass.interfaces.indexOf("light") >= 0) { + return lightsComponent; + } + if (delegateRoot.deviceClass.interfaces.indexOf("sensor") >= 0) { + return sensorsComponent; + } + if (delegateRoot.deviceClass.interfaces.indexOf("weather") >= 0) { + return sensorsComponent; + } + } + Binding { target: loader.item ? loader.item : null; property: "deviceClass"; value: delegateRoot.deviceClass } + Binding { target: loader.item ? loader.item : null; property: "device"; value: delegateRoot.device } + } + + SequentialAnimation { + loops: Animation.Infinite + running: root.editMode + alwaysRunToEnd: true + NumberAnimation { from: 0; to: 3; target: delegateRoot; duration: 75; property: "rotation" } + NumberAnimation { from: 3; to: -3; target: delegateRoot; duration: 150; property: "rotation" } + NumberAnimation { from: -3; to: 0; target: delegateRoot; duration: 75; property: "rotation" } + } + } + MouseArea { - id: dndArea + id: dragArea anchors.fill: parent + enabled: root.editMode propagateComposedEvents: true + property var dragOffset: ({}) + property var draggedItem: null + + onPressed: { + var item = gridView.itemAt(mouseX, mouseY) + draggedItem = item; + dragOffset = mapToItem(item, mouseX, mouseY) + fakeDragItem.x = mouseX - dragOffset.x; + fakeDragItem.y = mouseY - dragOffset.y; + fakeDragItem.text = item.text + fakeDragItem.iconName = item.iconName + fakeDragItem.iconColor = item.iconColor; + fakeDragItem.deviceId = item.device.id + fakeDragItem.batteryCritical = item.batteryCritical + fakeDragItem.disconnected = item.disconnected + drag.target = fakeDragItem + } + onReleased: { + drag.target = null + draggedItem = null + } + + onClicked: { + root.editMode = false + } + } + + MainPageTile { + id: fakeDragItem + width: gridView.cellWidth + height: gridView.cellHeight + Drag.active: dragArea.drag.active + visible: Drag.active + property var deviceId + } + + DropArea { + id: dropArea + anchors.fill: gridView property int from: -1 property int to: -1 - property int index: gridView.indexAt(mouseX, mouseY) // Item underneath cursor - property var dndDelegate: null - property int dx - property int dy - - onPressAndHold: { - //currentId = icons.get(newIndex = index).gridId - preventStealing = true; + onEntered: { + var index = gridView.indexAt(drag.x + dragArea.dragOffset.x, drag.y + dragArea.dragOffset.y); from = index; - print("pressandHold on", index) - var tag = tagsProxy.get(index); - var originalDelegate = gridView.itemAt(mouseX, mouseY) - dndDelegate = favoritesDelegateComponent.createObject(dndArea, {deviceId: tag.deviceId, ruleId: tag.ruleId, x: originalDelegate.x, y: originalDelegate.y}) - dx = mouseX - originalDelegate.x; - dy = mouseY - originalDelegate.y; - } - onReleased: { - preventStealing = false; - from = -1; - dndDelegate.destroy(); + to = index; } onPositionChanged: { - if (dndDelegate) { - dndDelegate.x = mouseX - dx - dndDelegate.y = mouseY - dy - } - - if (dndArea.from >= 0 && to != index && from != index) { + var index = gridView.indexAt(drag.x + dragArea.dragOffset.x, drag.y + dragArea.dragOffset.y); + if (to !== index && from !== index && index >= 0 && index <= tagsProxy.count) { to = index; print("should move", from, "to", to) for (var i = 0; i < tagsProxy.count; i++) { @@ -88,80 +164,248 @@ Item { } from = index; } - // tagsProxy.move(newIndex, newIndex = index, 1) } } - - } Component { - id: favoritesDelegateComponent - Item { - id: delegateRoot - property string deviceId: model.deviceId - property string ruleId: model.ruleId - readonly property var device: Engine.deviceManager.devices.getDevice(deviceId) - readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null + id: lightsComponent + RowLayout { + property var device: null + property var deviceClass: null - visible: index !== undefined && index !== dndArea.from - width: gridView.cellWidth - height: gridView.cellHeight - - Pane { - id: pane - anchors.fill: parent - anchors.margins: app.margins / 2 - Material.elevation: 1 - ColumnLayout { - anchors.centerIn: parent - width: parent.width - app.margins * 2 - spacing: app.margins - - ColorIcon { - Layout.preferredWidth: app.iconSize * 1.3 - Layout.preferredHeight: width - name: app.interfacesToIcon(delegateRoot.deviceClass.interfaces) - color: app.guhAccent - Layout.alignment: Qt.AlignHCenter - } - - Label { - Layout.fillWidth: true - text: delegateRoot.device.name.toUpperCase() - font.pixelSize: app.extraSmallFont - font.bold: true - font.letterSpacing: 1 - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignHCenter - } + ItemDelegate { + Layout.preferredWidth: app.iconSize + Layout.preferredHeight: app.iconSize + Layout.leftMargin: app.margins / 2 + Layout.alignment: Qt.AlignVCenter + padding: 0; topPadding: 0; bottomPadding: 0 + contentItem: ColorIcon { + name: "../images/light-off.svg" + color: app.guhAccent } - } - - Row { - id: quickAlertPane - anchors { top: parent.top; right: parent.right; margins: app.margins } - ColorIcon { - height: app.iconSize / 2 - width: height - name: "../images/dialog-warning-symbolic.svg" - color: "red" - property var connectedState: delegateRoot.deviceClass.interfaces.indexOf("connectable") >= 0 ? delegateRoot.device.states.getState(delegateRoot.deviceClass.stateTypes.findByName("connected").id) : null - visible: connectedState !== null && connectedState.value === false - } - ColorIcon { - height: app.iconSize / 2 - width: height - name: "../images/battery/battery-010.svg" - property var batteryCriticalState: delegateRoot.deviceClass.interfaces.indexOf("battery") >= 0 ? delegateRoot.device.states.getState(delegateRoot.deviceClass.stateTypes.findByName("batteryCritical").id) : null - visible: batteryCriticalState !== null && batteryCriticalState.value === true - } - } - - MouseArea { - anchors.fill: parent onClicked: { - pageStack.push(Qt.resolvedUrl("../devicepages/" + app.interfaceListToDevicePage(delegateRoot.deviceClass.interfaces)), {device: delegateRoot.device}) + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var actionType = deviceClass.actionTypes.findByName("power"); + var params = []; + var powerParam = {} + powerParam["paramTypeId"] = actionType.paramTypes.get(0).id; + powerParam["value"] = false; + params.push(powerParam) + Engine.deviceManager.executeAction(device.id, actionType.id, params); + } + } + + ThrottledSlider { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + visible: deviceClass.interfaces.indexOf("dimmablelight") >= 0 + readonly property var brightnessStateType: deviceClass.stateTypes.findByName("brightness"); + readonly property var brightnessState: device.states.getState(brightnessStateType.id) + from: 0 + to: 100 + value: brightnessState.value + onMoved: { + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var actionType = deviceClass.actionTypes.findByName("brightness"); + var params = []; + var powerParam = {} + powerParam["paramTypeId"] = actionType.paramTypes.get(0).id; + powerParam["value"] = value; + params.push(powerParam) + Engine.deviceManager.executeAction(device.id, actionType.id, params); + } + } + + ItemDelegate { + Layout.preferredWidth: app.iconSize + Layout.preferredHeight: width + Layout.rightMargin: app.margins / 2 + Layout.alignment: Qt.AlignVCenter + padding: 0; topPadding: 0; bottomPadding: 0 + contentItem: ColorIcon { + name: "../images/light-on.svg" + color: app.guhAccent + } + onClicked: { + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var actionType = deviceClass.actionTypes.findByName("power"); + var params = []; + var powerParam = {} + powerParam["paramTypeId"] = actionType.paramTypes.get(0).id; + powerParam["value"] = true; + params.push(powerParam) + Engine.deviceManager.executeAction(device.id, actionType.id, params); + } + } + } + } + + Component { + id: sensorsComponent + RowLayout { + id: sensorsRoot + property var device: null + property var deviceClass: null + spacing: 0 + + property var shownInterfaces: [] + property int currentStateIndex: -1 + property var currentStateType: deviceClass ? deviceClass.stateTypes.findByName(shownInterfaces[currentStateIndex].state) : null + property var currentState: currentStateType ? device.states.getState(currentStateType.id) : null + + onDeviceClassChanged: { + if (deviceClass == null) { + return; + } + + var tmp = [] + if (deviceClass.interfaces.indexOf("temperaturesensor") >= 0) { + tmp.push({iface: "temperaturesensor", state: "temperature"}); + } + if (deviceClass.interfaces.indexOf("humiditysensor") >= 0) { + tmp.push({iface: "humiditysensor", state: "humidity"}); + } + if (deviceClass.interfaces.indexOf("moisturesensor") >= 0) { + tmp.push({iface: "moisturesensor", state: "moisture"}); + } + if (deviceClass.interfaces.indexOf("pressuresensor") >= 0) { + tmp.push({iface: "pressuresensor", state: "pressure"}); + } + if (deviceClass.interfaces.indexOf("lightsensor") >= 0) { + tmp.push({iface: "lightsensor", state: "lightIntensity"}); + } + if (deviceClass.interfaces.indexOf("conductivitysensor") >= 0) { + tmp.push({iface: "conductivitysensor", state: "conductivity"}); + } + if (deviceClass.interfaces.indexOf("weather") >= 0) { + tmp.push({iface: "temperaturesensor", state: "temperature"}); + tmp.push({iface: "humiditysensor", state: "humidity"}); + tmp.push({iface: "pressuresensor", state: "pressure"}); + } + + shownInterfaces = tmp + currentStateIndex = 0 + } + + ItemDelegate { + Layout.preferredWidth: app.iconSize + Layout.preferredHeight: width + Layout.leftMargin: app.margins / 2 + Layout.alignment: Qt.AlignVCenter + padding: 0; topPadding: 0; bottomPadding: 0 + visible: sensorsRoot.shownInterfaces.length > 1 + contentItem: ColorIcon { + name: "../images/back.svg" + } + onClicked: { + var newIndex = sensorsRoot.currentStateIndex - 1; + if (newIndex < 0) newIndex = sensorsRoot.shownInterfaces.length - 1 + sensorsRoot.currentStateIndex = newIndex; + } + } + + ColorIcon { + Layout.preferredWidth: app.iconSize + Layout.preferredHeight: width + Layout.alignment: Qt.AlignVCenter + color: app.interfaceToColor(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface) + name: app.interfaceToIcon(sensorsRoot.shownInterfaces[sensorsRoot.currentStateIndex].iface) + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + text: sensorsRoot.currentStateType.unitString + font.pixelSize: app.smallFont + color: app.guhAccent + } + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + text: sensorsRoot.currentState.value// + " " + sensorsRoot.currentStateType.unitString + color: app.guhAccent + } + } + + ItemDelegate { + Layout.preferredWidth: app.iconSize + Layout.preferredHeight: width + Layout.rightMargin: app.margins / 2 + Layout.alignment: Qt.AlignVCenter + padding: 0; topPadding: 0; bottomPadding: 0 + visible: sensorsRoot.shownInterfaces.length > 1 + contentItem: ColorIcon { + name: "../images/next.svg" + } + onClicked: { + var newIndex = sensorsRoot.currentStateIndex + 1; + if (newIndex >= sensorsRoot.shownInterfaces.length) newIndex = 0; + sensorsRoot.currentStateIndex = newIndex; + } + } + + } + } + + Component { + id: closableComponent + RowLayout { + property var device: null + property var deviceClass: null + + ItemDelegate { + Layout.preferredWidth: app.iconSize + Layout.preferredHeight: width + Layout.leftMargin: app.margins / 2 + Layout.alignment: Qt.AlignVCenter + padding: 0; topPadding: 0; bottomPadding: 0 + contentItem: ColorIcon { + name: "../images/up.svg" + color: app.guhAccent + } + onClicked: { + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var actionType = deviceClass.actionTypes.findByName("open"); + Engine.deviceManager.executeAction(device.id, actionType.id); + } + } + + Slider { + id: closableSlider + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + visible: deviceClass.interfaces.indexOf("extendedclosable") >= 0 + readonly property var percentageStateType: deviceClass.stateTypes.findByName("percentage"); + readonly property var percentateState: device.states.getState(percentageStateType.id) + from: 0 + to: 100 + value: percentateState.value + } + Item { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + visible: !closableSlider.visible + } + + ItemDelegate { + Layout.preferredWidth: app.iconSize + Layout.preferredHeight: width + Layout.rightMargin: app.margins / 2 + Layout.alignment: Qt.AlignVCenter + padding: 0; topPadding: 0; bottomPadding: 0 + contentItem: ColorIcon { + name: "../images/down.svg" + color: app.guhAccent + } + onClicked: { + var deviceClass = Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); + var actionType = deviceClass.actionTypes.findByName("close"); + Engine.deviceManager.executeAction(device.id, actionType.id); } } } diff --git a/nymea-app/ui/mainviews/ScenesView.qml b/nymea-app/ui/mainviews/ScenesView.qml index c089f9cb..b04f0819 100644 --- a/nymea-app/ui/mainviews/ScenesView.qml +++ b/nymea-app/ui/mainviews/ScenesView.qml @@ -25,13 +25,20 @@ Item { } cellWidth: width / tilesPerRow cellHeight: cellWidth - delegate: Item { - id: scenesDelegate + + delegate: MainPageTile { width: interfacesGridView.cellWidth height: interfacesGridView.cellHeight + iconName: iconTag ? "../images/" + iconTag.value + ".svg" : "../images/slideshow.svg"; + fallbackIconName: "../images/slideshow.svg" + iconColor: colorTag ? colorTag.value : app.guhAccent; + text: model.name.toUpperCase() property var colorTag: Engine.tagsManager.tags.findRuleTag(model.id, "color") property var iconTag: Engine.tagsManager.tags.findRuleTag(model.id, "icon") + + onClicked: Engine.ruleManager.executeActions(model.id) + Connections { target: Engine.tagsManager.tags onCountChanged: { @@ -39,51 +46,6 @@ Item { iconTag = Engine.tagsManager.tags.findRuleTag(model.id, "icon") } } - - Pane { - anchors.fill: parent - anchors.margins: app.margins / 2 - Material.elevation: 1 - padding: 0 - ItemDelegate { - anchors.fill: parent - onClicked: { - Engine.ruleManager.executeActions(model.id) - } - contentItem: ColumnLayout { - width: parent.width - anchors.centerIn: parent - spacing: app.margins - - ColorIcon { - Layout.preferredHeight: app.iconSize * 1.3 - Layout.preferredWidth: height - Layout.alignment: Qt.AlignHCenter - name: scenesDelegate.iconTag ? "../images/" + scenesDelegate.iconTag.value + ".svg" : "../images/slideshow.svg"; - color: scenesDelegate.colorTag ? scenesDelegate.colorTag.value : app.guhAccent; - - ColorIcon { - anchors.fill: parent - name: "../images/slideshow.svg" - color: app.guhAccent - visible: parent.status === Image.Error - } - } - - Label { - Layout.fillWidth: true - text: model.name.toUpperCase() - font.pixelSize: app.extraSmallFont - font.bold: true - font.letterSpacing: 1 - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignHCenter - maximumLineCount: 2 - elide: Text.ElideRight - } - } - } - } } } }