|
|
|
|
@ -1,4 +1,5 @@
|
|
|
|
|
#include "messagedelegate.h" |
|
|
|
|
#include "message_links.h" |
|
|
|
|
#include "chatview.h" |
|
|
|
|
#include "debug_ui.h" |
|
|
|
|
#include "emoji.h" |
|
|
|
|
@ -24,10 +25,9 @@
|
|
|
|
|
#include <QDebug> |
|
|
|
|
#include <QImageReader> |
|
|
|
|
#include <QPersistentModelIndex> |
|
|
|
|
#include <QRegularExpression> |
|
|
|
|
|
|
|
|
|
/* Один layout для размеров, отрисовки и hit-test длинных invite-ссылок. */ |
|
|
|
|
static qreal layoutInviteText(QTextLayout& layout, int width, const QString& compactLink = {}) { |
|
|
|
|
/* Один layout для размеров, отрисовки, выделения и hit-test ссылок. */ |
|
|
|
|
static qreal layoutMessageText(QTextLayout& layout, int width, const QString& compactLink = {}) { |
|
|
|
|
int linkStart = -1; |
|
|
|
|
if (!compactLink.isEmpty() && layout.text().endsWith(compactLink)) { |
|
|
|
|
linkStart = layout.text().length() - compactLink.length(); |
|
|
|
|
@ -36,16 +36,14 @@ static qreal layoutInviteText(QTextLayout& layout, int width, const QString& com
|
|
|
|
|
} |
|
|
|
|
QTextOption option; option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); |
|
|
|
|
layout.setTextOption(option); |
|
|
|
|
static const QRegularExpression pattern(QStringLiteral("utun://[A-Za-z0-9+/]+={0,2}")); |
|
|
|
|
QVector<QTextLayout::FormatRange> formats; |
|
|
|
|
auto matches = pattern.globalMatch(layout.text()); |
|
|
|
|
while (linkStart >= 0 || matches.hasNext()) { |
|
|
|
|
const auto links = linkStart >= 0 ? QVector<MessageLink>{{linkStart, (int)layout.text().length() - linkStart, compactLink}} |
|
|
|
|
: messageLinks(layout.text()); |
|
|
|
|
for (const auto& link : links) { |
|
|
|
|
QTextLayout::FormatRange range; |
|
|
|
|
if (linkStart >= 0) { range.start = linkStart; range.length = layout.text().length() - linkStart; } |
|
|
|
|
else { auto match = matches.next(); range.start = match.capturedStart(); range.length = match.capturedLength(); } |
|
|
|
|
range.start = link.start; range.length = link.length; |
|
|
|
|
range.format.setForeground(QColor(0x33, 0x90, 0xEC)); range.format.setFontUnderline(true); |
|
|
|
|
formats.append(range); |
|
|
|
|
if (linkStart >= 0) break; |
|
|
|
|
} |
|
|
|
|
layout.setFormats(formats); |
|
|
|
|
layout.beginLayout(); |
|
|
|
|
@ -59,9 +57,9 @@ static qreal layoutInviteText(QTextLayout& layout, int width, const QString& com
|
|
|
|
|
return y; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QString MessageDelegate::inviteLinkAt(const QStyleOptionViewItem& option, const QModelIndex& index, const QPoint& point) const { |
|
|
|
|
QString MessageDelegate::linkAt(const QStyleOptionViewItem& option, const QModelIndex& index, const QPoint& point) const { |
|
|
|
|
const QString text = index.data(MsgTextRole).toString(); |
|
|
|
|
if (!text.contains(QStringLiteral("utun://"))) return {}; |
|
|
|
|
if (messageLinks(text).isEmpty()) return {}; |
|
|
|
|
const Layout geometry = calcLayout(option, index); |
|
|
|
|
if (geometry.isVoice || geometry.isFile || geometry.isVideo || geometry.isImage || geometry.emojiOnly) return {}; |
|
|
|
|
const QPoint local = point - option.rect.topLeft() - geometry.textRect.topLeft(); |
|
|
|
|
@ -69,7 +67,7 @@ QString MessageDelegate::inviteLinkAt(const QStyleOptionViewItem& option, const
|
|
|
|
|
QString display = text; display.replace('\n', QChar(0x2028)); |
|
|
|
|
QTextLayout layout(display, option.font); |
|
|
|
|
const QString compactLink = index.data(MsgInviteLinkRole).toString(); |
|
|
|
|
layoutInviteText(layout, geometry.textRect.width(), compactLink); |
|
|
|
|
layoutMessageText(layout, geometry.textRect.width(), compactLink); |
|
|
|
|
for (int i = 0; i < layout.lineCount(); ++i) { |
|
|
|
|
const auto line = layout.lineAt(i); |
|
|
|
|
if (local.y() < line.y() || local.y() >= line.y() + line.height()) continue; |
|
|
|
|
@ -325,10 +323,10 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem &option,
|
|
|
|
|
|
|
|
|
|
static int measureTextHeight(const QFont &font, const QString &text, int width, const QString& compactLink = {}) { |
|
|
|
|
if (text.isEmpty() || width <= 0) return 0; |
|
|
|
|
if (text.contains(QStringLiteral("utun://"))) { |
|
|
|
|
if (!messageLinks(text).isEmpty()) { |
|
|
|
|
QString display = text; display.replace('\n', QChar(0x2028)); |
|
|
|
|
QTextLayout layout(display, font); |
|
|
|
|
return (int)std::ceil(layoutInviteText(layout, width, compactLink)); |
|
|
|
|
return (int)std::ceil(layoutMessageText(layout, width, compactLink)); |
|
|
|
|
} |
|
|
|
|
QRect r = QFontMetrics(font).boundingRect(QRect(0, 0, width, 100000), |
|
|
|
|
Qt::AlignLeft | Qt::TextWordWrap, text); |
|
|
|
|
@ -832,7 +830,7 @@ static void drawAnimatedEmojiOverlay(QPainter *p, const QFont &font,
|
|
|
|
|
|
|
|
|
|
if (!para.isEmpty()) { |
|
|
|
|
QTextLayout layout(para, font); |
|
|
|
|
if (text.contains(QStringLiteral("utun://"))) { |
|
|
|
|
if (!messageLinks(text).isEmpty()) { |
|
|
|
|
QTextOption wrap; wrap.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); layout.setTextOption(wrap); |
|
|
|
|
} |
|
|
|
|
layout.beginLayout(); |
|
|
|
|
@ -1381,12 +1379,12 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
|
|
|
|
|
|
|
|
|
QString msgText = index.data(MsgTextRole).toString(); |
|
|
|
|
QString overlayText = msgText; |
|
|
|
|
const bool hasInvite = msgText.contains(QStringLiteral("utun://")); |
|
|
|
|
if (hasInvite) { |
|
|
|
|
const bool hasLinks = !messageLinks(msgText).isEmpty(); |
|
|
|
|
if (hasLinks) { |
|
|
|
|
QString display = msgText; display.replace('\n', QChar(0x2028)); |
|
|
|
|
QTextLayout layout(display, textFont); |
|
|
|
|
const QString compactLink = index.data(MsgInviteLinkRole).toString(); |
|
|
|
|
layoutInviteText(layout, L.textRect.width(), compactLink); |
|
|
|
|
layoutMessageText(layout, L.textRect.width(), compactLink); |
|
|
|
|
overlayText = layout.text(); overlayText.replace(QChar(0x2028), '\n'); |
|
|
|
|
QVector<QTextLayout::FormatRange> selections; |
|
|
|
|
if (singleTextSel && cv) { |
|
|
|
|
@ -1418,7 +1416,7 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
|
|
|
|
layout.draw(painter, L.textRect.topLeft(), selections); |
|
|
|
|
} else painter->drawText(L.textRect, Qt::AlignLeft | Qt::TextWordWrap, msgText); |
|
|
|
|
|
|
|
|
|
if (singleTextSel && cv && !hasInvite) { |
|
|
|
|
if (singleTextSel && cv && !hasLinks) { |
|
|
|
|
const auto &ts = cv->textSelection(); |
|
|
|
|
QPoint localP1(ts.clickPos.x() - L.textRect.left(), |
|
|
|
|
ts.clickPos.y() - L.textRect.top()); |
|
|
|
|
|