diff --git a/ui/models/messagefeed.cpp b/ui/models/messagefeed.cpp index a5a6b15..78c216f 100644 --- a/ui/models/messagefeed.cpp +++ b/ui/models/messagefeed.cpp @@ -31,6 +31,7 @@ const QHash Models::MessageFeed::roles = { {SentByMe,"sentByMe"}, {Avatar, "avatar"}, {Attach, "attach"}, + {Id, "id"}, {Bulk, "bulk"} }; @@ -94,8 +95,12 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: - case Text: - answer = msg->getBody(); + case Text: { + QString body = msg->getBody(); + if (body != msg->getOutOfBandUrl()) { + answer = body; + } + } break; case Sender: if (sentByMe(*msg)) { @@ -143,13 +148,22 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const case Attach: answer.setValue(fillAttach(*msg)); break; + case Id: + answer.setValue(msg->getId()); + break; case Bulk: { FeedItem item; + item.id = msg->getId(); item.sentByMe = sentByMe(*msg); item.date = msg->getTime(); item.state = msg->getState(); item.correction = msg->getEdited(); - item.text = msg->getBody(); + + QString body = msg->getBody(); + if (body != msg->getOutOfBandUrl()) { + item.text = body; + } + item.avatar.clear(); if (item.sentByMe) { item.sender = rosterItem->getAccountName(); diff --git a/ui/models/messagefeed.h b/ui/models/messagefeed.h index d0e7599..bc403c1 100644 --- a/ui/models/messagefeed.h +++ b/ui/models/messagefeed.h @@ -76,6 +76,7 @@ public: SentByMe, Avatar, Attach, + Id, Bulk }; @@ -146,6 +147,7 @@ struct Attachment { }; struct FeedItem { + QString id; QString text; QString sender; QString avatar; diff --git a/ui/utils/feedview.cpp b/ui/utils/feedview.cpp index 47cbc63..21f2956 100644 --- a/ui/utils/feedview.cpp +++ b/ui/utils/feedview.cpp @@ -23,13 +23,17 @@ #include #include +#include "messagedelegate.h" + constexpr int maxMessageHeight = 10000; constexpr int approximateSingleMessageHeight = 20; FeedView::FeedView(QWidget* parent): QAbstractItemView(parent), hints(), - vo(0) + vo(0), + specialDelegate(false), + clearWidgetsMode(false) { horizontalScrollBar()->setRange(0, 0); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); @@ -163,6 +167,10 @@ void FeedView::updateGeometries() bar->setPageStep(layoutBounds.height()); bar->setValue(previousOffset - layoutBounds.height() - vo); } + + if (specialDelegate) { + clearWidgetsMode = true; + } } 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; QPoint cursor = vp->mapFromGlobal(QCursor::pos()); + if (clearWidgetsMode && specialDelegate) { + MessageDelegate* del = dynamic_cast(itemDelegate()); + del->beginClearWidgets(); + } + for (const QModelIndex& index : toRener) { option.rect = visualRect(index); option.state.setFlag(QStyle::State_MouseOver, option.rect.contains(cursor)); itemDelegate(index)->paint(&painter, option, index); } + + if (clearWidgetsMode && specialDelegate) { + MessageDelegate* del = dynamic_cast(itemDelegate()); + del->endClearWidgets(); + clearWidgetsMode = false; + } } void FeedView::verticalScrollbarValueChanged(int value) { vo = verticalScrollBar()->maximum() - value; + if (specialDelegate) { + clearWidgetsMode = true; + } + QAbstractItemView::verticalScrollbarValueChanged(vo); } @@ -250,3 +273,15 @@ QFont FeedView::getFont() const { return viewOptions().font; } + +void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) +{ + QAbstractItemView::setItemDelegate(delegate); + + MessageDelegate* del = dynamic_cast(delegate); + if (del) { + specialDelegate = true; + } else { + specialDelegate = false; + } +} diff --git a/ui/utils/feedview.h b/ui/utils/feedview.h index 50f46e4..0256a4d 100644 --- a/ui/utils/feedview.h +++ b/ui/utils/feedview.h @@ -42,6 +42,7 @@ public: QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override; void setSelection(const QRect & rect, QItemSelectionModel::SelectionFlags command) override; QRegion visualRegionForSelection(const QItemSelection & selection) const override; + void setItemDelegate(QAbstractItemDelegate* delegate); QFont getFont() const; @@ -69,6 +70,8 @@ private: }; std::deque hints; int vo; + bool specialDelegate; + bool clearWidgetsMode; }; diff --git a/ui/utils/messagedelegate.cpp b/ui/utils/messagedelegate.cpp index 52f9f35..5aebebe 100644 --- a/ui/utils/messagedelegate.cpp +++ b/ui/utils/messagedelegate.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "messagedelegate.h" #include "ui/models/messagefeed.h" @@ -34,23 +35,23 @@ dateFont(), bodyMetrics(bodyFont), nickMetrics(nickFont), dateMetrics(dateFont), -downloadButton(new QPushButton()), -uploadButton(new QPushButton()) +buttonHeight(0), +buttons(new std::map()), +idsToKeep(new std::set()), +clearingWidgets(false) { - downloadButton->setText(tr("Download")); - uploadButton->setText(tr("Retry")); - - //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(); + QPushButton btn; + buttonHeight = btn.sizeHint().height(); } MessageDelegate::~MessageDelegate() { - delete uploadButton; - delete downloadButton; + for (const std::pair& pair: *buttons){ + delete pair.second; + } + + delete idsToKeep; + delete buttons; } 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; - 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() += dateMetrics.height(); if (messageSize.width() < opt.rect.width()) { @@ -111,32 +115,19 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio case Models::downloading: break; 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: - if (data.sentByMe) { - 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); + paintButton(getButton(data), painter, data.sentByMe, opt); break; case Models::ready: break; } painter->restore(); - painter->setFont(bodyFont); - painter->drawText(opt.rect, opt.displayAlignment | Qt::TextWordWrap, data.text, &rect); - - opt.rect.adjust(0, rect.height(), 0, 0); + if (data.text.size() > 0) { + painter->setFont(bodyFont); + painter->drawText(opt.rect, opt.displayAlignment | Qt::TextWordWrap, data.text, &rect); + opt.rect.adjust(0, rect.height(), 0, 0); + } painter->setFont(dateFont); QColor q = painter->pen().color(); 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->restore(); + + if (clearingWidgets) { + idsToKeep->insert(data.id); + } } 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; QVariant va = index.data(Models::MessageFeed::Attach); Models::Attachment attach = qvariant_cast(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) { case Models::none: @@ -163,7 +162,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel break; case Models::remote: case Models::local: - messageSize.rheight() += downloadButton->height(); + messageSize.rheight() += buttonHeight; break; case Models::ready: break; @@ -204,9 +203,88 @@ void MessageDelegate::initializeFonts(const QFont& font) bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { //qDebug() << event->type(); + + 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(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::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 toRemove; + for (const std::pair& 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 // { diff --git a/ui/utils/messagedelegate.h b/ui/utils/messagedelegate.h index 396ff43..b71163c 100644 --- a/ui/utils/messagedelegate.h +++ b/ui/utils/messagedelegate.h @@ -19,13 +19,21 @@ #ifndef MESSAGEDELEGATE_H #define MESSAGEDELEGATE_H +#include +#include + #include +#include #include #include #include #include "shared/icons.h" +namespace Models { + struct FeedItem; +}; + class MessageDelegate : public QStyledItemDelegate { Q_OBJECT @@ -39,8 +47,20 @@ public: void initializeFonts(const QFont& font); 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: + class FeedButton : public QPushButton { + public: + QString messageId; + bool download; + }; + QFont bodyFont; QFont nickFont; QFont dateFont; @@ -48,8 +68,11 @@ private: QFontMetrics nickMetrics; QFontMetrics dateMetrics; - QPushButton* downloadButton; - QPushButton* uploadButton; + int buttonHeight; + + std::map* buttons; + std::set* idsToKeep; + bool clearingWidgets; }; #endif // MESSAGEDELEGATE_H diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index b31b59d..e10058d 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -46,7 +46,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, overlay(new QWidget()), filesToAttach(), feed(new FeedView()), - delegate(new MessageDelegate()), + delegate(new MessageDelegate(this)), scroll(down), manualSliderChange(false), requestingHistory(false),