diff --git a/autotests/chatkeyhelpertesthelper.h b/autotests/chatkeyhelpertesthelper.h index 50be4e9b0..20528cec5 100644 --- a/autotests/chatkeyhelpertesthelper.h +++ b/autotests/chatkeyhelpertesthelper.h @@ -39,7 +39,7 @@ public: return; } m_textItem = textItem; - m_keyHelper->setTextItem(textItem); + m_keyHelper->textItem = textItem; Q_EMIT textItemChanged(); } diff --git a/src/app/qml/UserMenu.qml b/src/app/qml/UserMenu.qml index 1c2859f40..ac30a6b4c 100644 --- a/src/app/qml/UserMenu.qml +++ b/src/app/qml/UserMenu.qml @@ -68,7 +68,7 @@ KirigamiComponents.ConvergentContextMenu { text: i18nc("@action:button", "Mention") icon.name: "username-copy-symbolic" onTriggered: { - RoomManager.currentRoom.mainCache.mentionAdded(root.author.id); + RoomManager.currentRoom.mainCache.mentionAdded(root.author.disambiguatedName, "https://matrix.to/#/" + root.author.id); } } } diff --git a/src/chatbar/ChatBar.qml b/src/chatbar/ChatBar.qml index e249f66b0..59a101056 100644 --- a/src/chatbar/ChatBar.qml +++ b/src/chatbar/ChatBar.qml @@ -40,12 +40,45 @@ QQC2.Control { onCurrentRoomChanged: { _private.chatBarCache = currentRoom.mainCache if (ShareHandler.text.length > 0 && ShareHandler.room === root.currentRoom.id) { + contentModel.focusedTextItem. textField.text = ShareHandler.text; ShareHandler.text = ""; ShareHandler.room = ""; } } + Connections { + target: contentModel.keyHelper + + function onUnhandledUp(isCompleting: bool): void { + if (!isCompleting) { + return; + } + completionMenu.decrementIndex(); + } + + function onUnhandledDown(isCompleting: bool): void { + if (!isCompleting) { + return; + } + completionMenu.incrementIndex(); + } + + function onUnhandledTab(isCompleting: bool): void { + if (!isCompleting) { + return; + } + completionMenu.completeCurrent(); + } + + function onUnhandledReturn(isCompleting: bool): void { + if (!isCompleting) { + return; + } + completionMenu.completeCurrent(); + } + } + Connections { target: ShareHandler function onRoomChanged(): void { @@ -60,13 +93,10 @@ QQC2.Control { Connections { target: root.currentRoom.mainCache - function onMentionAdded(mention: string): void { - // add mention text - textField.append(mention + " "); - // move cursor to the end - textField.cursorPosition = textField.text.length; + function onMentionAdded(text: string, hRef: string): void { + completionMenu.complete(text, hRef); // move the focus back to the chat bar - textField.forceActiveFocus(Qt.OtherFocusReason); + contentModel.refocusCurrentComponent(); } } @@ -93,30 +123,39 @@ QQC2.Control { topPadding: Kirigami.Units.smallSpacing bottomPadding: Kirigami.Units.smallSpacing - contentItem: QQC2.ScrollView { - id: chatScrollView - ColumnLayout { - spacing: Kirigami.Units.smallSpacing + contentItem: ColumnLayout { + QQC2.ScrollView { + id: chatScrollView + Layout.fillWidth: true + Layout.maximumHeight: Kirigami.Units.gridUnit * 8 - Repeater { - id: chatContentView - model: ChatBarMessageContentModel { - id: contentModel - type: ChatBarType.Room - room: root.currentRoom + clip: true + + ColumnLayout { + width: chatScrollView.width + spacing: Kirigami.Units.smallSpacing + + Repeater { + id: chatContentView + model: ChatBarMessageContentModel { + id: contentModel + type: ChatBarType.Room + room: root.currentRoom + } + + delegate: MessageComponentChooser {} } - - delegate: MessageComponentChooser {} } - RichEditBar { - id: richEditBar - maxAvailableWidth: chatBarSizeHelper.availableWidth - Kirigami.Units.largeSpacing * 2 + } + RichEditBar { + id: richEditBar + Layout.alignment: Qt.AlignCenter + maxAvailableWidth: chatBarSizeHelper.availableWidth - Kirigami.Units.largeSpacing * 2 - room: root.currentRoom - contentModel: chatContentView.model + room: root.currentRoom + contentModel: chatContentView.model - onClicked: contentModel.refocusCurrentComponent() - } + onClicked: contentModel.refocusCurrentComponent() } } @@ -151,7 +190,7 @@ QQC2.Control { id: completionMenu room: root.currentRoom type: LibNeoChat.ChatBarType.Room - textItem: chatContentView.model.focusedTextItem + textItem: contentModel.focusedTextItem x: 1 y: -height diff --git a/src/chatbar/CompletionMenu.qml b/src/chatbar/CompletionMenu.qml index f1d97e58d..4f8b1ebe0 100644 --- a/src/chatbar/CompletionMenu.qml +++ b/src/chatbar/CompletionMenu.qml @@ -46,8 +46,12 @@ QQC2.Popup { completions.decrementCurrentIndex(); } - function complete() { - completionModel.insertCompletion(completions.currentItem.replacedText, completions.currentItem.hRef) + function complete(text: string, hRef: string) { + completionModel.insertCompletion(text, hRef); + } + + function completeCurrent() { + completionModel.insertCompletion(completions.currentItem.replacedText, completions.currentItem.hRef); } leftPadding: 0 diff --git a/src/libneochat/chatbarcache.h b/src/libneochat/chatbarcache.h index ada09c4e2..67a3d1dde 100644 --- a/src/libneochat/chatbarcache.h +++ b/src/libneochat/chatbarcache.h @@ -205,7 +205,7 @@ Q_SIGNALS: void relationIdChanged(const QString &oldEventId, const QString &newEventId); void threadIdChanged(const QString &oldThreadId, const QString &newThreadId); void attachmentPathChanged(); - void mentionAdded(const QString &mention); + void mentionAdded(const QString &text, const QString &hRef); void relationAuthorIsPresentChanged(); private: diff --git a/src/libneochat/chatkeyhelper.cpp b/src/libneochat/chatkeyhelper.cpp index fb2479f29..978eb43b0 100644 --- a/src/libneochat/chatkeyhelper.cpp +++ b/src/libneochat/chatkeyhelper.cpp @@ -3,120 +3,191 @@ #include "chatkeyhelper.h" +#include "chattextitemhelper.h" +#include "clipboard.h" +#include "neochatroom.h" + ChatKeyHelper::ChatKeyHelper(QObject *parent) : QObject(parent) { } -ChatTextItemHelper *ChatKeyHelper::textItem() const +bool ChatKeyHelper::handleKey(Qt::Key key, Qt::KeyboardModifiers modifiers) { - return m_textItem; + switch (key) { + case Qt::Key_V: + return vKey(modifiers); + case Qt::Key_Up: + return up(modifiers); + case Qt::Key_Down: + return down(); + case Qt::Key_Tab: + return tab(); + case Qt::Key_Delete: + return deleteChar(); + case Qt::Key_Backspace: + return backspace(); + case Qt::Key_Enter: + case Qt::Key_Return: + return insertReturn(); + default: + return false; + } } -void ChatKeyHelper::setTextItem(ChatTextItemHelper *textItem) +bool ChatKeyHelper::vKey(Qt::KeyboardModifiers modifiers) { - if (textItem == m_textItem) { - return; + if (!textItem) { + return false; } - if (m_textItem) { - m_textItem->disconnect(this); + if (modifiers.testFlag(Qt::ControlModifier)) { + return pasteImage(); } - - m_textItem = textItem; - - if (m_textItem) { - connect(m_textItem, &ChatTextItemHelper::textItemChanged, this, &ChatKeyHelper::textItemChanged); - } - - Q_EMIT textItemChanged(); + return false; } -void ChatKeyHelper::up() +bool ChatKeyHelper::up(Qt::KeyboardModifiers modifiers) { - if (!m_textItem) { - return; + if (!textItem) { + return false; } - QTextCursor cursor = m_textItem->textCursor(); + + if (modifiers.testFlag(Qt::ControlModifier)) { + room->replyLastMessage(); + return true; + } + + if (textItem->isEmpty()) { + room->editLastMessage(); + return true; + } + + if (textItem->isCompleting) { + Q_EMIT unhandledUp(true); + return true; + } + + QTextCursor cursor = textItem->textCursor(); if (cursor.isNull()) { - return; + return false; } if (cursor.blockNumber() == 0 && cursor.block().layout()->lineForTextPosition(cursor.positionInBlock()).lineNumber() == 0) { - Q_EMIT unhandledUp(); - return; + Q_EMIT unhandledUp(false); + return true; } - cursor.movePosition(QTextCursor::Up); - m_textItem->setCursorPosition(cursor.position()); + return false; } -void ChatKeyHelper::down() +bool ChatKeyHelper::down() { - if (!m_textItem) { - return; + if (!textItem) { + return false; } - QTextCursor cursor = m_textItem->textCursor(); + if (textItem->isCompleting) { + Q_EMIT unhandledDown(true); + return true; + } + + QTextCursor cursor = textItem->textCursor(); if (cursor.isNull()) { - return; + return false; } if (cursor.blockNumber() == cursor.document()->blockCount() - 1 && cursor.block().layout()->lineForTextPosition(cursor.positionInBlock()).lineNumber() == (cursor.block().layout()->lineCount() - 1)) { - Q_EMIT unhandledDown(); - return; + Q_EMIT unhandledDown(false); + return true; } - cursor.movePosition(QTextCursor::Down); - m_textItem->setCursorPosition(cursor.position()); + return false; } -void ChatKeyHelper::tab() +bool ChatKeyHelper::tab() { - QTextCursor cursor = m_textItem->textCursor(); + if (!textItem) { + return false; + } + if (textItem->isCompleting) { + Q_EMIT unhandledTab(true); + return true; + } + + QTextCursor cursor = textItem->textCursor(); if (cursor.isNull()) { - return; + return false; } - if (cursor.currentList() && m_textItem->canIndentListMoreAtCursor()) { - m_textItem->indentListMoreAtCursor(); - return; + if (cursor.currentList() && textItem->canIndentListMoreAtCursor()) { + textItem->indentListMoreAtCursor(); + return true; } - cursor.insertText(u" "_s); + return false; } -void ChatKeyHelper::deleteChar() +bool ChatKeyHelper::deleteChar() { - QTextCursor cursor = m_textItem->textCursor(); - if (cursor.isNull()) { - return; + if (!textItem) { + return false; } - if (cursor.position() >= m_textItem->document()->characterCount() - m_textItem->fixedEndChars().length() - 1) { + + QTextCursor cursor = textItem->textCursor(); + if (cursor.isNull()) { + return false; + } + if (cursor.position() >= textItem->document()->characterCount() - textItem->fixedEndChars().length() - 1) { Q_EMIT unhandledDelete(); - return; + return true; } - cursor.deleteChar(); + return false; } -void ChatKeyHelper::backspace() +bool ChatKeyHelper::backspace() { - QTextCursor cursor = m_textItem->textCursor(); - if (cursor.isNull()) { - return; + if (!textItem) { + return false; } - if (cursor.position() <= m_textItem->fixedStartChars().length()) { - if (cursor.currentList() && m_textItem->canIndentListLessAtCursor()) { - m_textItem->indentListLessAtCursor(); - return; + + QTextCursor cursor = textItem->textCursor(); + if (cursor.isNull()) { + return false; + } + if (cursor.position() <= textItem->fixedStartChars().length()) { + if (cursor.currentList() && textItem->canIndentListLessAtCursor()) { + textItem->indentListLessAtCursor(); + return true; } Q_EMIT unhandledBackspace(); - return; + return true; } - cursor.deletePreviousChar(); + return false; } -void ChatKeyHelper::insertReturn() +bool ChatKeyHelper::insertReturn() { - QTextCursor cursor = m_textItem->textCursor(); + if (!textItem) { + return false; + } + if (textItem->isCompleting) { + Q_EMIT unhandledReturn(true); + return true; + } + + QTextCursor cursor = textItem->textCursor(); if (cursor.isNull()) { - return; + return false; } cursor.insertBlock(); + return true; +} + +bool ChatKeyHelper::pasteImage() +{ + if (!textItem) { + return false; + } + const auto savePath = Clipboard().saveImage(); + if (!savePath.isEmpty()) { + Q_EMIT imagePasted(savePath); + } + return false; } #include "moc_chatkeyhelper.cpp" diff --git a/src/libneochat/chatkeyhelper.h b/src/libneochat/chatkeyhelper.h index e230dabf8..d11d7e00d 100644 --- a/src/libneochat/chatkeyhelper.h +++ b/src/libneochat/chatkeyhelper.h @@ -6,8 +6,18 @@ #include #include -#include "chattextitemhelper.h" +class NeoChatRoom; +class ChatTextItemHelper; +/** + * @class ChatKeyHelper + * + * A class to handle some key presses on behalf of a ChatTextItemHelper. + * + * This is used to manage complex rich text interactions. + * + * @sa ChatTextItemHelper + */ class ChatKeyHelper : public QObject { Q_OBJECT @@ -16,72 +26,94 @@ class ChatKeyHelper : public QObject public: explicit ChatKeyHelper(QObject *parent = nullptr); - ChatTextItemHelper *textItem() const; - void setTextItem(ChatTextItemHelper *textItem); + /** + * @brief The ChatTextItemHelper that ChatKeyHelper is handling key presses for. + * + * @sa ChatTextItemHelper + */ + QPointer room; /** - * @brief Handle up key at current cursor location. + * @brief The ChatTextItemHelper that ChatKeyHelper is handling key presses for. + * + * @sa ChatTextItemHelper */ - Q_INVOKABLE void up(); + QPointer textItem; - /** - * @brief Handle down key at current cursor location. - */ - Q_INVOKABLE void down(); - - /** - * @brief Handle tab key at current cursor location. - */ - Q_INVOKABLE void tab(); - - /** - * @brief Handle delete key at current cursor location. - */ - Q_INVOKABLE void deleteChar(); - - /** - * @brief Handle backspace key at current cursor location. - */ - Q_INVOKABLE void backspace(); - - /** - * @brief Handle return key at current cursor location. - */ - Q_INVOKABLE void insertReturn(); + Q_INVOKABLE bool handleKey(Qt::Key key, Qt::KeyboardModifiers modifiers); Q_SIGNALS: - void textItemChanged(); - /** * @brief There is an unhandled up key press. * - * i.e. up is pressed on the first line of the first block of the text item. + * Current trigger conditions: + * - Up is pressed on the first line of the first block of the text item. + * - Return clicked when a completion has been started. */ - void unhandledUp(); + void unhandledUp(bool isCompleting); /** * @brief There is an unhandled down key press. * - * i.e. down is pressed on the last line of the last block of the text item. + * Current trigger conditions: + * - Down is pressed on the last line of the last block of the text item. + * - Return clicked when a completion has been started. */ - void unhandledDown(); + void unhandledDown(bool isCompleting); + + /** + * @brief There is an unhandled tab key press. + * + * Current trigger conditions: + * - Tab clicked when a completion has been started. + */ + void unhandledTab(bool isCompleting); /** * @brief There is an unhandled delete key press. * - * i.e. delete is pressed at the end of the last line of the last block of the - * text item. + * Current trigger conditions: + * - Delete is pressed at the end of the last line of the last block of the + * text item. */ void unhandledDelete(); /** * @brief There is an unhandled backspace key press. * - * i.e. backspace is pressed at the beginning of the first line of the first - * block of the text item. + * Current trigger conditions: + * - Backspace is pressed at the beginning of the first line of the first + * block of the text item. */ void unhandledBackspace(); + /** + * @brief There is an unhandled return key press. + * + * Current trigger conditions: + * - Return clicked when a completion has been started. + */ + void unhandledReturn(bool isCompleting); + + /** + * @brief An image has been pasted. + */ + void imagePasted(const QString &filePath); + private: - QPointer m_textItem; + bool vKey(Qt::KeyboardModifiers modifiers); + + bool up(Qt::KeyboardModifiers modifiers); + + bool down(); + + bool tab(); + + bool deleteChar(); + + bool backspace(); + + bool insertReturn(); + + bool pasteImage(); }; diff --git a/src/libneochat/chattextitemhelper.h b/src/libneochat/chattextitemhelper.h index e0610ec1e..fe7053837 100644 --- a/src/libneochat/chattextitemhelper.h +++ b/src/libneochat/chattextitemhelper.h @@ -64,6 +64,11 @@ public: QQuickItem *textItem() const; void setTextItem(QQuickItem *textItem); + /** + * @brief Whether a completion has started based on recent text entry. + */ + bool isCompleting = false; + /** * @brief The fixed characters that will always be at the beginning of the text item. */ @@ -98,6 +103,13 @@ public: */ QTextDocument *document() const; + /** + * @brief Whetehr the underlying QTextDocument is empty. + * + * @sa QTextDocument + */ + bool isEmpty() const; + /** * @brief The line count of the text item. */ @@ -238,7 +250,6 @@ private: void initializeChars(); bool m_initializingChars = false; - bool isEmpty() const; std::optional lineLength(int lineNumber) const; int selectionStart() const; diff --git a/src/libneochat/models/completionmodel.cpp b/src/libneochat/models/completionmodel.cpp index 2832d9329..7a9764dbd 100644 --- a/src/libneochat/models/completionmodel.cpp +++ b/src/libneochat/models/completionmodel.cpp @@ -240,6 +240,8 @@ void CompletionModel::updateCompletion() } beginResetModel(); endResetModel(); + + m_textItem->isCompleting = rowCount() > 0; } CompletionModel::AutoCompletionType CompletionModel::autoCompletionType() const diff --git a/src/libneochat/utils.cpp b/src/libneochat/utils.cpp index 0f3a55a6e..e029f3742 100644 --- a/src/libneochat/utils.cpp +++ b/src/libneochat/utils.cpp @@ -55,6 +55,10 @@ QString QmlUtils::nameForPowerLevelValue(const int value) bool Utils::isEmoji(const QString &text) { + if (text.isEmpty()) { + return false; + } + #ifdef HAVE_ICU QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, text); int from = 0; diff --git a/src/messagecontent/ChatBarComponent.qml b/src/messagecontent/ChatBarComponent.qml index 02580e424..c39bd68b1 100644 --- a/src/messagecontent/ChatBarComponent.qml +++ b/src/messagecontent/ChatBarComponent.qml @@ -75,7 +75,7 @@ QQC2.Control { Keys.onEnterPressed: event => { if (completionMenu.visible) { - completionMenu.complete(); + completionMenu.completeCurrent(); } else if (event.modifiers & Qt.ShiftModifier) { textArea.insert(cursorPosition, "\n"); } else { @@ -84,7 +84,7 @@ QQC2.Control { } Keys.onReturnPressed: event => { if (completionMenu.visible) { - completionMenu.complete(); + completionMenu.completeCurrent(); } else if (event.modifiers & Qt.ShiftModifier) { textArea.insert(cursorPosition, "\n"); } else { @@ -93,7 +93,7 @@ QQC2.Control { } Keys.onTabPressed: { if (completionMenu.visible) { - completionMenu.complete(); + completionMenu.completeCurrent(); } } Keys.onPressed: event => { diff --git a/src/messagecontent/CodeComponent.qml b/src/messagecontent/CodeComponent.qml index 0382c3b34..e30d082a6 100644 --- a/src/messagecontent/CodeComponent.qml +++ b/src/messagecontent/CodeComponent.qml @@ -91,27 +91,8 @@ QQC2.Control { QQC2.TextArea { id: codeText - Keys.onUpPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.up(); - } - Keys.onDownPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.down(); - } - - Keys.onDeletePressed: (event) => { - event.accepted = true; - root.Message.contentModel.keyHelper.deleteChar(); - } - Keys.onPressed: (event) => { - if (event.key == Qt.Key_Backspace && cursorPosition == 0) { - event.accepted = true; - root.Message.contentModel.keyHelper.backspace(); - return; - } - event.accepted = false; + event.accepted = Message.contentModel.keyHelper.handleKey(event.key, event.modifiers); } onFocusChanged: if (focus && !root.currentFocus) { diff --git a/src/messagecontent/QuoteComponent.qml b/src/messagecontent/QuoteComponent.qml index 810f59d3f..05727553e 100644 --- a/src/messagecontent/QuoteComponent.qml +++ b/src/messagecontent/QuoteComponent.qml @@ -63,26 +63,8 @@ QQC2.TextArea { */ signal selectedTextChanged(string selectedText) - Keys.onUpPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.up(); - } - Keys.onDownPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.down(); - } - - Keys.onDeletePressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.deleteChar(); - } Keys.onPressed: (event) => { - if (event.key == Qt.Key_Backspace) { - event.accepted = true; - Message.contentModel.keyHelper.backspace(); - return; - } - event.accepted = false; + event.accepted = Message.contentModel.keyHelper.handleKey(event.key, event.modifiers); } Layout.fillWidth: true diff --git a/src/messagecontent/TextComponent.qml b/src/messagecontent/TextComponent.qml index 9a49b474e..0d3b83a02 100644 --- a/src/messagecontent/TextComponent.qml +++ b/src/messagecontent/TextComponent.qml @@ -75,41 +75,8 @@ TextEdit { Layout.fillWidth: true Layout.maximumWidth: Message.maxContentWidth - Keys.onUpPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.up(); - } - Keys.onDownPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.down(); - } - - Keys.onTabPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.tab(); - } - - Keys.onDeletePressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.deleteChar(); - } - Keys.onPressed: (event) => { - if (event.key == Qt.Key_Backspace && cursorPosition == 0) { - event.accepted = true; - Message.contentModel.keyHelper.backspace(); - return; - } - event.accepted = false; - } - - Keys.onEnterPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.insertReturn(); - } - Keys.onReturnPressed: (event) => { - event.accepted = true; - Message.contentModel.keyHelper.insertReturn(); + event.accepted = Message.contentModel.keyHelper.handleKey(event.key, event.modifiers); } onFocusChanged: if (focus && !root.currentFocus) { diff --git a/src/messagecontent/models/chatbarmessagecontentmodel.cpp b/src/messagecontent/models/chatbarmessagecontentmodel.cpp index b21ee9126..22b268c27 100644 --- a/src/messagecontent/models/chatbarmessagecontentmodel.cpp +++ b/src/messagecontent/models/chatbarmessagecontentmodel.cpp @@ -25,8 +25,6 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent) , m_keyHelper(new ChatKeyHelper(this)) { m_editableActive = true; - connectKeyHelper(); - initializeModel(); connect(this, &ChatBarMessageContentModel::roomChanged, this, [this]() { if (m_type == ChatBarType::None || !m_room) { @@ -34,15 +32,23 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent) } connect(m_room->cacheForType(m_type), &ChatBarCache::relationIdChanged, this, &ChatBarMessageContentModel::updateReplyModel); - clearModel(); - - beginResetModel(); + connect(m_room->cacheForType(m_type), &ChatBarCache::attachmentPathChanged, this, [this]() { + if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) { + addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath())); + } + }); if (m_room->cacheForType(m_type)->attachmentPath().length() > 0) { addAttachment(QUrl(m_room->cacheForType(m_type)->attachmentPath())); } const auto textSections = m_room->cacheForType(m_type)->text().split(u"\n\n"_s); + if (textSections.length() == 1 && textSections[0].isEmpty()) { + return; + } + + clearModel(); + beginResetModel(); for (const auto §ion : textSections) { const auto type = MessageComponentType::typeForString(section); auto cleanText = section; @@ -54,14 +60,14 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent) } insertComponent(rowCount(), type, {}, cleanText); } - m_currentFocusComponent = QPersistentModelIndex(index(rowCount() - 1)); endResetModel(); + m_currentFocusComponent = QPersistentModelIndex(index(rowCount() - 1)); Q_EMIT focusRowChanged(); }); connect(this, &ChatBarMessageContentModel::focusRowChanged, this, [this]() { m_markdownHelper->setTextItem(focusedTextItem()); - m_keyHelper->setTextItem(focusedTextItem()); + m_keyHelper->textItem = focusedTextItem(); }); connect(this, &ChatBarMessageContentModel::roomChanged, this, [this]() { for (const auto &component : m_components) { @@ -69,6 +75,7 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent) textItem->setRoom(m_room); } } + m_keyHelper->room = m_room; }); connect(this, &ChatBarMessageContentModel::typeChanged, this, [this]() { for (const auto &component : m_components) { @@ -78,6 +85,9 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent) } }); connect(m_markdownHelper, &ChatMarkdownHelper::unhandledBlockFormat, this, &ChatBarMessageContentModel::insertStyleAtCursor); + + connectKeyHelper(); + initializeModel(); } void ChatBarMessageContentModel::initializeModel() @@ -92,9 +102,9 @@ void ChatBarMessageContentModel::initializeModel() .display = {}, .attributes = {{TextItemKey, QVariant::fromValue(textItem)}}, }; - m_currentFocusComponent = QPersistentModelIndex(index(0)); endInsertRows(); + m_currentFocusComponent = QPersistentModelIndex(index(0)); Q_EMIT focusRowChanged(); } @@ -119,10 +129,16 @@ ChatKeyHelper *ChatBarMessageContentModel::keyHelper() const void ChatBarMessageContentModel::connectKeyHelper() { - connect(m_keyHelper, &ChatKeyHelper::unhandledUp, this, [this]() { + connect(m_keyHelper, &ChatKeyHelper::unhandledUp, this, [this](bool isCompleting) { + if (isCompleting) { + return; + } setFocusRow(m_currentFocusComponent.row() - 1); }); - connect(m_keyHelper, &ChatKeyHelper::unhandledDown, this, [this]() { + connect(m_keyHelper, &ChatKeyHelper::unhandledDown, this, [this](bool isCompleting) { + if (isCompleting) { + return; + } setFocusRow(m_currentFocusComponent.row() + 1); }); connect(m_keyHelper, &ChatKeyHelper::unhandledDelete, this, [this]() { @@ -150,6 +166,9 @@ void ChatBarMessageContentModel::connectKeyHelper() insertComponentAtCursor(MessageComponentType::Text); } }); + connect(m_keyHelper, &ChatKeyHelper::imagePasted, this, [this](const QString &filePath) { + m_room->cacheForType(m_type)->setAttachmentPath(filePath); + }); } int ChatBarMessageContentModel::focusRow() const @@ -508,7 +527,6 @@ void ChatBarMessageContentModel::postMessage() return; } - qWarning() << m_room->cacheForType(m_type)->text(); m_room->cacheForType(m_type)->postMessage(); clearModel(); initializeModel();