forked from blue/squawk
message bubbles, avatar rounding, roster adjusments
This commit is contained in:
parent
9ac0ca10f3
commit
8a2658e4fc
@ -39,7 +39,11 @@ Squawk::Squawk(QWidget *parent) :
|
||||
m_ui->setupUi(this);
|
||||
m_ui->roster->setModel(&rosterModel);
|
||||
m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_ui->roster->setColumnWidth(1, 30);
|
||||
if (QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1) {
|
||||
m_ui->roster->setColumnWidth(1, 52);
|
||||
} else {
|
||||
m_ui->roster->setColumnWidth(1, 26);
|
||||
}
|
||||
m_ui->roster->setIconSize(QSize(20, 20));
|
||||
m_ui->roster->header()->setStretchLastSection(false);
|
||||
m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
|
23
ui/squawk.ui
23
ui/squawk.ui
@ -73,6 +73,9 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="1">
|
||||
<widget class="QTreeView" name="roster">
|
||||
<property name="frameShape">
|
||||
@ -96,16 +99,31 @@
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="headerMinimumSectionSize">
|
||||
<number>10</number>
|
||||
</attribute>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@ -115,6 +133,9 @@
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -162,7 +183,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>718</width>
|
||||
<height>27</height>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuSettings">
|
||||
|
@ -25,12 +25,16 @@
|
||||
#include <QTimer>
|
||||
#include <QFileDialog>
|
||||
#include <QMimeDatabase>
|
||||
#include <unistd.h>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QCoreApplication>
|
||||
#include <QTemporaryFile>
|
||||
#include <QDir>
|
||||
#include <QMenu>
|
||||
#include <QBitmap>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
constexpr QSize avatarSize(50, 50);
|
||||
|
||||
Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent):
|
||||
QWidget(parent),
|
||||
@ -348,11 +352,25 @@ void Conversation::onClearButton()
|
||||
|
||||
void Conversation::setAvatar(const QString& path)
|
||||
{
|
||||
QPixmap pixmap;
|
||||
if (path.size() == 0) {
|
||||
m_ui->avatar->setPixmap(Shared::icon("user", true).pixmap(QSize(50, 50)));
|
||||
pixmap = Shared::icon("user", true).pixmap(avatarSize);
|
||||
} else {
|
||||
m_ui->avatar->setPixmap(path);
|
||||
pixmap = QPixmap(path).scaled(avatarSize);
|
||||
}
|
||||
|
||||
|
||||
QPixmap result(avatarSize);
|
||||
result.fill(Qt::transparent);
|
||||
QPainter painter(&result);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
QPainterPath maskPath;
|
||||
maskPath.addEllipse(0, 0, avatarSize.width(), avatarSize.height());
|
||||
painter.setClipPath(maskPath);
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
|
||||
m_ui->avatar->setPixmap(result);
|
||||
}
|
||||
|
||||
void Conversation::onTextEditDocSizeChanged(const QSizeF& size)
|
||||
|
@ -141,6 +141,14 @@ protected:
|
||||
|
||||
ShadowOverlay shadow;
|
||||
QMenu* contextMenu;
|
||||
|
||||
private:
|
||||
static bool painterInitialized;
|
||||
static QPainterPath* avatarMask;
|
||||
static QPixmap* avatarPixmap;
|
||||
static QPainter* avatarPainter;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // CONVERSATION_H
|
||||
|
@ -31,10 +31,6 @@ constexpr int approximateSingleMessageHeight = 20;
|
||||
constexpr int progressSize = 70;
|
||||
constexpr int dateDeviderMargin = 10;
|
||||
|
||||
constexpr int avatarHeight = 50;
|
||||
constexpr int margin = 6;
|
||||
constexpr int halfMargin = 3;
|
||||
|
||||
const std::set<int> FeedView::geometryChangingRoles = {
|
||||
Models::MessageFeed::Attach,
|
||||
Models::MessageFeed::Text,
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QApplication>
|
||||
#include <QMouseEvent>
|
||||
#include <QAbstractItemView>
|
||||
@ -29,6 +30,11 @@ constexpr int avatarHeight = 50;
|
||||
constexpr int margin = 6;
|
||||
constexpr int textMargin = 2;
|
||||
constexpr int statusIconSize = 16;
|
||||
constexpr float nickFontMultiplier = 1.1;
|
||||
constexpr float dateFontMultiplier = 0.8;
|
||||
|
||||
constexpr int bubbleMargin = 6;
|
||||
constexpr int bubbleBorderRadius = 3;
|
||||
|
||||
MessageDelegate::MessageDelegate(QObject* parent):
|
||||
QStyledItemDelegate(parent),
|
||||
@ -101,9 +107,10 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
if (option.state & QStyle::State_MouseOver) {
|
||||
painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight));
|
||||
}
|
||||
// if (option.state & QStyle::State_MouseOver) {
|
||||
// painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight));
|
||||
// }
|
||||
|
||||
|
||||
bool ntds = needToDrawSender(index, data);
|
||||
if (ntds || option.rect.y() < 1) {
|
||||
@ -118,6 +125,9 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
} else {
|
||||
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
|
||||
}
|
||||
|
||||
QPoint bubbleBegin = messageRect.topLeft();
|
||||
messageRect.adjust(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2);
|
||||
opt.rect = messageRect;
|
||||
|
||||
QSize messageSize(0, 0);
|
||||
@ -127,9 +137,6 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
bodySize = messageSize;
|
||||
}
|
||||
|
||||
if (ntds) {
|
||||
messageSize.rheight() += nickMetrics.lineSpacing();
|
||||
}
|
||||
messageSize.rheight() += dateMetrics.height();
|
||||
QString dateString = data.date.toLocalTime().toString("hh:mm");
|
||||
|
||||
@ -156,45 +163,63 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
messageSize.setWidth(opt.rect.width());
|
||||
}
|
||||
|
||||
QRect rect;
|
||||
if (ntds) {
|
||||
painter->setFont(nickFont);
|
||||
painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect);
|
||||
opt.rect.adjust(0, rect.height() + textMargin, 0, 0);
|
||||
}
|
||||
painter->save();
|
||||
|
||||
int storedY = opt.rect.y();
|
||||
if (ntds) {
|
||||
opt.rect.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0);
|
||||
}
|
||||
|
||||
int attWidth(0);
|
||||
switch (data.attach.state) {
|
||||
case Models::none:
|
||||
clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed
|
||||
break; //but it's a possible performance problem
|
||||
case Models::uploading:
|
||||
paintPreview(data, painter, opt);
|
||||
attWidth = std::max(paintPreview(data, painter, opt), attWidth);
|
||||
[[fallthrough]];
|
||||
case Models::downloading:
|
||||
paintBar(getBar(data), painter, data.sentByMe, opt);
|
||||
messageSize.setWidth(opt.rect.width());
|
||||
messageSize.rheight() += barHeight + textMargin + opt.rect.y() - storedY;
|
||||
paintBubble(data, painter, messageSize, opt, bubbleBegin);
|
||||
attWidth = std::max(paintBar(getBar(data), painter, data.sentByMe, opt), attWidth);
|
||||
break;
|
||||
case Models::remote:
|
||||
paintButton(getButton(data), painter, data.sentByMe, opt);
|
||||
attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth);
|
||||
break;
|
||||
case Models::ready:
|
||||
case Models::local:
|
||||
clearHelperWidget(data);
|
||||
paintPreview(data, painter, opt);
|
||||
attWidth = std::max(paintPreview(data, painter, opt), attWidth);
|
||||
break;
|
||||
case Models::errorDownload: {
|
||||
paintButton(getButton(data), painter, data.sentByMe, opt);
|
||||
paintComment(data, painter, opt);
|
||||
attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth);
|
||||
attWidth = std::max(paintComment(data, painter, opt), attWidth);
|
||||
}
|
||||
|
||||
break;
|
||||
case Models::errorUpload:{
|
||||
clearHelperWidget(data);
|
||||
paintPreview(data, painter, opt);
|
||||
paintComment(data, painter, opt);
|
||||
attWidth = std::max(paintPreview(data, painter, opt), attWidth);
|
||||
attWidth = std::max(paintComment(data, painter, opt), attWidth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
painter->restore();
|
||||
if (data.attach.state != Models::uploading && data.attach.state != Models::downloading) {
|
||||
messageSize.rheight() += opt.rect.y() - storedY;
|
||||
messageSize.setWidth(std::max(attWidth, messageSize.width()));
|
||||
paintBubble(data, painter, messageSize, opt, bubbleBegin);
|
||||
}
|
||||
|
||||
QRect rect;
|
||||
if (ntds) {
|
||||
painter->setFont(nickFont);
|
||||
int storedY2 = opt.rect.y();
|
||||
opt.rect.setY(storedY);
|
||||
painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect);
|
||||
opt.rect.setY(storedY2);
|
||||
}
|
||||
|
||||
int messageLeft = INT16_MAX;
|
||||
int messageRight = opt.rect.x() + messageSize.width();
|
||||
@ -256,6 +281,23 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
}
|
||||
}
|
||||
|
||||
void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const
|
||||
{
|
||||
painter->save();
|
||||
if (data.sentByMe) {
|
||||
bubbleBegin.setX(option.rect.topRight().x() - messageSize.width() - bubbleMargin);
|
||||
painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight));
|
||||
} else {
|
||||
painter->setBrush(option.palette.brush(QPalette::Window));
|
||||
}
|
||||
QSize bubbleAddition(2 * bubbleMargin, 1.5 * bubbleMargin);
|
||||
QRect bubble(bubbleBegin, messageSize + bubbleAddition);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(bubble, bubbleBorderRadius, bubbleBorderRadius);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
|
||||
void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const
|
||||
{
|
||||
int currentRow = index.row();
|
||||
@ -282,11 +324,22 @@ void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelInde
|
||||
y = std::min(0, rect.bottom() - margin - avatarHeight);
|
||||
--currentRow;
|
||||
}
|
||||
|
||||
QPixmap pixmap = icon.pixmap(avatarHeight, avatarHeight);
|
||||
QPainterPath path;
|
||||
int ax;
|
||||
|
||||
if (data.sentByMe) {
|
||||
painter->drawPixmap(option.rect.width() - avatarHeight - margin, y + margin / 2, icon.pixmap(avatarHeight, avatarHeight));
|
||||
ax = option.rect.width() - avatarHeight - margin;
|
||||
} else {
|
||||
painter->drawPixmap(margin, y + margin / 2, icon.pixmap(avatarHeight, avatarHeight));
|
||||
ax = margin;
|
||||
}
|
||||
|
||||
path.addEllipse(ax, y + margin / 2, avatarHeight, avatarHeight);
|
||||
painter->save();
|
||||
painter->setClipPath(path);
|
||||
painter->drawPixmap(ax, y + margin / 2, pixmap);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
bool MessageDelegate::needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const
|
||||
@ -309,7 +362,7 @@ bool MessageDelegate::needToDrawSender(const QModelIndex& index, const Models::F
|
||||
|
||||
QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
QRect messageRect = option.rect.adjusted(0, margin / 2, -(avatarHeight + 3 * margin), -margin / 2);
|
||||
QRect messageRect = option.rect.adjusted(bubbleMargin, margin / 2 + bubbleMargin, -(avatarHeight + 3 * margin + bubbleMargin), -(margin + bubbleMargin) / 2);
|
||||
QStyleOptionViewItem opt = option;
|
||||
opt.rect = messageRect;
|
||||
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
||||
@ -347,10 +400,10 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
|
||||
}
|
||||
|
||||
if (needToDrawSender(index, data)) {
|
||||
messageSize.rheight() += nickMetrics.lineSpacing();
|
||||
messageSize.rheight() += textMargin;
|
||||
messageSize.rheight() += nickMetrics.lineSpacing() + textMargin;
|
||||
}
|
||||
|
||||
messageSize.rheight() += bubbleMargin + bubbleMargin / 2;
|
||||
messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize;
|
||||
|
||||
// if (messageSize.height() < avatarHeight) {
|
||||
@ -372,17 +425,17 @@ void MessageDelegate::initializeFonts(const QFont& font)
|
||||
|
||||
float ndps = nickFont.pointSizeF();
|
||||
if (ndps != -1) {
|
||||
nickFont.setPointSizeF(ndps * 1.2);
|
||||
nickFont.setPointSizeF(ndps * nickFontMultiplier);
|
||||
} else {
|
||||
nickFont.setPointSize(nickFont.pointSize() + 2);
|
||||
nickFont.setPointSize(nickFont.pointSize() * nickFontMultiplier);
|
||||
}
|
||||
|
||||
dateFont.setItalic(true);
|
||||
float dps = dateFont.pointSizeF();
|
||||
if (dps != -1) {
|
||||
dateFont.setPointSizeF(dps * 0.8);
|
||||
dateFont.setPointSizeF(dps * dateFontMultiplier);
|
||||
} else {
|
||||
dateFont.setPointSize(dateFont.pointSize() - 2);
|
||||
dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier);
|
||||
}
|
||||
|
||||
bodyMetrics = QFontMetrics(bodyFont);
|
||||
@ -400,11 +453,11 @@ bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, cons
|
||||
return QStyledItemDelegate::editorEvent(event, model, option, index);
|
||||
}
|
||||
|
||||
void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
|
||||
int MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
|
||||
{
|
||||
QPoint start;
|
||||
if (sentByMe) {
|
||||
start = {option.rect.width() - btn->width(), option.rect.top()};
|
||||
start = {option.rect.x() + option.rect.width() - btn->width(), option.rect.top()};
|
||||
} else {
|
||||
start = option.rect.topLeft();
|
||||
}
|
||||
@ -415,9 +468,10 @@ void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sent
|
||||
btn->show();
|
||||
|
||||
option.rect.adjust(0, buttonHeight + textMargin, 0, 0);
|
||||
return btn->width();
|
||||
}
|
||||
|
||||
void MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const
|
||||
int MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const
|
||||
{
|
||||
painter->setFont(dateFont);
|
||||
QColor q = painter->pen().color();
|
||||
@ -426,9 +480,11 @@ void MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* paint
|
||||
QRect rect;
|
||||
painter->drawText(option.rect, option.displayAlignment, data.attach.error, &rect);
|
||||
option.rect.adjust(0, rect.height() + textMargin, 0, 0);
|
||||
|
||||
return rect.width();
|
||||
}
|
||||
|
||||
void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
|
||||
int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
|
||||
{
|
||||
QPoint start = option.rect.topLeft();
|
||||
bar->resize(option.rect.width(), barHeight);
|
||||
@ -437,9 +493,11 @@ void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentBy
|
||||
bar->render(painter, QPoint(), QRegion(), QWidget::DrawChildren);
|
||||
|
||||
option.rect.adjust(0, barHeight + textMargin, 0, 0);
|
||||
|
||||
return option.rect.width();
|
||||
}
|
||||
|
||||
void MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const
|
||||
int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const
|
||||
{
|
||||
Preview* preview = 0;
|
||||
std::map<QString, Preview*>::iterator itr = previews->find(data.id);
|
||||
@ -458,7 +516,10 @@ void MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* paint
|
||||
emit invalidPath(data.id); //or deleted. This signal notifies the model, and the model notifies the core, preview can
|
||||
} //handle being invalid for as long as I need and can be even become valid again with a new path
|
||||
|
||||
option.rect.adjust(0, preview->size().height() + textMargin, 0, 0);
|
||||
QSize pSize(preview->size());
|
||||
option.rect.adjust(0, pSize.height() + textMargin, 0, 0);
|
||||
|
||||
return pSize.width();
|
||||
}
|
||||
|
||||
QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const
|
||||
|
@ -61,11 +61,12 @@ signals:
|
||||
void invalidPath(const QString& messageId) const;
|
||||
|
||||
protected:
|
||||
void paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||
void paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||
void paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||
void paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||
int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||
int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||
int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||
int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||
void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const;
|
||||
void paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const;
|
||||
QPushButton* getButton(const Models::FeedItem& data) const;
|
||||
QProgressBar* getBar(const Models::FeedItem& data) const;
|
||||
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
||||
|
Loading…
Reference in New Issue
Block a user