diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 41eb5c0bde9a2c84ae765985a4a8694b10105c01..883d0c744db6fd5270f406627e91e129861c6bed 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -881,6 +881,8 @@ void QQuickTextInput::setFocusOnPress(bool b) Whether the TextInput should scroll when the text is longer than the width. By default this is set to true. + + \sa ensureVisible() */ bool QQuickTextInput::autoScroll() const { @@ -1721,33 +1723,24 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry, QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); } -void QQuickTextInputPrivate::updateHorizontalScroll() +void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int preeditLength) { Q_Q(QQuickTextInput); -#ifndef QT_NO_IM - QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor); - const int preeditLength = m_textLayout.preeditAreaText().length(); -#else - QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor); -#endif + QTextLine textLine = m_textLayout.lineForTextPosition(position + preeditCursor); const qreal width = qMax<qreal>(0, q->width()); qreal cix = 0; qreal widthUsed = 0; - if (currentLine.isValid()) { -#ifndef QT_NO_IM - cix = currentLine.cursorToX(m_cursor + preeditLength); -#else - cix = currentLine.cursorToX(m_cursor); -#endif + if (textLine.isValid()) { + cix = textLine.cursorToX(position + preeditLength); const qreal cursorWidth = cix >= 0 ? cix : width - cix; - widthUsed = qMax(currentLine.naturalTextWidth(), cursorWidth); + widthUsed = qMax(textLine.naturalTextWidth(), cursorWidth); } int previousScroll = hscroll; - if (!autoScroll || widthUsed <= width || m_echoMode == QQuickTextInput::NoEcho) { + if (widthUsed <= width) { hscroll = 0; } else { - Q_ASSERT(currentLine.isValid()); + Q_ASSERT(textLine.isValid()); if (cix - hscroll >= width) { // text doesn't fit, cursor is to the right of br (scroll right) hscroll = cix - width; @@ -1767,7 +1760,7 @@ void QQuickTextInputPrivate::updateHorizontalScroll() if (preeditLength > 0) { // check to ensure long pre-edit text doesn't push the cursor // off to the left - cix = currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1)); + cix = textLine.cursorToX(position + qMax(0, preeditCursor - 1)); if (cix < hscroll) hscroll = cix; } @@ -1777,6 +1770,20 @@ void QQuickTextInputPrivate::updateHorizontalScroll() textLayoutDirty = true; } +void QQuickTextInputPrivate::updateHorizontalScroll() +{ + if (autoScroll && m_echoMode != QQuickTextInput::NoEcho) { +#ifndef QT_NO_IM + const int preeditLength = m_textLayout.preeditAreaText().length(); + ensureVisible(m_cursor, m_preeditCursor, preeditLength); +#else + ensureVisible(m_cursor); +#endif + } else { + hscroll = 0; + } +} + void QQuickTextInputPrivate::updateVerticalScroll() { Q_Q(QQuickTextInput); @@ -2634,14 +2641,16 @@ void QQuickTextInputPrivate::init() } } -void QQuickTextInput::updateCursorRectangle() +void QQuickTextInput::updateCursorRectangle(bool scroll) { Q_D(QQuickTextInput); if (!isComponentComplete()) return; - d->updateHorizontalScroll(); - d->updateVerticalScroll(); + if (scroll) { + d->updateHorizontalScroll(); + d->updateVerticalScroll(); + } d->updateType = QQuickTextInputPrivate::UpdatePaintNode; update(); emit cursorRectangleChanged(); @@ -4402,5 +4411,21 @@ void QQuickTextInputPrivate::deleteEndOfLine() finishChange(priorState); } +/*! + \qmlmethod QtQuick::TextInput::ensureVisible(int position) + \since 5.4 + + Scrolls the contents of the text input so that the specified character + \a position is visible inside the boundaries of the text input. + + \sa autoScroll +*/ +void QQuickTextInput::ensureVisible(int position) +{ + Q_D(QQuickTextInput); + d->ensureVisible(position); + updateCursorRectangle(false); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 211aba87038fe3dea64eea94e7175ba459faa140..66cabb9cfef33e873aaac0eb7a0693663bd64c18 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -355,11 +355,12 @@ public Q_SLOTS: void redo(); void insert(int position, const QString &text); void remove(int start, int end); + Q_REVISION(3) void ensureVisible(int position); private Q_SLOTS: void selectionChanged(); void createCursor(); - void updateCursorRectangle(); + void updateCursorRectangle(bool scroll = true); void q_canPasteChanged(); void q_updateAlignment(); void triggerPreprocess(); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 2cf127608eca40dbb622e68c22c511afc5f42ada..facc6356a1f8c1357739b958a5a5bee631fe62e7 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -148,6 +148,7 @@ public: void init(); void startCreatingCursor(); + void ensureVisible(int position, int preeditCursor = 0, int preeditLength = 0); void updateHorizontalScroll(); void updateVerticalScroll(); bool determineHorizontalAlignment(); diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 8451b527cef4eee05abba29065e116677c8eceaa..fc1be16bc867ee01663dd1b2256ef0504b419915 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -232,6 +232,8 @@ private slots: void baselineOffset_data(); void baselineOffset(); + void ensureVisible(); + private: void simulateKey(QWindow *, int key); @@ -6464,6 +6466,50 @@ void tst_qquicktextinput::baselineOffset() } } +void tst_qquicktextinput::ensureVisible() +{ + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\n TextInput {}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data()); + QVERIFY(input); + + input->setWidth(QFontMetrics(input->font()).averageCharWidth() * 3); + input->setText("Hello World"); + + QTextLayout layout; + layout.setText(input->text()); + layout.setFont(input->font()); + + if (!qmlDisableDistanceField()) { + QTextOption option; + option.setUseDesignMetrics(true); + layout.setTextOption(option); + } + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + + input->ensureVisible(0); + + QCOMPARE(input->boundingRect().x(), qreal(0)); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); + + QSignalSpy cursorSpy(input, SIGNAL(cursorRectangleChanged())); + QVERIFY(cursorSpy.isValid()); + + input->ensureVisible(input->length()); + + QCOMPARE(cursorSpy.count(), 1); + + QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth()); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); +} + QTEST_MAIN(tst_qquicktextinput) #include "tst_qquicktextinput.moc"