diff --git a/src/qml/RichLabel.qml b/src/qml/RichLabel.qml index 82902d039..2ac19d5b7 100644 --- a/src/qml/RichLabel.qml +++ b/src/qml/RichLabel.qml @@ -43,13 +43,18 @@ TextEdit { */ property bool spoilerRevealed: !hasSpoiler.test(textMessage) + property bool isDelegate: false + ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage)) persistentSelection: true // Work around QTBUG 93281 - Component.onCompleted: if (text.includes(" @@ -116,4 +121,26 @@ a{ enabled: !parent.hoveredLink && !spoilerRevealed onTapped: spoilerRevealed = true } + + Connections { + target: selectionArea + enabled: root.isDelegate + function onSelectionChanged() { + updateSelection(); + } + } + + function updateSelection() { + if (index < selectionArea.lowerIndex || index > selectionArea.upperIndex) { + root.select(0, 0); + } else if (index > selectionArea.lowerIndex && index < selectionArea.upperIndex) { + root.selectAll(); + } else if (index === selectionArea.selectionStartIndex && index === selectionArea.selectionEndIndex) { + root.select(selectionArea.upperPos, selectionArea.lowerPos); + } else if (index === selectionArea.upperIndex) { + root.select(selectionArea.upperPos, root.length); + } else if (index === selectionArea.lowerIndex) { + root.select(0, selectionArea.lowerPos); + } + } } diff --git a/src/qml/TextDelegate.qml b/src/qml/TextDelegate.qml index c89de9909..fc06f0ddf 100644 --- a/src/qml/TextDelegate.qml +++ b/src/qml/TextDelegate.qml @@ -17,6 +17,14 @@ import org.kde.neochat.config MessageDelegate { id: root + function positionAt(x, y) { + let point = label.mapFromItem(root, x, y) + return label.positionAt(point.x, point.y) + } + + property alias selectedText: label.selectedText + + /** * @brief The link preview properties. * @@ -54,6 +62,7 @@ MessageDelegate { acceptedButtons: Qt.LeftButton onLongPressed: root.openContextMenu() } + isDelegate: true } Loader { Layout.fillWidth: true diff --git a/src/qml/TimelineView.qml b/src/qml/TimelineView.qml index 285ba385c..9432e42af 100644 --- a/src/qml/TimelineView.qml +++ b/src/qml/TimelineView.qml @@ -341,6 +341,66 @@ QQC2.ScrollView { } } + function indexAtRelative(x, y) { + return indexAt(x + contentX, y + contentY) + } + + MouseArea { + id: selectionArea + + anchors.fill: parent + + property int selectionStartIndex + property int selectionEndIndex + property int selectionStartPos + property int selectionEndPos + + property int upperIndex: selectionStartIndex > selectionEndIndex ? selectionStartIndex : selectionEndIndex + property int upperPos: selectionStartIndex > selectionEndIndex ? selectionStartPos : (selectionStartIndex == selectionEndIndex ? (selectionStartPos > selectionEndPos ? selectionEndPos : selectionStartPos) : selectionEndPos) + property int lowerIndex: selectionStartIndex > selectionEndIndex ? selectionEndIndex : selectionStartIndex + property int lowerPos: selectionStartIndex > selectionEndIndex ? selectionEndPos : (selectionStartIndex == selectionEndIndex ? (selectionStartPos > selectionEndPos ? selectionStartPos : selectionEndPos) : selectionStartPos) + + signal selectionChanged + + function indexAndPos(x, y) { + const index = messageListView.indexAtRelative(x, y); + if (index == -1) { + return; + } + const item = messageListView.itemAtIndex(index); + const relItemY = item.y - messageListView.contentY; + const pos = item.positionAt(x, y - relItemY); + return [index, pos] + } + + onPressed: { + [selectionArea.selectionEndIndex, selectionArea.selectionEndPos] = selectionArea.indexAndPos(mouse.x, mouse.y); + [selectionArea.selectionStartIndex, selectionArea.selectionStartPos] = selectionArea.indexAndPos(mouse.x, mouse.y); + selectionChanged(); + } + onPositionChanged: { + if (!pressed) { + return + } + [selectionEndIndex, selectionEndPos] = selectionArea.indexAndPos(mouse.x, mouse.y); + selectionChanged(); + } + } + + Kirigami.Action { + onTriggered: { + var text = "" + for (let i = selectionArea.upperIndex; i >= selectionArea.lowerIndex; i--) { + text += messageListView.itemAtIndex(i).selectedText + if (i > selectionArea.lowerIndex) { + text += " " + } + } + Clipboard.saveText(text) + } + shortcut: "Ctrl+C" + } + function showMaximizedMedia(index) { var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, { initialIndex: index