QTextDocument: fix an off-by-one in the changed signal for lists

When blocks are added or removed in block groups, i.e. items added or
removed from text lists, the whole group is marked as changed, but the
calculation of the before/after group length would be one off. That
was reflected in the contentsChange signal.

Add unit test. Since the whole group changes when list items are
added, text is removed and the change-begin is not where the cursor
was when the change was made.

Fixes: QTBUG-82455
Pick-to: 6.2 5.15
Change-Id: I99ee2cfef4944fcac8aca492741fd0f3b0de4920
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
Eirik Aavitsland 2020-04-21 09:55:05 +02:00 committed by Volker Hilsheimer
parent a892a7b2ca
commit 8fbedf2196
2 changed files with 64 additions and 1 deletions

View File

@ -382,8 +382,11 @@ int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blo
Q_ASSERT(blocks.length() == fragments.length()); Q_ASSERT(blocks.length() == fragments.length());
QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat)); QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
if (group) if (group) {
group->blockInserted(QTextBlock(this, b)); group->blockInserted(QTextBlock(this, b));
docChangeOldLength--;
docChangeLength--;
}
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format))); QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
if (frame) { if (frame) {

View File

@ -198,6 +198,9 @@ private slots:
void resourceProvider(); void resourceProvider();
void contentsChangeIndices_data();
void contentsChangeIndices();
private: private:
void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
void buildRegExpData(); void buildRegExpData();
@ -3820,5 +3823,62 @@ void tst_QTextDocument::resourceProvider()
QCOMPARE(providerCalled, 2); QCOMPARE(providerCalled, 2);
} }
void tst_QTextDocument::contentsChangeIndices_data()
{
QTest::addColumn<QString>("html");
// adding list entries change the entire block, so change position is
// not the same as the cursor position if this value is >= 0
QTest::addColumn<int>("expectedBegin");
QTest::addRow("text") << "Test" << -1;
QTest::addRow("unnumbered list") << "<ul><li>Test</li></ul>" << 0;
QTest::addRow("numbered list") << "<ol><li>Test</li></ol>" << 0;
QTest::addRow("table") << "<table><tr><td>Test</td></tr></table>" << -1;
}
void tst_QTextDocument::contentsChangeIndices()
{
QFETCH(QString, html);
QFETCH(int, expectedBegin);
QTextDocument doc;
QTestDocumentLayout *layout = new QTestDocumentLayout(&doc);
doc.setDocumentLayout(layout);
doc.setHtml(QString("<html><body>%1</body></html>").arg(html));
int documentLength = 0;
int cursorLength = 0;
int changeBegin = 0;
int changeRemoved = 0;
int changeAdded = 0;
connect(&doc, &QTextDocument::contentsChange, this, [&](int pos, int removed, int added){
documentLength = doc.characterCount();
QTextCursor cursor(&doc);
cursor.movePosition(QTextCursor::End);
// includes end-of-paragraph character
cursorLength = cursor.position() + 1;
changeBegin = pos;
changeRemoved = removed;
changeAdded = added;
});
QTextCursor cursor(&doc);
cursor.movePosition(QTextCursor::End);
if (expectedBegin < 0)
expectedBegin = cursor.position();
cursor.insertBlock();
const int changeEnd = changeBegin + changeAdded;
QVERIFY(documentLength > 0);
QCOMPARE(documentLength, cursorLength);
QVERIFY(documentLength >= changeEnd);
QCOMPARE(changeBegin, expectedBegin);
QCOMPARE(changeAdded - changeRemoved, 1);
}
QTEST_MAIN(tst_QTextDocument) QTEST_MAIN(tst_QTextDocument)
#include "tst_qtextdocument.moc" #include "tst_qtextdocument.moc"