diff options
-rw-r--r-- | src/quick/items/qquicktextedit.cpp | 23 | ||||
-rw-r--r-- | src/quick/items/qquicktextedit_p_p.h | 4 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextedit/data/viewport.qml | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp | 38 |
4 files changed, 52 insertions, 15 deletions
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 9ef041bbb1..d0614bbeff 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2087,7 +2087,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * QQuickTextNodeEngine engine; QQuickTextNodeEngine frameDecorationsEngine; - if (!oldNode || nodeIterator < d->textNodeMap.end()) { + if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) { if (!oldNode) rootNode = new RootNode; @@ -2184,9 +2184,10 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * QTextFrame::iterator it = textFrame->begin(); while (!it.atEnd()) { QTextBlock block = it.currentBlock(); - ++it; - if (block.position() < firstDirtyPos) + if (block.position() < firstDirtyPos) { + ++it; continue; + } if (!engine.hasContents()) nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft(); @@ -2199,6 +2200,21 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * inView = coveredRegion.bottom() > viewport.top(); } if (d->firstBlockInViewport < 0 && inView) { + // During backward scrolling, we need to iterate backwards from textNodeMap.begin() to fill the top of the viewport. + if (coveredRegion.top() > viewport.top() + 1) { + qCDebug(lcVP) << "checking backwards from block" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion; + while (it != textFrame->begin() && it.currentBlock().layout() && + it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) + --it; + if (!it.currentBlock().layout()) + ++it; + if (Q_LIKELY(it.currentBlock().layout())) { + block = it.currentBlock(); + coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y()); + } else { + qCWarning(lcVP) << "failed to find a text block with layout during back-scrolling"; + } + } qCDebug(lcVP) << "first block in viewport" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion; d->firstBlockInViewport = block.blockNumber(); if (block.layout()) @@ -2240,6 +2256,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor); nodeStart = block.next().position(); } + ++it; } } if (Q_LIKELY(node && !node->parent())) diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index b1937dab9e..2381b46a05 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -190,8 +190,8 @@ public: int lastSelectionStart; int lastSelectionEnd; int lineCount; - int firstBlockInViewport = -1; - int firstBlockPastViewport = -1; + int firstBlockInViewport = -1; // only for the autotest; can be wrong after scrolling sometimes + int firstBlockPastViewport = -1; // only for the autotest QRectF renderedRegion; enum UpdateType { diff --git a/tests/auto/quick/qquicktextedit/data/viewport.qml b/tests/auto/quick/qquicktextedit/data/viewport.qml index c7c1dea526..4c7ffb88c0 100644 --- a/tests/auto/quick/qquicktextedit/data/viewport.qml +++ b/tests/auto/quick/qquicktextedit/data/viewport.qml @@ -10,7 +10,7 @@ Item { border.color: "red" TextEdit { - font.pointSize: 10 + font.pixelSize: 10 cursorDelegate: Rectangle { border.color: "green" border.width: 2 diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 812b541cb0..ccdf3e6b92 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -3693,6 +3693,8 @@ void tst_qquicktextedit::largeTextObservesViewport_data() QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat"); QTest::addColumn<bool>("parentIsViewport"); QTest::addColumn<int>("cursorPos"); + QTest::addColumn<int>("scrollDelta"); // non-zero to move TextEdit in viewport + QTest::addColumn<int>("expectedBlockTolerance"); QTest::addColumn<int>("expectedBlocksAboveViewport"); QTest::addColumn<int>("expectedBlocksPastViewport"); @@ -3715,10 +3717,12 @@ void tst_qquicktextedit::largeTextObservesViewport_data() // by default, the root item acts as the viewport: // QQuickTextEdit doesn't populate lines of text beyond the bottom of the window // cursor position 1000 is on line 121 - QTest::newRow("default plain text") << text << QQuickTextEdit::PlainText << false << 1000 << 4 << 115 << 147 << 1700 << 2700; + QTest::newRow("default plain text") << text << QQuickTextEdit::PlainText << false << 1000 << 0 + << 5 << 114 << 155 << 1200 << 2200; // make the rectangle into a viewport item, and move the text upwards: // QQuickTextEdit doesn't populate lines of text beyond the bottom of the viewport rectangle - QTest::newRow("clipped plain text") << text << QQuickTextEdit::PlainText << true << 1000 << 4 << 123 << 141 << 1800 << 2600; + QTest::newRow("clipped plain text") << text << QQuickTextEdit::PlainText << true << 1000 << 0 + << 5 << 123 << 147 << 1200 << 2100; { QStringList lines; @@ -3741,12 +3745,21 @@ void tst_qquicktextedit::largeTextObservesViewport_data() // by default, the root item acts as the viewport: // QQuickTextEdit doesn't populate blocks beyond the bottom of the window - QTest::newRow("default styled text") << text << QQuickTextEdit::RichText << false << 1000 << 4 << 123 << 141 << 3200 << 4100; + QTest::newRow("default styled text") << text << QQuickTextEdit::RichText << false << 1000 << 0 + << 120 << 7 << 143 << 2700 << 3700; // make the rectangle into a viewport item, and move the text upwards: // QQuickTextEdit doesn't populate blocks that don't intersect the viewport rectangle - QTest::newRow("clipped styled text") << text << QQuickTextEdit::RichText << true << 1000 << 4 << 127 << 138 << 3300 << 4100; + QTest::newRow("clipped styled text") << text << QQuickTextEdit::RichText << true << 1000 << 0 + << 3 << 127 << 139 << 2800 << 3600; // get the "chapter 2" heading into the viewport - QTest::newRow("heading visible") << text << QQuickTextEdit::RichText << true << 780 << 4 << 102 << 112 << 2600 << 3300; + QTest::newRow("heading visible") << text << QQuickTextEdit::RichText << true << 800 << 0 + << 3 << 105 << 116 << 2300 << 3000; + // get the "chapter 2" heading into the viewport, and then scroll backwards + QTest::newRow("scroll backwards") << text << QQuickTextEdit::RichText << true << 800 << 20 + << 3 << 104 << 116 << 2200 << 3000; + // get the "chapter 2" heading into the viewport, and then scroll forwards + QTest::newRow("scroll forwards") << text << QQuickTextEdit::RichText << true << 800 << -50 + << 3 << 107 << 119 << 2300 << 3100; } void tst_qquicktextedit::largeTextObservesViewport() @@ -3759,6 +3772,7 @@ void tst_qquicktextedit::largeTextObservesViewport() QFETCH(QQuickTextEdit::TextFormat, textFormat); QFETCH(bool, parentIsViewport); QFETCH(int, cursorPos); + QFETCH(int, scrollDelta); QFETCH(int, expectedBlockTolerance); QFETCH(int, expectedBlocksAboveViewport); QFETCH(int, expectedBlocksPastViewport); @@ -3784,6 +3798,13 @@ void tst_qquicktextedit::largeTextObservesViewport() textItem->setCursorPosition(cursorPos); auto cursorRect = textItem->cursorRectangle(); textItem->setY(-cursorRect.top()); + if (lcTests().isDebugEnabled()) + QTest::qWait(500); + if (scrollDelta) { + textItem->setY(textItem->y() + scrollDelta); + if (lcTests().isDebugEnabled()) + QTest::qWait(500); + } qCDebug(lcTests) << "text size" << textItem->text().size() << "lines" << textItem->lineCount() << "font" << textItem->font(); Q_ASSERT(textItem->text().size() > QQuickTextEditPrivate::largeTextSizeThreshold); QVERIFY(textItem->flags().testFlag(QQuickItem::ItemObservesViewport)); // large text sets this flag automatically @@ -3793,11 +3814,10 @@ void tst_qquicktextedit::largeTextObservesViewport() << "expected" << expectedBlocksAboveViewport << "first block past viewport" << textPriv->firstBlockPastViewport << "expected" << expectedBlocksPastViewport - << "region" << textPriv->renderedRegion + << "region" << textPriv->renderedRegion << "bottom" << textPriv->renderedRegion.bottom() << "expected range" << expectedRenderedRegionMin << expectedRenderedRegionMax; - if (lcTests().isDebugEnabled()) - QTest::qWait(1000); - QVERIFY(qAbs(textPriv->firstBlockInViewport - expectedBlocksAboveViewport) < expectedBlockTolerance); + if (scrollDelta >= 0) // unfortunately firstBlockInViewport isn't always reliable after scrolling + QTRY_VERIFY(qAbs(textPriv->firstBlockInViewport - expectedBlocksAboveViewport) < expectedBlockTolerance); QVERIFY(qAbs(textPriv->firstBlockPastViewport - expectedBlocksPastViewport) < expectedBlockTolerance); QVERIFY(textPriv->renderedRegion.top() > expectedRenderedRegionMin); QVERIFY(textPriv->renderedRegion.bottom() < expectedRenderedRegionMax); |