Spearate completion from ChatDocumentHandler

This commit is contained in:
James Graham
2025-12-23 16:22:09 +00:00
parent 02bed79265
commit 416d85af3b
13 changed files with 283 additions and 210 deletions

View File

@@ -2,35 +2,55 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "completionmodel.h"
#include <QDebug>
#include <QTextCursor>
#include "completionproxymodel.h"
#include "models/actionsmodel.h"
#include "models/customemojimodel.h"
#include "models/emojimodel.h"
#include "neochatroom.h"
#include "qmltextitemwrapper.h"
#include "userlistmodel.h"
CompletionModel::CompletionModel(QObject *parent)
: QAbstractListModel(parent)
, m_textItem(new QmlTextItemWrapper(this))
, m_filterModel(new CompletionProxyModel(this))
, m_emojiModel(new QConcatenateTablesProxyModel(this))
{
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
connect(m_textItem, &QmlTextItemWrapper::textItemChanged, this, &CompletionModel::textItemChanged);
connect(m_textItem, &QmlTextItemWrapper::textDocumentCursorPositionChanged, this, &CompletionModel::updateTextStart);
connect(m_textItem, &QmlTextItemWrapper::textDocumentContentsChanged, this, &CompletionModel::updateCompletion);
m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
m_emojiModel->addSourceModel(&EmojiModel::instance());
}
QString CompletionModel::text() const
QQuickItem *CompletionModel::textItem() const
{
return m_text;
return m_textItem->textItem();
}
void CompletionModel::setText(const QString &text, const QString &fullText)
void CompletionModel::setTextItem(QQuickItem *textItem)
{
m_text = text;
m_fullText = fullText;
Q_EMIT textChanged();
m_textItem->setTextItem(textItem);
}
void CompletionModel::updateTextStart()
{
auto cursor = m_textItem->textCursor();
if (cursor.isNull()) {
return;
}
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
while (cursor.selectedText() != u' ' && !cursor.atBlockStart()) {
cursor.movePosition(QTextCursor::PreviousCharacter);
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
}
m_textStart = cursor.position() == 0 && cursor.selectedText() != u' ' ? 0 : cursor.position() + 1;
updateCompletion();
}
int CompletionModel::rowCount(const QModelIndex &parent) const
@@ -58,6 +78,12 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
}
if (role == ReplacedTextRole) {
return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
}
if (role == HRefRole) {
return u"https://matrix.to/#/%1"_s.arg(m_filterModel->data(filterIndex, UserListModel::UserIdRole).toString());
}
}
if (m_autoCompletionType == Command) {
@@ -85,6 +111,12 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
}
if (role == ReplacedTextRole) {
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
}
if (role == HRefRole) {
return u"https://matrix.to/#/%1"_s.arg(m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole).toString());
}
}
if (m_autoCompletionType == Emoji) {
if (role == DisplayNameRole) {
@@ -111,44 +143,57 @@ QHash<int, QByteArray> CompletionModel::roleNames() const
{SubtitleRole, "subtitle"},
{IconNameRole, "iconName"},
{ReplacedTextRole, "replacedText"},
{HRefRole, "hRef"},
};
}
void CompletionModel::updateCompletion()
{
if (text().startsWith(QLatin1Char('@'))) {
auto cursor = m_textItem->textCursor();
if (cursor.isNull()) {
return;
}
cursor.setPosition(m_textStart);
while (!cursor.selectedText().endsWith(u' ') && !cursor.atBlockEnd()) {
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
}
const auto text = cursor.selectedText().trimmed();
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
const auto fullText = cursor.selectedText();
if (text.startsWith(QLatin1Char('@'))) {
m_filterModel->setSourceModel(m_userListModel);
m_filterModel->setFilterRole(UserListModel::UserIdRole);
m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_filterModel->setFullText(fullText);
m_filterModel->setFilterText(text);
m_autoCompletionType = User;
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char('/'))) {
} else if (text.startsWith(QLatin1Char('/'))) {
m_filterModel->setSourceModel(&ActionsModel::instance());
m_filterModel->setFilterRole(ActionsModel::Prefix);
m_filterModel->setSecondaryFilterRole(-1);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text.mid(1));
m_filterModel->setFullText(fullText);
m_filterModel->setFilterText(text.mid(1));
m_autoCompletionType = Command;
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char('#'))) {
} else if (text.startsWith(QLatin1Char('#'))) {
m_autoCompletionType = Room;
m_filterModel->setSourceModel(m_roomListModel);
m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_filterModel->setFullText(fullText);
m_filterModel->setFilterText(text);
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
} else if (text.startsWith(QLatin1Char(':')) && text.size() > 1 && !text[1].isUpper()
&& (fullText.indexOf(QLatin1Char(':'), 1) == -1
|| (fullText.indexOf(QLatin1Char(' ')) != -1 && fullText.indexOf(QLatin1Char(':'), 1) > fullText.indexOf(QLatin1Char(' '), 1)))) {
m_filterModel->setSourceModel(m_emojiModel);
m_autoCompletionType = Emoji;
m_filterModel->setFilterRole(CustomEmojiModel::Name);
m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_filterModel->setFullText(fullText);
m_filterModel->setFilterText(text);
m_filterModel->invalidate();
} else {
m_autoCompletionType = None;
@@ -157,17 +202,6 @@ void CompletionModel::updateCompletion()
endResetModel();
}
NeoChatRoom *CompletionModel::room() const
{
return m_room;
}
void CompletionModel::setRoom(NeoChatRoom *room)
{
m_room = room;
Q_EMIT roomChanged();
}
CompletionModel::AutoCompletionType CompletionModel::autoCompletionType() const
{
return m_autoCompletionType;