diff --git a/harbour-fernschreiber.pro b/harbour-fernschreiber.pro index 34d593d..d19ecd1 100644 --- a/harbour-fernschreiber.pro +++ b/harbour-fernschreiber.pro @@ -38,6 +38,7 @@ SOURCES += src/harbour-fernschreiber.cpp \ src/tdlibfile.cpp \ src/tdlibreceiver.cpp \ src/tdlibwrapper.cpp \ + src/textfiltermodel.cpp \ src/tgsplugin.cpp DISTFILES += qml/harbour-fernschreiber.qml \ @@ -228,6 +229,7 @@ HEADERS += \ src/tdlibreceiver.h \ src/tdlibsecrets.h \ src/tdlibwrapper.h \ + src/textfiltermodel.h \ src/tgsplugin.h # https://github.com/Samsung/rlottie.git diff --git a/qml/pages/OverviewPage.qml b/qml/pages/OverviewPage.qml index 4911a34..1fd3104 100644 --- a/qml/pages/OverviewPage.qml +++ b/qml/pages/OverviewPage.qml @@ -103,19 +103,11 @@ Page { } } - Timer { - id: searchChatTimer - interval: 300 - running: false - repeat: false - onTriggered: { - if (chatSearchField.text === "") { - chatListView.model = chatListModel; - } else { - chatListView.model = chatListProxyModel; - } - chatListProxyModel.setFilterWildcard("*" + chatSearchField.text + "*"); - } + TextFilterModel { + id: chatListProxyModel + sourceModel: (chatSearchField.opacity > 0) ? chatListModel : null + filterRoleName: "filter" + filterText: chatSearchField.text } function openMessage(chatId, messageId) { @@ -355,10 +347,6 @@ Page { placeholderText: qsTr("Filter your chats...") canHide: text === "" - onTextChanged: { - searchChatTimer.restart(); - } - onHideClicked: { resetFocus(); } @@ -380,7 +368,7 @@ Page { clip: true opacity: (overviewPage.chatListCreated && !overviewPage.logoutLoading) ? 1 : 0 Behavior on opacity { FadeAnimation {} } - model: chatListModel + model: chatListProxyModel.sourceModel ? chatListProxyModel : chatListModel delegate: ChatListViewItem { ownUserId: overviewPage.ownUserId isVerified: is_verified @@ -394,7 +382,7 @@ Page { ViewPlaceholder { enabled: chatListView.count === 0 - text: chatSearchField.text === "" ? qsTr("You don't have any chats yet.") : qsTr("No matching chats found.") + text: chatListModel.count === 0 ? qsTr("You don't have any chats yet.") : qsTr("No matching chats found.") hintText: qsTr("You can search public chats or create a new chat via the pull-down menu.") } diff --git a/src/chatlistmodel.cpp b/src/chatlistmodel.cpp index cb3ae62..9cc589e 100644 --- a/src/chatlistmodel.cpp +++ b/src/chatlistmodel.cpp @@ -320,6 +320,7 @@ QVector ChatListModel::ChatData::updateLastMessage(const QVariantMap &messa changedRoles.append(RoleLastMessageDate); } if (prevSenderMessageText != senderMessageText()) { + changedRoles.append(RoleFilter); changedRoles.append(RoleLastMessageText); } if (prevSenderMessageStatus != senderMessageStatus()) { @@ -388,6 +389,9 @@ ChatListModel::ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSetting relativeTimeRefreshTimer->setSingleShot(false); relativeTimeRefreshTimer->setInterval(30000); connect(relativeTimeRefreshTimer, SIGNAL(timeout()), SLOT(handleRelativeTimeRefreshTimer())); + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged())); + connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged())); + connect(this, SIGNAL(modelReset()), SIGNAL(countChanged())); } ChatListModel::~ChatListModel() @@ -457,7 +461,7 @@ QVariant ChatListModel::data(const QModelIndex &index, int role) const case ChatListModel::RoleIsChannel: return data->isChannel(); case ChatListModel::RoleIsMarkedAsUnread: return data->isMarkedAsUnread(); case ChatListModel::RoleIsPinned: return data->isPinned(); - case ChatListModel::RoleFilter: return QString(data->title() + " " + data->senderMessageText()).trimmed(); + case ChatListModel::RoleFilter: return data->title() + " " + data->senderMessageText(); case ChatListModel::RoleDraftMessageText: return data->draftMessageText(); case ChatListModel::RoleDraftMessageDate: return data->draftMessageDate(); } @@ -891,6 +895,7 @@ void ChatListModel::handleChatTitleUpdated(const QString &chatId, const QString chat->chatData.insert(TITLE, title); QVector changedRoles; changedRoles.append(ChatListModel::RoleTitle); + changedRoles.append(ChatListModel::RoleFilter); const QModelIndex modelIndex(index(chatIndex)); emit dataChanged(modelIndex, modelIndex, changedRoles); } else { diff --git a/src/chatlistmodel.h b/src/chatlistmodel.h index 8c17d75..f3659b7 100644 --- a/src/chatlistmodel.h +++ b/src/chatlistmodel.h @@ -28,6 +28,7 @@ class ChatListModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(bool showAllChats READ showAllChats WRITE setShowAllChats NOTIFY showAllChatsChanged) + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) public: @@ -57,9 +58,9 @@ public: ChatListModel(TDLibWrapper *tdLibWrapper, AppSettings *appSettings); ~ChatListModel() override; - virtual QHash roleNames() const override; - virtual int rowCount(const QModelIndex&) const override; - virtual QVariant data(const QModelIndex &index, int role) const override; + QHash roleNames() const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &index = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; Q_INVOKABLE void redrawModel(); Q_INVOKABLE QVariantMap get(int row); @@ -90,6 +91,7 @@ private slots: void handleRelativeTimeRefreshTimer(); signals: + void countChanged(); void showAllChatsChanged(); void chatChanged(const qlonglong &changedChatId); void chatJoined(const qlonglong &chatId, const QString &chatTitle); diff --git a/src/harbour-fernschreiber.cpp b/src/harbour-fernschreiber.cpp index 51febb6..1754fcf 100644 --- a/src/harbour-fernschreiber.cpp +++ b/src/harbour-fernschreiber.cpp @@ -42,6 +42,7 @@ #include "dbusadaptor.h" #include "processlauncher.h" #include "stickermanager.h" +#include "textfiltermodel.h" #include "tgsplugin.h" #include "fernschreiberutils.h" #include "knownusersmodel.h" @@ -69,6 +70,7 @@ int main(int argc, char *argv[]) const char *uri = "WerkWolf.Fernschreiber"; qmlRegisterType(uri, 1, 0, "TDLibFile"); qmlRegisterType(uri, 1, 0, "NamedAction"); + qmlRegisterType(uri, 1, 0, "TextFilterModel"); qmlRegisterSingletonType(uri, 1, 0, "DebugLog", DebugLogJS::createSingleton); AppSettings *appSettings = new AppSettings(view.data()); @@ -89,11 +91,6 @@ int main(int argc, char *argv[]) ChatListModel chatListModel(tdLibWrapper, appSettings); context->setContextProperty("chatListModel", &chatListModel); - QSortFilterProxyModel chatListProxyModel(view.data()); - chatListProxyModel.setSourceModel(&chatListModel); - chatListProxyModel.setFilterRole(ChatListModel::RoleFilter); - chatListProxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive); - context->setContextProperty("chatListProxyModel", &chatListProxyModel); ChatModel chatModel(tdLibWrapper); context->setContextProperty("chatModel", &chatModel); diff --git a/src/textfiltermodel.cpp b/src/textfiltermodel.cpp new file mode 100644 index 0000000..d747d30 --- /dev/null +++ b/src/textfiltermodel.cpp @@ -0,0 +1,98 @@ +/* + This file is part of Fernschreiber. + + Fernschreiber 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. + + Fernschreiber 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 Fernschreiber. If not, see . +*/ + +#include "textfiltermodel.h" + +#define DEBUG_MODULE TextFilterModel +#include "debuglog.h" + +TextFilterModel::TextFilterModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + setDynamicSortFilter(true); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setFilterFixedString(QString()); +} + +void TextFilterModel::setSource(QObject *model) +{ + setSourceModel(qobject_cast(model)); +} + +void TextFilterModel::setSourceModel(QAbstractItemModel *model) +{ + if (sourceModel() != model) { + LOG(model); + QSortFilterProxyModel::setSourceModel(model); + updateFilterRole(); + emit sourceChanged(); + } +} + +QString TextFilterModel::getFilterRoleName() const +{ + return filterRoleName; +} + +void TextFilterModel::setFilterRoleName(QString role) +{ + if (filterRoleName != role) { + filterRoleName = role; + LOG(role); + updateFilterRole(); + emit filterRoleNameChanged(); + } +} + +QString TextFilterModel::getFilterText() const +{ + return filterText; +} + +void TextFilterModel::setFilterText(QString text) +{ + if (filterText.compare(text, Qt::CaseInsensitive) != 0) { + filterText = text; + LOG(text); + setFilterFixedString(text); + emit filterTextChanged(); + } +} + +int TextFilterModel::findRole(QAbstractItemModel *model, QString role) +{ + if (model && !role.isEmpty()) { + const QByteArray roleName(role.toUtf8()); + const QHash roleMap(model->roleNames()); + const QList roles(roleMap.keys()); + const int n = roles.count(); + for (int i = 0; i < n; i++) { + const QByteArray name(roleMap.value(roles.at(i))); + if (name == roleName) { + LOG(role << roles.at(i)); + return roles.at(i); + } + } + LOG("Unknown role" << role); + } + return -1; +} + +void TextFilterModel::updateFilterRole() +{ + const int role = findRole(sourceModel(), filterRoleName); + setFilterRole((role >= 0) ? role : Qt::DisplayRole); +} diff --git a/src/textfiltermodel.h b/src/textfiltermodel.h new file mode 100644 index 0000000..7f296c0 --- /dev/null +++ b/src/textfiltermodel.h @@ -0,0 +1,58 @@ +/* + This file is part of Fernschreiber. + + Fernschreiber 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. + + Fernschreiber 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 Fernschreiber. If not, see . +*/ + +#ifndef TEXTFILTERMODEL_H +#define TEXTFILTERMODEL_H + +#include + +class TextFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(QString filterRoleName READ getFilterRoleName WRITE setFilterRoleName NOTIFY filterRoleNameChanged) + Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged) + Q_PROPERTY(QObject* sourceModel READ sourceModel WRITE setSource NOTIFY sourceChanged) + +public: + TextFilterModel(QObject *parent = Q_NULLPTR); + + void setSource(QObject* model); + void setSourceModel(QAbstractItemModel *model) Q_DECL_OVERRIDE; + + QString getFilterRoleName() const; + void setFilterRoleName(QString role); + + QString getFilterText() const; + void setFilterText(QString text); + +signals: + void sourceChanged(); + void filterRoleNameChanged(); + void filterTextChanged(); + +private slots: + void updateFilterRole(); + +private: + static int findRole(QAbstractItemModel *model, QString role); + +private: + QString filterRoleName; + QString filterText; +}; + +#endif // TEXTFILTERMODEL_H