You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
10 KiB
239 lines
10 KiB
#include "channeldelegate.h" |
|
#include <QPainter> |
|
#include <QApplication> |
|
|
|
ChannelDelegate::ChannelDelegate(QObject *parent) |
|
: QStyledItemDelegate(parent) {} |
|
|
|
QRect ChannelDelegate::inviteButtonRect(const QRect &itemRect) { |
|
const int margin = 6; |
|
return QRect(itemRect.right() - margin - InviteBtnSize, |
|
itemRect.top() + margin, |
|
InviteBtnSize, InviteBtnSize); |
|
} |
|
|
|
QRect ChannelDelegate::inviteByLinkButtonRect(const QRect &itemRect) { |
|
const int margin = 6; |
|
return QRect(itemRect.right() - margin - InviteBtnSize * 2 - InviteBtnGap, |
|
itemRect.top() + margin, |
|
InviteBtnSize, InviteBtnSize); |
|
} |
|
|
|
QRect ChannelDelegate::settingsButtonRect(const QRect &itemRect) { |
|
const int margin = 6; |
|
return QRect(itemRect.right() - margin - InviteBtnSize * 3 - InviteBtnGap * 2, |
|
itemRect.top() + margin, |
|
InviteBtnSize, InviteBtnSize); |
|
} |
|
|
|
void ChannelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, |
|
const QModelIndex &index) const { |
|
painter->save(); |
|
|
|
/* ── заголовок секции (например, «Личные») ── */ |
|
if (index.data(ChannelIsSectionRole).toBool()) { |
|
painter->fillRect(option.rect, option.palette.window()); |
|
painter->setPen(option.palette.mid().color()); |
|
QFont f = option.font; |
|
f.setBold(true); |
|
painter->setFont(f); |
|
QRect tr = option.rect.adjusted(12, 0, -8, 0); |
|
painter->drawText(tr, Qt::AlignLeft | Qt::AlignVCenter, |
|
index.data(ChannelNameRole).toString()); |
|
painter->restore(); |
|
return; |
|
} |
|
|
|
const int barWidth = 4; |
|
const int margin = 8; |
|
const int iconSize = 32; |
|
|
|
int peersOnline = index.data(ChannelPeersOnlineRole).toInt(); |
|
int autoActive = index.data(ChannelAutoConnectActiveRole).toInt(); |
|
|
|
/* ── background ── */ |
|
if (option.state & QStyle::State_Selected) { |
|
painter->fillRect(option.rect, option.palette.highlight()); |
|
} else if (option.state & QStyle::State_MouseOver) { |
|
painter->fillRect(option.rect, option.palette.light()); |
|
} else { |
|
painter->fillRect(option.rect, option.palette.base()); |
|
} |
|
|
|
/* ── vertical status bar (left edge, over background) ── */ |
|
QColor barColor = Qt::transparent; |
|
if (peersOnline > 0) barColor = QColor("#4CAF50"); |
|
else if (autoActive && peersOnline == 0) barColor = QColor("#FFC107"); |
|
if (barColor.alpha() > 0) { |
|
QRect barRect(option.rect.left(), option.rect.top(), barWidth, option.rect.height()); |
|
painter->fillRect(barRect, barColor); |
|
} |
|
|
|
QRect iconRect(option.rect.left() + margin + barWidth, |
|
option.rect.top() + (option.rect.height() - iconSize) / 2, |
|
iconSize, iconSize); |
|
|
|
QIcon icon = qvariant_cast<QIcon>(index.data(ChannelIconRole)); |
|
if (!icon.isNull()) { |
|
icon.paint(painter, iconRect); |
|
} |
|
|
|
/* ── online count badge (top-left of avatar, above it) ── */ |
|
if (peersOnline > 0) { |
|
int badgeSize = 16; |
|
QRect badgeRect(iconRect.left() - 4, iconRect.top() - 6, badgeSize, badgeSize); |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
painter->setBrush(QColor("#4CAF50")); |
|
painter->setPen(QPen(Qt::white, 1.5)); |
|
painter->drawEllipse(badgeRect); |
|
QFont badgeFont = option.font; |
|
badgeFont.setBold(true); |
|
badgeFont.setPixelSize(10); |
|
painter->setFont(badgeFont); |
|
painter->setPen(Qt::white); |
|
QString num = QString::number(peersOnline); |
|
if (peersOnline > 9) num = "9+"; |
|
painter->drawText(badgeRect, Qt::AlignCenter, num); |
|
} |
|
|
|
QRect textRect(iconRect.right() + margin, option.rect.top() + margin, |
|
option.rect.right() - iconRect.right() - 2 * margin, |
|
option.rect.height() - 2 * margin); |
|
|
|
QString name = index.data(ChannelNameRole).toString(); |
|
QFont nameFont = option.font; |
|
nameFont.setBold(true); |
|
nameFont.setPointSize(nameFont.pointSize() + 1); |
|
painter->setFont(nameFont); |
|
|
|
QColor textColor = (option.state & QStyle::State_Selected) |
|
? option.palette.highlightedText().color() |
|
: option.palette.text().color(); |
|
painter->setPen(textColor); |
|
|
|
QRect nameRect = textRect; |
|
nameRect.setHeight(QFontMetrics(nameFont).height()); |
|
nameRect.setRight(nameRect.right() - InviteBtnSize * 3 - InviteBtnGap * 2 - 4); |
|
painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignTop, |
|
painter->fontMetrics().elidedText(name, Qt::ElideRight, nameRect.width())); |
|
|
|
QString lastMsg = index.data(ChannelLastMessageRole).toString(); |
|
if (!lastMsg.isEmpty()) { |
|
QFont msgFont = option.font; |
|
msgFont.setPointSize(msgFont.pointSize() - 1); |
|
QColor nickColor(0x33, 0x90, 0xEC); |
|
QColor msgColor = (option.state & QStyle::State_Selected) |
|
? option.palette.highlightedText().color().darker(120) |
|
: QColor(128, 128, 128); |
|
if (option.state & QStyle::State_Selected) nickColor = nickColor.lighter(140); |
|
|
|
QRect msgRect(textRect.left(), nameRect.bottom() + 4, |
|
textRect.width(), |
|
option.rect.bottom() - nameRect.bottom() - margin); |
|
QFontMetrics msgFm(msgFont); |
|
|
|
int colon = lastMsg.indexOf(QString::fromUtf8(": ")); |
|
if (colon >= 0) { |
|
QString nick = lastMsg.left(colon + 1); |
|
QString body = lastMsg.mid(colon + 2); |
|
|
|
painter->setFont(msgFont); |
|
painter->setPen(nickColor); |
|
QRect nickRect = msgRect; |
|
nickRect.setWidth(msgFm.horizontalAdvance(nick)); |
|
painter->drawText(nickRect, Qt::AlignLeft | Qt::AlignTop, nick); |
|
|
|
painter->setPen(msgColor); |
|
QRect bodyRect(msgRect.left() + nickRect.width(), msgRect.top(), |
|
msgRect.width() - nickRect.width(), msgRect.height()); |
|
QString elidedBody = msgFm.elidedText(body, Qt::ElideRight, bodyRect.width()); |
|
painter->drawText(bodyRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, elidedBody); |
|
} else { |
|
painter->setFont(msgFont); |
|
painter->setPen(msgColor); |
|
QString elided = msgFm.elidedText(lastMsg, Qt::ElideRight, msgRect.width()); |
|
painter->drawText(msgRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, elided); |
|
} |
|
} |
|
|
|
/* ── unread count badge (right of channel name) ── */ |
|
int unread = index.data(ChannelUnreadCountRole).toInt(); |
|
if (unread > 0) { |
|
QString unreadText = unread > 99 ? QStringLiteral("99+") : QString::number(unread); |
|
QFont badgeFont = option.font; |
|
badgeFont.setBold(true); |
|
badgeFont.setPixelSize(11); |
|
QFontMetrics badgeFm(badgeFont); |
|
int textW = badgeFm.horizontalAdvance(unreadText) + 8; |
|
int badgeH = 18; |
|
if (textW < badgeH) textW = badgeH; |
|
QRect badgeRect(textRect.right() - textW, nameRect.top(), |
|
textW, badgeH); |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
painter->setBrush(QColor("#E53935")); |
|
painter->setPen(Qt::NoPen); |
|
painter->drawRoundedRect(badgeRect, badgeH / 2, badgeH / 2); |
|
painter->setFont(badgeFont); |
|
painter->setPen(Qt::white); |
|
painter->drawText(badgeRect, Qt::AlignCenter, unreadText); |
|
} |
|
|
|
/* ── invite plus button (top-right) ── */ |
|
QRect plusRect = inviteButtonRect(option.rect); |
|
bool plusHovered = index.data(ChannelInviteBtnHoveredRole).toInt() != 0; |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
QColor plusFill = plusHovered ? QColor(0x4A, 0xA4, 0xF0) : QColor(0x33, 0x90, 0xEC); |
|
painter->setBrush(plusFill); |
|
painter->setPen(Qt::NoPen); |
|
painter->drawEllipse(plusRect); |
|
painter->setPen(QPen(Qt::white, 2)); |
|
painter->drawLine(plusRect.center().x(), plusRect.top() + 5, |
|
plusRect.center().x(), plusRect.bottom() - 5); |
|
painter->drawLine(plusRect.left() + 5, plusRect.center().y(), |
|
plusRect.right() - 5, plusRect.center().y()); |
|
|
|
/* ── invite-by-link button (left of plus, orange square with arrow) ── */ |
|
QRect invRect = inviteByLinkButtonRect(option.rect); |
|
bool invHovered = index.data(ChannelInviteByLinkBtnHoveredRole).toInt() != 0; |
|
painter->setRenderHint(QPainter::Antialiasing); |
|
QColor invFill = invHovered ? QColor(0xF0, 0x90, 0x30) : QColor(0xE6, 0x7E, 0x22); |
|
painter->setBrush(invFill); |
|
painter->setPen(Qt::NoPen); |
|
painter->drawRoundedRect(invRect, 3, 3); |
|
painter->setPen(QPen(Qt::white, 1.5)); |
|
/* arrow: ← */ |
|
int cx = invRect.center().x(), cy = invRect.center().y(); |
|
int dx = 3; |
|
painter->drawLine(cx + dx, cy - 3, cx - dx, cy); |
|
painter->drawLine(cx + dx, cy + 3, cx - dx, cy); |
|
painter->drawLine(cx + dx + 1, cy, cx + dx + 1, cy - 3 + 1); |
|
painter->drawLine(cx + dx + 1, cy, cx + dx + 1, cy + 3 - 1); |
|
|
|
/* ── settings gear (left of invite-by-link button) ── */ |
|
QRect gearRect = settingsButtonRect(option.rect); |
|
bool gearHovered = index.data(ChannelSettingsBtnHoveredRole).toInt() != 0; |
|
QColor gearColor = gearHovered ? QColor("#3390EC") : QColor("#555"); |
|
painter->setPen(QPen(gearColor, 2)); |
|
painter->setBrush(Qt::NoBrush); |
|
int gr = 5; |
|
painter->drawEllipse(gearRect.center(), gr, gr); |
|
painter->drawEllipse(gearRect.center(), gr / 2, gr / 2); |
|
int gcx = gearRect.center().x(), gcy = gearRect.center().y(); |
|
painter->drawLine(gcx, gcy - gr, gcx, gcy - gr + 1); |
|
painter->drawLine(gcx, gcy + gr, gcx, gcy + gr - 1); |
|
painter->drawLine(gcx - gr, gcy, gcx - gr + 1, gcy); |
|
painter->drawLine(gcx + gr, gcy, gcx + gr - 1, gcy); |
|
painter->drawLine(gcx - 3, gcy - 4, gcx - 3, gcy - 5); |
|
painter->drawLine(gcx + 3, gcy + 4, gcx + 3, gcy + 5); |
|
painter->drawLine(gcx - 4, gcy - 3, gcx - 5, gcy - 3); |
|
painter->drawLine(gcx + 4, gcy + 3, gcx + 5, gcy + 3); |
|
|
|
painter->restore(); |
|
} |
|
|
|
QSize ChannelDelegate::sizeHint(const QStyleOptionViewItem &option, |
|
const QModelIndex &index) const { |
|
Q_UNUSED(option); |
|
Q_UNUSED(index); |
|
return QSize(200, 56); |
|
}
|
|
|