1
0
forked from blue/squawk

seems like i have found solution how to properly render buttons

This commit is contained in:
Blue 2021-02-02 01:55:15 +03:00
parent 00ffbac6b0
commit b3c6860e25
7 changed files with 196 additions and 41 deletions

View File

@ -31,6 +31,7 @@ const QHash<int, QByteArray> Models::MessageFeed::roles = {
{SentByMe,"sentByMe"}, {SentByMe,"sentByMe"},
{Avatar, "avatar"}, {Avatar, "avatar"},
{Attach, "attach"}, {Attach, "attach"},
{Id, "id"},
{Bulk, "bulk"} {Bulk, "bulk"}
}; };
@ -94,8 +95,12 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
case Text: case Text: {
answer = msg->getBody(); QString body = msg->getBody();
if (body != msg->getOutOfBandUrl()) {
answer = body;
}
}
break; break;
case Sender: case Sender:
if (sentByMe(*msg)) { if (sentByMe(*msg)) {
@ -143,13 +148,22 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
case Attach: case Attach:
answer.setValue(fillAttach(*msg)); answer.setValue(fillAttach(*msg));
break; break;
case Id:
answer.setValue(msg->getId());
break;
case Bulk: { case Bulk: {
FeedItem item; FeedItem item;
item.id = msg->getId();
item.sentByMe = sentByMe(*msg); item.sentByMe = sentByMe(*msg);
item.date = msg->getTime(); item.date = msg->getTime();
item.state = msg->getState(); item.state = msg->getState();
item.correction = msg->getEdited(); item.correction = msg->getEdited();
item.text = msg->getBody();
QString body = msg->getBody();
if (body != msg->getOutOfBandUrl()) {
item.text = body;
}
item.avatar.clear(); item.avatar.clear();
if (item.sentByMe) { if (item.sentByMe) {
item.sender = rosterItem->getAccountName(); item.sender = rosterItem->getAccountName();

View File

@ -76,6 +76,7 @@ public:
SentByMe, SentByMe,
Avatar, Avatar,
Attach, Attach,
Id,
Bulk Bulk
}; };
@ -146,6 +147,7 @@ struct Attachment {
}; };
struct FeedItem { struct FeedItem {
QString id;
QString text; QString text;
QString sender; QString sender;
QString avatar; QString avatar;

View File

@ -23,13 +23,17 @@
#include <QScrollBar> #include <QScrollBar>
#include <QDebug> #include <QDebug>
#include "messagedelegate.h"
constexpr int maxMessageHeight = 10000; constexpr int maxMessageHeight = 10000;
constexpr int approximateSingleMessageHeight = 20; constexpr int approximateSingleMessageHeight = 20;
FeedView::FeedView(QWidget* parent): FeedView::FeedView(QWidget* parent):
QAbstractItemView(parent), QAbstractItemView(parent),
hints(), hints(),
vo(0) vo(0),
specialDelegate(false),
clearWidgetsMode(false)
{ {
horizontalScrollBar()->setRange(0, 0); horizontalScrollBar()->setRange(0, 0);
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
@ -163,6 +167,10 @@ void FeedView::updateGeometries()
bar->setPageStep(layoutBounds.height()); bar->setPageStep(layoutBounds.height());
bar->setValue(previousOffset - layoutBounds.height() - vo); bar->setValue(previousOffset - layoutBounds.height() - vo);
} }
if (specialDelegate) {
clearWidgetsMode = true;
}
} }
bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight)
@ -222,17 +230,32 @@ void FeedView::paintEvent(QPaintEvent* event)
option.features = QStyleOptionViewItem::WrapText; option.features = QStyleOptionViewItem::WrapText;
QPoint cursor = vp->mapFromGlobal(QCursor::pos()); QPoint cursor = vp->mapFromGlobal(QCursor::pos());
if (clearWidgetsMode && specialDelegate) {
MessageDelegate* del = dynamic_cast<MessageDelegate*>(itemDelegate());
del->beginClearWidgets();
}
for (const QModelIndex& index : toRener) { for (const QModelIndex& index : toRener) {
option.rect = visualRect(index); option.rect = visualRect(index);
option.state.setFlag(QStyle::State_MouseOver, option.rect.contains(cursor)); option.state.setFlag(QStyle::State_MouseOver, option.rect.contains(cursor));
itemDelegate(index)->paint(&painter, option, index); itemDelegate(index)->paint(&painter, option, index);
} }
if (clearWidgetsMode && specialDelegate) {
MessageDelegate* del = dynamic_cast<MessageDelegate*>(itemDelegate());
del->endClearWidgets();
clearWidgetsMode = false;
}
} }
void FeedView::verticalScrollbarValueChanged(int value) void FeedView::verticalScrollbarValueChanged(int value)
{ {
vo = verticalScrollBar()->maximum() - value; vo = verticalScrollBar()->maximum() - value;
if (specialDelegate) {
clearWidgetsMode = true;
}
QAbstractItemView::verticalScrollbarValueChanged(vo); QAbstractItemView::verticalScrollbarValueChanged(vo);
} }
@ -250,3 +273,15 @@ QFont FeedView::getFont() const
{ {
return viewOptions().font; return viewOptions().font;
} }
void FeedView::setItemDelegate(QAbstractItemDelegate* delegate)
{
QAbstractItemView::setItemDelegate(delegate);
MessageDelegate* del = dynamic_cast<MessageDelegate*>(delegate);
if (del) {
specialDelegate = true;
} else {
specialDelegate = false;
}
}

View File

@ -42,6 +42,7 @@ public:
QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override; QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
void setSelection(const QRect & rect, QItemSelectionModel::SelectionFlags command) override; void setSelection(const QRect & rect, QItemSelectionModel::SelectionFlags command) override;
QRegion visualRegionForSelection(const QItemSelection & selection) const override; QRegion visualRegionForSelection(const QItemSelection & selection) const override;
void setItemDelegate(QAbstractItemDelegate* delegate);
QFont getFont() const; QFont getFont() const;
@ -69,6 +70,8 @@ private:
}; };
std::deque<Hint> hints; std::deque<Hint> hints;
int vo; int vo;
bool specialDelegate;
bool clearWidgetsMode;
}; };

View File

@ -19,6 +19,7 @@
#include <QDebug> #include <QDebug>
#include <QPainter> #include <QPainter>
#include <QApplication> #include <QApplication>
#include <QMouseEvent>
#include "messagedelegate.h" #include "messagedelegate.h"
#include "ui/models/messagefeed.h" #include "ui/models/messagefeed.h"
@ -34,23 +35,23 @@ dateFont(),
bodyMetrics(bodyFont), bodyMetrics(bodyFont),
nickMetrics(nickFont), nickMetrics(nickFont),
dateMetrics(dateFont), dateMetrics(dateFont),
downloadButton(new QPushButton()), buttonHeight(0),
uploadButton(new QPushButton()) buttons(new std::map<QString, FeedButton*>()),
idsToKeep(new std::set<QString>()),
clearingWidgets(false)
{ {
downloadButton->setText(tr("Download")); QPushButton btn;
uploadButton->setText(tr("Retry")); buttonHeight = btn.sizeHint().height();
//this is done for the buttons to calculate their acual size for we are going to use them further
downloadButton->setAttribute(Qt::WA_DontShowOnScreen);
uploadButton->setAttribute(Qt::WA_DontShowOnScreen);
downloadButton->show();
uploadButton->show();
} }
MessageDelegate::~MessageDelegate() MessageDelegate::~MessageDelegate()
{ {
delete uploadButton; for (const std::pair<const QString, FeedButton*>& pair: *buttons){
delete downloadButton; delete pair.second;
}
delete idsToKeep;
delete buttons;
} }
void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
@ -85,7 +86,10 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
} }
opt.rect = messageRect; opt.rect = messageRect;
QSize messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); QSize messageSize(0, 0);
if (data.text.size() > 0) {
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size();
}
messageSize.rheight() += nickMetrics.lineSpacing(); messageSize.rheight() += nickMetrics.lineSpacing();
messageSize.rheight() += dateMetrics.height(); messageSize.rheight() += dateMetrics.height();
if (messageSize.width() < opt.rect.width()) { if (messageSize.width() < opt.rect.width()) {
@ -111,32 +115,19 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
case Models::downloading: case Models::downloading:
break; break;
case Models::remote: case Models::remote:
if (data.sentByMe) {
painter->translate(option.rect.width() - avatarHeight - margin * 2 - downloadButton->width(), opt.rect.top());
} else {
painter->translate(opt.rect.topLeft());
}
downloadButton->render(painter, QPoint(), QRegion(), QWidget::DrawChildren);
opt.rect.adjust(0, downloadButton->height(), 0, 0);
break;
case Models::local: case Models::local:
if (data.sentByMe) { paintButton(getButton(data), painter, data.sentByMe, opt);
painter->translate(option.rect.width() - avatarHeight - margin * 2 - uploadButton->width(), opt.rect.top());
} else {
painter->translate(opt.rect.topLeft());
}
uploadButton->render(painter, QPoint(), QRegion(), QWidget::DrawChildren);
opt.rect.adjust(0, uploadButton->height(), 0, 0);
break; break;
case Models::ready: case Models::ready:
break; break;
} }
painter->restore(); painter->restore();
if (data.text.size() > 0) {
painter->setFont(bodyFont); painter->setFont(bodyFont);
painter->drawText(opt.rect, opt.displayAlignment | Qt::TextWordWrap, data.text, &rect); painter->drawText(opt.rect, opt.displayAlignment | Qt::TextWordWrap, data.text, &rect);
opt.rect.adjust(0, rect.height(), 0, 0); opt.rect.adjust(0, rect.height(), 0, 0);
}
painter->setFont(dateFont); painter->setFont(dateFont);
QColor q = painter->pen().color(); QColor q = painter->pen().color();
q.setAlpha(180); q.setAlpha(180);
@ -144,6 +135,10 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
painter->drawText(opt.rect, opt.displayAlignment, data.date.toLocalTime().toString(), &rect); painter->drawText(opt.rect, opt.displayAlignment, data.date.toLocalTime().toString(), &rect);
painter->restore(); painter->restore();
if (clearingWidgets) {
idsToKeep->insert(data.id);
}
} }
QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
@ -153,7 +148,11 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
opt.rect = messageRect; opt.rect = messageRect;
QVariant va = index.data(Models::MessageFeed::Attach); QVariant va = index.data(Models::MessageFeed::Attach);
Models::Attachment attach = qvariant_cast<Models::Attachment>(va); Models::Attachment attach = qvariant_cast<Models::Attachment>(va);
QSize messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, index.data(Models::MessageFeed::Text).toString()).size(); QString body = index.data(Models::MessageFeed::Text).toString();
QSize messageSize(0, 0);
if (body.size() > 0) {
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, body).size();
}
switch (attach.state) { switch (attach.state) {
case Models::none: case Models::none:
@ -163,7 +162,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
break; break;
case Models::remote: case Models::remote:
case Models::local: case Models::local:
messageSize.rheight() += downloadButton->height(); messageSize.rheight() += buttonHeight;
break; break;
case Models::ready: case Models::ready:
break; break;
@ -204,9 +203,88 @@ void MessageDelegate::initializeFonts(const QFont& font)
bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{ {
//qDebug() << event->type(); //qDebug() << event->type();
return QStyledItemDelegate::editorEvent(event, model, option, index); return QStyledItemDelegate::editorEvent(event, model, option, index);
} }
void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
{
QPoint start;
if (sentByMe) {
start = {option.rect.width() - btn->width(), option.rect.top()};
} else {
start = option.rect.topLeft();
}
QWidget* vp = static_cast<QWidget*>(painter->device());
btn->setParent(vp);
btn->move(start);
btn->show();
option.rect.adjust(0, buttonHeight, 0, 0);
}
QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const
{
std::map<QString, FeedButton*>::const_iterator itr = buttons->find(data.id);
FeedButton* result = 0;
if (itr != buttons->end()) {
if (
(data.attach.state == Models::remote && itr->second->download) ||
(data.attach.state == Models::local && !itr->second->download)
) {
result = itr->second;
} else {
delete itr->second;
buttons->erase(itr);
}
}
if (result == 0) {
result = new FeedButton();
result->messageId = data.id;
if (data.attach.state == Models::remote) {
result->setText(QCoreApplication::translate("MessageLine", "Download"));
result->download = true;
} else {
result->setText(QCoreApplication::translate("MessageLine", "Upload"));
result->download = false;
}
buttons->insert(std::make_pair(data.id, result));
}
return result;
}
void MessageDelegate::beginClearWidgets()
{
idsToKeep->clear();
clearingWidgets = true;
}
void MessageDelegate::endClearWidgets()
{
if (clearingWidgets) {
std::set<QString> toRemove;
for (const std::pair<const QString, FeedButton*>& pair: *buttons){
if (idsToKeep->find(pair.first) == idsToKeep->end()) {
delete pair.second;
toRemove.insert(pair.first);
}
}
for (const QString& key : toRemove) {
buttons->erase(key);
}
idsToKeep->clear();
clearingWidgets = false;
}
}
// void MessageDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const // void MessageDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
// { // {

View File

@ -19,13 +19,21 @@
#ifndef MESSAGEDELEGATE_H #ifndef MESSAGEDELEGATE_H
#define MESSAGEDELEGATE_H #define MESSAGEDELEGATE_H
#include <map>
#include <set>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QStyleOptionButton>
#include <QFont> #include <QFont>
#include <QFontMetrics> #include <QFontMetrics>
#include <QPushButton> #include <QPushButton>
#include "shared/icons.h" #include "shared/icons.h"
namespace Models {
struct FeedItem;
};
class MessageDelegate : public QStyledItemDelegate class MessageDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
@ -39,8 +47,20 @@ public:
void initializeFonts(const QFont& font); void initializeFonts(const QFont& font);
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
void endClearWidgets();
void beginClearWidgets();
protected:
void paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
QPushButton* getButton(const Models::FeedItem& data) const;
private: private:
class FeedButton : public QPushButton {
public:
QString messageId;
bool download;
};
QFont bodyFont; QFont bodyFont;
QFont nickFont; QFont nickFont;
QFont dateFont; QFont dateFont;
@ -48,8 +68,11 @@ private:
QFontMetrics nickMetrics; QFontMetrics nickMetrics;
QFontMetrics dateMetrics; QFontMetrics dateMetrics;
QPushButton* downloadButton; int buttonHeight;
QPushButton* uploadButton;
std::map<QString, FeedButton*>* buttons;
std::set<QString>* idsToKeep;
bool clearingWidgets;
}; };
#endif // MESSAGEDELEGATE_H #endif // MESSAGEDELEGATE_H

View File

@ -46,7 +46,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
overlay(new QWidget()), overlay(new QWidget()),
filesToAttach(), filesToAttach(),
feed(new FeedView()), feed(new FeedView()),
delegate(new MessageDelegate()), delegate(new MessageDelegate(this)),
scroll(down), scroll(down),
manualSliderChange(false), manualSliderChange(false),
requestingHistory(false), requestingHistory(false),