Files
neochat/src/timeline/MessageEditComponent.qml
2024-04-25 23:02:59 +02:00

185 lines
5.6 KiB
QML

// 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
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
import org.kde.neochat.chatbar
/**
* @brief A component to show an edit text field for a text message being edited.
*/
QQC2.TextArea {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
onRoomChanged: {
_private.chatBarCache = room.editCache;
_private.chatBarCache.relationIdChanged.connect(_private.updateEditText());
}
/**
* @brief The ActionsHandler object to use.
*
* This is expected to have the correct room set otherwise messages will be sent
* to the wrong room.
*/
required property ActionsHandler actionsHandler
property var minimumHeight: editButtons.height + topPadding + bottomPadding
property var preferredWidth: editTextMetrics.advanceWidth + rightPadding + Kirigami.Units.smallSpacing + Kirigami.Units.gridUnit
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Component.onCompleted: _private.updateEditText()
rightPadding: editButtons.width + editButtons.anchors.rightMargin * 2
color: Kirigami.Theme.textColor
verticalAlignment: TextEdit.AlignVCenter
wrapMode: TextEdit.Wrap
onTextChanged: {
_private.chatBarCache.text = 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: event => {
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: {
_private.chatBarCache.editId = "";
}
shortcut: "Escape"
}
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
}
}
CompletionMenu {
id: completionMenu
height: implicitHeight
y: -height - 5
z: 10
connection: root.room.connection
chatDocumentHandler: documentHandler
Behavior on height {
NumberAnimation {
property: "height"
duration: Kirigami.Units.shortDuration
easing.type: Easing.OutCubic
}
}
}
// opt-out of whatever spell checker a styled TextArea might come with
Kirigami.SpellCheck.enabled: false
ChatDocumentHandler {
id: documentHandler
document: root.textDocument
cursorPosition: root.cursorPosition
selectionStart: root.selectionStart
selectionEnd: root.selectionEnd
room: root.room // We don't care about saving for edits so this is OK.
mentionColor: Kirigami.Theme.linkColor
errorColor: Kirigami.Theme.negativeTextColor
}
TextMetrics {
id: editTextMetrics
text: root.text
}
function postEdit() {
root.actionsHandler.handleMessageEvent(_private.chatBarCache);
root.clear();
_private.chatBarCache.editId = "";
}
QtObject {
id: _private
property ChatBarCache chatBarCache
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
function updateEditText() {
// This could possibly be undefined due to some esoteric QtQuick issue. Referencing it somewhere in JS is enough.
documentHandler.document;
if (chatBarCache?.isEditing && chatBarCache.relationMessage.length > 0) {
root.text = chatBarCache.relationMessage;
chatBarCache.updateMentions(root.textDocument, documentHandler);
root.forceActiveFocus();
root.cursorPosition = root.length;
}
}
}
}