chatgui: MsgIsOutgoingRole support for outgoing message alignment

This commit is contained in:
Evgeny committed 2026-07-24 15:55:34 +03:00
1 parent fbc1bff1e1
commit e8920fe00e
5 files changed
+198 -33

No files matched your search

+148 -33
View File
@@ -65,6 +65,33 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
bool hasQuote = index.data(MsgHasQuoteRole).toBool();
QString text = index.data(MsgTextRole).toString();
L.isOutgoing = index.data(MsgIsOutgoingRole).toBool();
L.emojiCount = detectEmojiOnly(text);
L.emojiOnly = (L.emojiCount >= 1);
if (L.emojiOnly) {
QFontMetrics fm(option.font);
int lineH = fm.lineSpacing();
int availW = L.viewWidth - kMarginH - kAvatar - kGap - kMarginH;
bool narrow = (availW < 400);
int contentEdge = !L.isOutgoing
? (narrow ? kMarginH : (kMarginH + kAvatar + kGap))
: L.viewWidth - (narrow ? kMarginH : (kMarginH + kAvatar + kGap));
int eSize = (L.emojiCount == 1) ? lineH * 4 : lineH * 2;
int emojiGap = lineH / 2;
int totalW = L.emojiCount * eSize + (L.emojiCount - 1) * emojiGap;
int x = !L.isOutgoing
? (contentEdge + kPadH)
: (contentEdge - kPadH - totalW);
L.emojiArea = QRect(x, kGap * 2, totalW, eSize);
L.totalHeight = eSize + kGap * 2 + kItemGap;
L.bubbleRect = QRect();
return L;
}
QString qText = hasQuote ? index.data(MsgQuoteTextRole).toString() : QString();
int availW = L.viewWidth - kMarginH - kAvatar - kGap - kMarginH;
@@ -77,14 +104,18 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
QFontMetrics fm(option.font);
int contentW = calcMaxLineWidth(option.font, text, limitW);
QString author = index.data(MsgAuthorRole).toString();
QFont boldFm = option.font;
boldFm.setBold(true);
int authorW = QFontMetrics(boldFm).horizontalAdvance(author);
contentW = qMax(contentW, authorW);
if (!L.isOutgoing) {
QString author = index.data(MsgAuthorRole).toString();
QFont boldFm = option.font;
boldFm.setBold(true);
int authorW = QFontMetrics(boldFm).horizontalAdvance(author);
contentW = qMax(contentW, authorW);
}
if (hasQuote) {
QString qAuthor = index.data(MsgQuoteAuthorRole).toString();
QFont boldFm = option.font;
boldFm.setBold(true);
int qAuthorW = QFontMetrics(boldFm).horizontalAdvance(qAuthor);
contentW = qMax(contentW, qAuthorW + kQuoteBarW + kQuotePadH + 4);
int qTextW = qMin(contentW + 8, limitW);
@@ -107,23 +138,39 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
int textH = measureTextHeight(option.font, text, textW);
int authorH = fm.height();
int bubbleH = kPadTop + authorH + 2 + (hasQuote ? qH + kQuoteGap : 0) + textH + kStatusH + kPadBot;
int bubbleH = L.isOutgoing
? kPadTop + (hasQuote ? qH + kQuoteGap : 0) + textH + kStatusH + kPadBot
: kPadTop + authorH + 2 + (hasQuote ? qH + kQuoteGap : 0) + textH + kStatusH + kPadBot;
L.totalHeight = qMax(bubbleH, kAvatar) + kItemGap;
int contentLeft = L.narrow ? kMarginH : (kMarginH + kAvatar + kGap);
int contentEdge = L.isOutgoing
? L.viewWidth - (L.narrow ? kMarginH : (kMarginH + kAvatar + kGap))
: (L.narrow ? kMarginH : (kMarginH + kAvatar + kGap));
L.bubbleRect = QRect(contentLeft, 0, bubbleW, bubbleH);
if (!L.isOutgoing)
L.bubbleRect = QRect(contentEdge, 0, bubbleW, bubbleH);
else
L.bubbleRect = QRect(contentEdge - bubbleW, 0, bubbleW, bubbleH);
int contentTop = kPadTop;
L.authorRect = QRect(contentLeft + kPadH, contentTop, textW, authorH);
contentTop += authorH + 2;
if (!L.isOutgoing) {
L.authorRect = QRect(contentEdge + kPadH, contentTop, textW, authorH);
contentTop += authorH + 2;
}
if (hasQuote) {
L.quoteRect = QRect(contentLeft + kPadH, contentTop, bubbleW - 2 * kPadH, qH);
L.quoteBarRect = QRect(L.quoteRect.left(), L.quoteRect.top(),
kQuoteBarW, qH);
int tx = L.quoteBarRect.right() + kQuotePadH;
int qX = !L.isOutgoing
? (contentEdge + kPadH)
: (contentEdge - kPadH - (bubbleW - 2 * kPadH));
L.quoteRect = QRect(qX, contentTop, bubbleW - 2 * kPadH, qH);
if (!L.isOutgoing)
L.quoteBarRect = QRect(L.quoteRect.left(), L.quoteRect.top(), kQuoteBarW, qH);
else
L.quoteBarRect = QRect(L.quoteRect.right() - kQuoteBarW, L.quoteRect.top(), kQuoteBarW, qH);
int tx = !L.isOutgoing
? (L.quoteBarRect.right() + kQuotePadH)
: (L.quoteRect.left() + kQuotePadH);
L.quoteAuthorRect = QRect(tx, L.quoteRect.top() + kQuotePadV,
L.quoteRect.width() - kQuoteBarW - kQuotePadH,
QFontMetrics(option.font).height());
@@ -134,15 +181,18 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
contentTop += qH + kQuoteGap;
}
L.textRect = QRect(contentLeft + kPadH, contentTop,
textW, textH);
L.statusRect = QRect(contentLeft + kPadH,
contentTop + textH + 2,
textW, kStatusH);
int txRect = !L.isOutgoing
? (contentEdge + kPadH)
: (contentEdge - kPadH - textW);
L.textRect = QRect(txRect, contentTop, textW, textH);
L.statusRect = QRect(txRect, contentTop + textH + 2, textW, kStatusH);
if (!L.narrow) {
int avatarY = L.bubbleRect.bottom() - kAvatar;
L.avatarRect = QRect(kMarginH, avatarY, kAvatar, kAvatar);
if (!L.isOutgoing)
L.avatarRect = QRect(kMarginH, avatarY, kAvatar, kAvatar);
else
L.avatarRect = QRect(L.viewWidth - kMarginH - kAvatar, avatarY, kAvatar, kAvatar);
}
return L;
@@ -349,6 +399,56 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
Layout L = calcLayout(option, index);
if (L.emojiOnly) {
QString msgText = index.data(MsgTextRole).toString();
QString trimmed = msgText.trimmed();
QFontMetrics fm(option.font);
int lineH = fm.lineSpacing();
int eSize = (L.emojiCount == 1) ? lineH * 4 : lineH * 2;
int x = L.emojiArea.left();
int y = L.emojiArea.top();
int pos = 0;
while (pos < trimmed.length()) {
if (trimmed.at(pos).isSpace()) { ++pos; continue; }
QString emojiStr;
for (const auto &cat : builtinEmojiSet())
for (const auto &e : cat.items) {
if (e.unicode.length() > trimmed.length() - pos) continue;
if (QStringView(trimmed).mid(pos, e.unicode.length()) == e.unicode) {
emojiStr = e.unicode;
pos += e.unicode.length();
break;
}
}
if (emojiStr.isEmpty()) { ++pos; continue; }
bool animated = g_animatedEmojiMap.contains(emojiStr);
if (animated) {
LottieIcon *icon = AnimTimer::instance()->icon(g_animatedEmojiMap[emojiStr]);
if (icon && icon->isValid()) {
QImage frame = icon->renderFrame(icon->currentFrame(), QSize(eSize, eSize));
painter->drawImage(x, y, frame);
} else {
QFont big = option.font;
big.setPointSize((int)(big.pointSize() * (L.emojiCount == 1 ? 4.0 : 2.0)));
painter->setFont(big);
painter->setPen(option.palette.text().color());
painter->drawText(QRect(x, y, eSize, eSize), Qt::AlignCenter, emojiStr);
}
} else {
QFont big = option.font;
big.setPointSize((int)(big.pointSize() * (L.emojiCount == 1 ? 4.0 : 2.0)));
painter->setFont(big);
painter->setPen(option.palette.text().color());
painter->drawText(QRect(x, y, eSize, eSize), Qt::AlignCenter, emojiStr);
}
x += eSize + lineH / 2;
}
painter->restore();
return;
}
auto *cv = qobject_cast<const ChatView *>(option.widget);
bool singleTextSel = false;
bool multiRowSel = false;
@@ -379,12 +479,14 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
if (!L.narrow)
drawAvatar(painter, L, index);
QFont authorFont = option.font;
authorFont.setBold(true);
painter->setFont(authorFont);
painter->setPen(QColor(0x33, 0x90, 0xEC));
painter->drawText(L.authorRect, Qt::AlignLeft | Qt::AlignVCenter,
index.data(MsgAuthorRole).toString());
if (!L.isOutgoing) {
QFont authorFont = option.font;
authorFont.setBold(true);
painter->setFont(authorFont);
painter->setPen(QColor(0x33, 0x90, 0xEC));
painter->drawText(L.authorRect, Qt::AlignLeft | Qt::AlignVCenter,
index.data(MsgAuthorRole).toString());
}
painter->setPen(option.palette.text().color());
QFont textFont = option.font;
@@ -458,17 +560,30 @@ void MessageDelegate::drawBubble(QPainter *p, const Layout &L, const QStyleOptio
body.addRoundedRect(b, r, r);
QPainterPath tail;
qreal tx = b.left();
qreal ty = b.bottom();
tail.moveTo(tx, ty - r);
tail.lineTo(tx - t, ty - t / 2.0);
tail.lineTo(tx + r, ty);
if (!L.isOutgoing) {
qreal tx = b.left();
qreal ty = b.bottom();
tail.moveTo(tx, ty - r);
tail.lineTo(tx - t, ty - t / 2.0);
tail.lineTo(tx + r, ty);
} else {
qreal tx = b.right();
qreal ty = b.bottom();
tail.moveTo(tx, ty - r);
tail.lineTo(tx + t, ty - t / 2.0);
tail.lineTo(tx - r, ty);
}
tail.closeSubpath();
QPainterPath combined = body.united(tail);
QColor base = opt.palette.window().color();
QColor bubbleFill = base.lighter(135);
QColor bubbleFill;
if (!L.isOutgoing) {
QColor base = opt.palette.window().color();
bubbleFill = base.lighter(135);
} else {
bubbleFill = QColor(0xE1, 0xF5, 0xE1);
}
p->setPen(Qt::NoPen);
p->setBrush(bubbleFill);
p->drawPath(combined);