aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquicktextedit.cpp16
-rw-r--r--src/quick/items/qquicktextedit_p_p.h8
-rw-r--r--tests/auto/quick/qquicktextedit/data/threeLines.qml7
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp87
4 files changed, 116 insertions, 2 deletions
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 4752e3e299..0df71fc559 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -2255,11 +2255,13 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
}
}
+ bool createdNodeInView = false;
if (inView) {
if (!engine.hasContents()) {
if (node && !node->parent())
d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
node = d->createTextNode();
+ createdNodeInView = true;
updateNodeTransform(node, nodeOffset);
nodeStart = block.position();
}
@@ -2269,13 +2271,13 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
break; // last node that needed replacing or last block of the frame
-
QList<int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position());
if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
currentNodeSize = 0;
if (!node->parent())
d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
- node = d->createTextNode();
+ if (!createdNodeInView)
+ node = d->createTextNode();
resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
nodeStart = block.next().position();
}
@@ -3322,6 +3324,16 @@ void QQuickTextEdit::clear()
d->control->clear();
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickTextEditPrivate::Node &n)
+{
+ QDebugStateSaver saver(debug);
+ debug.space();
+ debug << "Node(startPos:" << n.m_startPos << "dirty:" << n.m_dirty << n.m_node << ')';
+ return debug;
+}
+#endif
+
QT_END_NAMESPACE
#include "moc_qquicktextedit_p.cpp"
diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h
index 4ec2752eb5..1f0099e00e 100644
--- a/src/quick/items/qquicktextedit_p_p.h
+++ b/src/quick/items/qquicktextedit_p_p.h
@@ -89,6 +89,10 @@ public:
int m_startPos;
QQuickTextNode* m_node;
bool m_dirty;
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug, const Node &);
+#endif
};
typedef QList<Node>::iterator TextNodeIterator;
@@ -237,6 +241,10 @@ public:
static const int largeTextSizeThreshold;
};
+#ifndef QT_NO_DEBUG_STREAM
+QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug debug, const QQuickTextEditPrivate::Node &);
+#endif
+
QT_END_NAMESPACE
#endif // QQUICKTEXTEDIT_P_P_H
diff --git a/tests/auto/quick/qquicktextedit/data/threeLines.qml b/tests/auto/quick/qquicktextedit/data/threeLines.qml
new file mode 100644
index 0000000000..cee03bfa15
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/data/threeLines.qml
@@ -0,0 +1,7 @@
+import QtQuick
+import Qt.test 1.0
+
+NodeCheckerTextEdit {
+ width: 200; height: 100
+ text: "Line 1\nLine 2\nLine 3\n"
+}
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index fbbd36e18a..66d1d29ae6 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -101,6 +101,8 @@ public:
tst_qquicktextedit();
private slots:
+ void initTestCase() override;
+
void cleanup();
void text();
void width();
@@ -182,6 +184,7 @@ private slots:
void implicitSizeBinding();
void largeTextObservesViewport_data();
void largeTextObservesViewport();
+ void renderingAroundSelection();
void signal_editingfinished();
@@ -369,6 +372,56 @@ tst_qquicktextedit::tst_qquicktextedit()
//
}
+class NodeCheckerTextEdit : public QQuickTextEdit
+{
+public:
+ NodeCheckerTextEdit(QQuickItem *parent = nullptr) : QQuickTextEdit(parent) {}
+
+ void populateLinePositions(QSGNode *node)
+ {
+ linePositions.clear();
+ lastLinePosition = 0;
+ QSGNode *ch = node->firstChild();
+ while (ch != node->lastChild()) {
+ QCOMPARE(ch->type(), QSGNode::TransformNodeType);
+ QSGTransformNode *tn = static_cast<QSGTransformNode *>(ch);
+ int y = 0;
+ if (!tn->matrix().isIdentity())
+ y = tn->matrix().column(3).y();
+ if (tn->childCount() == 0) {
+ // A TransformNode with no children is a waste of memory.
+ // So far, QQuickTextEdit still creates a couple of extras.
+ qCDebug(lcTests) << "ignoring leaf TransformNode" << tn << "@ y" << y;
+ } else {
+ qCDebug(lcTests) << "child" << tn << "@ y" << y << "has children" << tn->childCount();
+ if (!linePositions.contains(y)) {
+ linePositions.append(y);
+ lastLinePosition = qMax(lastLinePosition, y);
+ }
+ }
+ ch = ch->nextSibling();
+ }
+ std::sort(linePositions.begin(), linePositions.end());
+ }
+
+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override
+ {
+ QSGNode *ret = QQuickTextEdit::updatePaintNode(node, data);
+ qCDebug(lcTests) << "updated root node" << ret;
+ populateLinePositions(ret);
+ return ret;
+ }
+
+ QList<int> linePositions;
+ int lastLinePosition;
+};
+
+void tst_qquicktextedit::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ qmlRegisterType<NodeCheckerTextEdit>("Qt.test", 1, 0, "NodeCheckerTextEdit");
+}
+
void tst_qquicktextedit::cleanup()
{
// ensure not even skipped tests with custom input context leave it dangling
@@ -3824,6 +3877,40 @@ void tst_qquicktextedit::largeTextObservesViewport()
QCOMPARE(textPriv->cursorItem->isVisible(), textPriv->renderedRegion.intersects(textItem->cursorRectangle()));
}
+void tst_qquicktextedit::renderingAroundSelection()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("threeLines.qml")));
+ NodeCheckerTextEdit *textItem = qmlobject_cast<NodeCheckerTextEdit*>(window.rootObject());
+ QVERIFY(textItem);
+ QTRY_VERIFY(textItem->linePositions.count() > 0);
+ const auto linePositions = textItem->linePositions;
+ const int lastLinePosition = textItem->lastLinePosition;
+ QQuickTextEditPrivate *textPriv = QQuickTextEditPrivate::get(textItem);
+ QSignalSpy renderSpy(&window, &QQuickWindow::afterRendering);
+
+ if (lcTests().isDebugEnabled())
+ QTest::qWait(500); // for visual check; not needed in CI
+
+ const int renderCount = renderSpy.count();
+ QPoint p1 = textItem->mapToScene(textItem->positionToRectangle(8).center()).toPoint();
+ QPoint p2 = textItem->mapToScene(textItem->positionToRectangle(10).center()).toPoint();
+ qCDebug(lcTests) << "drag from" << p1 << "to" << p2;
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTest::mouseMove(&window, p2);
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p2);
+ // ensure that QQuickTextEdit::updatePaintNode() has a chance to run
+ QTRY_VERIFY(renderSpy.count() > renderCount);
+
+ if (lcTests().isDebugEnabled())
+ QTest::qWait(500); // for visual check; not needed in CI
+
+ qCDebug(lcTests) << "TextEdit's nodes" << textPriv->textNodeMap;
+ qCDebug(lcTests) << "font" << textItem->font() << "line positions" << textItem->linePositions << "should be" << linePositions;
+ QCOMPARE(textItem->lastLinePosition, lastLinePosition);
+ QTRY_COMPARE(textItem->linePositions, linePositions);
+}
+
void tst_qquicktextedit::signal_editingfinished()
{
QQuickView *window = new QQuickView(nullptr);