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 @@
+
+
+
+
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
- }
- }
- }
- }
}
}
}