QTextLayout: fix maximumWidth() for a text containing line separator

This is improved version of previous fix
013c346a8d that was reverted because it
broke some tests for Quick Text. The problem was that it did not work
correctly in the case the text was wrapped to a fixed width.
To deal with this we'll accumulate current line full width (as if it
hadn't been wrapped) in layout data (layoutData->currentMaxWidth).
Then when the next line is explicitly wrapped by line or paragraph
separator, this accumulated width will be used to adjust layout's
maximum width.

Change-Id: Iad7119d9808e1db15fe1fbc5db049c3db928529f
Fixes: QTBUG-89557
Fixes: QTBUG-104986
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Vladimir Belyavsky 2022-09-27 16:57:08 +03:00
parent b2ed29b8d9
commit 991c056438
4 changed files with 30 additions and 6 deletions

View File

@ -2604,6 +2604,7 @@ QTextEngine::LayoutData::LayoutData()
haveCharAttributes = false;
logClustersPtr = nullptr;
available_glyphs = 0;
currentMaxWidth = 0;
}
QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
@ -2636,6 +2637,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
hasBidi = false;
layoutState = LayoutEmpty;
haveCharAttributes = false;
currentMaxWidth = 0;
}
QTextEngine::LayoutData::~LayoutData()
@ -2721,6 +2723,7 @@ void QTextEngine::freeMemory()
layoutData->hasBidi = false;
layoutData->layoutState = LayoutEmpty;
layoutData->haveCharAttributes = false;
layoutData->currentMaxWidth = 0;
layoutData->items.clear();
}
if (specialData)

View File

@ -385,6 +385,7 @@ public:
uint layoutState : 2;
uint memory_on_stack : 1;
uint haveCharAttributes : 1;
QFixed currentMaxWidth;
QString string;
bool reallocate(int totalGlyphs);
};

View File

@ -1865,6 +1865,7 @@ void QTextLine::layout_helper(int maxGlyphs)
lbh.logClusters = eng->layoutData->logClustersPtr;
lbh.previousGlyph = 0;
bool manuallyWrapped = false;
bool hasInlineObject = false;
QFixed maxInlineObjectHeight = 0;
@ -1940,6 +1941,7 @@ void QTextLine::layout_helper(int maxGlyphs)
lbh.calculateRightBearingForPreviousGlyph();
}
line += lbh.tmpData;
manuallyWrapped = true;
goto found;
} else if (current.analysis.flags == QScriptAnalysis::Object) {
lbh.whiteSpaceOrObject = true;
@ -1974,11 +1976,10 @@ void QTextLine::layout_helper(int maxGlyphs)
addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
current, lbh.logClusters, lbh.glyphs);
}
if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
goto found;
}
} else {
if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
goto found;
lbh.whiteSpaceOrObject = false;
bool sb_or_ws = false;
lbh.saveCurrentGlyph();
@ -2161,7 +2162,12 @@ found:
eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
} else {
eng->minWidth = qMax(eng->minWidth, lbh.minw);
eng->maxWidth += line.textWidth + lbh.spaceData.textWidth;
eng->layoutData->currentMaxWidth += line.textWidth;
if (!manuallyWrapped)
eng->layoutData->currentMaxWidth += lbh.spaceData.textWidth;
eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
if (manuallyWrapped)
eng->layoutData->currentMaxWidth = 0;
}
line.textWidth += trailingSpace;

View File

@ -2655,17 +2655,28 @@ void tst_QTextLayout::min_maximumWidth_data()
QTest::newRow("long string") << QStringLiteral("lmong_long_crazy_87235982735_23857239682376923876923876-fuwhfhfw-names-AAAA-deeaois2019-03-03.and.more");
QTest::newRow("QTBUG-106947") << QStringLiteral("text text");
QTest::newRow("spaces") << QStringLiteral(" text text ");
QTest::newRow("QTBUG-104986") << QStringLiteral("text\ntext\ntext");
QTest::newRow("spaces + line breaks") << QStringLiteral(" \n text\n \ntext \n ");
}
void tst_QTextLayout::min_maximumWidth()
{
QFETCH(QString, text);
text.replace('\n', QChar::LineSeparator);
QTextLayout layout(text, testFont);
layout.setCacheEnabled(true);
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
layout.setTextOption(opt);
layout.beginLayout();
while (layout.createLine().isValid()) { }
layout.endLayout();
const qreal nonWrappedMaxWidth = layout.maximumWidth();
for (int wrapMode = QTextOption::NoWrap; wrapMode <= QTextOption::WrapAtWordBoundaryOrAnywhere; ++wrapMode) {
QTextOption opt;
opt.setWrapMode((QTextOption::WrapMode)wrapMode);
layout.setTextOption(opt);
layout.beginLayout();
@ -2674,6 +2685,9 @@ void tst_QTextLayout::min_maximumWidth()
const qreal minWidth = layout.minimumWidth();
const qreal maxWidth = layout.maximumWidth();
QCOMPARE_LE(minWidth, maxWidth);
QCOMPARE_LE(maxWidth, nonWrappedMaxWidth); // maxWidth for wrapped text shouldn't exceed maxWidth for the text without wrapping.
// Try the layout from slightly wider than the widest (maxWidth)
// and narrow it down to slighly narrower than minWidth
// layout.maximumWidth() should return the same regardless