aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2021-08-16 17:58:27 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2021-09-29 16:14:44 +0200
commite57ba6916efec2a9506b5b12ed34e402c48955d1 (patch)
treee135ed41875a968739b459b0db7f9abe964534c7 /src
parent6a50f9350d8abf65e23c65daef76b862097ec7e8 (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>
Diffstat (limited to 'src')
-rw-r--r--src/quick/items/qquickflipable.cpp6
-rw-r--r--src/quick/items/qquickitem.cpp20
-rw-r--r--src/quick/items/qquickitem_p.h2
-rw-r--r--src/quick/items/qquicktext.cpp18
-rw-r--r--src/quick/items/qquicktext_p_p.h2
-rw-r--r--src/quick/items/qquicktextnode.cpp25
6 files changed, 64 insertions, 9 deletions
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 &centroid,
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);