forked from blue/squawk
link clicking and hovering in message body now works!
This commit is contained in:
parent
eac87e713f
commit
7ba94e9deb
@ -51,7 +51,8 @@ FeedView::FeedView(QWidget* parent):
|
|||||||
progress(),
|
progress(),
|
||||||
dividerFont(),
|
dividerFont(),
|
||||||
dividerMetrics(dividerFont),
|
dividerMetrics(dividerFont),
|
||||||
mousePressed(false)
|
mousePressed(false),
|
||||||
|
anchorHovered(false)
|
||||||
{
|
{
|
||||||
horizontalScrollBar()->setRange(0, 0);
|
horizontalScrollBar()->setRange(0, 0);
|
||||||
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
|
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
|
||||||
@ -408,6 +409,18 @@ void FeedView::verticalScrollbarValueChanged(int value)
|
|||||||
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FeedView::setAnchorHovered(bool hovered)
|
||||||
|
{
|
||||||
|
if (anchorHovered != hovered) {
|
||||||
|
anchorHovered = hovered;
|
||||||
|
if (anchorHovered) {
|
||||||
|
setCursor(Qt::PointingHandCursor);
|
||||||
|
} else {
|
||||||
|
setCursor(Qt::ArrowCursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FeedView::mouseMoveEvent(QMouseEvent* event)
|
void FeedView::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
@ -418,6 +431,22 @@ void FeedView::mouseMoveEvent(QMouseEvent* event)
|
|||||||
//qDebug() << event;
|
//qDebug() << event;
|
||||||
|
|
||||||
QAbstractItemView::mouseMoveEvent(event);
|
QAbstractItemView::mouseMoveEvent(event);
|
||||||
|
|
||||||
|
if (specialDelegate) {
|
||||||
|
QPoint point = event->localPos().toPoint();
|
||||||
|
QModelIndex index = indexAt(point);
|
||||||
|
if (index.isValid()) {
|
||||||
|
QRect rect = visualRect(index);
|
||||||
|
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
||||||
|
if (rect.contains(point)) {
|
||||||
|
setAnchorHovered(del->isAnchorHovered(point, index, rect));
|
||||||
|
} else {
|
||||||
|
setAnchorHovered(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setAnchorHovered(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::mousePressEvent(QMouseEvent* event)
|
void FeedView::mousePressEvent(QMouseEvent* event)
|
||||||
@ -487,6 +516,7 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate)
|
|||||||
elementMargin = MessageDelegate::margin;
|
elementMargin = MessageDelegate::margin;
|
||||||
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
||||||
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
||||||
|
connect(del, &MessageDelegate::openLink, &QDesktopServices::openUrl);
|
||||||
} else {
|
} else {
|
||||||
specialDelegate = false;
|
specialDelegate = false;
|
||||||
elementMargin = 0;
|
elementMargin = 0;
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#define FEEDVIEW_H
|
#define FEEDVIEW_H
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -76,6 +78,7 @@ private:
|
|||||||
bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight);
|
bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight);
|
||||||
void positionProgress();
|
void positionProgress();
|
||||||
void drawDateDevider(int top, const QDateTime& date, QPainter& painter);
|
void drawDateDevider(int top, const QDateTime& date, QPainter& painter);
|
||||||
|
void setAnchorHovered(bool hovered);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Hint {
|
struct Hint {
|
||||||
@ -96,6 +99,7 @@ private:
|
|||||||
QFont dividerFont;
|
QFont dividerFont;
|
||||||
QFontMetrics dividerMetrics;
|
QFontMetrics dividerMetrics;
|
||||||
bool mousePressed;
|
bool mousePressed;
|
||||||
|
bool anchorHovered;
|
||||||
|
|
||||||
static const std::set<int> geometryChangingRoles;
|
static const std::set<int> geometryChangingRoles;
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ MessageDelegate::MessageDelegate(QObject* parent):
|
|||||||
bodyFont(),
|
bodyFont(),
|
||||||
nickFont(),
|
nickFont(),
|
||||||
dateFont(),
|
dateFont(),
|
||||||
bodyMetrics(bodyFont),
|
|
||||||
bodyRenderer(new QTextDocument()),
|
bodyRenderer(new QTextDocument()),
|
||||||
nickMetrics(nickFont),
|
nickMetrics(nickFont),
|
||||||
dateMetrics(dateFont),
|
dateMetrics(dateFont),
|
||||||
@ -55,7 +54,6 @@ MessageDelegate::MessageDelegate(QObject* parent):
|
|||||||
bars(new std::map<QString, QProgressBar*>()),
|
bars(new std::map<QString, QProgressBar*>()),
|
||||||
statusIcons(new std::map<QString, QLabel*>()),
|
statusIcons(new std::map<QString, QLabel*>()),
|
||||||
pencilIcons(new std::map<QString, QLabel*>()),
|
pencilIcons(new std::map<QString, QLabel*>()),
|
||||||
bodies(new std::map<QString, QTextBrowser*>()),
|
|
||||||
previews(new std::map<QString, Preview*>()),
|
previews(new std::map<QString, Preview*>()),
|
||||||
idsToKeep(new std::set<QString>()),
|
idsToKeep(new std::set<QString>()),
|
||||||
clearingWidgets(false)
|
clearingWidgets(false)
|
||||||
@ -88,10 +86,6 @@ MessageDelegate::~MessageDelegate()
|
|||||||
delete pair.second;
|
delete pair.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::pair<const QString, QTextBrowser*>& pair: *bodies){
|
|
||||||
delete pair.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::pair<const QString, Preview*>& pair: *previews){
|
for (const std::pair<const QString, Preview*>& pair: *previews){
|
||||||
delete pair.second;
|
delete pair.second;
|
||||||
}
|
}
|
||||||
@ -101,7 +95,6 @@ MessageDelegate::~MessageDelegate()
|
|||||||
delete idsToKeep;
|
delete idsToKeep;
|
||||||
delete buttons;
|
delete buttons;
|
||||||
delete bars;
|
delete bars;
|
||||||
delete bodies;
|
|
||||||
delete previews;
|
delete previews;
|
||||||
delete bodyRenderer;
|
delete bodyRenderer;
|
||||||
}
|
}
|
||||||
@ -366,11 +359,8 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
|
|||||||
return messageSize;
|
return messageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const
|
QRect MessageDelegate::getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const
|
||||||
{
|
{
|
||||||
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
|
||||||
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
|
||||||
if (data.text.size() > 0) {
|
|
||||||
QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2);
|
QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2);
|
||||||
if (needToDrawSender(index, data)) {
|
if (needToDrawSender(index, data)) {
|
||||||
localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0);
|
localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0);
|
||||||
@ -411,21 +401,41 @@ void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, c
|
|||||||
int bottomSize = std::max(dateMetrics.lineSpacing(), statusIconSize);
|
int bottomSize = std::max(dateMetrics.lineSpacing(), statusIconSize);
|
||||||
localHint.adjust(0, attachHeight, 0, -(bottomSize + textMargin));
|
localHint.adjust(0, attachHeight, 0, -(bottomSize + textMargin));
|
||||||
|
|
||||||
|
return localHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const
|
||||||
|
{
|
||||||
|
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
||||||
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint);
|
||||||
|
|
||||||
if (localHint.contains(point)) {
|
if (localHint.contains(point)) {
|
||||||
qDebug() << "MESSAGE CLICKED";
|
|
||||||
QPoint translated = point - localHint.topLeft();
|
QPoint translated = point - localHint.topLeft();
|
||||||
|
|
||||||
bodyRenderer->setPlainText(data.text);
|
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
||||||
bodyRenderer->setTextWidth(localHint.size().width());
|
bodyRenderer->setTextWidth(localHint.size().width());
|
||||||
|
|
||||||
int pos = bodyRenderer->documentLayout()->hitTest(translated, Qt::FuzzyHit);
|
return bodyRenderer->documentLayout()->anchorAt(translated);
|
||||||
QTextBlock block = bodyRenderer->findBlock(pos);
|
|
||||||
QString text = block.text();
|
|
||||||
if (text.size() > 0) {
|
|
||||||
qDebug() << text;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const
|
||||||
|
{
|
||||||
|
QString anchor = getAnchor(point, index, sizeHint);
|
||||||
|
if (anchor.size() > 0) {
|
||||||
|
emit openLink(anchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageDelegate::isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const
|
||||||
|
{
|
||||||
|
QString anchor = getAnchor(point, index, sizeHint);
|
||||||
|
return anchor.size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDelegate::initializeFonts(const QFont& font)
|
void MessageDelegate::initializeFonts(const QFont& font)
|
||||||
@ -452,7 +462,6 @@ void MessageDelegate::initializeFonts(const QFont& font)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bodyFont.setKerning(false);
|
bodyFont.setKerning(false);
|
||||||
bodyMetrics = QFontMetrics(bodyFont);
|
|
||||||
nickMetrics = QFontMetrics(nickFont);
|
nickMetrics = QFontMetrics(nickFont);
|
||||||
dateMetrics = QFontMetrics(dateFont);
|
dateMetrics = QFontMetrics(dateFont);
|
||||||
|
|
||||||
@ -636,36 +645,6 @@ QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextBrowser * MessageDelegate::getBody(const Models::FeedItem& data) const
|
|
||||||
{
|
|
||||||
std::map<QString, QTextBrowser*>::const_iterator itr = bodies->find(data.id);
|
|
||||||
QTextBrowser* result = 0;
|
|
||||||
|
|
||||||
if (itr != bodies->end()) {
|
|
||||||
result = itr->second;
|
|
||||||
} else {
|
|
||||||
result = new QTextBrowser();
|
|
||||||
result->setFont(bodyFont);
|
|
||||||
result->setContextMenuPolicy(Qt::NoContextMenu);
|
|
||||||
result->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
result->setContentsMargins(0, 0, 0, 0);
|
|
||||||
//result->viewport()->setAutoFillBackground(false);
|
|
||||||
result->document()->setDocumentMargin(0);
|
|
||||||
result->setFrameStyle(0);
|
|
||||||
result->setLineWidth(0);
|
|
||||||
//result->setAutoFillBackground(false);
|
|
||||||
//->setWordWrap(true);
|
|
||||||
result->setOpenExternalLinks(true);
|
|
||||||
//result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
|
||||||
result->setOpenExternalLinks(true);
|
|
||||||
bodies->insert(std::make_pair(data.id, result));
|
|
||||||
}
|
|
||||||
|
|
||||||
result->setHtml(Shared::processMessageBody(data.text));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void removeElements(std::map<QString, T*>* elements, std::set<QString>* idsToKeep) {
|
void removeElements(std::map<QString, T*>* elements, std::set<QString>* idsToKeep) {
|
||||||
std::set<QString> toRemove;
|
std::set<QString> toRemove;
|
||||||
@ -691,26 +670,6 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter,
|
|||||||
painter->restore();
|
painter->restore();
|
||||||
QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height()));
|
QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height()));
|
||||||
|
|
||||||
/*
|
|
||||||
QTextBrowser* editor = nullptr;
|
|
||||||
if (option.state.testFlag(QStyle::State_MouseOver)) {
|
|
||||||
std::set<QString> ids({data.id});
|
|
||||||
removeElements(bodies, &ids);
|
|
||||||
editor = getBody(data);
|
|
||||||
editor->setParent(static_cast<QWidget*>(painter->device()));
|
|
||||||
} else {
|
|
||||||
std::map<QString, QTextBrowser*>::const_iterator itr = bodies->find(data.id);
|
|
||||||
if (itr != bodies->end()) {
|
|
||||||
editor = itr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (editor != nullptr) {
|
|
||||||
editor->setMinimumSize(bodySize);
|
|
||||||
editor->setMaximumSize(bodySize);
|
|
||||||
editor->move(option.rect.left(), option.rect.y());
|
|
||||||
editor->show();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
option.rect.adjust(0, bodySize.height() + textMargin, 0, 0);
|
option.rect.adjust(0, bodySize.height() + textMargin, 0, 0);
|
||||||
return bodySize.width();
|
return bodySize.width();
|
||||||
}
|
}
|
||||||
@ -723,8 +682,6 @@ void MessageDelegate::beginClearWidgets()
|
|||||||
clearingWidgets = true;
|
clearingWidgets = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void MessageDelegate::endClearWidgets()
|
void MessageDelegate::endClearWidgets()
|
||||||
{
|
{
|
||||||
if (clearingWidgets) {
|
if (clearingWidgets) {
|
||||||
@ -732,7 +689,6 @@ void MessageDelegate::endClearWidgets()
|
|||||||
removeElements(bars, idsToKeep);
|
removeElements(bars, idsToKeep);
|
||||||
removeElements(statusIcons, idsToKeep);
|
removeElements(statusIcons, idsToKeep);
|
||||||
removeElements(pencilIcons, idsToKeep);
|
removeElements(pencilIcons, idsToKeep);
|
||||||
removeElements(bodies, idsToKeep);
|
|
||||||
removeElements(previews, idsToKeep);
|
removeElements(previews, idsToKeep);
|
||||||
|
|
||||||
idsToKeep->clear();
|
idsToKeep->clear();
|
||||||
@ -760,8 +716,3 @@ void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// void MessageDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
@ -29,8 +29,6 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QTextEdit>
|
|
||||||
#include <QTextBrowser>
|
|
||||||
|
|
||||||
#include "shared/icons.h"
|
#include "shared/icons.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
@ -59,6 +57,7 @@ public:
|
|||||||
void endClearWidgets();
|
void endClearWidgets();
|
||||||
void beginClearWidgets();
|
void beginClearWidgets();
|
||||||
void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
||||||
|
bool isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
||||||
|
|
||||||
static int avatarHeight;
|
static int avatarHeight;
|
||||||
static int margin;
|
static int margin;
|
||||||
@ -66,6 +65,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void buttonPushed(const QString& messageId) const;
|
void buttonPushed(const QString& messageId) const;
|
||||||
void invalidPath(const QString& messageId) const;
|
void invalidPath(const QString& messageId) const;
|
||||||
|
void openLink(const QString& href) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||||
@ -80,12 +80,14 @@ protected:
|
|||||||
QProgressBar* getBar(const Models::FeedItem& data) const;
|
QProgressBar* getBar(const Models::FeedItem& data) const;
|
||||||
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
||||||
QLabel* getPencilIcon(const Models::FeedItem& data) const;
|
QLabel* getPencilIcon(const Models::FeedItem& data) const;
|
||||||
QTextBrowser* getBody(const Models::FeedItem& data) const;
|
|
||||||
void clearHelperWidget(const Models::FeedItem& data) const;
|
void clearHelperWidget(const Models::FeedItem& data) const;
|
||||||
|
|
||||||
bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const;
|
bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const;
|
||||||
bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const;
|
bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const;
|
||||||
|
|
||||||
|
QRect getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const;
|
||||||
|
QString getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onButtonPushed() const;
|
void onButtonPushed() const;
|
||||||
|
|
||||||
@ -98,7 +100,6 @@ private:
|
|||||||
QFont bodyFont;
|
QFont bodyFont;
|
||||||
QFont nickFont;
|
QFont nickFont;
|
||||||
QFont dateFont;
|
QFont dateFont;
|
||||||
QFontMetrics bodyMetrics;
|
|
||||||
QTextDocument* bodyRenderer;
|
QTextDocument* bodyRenderer;
|
||||||
QFontMetrics nickMetrics;
|
QFontMetrics nickMetrics;
|
||||||
QFontMetrics dateMetrics;
|
QFontMetrics dateMetrics;
|
||||||
@ -111,7 +112,6 @@ private:
|
|||||||
std::map<QString, QProgressBar*>* bars;
|
std::map<QString, QProgressBar*>* bars;
|
||||||
std::map<QString, QLabel*>* statusIcons;
|
std::map<QString, QLabel*>* statusIcons;
|
||||||
std::map<QString, QLabel*>* pencilIcons;
|
std::map<QString, QLabel*>* pencilIcons;
|
||||||
std::map<QString, QTextBrowser*>* bodies;
|
|
||||||
std::map<QString, Preview*>* previews;
|
std::map<QString, Preview*>* previews;
|
||||||
std::set<QString>* idsToKeep;
|
std::set<QString>* idsToKeep;
|
||||||
bool clearingWidgets;
|
bool clearingWidgets;
|
||||||
|
Loading…
Reference in New Issue
Block a user