|
|
|
|
@ -154,12 +154,13 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
|
|
|
|
&& (mouse->pos() - m_reactionPressPosition).manhattanLength() < 5; |
|
|
|
|
m_reactionPress = QPersistentModelIndex(); m_pressedReaction = -2; |
|
|
|
|
if (activateReaction) { emit reactionRequested(index, reaction); return true; } |
|
|
|
|
if (hadReactionPress || onReaction || (L.reactionHeart.isValid() && point.y() >= L.reactionHeart.top())) return true; |
|
|
|
|
if (hadReactionPress || onReaction) return true; |
|
|
|
|
if (activateAvatar) { |
|
|
|
|
emit avatarRequested(index.data(MsgChannelIdRole).toString(), index.data(MsgAuthorNodeIdRole).toULongLong()); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (onAvatar) return true; |
|
|
|
|
if (L.reactionHeart.isValid() && point.y() >= L.reactionHeart.top()) return true; |
|
|
|
|
|
|
|
|
|
if (L.isVideo) { |
|
|
|
|
int dlState = index.data(MsgFileDownloadStateRole).toInt(); |
|
|
|
|
@ -409,20 +410,34 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
|
|
|
|
|
if (layout.emojiOnly || channel.isEmpty() || channel.startsWith("dm:") |
|
|
|
|
|| index.data(MsgMessageKeyRole).toString().size() != 64 || layout.bubbleRect.isEmpty()) return layout; |
|
|
|
|
QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 1)); |
|
|
|
|
const int height = qMax(28, QFontMetrics(font).height() + 8), left = layout.bubbleRect.left(); |
|
|
|
|
int y = qMax(layout.bubbleRect.bottom(), qMax(layout.statusRect.bottom(), layout.deliveryRect.bottom())) + 4; |
|
|
|
|
int x = left + height + 4; |
|
|
|
|
layout.reactionHeart = QRect(left, y, height, height); |
|
|
|
|
QFont timeFont = option.font; timeFont.setPointSize(qMax(1, timeFont.pointSize() - 2)); |
|
|
|
|
const int height = qMax(24, QFontMetrics(font).height() + 6), left = layout.bubbleRect.left() + kPadH; |
|
|
|
|
layout.reactionTimeText = index.data(MsgEditedRole).toBool() ? QCoreApplication::translate("ChatGui", "edited ") : QString(); |
|
|
|
|
layout.reactionTimeText += index.data(MsgTimeRole).toString(); |
|
|
|
|
const int innerWidth = layout.bubbleRect.width() - 2 * kPadH; |
|
|
|
|
const int timeWidth = qMin(QFontMetrics(timeFont).horizontalAdvance(layout.reactionTimeText) + 2, qMax(0, innerWidth - height - 6)); |
|
|
|
|
const int reactionsRight = layout.bubbleRect.right() - kPadH - timeWidth - 6; |
|
|
|
|
int top; |
|
|
|
|
if (layout.isVoice) top = layout.voiceDurationRect.bottom() + 4; |
|
|
|
|
else if (layout.isVideo) top = layout.statusRect.bottom() + 4; |
|
|
|
|
else if (layout.isImage) top = layout.bubbleRect.bottom() + 3; |
|
|
|
|
else if (layout.isFile) top = qMax(layout.statusRect.bottom() + 4, layout.bubbleRect.bottom() - kPadBot); |
|
|
|
|
else top = layout.statusRect.top(); |
|
|
|
|
if (layout.deliveryRect.isValid()) top = qMax(top, layout.deliveryRect.bottom() + 4); |
|
|
|
|
int y = top, x = left + height + 4; |
|
|
|
|
layout.reactionHeart = QRect(left, top, height, height); |
|
|
|
|
for (const auto& value : index.data(MsgReactionsRole).toList()) { |
|
|
|
|
const auto reaction = value.toMap(); |
|
|
|
|
font.setBold(reaction.value("mine").toBool()); |
|
|
|
|
const int width = qMin(layout.bubbleRect.width(), 32 + QFontMetrics(font).horizontalAdvance(reaction.value("count").toString())); |
|
|
|
|
if (x + width > left + layout.bubbleRect.width()) { x = left; y += height + 4; } |
|
|
|
|
const auto reaction = value.toMap(); font.setBold(reaction.value("mine").toBool()); |
|
|
|
|
const int width = qMin(reactionsRight - left + 1, 30 + QFontMetrics(font).horizontalAdvance(reaction.value("count").toString())); |
|
|
|
|
if (x + width - 1 > reactionsRight) { x = left; y += height + 4; } |
|
|
|
|
layout.reactionChips.append({QRect(x, y, width, height), reaction.value("id").toInt(), |
|
|
|
|
reaction.value("emoji").toString(), reaction.value("count").toInt(), reaction.value("mine").toBool()}); |
|
|
|
|
x += width + 4; |
|
|
|
|
} |
|
|
|
|
layout.totalHeight = qMax(layout.totalHeight, y + height + kItemGap); |
|
|
|
|
layout.reactionTime = QRect(layout.bubbleRect.right() - kPadH - timeWidth + 1, y, timeWidth, height); |
|
|
|
|
layout.bubbleRect.setBottom(y + height + kPadBot - 1); |
|
|
|
|
layout.totalHeight = qMax(layout.bubbleRect.height(), kAvatar) + kItemGap; |
|
|
|
|
if (layout.avatarRect.isValid()) layout.avatarRect.moveTop(qMax(0, layout.bubbleRect.bottom() + 1 - kAvatar)); |
|
|
|
|
return layout; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -1127,8 +1142,8 @@ void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& o
|
|
|
|
|
QFont tsFont = option.font; |
|
|
|
|
tsFont.setPointSize(tsFont.pointSize() - 1); |
|
|
|
|
painter->setFont(tsFont); |
|
|
|
|
painter->drawText(L.voiceTimeRect, Qt::AlignRight | Qt::AlignVCenter, |
|
|
|
|
index.data(MsgTimeRole).toString()); |
|
|
|
|
if (!L.reactionTime.isValid()) painter->drawText(L.voiceTimeRect, Qt::AlignRight | Qt::AlignVCenter, |
|
|
|
|
index.data(MsgTimeRole).toString()); |
|
|
|
|
|
|
|
|
|
/* media bar (left edge, 3px) */ |
|
|
|
|
{ QColor barC = L.mediaBarColor == 2 ? QColor(0x40, 0xC0, 0x40) : L.mediaBarColor == 1 ? QColor(0xF0, 0xC0, 0x40) : QColor(0x80, 0x80, 0x80); |
|
|
|
|
@ -1250,8 +1265,8 @@ void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& o
|
|
|
|
|
|
|
|
|
|
if (!info.isEmpty()) |
|
|
|
|
painter->drawText(L.statusRect, Qt::AlignLeft | Qt::AlignVCenter, info.join(" \xC2\xB7 ")); |
|
|
|
|
painter->drawText(L.statusRect, Qt::AlignRight | Qt::AlignVCenter, |
|
|
|
|
index.data(MsgTimeRole).toString()); |
|
|
|
|
if (!L.reactionTime.isValid()) painter->drawText(L.statusRect, Qt::AlignRight | Qt::AlignVCenter, |
|
|
|
|
index.data(MsgTimeRole).toString()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
painter->restore(); |
|
|
|
|
@ -1581,7 +1596,7 @@ void MessageDelegate::drawBubble(QPainter *p, const Layout &L, const QStyleOptio
|
|
|
|
|
|
|
|
|
|
void MessageDelegate::drawReactions(QPainter* p, const Layout& layout, const QStyleOptionViewItem& option) const { |
|
|
|
|
p->save(); p->setRenderHint(QPainter::Antialiasing); |
|
|
|
|
const QPointF origin = QRectF(layout.reactionHeart).center() - QPointF(12, 12); |
|
|
|
|
const QPointF origin = QRectF(layout.reactionHeart).center() - QPointF(10, 10); |
|
|
|
|
QPainterPath heart; |
|
|
|
|
heart.moveTo(origin + QPointF(12, 21)); |
|
|
|
|
heart.cubicTo(origin + QPointF(10.5, 19.5), origin + QPointF(3, 14.2), origin + QPointF(3, 8.5)); |
|
|
|
|
@ -1589,26 +1604,33 @@ void MessageDelegate::drawReactions(QPainter* p, const Layout& layout, const QSt
|
|
|
|
|
heart.cubicTo(origin + QPointF(15, 2), origin + QPointF(21, 3), origin + QPointF(21, 8.5)); |
|
|
|
|
heart.cubicTo(origin + QPointF(21, 14.2), origin + QPointF(13.5, 19.5), origin + QPointF(12, 21)); |
|
|
|
|
heart.closeSubpath(); |
|
|
|
|
QTransform shrink; shrink.translate(origin.x(), origin.y()); shrink.scale(20.0 / 24, 20.0 / 24); |
|
|
|
|
shrink.translate(-origin.x(), -origin.y()); heart = shrink.map(heart); |
|
|
|
|
p->setBrush(Qt::NoBrush); |
|
|
|
|
p->setPen(QPen(QColor(0, 0, 0, 153), 1.8 + 2.0 / p->device()->devicePixelRatioF(), Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); |
|
|
|
|
p->setPen(QPen(QColor(0, 0, 0, 153), 1.5 + 2.0 / p->device()->devicePixelRatioF(), Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); |
|
|
|
|
p->drawPath(heart); |
|
|
|
|
p->setPen(QPen(Qt::white, 1.8, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p->drawPath(heart); |
|
|
|
|
p->setPen(QPen(Qt::white, 1.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); p->drawPath(heart); |
|
|
|
|
for (const auto& chip : layout.reactionChips) { |
|
|
|
|
p->setPen(Qt::NoPen); p->setBrush(chip.mine ? QColor(51, 144, 236, 100) : QColor(0, 0, 0, 40)); |
|
|
|
|
p->drawRoundedRect(chip.rect, 9, 9); |
|
|
|
|
const QRect iconRect(chip.rect.left() + 5, chip.rect.top() + (chip.rect.height() - 20) / 2, 20, 20); |
|
|
|
|
const QRect iconRect(chip.rect.left() + 5, chip.rect.top() + (chip.rect.height() - 18) / 2, 18, 18); |
|
|
|
|
LottieIcon* icon = g_animatedEmojiMap.contains(chip.emoji) ? AnimTimer::instance()->icon(g_animatedEmojiMap[chip.emoji]) : nullptr; |
|
|
|
|
QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 1)); p->setFont(font); |
|
|
|
|
if (icon && icon->isValid()) p->drawImage(iconRect, icon->renderFrame(icon->currentFrame(), QSize(20, 20))); |
|
|
|
|
if (icon && icon->isValid()) p->drawImage(iconRect, icon->renderFrame(icon->currentFrame(), QSize(18, 18))); |
|
|
|
|
else { p->setPen(option.palette.text().color()); p->drawText(iconRect, Qt::AlignCenter, chip.emoji); } |
|
|
|
|
font.setBold(chip.mine); p->setFont(font); p->setPen(option.palette.text().color()); |
|
|
|
|
p->drawText(chip.rect.adjusted(27, 0, -5, 0), Qt::AlignLeft | Qt::AlignVCenter, QString::number(chip.count)); |
|
|
|
|
p->drawText(chip.rect.adjusted(25, 0, -5, 0), Qt::AlignLeft | Qt::AlignVCenter, QString::number(chip.count)); |
|
|
|
|
} |
|
|
|
|
QFont timeFont = option.font; timeFont.setPointSize(qMax(1, timeFont.pointSize() - 2)); p->setFont(timeFont); |
|
|
|
|
p->setPen(QColor(128, 128, 128)); |
|
|
|
|
p->drawText(layout.reactionTime, Qt::AlignRight | Qt::AlignVCenter, |
|
|
|
|
QFontMetrics(timeFont).elidedText(layout.reactionTimeText, Qt::ElideLeft, layout.reactionTime.width())); |
|
|
|
|
p->restore(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MessageDelegate::drawStatusBar(QPainter* p, const Layout& layout, const QModelIndex& index, |
|
|
|
|
const QStyleOptionViewItem& option) const { |
|
|
|
|
if (layout.reactionTime.isValid()) return; |
|
|
|
|
p->save(); |
|
|
|
|
QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 2)); p->setFont(font); |
|
|
|
|
p->setPen(QColor(128, 128, 128)); |
|
|
|
|
|