Skip to content

Commit 2c1c604

Browse files
committed
Support vertical drag scrolling on document blank areas
1 parent 9c4c813 commit 2c1c604

4 files changed

Lines changed: 79 additions & 1 deletion

File tree

‎src/pdfselection.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ QPair<int, int> PdfView::selectionRange(int page, int count) const {
112112
}
113113

114114
void PdfView::clearSelection() {
115+
scrollDragPending = scrollDragging = false;
115116
selectionScrollTimer.stop();
116117
selecting = selectionVisible = false;
117118
selectionAnchor = selectionEnd = {};

‎src/pdfview.cpp‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,13 +434,16 @@ void PdfView::followLink(const LinkTarget &target) {
434434

435435
void PdfView::updateLinkCursor() {
436436
const auto point = viewport()->mapFromGlobal(QCursor::pos());
437-
if (selecting && selectionVisible) viewport()->setCursor(Qt::IBeamCursor);
437+
if (scrollDragging) viewport()->setCursor(Qt::ClosedHandCursor);
438+
else if (selecting && selectionVisible) viewport()->setCursor(Qt::IBeamCursor);
438439
else if (!viewport()->rect().contains(point)) viewport()->unsetCursor();
439440
else viewport()->setCursor(linkAt(point).valid() ? Qt::PointingHandCursor
440441
: textAt(point).valid() ? Qt::IBeamCursor : Qt::ArrowCursor);
441442
}
442443

443444
void PdfView::hideEvent(QHideEvent *event) {
445+
scrollDragPending = scrollDragging = false;
446+
viewport()->unsetCursor();
444447
selectionScrollTimer.stop();
445448
selecting = false;
446449
pressedLink = {};
@@ -449,6 +452,16 @@ void PdfView::hideEvent(QHideEvent *event) {
449452

450453
void PdfView::mouseMoveEvent(QMouseEvent *event) {
451454
selectionPointer = event->position().toPoint();
455+
if (scrollDragPending) {
456+
if (qAbs(selectionPointer.y() - pressPosition.y()) > QApplication::startDragDistance())
457+
scrollDragging = true;
458+
if (scrollDragging) {
459+
verticalScrollBar()->setValue(scrollDragStart + pressPosition.y() - selectionPointer.y());
460+
viewport()->setCursor(Qt::ClosedHandCursor);
461+
}
462+
event->accept();
463+
return;
464+
}
452465
if ((selectionPointer - pressPosition).manhattanLength() > QApplication::startDragDistance()) {
453466
pressedLink = {};
454467
if (selecting) {
@@ -482,6 +495,13 @@ void PdfView::mousePressEvent(QMouseEvent *event) {
482495
selectionAnchor = selectionEnd = textAt(pressPosition);
483496
selecting = selectionAnchor.valid();
484497
pressedLink = linkAt(pressPosition);
498+
const QPoint documentPoint = pressPosition
499+
+ QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value());
500+
scrollDragPending = !selecting && !pressedLink.valid()
501+
&& std::any_of(pages.cbegin(), pages.cend(), [&](const QRectF &page) {
502+
return page.contains(documentPoint);
503+
});
504+
scrollDragStart = verticalScrollBar()->value();
485505
event->accept();
486506
return;
487507
}
@@ -490,6 +510,9 @@ void PdfView::mousePressEvent(QMouseEvent *event) {
490510

491511
void PdfView::mouseReleaseEvent(QMouseEvent *event) {
492512
if (event->button() == Qt::LeftButton) {
513+
const bool wasScrollDragging = scrollDragging;
514+
scrollDragPending = scrollDragging = false;
515+
if (wasScrollDragging) updateLinkCursor();
493516
selectionScrollTimer.stop();
494517
if (selecting && selectionVisible) {
495518
selectionPointer = event->position().toPoint();

‎src/pdfview.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ class PdfView : public QAbstractScrollArea {
105105
QTimer selectionScrollTimer;
106106
LinkTarget pressedLink;
107107
QPoint pressPosition;
108+
bool scrollDragPending = false;
109+
bool scrollDragging = false;
110+
int scrollDragStart = 0;
108111
EpubDestinations epubDestinations;
109112
struct Match { int page; QVector<QRectF> rectangles; }; // Normalized display coordinates.
110113
QVector<Match> matches;

‎tests/viewer_tests.cpp‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,57 @@ private slots:
6666
QApplication::setFont(QFont(QFontDatabase::applicationFontFamilies(fontId).first(), 10));
6767
}
6868
void cleanupTestCase() { FPDF_DestroyLibrary(); }
69+
void documentDragScrolling() {
70+
QTemporaryDir directory;
71+
const auto path = directory.filePath("drag.pdf");
72+
{
73+
QPdfWriter writer(path);
74+
QPainter painter(&writer);
75+
painter.fillRect(QRect(0, 0, 100, 100), Qt::red);
76+
}
77+
PdfView view;
78+
view.resize(700, 450);
79+
view.show();
80+
QString error;
81+
QVERIFY2(view.open(path, {}, &error), qPrintable(error));
82+
view.setZoom(1.0);
83+
QTest::qWait(20);
84+
auto bar = view.verticalScrollBar();
85+
QVERIFY(bar->maximum() > 200);
86+
bar->setValue(0);
87+
const QPoint start(view.viewport()->width() / 2, 250);
88+
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, start);
89+
QTest::mouseMove(view.viewport(), start - QPoint(0, 2));
90+
QCOMPARE(bar->value(), 0);
91+
QTest::mouseMove(view.viewport(), start + QPoint(30, -100));
92+
QCOMPARE(bar->value(), 100);
93+
QCOMPARE(view.horizontalScrollBar()->value(), 0);
94+
QCOMPARE(view.viewport()->cursor().shape(), Qt::ClosedHandCursor);
95+
QVERIFY(!view.hasSelection());
96+
QTest::mouseMove(view.viewport(), start - QPoint(0, 40));
97+
QCOMPARE(bar->value(), 40);
98+
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, start - QPoint(0, 40));
99+
QVERIFY(view.viewport()->cursor().shape() != Qt::ClosedHandCursor);
100+
QTest::mouseMove(view.viewport(), start);
101+
QCOMPARE(bar->value(), 40);
102+
103+
// Background margins and right-button drags must not pan the document.
104+
for (const auto button : {Qt::LeftButton, Qt::RightButton}) {
105+
const QPoint origin = button == Qt::LeftButton ? QPoint(5, 250) : start;
106+
QTest::mousePress(view.viewport(), button, Qt::NoModifier, origin);
107+
QTest::mouseMove(view.viewport(), origin - QPoint(0, 80));
108+
QTest::mouseRelease(view.viewport(), button, Qt::NoModifier, origin - QPoint(0, 80));
109+
QCOMPARE(bar->value(), 40);
110+
}
111+
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, start);
112+
QTest::mouseMove(view.viewport(), start - QPoint(0, 80));
113+
QCOMPARE(bar->value(), 120);
114+
view.hide();
115+
view.show();
116+
QTest::mouseMove(view.viewport(), start);
117+
QCOMPARE(bar->value(), 120);
118+
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, start);
119+
}
69120
void textSelection_data() {
70121
QTest::addColumn<int>("rotation");
71122
for (int rotation : {0, 90, 180, 270})

0 commit comments

Comments
 (0)