diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2021-08-16 17:58:27 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2021-09-29 16:14:44 +0200 |
commit | e57ba6916efec2a9506b5b12ed34e402c48955d1 (patch) | |
tree | e135ed41875a968739b459b0db7f9abe964534c7 | |
parent | 6a50f9350d8abf65e23c65daef76b862097ec7e8 (diff) |
Large Text: populate only lines visible in Window into SG
Text is rendered top-to-bottom: QQuickTextNode::addTextLayout()
iterates lines of text. Now it first maps the window size() rectangle
into the Text item's coordinate system, and treats that as the viewport:
lines whose y coordinates fall outside are omitted.
This saves a huge amount of time and memory when the text is large.
So Flickable { Text { } } can actually be used in an ebook reader now,
for example.
QQuickItemPrivate::transformChanged(parent) is now used to inform
all children when a parent moves; so e.g. when a content item is
moved inside a "viewport" item (like a Flickable, or any other item
that is being used as a clipping viewport or just as a holder),
all children of the content item will be notified; and QQuickTextPrivate
uses this occasion to mark itself dirty so that the updatePaintNode()
will be called again. This happens directly after the actual movement:
QQuickTextPrivate::transformChanged qquicktext.cpp 2990
QQuickItemPrivate::transformChanged qquickitem.cpp 5200
QQuickItemPrivate::dirty qquickitem.cpp 6339
QQuickItem::setY qquickitem.cpp 6830
QQuickFlickablePrivate::setViewportY qquickflickable.cpp 1800
Task-number: QTBUG-60491
Task-number: QTBUG-90734
Change-Id: I152d23caa725f194a186a604fbc8d0c07f597b16
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r-- | examples/quick/pointerhandlers/fakeFlickable.qml | 28 | ||||
-rw-r--r-- | src/quick/items/qquickflipable.cpp | 6 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 20 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquicktext.cpp | 18 | ||||
-rw-r--r-- | src/quick/items/qquicktext_p_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquicktextnode.cpp | 25 |
7 files changed, 86 insertions, 15 deletions
diff --git a/examples/quick/pointerhandlers/fakeFlickable.qml b/examples/quick/pointerhandlers/fakeFlickable.qml index b6be729e3c..2234a7793a 100644 --- a/examples/quick/pointerhandlers/fakeFlickable.qml +++ b/examples/quick/pointerhandlers/fakeFlickable.qml @@ -120,15 +120,31 @@ Rectangle { } LeftDrawer { - width: 100 + width: buttonRow.implicitWidth + 20 anchors.verticalCenter: parent.verticalCenter - Slider { - id: slider - label: "font\nsize" + Column { anchors.fill: parent anchors.margins: 10 - maximumValue: 36 - value: 14 + Slider { + id: slider + width: parent.width + height: parent.height - buttonRow.implicitHeight + label: "font\nsize" + maximumValue: 36 + value: 14 + } + Row { + id: buttonRow + spacing: 4 + TapHandlerButton { + text: "⭯" + onClicked: ff.rotation -= 45 + } + TapHandlerButton { + text: "⭮" + onClicked: ff.rotation += 45 + } + } } } } diff --git a/src/quick/items/qquickflipable.cpp b/src/quick/items/qquickflipable.cpp index a338985622..27fb02987c 100644 --- a/src/quick/items/qquickflipable.cpp +++ b/src/quick/items/qquickflipable.cpp @@ -70,7 +70,7 @@ class QQuickFlipablePrivate : public QQuickItemPrivate public: QQuickFlipablePrivate() : current(QQuickFlipable::Front), front(nullptr), back(nullptr), sideDirty(false) {} - void transformChanged() override; + void transformChanged(QQuickItem *transformedItem) override; void updateSide(); void setBackTransform(); @@ -219,7 +219,7 @@ QQuickFlipable::Side QQuickFlipable::side() const return d->current; } -void QQuickFlipablePrivate::transformChanged() +void QQuickFlipablePrivate::transformChanged(QQuickItem *transformedItem) { Q_Q(QQuickFlipable); @@ -228,7 +228,7 @@ void QQuickFlipablePrivate::transformChanged() q->polish(); } - QQuickItemPrivate::transformChanged(); + QQuickItemPrivate::transformChanged(transformedItem); } void QQuickFlipable::updatePolish() diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 7c30746df3..0e5b33776e 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5241,8 +5241,24 @@ QPointF QQuickItemPrivate::computeTransformOrigin() const } } -void QQuickItemPrivate::transformChanged() +/*! + \internal + QQuickItemPrivate::dirty() calls transformChanged(q) to inform this item and + all its children that its transform has changed, with \a transformedItem always + being the parent item that caused the change. Override to react, e.g. to + call update() if the item needs to re-generate SG nodes based on visible extents. +*/ +void QQuickItemPrivate::transformChanged(QQuickItem *transformedItem) { + Q_Q(QQuickItem); + if (q != transformedItem) + return; + + // Inform the children in paint order: by the time we visit leaf items, + // they can see any consequences in their parents + for (auto child : paintOrderChildItems()) + QQuickItemPrivate::get(child)->transformChanged(transformedItem); + #if QT_CONFIG(quick_shadereffect) if (extra.isAllocated() && extra->layer) extra->layer->updateMatrix(); @@ -6380,7 +6396,7 @@ void QQuickItemPrivate::dirty(DirtyType type) { Q_Q(QQuickItem); if (type & (TransformOrigin | Transform | BasicTransform | Position | Size)) - transformChanged(); + transformChanged(q); if (!(dirtyAttributes & type) || (window && !prevDirtyItem)) { dirtyAttributes |= type; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index f55c655723..0d35cfd48c 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -617,7 +617,7 @@ public: } QPointF computeTransformOrigin() const; - virtual void transformChanged(); + virtual void transformChanged(QQuickItem *transformedItem); QPointF adjustedPosForTransform(const QPointF ¢roid, const QPointF &startPos, const QVector2D &activeTranslatation, diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 0f17af1975..05b62a11f2 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -76,6 +76,12 @@ Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace) const QChar QQuickTextPrivate::elideChar = QChar(0x2026); +#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD) + #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000 +#endif +// if QString::size() > largeTextSizeThreshold, we render more often, but only visible lines +const int QQuickTextPrivate::largeTextSizeThreshold = QQUICKTEXT_LARGETEXT_THRESHOLD; + QQuickTextPrivate::QQuickTextPrivate() : fontInfo(font), elideLayout(nullptr), textLine(nullptr), lineWidth(0) , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000) @@ -2979,6 +2985,18 @@ void QQuickText::hoverLeaveEvent(QHoverEvent *event) d->processHoverEvent(event); } +void QQuickTextPrivate::transformChanged(QQuickItem *transformedItem) +{ + QQuickItemPrivate::transformChanged(transformedItem); + + // If there's a lot of text, we may need QQuickText::updatePaintNode() to call + // QQuickTextNode::addTextLayout() again to populate a different range of lines + if (text.size() > largeTextSizeThreshold) { + updateType = UpdatePaintNode; + dirty(QQuickItemPrivate::Content); + } +} + /*! \qmlproperty int QtQuick::Text::renderTypeQuality diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index 7d8744671a..c34eca7824 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -89,6 +89,7 @@ public: void clearFormats(); void processHoverEvent(QHoverEvent *event); + void transformChanged(QQuickItem *transformedItem) override; QRectF layedOutTextRect; QSizeF advance; @@ -180,6 +181,7 @@ public: bool updateSizeRecursionGuard:1; static const QChar elideChar; + static const int largeTextSizeThreshold; qreal getImplicitWidth() const override; qreal getImplicitHeight() const override; diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 2ee8d08fff..87cae92565 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -53,6 +53,7 @@ #include <qabstracttextdocumentlayout.h> #include <qxmlstream.h> #include <private/qquickstyledtext_p.h> +#include <private/qquicktext_p_p.h> #include <private/qfont_p.h> #include <private/qfontengine_p.h> @@ -74,6 +75,8 @@ namespace { } +Q_DECLARE_LOGGING_CATEGORY(lcSgText) + /*! Creates an empty QQuickTextNode */ @@ -261,10 +264,18 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay QVarLengthArray<QTextLayout::FormatRange> colorChanges; engine.mergeFormats(textLayout, &colorChanges); + // If there's a lot of text, transform the window's bounds into the Text item's space + // and then insert only the range of lines that can possibly be visible within that viewport. + QRectF viewport; + if (textLayout->text().size() > QQuickTextPrivate::largeTextSizeThreshold && m_ownerElement->window()) { + viewport = m_ownerElement->mapRectFromScene(m_ownerElement->window()->contentItem()->boundingRect()); + qCDebug(lcSgText) << "text viewport" << viewport; + } lineCount = lineCount >= 0 ? qMin(lineStart + lineCount, textLayout->lineCount()) : textLayout->lineCount(); + bool inViewport = false; for (int i=lineStart; i<lineCount; ++i) { QTextLine line = textLayout->lineAt(i); @@ -279,9 +290,17 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay end += preeditLength; } #endif - - engine.setCurrentLine(line); - engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); + if (viewport.isNull() || (line.y() + line.height() > viewport.top() && line.y() < viewport.bottom())) { + if (!inViewport && !viewport.isNull()) + qCDebug(lcSgText) << "first line in viewport" << i << "@" << line.y(); + inViewport = true; + engine.setCurrentLine(line); + engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); + } else if (inViewport) { + Q_ASSERT(!viewport.isNull()); + qCDebug(lcSgText) << "first omitted line past bottom of viewport" << i << "@" << line.y(); + break; // went past the bottom of the viewport, so we're done + } } engine.addToSceneGraph(this, style, styleColor); |