diff --git a/nymea-app/configuredhostsmodel.cpp b/nymea-app/configuredhostsmodel.cpp index 04ed7ce6..6a2c973b 100644 --- a/nymea-app/configuredhostsmodel.cpp +++ b/nymea-app/configuredhostsmodel.cpp @@ -115,6 +115,20 @@ void ConfiguredHostsModel::removeHost(int index) } } +void ConfiguredHostsModel::move(int from, int to) +{ + // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/ + // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list + // adjust the model's index by +1 in case we're moving upwards + int newModelIndex = to > from ? to+1 : to; + + qWarning() << "from:" << from << "to" << to << "modelTo" << newModelIndex; + beginMoveRows(QModelIndex(), from, from, QModelIndex(), newModelIndex); + m_list.move(from, to); + saveToDisk(); + endMoveRows(); +} + int ConfiguredHostsModel::indexOf(ConfiguredHost *host) const { return m_list.indexOf(host); diff --git a/nymea-app/configuredhostsmodel.h b/nymea-app/configuredhostsmodel.h index 64890775..e24ab108 100644 --- a/nymea-app/configuredhostsmodel.h +++ b/nymea-app/configuredhostsmodel.h @@ -60,6 +60,7 @@ public: Q_INVOKABLE ConfiguredHost* get(int index) const; Q_INVOKABLE ConfiguredHost* createHost(); Q_INVOKABLE void removeHost(int index); + Q_INVOKABLE void move(int from, int to); signals: void countChanged(); diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc index 4979d230..20c1f0a8 100644 --- a/nymea-app/images.qrc +++ b/nymea-app/images.qrc @@ -304,5 +304,6 @@ ui/images/sensors/window-open.svg ui/images/infinity.svg ui/images/edit-paste.svg + ui/images/list-move.svg diff --git a/nymea-app/ui/MainMenu.qml b/nymea-app/ui/MainMenu.qml index 4657edac..326a00ac 100644 --- a/nymea-app/ui/MainMenu.qml +++ b/nymea-app/ui/MainMenu.qml @@ -33,6 +33,7 @@ Drawer { spacing: 0 Rectangle { + id: upperPart Layout.fillWidth: true Layout.preferredHeight: topSectionLayout.implicitHeight color: Qt.tint(Style.backgroundColor, Qt.rgba(Style.foregroundColor.r, Style.foregroundColor.g, Style.foregroundColor.b, 0.05)) @@ -66,21 +67,31 @@ Drawer { } } - - Repeater { + ListView { + id: hostsListView + Layout.fillWidth: true + Layout.preferredHeight: count * Style.smallDelegateHeight model: root.configuredHosts + clip: true + interactive: false + moveDisplaced: Transition { + NumberAnimation { property: "y"; duration: Style.animationDuration; easing.type: Easing.InOutQuad } + } + delegate: NymeaItemDelegate { id: hostDelegate + width: hostsListView.width + visible: !dndArea.dragging || dndArea.draggedIndex !== index readonly property ConfiguredHost configuredHost: root.configuredHosts.get(index) - Layout.fillWidth: true text: model.name.length > 0 ? model.name : qsTr("New connection") subText: configuredHost.engine.jsonRpcClient.currentConnection ? configuredHost.engine.jsonRpcClient.currentConnection.url : "" prominentSubText: false progressive: false additionalItem: RowLayout { anchors.verticalCenter: parent.verticalCenter + visible: !dndArea.dragging Rectangle { height: Style.smallIconSize width: height @@ -97,7 +108,7 @@ Drawer { onClicked: { tokenSettings.setValue(hostDelegate.configuredHost.uuid, "") configuredHostsModel.removeHost(index) - } + } Settings { id: tokenSettings @@ -105,26 +116,94 @@ Drawer { } } } - onClicked: { - if (topSectionLayout.configureConnections) { - var nymeaHost = nymeaDiscovery.nymeaHosts.find(hostDelegate.configuredHost.uuid); - if (nymeaHost) { - var connectionInfoDialog = Qt.createComponent("/ui/components/ConnectionInfoDialog.qml") - var popup = connectionInfoDialog.createObject(app,{nymeaEngine: configuredHost.engine, nymeaHost: nymeaHost}) - popup.open() - popup.connectionSelected.connect(function(connection) { - print("...") - configuredHost.engine.jsonRpcClient.disconnectFromHost(); - configuredHost.engine.jsonRpcClient.connectToHost(nymeaHost, connection) - configuredHostsModel.currentIndex = index - root.close() - }) + + MouseArea { + anchors.fill: parent + + onClicked: { + if (topSectionLayout.configureConnections) { + var nymeaHost = nymeaDiscovery.nymeaHosts.find(hostDelegate.configuredHost.uuid); + if (nymeaHost) { + var connectionInfoDialog = Qt.createComponent("/ui/components/ConnectionInfoDialog.qml") + var popup = connectionInfoDialog.createObject(app,{nymeaEngine: configuredHost.engine, nymeaHost: nymeaHost}) + popup.open() + popup.connectionSelected.connect(function(connection) { + print("...") + configuredHost.engine.jsonRpcClient.disconnectFromHost(); + configuredHost.engine.jsonRpcClient.connectToHost(nymeaHost, connection) + configuredHostsModel.currentIndex = index + root.close() + }) + } + } else { + configuredHostsModel.currentIndex = index + root.close() } - } else { - configuredHostsModel.currentIndex = index - root.close() } } + + } + + NymeaItemDelegate { + id: fakeDragItem + visible: dndArea.dragging + width: hostsListView.width + prominentSubText: false + progressive: false + background: Rectangle { + color: Style.tileBackgroundColor + } + additionalItem: ColorIcon { + anchors.verticalCenter: parent.verticalCenter + size: Style.iconSize + name: "list-move" + } + } + + MouseArea { + id: dndArea + anchors.fill: parent + propagateComposedEvents: true + preventStealing: dragging + property int draggedIndex: -1 + property bool dragging: false + property int startY: 0 + property int originY: 0 + + onPressed: { + startY = mouseY + } + + onPressAndHold: { + draggedIndex = hostsListView.indexAt(mouseX, startY) + var draggedItem = hostsListView.itemAt(mouseX, startY) + fakeDragItem.text = draggedItem.text + fakeDragItem.subText = draggedItem.subText + fakeDragItem.y = draggedItem.y + originY = draggedItem.y + dragging = true + } + + onMouseYChanged: { + if (!dragging) { + return; + } + var diff = startY - mouseY + fakeDragItem.y = Math.max(0, Math.min(hostsListView.height - fakeDragItem.height, originY - diff)) + + var hoveredIdx = hostsListView.indexAt(mouseX, mouseY) + if (hoveredIdx >= 0 && draggedIndex != hoveredIdx) { + print("moved", draggedIndex, "to", hoveredIdx) + root.configuredHosts.move(draggedIndex, hoveredIdx) + draggedIndex = hoveredIdx; + } + } + + onReleased: { + dragging = false + } + + } } @@ -148,6 +227,7 @@ Drawer { } } + Flickable { Layout.fillWidth: true Layout.fillHeight: true @@ -181,8 +261,8 @@ Drawer { iconName: "../images/magic.svg" progressive: false visible: root.currentEngine && root.currentEngine.jsonRpcClient.currentHost - && NymeaUtils.hasPermissionScope(root.currentEngine.jsonRpcClient.permissions, UserInfo.PermissionScopeConfigureRules) - && root.currentEngine.jsonRpcClient.connected && Configuration.magicEnabled + && NymeaUtils.hasPermissionScope(root.currentEngine.jsonRpcClient.permissions, UserInfo.PermissionScopeConfigureRules) + && root.currentEngine.jsonRpcClient.connected && Configuration.magicEnabled onClicked: { root.openMagicSettings(); root.close(); @@ -252,11 +332,11 @@ Drawer { } } -// Component { -// id: hostConnectionInfoComponent -// MeaDialog { + // Component { + // id: hostConnectionInfoComponent + // MeaDialog { -// } -// } + // } + // } } diff --git a/nymea-app/ui/images/list-move.svg b/nymea-app/ui/images/list-move.svg new file mode 100644 index 00000000..afef27d1 --- /dev/null +++ b/nymea-app/ui/images/list-move.svg @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + +