|
|
|
|
@ -139,21 +139,27 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
|
|
|
|
const auto* mouse = static_cast<QMouseEvent*>(event); |
|
|
|
|
if (mouse->button() != Qt::LeftButton) return false; |
|
|
|
|
const QPoint point = mouse->pos() - option.rect.topLeft(); |
|
|
|
|
const bool onQuote = L.quoteRect.contains(point); |
|
|
|
|
int reaction = L.reactionHeart.contains(point) ? -1 : -2; |
|
|
|
|
for (const auto& chip : L.reactionChips) if (chip.rect.contains(point)) { reaction = chip.id; break; } |
|
|
|
|
const bool onReaction = mouse->button() == Qt::LeftButton && reaction != -2; |
|
|
|
|
const bool onAvatar=mouse->button()==Qt::LeftButton && !L.narrow && L.avatarRect.contains(mouse->pos()-option.rect.topLeft()); |
|
|
|
|
const bool onPoll = index.data(MsgContentTypeRole).toString() == "poll" |
|
|
|
|
const bool onPoll = !onQuote && index.data(MsgContentTypeRole).toString() == "poll" |
|
|
|
|
&& !index.data(MsgChannelIdRole).toString().startsWith("dm:") |
|
|
|
|
&& (L.bubbleRect.contains(point) || L.emojiArea.contains(point)) |
|
|
|
|
&& (!L.reactionHeart.isValid() || point.y() < L.reactionHeart.top()); |
|
|
|
|
if (event->type()==QEvent::MouseButtonPress) { |
|
|
|
|
m_quotePress = onQuote ? index : QModelIndex(); m_quotePressPosition = mouse->pos(); |
|
|
|
|
m_reactionPress = onReaction ? index : QModelIndex(); m_pressedReaction = reaction; m_reactionPressPosition = mouse->pos(); |
|
|
|
|
m_avatarPress=onAvatar ? index : QModelIndex(); m_avatarPressPosition=mouse->pos(); |
|
|
|
|
m_pollPress = onPoll ? index : QModelIndex(); m_pollPressPosition = mouse->pos(); |
|
|
|
|
return onAvatar || onReaction || onPoll; |
|
|
|
|
return onQuote || onAvatar || onReaction || onPoll; |
|
|
|
|
} |
|
|
|
|
const auto* view = qobject_cast<const ChatView*>(option.widget); |
|
|
|
|
const bool hadQuotePress = m_quotePress.isValid(); |
|
|
|
|
const bool activateQuote = onQuote && m_quotePress == index |
|
|
|
|
&& (mouse->pos() - m_quotePressPosition).manhattanLength() < 5 && (!view || !view->isSelecting()); |
|
|
|
|
m_quotePress = QPersistentModelIndex(); |
|
|
|
|
const bool activatePoll = onPoll && m_pollPress == index && (mouse->pos() - m_pollPressPosition).manhattanLength() < 5 |
|
|
|
|
&& (!view || !view->textSelection().active); |
|
|
|
|
m_pollPress = QPersistentModelIndex(); |
|
|
|
|
@ -163,6 +169,8 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
|
|
|
|
const bool activateReaction = onReaction && m_reactionPress == index && m_pressedReaction == reaction |
|
|
|
|
&& (mouse->pos() - m_reactionPressPosition).manhattanLength() < 5; |
|
|
|
|
m_reactionPress = QPersistentModelIndex(); m_pressedReaction = -2; |
|
|
|
|
if (activateQuote) { emit replyRequested(index.data(MsgReplyToRole).toString()); return true; } |
|
|
|
|
if (hadQuotePress || onQuote) return true; |
|
|
|
|
if (activateReaction) { emit reactionRequested(index, reaction); return true; } |
|
|
|
|
if (hadReactionPress || onReaction) return true; |
|
|
|
|
if (activateAvatar) { |
|
|
|
|
@ -428,6 +436,24 @@ static int calcMaxLineWidth(const QFont &font, const QString &text, int availW)
|
|
|
|
|
MessageDelegate::Layout MessageDelegate::calcLayout( |
|
|
|
|
const QStyleOptionViewItem& option, const QModelIndex& index) const { |
|
|
|
|
Layout layout = calcBodyLayout(option, index); |
|
|
|
|
// Медиа и крупные emoji получают ту же цитату над своим содержимым.
|
|
|
|
|
if (index.data(MsgHasQuoteRole).toBool() && !layout.quoteRect.isValid()) { |
|
|
|
|
const int line = QFontMetrics(option.font).height(), height = 2 * line + 2 * kQuotePadV + 2; |
|
|
|
|
const QRect body = layout.emojiOnly ? layout.emojiArea : layout.bubbleRect; |
|
|
|
|
const int width = qMin(qMax(200, body.width()), qMax(40, layout.viewWidth - 2 * kMarginH)); |
|
|
|
|
const int x = layout.isOutgoing ? body.right() - width + 1 : body.left(); |
|
|
|
|
layout.quoteRect = QRect(qMax(kMarginH, x), 0, width, height); |
|
|
|
|
layout.quoteBarRect = QRect(layout.quoteRect.left(), 0, kQuoteBarW, height); |
|
|
|
|
layout.quoteAuthorRect = QRect(layout.quoteRect.left() + kQuotePadH + kQuoteBarW, kQuotePadV, |
|
|
|
|
width - 2 * kQuotePadH - kQuoteBarW, line); |
|
|
|
|
layout.quoteTextRect = layout.quoteAuthorRect.translated(0, line + 2); |
|
|
|
|
for (QRect* rect : {&layout.bubbleRect, &layout.avatarRect, &layout.authorRect, &layout.textRect, &layout.statusRect, |
|
|
|
|
&layout.deliveryRect, &layout.emojiArea, &layout.voicePlayBtnRect, &layout.voiceWaveformRect, |
|
|
|
|
&layout.voiceDurationRect, &layout.voiceTimeRect, &layout.mediaBarRect, &layout.progressBarRect, |
|
|
|
|
&layout.videoThumbRect, &layout.videoPlayBtnRect, &layout.videoDurBadgeRect, &layout.imageThumbRect, |
|
|
|
|
&layout.imageBadgeRect}) if (rect->isValid()) rect->translate(0, height + kQuoteGap); |
|
|
|
|
layout.totalHeight += height + kQuoteGap; |
|
|
|
|
} |
|
|
|
|
const QString channel = index.data(MsgChannelIdRole).toString(); |
|
|
|
|
if (layout.emojiOnly || channel.isEmpty() || channel.startsWith("dm:") |
|
|
|
|
|| index.data(MsgMessageKeyRole).toString().size() != 64 || layout.bubbleRect.isEmpty()) return layout; |
|
|
|
|
@ -458,7 +484,7 @@ MessageDelegate::Layout MessageDelegate::calcLayout(
|
|
|
|
|
} |
|
|
|
|
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; |
|
|
|
|
layout.totalHeight = qMax(layout.bubbleRect.bottom() + 1, kAvatar) + kItemGap; |
|
|
|
|
if (layout.avatarRect.isValid()) layout.avatarRect.moveTop(qMax(0, layout.bubbleRect.bottom() + 1 - kAvatar)); |
|
|
|
|
return layout; |
|
|
|
|
} |
|
|
|
|
@ -705,8 +731,7 @@ MessageDelegate::Layout MessageDelegate::calcBodyLayout(
|
|
|
|
|
|
|
|
|
|
int qH = 0; |
|
|
|
|
if (hasQuote) { |
|
|
|
|
int qTextW = bubbleW - 2 * kPadH - kQuoteBarW - kQuotePadH; |
|
|
|
|
int qTextH = measureTextHeight(option.font, qText, qTextW); |
|
|
|
|
int qTextH = fm.height(); |
|
|
|
|
int qOneLineH = fm.height(); |
|
|
|
|
qH = kQuotePadV + qOneLineH + (qText.isEmpty() ? 0 : 2 + qTextH) + kQuotePadV; |
|
|
|
|
} |
|
|
|
|
@ -973,10 +998,15 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
|
|
|
|
const QModelIndex &index) const { |
|
|
|
|
const Layout layout = calcLayout(option, index); |
|
|
|
|
paintBody(painter, option, index, layout); |
|
|
|
|
if (!layout.reactionHeart.isValid()) return; |
|
|
|
|
painter->save(); painter->translate(option.rect.topLeft()); |
|
|
|
|
painter->setClipRect(QRect(QPoint(0, 0), option.rect.size())); |
|
|
|
|
drawReactions(painter, layout, option); painter->restore(); |
|
|
|
|
if (layout.quoteRect.isValid()) drawQuote(painter, layout, index, option); |
|
|
|
|
if (layout.reactionHeart.isValid()) drawReactions(painter, layout, option); |
|
|
|
|
if (index.data(MsgHighlightRole).toBool()) { |
|
|
|
|
QColor color = option.palette.highlight().color(); color.setAlpha(55); |
|
|
|
|
painter->fillRect(QRect(QPoint(0, 0), option.rect.size()), color); |
|
|
|
|
} |
|
|
|
|
painter->restore(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& option, |
|
|
|
|
@ -1388,7 +1418,7 @@ void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& o
|
|
|
|
|
iconFont.setPointSize(iconFont.pointSize() + 6); |
|
|
|
|
painter->setFont(iconFont); |
|
|
|
|
painter->setPen(option.palette.text().color()); |
|
|
|
|
QRect iconRect(L.bubbleRect.left() + kPadH, kPadTop, 24, 24); |
|
|
|
|
QRect iconRect(L.bubbleRect.left() + kPadH, L.bubbleRect.top() + kPadTop, 24, 24); |
|
|
|
|
if (dlState == 2) |
|
|
|
|
painter->drawText(iconRect, Qt::AlignCenter, QString::fromUtf8("\xE2\x9C\x85")); |
|
|
|
|
else |
|
|
|
|
@ -1457,10 +1487,6 @@ void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& o
|
|
|
|
|
painter->restore(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (index.data(MsgHasQuoteRole).toBool()) { |
|
|
|
|
drawQuote(painter, L, index, option); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!L.narrow) |
|
|
|
|
drawAvatar(painter, L, index); |
|
|
|
|
|
|
|
|
|
@ -1547,24 +1573,25 @@ void MessageDelegate::drawQuote(QPainter *p, const Layout &L,
|
|
|
|
|
p->save(); |
|
|
|
|
|
|
|
|
|
p->setPen(Qt::NoPen); |
|
|
|
|
p->setBrush(QColor(0xFF, 0xEE, 0xEE)); |
|
|
|
|
QColor background = opt.palette.highlight().color(); background.setAlpha(28); |
|
|
|
|
p->setBrush(background); |
|
|
|
|
p->drawRoundedRect(L.quoteRect, 5, 5); |
|
|
|
|
|
|
|
|
|
p->setPen(Qt::NoPen); |
|
|
|
|
p->setBrush(QColor(0xE0, 0x40, 0x40)); |
|
|
|
|
p->setBrush(opt.palette.highlight()); |
|
|
|
|
p->drawRect(L.quoteBarRect); |
|
|
|
|
|
|
|
|
|
QFont boldFont = opt.font; |
|
|
|
|
boldFont.setBold(true); |
|
|
|
|
p->setFont(boldFont); |
|
|
|
|
p->setPen(QColor(0xE0, 0x40, 0x40)); |
|
|
|
|
p->setPen(opt.palette.text().color()); |
|
|
|
|
p->drawText(L.quoteAuthorRect, Qt::AlignLeft | Qt::AlignVCenter, |
|
|
|
|
index.data(MsgQuoteAuthorRole).toString()); |
|
|
|
|
QFontMetrics(boldFont).elidedText(index.data(MsgQuoteAuthorRole).toString(), Qt::ElideRight, L.quoteAuthorRect.width())); |
|
|
|
|
|
|
|
|
|
p->setFont(opt.font); |
|
|
|
|
p->setPen(QColor(0x99, 0x55, 0x55)); |
|
|
|
|
p->drawText(L.quoteTextRect, Qt::AlignLeft | Qt::TextWordWrap, |
|
|
|
|
index.data(MsgQuoteTextRole).toString()); |
|
|
|
|
p->setPen(opt.palette.text().color()); |
|
|
|
|
p->drawText(L.quoteTextRect, Qt::AlignLeft | Qt::AlignVCenter, |
|
|
|
|
QFontMetrics(opt.font).elidedText(index.data(MsgQuoteTextRole).toString(), Qt::ElideRight, L.quoteTextRect.width())); |
|
|
|
|
|
|
|
|
|
p->restore(); |
|
|
|
|
} |
|
|
|
|
|