QTextDocument: add setLayoutEnabled()

This allows to set up everything first - without paying for the layout
calculation at every step - and only then trigger the layout once.

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

Change-Id: I138bd1d58941d029bc0a36d2730216778f1fbd97
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
David Faure 2022-05-25 22:07:36 +02:00
parent 2996ca031e
commit 09c7457f4a
6 changed files with 85 additions and 4 deletions

View File

@ -634,6 +634,40 @@ bool QTextDocument::useDesignMetrics() const
return d->defaultTextOption.useDesignMetrics(); return d->defaultTextOption.useDesignMetrics();
} }
/*!
\property QTextDocument::layoutEnabled
\since 6.4
\brief whether QTextDocument should recalculate the layout after every change
If this property is set to true, any change to the document triggers a layout,
which makes everything work as expected but takes time.
Temporarily disabling the layout can save time when making multiple changes
(not just text content, but also default font, default text option....)
so that the document is only laid out once at the end. This can be useful when
the text width or page size isn't yet known, for instance.
By default, this property is \c true.
\sa setTextWidth
*/
void QTextDocument::setLayoutEnabled(bool b)
{
Q_D(QTextDocument);
if (d->layoutEnabled == b)
return;
d->layoutEnabled = b;
if (b && d->lout)
d->lout->documentChanged(0, 0, d->length());
}
bool QTextDocument::isLayoutEnabled() const
{
Q_D(const QTextDocument);
return d->layoutEnabled;
}
/*! /*!
\since 4.2 \since 4.2

View File

@ -62,6 +62,7 @@ class Q_GUI_EXPORT QTextDocument : public QObject
Q_PROPERTY(QSizeF pageSize READ pageSize WRITE setPageSize) Q_PROPERTY(QSizeF pageSize READ pageSize WRITE setPageSize)
Q_PROPERTY(QFont defaultFont READ defaultFont WRITE setDefaultFont) Q_PROPERTY(QFont defaultFont READ defaultFont WRITE setDefaultFont)
Q_PROPERTY(bool useDesignMetrics READ useDesignMetrics WRITE setUseDesignMetrics) Q_PROPERTY(bool useDesignMetrics READ useDesignMetrics WRITE setUseDesignMetrics)
Q_PROPERTY(bool layoutEnabled READ isLayoutEnabled WRITE setLayoutEnabled)
Q_PROPERTY(QSizeF size READ size) Q_PROPERTY(QSizeF size READ size)
Q_PROPERTY(qreal textWidth READ textWidth WRITE setTextWidth) Q_PROPERTY(qreal textWidth READ textWidth WRITE setTextWidth)
Q_PROPERTY(int blockCount READ blockCount) Q_PROPERTY(int blockCount READ blockCount)
@ -218,6 +219,9 @@ public:
void setUseDesignMetrics(bool b); void setUseDesignMetrics(bool b);
bool useDesignMetrics() const; bool useDesignMetrics() const;
void setLayoutEnabled(bool b);
bool isLayoutEnabled() const;
void drawContents(QPainter *painter, const QRectF &rect = QRectF()); void drawContents(QPainter *painter, const QRectF &rect = QRectF());
void setTextWidth(qreal width); void setTextWidth(qreal width);

View File

@ -291,6 +291,8 @@ public:
return get(object->document()); return get(object->document());
} }
bool canLayout() const { return layoutEnabled && !pageSize.isNull(); }
private: private:
QTextDocumentPrivate(const QTextDocumentPrivate& m); QTextDocumentPrivate(const QTextDocumentPrivate& m);
QTextDocumentPrivate& operator= (const QTextDocumentPrivate& m); QTextDocumentPrivate& operator= (const QTextDocumentPrivate& m);
@ -337,6 +339,7 @@ private:
public: public:
bool inContentsChange; bool inContentsChange;
bool layoutEnabled = true;
QTextOption defaultTextOption; QTextOption defaultTextOption;
Qt::CursorMoveStyle defaultCursorMoveStyle; Qt::CursorMoveStyle defaultCursorMoveStyle;
#ifndef QT_NO_CSSPARSER #ifndef QT_NO_CSSPARSER

View File

@ -3754,7 +3754,7 @@ void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next()) for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
blockIt.clearLayout(); blockIt.clearLayout();
if (d->docPrivate->pageSize.isNull()) if (!d->docPrivate->canLayout())
return; return;
QRectF updateRect; QRectF updateRect;
@ -4032,7 +4032,7 @@ QRectF QTextDocumentLayout::tableCellBoundingRect(QTextTable *table, const QText
QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const
{ {
Q_D(const QTextDocumentLayout); Q_D(const QTextDocumentLayout);
if (d->docPrivate->pageSize.isNull()) if (!d->docPrivate->canLayout())
return QRectF(); return QRectF();
d->ensureLayoutFinished(); d->ensureLayoutFinished();
@ -4059,7 +4059,7 @@ QRectF QTextDocumentLayout::tableBoundingRect(QTextTable *table) const
QRectF QTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const QRectF QTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
{ {
Q_D(const QTextDocumentLayout); Q_D(const QTextDocumentLayout);
if (d->docPrivate->pageSize.isNull()) if (!d->docPrivate->canLayout())
return QRectF(); return QRectF();
d->ensureLayoutFinished(); d->ensureLayoutFinished();
return d->frameBoundingRectInternal(frame); return d->frameBoundingRectInternal(frame);
@ -4088,7 +4088,7 @@ QRectF QTextDocumentLayoutPrivate::frameBoundingRectInternal(QTextFrame *frame)
QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
{ {
Q_D(const QTextDocumentLayout); Q_D(const QTextDocumentLayout);
if (d->docPrivate->pageSize.isNull() || !block.isValid() || !block.isVisible()) if (!d->docPrivate->canLayout() || !block.isValid() || !block.isVisible())
return QRectF(); return QRectF();
d->ensureLayoutedByPosition(block.position() + block.length()); d->ensureLayoutedByPosition(block.position() + block.length());
QTextFrame *frame = d->document->frameAt(block.position()); QTextFrame *frame = d->document->frameAt(block.position());

View File

@ -180,6 +180,8 @@ private slots:
void insertHtmlWithComments_data(); void insertHtmlWithComments_data();
void insertHtmlWithComments(); void insertHtmlWithComments();
void delayedLayout();
private: private:
void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
void buildRegExpData(); void buildRegExpData();
@ -3931,5 +3933,24 @@ void tst_QTextDocument::insertHtmlWithComments()
QCOMPARE(blockContent, expectedBlocks); QCOMPARE(blockContent, expectedBlocks);
} }
void tst_QTextDocument::delayedLayout()
{
QTextDocument doc;
doc.setHtml("<html>Foobar</html>");
QCOMPARE(doc.blockCount(), 1);
doc.setLayoutEnabled(false);
// Force creation of a layout
QVERIFY(doc.documentLayout());
QTextBlock block = doc.begin();
QTextLayout *layout = block.layout();
QCOMPARE(layout->lineCount(), 0); // layout didn't happen yet
doc.setLayoutEnabled(true);
QCOMPARE(layout->lineCount(), 1); // layout happened
}
QTEST_MAIN(tst_QTextDocument) QTEST_MAIN(tst_QTextDocument)
#include "tst_qtextdocument.moc" #include "tst_qtextdocument.moc"

View File

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