Fix bounding rects of text items.
authorAndrew den Exter <andrew.den-exter@nokia.com>
Thu, 19 Apr 2012 01:08:28 +0000 (11:08 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 27 Apr 2012 04:27:46 +0000 (06:27 +0200)
Ensure the rectangles are correctly positioned with right
and center aligned text, not just sized correctly. Also add
padding to the clip rects so the cursor and styled text aren't
clipped at the item boundaries.

Change-Id: I03ef140589154ebd49b600b0a4c4fbeff845c10f
Reviewed-by: Yann Bodson <yann.bodson@nokia.com>
src/quick/items/qquicktext.cpp
src/quick/items/qquicktext_p.h
src/quick/items/qquicktextedit.cpp
src/quick/items/qquicktextedit_p.h
src/quick/items/qquicktextinput.cpp
src/quick/items/qquicktextinput_p.h
src/quick/items/qquicktextinput_p_p.h
tests/auto/quick/qquicktext/tst_qquicktext.cpp
tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
tests/auto/quick/qquicktextinput/data/horizontalAlignment_RightToLeft.qml
tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp

index 2407ade..4d39f0b 100644 (file)
@@ -2039,6 +2039,16 @@ QRectF QQuickText::boundingRect() const
     return rect;
 }
 
+QRectF QQuickText::clipRect() const
+{
+    Q_D(const QQuickText);
+
+    QRectF rect = QQuickImplicitSizeItem::clipRect();
+    if (d->style != Normal)
+        rect.adjust(-1, 0, 1, 2);
+    return rect;
+}
+
 /*! \internal */
 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
 {
index e643d1d..e69159e 100644 (file)
@@ -197,6 +197,7 @@ public:
     qreal contentHeight() const;
 
     QRectF boundingRect() const;
+    QRectF clipRect() const;
     Q_INVOKABLE void doLayout();
 
 Q_SIGNALS:
index 59d78ac..cd5f262 100644 (file)
@@ -1818,7 +1818,48 @@ void QQuickTextEdit::updateSelectionMarkers()
 QRectF QQuickTextEdit::boundingRect() const
 {
     Q_D(const QQuickTextEdit);
-    QRectF r = QQuickImplicitSizeItem::boundingRect();
+    QRectF r(0, -d->yoff, d->contentSize.width(), d->contentSize.height());
+    int cursorWidth = 1;
+    if (d->cursor)
+        cursorWidth = 0;
+    else if (!d->document->isEmpty())
+        cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
+
+    // Could include font max left/right bearings to either side of rectangle.
+
+    r.setRight(r.right() + cursorWidth);
+
+    qreal h = height();
+    switch (d->vAlign) {
+    case AlignTop:
+        break;
+    case AlignBottom:
+        r.moveTop(h - r.height());
+        break;
+    case AlignVCenter:
+        r.moveTop((h - r.height()) / 2);
+        break;
+    }
+
+    qreal w = width();
+    switch (d->hAlign) {
+    case AlignLeft:
+        break;
+    case AlignRight:
+        r.moveLeft(w - r.width());
+        break;
+    case AlignHCenter:
+        r.moveLeft((w - r.width()) / 2);
+        break;
+    }
+
+    return r;
+}
+
+QRectF QQuickTextEdit::clipRect() const
+{
+    Q_D(const QQuickTextEdit);
+    QRectF r = QQuickImplicitSizeItem::clipRect();
     int cursorWidth = 1;
     if (d->cursor)
         cursorWidth = d->cursor->width();
@@ -1828,7 +1869,7 @@ QRectF QQuickTextEdit::boundingRect() const
     // Could include font max left/right bearings to either side of rectangle.
 
     r.setRight(r.right() + cursorWidth);
-    return r.translated(0,d->yoff);
+    return r;
 }
 
 qreal QQuickTextEditPrivate::getImplicitWidth() const
@@ -1903,7 +1944,7 @@ void QQuickTextEdit::updateSize()
         if (!widthValid() && !d->requireImplicitWidth)
             iWidth = newWidth;
 
-        qreal newHeight = d->document->isEmpty() ? fm.height() : d->document->size().height();
+        qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
 
         if (iWidth > -1)
             setImplicitSize(iWidth, newHeight);
index b28ec9d..9f904ca 100644 (file)
@@ -229,6 +229,7 @@ public:
     Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
 
     QRectF boundingRect() const;
+    QRectF clipRect() const;
 
     bool isInputMethodComposing() const;
 
index 8637294..c9019c6 100644 (file)
@@ -1655,7 +1655,7 @@ void QQuickTextInputPrivate::updateVerticalScroll()
     Q_Q(QQuickTextInput);
     const int preeditLength = m_textLayout.preeditAreaText().length();
     const qreal height = qMax<qreal>(0, q->height());
-    qreal heightUsed = boundingRect.height();
+    qreal heightUsed = contentSize.height();
     qreal previousScroll = vscroll;
 
     if (!autoScroll || heightUsed <=  height) {
@@ -2265,7 +2265,7 @@ bool QQuickTextInput::canRedo() const
 qreal QQuickTextInput::contentWidth() const
 {
     Q_D(const QQuickTextInput);
-    return d->boundingRect.width();
+    return d->contentSize.width();
 }
 
 /*!
@@ -2278,7 +2278,7 @@ qreal QQuickTextInput::contentWidth() const
 qreal QQuickTextInput::contentHeight() const
 {
     Q_D(const QQuickTextInput);
-    return d->boundingRect.height();
+    return d->contentSize.height();
 }
 
 void QQuickTextInput::moveCursorSelection(int position)
@@ -2597,10 +2597,36 @@ QRectF QQuickTextInput::boundingRect() const
 {
     Q_D(const QQuickTextInput);
 
+    int cursorWidth = d->cursorItem ? 0 : 1;
+
+    qreal hscroll = d->hscroll;
+    if (!d->autoScroll || d->contentSize.width() < width()) {
+        switch (effectiveHAlign()) {
+        case AlignLeft:
+            break;
+        case AlignRight:
+            hscroll += d->contentSize.width() - width();
+            break;
+        case AlignHCenter:
+            hscroll += (d->contentSize.width() - width()) / 2;
+            break;
+        }
+    }
+
+    // Could include font max left/right bearings to either side of rectangle.
+    QRectF r(-hscroll, -d->vscroll, d->contentSize.width(), d->contentSize.height());
+    r.setRight(r.right() + cursorWidth);
+    return r;
+}
+
+QRectF QQuickTextInput::clipRect() const
+{
+    Q_D(const QQuickTextInput);
+
     int cursorWidth = d->cursorItem ? d->cursorItem->width() : 1;
 
     // Could include font max left/right bearings to either side of rectangle.
-    QRectF r = QQuickImplicitSizeItem::boundingRect();
+    QRectF r = QQuickImplicitSizeItem::clipRect();
     r.setRight(r.right() + cursorWidth);
     return r;
 }
@@ -2727,7 +2753,6 @@ void QQuickTextInputPrivate::updateLayout()
     if (!q->isComponentComplete())
         return;
 
-    const QRectF previousRect = boundingRect;
 
     QTextOption option = m_textLayout.textOption();
     option.setTextDirection(layoutDirection());
@@ -2736,8 +2761,8 @@ void QQuickTextInputPrivate::updateLayout()
     m_textLayout.setTextOption(option);
     m_textLayout.setFont(font);
 
-    boundingRect = QRectF();
     m_textLayout.beginLayout();
+
     QTextLine line = m_textLayout.createLine();
     if (requireImplicitWidth) {
         line.setLineWidth(INT_MAX);
@@ -2750,12 +2775,14 @@ void QQuickTextInputPrivate::updateLayout()
     }
     qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
     qreal height = 0;
+    qreal width = 0;
     do {
         line.setLineWidth(lineWidth);
-        line.setPosition(QPointF(line.position().x(), height));
-        boundingRect = boundingRect.united(line.naturalTextRect());
+        line.setPosition(QPointF(0, height));
 
         height += line.height();
+        width = qMax(width, line.naturalTextWidth());
+
         line = m_textLayout.createLine();
     } while (line.isValid());
     m_textLayout.endLayout();
@@ -2765,15 +2792,18 @@ void QQuickTextInputPrivate::updateLayout()
 
     textLayoutDirty = true;
 
+    const QSizeF previousSize = contentSize;
+    contentSize = QSizeF(width, height);
+
     updateType = UpdatePaintNode;
     q->update();
 
     if (!requireImplicitWidth && !q->widthValid())
-        q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
+        q->setImplicitSize(width, height);
     else
-        q->setImplicitHeight(qCeil(boundingRect.height()));
+        q->setImplicitHeight(height);
 
-    if (previousRect != boundingRect)
+    if (previousSize != contentSize)
         emit q->contentSizeChanged();
 }
 
index 9d32eee..9a3be0f 100644 (file)
@@ -245,6 +245,8 @@ public:
     QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
 
     QRectF boundingRect() const;
+    QRectF clipRect() const;
+
 #ifndef QT_NO_CLIPBOARD
     bool canPaste() const;
 #endif
index 1bc2cf5..d952b27 100644 (file)
@@ -172,7 +172,7 @@ public:
     };
 
     QElapsedTimer tripleClickTimer;
-    QRectF boundingRect;
+    QSizeF contentSize;
     QPointF pressPos;
     QPointF tripleClickStartPoint;
 
index 297fa01..d1899a6 100644 (file)
@@ -114,6 +114,7 @@ private slots:
 
     void boundingRect_data();
     void boundingRect();
+    void clipRect();
     void lineLaidOut();
 
     void imgTagsBaseUrl_data();
@@ -1802,6 +1803,50 @@ void tst_qquicktext::boundingRect()
     QVERIFY(text->boundingRect().height() > line.height());
 }
 
+void tst_qquicktext::clipRect()
+{
+    QQmlComponent component(&engine);
+    component.setData("import QtQuick 2.0\n Text {}", QUrl());
+    QScopedPointer<QObject> object(component.create());
+    QQuickText *text = qobject_cast<QQuickText *>(object.data());
+    QVERIFY(text);
+
+    QTextLayout layout;
+    layout.setFont(text->font());
+
+    QCOMPARE(text->clipRect().x(), qreal(0));
+    QCOMPARE(text->clipRect().y(), qreal(0));
+    QCOMPARE(text->clipRect().width(), text->width());
+    QCOMPARE(text->clipRect().height(), text->height());
+
+    text->setText("Hello World");
+
+    QCOMPARE(text->clipRect().x(), qreal(0));
+    QCOMPARE(text->clipRect().y(), qreal(0));
+    QCOMPARE(text->clipRect().width(), text->width());
+    QCOMPARE(text->clipRect().height(), text->height());
+
+    // Clip rect follows the item not content dimensions.
+    text->setWidth(text->width() / 2);
+    QCOMPARE(text->clipRect().x(), qreal(0));
+    QCOMPARE(text->clipRect().y(), qreal(0));
+    QCOMPARE(text->clipRect().width(), text->width());
+    QCOMPARE(text->clipRect().height(), text->height());
+
+    text->setHeight(text->height() * 2);
+    QCOMPARE(text->clipRect().x(), qreal(0));
+    QCOMPARE(text->clipRect().y(), qreal(0));
+    QCOMPARE(text->clipRect().width(), text->width());
+    QCOMPARE(text->clipRect().height(), text->height());
+
+    // Setting a style adds a small amount of padding to the clip rect.
+    text->setStyle(QQuickText::Outline);
+    QCOMPARE(text->clipRect().x(), qreal(-1));
+    QCOMPARE(text->clipRect().y(), qreal(0));
+    QCOMPARE(text->clipRect().width(), text->width() + 2);
+    QCOMPARE(text->clipRect().height(), text->height() + 2);
+}
+
 void tst_qquicktext::lineLaidOut()
 {
     QQuickView *canvas = createView(testFile("lineLayout.qml"));
index 1a5f52f..9c34a7c 100644 (file)
@@ -152,6 +152,8 @@ private slots:
     void implicitSize_data();
     void implicitSize();
     void contentSize();
+    void boundingRect();
+    void clipRect();
     void implicitSizeBinding_data();
     void implicitSizeBinding();
 
@@ -2657,6 +2659,152 @@ void tst_qquicktextedit::implicitSizeBinding()
     QCOMPARE(textObject->height(), textObject->implicitHeight());
 }
 
+void tst_qquicktextedit::clipRect()
+{
+    QQmlComponent component(&engine);
+    component.setData("import QtQuick 2.0\n TextEdit {}", QUrl());
+    QScopedPointer<QObject> object(component.create());
+    QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data());
+    QVERIFY(edit);
+
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+
+    QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width());
+    QCOMPARE(edit->clipRect().height(), edit->height());
+
+    edit->setText("Hello World");
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+    // XXX: TextEdit allows an extra 3 pixels boundary for the cursor beyond it's width for non
+    // empty text. TextInput doesn't.
+    QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
+    QCOMPARE(edit->clipRect().height(), edit->height());
+
+    // clip rect shouldn't exceed the size of the item, expect for the cursor width;
+    edit->setWidth(edit->width() / 2);
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+    QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
+    QCOMPARE(edit->clipRect().height(), edit->height());
+
+    edit->setHeight(edit->height() * 2);
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+    QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
+    QCOMPARE(edit->clipRect().height(), edit->height());
+
+    QQmlComponent cursorComponent(&engine);
+    cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
+
+    edit->setCursorDelegate(&cursorComponent);
+
+    // If a cursor delegate is used it's size should determine the excess width.
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+    QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
+    QCOMPARE(edit->clipRect().height(), edit->height());
+
+    // Alignment and wrapping don't affect the clip rect.
+    edit->setHAlign(QQuickTextEdit::AlignRight);
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+    QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
+    QCOMPARE(edit->clipRect().height(), edit->height());
+
+    edit->setWrapMode(QQuickTextEdit::Wrap);
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+    QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
+    QCOMPARE(edit->clipRect().height(), edit->height());
+
+    edit->setVAlign(QQuickTextEdit::AlignBottom);
+    QCOMPARE(edit->clipRect().x(), qreal(0));
+    QCOMPARE(edit->clipRect().y(), qreal(0));
+    QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
+    QCOMPARE(edit->clipRect().height(), edit->height());
+}
+
+void tst_qquicktextedit::boundingRect()
+{
+    QQmlComponent component(&engine);
+    component.setData("import QtQuick 2.0\n TextEdit {}", QUrl());
+    QScopedPointer<QObject> object(component.create());
+    QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data());
+    QVERIFY(edit);
+
+    QTextLayout layout;
+    layout.setFont(edit->font());
+
+    if (!qmlDisableDistanceField()) {
+        QTextOption option;
+        option.setUseDesignMetrics(true);
+        layout.setTextOption(option);
+    }
+    layout.beginLayout();
+    QTextLine line = layout.createLine();
+    layout.endLayout();
+
+    QCOMPARE(edit->boundingRect().x(), qreal(0));
+    QCOMPARE(edit->boundingRect().y(), qreal(0));
+    QCOMPARE(edit->boundingRect().width(), edit->cursorRectangle().width());
+    QCOMPARE(edit->boundingRect().height(), line.height());
+
+    edit->setText("Hello World");
+
+    layout.setText(edit->text());
+    layout.beginLayout();
+    line = layout.createLine();
+    layout.endLayout();
+
+    QCOMPARE(edit->boundingRect().x(), qreal(0));
+    QCOMPARE(edit->boundingRect().y(), qreal(0));
+    QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
+    QCOMPARE(edit->boundingRect().height(), line.height());
+
+    // the size of the bounding rect shouldn't be bounded by the size of item.
+    edit->setWidth(edit->width() / 2);
+    QCOMPARE(edit->boundingRect().x(), qreal(0));
+    QCOMPARE(edit->boundingRect().y(), qreal(0));
+    QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
+    QCOMPARE(edit->boundingRect().height(), line.height());
+
+    edit->setHeight(edit->height() * 2);
+    QCOMPARE(edit->boundingRect().x(), qreal(0));
+    QCOMPARE(edit->boundingRect().y(), qreal(0));
+    QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
+    QCOMPARE(edit->boundingRect().height(), line.height());
+
+    QQmlComponent cursorComponent(&engine);
+    cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
+
+    edit->setCursorDelegate(&cursorComponent);
+
+    // Don't include the size of a cursor delegate as it has its own bounding rect.
+    QCOMPARE(edit->boundingRect().x(), qreal(0));
+    QCOMPARE(edit->boundingRect().y(), qreal(0));
+    QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
+    QCOMPARE(edit->boundingRect().height(), line.height());
+
+    edit->setHAlign(QQuickTextEdit::AlignRight);
+    QCOMPARE(edit->boundingRect().x(), edit->width() - line.naturalTextWidth());
+    QCOMPARE(edit->boundingRect().y(), qreal(0));
+    QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
+    QCOMPARE(edit->boundingRect().height(), line.height());
+
+    edit->setWrapMode(QQuickTextEdit::Wrap);
+    QCOMPARE(edit->boundingRect().right(), edit->width());
+    QCOMPARE(edit->boundingRect().y(), qreal(0));
+    QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
+    QVERIFY(edit->boundingRect().height() > line.height());
+
+    edit->setVAlign(QQuickTextEdit::AlignBottom);
+    QCOMPARE(edit->boundingRect().right(), edit->width());
+    QCOMPARE(edit->boundingRect().bottom(), edit->height());
+    QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
+    QVERIFY(edit->boundingRect().height() > line.height());
+}
+
 void tst_qquicktextedit::preeditCursorRectangle()
 {
     QString preeditText = "super";
index 5f88025..d14caea 100644 (file)
@@ -19,6 +19,8 @@ Rectangle {
             anchors.fill: parent
             text: top.text
             focus: true
+
+            cursorDelegate: Rectangle { }
         }
     }
 }
index 6f75698..27e557b 100644 (file)
@@ -129,6 +129,7 @@ private slots:
     void horizontalAlignment_RightToLeft();
     void verticalAlignment();
 
+    void clipRect();
     void boundingRect();
 
     void positionAt();
@@ -1309,43 +1310,41 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
 
     const QString rtlText = textInput->text();
 
-    QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
-    QVERIFY(textInputPrivate != 0);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // implicit alignment should follow the reading direction of RTL text
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // explicitly left aligned
     textInput->setHAlign(QQuickTextInput::AlignLeft);
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
-    QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
+    QCOMPARE(textInput->boundingRect().left(), qreal(0));
 
     // explicitly right aligned
     textInput->setHAlign(QQuickTextInput::AlignRight);
     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // explicitly center aligned
     textInput->setHAlign(QQuickTextInput::AlignHCenter);
     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
-    QVERIFY(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll > 0);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll < textInput->width());
+    QVERIFY(textInput->boundingRect().left() > 0);
+    QVERIFY(textInput->boundingRect().right() < textInput->width());
 
     // reseted alignment should go back to following the text reading direction
     textInput->resetHAlign();
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // mirror the text item
     QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
@@ -1353,21 +1352,21 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
     // mirrored implicit alignment should continue to follow the reading direction of the text
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // explicitly right aligned behaves as left aligned
     textInput->setHAlign(QQuickTextInput::AlignRight);
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
-    QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
+    QCOMPARE(textInput->boundingRect().left(), qreal(0));
 
     // mirrored explicitly left aligned behaves as right aligned
     textInput->setHAlign(QQuickTextInput::AlignLeft);
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // disable mirroring
     QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
@@ -1377,7 +1376,7 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
     // English text should be implicitly left aligned
     textInput->setText("Hello world!");
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
-    QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
+    QCOMPARE(textInput->boundingRect().left(), qreal(0));
 
     canvas.requestActivateWindow();
     QTest::qWaitForWindowShown(&canvas);
@@ -1400,15 +1399,15 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
     platformInputContext.setInputDirection(Qt::LeftToRight);
     QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
-    QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
+    QCOMPARE(textInput->boundingRect().left(), qreal(0));
 
     QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
     platformInputContext.setInputDirection(Qt::RightToLeft);
     QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
     QCOMPARE(cursorRectangleSpy.count(), 1);
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // set input direction while having content
     platformInputContext.setInputDirection(Qt::LeftToRight);
@@ -1417,8 +1416,8 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
     QTest::keyClick(&canvas, Qt::Key_Backspace);
     QVERIFY(textInput->text().isEmpty());
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     // input direction changed while not having focus
     platformInputContext.setInputDirection(Qt::LeftToRight);
@@ -1426,13 +1425,13 @@ void tst_qquicktextinput::horizontalAlignment_RightToLeft()
     platformInputContext.setInputDirection(Qt::RightToLeft);
     textInput->setFocus(true);
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 
     textInput->setHAlign(QQuickTextInput::AlignRight);
     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
-    QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
+    QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
+    QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
 }
 
 void tst_qquicktextinput::verticalAlignment()
@@ -1442,33 +1441,30 @@ void tst_qquicktextinput::verticalAlignment()
     QVERIFY(textInput != 0);
     canvas.show();
 
-    QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
-    QVERIFY(textInputPrivate != 0);
-
     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
-    QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll < canvas.height() / 2);
+    QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
     QVERIFY(textInput->cursorRectangle().bottom() < canvas.height() / 2);
     QVERIFY(textInput->positionToRectangle(0).bottom() < canvas.height() / 2);
 
     // bottom aligned
     textInput->setVAlign(QQuickTextInput::AlignBottom);
     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
-    QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll > canvas.height() / 2);
+    QVERIFY(textInput->boundingRect().top() > canvas.height() / 2);
     QVERIFY(textInput->cursorRectangle().top() > canvas.height() / 2);
     QVERIFY(textInput->positionToRectangle(0).top() > canvas.height() / 2);
 
     // explicitly center aligned
     textInput->setVAlign(QQuickTextInput::AlignVCenter);
     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
-    QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll < canvas.height() / 2);
-    QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll > canvas.height() / 2);
+    QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
+    QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
     QVERIFY(textInput->cursorRectangle().top() < canvas.height() / 2);
     QVERIFY(textInput->cursorRectangle().bottom() > canvas.height() / 2);
     QVERIFY(textInput->positionToRectangle(0).top() < canvas.height() / 2);
     QVERIFY(textInput->positionToRectangle(0).bottom() > canvas.height() / 2);
 }
 
-void tst_qquicktextinput::boundingRect()
+void tst_qquicktextinput::clipRect()
 {
     QQmlComponent component(&engine);
     component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
@@ -1476,21 +1472,29 @@ void tst_qquicktextinput::boundingRect()
     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
     QVERIFY(input);
 
-    QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
-    QCOMPARE(input->height(), input->boundingRect().height());
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
+    QCOMPARE(input->clipRect().height(), input->height());
 
     input->setText("Hello World");
-    QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
-    QCOMPARE(input->height(), input->boundingRect().height());
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
+    QCOMPARE(input->clipRect().height(), input->height());
 
-    // bounding rect shouldn't exceed the size of the item, expect for the cursor width;
+    // clip rect shouldn't exceed the size of the item, expect for the cursor width;
     input->setWidth(input->width() / 2);
-    QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
-    QCOMPARE(input->height(), input->boundingRect().height());
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
+    QCOMPARE(input->clipRect().height(), input->height());
 
     input->setHeight(input->height() * 2);
-    QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
-    QCOMPARE(input->height(), input->boundingRect().height());
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
+    QCOMPARE(input->clipRect().height(), input->height());
 
     QQmlComponent cursorComponent(&engine);
     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
@@ -1498,8 +1502,122 @@ void tst_qquicktextinput::boundingRect()
     input->setCursorDelegate(&cursorComponent);
 
     // If a cursor delegate is used it's size should determine the excess width.
-    QCOMPARE(input->width() + 8, input->boundingRect().width());
-    QCOMPARE(input->height(), input->boundingRect().height());
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + 8);
+    QCOMPARE(input->clipRect().height(), input->height());
+
+    // Alignment, auto scroll, wrapping all don't affect the clip rect.
+    input->setAutoScroll(false);
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + 8);
+    QCOMPARE(input->clipRect().height(), input->height());
+
+    input->setHAlign(QQuickTextInput::AlignRight);
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + 8);
+    QCOMPARE(input->clipRect().height(), input->height());
+
+    input->setWrapMode(QQuickTextInput::Wrap);
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + 8);
+    QCOMPARE(input->clipRect().height(), input->height());
+
+    input->setVAlign(QQuickTextInput::AlignBottom);
+    QCOMPARE(input->clipRect().x(), qreal(0));
+    QCOMPARE(input->clipRect().y(), qreal(0));
+    QCOMPARE(input->clipRect().width(), input->width() + 8);
+    QCOMPARE(input->clipRect().height(), input->height());
+}
+
+void tst_qquicktextinput::boundingRect()
+{
+    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);
+
+    QTextLayout layout;
+    layout.setFont(input->font());
+
+    if (!qmlDisableDistanceField()) {
+        QTextOption option;
+        option.setUseDesignMetrics(true);
+        layout.setTextOption(option);
+    }
+    layout.beginLayout();
+    QTextLine line = layout.createLine();
+    layout.endLayout();
+
+    QCOMPARE(input->boundingRect().x(), qreal(0));
+    QCOMPARE(input->boundingRect().y(), qreal(0));
+    QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
+    QCOMPARE(input->boundingRect().height(), line.height());
+
+    input->setText("Hello World");
+
+    layout.setText(input->text());
+    layout.beginLayout();
+    line = layout.createLine();
+    layout.endLayout();
+
+    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());
+
+    // the size of the bounding rect shouldn't be bounded by the size of item.
+    input->setWidth(input->width() / 2);
+    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());
+
+    input->setHeight(input->height() * 2);
+    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());
+
+    QQmlComponent cursorComponent(&engine);
+    cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
+
+    input->setCursorDelegate(&cursorComponent);
+
+    // Don't include the size of a cursor delegate as it has its own bounding rect.
+    QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
+    QCOMPARE(input->boundingRect().y(), qreal(0));
+    QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
+    QCOMPARE(input->boundingRect().height(), line.height());
+
+    // Bounding rect left aligned when auto scroll is disabled;
+    input->setAutoScroll(false);
+    QCOMPARE(input->boundingRect().x(), qreal(0));
+    QCOMPARE(input->boundingRect().y(), qreal(0));
+    QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
+    QCOMPARE(input->boundingRect().height(), line.height());
+
+    input->setHAlign(QQuickTextInput::AlignRight);
+    QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
+    QCOMPARE(input->boundingRect().y(), qreal(0));
+    QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
+    QCOMPARE(input->boundingRect().height(), line.height());
+
+    input->setWrapMode(QQuickTextInput::Wrap);
+    QCOMPARE(input->boundingRect().right(), input->width());
+    QCOMPARE(input->boundingRect().y(), qreal(0));
+    QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
+    QVERIFY(input->boundingRect().height() > line.height());
+
+    input->setVAlign(QQuickTextInput::AlignBottom);
+    QCOMPARE(input->boundingRect().right(), input->width());
+    QCOMPARE(input->boundingRect().bottom(), input->height());
+    QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
+    QVERIFY(input->boundingRect().height() > line.height());
 }
 
 void tst_qquicktextinput::positionAt()