From add5903fb0abc76d77ce4369c4679a95be03b433 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 20 Nov 2020 02:38:08 +0100 Subject: [PATCH] Working User completer --- CMakeLists.txt | 2 ++ resources/qml/Completer.qml | 8 +++++++- resources/qml/MessageInput.qml | 20 ++++++++++++++++---- src/CompletionModel.h | 20 -------------------- src/CompletionModelRoles.h | 2 +- src/CompletionProxyModel.h | 21 ++++++++++++++++++++- src/UsersModel.cpp | 28 ++++++++++++++++++++++------ src/UsersModel.h | 12 +++++++----- src/timeline/InputBar.cpp | 8 ++++++++ 9 files changed, 83 insertions(+), 38 deletions(-) delete mode 100644 src/CompletionModel.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aa81f285..7e68db77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -492,6 +492,7 @@ qt5_wrap_cpp(MOC_HEADERS src/ChatPage.h src/CommunitiesList.h src/CommunitiesListItem.h + src/CompletionProxyModel.h src/DeviceVerificationFlow.h src/InviteeItem.h src/LoginPage.h @@ -508,6 +509,7 @@ qt5_wrap_cpp(MOC_HEADERS src/TrayIcon.h src/UserInfoWidget.h src/UserSettingsPage.h + src/UsersModel.h src/WebRTCSession.h src/WelcomePage.h src/popups/PopupItem.h diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml index d53eae62..2c520dec 100644 --- a/resources/qml/Completer.qml +++ b/resources/qml/Completer.qml @@ -34,11 +34,17 @@ Popup { onCompleterNameChanged: { if (completerName) completer = TimelineManager.timeline.input.completerFor(completerName); - + else + completer = undefined; } padding: 0 onAboutToShow: currentIndex = -1 + Connections { + onTimelineChanged: completer = null + target: TimelineManager + } + ColumnLayout { anchors.fill: parent spacing: 0 diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml index a4a47a3e..50ff7324 100644 --- a/resources/qml/MessageInput.qml +++ b/resources/qml/MessageInput.qml @@ -77,14 +77,13 @@ Rectangle { onTextChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text) onCursorPositionChanged: { TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text); - if (cursorPosition < completerTriggeredAt) { + if (cursorPosition <= completerTriggeredAt) { completerTriggeredAt = -1; popup.close(); } } onSelectionStartChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text) onSelectionEndChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text) - // Ensure that we get escape key press events first. Keys.onShortcutOverride: event.accepted = (completerTriggeredAt != -1 && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter)) Keys.onPressed: { if (event.matches(StandardKey.Paste)) { @@ -97,18 +96,20 @@ Rectangle { } else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_N) { textArea.text = TimelineManager.timeline.input.nextText(); } else if (event.key == Qt.Key_At) { - completerTriggeredAt = cursorPosition + 1; + completerTriggeredAt = cursorPosition; popup.completerName = "user"; popup.open(); } else if (event.key == Qt.Key_Escape && popup.opened) { completerTriggeredAt = -1; + popup.completerName = ""; event.accepted = true; popup.close(); } else if (event.matches(StandardKey.InsertParagraphSeparator) && popup.opened) { var currentCompletion = popup.currentCompletion(); + popup.completerName = ""; popup.close(); if (currentCompletion) { - textArea.remove(completerTriggeredAt - 1, cursorPosition); + textArea.remove(completerTriggeredAt, cursorPosition); textArea.insert(cursorPosition, currentCompletion); event.accepted = true; return ; @@ -129,6 +130,17 @@ Rectangle { } } + Connections { + onTimelineChanged: { + textArea.clear(); + textArea.append(TimelineManager.timeline.input.text()); + textArea.completerTriggeredAt = -1; + popup.completerName = ""; + } + target: TimelineManager + } + // Ensure that we get escape key press events first. + Completer { id: popup diff --git a/src/CompletionModel.h b/src/CompletionModel.h deleted file mode 100644 index ed021051..00000000 --- a/src/CompletionModel.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -// Class for showing a limited amount of completions at a time - -#include - -class CompletionModel : public QSortFilterProxyModel -{ -public: - CompletionModel(QAbstractItemModel *model, QObject *parent = nullptr) - : QSortFilterProxyModel(parent) - { - setSourceModel(model); - } - int rowCount(const QModelIndex &parent) const override - { - auto row_count = QSortFilterProxyModel::rowCount(parent); - return (row_count < 7) ? row_count : 7; - } -}; diff --git a/src/CompletionModelRoles.h b/src/CompletionModelRoles.h index bd6c4114..7c7307d3 100644 --- a/src/CompletionModelRoles.h +++ b/src/CompletionModelRoles.h @@ -10,6 +10,6 @@ enum Roles { CompletionRole = Qt::UserRole * 2, // The string to replace the active completion SearchRole, // String completer uses for search + SearchRole2, // Secondary string completer uses for search }; - } diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h index ee38075e..757aa990 100644 --- a/src/CompletionProxyModel.h +++ b/src/CompletionProxyModel.h @@ -4,17 +4,36 @@ #include +#include "CompletionModelRoles.h" + class CompletionProxyModel : public QSortFilterProxyModel { + Q_OBJECT + public: CompletionProxyModel(QAbstractItemModel *model, QObject *parent = nullptr) : QSortFilterProxyModel(parent) { setSourceModel(model); } - int rowCount(const QModelIndex &parent) const override + + QHash roleNames() const override + { + return this->sourceModel()->roleNames(); + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const override { auto row_count = QSortFilterProxyModel::rowCount(parent); return (row_count < 7) ? row_count : 7; } + +public slots: + QVariant completionAt(int i) const + { + if (i >= 0 && i < rowCount()) + return data(index(i, 0), CompletionModel::CompletionRole); + else + return {}; + } }; diff --git a/src/UsersModel.cpp b/src/UsersModel.cpp index c63292bb..f102cff1 100644 --- a/src/UsersModel.cpp +++ b/src/UsersModel.cpp @@ -3,12 +3,23 @@ #include "Cache.h" #include "CompletionModelRoles.h" -#include - UsersModel::UsersModel(const std::string &roomId, QObject *parent) : QAbstractListModel(parent) + , room_id(roomId) { - roomMembers_ = cache::getMembers(roomId, 0, 9999); + roomMembers_ = cache::roomMembers(roomId); +} + +QHash +UsersModel::roleNames() const +{ + return { + {CompletionModel::CompletionRole, "completionRole"}, + {CompletionModel::SearchRole, "searchRole"}, + {CompletionModel::SearchRole2, "searchRole2"}, + {Roles::DisplayName, "displayName"}, + {Roles::AvatarUrl, "avatarUrl"}, + }; } QVariant @@ -19,9 +30,14 @@ UsersModel::data(const QModelIndex &index, int role) const case CompletionModel::CompletionRole: case CompletionModel::SearchRole: case Qt::DisplayRole: - return roomMembers_[index.row()].display_name; - case Avatar: - return roomMembers_[index.row()].avatar; + case Roles::DisplayName: + return QString::fromStdString( + cache::displayName(room_id, roomMembers_[index.row()])); + case CompletionModel::SearchRole2: + return QString::fromStdString(roomMembers_[index.row()]); + case Roles::AvatarUrl: + return cache::avatarUrl(QString::fromStdString(room_id), + QString::fromStdString(roomMembers_[index.row()])); } } return {}; diff --git a/src/UsersModel.h b/src/UsersModel.h index 09ccbf25..6ee8261f 100644 --- a/src/UsersModel.h +++ b/src/UsersModel.h @@ -2,23 +2,25 @@ #include -class RoomMember; - class UsersModel : public QAbstractListModel { public: enum Roles { - Avatar = Qt::UserRole // QImage avatar + AvatarUrl = Qt::UserRole, + DisplayName, }; UsersModel(const std::string &roomId, QObject *parent = nullptr); + QHash roleNames() const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override { - return (parent == QModelIndex()) ? roomMembers_.size() : 0; + (void)parent; + return roomMembers_.size(); } QVariant data(const QModelIndex &index, int role) const override; private: - std::vector roomMembers_; + std::string room_id; + std::vector roomMembers_; }; diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index 82649faa..641d8379 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -14,12 +14,14 @@ #include "Cache.h" #include "CallManager.h" #include "ChatPage.h" +#include "CompletionProxyModel.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" #include "Olm.h" #include "TimelineModel.h" #include "UserSettingsPage.h" +#include "UsersModel.h" #include "Utils.h" #include "dialogs/PlaceCall.h" #include "dialogs/PreviewUploadOverlay.h" @@ -166,6 +168,12 @@ InputBar::nextText() QObject * InputBar::completerFor(QString completerName) { + if (completerName == "user") { + auto userModel = new UsersModel(room->roomId().toStdString()); + auto proxy = new CompletionProxyModel(userModel); + userModel->setParent(proxy); + return proxy; + } return nullptr; }