diff --git a/client/chatroomwidget.cpp b/client/chatroomwidget.cpp index 962f96b8d..55748395b 100644 --- a/client/chatroomwidget.cpp +++ b/client/chatroomwidget.cpp @@ -131,6 +131,11 @@ ChatRoomWidget::ChatRoomWidget(QWidget* parent) m_chatEdit->setAcceptRichText(false); m_chatEdit->setMaximumHeight(maximumChatEditHeight()); connect( m_chatEdit, &KChatEdit::returnPressed, this, &ChatRoomWidget::sendInput ); + connect(m_chatEdit, &KChatEdit::copyRequested, this, [=] { + QApplication::clipboard()->setText( + m_chatEdit->textCursor().hasSelection() ? m_chatEdit->textCursor().selectedText() : selectedText + ); + }); connect(m_chatEdit, &ChatEdit::proposedCompletion, this, [=](const QStringList& matches, int pos) { @@ -725,6 +730,14 @@ void ChatRoomWidget::showMenu(int index, const QString& hoveredLink, menu.exec(QCursor::pos()); } +void ChatRoomWidget::setGlobalSelectionBuffer(QString text) +{ + if (QApplication::clipboard()->supportsSelection()) + QApplication::clipboard()->setText(text, QClipboard::Selection); + + selectedText = text; +} + void ChatRoomWidget::reStartShownTimer() { if (!readMarkerOnScreen || indicesOnScreen.empty() || @@ -812,3 +825,8 @@ void ChatRoomWidget::textDrop(const QString& text) { m_chatEdit->setText(text); } + +Qt::KeyboardModifiers ChatRoomWidget::getModifierKeys() +{ + return QGuiApplication::keyboardModifiers(); +} diff --git a/client/chatroomwidget.h b/client/chatroomwidget.h index 86e0af987..9a4d4c071 100644 --- a/client/chatroomwidget.h +++ b/client/chatroomwidget.h @@ -79,6 +79,8 @@ class ChatRoomWidget: public QWidget void showMenu(int index, const QString& hoveredLink, bool showingDetails); void fileDrop(const QString& url); void textDrop(const QString& text); + void setGlobalSelectionBuffer(QString text); + Qt::KeyboardModifiers getModifierKeys(); private slots: void sendInput(); @@ -110,6 +112,7 @@ class ChatRoomWidget: public QWidget bool readMarkerOnScreen; QMap> roomHistories; QString attachedFileName; + QString selectedText; void reStartShownTimer(); QString doSendInput(); diff --git a/client/kchatedit.cpp b/client/kchatedit.cpp index 3351bf0ab..9a9fb2a34 100644 --- a/client/kchatedit.cpp +++ b/client/kchatedit.cpp @@ -222,6 +222,12 @@ void KChatEdit::keyPressEvent(QKeyEvent *event) d->forwardHistory(); } break; + case Qt::Key_C: + if (event->modifiers() & Qt::ControlModifier) { + emit copyRequested(); + return; + } + break; default: break; } diff --git a/client/kchatedit.h b/client/kchatedit.h index 7ca137d48..48b890996 100644 --- a/client/kchatedit.h +++ b/client/kchatedit.h @@ -109,6 +109,11 @@ class KChatEdit : public QTextEdit */ void returnPressed(); + /** + * Emitted when the user presses Ctrl+C. + */ + void copyRequested(); + protected: void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; diff --git a/client/qml/ActiveLabel.qml b/client/qml/ActiveLabel.qml index 08f754412..117bd0f22 100644 --- a/client/qml/ActiveLabel.qml +++ b/client/qml/ActiveLabel.qml @@ -6,7 +6,7 @@ Label { font.italic: true textFormat: Text.PlainText - MouseArea + TimelineMouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor diff --git a/client/qml/FileContent.qml b/client/qml/FileContent.qml index db00a861d..51f496267 100644 --- a/client/qml/FileContent.qml +++ b/client/qml/FileContent.qml @@ -27,7 +27,7 @@ Attachment { textFormat: TextEdit.PlainText wrapMode: Text.Wrap; - MouseArea { + TimelineMouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton hoverEnabled: true @@ -37,7 +37,7 @@ Attachment { ? room.fileSource(eventId) : "") } - MouseArea { + TimelineMouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton cursorShape: Qt.IBeamCursor diff --git a/client/qml/ImageContent.qml b/client/qml/ImageContent.qml index 6c6d9185d..33fcb2018 100644 --- a/client/qml/ImageContent.qml +++ b/client/qml/ImageContent.qml @@ -25,7 +25,7 @@ Attachment { // easing.type: Easing.OutQuad // }} - MouseArea { + TimelineMouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true @@ -36,7 +36,7 @@ Attachment { onClicked: openExternally() } - MouseArea { + TimelineMouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton cursorShape: Qt.PointingHandCursor diff --git a/client/qml/Timeline.qml b/client/qml/Timeline.qml index 6303f877d..13b48e695 100644 --- a/client/qml/Timeline.qml +++ b/client/qml/Timeline.qml @@ -204,6 +204,13 @@ Rectangle { } } + // This covers the area above a short chatView. + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onReleased: controller.focusInput() + } + ListView { id: chatView @@ -234,6 +241,8 @@ Rectangle { contentHeight > 0 && count > 0 ? count / contentHeight : 0.03 // 0.03 is just an arbitrary reasonable number + property var textEditWithSelection + function ensurePreviousContent() { if (noNeedMoreContent) return diff --git a/client/qml/TimelineItem.qml b/client/qml/TimelineItem.qml index a96da2be6..275acfb99 100644 --- a/client/qml/TimelineItem.qml +++ b/client/qml/TimelineItem.qml @@ -131,6 +131,7 @@ Item { } TimelineMouseArea { anchors.fill: parent + acceptedButtons: Qt.AllButtons } } Loader { @@ -145,6 +146,7 @@ Item { TimelineMouseArea { anchors.fill: parent + acceptedButtons: Qt.AllButtons } } @@ -155,6 +157,7 @@ Item { TimelineMouseArea { anchors.fill: parent + acceptedButtons: Qt.AllButtons } // There are several layout styles (av - author avatar, @@ -203,7 +206,7 @@ Item { text: (actionEvent ? "* " : "") + authorName } - MouseArea { + TimelineMouseArea { anchors.left: authorAvatar.left anchors.right: authorLabel.right anchors.top: authorLabel.top @@ -328,8 +331,11 @@ Item { else Qt.openUrlExternally(link) } + + TimelineTextEditSelector {} } - MouseArea { + + TimelineMouseArea { anchors.fill: parent acceptedButtons: Qt.MiddleButton @@ -340,14 +346,14 @@ Item { } } - MouseArea { + TimelineMouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: controller.showMenu(index, textFieldImpl.hoveredLink, showingDetails) } - MouseArea { + TimelineMouseArea { anchors.fill: parent cursorShape: textFieldImpl.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor @@ -492,6 +498,8 @@ Item { anchors.left: parent.left anchors.leftMargin: 3 z: 1 + + TimelineTextEditSelector {} } TextEdit { text: ""+ eventId @@ -507,7 +515,9 @@ Item { onLinkActivated: Qt.openUrlExternally(link) - MouseArea { + TimelineTextEditSelector {} + + TimelineMouseArea { anchors.fill: parent cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : @@ -536,6 +546,8 @@ Item { width: parent.width anchors.top: detailsHeader.bottom + + TimelineTextEditSelector {} } } } diff --git a/client/qml/TimelineMouseArea.qml b/client/qml/TimelineMouseArea.qml index 522d80962..fbbfbaf27 100644 --- a/client/qml/TimelineMouseArea.qml +++ b/client/qml/TimelineMouseArea.qml @@ -2,4 +2,5 @@ import QtQuick 2.2 MouseArea { onWheel: chatView.onWheel(wheel) + onReleased: controller.focusInput() } diff --git a/client/qml/TimelineTextEditSelector.qml b/client/qml/TimelineTextEditSelector.qml new file mode 100644 index 000000000..84e92043b --- /dev/null +++ b/client/qml/TimelineTextEditSelector.qml @@ -0,0 +1,50 @@ +import QtQuick 2.2 + +/* + * Unfortunately, TextEdit captures LeftButton events for text selection in a way which + * is not compatible with our focus-cancelling mechanism, so we took over the task here. + */ +MouseArea { + property var textEdit: parent + property var selectionMode: TextEdit.SelectCharacters + + anchors.fill: parent + acceptedButtons: Qt.LeftButton + + onPressed: { + var x = mouse.x + var y = mouse.y + if (textEdit.flickableItem) { + x += textEdit.flickableItem.contentX + y += textEdit.flickableItem.contentY + } + var hasSelection = textEdit.selectionEnd > textEdit.selectionStart + if (hasSelection && controller.getModifierKeys() & Qt.ShiftModifier) { + textEdit.moveCursorSelection(textEdit.positionAt(x, y), selectionMode) + } else { + textEdit.cursorPosition = textEdit.positionAt(x, y) + if (chatView.textEditWithSelection) + chatView.textEditWithSelection.deselect() + } + } + onDoubleClicked: { + selectionMode = TextEdit.SelectWords + textEdit.selectWord() + } + onReleased: { + selectionMode = TextEdit.SelectCharacters + controller.setGlobalSelectionBuffer(textEdit.selectedText) + chatView.textEditWithSelection = textEdit + + controller.focusInput() + } + onPositionChanged: { + var x = mouse.x + var y = mouse.y + if (textEdit.flickableItem) { + x += textEdit.flickableItem.contentX + y += textEdit.flickableItem.contentY + } + textEdit.moveCursorSelection(textEdit.positionAt(x, y), selectionMode) + } +} diff --git a/client/resources.qrc b/client/resources.qrc index 00b83bfec..980632465 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -15,5 +15,6 @@ qml/TimelineItem.qml qml/ActiveLabel.qml qml/TimelineMouseArea.qml + qml/TimelineTextEditSelector.qml