From 7fbd3cbdadb5bfb509b9bc396d949fe38a067072 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 21 Feb 2018 13:35:11 +0100 Subject: Search in conversationview ...via syntax highligher or search api. --- components/mailviewer/qml/HtmlContent.qml | 5 +- components/mailviewer/qml/MailDataModel.qml | 8 +++ components/mailviewer/qml/MailViewer.qml | 1 + components/mailviewer/qml/TextContent.qml | 10 ++++ framework/qml/ConversationView.qml | 8 +++ framework/qml/MailListView.qml | 4 ++ framework/qml/MailViewer.qml | 1 + framework/qml/Messages.qml | 1 + framework/src/CMakeLists.txt | 4 +- framework/src/frameworkplugin.cpp | 2 + framework/src/viewhighlighter.cpp | 91 +++++++++++++++++++++++++++++ framework/src/viewhighlighter.h | 40 +++++++++++++ views/conversation/main.qml | 14 +++++ 13 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 framework/src/viewhighlighter.cpp create mode 100644 framework/src/viewhighlighter.h diff --git a/components/mailviewer/qml/HtmlContent.qml b/components/mailviewer/qml/HtmlContent.qml index f0249a01..c775a3ca 100644 --- a/components/mailviewer/qml/HtmlContent.qml +++ b/components/mailviewer/qml/HtmlContent.qml @@ -27,7 +27,10 @@ Item { property string content //We have to give it a minimum size so the html content starts to expand property int contentHeight: 10; - + property string searchString + onSearchStringChanged: { + htmlView.findText(searchString) + } WebEngineView { id: htmlView diff --git a/components/mailviewer/qml/MailDataModel.qml b/components/mailviewer/qml/MailDataModel.qml index d960b2ac..fdc3ce22 100644 --- a/components/mailviewer/qml/MailDataModel.qml +++ b/components/mailviewer/qml/MailDataModel.qml @@ -23,6 +23,8 @@ import org.kube.framework 1.0 as Kube DelegateModel { id: root + property string searchString: "" + delegate: Item { id: partColumn @@ -124,6 +126,12 @@ DelegateModel { } height: item ? item.contentHeight : 0 width: parent.width + Binding { + target: partLoader.item + property: "searchString" + value: root.searchString + when: partLoader.status == Loader.Ready + } } Component.onCompleted: { switch (model.type) { diff --git a/components/mailviewer/qml/MailViewer.qml b/components/mailviewer/qml/MailViewer.qml index 9031ec17..faf3bc61 100644 --- a/components/mailviewer/qml/MailViewer.qml +++ b/components/mailviewer/qml/MailViewer.qml @@ -22,6 +22,7 @@ Item { id: root property alias rootIndex: visualModel.rootIndex property alias model: visualModel.model + property alias searchString: visualModel.searchString height: partListView.height MailDataModel { diff --git a/components/mailviewer/qml/TextContent.qml b/components/mailviewer/qml/TextContent.qml index 316786ef..95e196b4 100644 --- a/components/mailviewer/qml/TextContent.qml +++ b/components/mailviewer/qml/TextContent.qml @@ -27,8 +27,14 @@ Item { property bool embedded: true property string type + property string searchString property int contentHeight: textEdit.height + onSearchStringChanged: { + //This is a workaround because otherwise the view will not take the ViewHighlighter changes into account. + textEdit.text = root.content + } + TextEdit { id: textEdit @@ -56,5 +62,9 @@ Item { acceptedButtons: Qt.NoButton // we don't want to eat clicks on the Text cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor } + Kube.ViewHighlighter { + textDocument: textEdit.textDocument + searchString: root.searchString + } } } diff --git a/framework/qml/ConversationView.qml b/framework/qml/ConversationView.qml index 2dab92ba..0fd76f8f 100644 --- a/framework/qml/ConversationView.qml +++ b/framework/qml/ConversationView.qml @@ -31,6 +31,12 @@ FocusScope { property variant mail; property bool hideTrash: true; property bool hideNonTrash: false; + property string searchString: "" + + Kube.Listener { + filter: Kube.Messages.searchString + onMessageReceived: root.searchString = message.searchString + } Rectangle { anchors.fill: parent @@ -46,6 +52,7 @@ FocusScope { left: parent.left right: parent.right } + //Shrink the listview if the content doesn't fill the full height, so the email appears on top instead of on the bottom. height: Math.min(contentHeight, parent.height) @@ -98,6 +105,7 @@ FocusScope { sent: model.sent incomplete: model.incomplete current: delegateRoot.isCurrentItem + searchString: root.searchString } } diff --git a/framework/qml/MailListView.qml b/framework/qml/MailListView.qml index 35c90618..cc68f003 100644 --- a/framework/qml/MailListView.qml +++ b/framework/qml/MailListView.qml @@ -35,6 +35,10 @@ FocusScope { property bool showFilter: false property string filter: null + onFilterChanged: { + Kube.Fabric.postMessage(Kube.Messages.searchString, {"searchString": filter}) + } + onParentFolderChanged: { currentMail = null filterField.clearSearch() diff --git a/framework/qml/MailViewer.qml b/framework/qml/MailViewer.qml index f52e694d..e37fb3f7 100644 --- a/framework/qml/MailViewer.qml +++ b/framework/qml/MailViewer.qml @@ -42,6 +42,7 @@ Rectangle { property bool incomplete: false; property bool current: false; property bool unread; + property alias searchString: mailViewer.searchString implicitHeight: header.height + attachments.height + body.height + incompleteBody.height + footer.height + Kube.Units.largeSpacing diff --git a/framework/qml/Messages.qml b/framework/qml/Messages.qml index 9df83863..35aa750a 100644 --- a/framework/qml/Messages.qml +++ b/framework/qml/Messages.qml @@ -42,6 +42,7 @@ Item { property string progressNotification: "progressNotification" property string errorNotification: "errorNotification" property string search: "search" + property string searchString: "searchString" property string synchronize: "synchronize" property string reply: "reply" property string forward: "forward" diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index 97cb453b..f11e6077 100644 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt @@ -1,5 +1,5 @@ -find_package(Qt5 COMPONENTS REQUIRED Core Concurrent Quick Qml WebEngineWidgets Test WebEngine) +find_package(Qt5 COMPONENTS REQUIRED Core Concurrent Quick Qml WebEngineWidgets Test WebEngine Gui) find_package(KF5Mime "4.87.0" CONFIG REQUIRED) find_package(Sink CONFIG REQUIRED) find_package(KAsync CONFIG REQUIRED) @@ -48,6 +48,7 @@ add_library(kubeframework SHARED keyring.cpp domainobjectcontroller.cpp extensionmodel.cpp + viewhighlighter.cpp ) target_link_libraries(kubeframework sink @@ -58,6 +59,7 @@ target_link_libraries(kubeframework Qt5::WebEngineWidgets Qt5::Test Qt5::WebEngine + Qt5::Gui KF5::Codecs KF5::Contacts KAsync diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp index b0e2f9c9..d512ce10 100644 --- a/framework/src/frameworkplugin.cpp +++ b/framework/src/frameworkplugin.cpp @@ -42,6 +42,7 @@ #include "controller.h" #include "domainobjectcontroller.h" #include "extensionmodel.h" +#include "viewhighlighter.h" #include #include @@ -142,6 +143,7 @@ void FrameworkPlugin::registerTypes (const char *uri) qmlRegisterType(uri, 1, 0, "KubeImage"); qmlRegisterType(uri, 1, 0, "Clipboard"); qmlRegisterType(uri, 1, 0, "StartupCheck"); + qmlRegisterType(uri, 1, 0, "ViewHighlighter"); qmlRegisterSingletonType(uri, 1, 0, "WebEngineProfile", webengineprofile_singletontype_provider); qmlRegisterSingletonType(uri, 1, 0, "Keyring", keyring_singletontype_provider); } diff --git a/framework/src/viewhighlighter.cpp b/framework/src/viewhighlighter.cpp new file mode 100644 index 00000000..553646f2 --- /dev/null +++ b/framework/src/viewhighlighter.cpp @@ -0,0 +1,91 @@ +/* + Copyright (c) 2018 Christian Mollekopf + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#include "viewhighlighter.h" + +#include +#include +#include +#include + +class SearchHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + SearchHighlighter(QTextDocument *parent = 0) + : QSyntaxHighlighter(parent) + { + } + + void setSearchString(const QString &s) + { + mSearchString = s; + rehighlight(); + } + +protected: + void highlightBlock(const QString &text) override + { + if (!mSearchString.isEmpty()) { + QTextCharFormat format; + format.setFontWeight(QFont::Bold); + format.setBackground(QColor{"#f67400"}); + + QRegularExpression expression(mSearchString, QRegularExpression::CaseInsensitiveOption); + auto i = expression.globalMatch(text); + while (i.hasNext()) { + auto match = i.next(); + setFormat(match.capturedStart(), match.capturedLength(), format); + } + } + + } + +private: + QString mSearchString; +}; + +struct ViewHighlighter::Private { + SearchHighlighter *searchHighligher; + +}; + + +ViewHighlighter::ViewHighlighter(QObject *parent) + : QObject(parent), + d{new Private} +{ + +} + +void ViewHighlighter::setTextDocument(QQuickTextDocument *document) +{ + if (document) { + d->searchHighligher = new SearchHighlighter{document->textDocument()}; + } +} + +void ViewHighlighter::setSearchString(const QString &s) +{ + if (d->searchHighligher) { + d->searchHighligher->setSearchString(s); + } +} + +#include "viewhighlighter.moc" diff --git a/framework/src/viewhighlighter.h b/framework/src/viewhighlighter.h new file mode 100644 index 00000000..00b4d4b6 --- /dev/null +++ b/framework/src/viewhighlighter.h @@ -0,0 +1,40 @@ +/* + Copyright (c) 2018 Christian Mollekopf + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include +#include + +class QQuickTextDocument; + +class ViewHighlighter : public QObject { + Q_OBJECT + + Q_PROPERTY(QString searchString WRITE setSearchString) + Q_PROPERTY(QQuickTextDocument *textDocument WRITE setTextDocument CONSTANT) + +public: + ViewHighlighter(QObject *parent = nullptr); + + void setTextDocument(QQuickTextDocument *); + void setSearchString(const QString& ); + +private: + struct Private; + QSharedPointer d; +}; diff --git a/views/conversation/main.qml b/views/conversation/main.qml index 686aeecb..64f7f273 100644 --- a/views/conversation/main.qml +++ b/views/conversation/main.qml @@ -117,12 +117,26 @@ ApplicationWindow { to: ["to@example.org"], unread: true }, + { + resource: "resource1", + date: "2017-07-20T17:46:29", + subject: "ComplexHTMLLongLine", + //We assume that @media trigger the complex html view + body: "
Hi Mélanie,\n\nI'm sorry @media to start this on such late notice, but we'd like to get Foo and boo to woo next week, because the following weeks are unfortunately not possible for us.\n
",
+                            bodyIsHtml: true,
+                            to: ["to@example.org"],
+                            unread: true
+                        },
                     ]
                 }],
         }
         TestStore.setup(initialState)
     }
 
+    Shortcut {
+        onActivated: Kube.Fabric.postMessage(Kube.Messages.search, {})
+        sequence: StandardKey.Find
+    }
     View {
         anchors.fill: parent
     }
-- 
cgit v1.2.3