Inline Edits
Edit text messages inline instead of in the chatbar
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "models/actionsmodel.h"
|
||||
#include "models/customemojimodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatuser.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
@@ -58,9 +59,9 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage()
|
||||
void ActionsHandler::handleNewMessage()
|
||||
{
|
||||
checkEffects();
|
||||
checkEffects(m_room->chatBoxText());
|
||||
if (!m_room->chatBoxAttachmentPath().isEmpty()) {
|
||||
QUrl url(m_room->chatBoxAttachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
@@ -69,13 +70,39 @@ void ActionsHandler::handleMessage()
|
||||
m_room->setChatBoxText({});
|
||||
return;
|
||||
}
|
||||
QString handledText = m_room->chatBoxText();
|
||||
|
||||
std::sort(m_room->mentions()->begin(), m_room->mentions()->end(), [](const auto &a, const auto &b) -> bool {
|
||||
QString handledText = m_room->chatBoxText();
|
||||
handledText = handleMentions(handledText);
|
||||
handleMessage(m_room->chatBoxText(), handledText);
|
||||
}
|
||||
|
||||
void ActionsHandler::handleEdit()
|
||||
{
|
||||
checkEffects(m_room->editText());
|
||||
|
||||
QString handledText = m_room->editText();
|
||||
handledText = handleMentions(handledText, true);
|
||||
handleMessage(m_room->editText(), handledText, true);
|
||||
}
|
||||
|
||||
QString ActionsHandler::handleMentions(QString handledText, const bool &isEdit)
|
||||
{
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVector<Mention> *mentions;
|
||||
if (isEdit) {
|
||||
mentions = m_room->editMentions();
|
||||
} else {
|
||||
mentions = m_room->mentions();
|
||||
}
|
||||
|
||||
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
|
||||
return a.cursor.anchor() > b.cursor.anchor();
|
||||
});
|
||||
|
||||
for (const auto &mention : *m_room->mentions()) {
|
||||
for (const auto &mention : *mentions) {
|
||||
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -83,11 +110,16 @@ void ActionsHandler::handleMessage()
|
||||
mention.cursor.position() - mention.cursor.anchor(),
|
||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text, mention.id));
|
||||
}
|
||||
m_room->mentions()->clear();
|
||||
mentions->clear();
|
||||
|
||||
return handledText;
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, const bool &isEdit)
|
||||
{
|
||||
if (NeoChatConfig::allowQuickEdit()) {
|
||||
QRegularExpression sed("^s/([^/]*)/([^/]*)(/g)?$");
|
||||
auto match = sed.match(m_room->chatBoxText());
|
||||
auto match = sed.match(text);
|
||||
if (match.hasMatch()) {
|
||||
const QString regex = match.captured(1);
|
||||
const QString replacement = match.captured(2).toHtmlEscaped();
|
||||
@@ -146,13 +178,13 @@ void ActionsHandler::handleMessage()
|
||||
if (handledText.length() == 0) {
|
||||
return;
|
||||
}
|
||||
m_room->postMessage(m_room->chatBoxText(), handledText, messageType, m_room->chatBoxReplyId(), m_room->chatBoxEditId());
|
||||
|
||||
m_room->postMessage(text, handledText, messageType, m_room->chatBoxReplyId(), isEdit ? m_room->chatBoxEditId() : "");
|
||||
}
|
||||
|
||||
void ActionsHandler::checkEffects()
|
||||
void ActionsHandler::checkEffects(const QString &text)
|
||||
{
|
||||
std::optional<QString> effect = std::nullopt;
|
||||
const auto &text = m_room->chatBoxText();
|
||||
if (text.contains("\u2744")) {
|
||||
effect = QLatin1String("snowflake");
|
||||
} else if (text.contains("\u1F386")) {
|
||||
|
||||
@@ -33,14 +33,22 @@ Q_SIGNALS:
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/// \brief Post a message.
|
||||
///
|
||||
/// This also interprets commands if any.
|
||||
void handleMessage();
|
||||
/**
|
||||
* @brief Pre-process text and send message.
|
||||
*/
|
||||
void handleNewMessage();
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send edit.
|
||||
*/
|
||||
void handleEdit();
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
void checkEffects();
|
||||
void checkEffects(const QString &text);
|
||||
|
||||
QString handleMentions(QString handledText, const bool &isEdit = false);
|
||||
void handleMessage(const QString &text, QString handledText, const bool &isEdit = false);
|
||||
};
|
||||
|
||||
QString markdownToHTML(const QString &markdown);
|
||||
|
||||
@@ -108,11 +108,16 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
||||
if (previousRoom) {
|
||||
disconnect(previousRoom, &NeoChatRoom::chatBoxTextChanged, this, nullptr);
|
||||
disconnect(previousRoom, &NeoChatRoom::editTextChanged, this, nullptr);
|
||||
}
|
||||
previousRoom = m_room;
|
||||
connect(m_room, &NeoChatRoom::chatBoxTextChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(m_room->chatBoxText().mid(start, cursorPosition() - start), m_room->chatBoxText().mid(start));
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::editTextChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
});
|
||||
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
||||
@@ -123,7 +128,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
return;
|
||||
}
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(m_room->chatBoxText().mid(start, cursorPosition() - start), m_room->chatBoxText().mid(start));
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,7 +143,7 @@ int ChatDocumentHandler::completionStartIndex() const
|
||||
#else
|
||||
const auto cursor = cursorPosition();
|
||||
#endif
|
||||
const auto &text = m_room->chatBoxText();
|
||||
const auto &text = getText();
|
||||
auto start = std::min(cursor, text.size()) - 1;
|
||||
while (start > -1) {
|
||||
if (text.at(start) == QLatin1Char(' ')) {
|
||||
@@ -150,6 +155,20 @@ int ChatDocumentHandler::completionStartIndex() const
|
||||
return start;
|
||||
}
|
||||
|
||||
bool ChatDocumentHandler::isEdit() const
|
||||
{
|
||||
return m_isEdit;
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::setIsEdit(bool edit)
|
||||
{
|
||||
if (edit == m_isEdit) {
|
||||
return;
|
||||
}
|
||||
m_isEdit = edit;
|
||||
Q_EMIT isEditChanged();
|
||||
}
|
||||
|
||||
QQuickTextDocument *ChatDocumentHandler::document() const
|
||||
{
|
||||
return m_document;
|
||||
@@ -204,7 +223,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Text).toString();
|
||||
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -213,11 +232,11 @@ void ChatDocumentHandler::complete(int index)
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
m_room->mentions()->push_back({cursor, name, 0, 0, id});
|
||||
pushMention({cursor, name, 0, 0, id});
|
||||
m_highlighter->rehighlight();
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
|
||||
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('/'));
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -225,7 +244,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
cursor.insertText(QStringLiteral("/%1 ").arg(command));
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
|
||||
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -234,11 +253,11 @@ void ChatDocumentHandler::complete(int index)
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
m_room->mentions()->push_back({cursor, alias, 0, 0, alias});
|
||||
pushMention({cursor, alias, 0, 0, alias});
|
||||
m_highlighter->rehighlight();
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
|
||||
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char(':'));
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -281,3 +300,27 @@ void ChatDocumentHandler::setSelectionEnd(int position)
|
||||
m_selectionEnd = position;
|
||||
Q_EMIT selectionEndChanged();
|
||||
}
|
||||
|
||||
QString ChatDocumentHandler::getText() const
|
||||
{
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
if (m_isEdit) {
|
||||
return m_room->editText();
|
||||
} else {
|
||||
return m_room->chatBoxText();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||
{
|
||||
if (!m_room) {
|
||||
return;
|
||||
}
|
||||
if (m_isEdit) {
|
||||
m_room->editMentions()->push_back(mention);
|
||||
} else {
|
||||
m_room->mentions()->push_back(mention);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "models/completionmodel.h"
|
||||
#include "models/userlistmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
class QTextDocument;
|
||||
class NeoChatRoom;
|
||||
@@ -17,6 +18,14 @@ class SyntaxHighlighter;
|
||||
class ChatDocumentHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
/**
|
||||
* @brief Is the instance being used to handle an edit message.
|
||||
*
|
||||
* This is needed to ensure that the text and mentions are saved and retrieved
|
||||
* from the correct parameters in the assigned room.
|
||||
*/
|
||||
Q_PROPERTY(bool isEdit READ isEdit WRITE setIsEdit NOTIFY isEditChanged)
|
||||
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
|
||||
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
|
||||
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
|
||||
@@ -24,11 +33,14 @@ class ChatDocumentHandler : public QObject
|
||||
|
||||
Q_PROPERTY(CompletionModel *completionModel READ completionModel NOTIFY completionModelChanged)
|
||||
|
||||
Q_PROPERTY(NeoChatRoom *room READ room NOTIFY roomChanged)
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
public:
|
||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool isEdit() const;
|
||||
void setIsEdit(bool edit);
|
||||
|
||||
[[nodiscard]] QQuickTextDocument *document() const;
|
||||
void setDocument(QQuickTextDocument *document);
|
||||
|
||||
@@ -49,6 +61,7 @@ public:
|
||||
void updateCompletions();
|
||||
CompletionModel *completionModel() const;
|
||||
Q_SIGNALS:
|
||||
void isEditChanged();
|
||||
void documentChanged();
|
||||
void cursorPositionChanged();
|
||||
void roomChanged();
|
||||
@@ -59,6 +72,8 @@ Q_SIGNALS:
|
||||
private:
|
||||
int completionStartIndex() const;
|
||||
|
||||
bool m_isEdit;
|
||||
|
||||
QQuickTextDocument *m_document;
|
||||
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
@@ -68,6 +83,9 @@ private:
|
||||
int m_selectionStart;
|
||||
int m_selectionEnd;
|
||||
|
||||
QString getText() const;
|
||||
void pushMention(const Mention mention) const;
|
||||
|
||||
SyntaxHighlighter *m_highlighter = nullptr;
|
||||
|
||||
CompletionModel::AutoCompletionType m_completionType = CompletionModel::None;
|
||||
|
||||
@@ -1626,6 +1626,17 @@ void NeoChatRoom::setChatBoxText(const QString &text)
|
||||
Q_EMIT chatBoxTextChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::editText() const
|
||||
{
|
||||
return m_editText;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setEditText(const QString &text)
|
||||
{
|
||||
m_editText = text;
|
||||
Q_EMIT editTextChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxReplyId() const
|
||||
{
|
||||
return m_chatBoxReplyId;
|
||||
@@ -1702,6 +1713,11 @@ QVector<Mention> *NeoChatRoom::mentions()
|
||||
return &m_mentions;
|
||||
}
|
||||
|
||||
QVector<Mention> *NeoChatRoom::editMentions()
|
||||
{
|
||||
return &m_editMentions;
|
||||
}
|
||||
|
||||
QString NeoChatRoom::savedText() const
|
||||
{
|
||||
return m_savedText;
|
||||
|
||||
@@ -79,6 +79,11 @@ class NeoChatRoom : public Quotient::Room
|
||||
|
||||
// Due to problems with QTextDocument, unlike the other properties here, chatBoxText is *not* used to store the text when switching rooms
|
||||
Q_PROPERTY(QString chatBoxText READ chatBoxText WRITE setChatBoxText NOTIFY chatBoxTextChanged)
|
||||
|
||||
/**
|
||||
* @brief The text for any message currently being edited in the room.
|
||||
*/
|
||||
Q_PROPERTY(QString editText READ editText WRITE setEditText NOTIFY editTextChanged)
|
||||
Q_PROPERTY(QString chatBoxReplyId READ chatBoxReplyId WRITE setChatBoxReplyId NOTIFY chatBoxReplyIdChanged)
|
||||
Q_PROPERTY(QString chatBoxEditId READ chatBoxEditId WRITE setChatBoxEditId NOTIFY chatBoxEditIdChanged)
|
||||
Q_PROPERTY(NeoChatUser *chatBoxReplyUser READ chatBoxReplyUser NOTIFY chatBoxReplyIdChanged)
|
||||
@@ -271,6 +276,9 @@ public:
|
||||
QString chatBoxText() const;
|
||||
void setChatBoxText(const QString &text);
|
||||
|
||||
QString editText() const;
|
||||
void setEditText(const QString &text);
|
||||
|
||||
QString chatBoxReplyId() const;
|
||||
void setChatBoxReplyId(const QString &replyId);
|
||||
|
||||
@@ -288,6 +296,11 @@ public:
|
||||
|
||||
QVector<Mention> *mentions();
|
||||
|
||||
/**
|
||||
* @brief Vector of mentions in the current edit text.
|
||||
*/
|
||||
QVector<Mention> *editMentions();
|
||||
|
||||
QString savedText() const;
|
||||
void setSavedText(const QString &savedText);
|
||||
|
||||
@@ -337,10 +350,12 @@ private:
|
||||
QCoro::Task<void> doUploadFile(QUrl url, QString body = QString());
|
||||
|
||||
QString m_chatBoxText;
|
||||
QString m_editText;
|
||||
QString m_chatBoxReplyId;
|
||||
QString m_chatBoxEditId;
|
||||
QString m_chatBoxAttachmentPath;
|
||||
QVector<Mention> m_mentions;
|
||||
QVector<Mention> m_editMentions;
|
||||
QString m_savedText;
|
||||
#ifdef QUOTIENT_07
|
||||
QCache<QString, PollHandler> m_polls;
|
||||
@@ -363,6 +378,7 @@ Q_SIGNALS:
|
||||
void pushNotificationStateChanged(PushNotificationState::State state);
|
||||
void showMessage(MessageType messageType, const QString &message);
|
||||
void chatBoxTextChanged();
|
||||
void editTextChanged();
|
||||
void chatBoxReplyIdChanged();
|
||||
void chatBoxEditIdChanged();
|
||||
void chatBoxAttachmentPathChanged();
|
||||
|
||||
@@ -14,9 +14,7 @@ QQC2.Control {
|
||||
|
||||
property alias textField: textField
|
||||
property bool isReplying: currentRoom.chatBoxReplyId.length > 0
|
||||
property bool isEditing: currentRoom.chatBoxEditId.length > 0
|
||||
property bool replyPaneVisible: isReplying || isEditing
|
||||
property NeoChatUser replyUser: currentRoom.chatBoxReplyUser ?? currentRoom.chatBoxEditUser
|
||||
property NeoChatUser replyUser: currentRoom.chatBoxReplyUser
|
||||
property bool attachmentPaneVisible: currentRoom.chatBoxAttachmentPath.length > 0
|
||||
|
||||
signal messageSent()
|
||||
@@ -122,7 +120,7 @@ QQC2.Control {
|
||||
leftPadding: LayoutMirroring.enabled ? actionsRow.width : (root.width > chatBoxMaxWidth ? 0 : Kirigami.Units.largeSpacing)
|
||||
rightPadding: LayoutMirroring.enabled ? (root.width > chatBoxMaxWidth ? 0 : Kirigami.Units.largeSpacing) : actionsRow.width
|
||||
|
||||
placeholderText: readOnly ? i18n("This room is encrypted. Build libQuotient with encryption enabled to send encrypted messages.") : currentRoom.chatBoxEditId.length > 0 ? i18n("Edit Message") : currentRoom.usesEncryption ? i18n("Send an encrypted message…") : currentRoom.chatBoxAttachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
|
||||
placeholderText: readOnly ? i18n("This room is encrypted. Build libQuotient with encryption enabled to send encrypted messages.") : currentRoom.usesEncryption ? i18n("Send an encrypted message…") : currentRoom.chatBoxAttachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
readOnly: (currentRoom.usesEncryption && !Controller.encryptionSupported)
|
||||
@@ -198,8 +196,8 @@ QQC2.Control {
|
||||
anchors.rightMargin: root.width > chatBoxMaxWidth ? 0 : (chatBarScrollView.QQC2.ScrollBar.vertical.visible ? Kirigami.Units.largeSpacing * 3.5 : Kirigami.Units.largeSpacing)
|
||||
|
||||
active: visible
|
||||
visible: root.replyPaneVisible || root.attachmentPaneVisible
|
||||
sourceComponent: root.replyPaneVisible ? replyPane : attachmentPane
|
||||
visible: root.isReplying || root.attachmentPaneVisible
|
||||
sourceComponent: root.isReplying ? replyPane : attachmentPane
|
||||
}
|
||||
Component {
|
||||
id: replyPane
|
||||
@@ -207,8 +205,7 @@ QQC2.Control {
|
||||
userName: root.replyUser ? root.replyUser.displayName : ""
|
||||
userColor: root.replyUser ? root.replyUser.color : ""
|
||||
userAvatar: root.replyUser ? "image://mxc/" + currentRoom.getUser(root.replyUser.id).avatarMediaId : ""
|
||||
isReply: root.isReplying
|
||||
text: isEditing ? currentRoom.chatBoxEditMessage : currentRoom.chatBoxReplyMessage
|
||||
text: currentRoom.chatBoxReplyMessage
|
||||
}
|
||||
}
|
||||
Component {
|
||||
@@ -263,14 +260,13 @@ QQC2.Control {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: (root.width - chatBoxMaxWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBoxMaxWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0)
|
||||
|
||||
visible: root.replyPaneVisible
|
||||
visible: root.isReplying
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
text: root.isReplying ? i18nc("@action:button", "Cancel reply") : i18nc("@action:button", "Cancel edit")
|
||||
text: i18nc("@action:button", "Cancel reply")
|
||||
icon.name: "dialog-close"
|
||||
onTriggered: {
|
||||
currentRoom.chatBoxReplyId = "";
|
||||
currentRoom.chatBoxEditId = "";
|
||||
currentRoom.chatBoxAttachmentPath = "";
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
@@ -356,15 +352,6 @@ QQC2.Control {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: currentRoom
|
||||
function onChatBoxEditIdChanged() {
|
||||
if (currentRoom.chatBoxEditMessage.length > 0) {
|
||||
textField.text = currentRoom.chatBoxEditMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChatDocumentHandler {
|
||||
id: documentHandler
|
||||
document: textField.textDocument
|
||||
@@ -398,12 +385,11 @@ QQC2.Control {
|
||||
}
|
||||
|
||||
function postMessage() {
|
||||
actionsHandler.handleMessage();
|
||||
actionsHandler.handleNewMessage();
|
||||
repeatTimer.stop()
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
textField.clear();
|
||||
currentRoom.chatBoxReplyId = "";
|
||||
currentRoom.chatBoxEditId = "";
|
||||
messageSent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ QQC2.Popup {
|
||||
connection: Controller.activeConnection
|
||||
}
|
||||
|
||||
required property var chatDocumentHandler
|
||||
property var chatDocumentHandler
|
||||
Component.onCompleted: {
|
||||
chatDocumentHandler.completionModel.roomListModel = roomListModel;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ GridLayout {
|
||||
property string userName
|
||||
property color userColor: Kirigami.Theme.highlightColor
|
||||
property var userAvatar: ""
|
||||
property bool isReply
|
||||
property var text
|
||||
|
||||
rows: 3
|
||||
@@ -30,7 +29,7 @@ GridLayout {
|
||||
Layout.columnSpan: 3
|
||||
topPadding: Kirigami.Units.smallSpacing
|
||||
|
||||
text: isReply ? i18n("Replying to:") : i18n("Editing message:")
|
||||
text: i18n("Replying to:")
|
||||
}
|
||||
Rectangle {
|
||||
id: verticalBorder
|
||||
|
||||
@@ -21,8 +21,14 @@ TimelineContainer {
|
||||
RichLabel {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
visible: currentRoom.chatBoxEditId !== model.eventId
|
||||
isEmote: messageDelegate.isEmote
|
||||
}
|
||||
MessageEditComponent {
|
||||
Layout.fillWidth: true
|
||||
messageId: model.eventId
|
||||
visible: currentRoom.chatBoxEditId === model.eventId
|
||||
}
|
||||
Loader {
|
||||
id: linkPreviewLoader
|
||||
Layout.fillWidth: true
|
||||
|
||||
149
src/qml/Component/Timeline/MessageEditComponent.qml
Normal file
149
src/qml/Component/Timeline/MessageEditComponent.qml
Normal file
@@ -0,0 +1,149 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
QQC2.TextArea {
|
||||
id: root
|
||||
|
||||
property string messageId
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: editButtons.height + topPadding + bottomPadding
|
||||
Layout.preferredWidth: editTextMetrics.advanceWidth + rightPadding + Kirigami.Units.smallSpacing + Kirigami.Units.gridUnit
|
||||
rightPadding: editButtons.width + editButtons.anchors.rightMargin * 2
|
||||
|
||||
color: Kirigami.Theme.textColor
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
forceActiveFocus();
|
||||
root.cursorPosition = root.length;
|
||||
}
|
||||
}
|
||||
onTextChanged: {
|
||||
currentRoom.editText = text
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.complete()
|
||||
} else if (event.modifiers & Qt.ShiftModifier) {
|
||||
root.insert(cursorPosition, "\n")
|
||||
} else {
|
||||
root.postEdit();
|
||||
}
|
||||
}
|
||||
Keys.onReturnPressed: {
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.complete()
|
||||
} else if (event.modifiers & Qt.ShiftModifier) {
|
||||
root.insert(cursorPosition, "\n")
|
||||
} else {
|
||||
root.postEdit();
|
||||
}
|
||||
}
|
||||
Keys.onTabPressed: {
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.complete()
|
||||
}
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_Up && completionMenu.visible) {
|
||||
completionMenu.decrementIndex()
|
||||
} else if (event.key === Qt.Key_Down && completionMenu.visible) {
|
||||
completionMenu.incrementIndex()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is anchored like this so that control expands properly as the edited
|
||||
* text grows in length.
|
||||
*/
|
||||
RowLayout {
|
||||
id: editButtons
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
||||
spacing: 0
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
text: i18nc("@action:button", "Confirm edit")
|
||||
icon.name: "checkmark"
|
||||
onTriggered: {
|
||||
root.postEdit();
|
||||
}
|
||||
}
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
text: i18nc("@action:button", "Cancel edit")
|
||||
icon.name: "dialog-close"
|
||||
onTriggered: {
|
||||
currentRoom.chatBoxEditId = "";
|
||||
}
|
||||
shortcut: "Escape"
|
||||
}
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: currentRoom
|
||||
function onChatBoxEditIdChanged() {
|
||||
if (currentRoom.chatBoxEditId == messageId && currentRoom.chatBoxEditMessage.length > 0) {
|
||||
root.text = currentRoom.chatBoxEditMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CompletionMenu {
|
||||
id: completionMenu
|
||||
height: implicitHeight
|
||||
y: -height - 5
|
||||
z: 10
|
||||
chatDocumentHandler: documentHandler
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
property: "height"
|
||||
duration: Kirigami.Units.shortDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChatDocumentHandler {
|
||||
id: documentHandler
|
||||
isEdit: true
|
||||
document: root.textDocument
|
||||
cursorPosition: root.cursorPosition
|
||||
selectionStart: root.selectionStart
|
||||
selectionEnd: root.selectionEnd
|
||||
room: currentRoom // We don't care about saving for edits so this is OK.
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: editTextMetrics
|
||||
text: root.text
|
||||
}
|
||||
|
||||
function postEdit() {
|
||||
actionsHandler.handleEdit();
|
||||
root.clear();
|
||||
currentRoom.chatBoxEditId = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -540,7 +540,6 @@ Kirigami.ScrollablePage {
|
||||
onClicked: {
|
||||
currentRoom.chatBoxEditId = hoverActions.event.eventId;
|
||||
currentRoom.chatBoxReplyId = "";
|
||||
chatBox.chatBar.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<file alias="PollDelegate.qml">qml/Component/Timeline/PollDelegate.qml</file>
|
||||
<file alias="MimeComponent.qml">qml/Component/Timeline/MimeComponent.qml</file>
|
||||
<file alias="StateComponent.qml">qml/Component/Timeline/StateComponent.qml</file>
|
||||
<file alias="MessageEditComponent.qml">qml/Component/Timeline/MessageEditComponent.qml</file>
|
||||
<file alias="LoginStep.qml">qml/Component/Login/LoginStep.qml</file>
|
||||
<file alias="Login.qml">qml/Component/Login/Login.qml</file>
|
||||
<file alias="Password.qml">qml/Component/Login/Password.qml</file>
|
||||
|
||||
@@ -125,6 +125,9 @@ void RoomManager::openRoomForActiveConnection()
|
||||
|
||||
void RoomManager::enterRoom(NeoChatRoom *room)
|
||||
{
|
||||
if (m_currentRoom && !m_currentRoom->chatBoxEditId().isEmpty()) {
|
||||
m_currentRoom->setChatBoxEditId("");
|
||||
}
|
||||
if (m_currentRoom && m_chatDocumentHandler) {
|
||||
// We're doing these things here because it is critical that they are switched at the same time
|
||||
m_currentRoom->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
||||
|
||||
Reference in New Issue
Block a user