diff --git a/libnymea-app-core/deviceclasses.cpp b/libnymea-app-core/deviceclasses.cpp index 2757ae27..0593a4b3 100644 --- a/libnymea-app-core/deviceclasses.cpp +++ b/libnymea-app-core/deviceclasses.cpp @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2017 Simon Stuerz * + * Copyright (C) 2019 Michael Zanetti * * * * This file is part of nymea:app * * * @@ -57,6 +58,10 @@ QVariant DeviceClasses::data(const QModelIndex &index, int role) const return deviceClass->pluginId().toString(); case RoleVendorId: return deviceClass->vendorId().toString(); + case RoleInterfaces: + return deviceClass->interfaces(); + case RoleBaseInterface: + return deviceClass->baseInterface(); } return QVariant(); } @@ -68,6 +73,9 @@ int DeviceClasses::count() const DeviceClass *DeviceClasses::get(int index) const { + if (index < 0 || index >= m_deviceClasses.count()) { + return nullptr; + } return m_deviceClasses.at(index); } @@ -108,5 +116,7 @@ QHash DeviceClasses::roleNames() const roles[RoleDisplayName] = "displayName"; roles[RolePluginId] = "pluginId"; roles[RoleVendorId] = "vendorId"; + roles[RoleInterfaces] = "interfaces"; + roles[RoleBaseInterface] = "baseInterface"; return roles; } diff --git a/libnymea-app-core/deviceclasses.h b/libnymea-app-core/deviceclasses.h index b6785de7..d6d3052c 100644 --- a/libnymea-app-core/deviceclasses.h +++ b/libnymea-app-core/deviceclasses.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2017 Simon Stuerz * + * Copyright (C) 2019 Michael Zanetti * * * * This file is part of nymea:app * * * @@ -37,10 +38,12 @@ public: RoleName, RoleDisplayName, RolePluginId, - RoleVendorId + RoleVendorId, + RoleInterfaces, + RoleBaseInterface }; - explicit DeviceClasses(QObject *parent = 0); + explicit DeviceClasses(QObject *parent = nullptr); QList deviceClasses(); diff --git a/libnymea-app-core/deviceclassesproxy.cpp b/libnymea-app-core/deviceclassesproxy.cpp index 58a9ab85..6fdc10dd 100644 --- a/libnymea-app-core/deviceclassesproxy.cpp +++ b/libnymea-app-core/deviceclassesproxy.cpp @@ -27,8 +27,10 @@ DeviceClassesProxy::DeviceClassesProxy(QObject *parent) : QSortFilterProxyModel(parent) { + setSortRole(DeviceClasses::RoleDisplayName); } + QUuid DeviceClassesProxy::vendorId() const { return m_vendorId; @@ -55,6 +57,35 @@ void DeviceClassesProxy::setDeviceClasses(DeviceClasses *deviceClasses) m_deviceClasses = deviceClasses; setSourceModel(deviceClasses); emit deviceClassesChanged(); + sort(0); +} + +QString DeviceClassesProxy::filterInterface() const +{ + return m_filterInterface; +} + +void DeviceClassesProxy::setFilterInterface(const QString &filterInterface) +{ + if (m_filterInterface != filterInterface) { + m_filterInterface = filterInterface; + emit filterInterfaceChanged(); + invalidateFilter(); + } +} + +bool DeviceClassesProxy::groupByInterface() const +{ + return m_groupByInterface; +} + +void DeviceClassesProxy::setGroupByInterface(bool groupByInterface) +{ + if (m_groupByInterface != groupByInterface) { + m_groupByInterface = groupByInterface; + emit groupByInterfaceChanged(); + invalidate(); + } } DeviceClass *DeviceClassesProxy::get(int index) const @@ -80,16 +111,27 @@ bool DeviceClassesProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sour if (deviceClass->createMethods().count() == 1 && deviceClass->createMethods().contains("CreateMethodAuto")) return false; - if (!m_vendorId.isNull() && deviceClass->vendorId() == m_vendorId) - return true; + if (!m_vendorId.isNull() && deviceClass->vendorId() != m_vendorId) + return false; - return false; + if (!m_filterInterface.isEmpty() && !deviceClass->interfaces().contains(m_filterInterface)) { + return false; + } + + return true; } bool DeviceClassesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const { - QVariant leftName = sourceModel()->data(left); - QVariant rightName = sourceModel()->data(right); + if (m_groupByInterface) { + QString leftBaseInterface = sourceModel()->data(left, DeviceClasses::RoleBaseInterface).toString(); + QString rightBaseInterface = sourceModel()->data(right, DeviceClasses::RoleBaseInterface).toString(); + if (leftBaseInterface != rightBaseInterface) { + return QString::localeAwareCompare(leftBaseInterface, rightBaseInterface) < 0; + } + } + QString leftName = sourceModel()->data(left, DeviceClasses::RoleDisplayName).toString(); + QString rightName = sourceModel()->data(right, DeviceClasses::RoleDisplayName).toString(); - return QString::localeAwareCompare(leftName.toString(), rightName.toString()) < 0; + return QString::localeAwareCompare(leftName, rightName) < 0; } diff --git a/libnymea-app-core/deviceclassesproxy.h b/libnymea-app-core/deviceclassesproxy.h index f87498aa..6a3a77bc 100644 --- a/libnymea-app-core/deviceclassesproxy.h +++ b/libnymea-app-core/deviceclassesproxy.h @@ -36,8 +36,12 @@ class DeviceClassesProxy : public QSortFilterProxyModel Q_PROPERTY(QUuid vendorId READ vendorId WRITE setVendorId NOTIFY vendorIdChanged) Q_PROPERTY(DeviceClasses *deviceClasses READ deviceClasses WRITE setDeviceClasses NOTIFY deviceClassesChanged) + Q_PROPERTY(QString filterInterface READ filterInterface WRITE setFilterInterface NOTIFY filterInterfaceChanged) + + Q_PROPERTY(bool groupByInterface READ groupByInterface WRITE setGroupByInterface NOTIFY groupByInterfaceChanged) + public: - explicit DeviceClassesProxy(QObject *parent = 0); + explicit DeviceClassesProxy(QObject *parent = nullptr); QUuid vendorId() const; void setVendorId(const QUuid &vendorId); @@ -45,10 +49,22 @@ public: DeviceClasses *deviceClasses(); void setDeviceClasses(DeviceClasses *deviceClasses); + QString filterInterface() const; + void setFilterInterface(const QString &filterInterface); + + bool groupByInterface() const; + void setGroupByInterface(bool groupByInterface); + Q_INVOKABLE DeviceClass *get(int index) const; Q_INVOKABLE void resetFilter(); +signals: + void vendorIdChanged(); + void deviceClassesChanged(); + void filterInterfaceChanged(); + void groupByInterfaceChanged(); + protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE; bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; @@ -56,11 +72,8 @@ protected: private: QUuid m_vendorId; DeviceClasses *m_deviceClasses; - -signals: - void vendorIdChanged(); - void deviceClassesChanged(); - + QString m_filterInterface; + bool m_groupByInterface = false; }; #endif // DEVICECLASSFILERMODEL_H diff --git a/libnymea-app-core/devices.cpp b/libnymea-app-core/devices.cpp index 1b231155..f69a5f94 100644 --- a/libnymea-app-core/devices.cpp +++ b/libnymea-app-core/devices.cpp @@ -74,68 +74,12 @@ QVariant Devices::data(const QModelIndex &index, int role) const return device->deviceClassId().toString(); case RoleSetupComplete: return device->setupComplete(); - case RoleInterfaces: { + case RoleInterfaces: return device->deviceClass()->interfaces(); - } - case RoleBaseInterface: { - QStringList interfaces = device->deviceClass()->interfaces(); - if (interfaces.contains("gateway")) { - return "gateway"; - } - if (interfaces.contains("shutter")) { - return "shutter"; - } - if (interfaces.contains("blind")) { - return "blind"; - } - if (interfaces.contains("garagegate")) { - return "garagegate"; - } - if (interfaces.contains("inputtrigger")) { - return "inputtrigger"; - } - if (interfaces.contains("awning")) { - return "awning"; - } - if (interfaces.contains("outputtrigger")) { - return "outputtrigger"; - } - if (interfaces.contains("light")) { - return "light"; - } - if (interfaces.contains("sensor")) { - return "sensor"; - } - if (interfaces.contains("weather")) { - return "weather"; - } - if (interfaces.contains("media")) { - return "media"; - } - if (interfaces.contains("button")) { - return "button"; - } - if (interfaces.contains("notifications")) { - return "notifications"; - } - if (interfaces.contains("smartmeter")) { - return "smartmeter"; - } - if (interfaces.contains("heating")) { - return "heating"; - } - if (interfaces.contains("evcharger")) { - return "evcharger"; - } - if (interfaces.contains("powersocket")) { - return "powersocket"; - } - return "uncategorized"; - } - + case RoleBaseInterface: + return device->deviceClass()->baseInterface(); } return QVariant(); - } void Devices::addDevice(Device *device) diff --git a/libnymea-app-core/interfacesmodel.cpp b/libnymea-app-core/interfacesmodel.cpp index 7f493719..2e7f4e40 100644 --- a/libnymea-app-core/interfacesmodel.cpp +++ b/libnymea-app-core/interfacesmodel.cpp @@ -65,6 +65,20 @@ void InterfacesModel::setShownInterfaces(const QStringList &shownInterfaces) } } +bool InterfacesModel::onlyConfiguredDevices() const +{ + return m_onlyConfiguredDevices; +} + +void InterfacesModel::setOnlyConfiguredDevices(bool onlyConfigured) +{ + if (m_onlyConfiguredDevices != onlyConfigured) { + m_onlyConfiguredDevices = onlyConfigured; + emit onlyConfiguredDevicesChanged(); + syncInterfaces(); + } +} + bool InterfacesModel::showUncategorized() const { return m_showUncategorized; @@ -79,30 +93,47 @@ void InterfacesModel::setShowUncategorized(bool showUncategorized) } } +QString InterfacesModel::get(int index) const +{ + if (index < 0 || index > m_interfaces.count()) { + return QString(); + } + return m_interfaces.at(index); +} + void InterfacesModel::syncInterfaces() { if (!m_deviceManager) { return; } + QStringList baseList; + if (m_onlyConfiguredDevices) { + for (int i = 0; i < m_deviceManager->devices()->rowCount(); i++) { + DeviceClass *dc = m_deviceManager->deviceClasses()->getDeviceClass(m_deviceManager->devices()->get(i)->deviceClassId()); + baseList << dc->interfaces(); + } + } else { + for (int i = 0; i < m_deviceManager->deviceClasses()->rowCount(); i++) { + DeviceClass *dc = m_deviceManager->deviceClasses()->get(i); + baseList << dc->interfaces(); + } + } + baseList.removeDuplicates(); + QStringList interfacesInSource; - for (int i = 0; i < m_deviceManager->devices()->rowCount(); i++) { - DeviceClass *dc = m_deviceManager->deviceClasses()->getDeviceClass(m_deviceManager->devices()->get(i)->deviceClassId()); -// qDebug() << "device" <name() << "has interfaces" << dc->interfaces(); - - bool isInShownIfaces = false; - foreach (const QString &interface, dc->interfaces()) { - if (!m_shownInterfaces.contains(interface)) { - continue; - } - - if (!interfacesInSource.contains(interface)) { - interfacesInSource.append(interface); - } - isInShownIfaces = true; + bool isInShownIfaces = false; + foreach (const QString &interface, baseList) { + if (!m_shownInterfaces.contains(interface)) { + continue; } - if (!isInShownIfaces && !interfacesInSource.contains("uncategorized")) { - interfacesInSource.append("uncategorized"); + + if (!interfacesInSource.contains(interface)) { + interfacesInSource.append(interface); } + isInShownIfaces = true; + } + if (!isInShownIfaces && !interfacesInSource.contains("uncategorized")) { + interfacesInSource.append("uncategorized"); } QStringList interfacesToAdd = interfacesInSource; QStringList interfacesToRemove; diff --git a/libnymea-app-core/interfacesmodel.h b/libnymea-app-core/interfacesmodel.h index 2d39a249..771244eb 100644 --- a/libnymea-app-core/interfacesmodel.h +++ b/libnymea-app-core/interfacesmodel.h @@ -14,6 +14,8 @@ class InterfacesModel : public QAbstractListModel Q_PROPERTY(int count READ rowCount NOTIFY countChanged) Q_PROPERTY(DeviceManager* deviceManager READ deviceManager WRITE setDeviceManager NOTIFY deviceManagerChanged) Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged) + + Q_PROPERTY(bool onlyConfiguredDevices READ onlyConfiguredDevices WRITE setOnlyConfiguredDevices NOTIFY onlyConfiguredDevicesChanged) Q_PROPERTY(bool showUncategorized READ showUncategorized WRITE setShowUncategorized NOTIFY showUncategorizedChanged) public: @@ -34,13 +36,19 @@ public: QStringList shownInterfaces() const; void setShownInterfaces(const QStringList &shownInterfaces); + bool onlyConfiguredDevices() const; + void setOnlyConfiguredDevices(bool onlyConfigured); + bool showUncategorized() const; void setShowUncategorized(bool showUncategorized); + Q_INVOKABLE QString get(int index) const; + signals: void countChanged(); void deviceManagerChanged(); void shownInterfacesChanged(); + bool onlyConfiguredDevicesChanged(); void showUncategorizedChanged(); private slots: @@ -52,6 +60,7 @@ private: QStringList m_interfaces; QStringList m_shownInterfaces; + bool m_onlyConfiguredDevices = true; bool m_showUncategorized = false; }; diff --git a/libnymea-app-core/jsonrpc/jsontypes.cpp b/libnymea-app-core/jsonrpc/jsontypes.cpp index 7782705e..890166c6 100644 --- a/libnymea-app-core/jsonrpc/jsontypes.cpp +++ b/libnymea-app-core/jsonrpc/jsontypes.cpp @@ -50,7 +50,7 @@ JsonTypes::JsonTypes(QObject *parent) : Vendor *JsonTypes::unpackVendor(const QVariantMap &vendorMap) { - Vendor *v = new Vendor(vendorMap.value("id").toUuid(), vendorMap.value("name").toString()); + Vendor *v = new Vendor(vendorMap.value("id").toString(), vendorMap.value("name").toString()); v->setDisplayName(vendorMap.value("displayName").toString()); return v; } diff --git a/libnymea-app-core/vendorsproxy.h b/libnymea-app-core/vendorsproxy.h index d86a62c9..2ddd2bdd 100644 --- a/libnymea-app-core/vendorsproxy.h +++ b/libnymea-app-core/vendorsproxy.h @@ -34,7 +34,7 @@ class VendorsProxy : public QSortFilterProxyModel Q_PROPERTY(Vendors *vendors READ vendors WRITE setVendors NOTIFY vendorsChanged) public: - explicit VendorsProxy(QObject *parent = 0); + explicit VendorsProxy(QObject *parent = nullptr); Vendors *vendors(); void setVendors(Vendors *vendors); diff --git a/libnymea-common/types/deviceclass.cpp b/libnymea-common/types/deviceclass.cpp index b078740a..ff7870ba 100644 --- a/libnymea-common/types/deviceclass.cpp +++ b/libnymea-common/types/deviceclass.cpp @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2017 Simon Stuerz * + * Copyright (C) 2019 Michael Zanetti * * * * This file is part of nymea:app * * * @@ -129,6 +130,63 @@ void DeviceClass::setInterfaces(const QStringList &interfaces) m_interfaces = interfaces; } +QString DeviceClass::baseInterface() const +{ + if (m_interfaces.contains("gateway")) { + return "gateway"; + } + if (m_interfaces.contains("shutter")) { + return "shutter"; + } + if (m_interfaces.contains("blind")) { + return "blind"; + } + if (m_interfaces.contains("garagegate")) { + return "garagegate"; + } + if (m_interfaces.contains("inputtrigger")) { + return "inputtrigger"; + } + if (m_interfaces.contains("awning")) { + return "awning"; + } + if (m_interfaces.contains("outputtrigger")) { + return "outputtrigger"; + } + if (m_interfaces.contains("light")) { + return "light"; + } + if (m_interfaces.contains("sensor")) { + return "sensor"; + } + if (m_interfaces.contains("weather")) { + return "weather"; + } + if (m_interfaces.contains("media")) { + return "media"; + } + if (m_interfaces.contains("button")) { + return "button"; + } + if (m_interfaces.contains("notifications")) { + return "notifications"; + } + if (m_interfaces.contains("smartmeter")) { + return "smartmeter"; + } + if (m_interfaces.contains("heating")) { + return "heating"; + } + if (m_interfaces.contains("evcharger")) { + return "evcharger"; + } + if (m_interfaces.contains("powersocket")) { + return "powersocket"; + } + return "uncategorized"; + +} + ParamTypes *DeviceClass::paramTypes() const { return m_paramTypes; diff --git a/libnymea-common/types/deviceclass.h b/libnymea-common/types/deviceclass.h index 3e6db9c3..d45e6c9f 100644 --- a/libnymea-common/types/deviceclass.h +++ b/libnymea-common/types/deviceclass.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2017 Simon Stuerz * + * Copyright (C) 2019 Michael Zanetti * * * * This file is part of nymea:app * * * @@ -45,6 +46,7 @@ class DeviceClass : public QObject Q_PROPERTY(SetupMethod setupMethod READ setupMethod CONSTANT) Q_PROPERTY(QStringList basicTags READ basicTagNames CONSTANT) Q_PROPERTY(QStringList interfaces READ interfaces CONSTANT) + Q_PROPERTY(QString baseInterface READ baseInterface CONSTANT) Q_PROPERTY(ParamTypes *paramTypes READ paramTypes NOTIFY paramTypesChanged) Q_PROPERTY(ParamTypes *discoveryParamTypes READ discoveryParamTypes NOTIFY discoveryParamTypesChanged) Q_PROPERTY(StateTypes *stateTypes READ stateTypes NOTIFY stateTypesChanged) @@ -116,6 +118,8 @@ public: QStringList interfaces() const; void setInterfaces(const QStringList &interfaces); + QString baseInterface() const; + ParamTypes *paramTypes() const; void setParamTypes(ParamTypes *paramTypes); diff --git a/libnymea-common/types/vendor.cpp b/libnymea-common/types/vendor.cpp index d3b16256..714c153b 100644 --- a/libnymea-common/types/vendor.cpp +++ b/libnymea-common/types/vendor.cpp @@ -22,19 +22,19 @@ #include "vendor.h" -Vendor::Vendor(const QUuid &id, const QString &name, QObject *parent) : +Vendor::Vendor(const QString &id, const QString &name, QObject *parent) : QObject(parent), m_id(id), m_name(name) { } -QUuid Vendor::id() const +QString Vendor::id() const { return m_id; } -void Vendor::setId(const QUuid &id) +void Vendor::setId(const QString &id) { m_id = id; } diff --git a/libnymea-common/types/vendor.h b/libnymea-common/types/vendor.h index ef2ec948..72b7c4c5 100644 --- a/libnymea-common/types/vendor.h +++ b/libnymea-common/types/vendor.h @@ -32,13 +32,13 @@ class Vendor : public QObject Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString displayName READ displayName CONSTANT) - Q_PROPERTY(QUuid id READ id CONSTANT) + Q_PROPERTY(QString id READ id CONSTANT) public: - Vendor(const QUuid &id = QUuid(), const QString &name = QString(), QObject *parent = nullptr); + Vendor(const QString &id = QString(), const QString &name = QString(), QObject *parent = nullptr); - QUuid id() const; - void setId(const QUuid &id); + QString id() const; + void setId(const QString &id); QString name() const; void setName(const QString &name); @@ -47,7 +47,7 @@ public: void setDisplayName(const QString &displayName); private: - QUuid m_id; + QString m_id; QString m_name; QString m_displayName; }; diff --git a/libnymea-common/types/vendors.cpp b/libnymea-common/types/vendors.cpp index 34e32413..a1abd887 100644 --- a/libnymea-common/types/vendors.cpp +++ b/libnymea-common/types/vendors.cpp @@ -29,24 +29,14 @@ Vendors::Vendors(QObject *parent) : { } -QList Vendors::vendors() -{ - return m_vendors; -} - -int Vendors::count() const -{ - return m_vendors.count(); -} - -Vendor *Vendors::getVendor(const QUuid &vendorId) const +Vendor *Vendors::getVendor(const QString &vendorId) const { foreach (Vendor *vendor, m_vendors) { if (vendor->id() == vendorId) { return vendor; } } - return 0; + return nullptr; } int Vendors::rowCount(const QModelIndex &parent) const @@ -67,7 +57,7 @@ QVariant Vendors::data(const QModelIndex &index, int role) const case RoleDisplayName: return vendor->displayName(); case RoleId: - return vendor->id().toString(); + return vendor->id(); } return QVariant(); } @@ -79,15 +69,24 @@ void Vendors::addVendor(Vendor *vendor) //qDebug() << "Vendors: loaded vendor" << vendor->name(); m_vendors.append(vendor); endInsertRows(); + emit countChanged(); } void Vendors::clearModel() { beginResetModel(); - qDebug() << "Vendors: delete all vendors"; qDeleteAll(m_vendors); m_vendors.clear(); endResetModel(); + emit countChanged(); +} + +Vendor *Vendors::get(int index) const +{ + if (index < 0 || index >= m_vendors.count()) { + return nullptr; + } + return m_vendors.at(index); } QHash Vendors::roleNames() const diff --git a/libnymea-common/types/vendors.h b/libnymea-common/types/vendors.h index 6ab43700..35b56707 100644 --- a/libnymea-common/types/vendors.h +++ b/libnymea-common/types/vendors.h @@ -30,6 +30,8 @@ class Vendors : public QAbstractListModel { Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + public: enum Role { RoleId, @@ -37,20 +39,20 @@ public: RoleDisplayName }; - explicit Vendors(QObject *parent = 0); - - QList vendors(); - - Q_INVOKABLE int count() const; - Q_INVOKABLE Vendor *getVendor(const QUuid &vendorId) const; + explicit Vendors(QObject *parent = nullptr); int rowCount(const QModelIndex & parent = QModelIndex()) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; void addVendor(Vendor *vendor); - void clearModel(); + Q_INVOKABLE Vendor* get(int index) const; + Q_INVOKABLE Vendor *getVendor(const QString &vendorId) const; + +signals: + void countChanged(); + protected: QHash roleNames() const; diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 276d5240..a83a2858 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -32,7 +32,6 @@ ui/components/IconMenuItem.qml ui/components/Graph.qml ui/components/ErrorDialog.qml - ui/components/InterfacesModels.qml ui/components/ShutterControls.qml ui/components/MeaDialog.qml ui/components/MeaListItemDelegate.qml @@ -147,5 +146,6 @@ ui/devicepages/BoolSensorDevicePage.qml ui/devicepages/PowersocketDevicePage.qml ui/devicelistpages/PowerSocketsDeviceListPage.qml + ui/components/GroupedListView.qml diff --git a/nymea-app/ui/EditDevicesPage.qml b/nymea-app/ui/EditDevicesPage.qml index 3d45a51c..ab62f3b9 100644 --- a/nymea-app/ui/EditDevicesPage.qml +++ b/nymea-app/ui/EditDevicesPage.qml @@ -60,7 +60,7 @@ Page { Layout.fillWidth: true } - ListView { + GroupedListView { Layout.fillWidth: true Layout.fillHeight: true clip: true @@ -71,11 +71,6 @@ Page { groupByInterface: true nameFilter: filterInput.shown ? filterInput.text : "" } - section.property: "baseInterface" - section.criteria: ViewSection.FullString - section.delegate: ListSectionHeader { - text: app.interfaceToString(section) - } delegate: ThingDelegate { device: deviceProxy.get(index) diff --git a/nymea-app/ui/NewDeviceWizard.qml b/nymea-app/ui/NewDeviceWizard.qml index 099d855d..5449908d 100644 --- a/nymea-app/ui/NewDeviceWizard.qml +++ b/nymea-app/ui/NewDeviceWizard.qml @@ -1,6 +1,7 @@ import QtQuick 2.5 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.1 import Nymea 1.0 import "components" import "delegates" @@ -27,7 +28,7 @@ Page { property DeviceClass deviceClass: null property DeviceDescriptor deviceDescriptor: null property var discoveryParams: [] - property string deviceName: null + property string deviceName: "" property int pairRequestId: 0 property var pairingTransactionId: null property int addRequestId: 0 @@ -70,7 +71,12 @@ Page { StackView { id: internalPageStack anchors.fill: parent - initialItem: Page { + Component.onCompleted: push(deviceClassesPage) + } + + Component { + id: vendorsPage + Page { ListView { anchors.fill: parent model: VendorsProxy { @@ -92,18 +98,98 @@ Page { Component { id: deviceClassesPage Page { - ListView { - anchors.fill: parent + Pane { + id: filterPane + anchors { left: parent.left; top: parent.top; right: parent.right } + Behavior on height { NumberAnimation { duration: 120; easing.type: Easing.InOutQuad } } + + height: implicitHeight + app.margins * 2 + Material.elevation: 1 + z: 1 + + leftPadding: 0; rightPadding: 0; topPadding: 0; bottomPadding: 0 + contentItem: Rectangle { + color: app.primaryColor + clip: true + GridLayout { + anchors.fill: parent + anchors.margins: app.margins + columnSpacing: app.margins + columns: 2 + Label { + text: qsTr("Vendor") + } + + ComboBox { + id: vendorFilterComboBox + Layout.fillWidth: true + textRole: "displayName" + model: ListModel { + id: vendorsFilterModel + ListElement { displayName: qsTr("All"); vendorId: "" } + + Component.onCompleted: { + for (var i = 0; i < engine.deviceManager.vendors.count; i++) { + var vendor = engine.deviceManager.vendors.get(i); + append({displayName: vendor.displayName, vendorId: vendor.id}) + } + } + } + } + Label { + text: qsTr("Type") + } + + ComboBox { + id: typeFilterComboBox + Layout.fillWidth: true + textRole: "displayName" + InterfacesModel { + id: interfacesModel + deviceManager: engine.deviceManager + shownInterfaces: app.supportedInterfaces + onlyConfiguredDevices: false + showUncategorized: false + } + model: ListModel { + id: typeFilterModel + ListElement { interfaceName: ""; displayName: qsTr("All") } + + Component.onCompleted: { + for (var i = 0; i < interfacesModel.count; i++) { + append({interfaceName: interfacesModel.get(i), displayName: app.interfaceToString(interfacesModel.get(i))}); + } + } + } + } + } + } + } + + GroupedListView { + anchors { + left: parent.left + top: filterPane.bottom + right: parent.right + bottom: parent.bottom + } + model: DeviceClassesProxy { id: deviceClassesProxy - vendorId: d.vendorId ? d.vendorId : "" + vendorId: vendorsFilterModel.get(vendorFilterComboBox.currentIndex).vendorId deviceClasses: engine.deviceManager.deviceClasses + filterInterface: typeFilterModel.get(typeFilterComboBox.currentIndex).interfaceName + groupByInterface: true } + delegate: MeaListItemDelegate { id: deviceClassDelegate width: parent.width text: model.displayName + subText: engine.deviceManager.vendors.getVendor(model.vendorId).displayName iconName: app.interfacesToIcon(deviceClass.interfaces) + prominentSubText: false + wrapTexts: false property var deviceClass: deviceClassesProxy.get(index) @@ -219,23 +305,46 @@ Page { property var deviceClass: null - ListView { + ColumnLayout { anchors.fill: parent - model: discovery - delegate: MeaListItemDelegate { - width: parent.width - height: app.delegateHeight - text: model.name - subText: model.description - iconName: app.interfacesToIcon(discoveryView.deviceClass.interfaces) - onClicked: { - d.deviceDescriptor = discovery.get(index); - d.deviceName = model.name; - internalPageStack.push(paramsPage) + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + model: discovery + delegate: MeaListItemDelegate { + width: parent.width + height: app.delegateHeight + text: model.name + subText: model.description + iconName: app.interfacesToIcon(discoveryView.deviceClass.interfaces) + onClicked: { + d.deviceDescriptor = discovery.get(index); + d.deviceName = model.name; + internalPageStack.push(paramsPage) + } } } + Button { + id: retryButton + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + text: qsTr("Search again") + onClicked: discovery.discoverDevices(d.deviceClass.id, d.discoveryParams) + visible: !discovery.busy && discovery.count > 0 + } + + Button { + id: manualAddButton + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins + visible: d.deviceClass.createMethods.indexOf("CreateMethodUser") >= 0 + text: qsTr("Add thing manually") + onClicked: internalPageStack.push(paramsPage) + } } + ColumnLayout { anchors.centerIn: parent width: parent.width - app.margins * 2 @@ -249,7 +358,6 @@ Page { } BusyIndicator { running: visible - onRunningChanged: print("********* running changed", running) anchors.horizontalCenter: parent.horizontalCenter } } diff --git a/nymea-app/ui/components/GroupedListView.qml b/nymea-app/ui/components/GroupedListView.qml new file mode 100644 index 00000000..401cf7ed --- /dev/null +++ b/nymea-app/ui/components/GroupedListView.qml @@ -0,0 +1,15 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.1 + +ListView { + + ScrollBar.vertical: ScrollBar {} + + clip: true + section.property: "baseInterface" + section.criteria: ViewSection.FullString + section.delegate: ListSectionHeader { + text: app.interfaceToString(section) + } + +} diff --git a/nymea-app/ui/components/InterfacesModels.qml b/nymea-app/ui/components/InterfacesModels.qml deleted file mode 100644 index 516d5740..00000000 --- a/nymea-app/ui/components/InterfacesModels.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 2.0 - -Item { - property ListModel eventTemplateModel: ListModel { - ListElement { interfaceName: "battery"; stateName: "batteryLevel"; stateDisplayName: qsTr("Battery level"); eventDisplayName: qsTr("Battery level changed") } - ListElement { interfaceName: "battery"; stateName: "batteryCritical"; stateDisplayName: qsTr("Battery critical"); eventDisplayName: qsTr("Battery critical changed") } - } -} diff --git a/nymea-app/ui/magic/SelectThingPage.qml b/nymea-app/ui/magic/SelectThingPage.qml index dfd2f0b1..a92c1607 100644 --- a/nymea-app/ui/magic/SelectThingPage.qml +++ b/nymea-app/ui/magic/SelectThingPage.qml @@ -64,16 +64,11 @@ Page { ThinDivider { visible: root.allowSelectAny } - ListView { + GroupedListView { Layout.fillWidth: true Layout.fillHeight: true model: root.selectInterface ? interfacesProxy : devicesProxy clip: true - section.property: "baseInterface" - section.criteria: ViewSection.FullString - section.delegate: ListSectionHeader { - text: app.interfaceToString(section) - } delegate: MeaListItemDelegate { width: parent.width text: root.selectInterface ? model.displayName : model.name