QTextDocument/QGraphicsTextItem: skip layout in setTextWidth(0)

In a QGraphicsTextItem without a width yet, there's no need to do any
layouting. The use case is obviously items with an app-defined size,
not the default where text items adapt to their contents.

Results:
 0.065 msecs to create a QGraphicsTextItem with some text (layouted)
 0.036 msecs to set everything up in a QGraphicsTextItem with 0 width

QTextEdit was abusing the width 0 to mean "no wrap, width comes from
contents", but since the value -1 means that already in QTextDocument,
QTextEdit now uses a width of -1 for that meaning.

Change-Id: I67ad59c305e5dd34830886e4e6c56dde03c93668
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
David Faure 2022-05-16 23:58:30 +02:00
parent 7fa17b5f79
commit bb2f4d08d9
5 changed files with 33 additions and 8 deletions

View File

@ -291,6 +291,11 @@ public:
return get(object->document());
}
// Only test the width for 0:
// * setTextWidth(x) leads to height -1, which is valid
// * the default page size of (-1, -1) means size determined from contents, this is valid too
bool canLayout() const { return !qIsNull(pageSize.width()); }
private:
QTextDocumentPrivate(const QTextDocumentPrivate& m);
QTextDocumentPrivate& operator= (const QTextDocumentPrivate& m);

View File

@ -3705,7 +3705,7 @@ void QTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
}
QFixed width = fd->size.width;
if (d->document->pageSize().width() == 0 && d->viewportRect.isValid()) {
if (d->document->pageSize().width() == -1 && d->viewportRect.isValid()) {
// we're in NoWrap mode, meaning the frame should expand to the viewport
// so that backgrounds are drawn correctly
fd->size.width = qMax(width, QFixed::fromReal(d->viewportRect.right()));
@ -3754,7 +3754,7 @@ void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
blockIt.clearLayout();
if (d->docPrivate->pageSize.isNull())
if (!d->docPrivate->canLayout())
return;
QRectF updateRect;
@ -4032,7 +4032,7 @@ QRectF QTextDocumentLayout::tableCellBoundingRect(QTextTable *table, const QText
QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const
{
Q_D(const QTextDocumentLayout);
if (d->docPrivate->pageSize.isNull())
if (!d->docPrivate->canLayout())
return QRectF();
d->ensureLayoutFinished();
@ -4059,7 +4059,7 @@ QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const
QRectF QTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
{
Q_D(const QTextDocumentLayout);
if (d->docPrivate->pageSize.isNull())
if (!d->docPrivate->canLayout())
return QRectF();
d->ensureLayoutFinished();
return d->frameBoundingRectInternal(frame);
@ -4088,7 +4088,7 @@ QRectF QTextDocumentLayoutPrivate::frameBoundingRectInternal(QTextFrame *frame)
QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
{
Q_D(const QTextDocumentLayout);
if (d->docPrivate->pageSize.isNull() || !block.isValid() || !block.isVisible())
if (!d->docPrivate->canLayout() || !block.isValid() || !block.isVisible())
return QRectF();
d->ensureLayoutedByPosition(block.position() + block.length());
QTextFrame *frame = d->document->frameAt(block.position());

View File

@ -1521,7 +1521,7 @@ void QTextEditPrivate::relayoutDocument()
QVariant alignmentProperty = doc->documentLayout()->property("contentHasAlignment");
if (alignmentProperty.userType() == QMetaType::Bool && !alignmentProperty.toBool()) {
width = 0;
width = -1;
}
}

View File

@ -1194,8 +1194,9 @@ void tst_QTextEdit::undoRedoShouldRepositionTextEditCursor()
void tst_QTextEdit::lineWrapModes()
{
ed->setLineWrapMode(QTextEdit::NoWrap);
// NoWrap at the same time as having all lines that are all left aligned means we optimize to only layout once. The effect is that the width is always 0
QCOMPARE(ed->document()->pageSize().width(), qreal(0));
// NoWrap at the same time as having all lines that are all left aligned means we optimize to
// only layout once. The effect is that the width is always -1
QCOMPARE(ed->document()->pageSize().width(), qreal(-1));
QTextCursor cursor = QTextCursor(ed->document());
cursor.insertText(QString("A simple line"));

View File

@ -5,6 +5,7 @@
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QTextDocument>
class tst_QGraphicsItem : public QObject
{
@ -33,6 +34,7 @@ private slots:
void shear();
void translate();
void createTextItem();
void createTextItemZeroWidth();
};
tst_QGraphicsItem::tst_QGraphicsItem()
@ -216,5 +218,22 @@ void tst_QGraphicsItem::createTextItem()
}
}
void tst_QGraphicsItem::createTextItemZeroWidth()
{
// Ensure QFontDatabase loaded the font beforehand
QFontInfo(qApp->font()).family();
const QString text = "This is some text";
QBENCHMARK {
QGraphicsTextItem item;
item.document()->setTextWidth(0);
// Prepare everything
item.setPlainText(text);
QTextOption option = item.document()->defaultTextOption();
option.setAlignment(Qt::AlignHCenter);
item.document()->setDefaultTextOption(option);
// And (in a real app) set actual text width here
}
}
QTEST_MAIN(tst_QGraphicsItem)
#include "tst_qgraphicsitem.moc"