commit 6e1bec6bceed73deff7ee3f482ee54c32d549422
parent b4631ca0b35322a6ed2d170491e8c39a227c487a
Author: Daniel Vrátil <dvratil@kde.org>
Date: Thu, 14 Feb 2019 21:13:38 +0100
Implement Search
Diffstat:
9 files changed, 214 insertions(+), 110 deletions(-)
diff --git a/harbour-passilic.pro b/harbour-passilic.pro
@@ -47,6 +47,8 @@ DISTFILES += \
rpm/harbour-passilic.yaml \
translations/*.ts \
harbour-passilic.desktop \
+ qml/pages/SearchPage.qml \
+ qml/components/PasswordDelegate.qml
OTHER_FILES += \
README.md
diff --git a/qml/components/GlobalPullDownMenu.qml b/qml/components/GlobalPullDownMenu.qml
@@ -24,4 +24,8 @@ PullDownMenu {
text: qsTr("About")
onClicked: app.pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml"))
}
+ MenuItem {
+ text: qsTr("Search")
+ onClicked: app.pageStack.push(searchPage)
+ }
}
diff --git a/qml/components/PasswordDelegate.qml b/qml/components/PasswordDelegate.qml
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 Daniel Vrátil <dvratil@kde.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.2
+import QtQml.Models 2.2
+import Sailfish.Silica 1.0
+import harbour.passilic 1.0
+
+ListItem {
+ id: listItem
+
+ property var modelData
+ property var password : null
+
+ signal folderSelected()
+
+ contentHeight: password === null ? Theme.itemSizeSmall : Theme.itemSizeLarge
+
+ Row {
+ anchors {
+ fill: parent
+ leftMargin: Theme.horizontalPageMargin
+ rightMargin: Theme.horizontalPageMargin
+ verticalCenter: parent.verticalCenter
+ topMargin: Theme.paddingMedium
+ }
+
+ Column {
+ spacing: Theme.paddingSmall
+ width: parent.width
+
+ Row {
+ spacing: Theme.paddingMedium
+
+ Image {
+ anchors.verticalCenter: parent.verticalCenter
+ source: "image://theme/"
+ + ((modelData.type === PasswordsModel.FolderEntry) ? "icon-m-folder" : "icon-m-device-lock")
+ + "?"
+ + (listItem.highlighted ? Theme.highlightColor : Theme.primaryColor)
+ width: Theme.iconSizeSmall
+ height: width
+ }
+
+ Label {
+ id: label
+ text: modelData.name
+ }
+ }
+
+ Row {
+ visible: password !== null
+ width: parent.width
+
+ Label {
+ id: errorLabel
+
+ visible: password !== null && password.hasError
+
+ text: password ? password.error : ""
+ font.pixelSize: Theme.fontSizeTiny
+ }
+
+ Label {
+ id: okLabel
+
+ visible: password !== null && password.valid
+
+ text: qsTr("Password copied to clipboard")
+ font.pixelSize: Theme.fontSizeTiny
+ }
+ }
+ }
+ }
+
+ RemorseItem {
+ id: remorse
+
+ cancelText: qsTr("Expire password")
+
+ // HACK: override RemorseItem._execute() to act as cancel when the timer expires
+ function _execute(closeAfterExecute) {
+ cancel()
+ }
+
+ onCanceled: {
+ if (listItem.password) {
+ listItem.password.expirePassword();
+ }
+ }
+ }
+
+ onClicked: {
+ if (modelData.type === PasswordsModel.FolderEntry) {
+ listItem.folderSelected()
+ } else {
+ modelData.password.requestPassword()
+ var dialog = pageStack.push(Qt.resolvedUrl("../pages/PassphraseRequester.qml"),
+ { "requester": modelData.password })
+ dialog.done.connect(function() {
+ listItem.password = modelData.password
+ listItem.password.validChanged.connect(function() {
+ if (listItem.password.valid) {
+ remorse.execute(listItem, qsTr("Password will expire"),
+ function() {
+ if (listItem.password) {
+ listItem.password.expirePassword();
+ }
+ }, listItem.password.defaultTimeout);
+ }
+ });
+ });
+ }
+ }
+}
diff --git a/qml/harbour-passilic.qml b/qml/harbour-passilic.qml
@@ -47,6 +47,14 @@ ApplicationWindow
}
Component {
+ id: searchPage
+
+ SearchPage {
+ model: filterModel
+ }
+ }
+
+ Component {
id: passwordsPage
PasswordListPage {
diff --git a/qml/pages/PasswordListPage.qml b/qml/pages/PasswordListPage.qml
@@ -51,108 +51,11 @@ Page {
rootIndex: passwordListPage.rootIndex
- delegate: ListItem {
- id: listItem
+ delegate: PasswordDelegate {
+ modelData: model
- property var password : null
-
- contentHeight: password === null ? Theme.itemSizeSmall : Theme.itemSizeLarge
-
- Row {
- anchors {
- fill: parent
- leftMargin: Theme.horizontalPageMargin
- rightMargin: Theme.horizontalPageMargin
- verticalCenter: parent.verticalCenter
- topMargin: Theme.paddingMedium
- }
-
- Column {
- spacing: Theme.paddingSmall
- width: parent.width
-
- Row {
- spacing: Theme.paddingMedium
-
- Image {
- anchors.verticalCenter: parent.verticalCenter
- source: "image://theme/"
- + ((model.type === PasswordsModel.FolderEntry) ? "icon-m-folder" : "icon-m-device-lock")
- + "?"
- + (listItem.highlighted ? Theme.highlightColor : Theme.primaryColor)
- width: Theme.iconSizeSmall
- height: width
- }
-
- Label {
- id: label
- text: model.name
- }
- }
-
- Row {
- visible: password !== null
- width: parent.width
-
- Label {
- id: errorLabel
-
- visible: password !== null && password.hasError
-
- text: password ? password.error : ""
- font.pixelSize: Theme.fontSizeTiny
- }
-
- Label {
- id: okLabel
-
- visible: password !== null && password.valid
-
- text: qsTr("Password copied to clipboard")
- font.pixelSize: Theme.fontSizeTiny
- }
- }
- }
- }
-
- RemorseItem {
- id: remorse
-
- cancelText: qsTr("Expire password")
-
- // HACK: override RemorseItem._execute() to act as cancel when the timer expires
- function _execute(closeAfterExecute) {
- cancel()
- }
-
- onCanceled: {
- if (listItem.password) {
- listItem.password.expirePassword();
- }
- }
- }
-
- onClicked: {
- if (model.type === PasswordsModel.FolderEntry) {
- passwordListPage.folderSelected(delegateModel.modelIndex(index), model.name);
- } else {
- model.password.requestPassword()
- var dialog = pageStack.push(Qt.resolvedUrl("PassphraseRequester.qml"),
- { "requester": model.password })
- dialog.done.connect(function() {
- listItem.password = model.password
- listItem.password.validChanged.connect(function() {
- if (listItem.password.valid) {
- remorse.execute(listItem, qsTr("Password will expire"),
- function() {
- if (listItem.password) {
- listItem.password.expirePassword();
- }
- }, listItem.password.defaultTimeout);
- }
- });
- });
- }
+ onFolderSelected: {
+ passwordListPage.folderSelected(delegateModel.modelIndex(index), model.name);
}
}
}
diff --git a/qml/pages/SearchPage.qml b/qml/pages/SearchPage.qml
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 Daniel Vrátil <dvratil@kde.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.2
+import QtQml.Models 2.2
+import Sailfish.Silica 1.0
+import "../components"
+
+Page {
+ id: searchPage
+
+ property alias model: listView.model
+
+ SilicaListView {
+ id: listView
+ anchors.fill: parent
+
+ header: SearchField {
+ id: searchField
+ width: parent.width
+ focus: true
+ onTextChanged: model.filter = text
+ }
+
+ delegate: PasswordDelegate {
+ modelData: model
+ }
+ }
+}
diff --git a/src/passwordfiltermodel.cpp b/src/passwordfiltermodel.cpp
@@ -79,7 +79,8 @@ bool PasswordFilterModel::filterAcceptsRow(int source_row, const QModelIndex &so
}
if (mFilter.isEmpty()) {
- return true;
+ // nothing matches an empty filter
+ return false;
}
const auto path = sourceModel()->data(src_index, PasswordsModel::FullNameRole).toString();
diff --git a/translations/harbour-passilic-zh_cn.ts b/translations/harbour-passilic-zh_cn.ts
@@ -34,13 +34,13 @@
<source>About</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>PasswordListPage</name>
<message>
- <source>Passilic</source>
+ <source>Search</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>PasswordDelegate</name>
<message>
<source>Password copied to clipboard</source>
<translation type="unfinished"></translation>
@@ -55,6 +55,13 @@
</message>
</context>
<context>
+ <name>PasswordListPage</name>
+ <message>
+ <source>Passilic</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>PasswordProvider</name>
<message>
<source>Failed to decrypt password: GPG is not available</source>
diff --git a/translations/harbour-passilic.ts b/translations/harbour-passilic.ts
@@ -34,13 +34,13 @@
<source>About</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>PasswordListPage</name>
<message>
- <source>Passilic</source>
+ <source>Search</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>PasswordDelegate</name>
<message>
<source>Password copied to clipboard</source>
<translation type="unfinished"></translation>
@@ -55,6 +55,13 @@
</message>
</context>
<context>
+ <name>PasswordListPage</name>
+ <message>
+ <source>Passilic</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
<name>PasswordProvider</name>
<message>
<source>Failed to decrypt password: GPG is not available</source>