diff --git a/tools/chatgui/src/messagedelegate.cpp b/tools/chatgui/src/messagedelegate.cpp index fb146008..789c097f 100644 --- a/tools/chatgui/src/messagedelegate.cpp +++ b/tools/chatgui/src/messagedelegate.cpp @@ -823,6 +823,9 @@ static void drawAnimatedEmojiOverlay(QPainter *p, const QFont &font, if (!para.isEmpty()) { QTextLayout layout(para, font); + if (text.contains(QStringLiteral("utun://"))) { + QTextOption wrap; wrap.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); layout.setTextOption(wrap); + } layout.beginLayout(); qreal localY = 0; while (true) { @@ -839,7 +842,7 @@ static void drawAnimatedEmojiOverlay(QPainter *p, const QFont &font, const QString &unicode = it.key(); int pos = text.indexOf(unicode, ls); while (pos >= 0 && pos + unicode.length() <= le) { - int linePos = pos - globalChar - line.textStart(); + int linePos = pos - globalChar; int cp1 = linePos, cp2 = linePos + unicode.length(); qreal x1 = line.cursorToX(&cp1); qreal x2 = line.cursorToX(&cp2); @@ -1368,14 +1371,34 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio painter->setFont(textFont); QString msgText = index.data(MsgTextRole).toString(); - if (msgText.contains(QStringLiteral("utun://"))) { + const bool hasInvite = msgText.contains(QStringLiteral("utun://")); + if (hasInvite) { QString display = msgText; display.replace('\n', QChar(0x2028)); QTextLayout layout(display, textFont); layoutInviteText(layout, L.textRect.width()); - layout.draw(painter, L.textRect.topLeft()); + QVector selections; + if (singleTextSel && cv) { + auto cursorAt = [&](const QPoint& point) { + for (int i = 0; i < layout.lineCount(); ++i) { + const auto line = layout.lineAt(i); + if (point.y() < line.y() + line.height()) return line.xToCursor(point.x()); + } + return msgText.length(); + }; + const auto& selection = cv->textSelection(); + int start = cursorAt(selection.clickPos - L.textRect.topLeft()); + int end = cursorAt(selection.dragPos - L.textRect.topLeft()); + if (start > end) qSwap(start, end); + if (start == end && end < msgText.length()) ++end; + const_cast(cv)->setTextSelChars(start, end); + QTextLayout::FormatRange range; + range.start = start; range.length = end - start; range.format.setBackground(QColor(0x33, 0x90, 0xEC, 60)); + selections.append(range); + } + layout.draw(painter, L.textRect.topLeft(), selections); } else painter->drawText(L.textRect, Qt::AlignLeft | Qt::TextWordWrap, msgText); - if (singleTextSel && cv) { + if (singleTextSel && cv && !hasInvite) { const auto &ts = cv->textSelection(); QPoint localP1(ts.clickPos.x() - L.textRect.left(), ts.clickPos.y() - L.textRect.top()); diff --git a/tools/chatgui/tests/test_invite_links.cpp b/tools/chatgui/tests/test_invite_links.cpp index 89231357..441def6c 100644 --- a/tools/chatgui/tests/test_invite_links.cpp +++ b/tools/chatgui/tests/test_invite_links.cpp @@ -53,6 +53,20 @@ private slots: QCOMPARE(clicked.count(), 1); QCOMPARE(clicked.last().first().toString(), first); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, secondPoint); QCOMPARE(clicked.count(), 2); QCOMPARE(clicked.last().first().toString(), second); + /* Выделение использует те же перенесённые строки, что и отрисовка ссылки. */ + QPoint dragEnd = firstPoint + QPoint(20, 0); + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, firstPoint); + QMouseEvent drag(QEvent::MouseMove, dragEnd, view.viewport()->mapToGlobal(dragEnd), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(view.viewport(), &drag); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, dragEnd); + QTest::qWait(20); + QCOMPARE(clicked.count(), 2); + const auto selection = view.textSelection(); + QVERIFY(selection.startChar >= text.indexOf(first)); + QVERIFY(selection.endChar <= text.indexOf(first) + first.length()); + QVERIFY(selection.endChar > selection.startChar); + QVERIFY(selection.endChar - selection.startChar < 20); } void memberPlusTargetsOtherMembersOnly() {