diff --git a/libnymea-app-core/devicesproxy.cpp b/libnymea-app-core/devicesproxy.cpp
index b9e2404d..0e9c7368 100644
--- a/libnymea-app-core/devicesproxy.cpp
+++ b/libnymea-app-core/devicesproxy.cpp
@@ -131,6 +131,21 @@ void DevicesProxy::setHiddenInterfaces(const QStringList &hiddenInterfaces)
}
}
+QString DevicesProxy::nameFilter() const
+{
+ return m_nameFilter;
+}
+
+void DevicesProxy::setNameFilter(const QString &nameFilter)
+{
+ if (m_nameFilter != nameFilter) {
+ m_nameFilter = nameFilter;
+ emit nameFilterChanged();
+ invalidateFilter();
+ countChanged();
+ }
+}
+
bool DevicesProxy::filterBatteryCritical() const
{
return m_filterBatteryCritical;
@@ -249,5 +264,11 @@ bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_pa
return false;
}
}
+
+ if (!m_nameFilter.isEmpty()) {
+ if (!device->name().toLower().contains(m_nameFilter.toLower().trimmed())) {
+ return false;
+ }
+ }
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
diff --git a/libnymea-app-core/devicesproxy.h b/libnymea-app-core/devicesproxy.h
index df6b4e36..63dfca95 100644
--- a/libnymea-app-core/devicesproxy.h
+++ b/libnymea-app-core/devicesproxy.h
@@ -40,6 +40,7 @@ class DevicesProxy : public QSortFilterProxyModel
Q_PROPERTY(QString filterTagId READ filterTagId WRITE setFilterTagId NOTIFY filterTagIdChanged)
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
Q_PROPERTY(QStringList hiddenInterfaces READ hiddenInterfaces WRITE setHiddenInterfaces NOTIFY hiddenInterfacesChanged)
+ Q_PROPERTY(QString nameFilter READ nameFilter WRITE setNameFilter NOTIFY nameFilterChanged)
// Setting this to true will imply filtering for "battery" interface
Q_PROPERTY(bool filterBatteryCritical READ filterBatteryCritical WRITE setFilterBatteryCritical NOTIFY filterBatteryCriticalChanged)
@@ -67,6 +68,9 @@ public:
QStringList hiddenInterfaces() const;
void setHiddenInterfaces(const QStringList &hiddenInterfaces);
+ QString nameFilter() const;
+ void setNameFilter(const QString &nameFilter);
+
bool filterBatteryCritical() const;
void setFilterBatteryCritical(bool filterBatteryCritical);
@@ -84,6 +88,7 @@ signals:
void filterTagIdChanged();
void shownInterfacesChanged();
void hiddenInterfacesChanged();
+ void nameFilterChanged();
void filterBatteryCriticalChanged();
void filterDisconnectedChanged();
void groupByInterfaceChanged();
@@ -97,6 +102,7 @@ private:
QString m_filterTagId;
QStringList m_shownInterfaces;
QStringList m_hiddenInterfaces;
+ QString m_nameFilter;
bool m_filterBatteryCritical = false;
bool m_filterDisconnected = false;
diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index 4bd2b005..4447fe24 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -264,5 +264,9 @@
ui/images/fingerprint/fingerprint_boxes.json
ui/images/fingerprint/fingerprint_segmented.png
ui/images/fingerprint.svg
+ ui/components/ListSectionHeader.qml
+ ui/images/find.svg
+ ui/images/erase.svg
+ ui/components/ListFilterInput.qml
diff --git a/nymea-app/ui/EditDevicesPage.qml b/nymea-app/ui/EditDevicesPage.qml
index 248698aa..c93a1a1e 100644
--- a/nymea-app/ui/EditDevicesPage.qml
+++ b/nymea-app/ui/EditDevicesPage.qml
@@ -11,6 +11,13 @@ Page {
text: qsTr("Configure Things")
onBackPressed: pageStack.pop()
+ HeaderButton {
+ imageSource: "../images/find.svg"
+ color: filterInput.shown ? app.accentColor : keyColor
+ onClicked: filterInput.shown = !filterInput.shown
+
+ }
+
HeaderButton {
imageSource: "../images/add.svg"
onClicked: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml"))
@@ -45,41 +52,46 @@ Page {
}
}
- ListView {
+ ColumnLayout {
anchors.fill: parent
- model: DevicesProxy {
- id: deviceProxy
- engine: _engine
- groupByInterface: true
- }
- section.property: "baseInterface"
- section.criteria: ViewSection.FullString
- section.delegate: ColumnLayout {
- width: parent.width
- Label {
- Layout.fillWidth: true
- Layout.leftMargin: app.margins
- Layout.rightMargin: app.margins
- Layout.topMargin: app.margins
- text: app.interfaceToString(section)
- horizontalAlignment: Text.AlignRight
- }
- ThinDivider {}
+
+ ListFilterInput {
+ id: filterInput
+ Layout.fillWidth: true
}
- delegate: ThingDelegate {
- device: deviceProxy.get(index)
- canDelete: true
- onClicked: {
- pageStack.push(Qt.resolvedUrl("devicepages/ConfigureThingPage.qml"), {device: deviceProxy.get(index)})
+ ListView {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ clip: true
+
+ model: DevicesProxy {
+ id: deviceProxy
+ engine: _engine
+ groupByInterface: true
+ nameFilter: filterInput.shown ? filterInput.text : ""
}
- onDeleteClicked: {
- d.deviceToRemove = deviceProxy.get(index);
- engine.deviceManager.removeDevice(d.deviceToRemove.id)
+ section.property: "baseInterface"
+ section.criteria: ViewSection.FullString
+ section.delegate: ListSectionHeader {
+ text: app.interfaceToString(section)
+ }
+
+ delegate: ThingDelegate {
+ device: deviceProxy.get(index)
+ canDelete: true
+ onClicked: {
+ pageStack.push(Qt.resolvedUrl("devicepages/ConfigureThingPage.qml"), {device: deviceProxy.get(index)})
+ }
+ onDeleteClicked: {
+ d.deviceToRemove = deviceProxy.get(index);
+ engine.deviceManager.removeDevice(d.deviceToRemove.id)
+ }
}
}
}
+
EmptyViewPlaceholder {
anchors { left: parent.left; right: parent.right; margins: app.margins }
anchors.verticalCenter: parent.verticalCenter
diff --git a/nymea-app/ui/components/ListFilterInput.qml b/nymea-app/ui/components/ListFilterInput.qml
new file mode 100644
index 00000000..9ab825db
--- /dev/null
+++ b/nymea-app/ui/components/ListFilterInput.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.6
+import QtQuick.Layouts 1.2
+import QtQuick.Controls 2.1
+import "../components"
+import "../delegates"
+import Nymea 1.0
+
+Item {
+ id: root
+ opacity: shown ? 1 : 0
+ implicitWidth: searchColumn.implicitWidth
+ implicitHeight: shown ? searchColumn.implicitHeight : 0
+ Behavior on implicitHeight {NumberAnimation { duration: 130; easing.type: Easing.InOutQuad }}
+
+ property bool shown: false
+ property alias text: searchTextField.displayText
+
+ ColumnLayout {
+ id: searchColumn
+ anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
+ RowLayout {
+ Layout.margins: app.margins
+ spacing: app.margins
+ TextField {
+ id: searchTextField
+ Layout.fillWidth: true
+ }
+
+ HeaderButton {
+ imageSource: "../images/erase.svg"
+ onClicked: searchTextField.text = ""
+ enabled: searchTextField.displayText.length > 0
+ color: enabled ? app.accentColor : keyColor
+ }
+ }
+ ThinDivider {}
+ }
+}
diff --git a/nymea-app/ui/components/ListSectionHeader.qml b/nymea-app/ui/components/ListSectionHeader.qml
new file mode 100644
index 00000000..b1b8461f
--- /dev/null
+++ b/nymea-app/ui/components/ListSectionHeader.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.9
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 2.2
+
+ColumnLayout {
+ width: parent.width
+ property alias text: label.text
+ Label {
+ id: label
+ Layout.fillWidth: true
+ Layout.leftMargin: app.margins
+ Layout.rightMargin: app.margins
+ Layout.topMargin: app.margins
+ horizontalAlignment: Text.AlignRight
+ }
+ ThinDivider {}
+}
diff --git a/nymea-app/ui/delegates/ThingDelegate.qml b/nymea-app/ui/delegates/ThingDelegate.qml
index 5537794b..d38d43d1 100644
--- a/nymea-app/ui/delegates/ThingDelegate.qml
+++ b/nymea-app/ui/delegates/ThingDelegate.qml
@@ -8,7 +8,7 @@ MeaListItemDelegate {
id: root
width: parent.width
iconName: deviceClass ? app.interfacesToIcon(deviceClass.interfaces) : ""
- text: device.name
+ text: device ? device.name : ""
progressive: true
secondaryIconName: batteryCritical ? "../images/battery/battery-010.svg" : ""
tertiaryIconName: disconnected ? "../images/dialog-warning-symbolic.svg" : ""
diff --git a/nymea-app/ui/images/erase.svg b/nymea-app/ui/images/erase.svg
new file mode 100644
index 00000000..821c2080
--- /dev/null
+++ b/nymea-app/ui/images/erase.svg
@@ -0,0 +1,170 @@
+
+
+
+
diff --git a/nymea-app/ui/images/find.svg b/nymea-app/ui/images/find.svg
new file mode 100644
index 00000000..25270e75
--- /dev/null
+++ b/nymea-app/ui/images/find.svg
@@ -0,0 +1,176 @@
+
+
+
+
diff --git a/nymea-app/ui/magic/SelectThingPage.qml b/nymea-app/ui/magic/SelectThingPage.qml
index a0b7a2ff..20a700ab 100644
--- a/nymea-app/ui/magic/SelectThingPage.qml
+++ b/nymea-app/ui/magic/SelectThingPage.qml
@@ -25,6 +25,12 @@ Page {
qsTr("Select a kind of things") :
root.shownInterfaces.length > 0 ? qsTr("Select a %1").arg(app.interfaceToDisplayName(root.shownInterfaces[0])) : qsTr("Select a thing")
onBackPressed: root.backPressed()
+
+ HeaderButton {
+ imageSource: "../images/find.svg"
+ color: filterInput.shown ? app.accentColor : keyColor
+ onClicked: filterInput.shown = !filterInput.shown
+ }
}
InterfacesProxy {
@@ -35,6 +41,8 @@ Page {
DevicesProxy {
id: devicesProxy
engine: _engine
+ groupByInterface: true
+ nameFilter: filterInput.shown ? filterInput.text : ""
}
ColumnLayout {
@@ -50,11 +58,21 @@ Page {
}
ThinDivider { visible: root.allowSelectAny }
+ ListFilterInput {
+ id: filterInput
+ Layout.fillWidth: true
+ }
+
ListView {
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