Working User completer

This commit is contained in:
Nicolas Werner 2020-11-20 02:38:08 +01:00
parent a3c4fece7e
commit add5903fb0
9 changed files with 83 additions and 38 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,20 +0,0 @@
#pragma once
// Class for showing a limited amount of completions at a time
#include <QSortFilterProxyModel>
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;
}
};

View File

@ -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
};
}

View File

@ -4,17 +4,36 @@
#include <QSortFilterProxyModel>
#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<int, QByteArray> 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 {};
}
};

View File

@ -3,12 +3,23 @@
#include "Cache.h"
#include "CompletionModelRoles.h"
#include <QPixmap>
UsersModel::UsersModel(const std::string &roomId, QObject *parent)
: QAbstractListModel(parent)
, room_id(roomId)
{
roomMembers_ = cache::getMembers(roomId, 0, 9999);
roomMembers_ = cache::roomMembers(roomId);
}
QHash<int, QByteArray>
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 {};

View File

@ -2,23 +2,25 @@
#include <QAbstractListModel>
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<int, QByteArray> 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<RoomMember> roomMembers_;
std::string room_id;
std::vector<std::string> roomMembers_;
};

View File

@ -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;
}