Browse Source

Keep desktop reactions and timestamp inside the bubble bottom row

master
evgeny 6 days ago
parent
commit
3dbbe02f69
  1. 62
      tools/chatgui/src/messagedelegate.cpp
  2. 2
      tools/chatgui/src/messagedelegate.h

62
tools/chatgui/src/messagedelegate.cpp

@ -154,12 +154,13 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
&& (mouse->pos() - m_reactionPressPosition).manhattanLength() < 5; && (mouse->pos() - m_reactionPressPosition).manhattanLength() < 5;
m_reactionPress = QPersistentModelIndex(); m_pressedReaction = -2; m_reactionPress = QPersistentModelIndex(); m_pressedReaction = -2;
if (activateReaction) { emit reactionRequested(index, reaction); return true; } 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) { if (activateAvatar) {
emit avatarRequested(index.data(MsgChannelIdRole).toString(), index.data(MsgAuthorNodeIdRole).toULongLong()); emit avatarRequested(index.data(MsgChannelIdRole).toString(), index.data(MsgAuthorNodeIdRole).toULongLong());
return true; return true;
} }
if (onAvatar) return true; if (onAvatar) return true;
if (L.reactionHeart.isValid() && point.y() >= L.reactionHeart.top()) return true;
if (L.isVideo) { if (L.isVideo) {
int dlState = index.data(MsgFileDownloadStateRole).toInt(); int dlState = index.data(MsgFileDownloadStateRole).toInt();
@ -409,20 +410,34 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
if (layout.emojiOnly || channel.isEmpty() || channel.startsWith("dm:") if (layout.emojiOnly || channel.isEmpty() || channel.startsWith("dm:")
|| index.data(MsgMessageKeyRole).toString().size() != 64 || layout.bubbleRect.isEmpty()) return layout; || index.data(MsgMessageKeyRole).toString().size() != 64 || layout.bubbleRect.isEmpty()) return layout;
QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 1)); QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 1));
const int height = qMax(28, QFontMetrics(font).height() + 8), left = layout.bubbleRect.left(); QFont timeFont = option.font; timeFont.setPointSize(qMax(1, timeFont.pointSize() - 2));
int y = qMax(layout.bubbleRect.bottom(), qMax(layout.statusRect.bottom(), layout.deliveryRect.bottom())) + 4; const int height = qMax(24, QFontMetrics(font).height() + 6), left = layout.bubbleRect.left() + kPadH;
int x = left + height + 4; layout.reactionTimeText = index.data(MsgEditedRole).toBool() ? QCoreApplication::translate("ChatGui", "edited ") : QString();
layout.reactionHeart = QRect(left, y, height, height); 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()) { for (const auto& value : index.data(MsgReactionsRole).toList()) {
const auto reaction = value.toMap(); const auto reaction = value.toMap(); font.setBold(reaction.value("mine").toBool());
font.setBold(reaction.value("mine").toBool()); const int width = qMin(reactionsRight - left + 1, 30 + QFontMetrics(font).horizontalAdvance(reaction.value("count").toString()));
const int width = qMin(layout.bubbleRect.width(), 32 + QFontMetrics(font).horizontalAdvance(reaction.value("count").toString())); if (x + width - 1 > reactionsRight) { x = left; y += height + 4; }
if (x + width > left + layout.bubbleRect.width()) { x = left; y += height + 4; }
layout.reactionChips.append({QRect(x, y, width, height), reaction.value("id").toInt(), layout.reactionChips.append({QRect(x, y, width, height), reaction.value("id").toInt(),
reaction.value("emoji").toString(), reaction.value("count").toInt(), reaction.value("mine").toBool()}); reaction.value("emoji").toString(), reaction.value("count").toInt(), reaction.value("mine").toBool()});
x += width + 4; 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; return layout;
} }
@ -1127,8 +1142,8 @@ void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& o
QFont tsFont = option.font; QFont tsFont = option.font;
tsFont.setPointSize(tsFont.pointSize() - 1); tsFont.setPointSize(tsFont.pointSize() - 1);
painter->setFont(tsFont); painter->setFont(tsFont);
painter->drawText(L.voiceTimeRect, Qt::AlignRight | Qt::AlignVCenter, if (!L.reactionTime.isValid()) painter->drawText(L.voiceTimeRect, Qt::AlignRight | Qt::AlignVCenter,
index.data(MsgTimeRole).toString()); index.data(MsgTimeRole).toString());
/* media bar (left edge, 3px) */ /* 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); { 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()) if (!info.isEmpty())
painter->drawText(L.statusRect, Qt::AlignLeft | Qt::AlignVCenter, info.join(" \xC2\xB7 ")); painter->drawText(L.statusRect, Qt::AlignLeft | Qt::AlignVCenter, info.join(" \xC2\xB7 "));
painter->drawText(L.statusRect, Qt::AlignRight | Qt::AlignVCenter, if (!L.reactionTime.isValid()) painter->drawText(L.statusRect, Qt::AlignRight | Qt::AlignVCenter,
index.data(MsgTimeRole).toString()); index.data(MsgTimeRole).toString());
} }
painter->restore(); 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 { void MessageDelegate::drawReactions(QPainter* p, const Layout& layout, const QStyleOptionViewItem& option) const {
p->save(); p->setRenderHint(QPainter::Antialiasing); 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; QPainterPath heart;
heart.moveTo(origin + QPointF(12, 21)); heart.moveTo(origin + QPointF(12, 21));
heart.cubicTo(origin + QPointF(10.5, 19.5), origin + QPointF(3, 14.2), origin + QPointF(3, 8.5)); 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(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.cubicTo(origin + QPointF(21, 14.2), origin + QPointF(13.5, 19.5), origin + QPointF(12, 21));
heart.closeSubpath(); 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->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->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) { 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->setPen(Qt::NoPen); p->setBrush(chip.mine ? QColor(51, 144, 236, 100) : QColor(0, 0, 0, 40));
p->drawRoundedRect(chip.rect, 9, 9); 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; 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); 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); } 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()); 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(); p->restore();
} }
void MessageDelegate::drawStatusBar(QPainter* p, const Layout& layout, const QModelIndex& index, void MessageDelegate::drawStatusBar(QPainter* p, const Layout& layout, const QModelIndex& index,
const QStyleOptionViewItem& option) const { const QStyleOptionViewItem& option) const {
if (layout.reactionTime.isValid()) return;
p->save(); p->save();
QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 2)); p->setFont(font); QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 2)); p->setFont(font);
p->setPen(QColor(128, 128, 128)); p->setPen(QColor(128, 128, 128));

2
tools/chatgui/src/messagedelegate.h

@ -116,6 +116,8 @@ private:
QRect imageThumbRect; QRect imageThumbRect;
QRect imageBadgeRect; QRect imageBadgeRect;
QRect reactionHeart; QRect reactionHeart;
QRect reactionTime;
QString reactionTimeText;
QVector<ReactionChip> reactionChips; QVector<ReactionChip> reactionChips;
}; };

Loading…
Cancel
Save