From 3c48577eeed23e5f21af70c0ae90695e91893ac7 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 2 May 2022 22:25:50 +0300 Subject: [PATCH] selection message body now actually working --- shared/utils.h | 6 ++ ui/widgets/conversation.cpp | 10 +++ ui/widgets/messageline/feedview.cpp | 92 ++++++++++++++++------ ui/widgets/messageline/feedview.h | 8 +- ui/widgets/messageline/messagedelegate.cpp | 54 ++++++++++--- ui/widgets/messageline/messagedelegate.h | 4 +- ui/widgets/messageline/messagefeed.h | 4 +- 7 files changed, 138 insertions(+), 40 deletions(-) diff --git a/shared/utils.h b/shared/utils.h index 564e2e6..0329cee 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -69,6 +69,12 @@ static const std::vector colorPalette = { QColor(17, 17, 80), QColor(54, 54, 94) }; + +enum class Hover { + nothing, + text, + anchor +}; } #endif // SHARED_UTILS_H diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 70a468c..b2c7a5f 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -499,6 +499,16 @@ void Conversation::onFeedContext(const QPoint& pos) }); } + QString selected = feed->getSelectedText(); + if (selected.size() > 0) { + showMenu = true; + QAction* copy = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected")); + connect(copy, &QAction::triggered, [selected] () { + QClipboard* cb = QApplication::clipboard(); + cb->setText(selected); + }); + } + QString body = item->getBody(); if (body.size() > 0) { showMenu = true; diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index f467f43..353d851 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include "messagedelegate.h" @@ -53,9 +55,10 @@ FeedView::FeedView(QWidget* parent): dividerMetrics(dividerFont), mousePressed(false), dragging(false), - anchorHovered(false), + hovered(Shared::Hover::nothing), dragStartPoint(), - dragEndPoint() + dragEndPoint(), + selectedText() { horizontalScrollBar()->setRange(0, 0); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); @@ -167,7 +170,7 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom void FeedView::updateGeometries() { - qDebug() << "updateGeometries"; + //qDebug() << "updateGeometries"; QScrollBar* bar = verticalScrollBar(); const QStyle* st = style(); @@ -307,7 +310,7 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt void FeedView::paintEvent(QPaintEvent* event) { - qDebug() << "paint" << event->rect(); + //qDebug() << "paint" << event->rect(); const QAbstractItemModel* m = model(); QWidget* vp = viewport(); QRect zone = event->rect().translated(0, -vo); @@ -412,14 +415,20 @@ void FeedView::verticalScrollbarValueChanged(int value) QAbstractItemView::verticalScrollbarValueChanged(vo); } -void FeedView::setAnchorHovered(bool hovered) +void FeedView::setAnchorHovered(Shared::Hover type) { - if (anchorHovered != hovered) { - anchorHovered = hovered; - if (anchorHovered) { - setCursor(Qt::PointingHandCursor); - } else { - setCursor(Qt::ArrowCursor); + if (hovered != type) { + hovered = type; + switch (hovered) { + case Shared::Hover::nothing: + setCursor(Qt::ArrowCursor); + break; + case Shared::Hover::text: + setCursor(Qt::IBeamCursor); + break; + case Shared::Hover::anchor: + setCursor(Qt::PointingHandCursor); + break; } } } @@ -441,25 +450,31 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) QAbstractItemView::mouseMoveEvent(event); if (specialDelegate) { - QModelIndex index = indexAt(dragEndPoint); - if (index.isValid()) { - QRect rect = visualRect(index); - if (rect.contains(dragEndPoint)) { - MessageDelegate* del = static_cast(itemDelegate()); - if (dragging) { - setAnchorHovered(false); - if (del->mouseDrag(dragStartPoint, dragEndPoint, index, rect)) { - qDebug() << "asking to repaint" << rect; + MessageDelegate* del = static_cast(itemDelegate()); + if (dragging) { + QModelIndex index = indexAt(dragStartPoint); + if (index.isValid()) { + QRect rect = visualRect(index); + if (rect.contains(dragStartPoint)) { + QString selected = del->mouseDrag(dragStartPoint, dragEndPoint, index, rect); + if (selectedText != selected) { + selectedText = selected; setDirtyRegion(rect); } - } else { - setAnchorHovered(del->isAnchorHovered(dragEndPoint, index, rect)); } - } else { - setAnchorHovered(false); } } else { - setAnchorHovered(false); + QModelIndex index = indexAt(dragEndPoint); + if (index.isValid()) { + QRect rect = visualRect(index); + if (rect.contains(dragEndPoint)) { + setAnchorHovered(del->hoverType(dragEndPoint, index, rect)); + } else { + setAnchorHovered(Shared::Hover::nothing); + } + } else { + setAnchorHovered(Shared::Hover::nothing); + } } } } @@ -470,6 +485,17 @@ void FeedView::mousePressEvent(QMouseEvent* event) mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { dragStartPoint = event->localPos().toPoint(); + if (specialDelegate && specialModel) { + MessageDelegate* del = static_cast(itemDelegate()); + QString lastSelectedId = del->clearSelection(); + if (lastSelectedId.size()) { + Models::MessageFeed* feed = static_cast(model()); + QModelIndex index = feed->modelIndexById(lastSelectedId); + if (index.isValid()) { + setDirtyRegion(visualRect(index)); + } + } + } } } @@ -494,6 +520,17 @@ void FeedView::mouseReleaseEvent(QMouseEvent* event) } } +void FeedView::keyPressEvent(QKeyEvent* event) +{ + QKeyEvent *key_event = static_cast(event); + if (key_event->matches(QKeySequence::Copy)) { + if (selectedText.size() > 0) { + QClipboard* cb = QApplication::clipboard(); + cb->setText(selectedText); + } + } +} + void FeedView::resizeEvent(QResizeEvent* event) { QAbstractItemView::resizeEvent(event); @@ -603,3 +640,8 @@ void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state) scheduleDelayedItemsLayout(); } } + +QString FeedView::getSelectedText() const +{ + return selectedText; +} diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index c0d6254..d0763a5 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -28,6 +28,7 @@ #include #include +#include /** * @todo write docs @@ -50,6 +51,7 @@ public: void setModel(QAbstractItemModel * model) override; QFont getFont() const; + QString getSelectedText() const; signals: void resized(); @@ -72,13 +74,14 @@ protected: void mouseMoveEvent(QMouseEvent * event) override; void mousePressEvent(QMouseEvent * event) override; void mouseReleaseEvent(QMouseEvent * event) override; + void keyPressEvent(QKeyEvent * event) override; void resizeEvent(QResizeEvent * event) override; private: bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight); void positionProgress(); void drawDateDevider(int top, const QDateTime& date, QPainter& painter); - void setAnchorHovered(bool hovered); + void setAnchorHovered(Shared::Hover type); private: struct Hint { @@ -100,9 +103,10 @@ private: QFontMetrics dividerMetrics; bool mousePressed; bool dragging; - bool anchorHovered; + Shared::Hover hovered; QPoint dragStartPoint; QPoint dragEndPoint; + QString selectedText; static const std::set geometryChangingRoles; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 197248a..840ef5c 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -434,13 +434,37 @@ void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, c } } -bool MessageDelegate::isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const +Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { - QString anchor = getAnchor(point, index, sizeHint); - return anchor.size() > 0; + QVariant vi = index.data(Models::MessageFeed::Bulk); + Models::FeedItem data = qvariant_cast(vi); + if (data.text.size() > 0) { + QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); + + if (localHint.contains(point)) { + QPoint translated = point - localHint.topLeft(); + + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); + bodyRenderer->setTextWidth(localHint.size().width()); + + QAbstractTextDocumentLayout* lay = bodyRenderer->documentLayout(); + QString anchor = lay->anchorAt(translated); + + if (anchor.size() > 0) { + return Shared::Hover::anchor; + } else { + int position = lay->hitTest(translated, Qt::HitTestAccuracy::ExactHit); + if (position != -1) { //this is a bad way, it's false positive on the end of the last + return Shared::Hover::text; //line of a multiline block, so it's not better the checking the rect + } + //return Shared::Hover::text; + } + } + } + return Shared::Hover::nothing; } -bool MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) +QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) { QVariant vi = index.data(Models::MessageFeed::Bulk); Models::FeedItem data = qvariant_cast(vi); @@ -448,19 +472,31 @@ bool MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QM QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); if (localHint.contains(start)) { - QPoint translated = start - localHint.topLeft(); + QPoint tl = localHint.topLeft(); + QPoint first = start - tl; + QPoint last = end - tl; + last.setX(std::max(last.x(), 0)); + last.setX(std::min(last.x(), localHint.width() - 1)); + last.setY(std::max(last.y(), 0)); + last.setY(std::min(last.y(), localHint.height())); + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); bodyRenderer->setTextWidth(localHint.size().width()); - selection.first = bodyRenderer->documentLayout()->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit); - selection.second = bodyRenderer->documentLayout()->hitTest(end - localHint.topLeft(), Qt::HitTestAccuracy::FuzzyHit); + selection.first = bodyRenderer->documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit); + selection.second = bodyRenderer->documentLayout()->hitTest(last, Qt::HitTestAccuracy::FuzzyHit); currentId = data.id; - return true; + if (selection.first != selection.second) { + QTextCursor cursor(bodyRenderer); + cursor.setPosition(selection.first, QTextCursor::MoveAnchor); + cursor.setPosition(selection.second, QTextCursor::KeepAnchor); + return cursor.selectedText(); + } } } - return false; + return ""; } QString MessageDelegate::clearSelection() diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 2aea240..dc0fb49 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -58,8 +58,8 @@ public: void endClearWidgets(); void beginClearWidgets(); void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - bool isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - bool mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); + Shared::Hover hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; + QString mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); QString clearSelection(); static int avatarHeight; diff --git a/ui/widgets/messageline/messagefeed.h b/ui/widgets/messageline/messagefeed.h index f362989..db174d2 100644 --- a/ui/widgets/messageline/messagefeed.h +++ b/ui/widgets/messageline/messagefeed.h @@ -57,6 +57,8 @@ public: void changeMessage(const QString& id, const QMap& data); void removeMessage(const QString& id); Shared::Message getMessage(const QString& id); + QModelIndex modelIndexById(const QString& id) const; + QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -126,8 +128,6 @@ protected: bool sentByMe(const Shared::Message& msg) const; Attachment fillAttach(const Shared::Message& msg) const; Edition fillCorrection(const Shared::Message& msg) const; - QModelIndex modelIndexById(const QString& id) const; - QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const; std::set detectChanges(const Shared::Message& msg, const QMap& data) const; private: