selection message body now actually working

This commit is contained in:
Blue 2022-05-02 22:25:50 +03:00
parent 0340db7f2f
commit 3c48577eee
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
7 changed files with 138 additions and 40 deletions

View File

@ -69,6 +69,12 @@ static const std::vector<QColor> colorPalette = {
QColor(17, 17, 80), QColor(17, 17, 80),
QColor(54, 54, 94) QColor(54, 54, 94)
}; };
enum class Hover {
nothing,
text,
anchor
};
} }
#endif // SHARED_UTILS_H #endif // SHARED_UTILS_H

View File

@ -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(); QString body = item->getBody();
if (body.size() > 0) { if (body.size() > 0) {
showMenu = true; showMenu = true;

View File

@ -21,6 +21,8 @@
#include <QPaintEvent> #include <QPaintEvent>
#include <QPainter> #include <QPainter>
#include <QScrollBar> #include <QScrollBar>
#include <QApplication>
#include <QClipboard>
#include <QDebug> #include <QDebug>
#include "messagedelegate.h" #include "messagedelegate.h"
@ -53,9 +55,10 @@ FeedView::FeedView(QWidget* parent):
dividerMetrics(dividerFont), dividerMetrics(dividerFont),
mousePressed(false), mousePressed(false),
dragging(false), dragging(false),
anchorHovered(false), hovered(Shared::Hover::nothing),
dragStartPoint(), dragStartPoint(),
dragEndPoint() dragEndPoint(),
selectedText()
{ {
horizontalScrollBar()->setRange(0, 0); horizontalScrollBar()->setRange(0, 0);
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
@ -167,7 +170,7 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom
void FeedView::updateGeometries() void FeedView::updateGeometries()
{ {
qDebug() << "updateGeometries"; //qDebug() << "updateGeometries";
QScrollBar* bar = verticalScrollBar(); QScrollBar* bar = verticalScrollBar();
const QStyle* st = style(); const QStyle* st = style();
@ -307,7 +310,7 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt
void FeedView::paintEvent(QPaintEvent* event) void FeedView::paintEvent(QPaintEvent* event)
{ {
qDebug() << "paint" << event->rect(); //qDebug() << "paint" << event->rect();
const QAbstractItemModel* m = model(); const QAbstractItemModel* m = model();
QWidget* vp = viewport(); QWidget* vp = viewport();
QRect zone = event->rect().translated(0, -vo); QRect zone = event->rect().translated(0, -vo);
@ -412,14 +415,20 @@ void FeedView::verticalScrollbarValueChanged(int value)
QAbstractItemView::verticalScrollbarValueChanged(vo); QAbstractItemView::verticalScrollbarValueChanged(vo);
} }
void FeedView::setAnchorHovered(bool hovered) void FeedView::setAnchorHovered(Shared::Hover type)
{ {
if (anchorHovered != hovered) { if (hovered != type) {
anchorHovered = hovered; hovered = type;
if (anchorHovered) { switch (hovered) {
setCursor(Qt::PointingHandCursor); case Shared::Hover::nothing:
} else { setCursor(Qt::ArrowCursor);
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); QAbstractItemView::mouseMoveEvent(event);
if (specialDelegate) { if (specialDelegate) {
QModelIndex index = indexAt(dragEndPoint); MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
if (index.isValid()) { if (dragging) {
QRect rect = visualRect(index); QModelIndex index = indexAt(dragStartPoint);
if (rect.contains(dragEndPoint)) { if (index.isValid()) {
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate()); QRect rect = visualRect(index);
if (dragging) { if (rect.contains(dragStartPoint)) {
setAnchorHovered(false); QString selected = del->mouseDrag(dragStartPoint, dragEndPoint, index, rect);
if (del->mouseDrag(dragStartPoint, dragEndPoint, index, rect)) { if (selectedText != selected) {
qDebug() << "asking to repaint" << rect; selectedText = selected;
setDirtyRegion(rect); setDirtyRegion(rect);
} }
} else {
setAnchorHovered(del->isAnchorHovered(dragEndPoint, index, rect));
} }
} else {
setAnchorHovered(false);
} }
} else { } 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; mousePressed = event->button() == Qt::LeftButton;
if (mousePressed) { if (mousePressed) {
dragStartPoint = event->localPos().toPoint(); dragStartPoint = event->localPos().toPoint();
if (specialDelegate && specialModel) {
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
QString lastSelectedId = del->clearSelection();
if (lastSelectedId.size()) {
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(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<QKeyEvent*>(event);
if (key_event->matches(QKeySequence::Copy)) {
if (selectedText.size() > 0) {
QClipboard* cb = QApplication::clipboard();
cb->setText(selectedText);
}
}
}
void FeedView::resizeEvent(QResizeEvent* event) void FeedView::resizeEvent(QResizeEvent* event)
{ {
QAbstractItemView::resizeEvent(event); QAbstractItemView::resizeEvent(event);
@ -603,3 +640,8 @@ void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state)
scheduleDelayedItemsLayout(); scheduleDelayedItemsLayout();
} }
} }
QString FeedView::getSelectedText() const
{
return selectedText;
}

View File

@ -28,6 +28,7 @@
#include <ui/widgets/messageline/messagefeed.h> #include <ui/widgets/messageline/messagefeed.h>
#include <ui/utils/progress.h> #include <ui/utils/progress.h>
#include <shared/utils.h>
/** /**
* @todo write docs * @todo write docs
@ -50,6 +51,7 @@ public:
void setModel(QAbstractItemModel * model) override; void setModel(QAbstractItemModel * model) override;
QFont getFont() const; QFont getFont() const;
QString getSelectedText() const;
signals: signals:
void resized(); void resized();
@ -72,13 +74,14 @@ protected:
void mouseMoveEvent(QMouseEvent * event) override; void mouseMoveEvent(QMouseEvent * event) override;
void mousePressEvent(QMouseEvent * event) override; void mousePressEvent(QMouseEvent * event) override;
void mouseReleaseEvent(QMouseEvent * event) override; void mouseReleaseEvent(QMouseEvent * event) override;
void keyPressEvent(QKeyEvent * event) override;
void resizeEvent(QResizeEvent * event) override; void resizeEvent(QResizeEvent * event) override;
private: 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); void setAnchorHovered(Shared::Hover type);
private: private:
struct Hint { struct Hint {
@ -100,9 +103,10 @@ private:
QFontMetrics dividerMetrics; QFontMetrics dividerMetrics;
bool mousePressed; bool mousePressed;
bool dragging; bool dragging;
bool anchorHovered; Shared::Hover hovered;
QPoint dragStartPoint; QPoint dragStartPoint;
QPoint dragEndPoint; QPoint dragEndPoint;
QString selectedText;
static const std::set<int> geometryChangingRoles; static const std::set<int> geometryChangingRoles;

View File

@ -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); QVariant vi = index.data(Models::MessageFeed::Bulk);
return anchor.size() > 0; Models::FeedItem data = qvariant_cast<Models::FeedItem>(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); QVariant vi = index.data(Models::MessageFeed::Bulk);
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi); Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
@ -448,19 +472,31 @@ bool MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QM
QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint);
if (localHint.contains(start)) { 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->setHtml(Shared::processMessageBody(data.text));
bodyRenderer->setTextWidth(localHint.size().width()); bodyRenderer->setTextWidth(localHint.size().width());
selection.first = bodyRenderer->documentLayout()->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit); selection.first = bodyRenderer->documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit);
selection.second = bodyRenderer->documentLayout()->hitTest(end - localHint.topLeft(), Qt::HitTestAccuracy::FuzzyHit); selection.second = bodyRenderer->documentLayout()->hitTest(last, Qt::HitTestAccuracy::FuzzyHit);
currentId = data.id; 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() QString MessageDelegate::clearSelection()

View File

@ -58,8 +58,8 @@ 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; Shared::Hover hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
bool mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); QString mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint);
QString clearSelection(); QString clearSelection();
static int avatarHeight; static int avatarHeight;

View File

@ -57,6 +57,8 @@ public:
void changeMessage(const QString& id, const QMap<QString, QVariant>& data); void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
void removeMessage(const QString& id); void removeMessage(const QString& id);
Shared::Message getMessage(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; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@ -126,8 +128,6 @@ protected:
bool sentByMe(const Shared::Message& msg) const; bool sentByMe(const Shared::Message& msg) const;
Attachment fillAttach(const Shared::Message& msg) const; Attachment fillAttach(const Shared::Message& msg) const;
Edition fillCorrection(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<MessageRoles> detectChanges(const Shared::Message& msg, const QMap<QString, QVariant>& data) const; std::set<MessageRoles> detectChanges(const Shared::Message& msg, const QMap<QString, QVariant>& data) const;
private: private: