All QWindow properties that have "window" in them have been renamed.
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquicktext / tst_qquicktext.cpp
index f4ba086..c5ffe7d 100644 (file)
@@ -1,38 +1,38 @@
 /****************************************************************************
 **
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the test suite of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
 ** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
 ** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
 **
 **
 ** $QT_END_LICENSE$
 
 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
 
+#define SERVER_PORT 14459
+#define SERVER_ADDR "http://127.0.0.1:14459"
+
 Q_DECLARE_METATYPE(QQuickText::TextFormat)
 
+QT_BEGIN_NAMESPACE
+extern void qt_setQtEnableTestFont(bool value);
+QT_END_NAMESPACE
+
 class tst_qquicktext : public QQmlDataTest
 {
     Q_OBJECT
@@ -72,11 +79,10 @@ private slots:
     void elide();
     void multilineElide_data();
     void multilineElide();
+    void implicitElide_data();
+    void implicitElide();
     void textFormat();
 
-    void alignments_data();
-    void alignments();
-
     void baseUrl();
     void embeddedImages_data();
     void embeddedImages();
@@ -88,10 +94,12 @@ private slots:
     void horizontalAlignment();
     void horizontalAlignment_RightToLeft();
     void verticalAlignment();
+    void hAlignImplicitWidth();
     void font();
     void style();
     void color();
     void smooth();
+    void renderType();
 
     // QQuickFontValueType
     void weight();
@@ -102,6 +110,7 @@ private slots:
     void letterSpacing();
     void wordSpacing();
 
+    void clickLink_data();
     void clickLink();
 
     void implicitSize_data();
@@ -109,10 +118,13 @@ private slots:
     void contentSize();
     void implicitSizeBinding_data();
     void implicitSizeBinding();
+    void geometryChanged();
 
     void boundingRect_data();
     void boundingRect();
+    void clipRect();
     void lineLaidOut();
+    void lineLaidOutRelayout();
 
     void imgTagsBaseUrl_data();
     void imgTagsBaseUrl();
@@ -131,6 +143,12 @@ private slots:
     void fontFormatSizes_data();
     void fontFormatSizes();
 
+    void baselineOffset_data();
+    void baselineOffset();
+
+    void htmlLists();
+    void htmlLists_data();
+
 private:
     QStringList standard;
     QStringList richText;
@@ -149,6 +167,7 @@ private:
     QQmlEngine engine;
 
     QQuickView *createView(const QString &filename);
+    int numberOfNonWhitePixels(int fromX, int toX, const QImage &image);
 };
 
 tst_qquicktext::tst_qquicktext()
@@ -202,14 +221,15 @@ tst_qquicktext::tst_qquicktext()
     // << "#AA0011DD"
     // << "#00F16B11";
     //
+    qt_setQtEnableTestFont(true);
 }
 
 QQuickView *tst_qquicktext::createView(const QString &filename)
 {
-    QQuickView *canvas = new QQuickView(0);
+    QQuickView *window = new QQuickView(0);
 
-    canvas->setSource(QUrl::fromLocalFile(filename));
-    return canvas;
+    window->setSource(QUrl::fromLocalFile(filename));
+    return window;
 }
 
 void tst_qquicktext::text()
@@ -415,6 +435,29 @@ void tst_qquicktext::wrap()
 
         delete textObject;
     }
+
+    {
+        QQmlComponent component(&engine);
+        component.setData("import QtQuick 2.0\n Text {}", QUrl());
+        QScopedPointer<QObject> object(component.create());
+        QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+        QVERIFY(textObject);
+
+        QSignalSpy spy(textObject, SIGNAL(wrapModeChanged()));
+
+        QCOMPARE(textObject->wrapMode(), QQuickText::NoWrap);
+
+        textObject->setWrapMode(QQuickText::Wrap);
+        QCOMPARE(textObject->wrapMode(), QQuickText::Wrap);
+        QCOMPARE(spy.count(), 1);
+
+        textObject->setWrapMode(QQuickText::Wrap);
+        QCOMPARE(spy.count(), 1);
+
+        textObject->setWrapMode(QQuickText::NoWrap);
+        QCOMPARE(textObject->wrapMode(), QQuickText::NoWrap);
+        QCOMPARE(spy.count(), 2);
+    }
 }
 
 void tst_qquicktext::elide()
@@ -480,9 +523,9 @@ void tst_qquicktext::multilineElide_data()
 void tst_qquicktext::multilineElide()
 {
     QFETCH(QQuickText::TextFormat, format);
-    QQuickView *canvas = createView(testFile("multilineelide.qml"));
+    QQuickView *window = createView(testFile("multilineelide.qml"));
 
-    QQuickText *myText = qobject_cast<QQuickText*>(canvas->rootObject());
+    QQuickText *myText = qobject_cast<QQuickText*>(window->rootObject());
     QVERIFY(myText != 0);
     myText->setTextFormat(format);
 
@@ -525,7 +568,57 @@ void tst_qquicktext::multilineElide()
     myText->setLineHeight(1.1);
     QCOMPARE(myText->lineCount(), 1);
 
-    delete canvas;
+    delete window;
+}
+
+void tst_qquicktext::implicitElide_data()
+{
+    QTest::addColumn<QString>("width");
+    QTest::addColumn<QString>("initialText");
+    QTest::addColumn<QString>("text");
+
+    QTest::newRow("maximum width, empty")
+            << "Math.min(implicitWidth, 100)"
+            << "";
+    QTest::newRow("maximum width, short")
+            << "Math.min(implicitWidth, 100)"
+            << "the";
+    QTest::newRow("maximum width, long")
+            << "Math.min(implicitWidth, 100)"
+            << "the quick brown fox jumped over the lazy dog";
+    QTest::newRow("reset width, empty")
+            << "implicitWidth > 100 ? 100 : undefined"
+            << "";
+    QTest::newRow("reset width, short")
+            << "implicitWidth > 100 ? 100 : undefined"
+            << "the";
+    QTest::newRow("reset width, long")
+            << "implicitWidth > 100 ? 100 : undefined"
+            << "the quick brown fox jumped over the lazy dog";
+}
+
+void tst_qquicktext::implicitElide()
+{
+    QFETCH(QString, width);
+    QFETCH(QString, initialText);
+
+    QString componentStr =
+            "import QtQuick 2.0\n"
+            "Text {\n"
+                "width: " + width + "\n"
+                "text: \"" + initialText + "\"\n"
+                "elide: Text.ElideRight\n"
+            "}";
+    QQmlComponent textComponent(&engine);
+    textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+    QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+
+    QVERIFY(textObject->contentWidth() <= textObject->width());
+
+    textObject->setText("the quick brown fox jumped over");
+
+    QVERIFY(textObject->contentWidth() > 0);
+    QVERIFY(textObject->contentWidth() <= textObject->width());
 }
 
 void tst_qquicktext::textFormat()
@@ -568,59 +661,29 @@ void tst_qquicktext::textFormat()
 
         delete textObject;
     }
-}
-
-
-void tst_qquicktext::alignments_data()
-{
-    QTest::addColumn<int>("hAlign");
-    QTest::addColumn<int>("vAlign");
-    QTest::addColumn<QString>("expectfile");
-
-    QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << testFile("alignments_lt.png");
-    QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << testFile("alignments_rt.png");
-    QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << testFile("alignments_ct.png");
-
-    QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << testFile("alignments_lb.png");
-    QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << testFile("alignments_rb.png");
-    QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << testFile("alignments_cb.png");
 
-    QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << testFile("alignments_lc.png");
-    QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << testFile("alignments_rc.png");
-    QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << testFile("alignments_cc.png");
-}
+    {
+        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);
 
+        QSignalSpy spy(text, SIGNAL(textFormatChanged(TextFormat)));
 
-void tst_qquicktext::alignments()
-{
-    QSKIP("Text alignment pixmap comparison tests will not work with scenegraph");
-#if (0)// No widgets in scenegraph
-    QFETCH(int, hAlign);
-    QFETCH(int, vAlign);
-    QFETCH(QString, expectfile);
+        QCOMPARE(text->textFormat(), QQuickText::AutoText);
 
-    QQuickView *canvas = createView(testFile("alignments.qml"));
-    canvas->show();
-    canvas->requestActivateWindow();
-    QTest::qWait(50);
-    QTRY_COMPARE(QGuiApplication::activeWindow(), static_cast<QWidget *>(canvas));
+        text->setTextFormat(QQuickText::StyledText);
+        QCOMPARE(text->textFormat(), QQuickText::StyledText);
+        QCOMPARE(spy.count(), 1);
 
-    QObject *ob = canvas->rootObject();
-    QVERIFY(ob != 0);
-    ob->setProperty("horizontalAlignment",hAlign);
-    ob->setProperty("verticalAlignment",vAlign);
-    QTRY_COMPARE(ob->property("running").toBool(),false);
-    QImage actual(canvas->width(), canvas->height(), QImage::Format_RGB32);
-    actual.fill(qRgb(255,255,255));
-    QPainter p(&actual);
-    canvas->render(&p);
+        text->setTextFormat(QQuickText::StyledText);
+        QCOMPARE(spy.count(), 1);
 
-    QImage expect(expectfile);
-    if (QGuiApplicationPrivate::graphics_system_name == "raster" || QGuiApplicationPrivate::graphics_system_name == "") {
-        QCOMPARE(actual,expect);
+        text->setTextFormat(QQuickText::AutoText);
+        QCOMPARE(text->textFormat(), QQuickText::AutoText);
+        QCOMPARE(spy.count(), 2);
     }
-    delete canvas;
-#endif
 }
 
 //the alignment tests may be trivial o.oa
@@ -662,10 +725,10 @@ void tst_qquicktext::horizontalAlignment()
 
 void tst_qquicktext::horizontalAlignment_RightToLeft()
 {
-    QQuickView *canvas = createView(testFile("horizontalAlignment_RightToLeft.qml"));
-    QQuickText *text = canvas->rootObject()->findChild<QQuickText*>("text");
+    QQuickView *window = createView(testFile("horizontalAlignment_RightToLeft.qml"));
+    QQuickText *text = window->rootObject()->findChild<QQuickText*>("text");
     QVERIFY(text != 0);
-    canvas->show();
+    window->show();
 
     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(text);
     QVERIFY(textPrivate != 0);
@@ -675,19 +738,19 @@ void tst_qquicktext::horizontalAlignment_RightToLeft()
     // implicit alignment should follow the reading direction of RTL text
     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
     QCOMPARE(text->effectiveHAlign(), text->hAlign());
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
 
     // explicitly left aligned text
     text->setHAlign(QQuickText::AlignLeft);
     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
     QCOMPARE(text->effectiveHAlign(), text->hAlign());
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
 
     // explicitly right aligned text
     text->setHAlign(QQuickText::AlignRight);
     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
     QCOMPARE(text->effectiveHAlign(), text->hAlign());
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
 
     // change to rich text
     QString textString = text->text();
@@ -720,13 +783,13 @@ void tst_qquicktext::horizontalAlignment_RightToLeft()
     text->setHAlign(QQuickText::AlignHCenter);
     QCOMPARE(text->hAlign(), QQuickText::AlignHCenter);
     QCOMPARE(text->effectiveHAlign(), text->hAlign());
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > window->width()/2);
 
     // reseted alignment should go back to following the text reading direction
     text->resetHAlign();
     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
 
     // mirror the text item
     QQuickItemPrivate::get(text)->setLayoutMirror(true);
@@ -734,19 +797,19 @@ void tst_qquicktext::horizontalAlignment_RightToLeft()
     // mirrored implicit alignment should continue to follow the reading direction of the text
     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
 
     // mirrored explicitly right aligned behaves as left aligned
     text->setHAlign(QQuickText::AlignRight);
     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignLeft);
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
 
     // mirrored explicitly left aligned behaves as right aligned
     text->setHAlign(QQuickText::AlignLeft);
     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
     QCOMPARE(text->effectiveHAlign(), QQuickText::AlignRight);
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > window->width()/2);
 
     // disable mirroring
     QQuickItemPrivate::get(text)->setLayoutMirror(false);
@@ -755,7 +818,7 @@ void tst_qquicktext::horizontalAlignment_RightToLeft()
     // English text should be implicitly left aligned
     text->setText("Hello world!");
     QCOMPARE(text->hAlign(), QQuickText::AlignLeft);
-    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2);
+    QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < window->width()/2);
 
     // empty text with implicit alignment follows the system locale-based
     // keyboard input direction from QInputMethod::inputDirection()
@@ -765,7 +828,7 @@ void tst_qquicktext::horizontalAlignment_RightToLeft()
     text->setHAlign(QQuickText::AlignRight);
     QCOMPARE(text->hAlign(), QQuickText::AlignRight);
 
-    delete canvas;
+    delete window;
 
     // alignment of Text with no text set to it
     QString componentStr = "import QtQuick 2.0\nText {}";
@@ -777,6 +840,59 @@ void tst_qquicktext::horizontalAlignment_RightToLeft()
     delete textObject;
 }
 
+int tst_qquicktext::numberOfNonWhitePixels(int fromX, int toX, const QImage &image)
+{
+    int pixels = 0;
+    for (int x = fromX; x < toX; ++x) {
+        for (int y = 0; y < image.height(); ++y) {
+            if (image.pixel(x, y) != qRgb(255, 255, 255))
+                pixels++;
+        }
+    }
+    return pixels;
+}
+
+void tst_qquicktext::hAlignImplicitWidth()
+{
+    QQuickView view(testFileUrl("hAlignImplicitWidth.qml"));
+    view.show();
+    view.requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(&view));
+
+    QQuickText *text = view.rootObject()->findChild<QQuickText*>("textItem");
+    QVERIFY(text != 0);
+
+    {
+        // Left Align
+        QImage image = view.grabWindow();
+        int left = numberOfNonWhitePixels(0, image.width() / 3, image);
+        int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
+        int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
+        QVERIFY(left > mid);
+        QVERIFY(mid > right);
+    }
+    {
+        // HCenter Align
+        text->setHAlign(QQuickText::AlignHCenter);
+        QImage image = view.grabWindow();
+        int left = numberOfNonWhitePixels(0, image.width() / 3, image);
+        int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
+        int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
+        QVERIFY(left < mid);
+        QVERIFY(mid > right);
+    }
+    {
+        // Right Align
+        text->setHAlign(QQuickText::AlignRight);
+        QImage image = view.grabWindow();
+        int left = numberOfNonWhitePixels(0, image.width() / 3, image);
+        int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
+        int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
+        QVERIFY(left < mid);
+        QVERIFY(mid < right);
+    }
+}
+
 void tst_qquicktext::verticalAlignment()
 {
     //test one align each, and then test if two align fails.
@@ -952,6 +1068,31 @@ void tst_qquicktext::color()
         QCOMPARE(textObject->color(), QColor("black"));
         QCOMPARE(textObject->linkColor(), QColor("blue"));
 
+        QSignalSpy colorSpy(textObject, SIGNAL(colorChanged()));
+        QSignalSpy linkColorSpy(textObject, SIGNAL(linkColorChanged()));
+
+        textObject->setColor(QColor("white"));
+        QCOMPARE(textObject->color(), QColor("white"));
+        QCOMPARE(colorSpy.count(), 1);
+
+        textObject->setLinkColor(QColor("black"));
+        QCOMPARE(textObject->linkColor(), QColor("black"));
+        QCOMPARE(linkColorSpy.count(), 1);
+
+        textObject->setColor(QColor("white"));
+        QCOMPARE(colorSpy.count(), 1);
+
+        textObject->setLinkColor(QColor("black"));
+        QCOMPARE(linkColorSpy.count(), 1);
+
+        textObject->setColor(QColor("black"));
+        QCOMPARE(textObject->color(), QColor("black"));
+        QCOMPARE(colorSpy.count(), 2);
+
+        textObject->setLinkColor(QColor("blue"));
+        QCOMPARE(textObject->linkColor(), QColor("blue"));
+        QCOMPARE(linkColorSpy.count(), 2);
+
         delete textObject;
     }
 
@@ -1114,6 +1255,30 @@ void tst_qquicktext::smooth()
     }
 }
 
+void tst_qquicktext::renderType()
+{
+    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);
+
+    QSignalSpy spy(text, SIGNAL(renderTypeChanged()));
+
+    QCOMPARE(text->renderType(), QQuickText::QtRendering);
+
+    text->setRenderType(QQuickText::NativeRendering);
+    QCOMPARE(text->renderType(), QQuickText::NativeRendering);
+    QCOMPARE(spy.count(), 1);
+
+    text->setRenderType(QQuickText::NativeRendering);
+    QCOMPARE(spy.count(), 1);
+
+    text->setRenderType(QQuickText::QtRendering);
+    QCOMPARE(text->renderType(), QQuickText::QtRendering);
+    QCOMPARE(spy.count(), 2);
+}
+
 void tst_qquicktext::weight()
 {
     {
@@ -1142,80 +1307,41 @@ void tst_qquicktext::weight()
 
 void tst_qquicktext::underline()
 {
-    {
-        QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
-        QQmlComponent textComponent(&engine);
-        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-        QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
-        QVERIFY(textObject != 0);
-        QCOMPARE(textObject->font().underline(), false);
-
-        delete textObject;
-    }
-    {
-        QString componentStr = "import QtQuick 2.0\nText { font.underline: true; text: \"Hello world!\" }";
-        QQmlComponent textComponent(&engine);
-        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-        QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
-        QVERIFY(textObject != 0);
-        QCOMPARE(textObject->font().underline(), true);
-
-        delete textObject;
-    }
+    QQuickView view(testFileUrl("underline.qml"));
+    view.show();
+    view.requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(&view));
+    QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
+    QVERIFY(textObject != 0);
+    QCOMPARE(textObject->font().overline(), false);
+    QCOMPARE(textObject->font().underline(), true);
+    QCOMPARE(textObject->font().strikeOut(), false);
 }
 
 void tst_qquicktext::overline()
 {
-    {
-        QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
-        QQmlComponent textComponent(&engine);
-        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-        QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
-        QVERIFY(textObject != 0);
-        QCOMPARE(textObject->font().overline(), false);
-
-        delete textObject;
-    }
-    {
-        QString componentStr = "import QtQuick 2.0\nText { font.overline: true; text: \"Hello world!\" }";
-        QQmlComponent textComponent(&engine);
-        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-        QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
-        QVERIFY(textObject != 0);
-        QCOMPARE(textObject->font().overline(), true);
-
-        delete textObject;
-    }
+    QQuickView view(testFileUrl("overline.qml"));
+    view.show();
+    view.requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(&view));
+    QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
+    QVERIFY(textObject != 0);
+    QCOMPARE(textObject->font().overline(), true);
+    QCOMPARE(textObject->font().underline(), false);
+    QCOMPARE(textObject->font().strikeOut(), false);
 }
 
 void tst_qquicktext::strikeout()
 {
-    {
-        QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
-        QQmlComponent textComponent(&engine);
-        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-        QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
-        QVERIFY(textObject != 0);
-        QCOMPARE(textObject->font().strikeOut(), false);
-
-        delete textObject;
-    }
-    {
-        QString componentStr = "import QtQuick 2.0\nText { font.strikeout: true; text: \"Hello world!\" }";
-        QQmlComponent textComponent(&engine);
-        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-        QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
-        QVERIFY(textObject != 0);
-        QCOMPARE(textObject->font().strikeOut(), true);
-
-        delete textObject;
-    }
+    QQuickView view(testFileUrl("strikeout.qml"));
+    view.show();
+    view.requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(&view));
+    QQuickText *textObject = view.rootObject()->findChild<QQuickText*>("myText");
+    QVERIFY(textObject != 0);
+    QCOMPARE(textObject->font().overline(), false);
+    QCOMPARE(textObject->font().underline(), false);
+    QCOMPARE(textObject->font().strikeOut(), true);
 }
 
 void tst_qquicktext::capitalization()
@@ -1351,9 +1477,6 @@ void tst_qquicktext::wordSpacing()
     }
 }
 
-
-
-
 class EventSender : public QQuickItem
 {
 public:
@@ -1362,6 +1485,8 @@ public:
             mousePressEvent(event);
         else if (event->type() == QEvent::MouseButtonRelease)
             mouseReleaseEvent(event);
+        else if (event->type() == QEvent::MouseMove)
+            mouseMoveEvent(event);
         else
             qWarning() << "Trying to send unsupported event type";
     }
@@ -1379,38 +1504,341 @@ public slots:
     void linkClicked(QString l) { link = l; }
 };
 
-void tst_qquicktext::clickLink()
+class TextMetrics
 {
+public:
+    TextMetrics(const QString &text, Qt::TextElideMode elideMode = Qt::ElideNone)
     {
-        QString componentStr = "import QtQuick 2.0\nText { text: \"<a href=\\\"http://qt.nokia.com\\\">Hello world!</a>\" }";
-        QQmlComponent textComponent(&engine);
-        textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
-        QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
-
-        QVERIFY(textObject != 0);
+        QString adjustedText = text;
+        adjustedText.replace(QLatin1Char('\n'), QChar(QChar::LineSeparator));
+        if (elideMode == Qt::ElideLeft)
+            adjustedText = QChar(0x2026) + adjustedText;
+        else if (elideMode == Qt::ElideRight)
+            adjustedText = adjustedText + QChar(0x2026);
+
+        layout.setText(adjustedText);
+        QTextOption option;
+        option.setUseDesignMetrics(true);
+        layout.setTextOption(option);
 
-        LinkTest test;
-        QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
+        layout.beginLayout();
+        qreal height = 0;
+        QTextLine line = layout.createLine();
+        while (line.isValid()) {
+            line.setLineWidth(FLT_MAX);
+            line.setPosition(QPointF(0, height));
+            height += line.height();
+            line = layout.createLine();
+        }
+        layout.endLayout();
+    }
 
-        {
-            QMouseEvent me(QEvent::MouseButtonPress,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
-            static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+    qreal width() const { return layout.maximumWidth(); }
 
+    QRectF characterRectangle(
+            int position,
+            int hAlign = Qt::AlignLeft,
+            int vAlign = Qt::AlignTop,
+            const QSizeF &bounds = QSizeF(240, 320)) const
+    {
+        qreal dy = 0;
+        switch (vAlign) {
+        case Qt::AlignBottom:
+            dy = bounds.height() - layout.boundingRect().height();
+            break;
+        case Qt::AlignVCenter:
+            dy = (bounds.height() - layout.boundingRect().height()) / 2;
+            break;
+        default:
+            break;
         }
 
-        {
-            QMouseEvent me(QEvent::MouseButtonRelease,QPointF(textObject->x()/2, textObject->y()/2), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
-            static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+        for (int i = 0; i < layout.lineCount(); ++i) {
+            QTextLine line = layout.lineAt(i);
+            if (position >= line.textStart() + line.textLength())
+                continue;
+            qreal dx = 0;
+            switch (hAlign) {
+            case Qt::AlignRight:
+                dx = bounds.width() - line.naturalTextWidth();
+                break;
+            case Qt::AlignHCenter:
+                dx = (bounds.width() - line.naturalTextWidth()) / 2;
+                break;
+            default:
+                break;
+            }
+
+            QRectF rect;
+            rect.setLeft(dx + line.cursorToX(position, QTextLine::Leading));
+            rect.setRight(dx + line.cursorToX(position, QTextLine::Trailing));
+            rect.setTop(dy + line.y());
+            rect.setBottom(dy + line.y() + line.height());
 
+            return rect;
         }
+        return QRectF();
+    }
 
+    QTextLayout layout;
+};
 
-        QCOMPARE(test.link, QLatin1String("http://qt.nokia.com"));
 
-        delete textObject;
+typedef QVector<QPointF> PointVector;
+Q_DECLARE_METATYPE(PointVector);
+
+void tst_qquicktext::clickLink_data()
+{
+    QTest::addColumn<QString>("text");
+    QTest::addColumn<qreal>("width");
+    QTest::addColumn<QString>("bindings");
+    QTest::addColumn<PointVector>("mousePositions");
+    QTest::addColumn<QString>("link");
+
+    const QString singleLineText = "this text has a <a href=\\\"http://qt-project.org/single\\\">link</a> in it";
+    const QString singleLineLink = "http://qt-project.org/single";
+    const QString multipleLineText = "this text<br/>has <a href=\\\"http://qt-project.org/multiple\\\">multiple<br/>lines</a> in it";
+    const QString multipleLineLink = "http://qt-project.org/multiple";
+    const QString nestedText = "this text has a <a href=\\\"http://qt-project.org/outer\\\">nested <a href=\\\"http://qt-project.org/inner\\\">link</a> in it</a>";
+    const QString outerLink = "http://qt-project.org/outer";
+    const QString innerLink = "http://qt-project.org/inner";
+
+    {
+        const TextMetrics metrics("this text has a link in it");
+
+        QTest::newRow("click on link")
+                << singleLineText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(18).center())
+                << singleLineLink;
+        QTest::newRow("click on text")
+                << singleLineText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(13).center())
+                << QString();
+        QTest::newRow("drag within link")
+                << singleLineText << 240.
+                << ""
+                << (PointVector()
+                    << metrics.characterRectangle(17).center()
+                    << metrics.characterRectangle(19).center())
+                << singleLineLink;
+        QTest::newRow("drag away from link")
+                << singleLineText << 240.
+                << ""
+                << (PointVector()
+                    << metrics.characterRectangle(18).center()
+                    << metrics.characterRectangle(13).center())
+                << QString();
+        QTest::newRow("drag on to link")
+                << singleLineText << 240.
+                << ""
+                << (PointVector()
+                    << metrics.characterRectangle(13).center()
+                    << metrics.characterRectangle(18).center())
+                << QString();
+        QTest::newRow("click on bottom right aligned link")
+                << singleLineText << 240.
+                << "horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
+                << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
+                << singleLineLink;
+        QTest::newRow("click on center aligned link")
+                << singleLineText << 240.
+                << "horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
+                << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
+                << singleLineLink;
+        QTest::newRow("click on rich text link")
+                << singleLineText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(18).center())
+                << singleLineLink;
+        QTest::newRow("click on rich text")
+                << singleLineText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(13).center())
+                << QString();
+        QTest::newRow("click on bottom right aligned rich text link")
+                << singleLineText << 240.
+                << "textFormat: Text.RichText; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom"
+                << (PointVector() << metrics.characterRectangle(18, Qt::AlignRight, Qt::AlignBottom).center())
+                << singleLineLink;
+        QTest::newRow("click on center aligned rich text link")
+                << singleLineText << 240.
+                << "textFormat: Text.RichText; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter"
+                << (PointVector() << metrics.characterRectangle(18, Qt::AlignHCenter, Qt::AlignVCenter).center())
+                << singleLineLink;
+    } {
+        const TextMetrics metrics("this text has a li", Qt::ElideRight);
+        QTest::newRow("click on right elided link")
+                << singleLineText << metrics.width() +  2
+                << "elide: Text.ElideRight"
+                << (PointVector() << metrics.characterRectangle(17).center())
+                << singleLineLink;
+    } {
+        const TextMetrics metrics("ink in it", Qt::ElideLeft);
+        QTest::newRow("click on left elided link")
+                << singleLineText << metrics.width() +  2
+                << "elide: Text.ElideLeft"
+                << (PointVector() << metrics.characterRectangle(2).center())
+                << singleLineLink;
+    } {
+        const TextMetrics metrics("this text\nhas multiple\nlines in it");
+        QTest::newRow("click on second line")
+                << multipleLineText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(18).center())
+                << multipleLineLink;
+        QTest::newRow("click on third line")
+                << multipleLineText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(25).center())
+                << multipleLineLink;
+        QTest::newRow("drag from second line to third")
+                << multipleLineText << 240.
+                << ""
+                << (PointVector()
+                    << metrics.characterRectangle(18).center()
+                    << metrics.characterRectangle(25).center())
+                << multipleLineLink;
+        QTest::newRow("click on rich text second line")
+                << multipleLineText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(18).center())
+                << multipleLineLink;
+        QTest::newRow("click on rich text third line")
+                << multipleLineText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(25).center())
+                << multipleLineLink;
+        QTest::newRow("drag rich text from second line to third")
+                << multipleLineText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector()
+                    << metrics.characterRectangle(18).center()
+                    << metrics.characterRectangle(25).center())
+                << multipleLineLink;
+    } {
+        const TextMetrics metrics("this text has a nested link in it");
+        QTest::newRow("click on left outer link")
+                << nestedText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(22).center())
+                << outerLink;
+        QTest::newRow("click on right outer link")
+                << nestedText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(27).center())
+                << outerLink;
+        QTest::newRow("click on inner link left")
+                << nestedText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(23).center())
+                << innerLink;
+        QTest::newRow("click on inner link right")
+                << nestedText << 240.
+                << ""
+                << (PointVector() << metrics.characterRectangle(26).center())
+                << innerLink;
+        QTest::newRow("drag from inner to outer link")
+                << nestedText << 240.
+                << ""
+                << (PointVector()
+                    << metrics.characterRectangle(25).center()
+                    << metrics.characterRectangle(30).center())
+                << QString();
+        QTest::newRow("drag from outer to inner link")
+                << nestedText << 240.
+                << ""
+                << (PointVector()
+                    << metrics.characterRectangle(30).center()
+                    << metrics.characterRectangle(25).center())
+                << QString();
+        QTest::newRow("click on left outer rich text link")
+                << nestedText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(22).center())
+                << outerLink;
+        QTest::newRow("click on right outer rich text link")
+                << nestedText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(27).center())
+                << outerLink;
+        QTest::newRow("click on inner rich text link left")
+                << nestedText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(23).center())
+                << innerLink;
+        QTest::newRow("click on inner rich text link right")
+                << nestedText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector() << metrics.characterRectangle(26).center())
+                << innerLink;
+        QTest::newRow("drag from inner to outer rich text link")
+                << nestedText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector()
+                    << metrics.characterRectangle(25).center()
+                    << metrics.characterRectangle(30).center())
+                << QString();
+        QTest::newRow("drag from outer to inner rich text link")
+                << nestedText << 240.
+                << "textFormat: Text.RichText"
+                << (PointVector()
+                    << metrics.characterRectangle(30).center()
+                    << metrics.characterRectangle(25).center())
+                << QString();
     }
 }
 
+void tst_qquicktext::clickLink()
+{
+    QFETCH(QString, text);
+    QFETCH(qreal, width);
+    QFETCH(QString, bindings);
+    QFETCH(PointVector, mousePositions);
+    QFETCH(QString, link);
+
+    QString componentStr =
+            "import QtQuick 2.0\nText {\n"
+                "width: " + QString::number(width) + "\n"
+                "height: 320\n"
+                "text: \"" + text + "\"\n"
+                "" + bindings + "\n"
+            "}";
+    QQmlComponent textComponent(&engine);
+    textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+    QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+
+    QVERIFY(textObject != 0);
+
+    LinkTest test;
+    QObject::connect(textObject, SIGNAL(linkActivated(QString)), &test, SLOT(linkClicked(QString)));
+
+    QVERIFY(mousePositions.count() > 0);
+
+    QPointF mousePosition = mousePositions.first();
+    {
+        QMouseEvent me(QEvent::MouseButtonPress, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+        static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+    }
+
+    for (int i = 1; i < mousePositions.count(); ++i) {
+        mousePosition = mousePositions.at(i);
+
+        QMouseEvent me(QEvent::MouseMove, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+        static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+    }
+
+    {
+        QMouseEvent me(QEvent::MouseButtonRelease, mousePosition, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
+        static_cast<EventSender*>(static_cast<QQuickItem*>(textObject))->sendEvent(&me);
+    }
+
+    QCOMPARE(test.link, link);
+
+    delete textObject;
+}
+
 void tst_qquicktext::baseUrl()
 {
     QUrl localUrl("file:///tests/text.qml");
@@ -1447,7 +1875,7 @@ void tst_qquicktext::embeddedImages_data()
     QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
     QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
     QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
-        << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading http://127.0.0.1:14453/notexists.png - server replied: Not found";
+                                  << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML Text: Error downloading " SERVER_ADDR "/notexists.png - server replied: Not found";
     QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
 }
 
@@ -1458,17 +1886,19 @@ void tst_qquicktext::embeddedImages()
     QFETCH(QUrl, qmlfile);
     QFETCH(QString, error);
 
-    TestHTTPServer server(14453);
+    TestHTTPServer server(SERVER_PORT);
     server.serveDirectory(testFile("http"));
 
     if (!error.isEmpty())
         QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
 
-    QQmlComponent textComponent(&engine, qmlfile);
-    QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+    QQuickView *view = new QQuickView(qmlfile);
+    view->show();
+    view->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(view));
+    QQuickText *textObject = qobject_cast<QQuickText*>(view->rootObject());
 
     QVERIFY(textObject != 0);
-
     QTRY_COMPARE(textObject->resourcesLoading(), 0);
 
     QPixmap pm(testFile("http/exists.png"));
@@ -1481,14 +1911,14 @@ void tst_qquicktext::embeddedImages()
         QCOMPARE(textObject->height(), 16.0);
     }
 
-    delete textObject;
+    delete view;
 }
 
 void tst_qquicktext::lineCount()
 {
-    QQuickView *canvas = createView(testFile("lineCount.qml"));
+    QQuickView *window = createView(testFile("lineCount.qml"));
 
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     QVERIFY(myText->lineCount() > 1);
@@ -1510,14 +1940,14 @@ void tst_qquicktext::lineCount()
     QCOMPARE(myText->truncated(), true);
     QCOMPARE(myText->maximumLineCount(), 2);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_qquicktext::lineHeight()
 {
-    QQuickView *canvas = createView(testFile("lineHeight.qml"));
+    QQuickView *window = createView(testFile("lineHeight.qml"));
 
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     QVERIFY(myText->lineHeight() == 1);
@@ -1525,7 +1955,7 @@ void tst_qquicktext::lineHeight()
 
     qreal h = myText->height();
     myText->setLineHeight(1.5);
-    QCOMPARE(myText->height(), qreal(qCeil(h * 1.5)));
+    QCOMPARE(myText->height(), qreal(qCeil(h)) * 1.5);
 
     myText->setLineHeightMode(QQuickText::FixedHeight);
     myText->setLineHeight(20);
@@ -1543,7 +1973,7 @@ void tst_qquicktext::lineHeight()
     myText->setLineHeight(10);
     QCOMPARE(myText->height(), myText->lineCount() * 10.0);
 
-    delete canvas;
+    delete window;
 }
 
 void tst_qquicktext::implicitSize_data()
@@ -1579,18 +2009,20 @@ void tst_qquicktext::implicitSize()
     QFETCH(QString, wrap);
     QFETCH(QString, elide);
     QString componentStr = "import QtQuick 2.0\nText { "
+            "property real iWidth: implicitWidth; "
             "text: \"" + text + "\"; "
             "width: " + width + "; "
             "textFormat: " + format + "; "
             "wrapMode: " + wrap + "; "
             "elide: " + elide + "; "
-            "maximumLineCount: 1 }";
+            "maximumLineCount: 2 }";
     QQmlComponent textComponent(&engine);
     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
     QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
 
     QVERIFY(textObject->width() < textObject->implicitWidth());
     QVERIFY(textObject->height() == textObject->implicitHeight());
+    QCOMPARE(textObject->property("iWidth").toReal(), textObject->implicitWidth());
 
     textObject->resetWidth();
     QVERIFY(textObject->width() == textObject->implicitWidth());
@@ -1642,6 +2074,260 @@ void tst_qquicktext::contentSize()
     QCOMPARE(spy.count(), ++spyCount);
 }
 
+void tst_qquicktext::geometryChanged()
+{
+    // Test that text is re-laid out when the geometry of the item by verifying changes in content
+    // size.  Implicit width is also tested as that in combination with item geometry provides a
+    // reference for expected content sizes.
+
+    QString componentStr = "import QtQuick 2.0\nText { font.family: \"__Qt__Box__Engine__\"; font.pixelSize: 10 }";
+    QQmlComponent textComponent(&engine);
+    textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+    QScopedPointer<QObject> object(textComponent.create());
+    QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
+
+    const qreal implicitHeight = textObject->implicitHeight();
+
+    const qreal widths[] = { 100, 2000, 3000, -100, 100 };
+    const qreal heights[] = { implicitHeight, 2000, 3000, -implicitHeight, implicitHeight };
+
+    QCOMPARE(textObject->implicitWidth(), 0.);
+    QVERIFY(implicitHeight > 0.);
+    QCOMPARE(textObject->width(), textObject->implicitWidth());
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), textObject->implicitWidth());
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setText("The quick red fox jumped over the lazy brown dog");
+
+    const qreal implicitWidth = textObject->implicitWidth();
+
+    QVERIFY(implicitWidth > 0.);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), textObject->implicitWidth());
+    QCOMPARE(textObject->height(), textObject->implicitHeight());
+    QCOMPARE(textObject->contentWidth(), textObject->implicitWidth());
+    QCOMPARE(textObject->contentHeight(), textObject->implicitHeight());
+
+    // Changing the geometry with no eliding, or wrapping doesn't change the content size.
+    for (int i = 0; i < 5; ++i) {
+        textObject->setWidth(widths[i]);
+        QCOMPARE(textObject->implicitWidth(), implicitWidth);
+        QCOMPARE(textObject->implicitHeight(), implicitHeight);
+        QCOMPARE(textObject->width(), widths[i]);
+        QCOMPARE(textObject->height(), implicitHeight);
+        QCOMPARE(textObject->contentWidth(), implicitWidth);
+        QCOMPARE(textObject->contentHeight(), implicitHeight);
+    }
+
+    // With eliding enabled the content width is bounded to the item width, but is never
+    // larger than the implicit width.
+    textObject->setElideMode(QQuickText::ElideRight);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(2000.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 2000.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(3000.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 3000.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(-100);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), -100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), 0.);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(100.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    // With wrapping enabled the implicit height changes with the width.
+    textObject->setElideMode(QQuickText::ElideNone);
+    textObject->setWrapMode(QQuickText::Wrap);
+    const qreal wrappedImplicitHeight = textObject->implicitHeight();
+
+    QVERIFY(wrappedImplicitHeight > implicitHeight);
+
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), wrappedImplicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+    textObject->setWidth(2000.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 2000.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(3000.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 3000.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(-100);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), -100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);    // 0 or negative width item won't wrap.
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(100.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), wrappedImplicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+    // With no eliding or maximum line count the content height is the same as the implicit height.
+    for (int i = 0; i < 5; ++i) {
+        textObject->setHeight(heights[i]);
+        QCOMPARE(textObject->implicitWidth(), implicitWidth);
+        QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+        QCOMPARE(textObject->width(), 100.);
+        QCOMPARE(textObject->height(), heights[i]);
+        QVERIFY(textObject->contentWidth() <= 100.);
+        QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+    }
+
+    // The implicit height is unaffected by eliding but the content height will change.
+    textObject->setElideMode(QQuickText::ElideRight);
+
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setHeight(2000);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), 2000.);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+    textObject->setHeight(3000);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), 3000.);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), wrappedImplicitHeight);
+
+    textObject->setHeight(-implicitHeight);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), -implicitHeight);
+    QVERIFY(textObject->contentWidth() <= 0.);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);  // content height is never less than font height. seems a little odd in this instance.
+
+    textObject->setHeight(implicitHeight);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), wrappedImplicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    // Varying the height with a maximum line count but no eliding won't affect the content height.
+    textObject->setElideMode(QQuickText::ElideNone);
+    textObject->setMaximumLineCount(2);
+    textObject->resetHeight();
+
+    const qreal maxLineCountImplicitHeight = textObject->implicitHeight();
+    QVERIFY(maxLineCountImplicitHeight > implicitHeight);
+    QVERIFY(maxLineCountImplicitHeight < wrappedImplicitHeight);
+
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), maxLineCountImplicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+
+    for (int i = 0; i < 5; ++i) {
+        textObject->setHeight(heights[i]);
+        QCOMPARE(textObject->implicitWidth(), implicitWidth);
+        QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
+        QCOMPARE(textObject->width(), 100.);
+        QCOMPARE(textObject->height(), heights[i]);
+        QVERIFY(textObject->contentWidth() <= 100.);
+        QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+    }
+
+    // Varying the width with a maximum line count won't increase the implicit height beyond the
+    // height of the maximum number of lines.
+    textObject->setWidth(2000.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 2000.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(3000.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), 3000.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(-100);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), implicitHeight);
+    QCOMPARE(textObject->width(), -100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QCOMPARE(textObject->contentWidth(), implicitWidth);    // 0 or negative width item won't wrap.
+    QCOMPARE(textObject->contentHeight(), implicitHeight);
+
+    textObject->setWidth(50.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
+    QCOMPARE(textObject->width(), 50.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QVERIFY(textObject->contentWidth() <= 50.);
+    QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+
+    textObject->setWidth(100.);
+    QCOMPARE(textObject->implicitWidth(), implicitWidth);
+    QCOMPARE(textObject->implicitHeight(), maxLineCountImplicitHeight);
+    QCOMPARE(textObject->width(), 100.);
+    QCOMPARE(textObject->height(), implicitHeight);
+    QVERIFY(textObject->contentWidth() <= 100.);
+    QCOMPARE(textObject->contentHeight(), maxLineCountImplicitHeight);
+}
+
 void tst_qquicktext::implicitSizeBinding_data()
 {
     implicitSize_data();
@@ -1654,7 +2340,7 @@ void tst_qquicktext::implicitSizeBinding()
     QFETCH(QString, format);
     QString componentStr = "import QtQuick 2.0\nText { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }";
 
-    QDeclarativeComponent textComponent(&engine);
+    QQmlComponent textComponent(&engine);
     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
     QScopedPointer<QObject> object(textComponent.create());
     QQuickText *textObject = qobject_cast<QQuickText *>(object.data());
@@ -1683,7 +2369,7 @@ void tst_qquicktext::boundingRect()
 {
     QFETCH(QString, format);
 
-    QDeclarativeComponent component(&engine);
+    QQmlComponent component(&engine);
     component.setData("import QtQuick 2.0\n Text { textFormat:" + format.toUtf8() + "}", QUrl());
     QScopedPointer<QObject> object(component.create());
     QQuickText *text = qobject_cast<QQuickText *>(object.data());
@@ -1692,7 +2378,7 @@ void tst_qquicktext::boundingRect()
     QCOMPARE(text->boundingRect().x(), qreal(0));
     QCOMPARE(text->boundingRect().y(), qreal(0));
     QCOMPARE(text->boundingRect().width(), qreal(0));
-    QCOMPARE(text->boundingRect().height(), QFontMetricsF(text->font()).height());
+    QCOMPARE(text->boundingRect().height(), qreal(qCeil(QFontMetricsF(text->font()).height())));
 
     text->setText("Hello World");
 
@@ -1745,11 +2431,55 @@ 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"));
+    QQuickView *window = createView(testFile("lineLayout.qml"));
 
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
@@ -1757,10 +2487,6 @@ void tst_qquicktext::lineLaidOut()
 
     QVERIFY(!textPrivate->extra.isAllocated());
 
-#if defined(Q_OS_MAC)
-    QVERIFY(myText->lineCount() == textPrivate->linesRects.count());
-#endif
-
     for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
         QRectF r = textPrivate->layout.lineAt(i).rect();
         QVERIFY(r.width() == i * 15);
@@ -1772,7 +2498,39 @@ void tst_qquicktext::lineLaidOut()
         }
     }
 
-    delete canvas;
+    delete window;
+}
+
+void tst_qquicktext::lineLaidOutRelayout()
+{
+    QQuickView *window = createView(testFile("lineLayoutRelayout.qml"));
+
+    window->show();
+    window->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(window));
+
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
+    QVERIFY(myText != 0);
+
+    QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
+    QVERIFY(textPrivate != 0);
+
+    QVERIFY(!textPrivate->extra.isAllocated());
+
+    qreal maxH = 0;
+    for (int i = 0; i < textPrivate->layout.lineCount(); ++i) {
+        QRectF r = textPrivate->layout.lineAt(i).rect();
+
+        if (r.x() == 0) {
+            QCOMPARE(r.y(), i * r.height());
+            maxH = qMax(maxH, r.y() + r.height());
+        } else {
+            QCOMPARE(r.x(), myText->width() / 2);
+            QCOMPARE(r.y(), (i * r.height()) - maxH);
+        }
+    }
+
+    delete window;
 }
 
 void tst_qquicktext::imgTagsBaseUrl_data()
@@ -1814,18 +2572,18 @@ void tst_qquicktext::imgTagsBaseUrl_data()
             << 181.;
 
     QTest::newRow("absolute remote")
-            << QUrl("http://127.0.0.1:14453/images/heart200.png")
+            << QUrl(SERVER_ADDR "/images/heart200.png")
             << QUrl()
             << QUrl()
             << 181.;
     QTest::newRow("relative remote base 1")
             << QUrl("images/heart200.png")
-            << QUrl("http://127.0.0.1:14453/")
+            << QUrl(SERVER_ADDR "/")
             << testFileUrl("nonexistant/app.qml")
             << 181.;
     QTest::newRow("relative remote base 2")
             << QUrl("heart200.png")
-            << QUrl("http://127.0.0.1:14453/images/")
+            << QUrl(SERVER_ADDR "/images/")
             << testFileUrl("nonexistant/app.qml")
             << 181.;
 }
@@ -1837,7 +2595,7 @@ void tst_qquicktext::imgTagsBaseUrl()
     QFETCH(QUrl, contextUrl);
     QFETCH(qreal, imgHeight);
 
-    TestHTTPServer server(14453);
+    TestHTTPServer server(SERVER_PORT);
     server.serveDirectory(testFile(""));
 
     QByteArray baseUrlFragment;
@@ -1916,8 +2674,8 @@ void tst_qquicktext::imgTagsMultipleImages()
 
 void tst_qquicktext::imgTagsElide()
 {
-    QQuickView *canvas = createView(testFile("imgTagsElide.qml"));
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickView *window = createView(testFile("imgTagsElide.qml"));
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(myText);
@@ -1927,13 +2685,13 @@ void tst_qquicktext::imgTagsElide()
     QTRY_VERIFY(textPrivate->visibleImgTags.count() == 1);
 
     delete myText;
-    delete canvas;
+    delete window;
 }
 
 void tst_qquicktext::imgTagsUpdates()
 {
-    QQuickView *canvas = createView(testFile("imgTagsUpdates.qml"));
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickView *window = createView(testFile("imgTagsUpdates.qml"));
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     QSignalSpy spy(myText, SIGNAL(contentSizeChanged()));
@@ -1954,7 +2712,7 @@ void tst_qquicktext::imgTagsUpdates()
     QVERIFY(spy.count() == 3);
 
     delete myText;
-    delete canvas;
+    delete window;
 }
 
 void tst_qquicktext::imgTagsError()
@@ -1981,10 +2739,10 @@ void tst_qquicktext::fontSizeMode()
 {
     QFETCH(QString, text);
 
-    QScopedPointer<QQuickView> canvas(createView(testFile("fontSizeMode.qml")));
-    canvas->show();
+    QScopedPointer<QQuickView> window(createView(testFile("fontSizeMode.qml")));
+    window->show();
 
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     myText->setText(text);
@@ -2224,10 +2982,10 @@ void tst_qquicktext::fontSizeModeMultiline()
 {
     QFETCH(QString, text);
 
-    QScopedPointer<QQuickView> canvas(createView(testFile("fontSizeMode.qml")));
-    canvas->show();
+    QScopedPointer<QQuickView> window(createView(testFile("fontSizeMode.qml")));
+    window->show();
 
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     myText->setText(text);
@@ -2472,10 +3230,10 @@ void tst_qquicktext::multilengthStrings()
 {
     QFETCH(QString, source);
 
-    QScopedPointer<QQuickView> canvas(createView(source));
-    canvas->show();
+    QScopedPointer<QQuickView> window(createView(source));
+    window->show();
 
-    QQuickText *myText = canvas->rootObject()->findChild<QQuickText*>("myText");
+    QQuickText *myText = window->rootObject()->findChild<QQuickText*>("myText");
     QVERIFY(myText != 0);
 
     const QString longText = "the quick brown fox jumped over the lazy dog";
@@ -2592,6 +3350,294 @@ void tst_qquicktext::fontFormatSizes()
     delete view;
 }
 
+typedef qreal (*ExpectedBaseline)(QQuickText *item);
+Q_DECLARE_METATYPE(ExpectedBaseline)
+
+static qreal expectedBaselineTop(QQuickText *item)
+{
+    QFontMetricsF fm(item->font());
+    return fm.ascent();
+}
+
+static qreal expectedBaselineBottom(QQuickText *item)
+{
+    QFontMetricsF fm(item->font());
+    return item->height() - item->contentHeight() + fm.ascent();
+}
+
+static qreal expectedBaselineCenter(QQuickText *item)
+{
+    QFontMetricsF fm(item->font());
+    return ((item->height() - item->contentHeight()) / 2) + fm.ascent();
+}
+
+static qreal expectedBaselineBold(QQuickText *item)
+{
+    QFont font = item->font();
+    font.setBold(true);
+    QFontMetricsF fm(font);
+    return fm.ascent();
+}
+
+static qreal expectedBaselineImage(QQuickText *item)
+{
+    QFontMetricsF fm(item->font());
+    // The line is positioned so the bottom of the line is aligned with the bottom of the image,
+    // or image height - line height and the baseline is line position + ascent.  Because
+    // QTextLine's height is rounded up this can give slightly different results to image height
+    // - descent.
+    return 181 - qCeil(fm.height()) + fm.ascent();
+}
+
+static qreal expectedBaselineCustom(QQuickText *item)
+{
+    QFontMetricsF fm(item->font());
+    return 16 + fm.ascent();
+}
+
+static qreal expectedBaselineScaled(QQuickText *item)
+{
+    QFont font = item->font();
+    QTextLayout layout(item->text().replace(QLatin1Char('\n'), QChar::LineSeparator));
+    do {
+        layout.setFont(font);
+        qreal width = 0;
+        layout.beginLayout();
+        for (QTextLine line = layout.createLine(); line.isValid(); line = layout.createLine()) {
+            line.setLineWidth(FLT_MAX);
+            width = qMax(line.naturalTextWidth(), width);
+        }
+        layout.endLayout();
+
+        if (width < item->width()) {
+            QFontMetricsF fm(layout.font());
+            return fm.ascent();
+        }
+        font.setPointSize(font.pointSize() - 1);
+    } while (font.pointSize() > 0);
+    return 0;
+}
+
+static qreal expectedBaselineFixedBottom(QQuickText *item)
+{
+    QFontMetricsF fm(item->font());
+    qreal dy = item->text().contains(QLatin1Char('\n'))
+            ? 160
+            : 180;
+    return dy + fm.ascent();
+}
+
+static qreal expectedBaselineProportionalBottom(QQuickText *item)
+{
+    QFontMetricsF fm(item->font());
+    qreal dy = item->text().contains(QLatin1Char('\n'))
+            ? 200 - (qCeil(fm.height()) * 3)
+            : 200 - (qCeil(fm.height()) * 1.5);
+    return dy + fm.ascent();
+}
+
+void tst_qquicktext::baselineOffset_data()
+{
+    qRegisterMetaType<ExpectedBaseline>();
+    QTest::addColumn<QString>("text");
+    QTest::addColumn<QString>("wrappedText");
+    QTest::addColumn<QByteArray>("bindings");
+    QTest::addColumn<ExpectedBaseline>("expectedBaseline");
+    QTest::addColumn<ExpectedBaseline>("expectedBaselineEmpty");
+
+    QTest::newRow("top align")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; verticalAlignment: Text.AlignTop")
+            << &expectedBaselineTop
+            << &expectedBaselineTop;
+    QTest::newRow("bottom align")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; verticalAlignment: Text.AlignBottom")
+            << &expectedBaselineBottom
+            << &expectedBaselineBottom;
+    QTest::newRow("center align")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; verticalAlignment: Text.AlignVCenter")
+            << &expectedBaselineCenter
+            << &expectedBaselineCenter;
+
+    QTest::newRow("bold")
+            << "<b>hello world</b>"
+            << "<b>hello<br/>world</b>"
+            << QByteArray("height: 200")
+            << &expectedBaselineTop
+            << &expectedBaselineBold;
+
+    QTest::newRow("richText")
+            << "<b>hello world</b>"
+            << "<b>hello<br/>world</b>"
+            << QByteArray("height: 200; textFormat: Text.RichText")
+            << &expectedBaselineTop
+            << &expectedBaselineTop;
+
+    QTest::newRow("elided")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("width: 20; height: 8; elide: Text.ElideRight")
+            << &expectedBaselineTop
+            << &expectedBaselineTop;
+
+    QTest::newRow("elided bottom align")
+            << "hello world"
+            << "hello\nworld!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
+            << QByteArray("width: 200; height: 200; elide: Text.ElideRight; verticalAlignment: Text.AlignBottom")
+            << &expectedBaselineBottom
+            << &expectedBaselineBottom;
+
+    QTest::newRow("image")
+            << "hello <img src=\"images/heart200.png\" /> world"
+            << "hello <img src=\"images/heart200.png\" /><br/>world"
+            << QByteArray("height: 200\n; baseUrl: \"") + testFileUrl("reference").toEncoded() + QByteArray("\"")
+            << &expectedBaselineImage
+            << &expectedBaselineTop;
+
+    QTest::newRow("customLine")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; onLineLaidOut: line.y += 16")
+            << &expectedBaselineCustom
+            << &expectedBaselineCustom;
+
+    QTest::newRow("scaled font")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("width: 200; minimumPointSize: 1; font.pointSize: 64; fontSizeMode: Text.HorizontalFit")
+            << &expectedBaselineScaled
+            << &expectedBaselineTop;
+
+    QTest::newRow("fixed line height top align")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignTop")
+            << &expectedBaselineTop
+            << &expectedBaselineTop;
+
+    QTest::newRow("fixed line height bottom align")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; lineHeightMode: Text.FixedHeight; lineHeight: 20; verticalAlignment: Text.AlignBottom")
+            << &expectedBaselineFixedBottom
+            << &expectedBaselineFixedBottom;
+
+    QTest::newRow("proportional line height top align")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignTop")
+            << &expectedBaselineTop
+            << &expectedBaselineTop;
+
+    QTest::newRow("proportional line height bottom align")
+            << "hello world"
+            << "hello\nworld"
+            << QByteArray("height: 200; lineHeightMode: Text.ProportionalHeight; lineHeight: 1.5; verticalAlignment: Text.AlignBottom")
+            << &expectedBaselineProportionalBottom
+            << &expectedBaselineProportionalBottom;
+}
+
+void tst_qquicktext::baselineOffset()
+{
+    QFETCH(QString, text);
+    QFETCH(QString, wrappedText);
+    QFETCH(QByteArray, bindings);
+    QFETCH(ExpectedBaseline, expectedBaseline);
+    QFETCH(ExpectedBaseline, expectedBaselineEmpty);
+
+    QQmlComponent component(&engine);
+    component.setData(
+            "import QtQuick 2.0\n"
+            "Text {\n"
+                + bindings + "\n"
+            "}", QUrl());
+
+    QScopedPointer<QObject> object(component.create());
+
+    QQuickText *item = qobject_cast<QQuickText *>(object.data());
+    QVERIFY(item);
+
+    {
+        qreal baseline = expectedBaselineEmpty(item);
+
+        QCOMPARE(item->baselineOffset(), baseline);
+
+        item->setText(text);
+        if (expectedBaseline != expectedBaselineEmpty)
+            baseline = expectedBaseline(item);
+
+        QCOMPARE(item->baselineOffset(), baseline);
+
+        item->setText(wrappedText);
+        QCOMPARE(item->baselineOffset(), expectedBaseline(item));
+    }
+
+    QFont font = item->font();
+    font.setPointSize(font.pointSize() + 8);
+
+    {
+        QCOMPARE(item->baselineOffset(), expectedBaseline(item));
+
+        item->setText(text);
+        qreal baseline = expectedBaseline(item);
+        QCOMPARE(item->baselineOffset(), baseline);
+
+        item->setText(QString());
+        if (expectedBaselineEmpty != expectedBaseline)
+            baseline = expectedBaselineEmpty(item);
+
+        QCOMPARE(item->baselineOffset(), baseline);
+    }
+}
+
+void tst_qquicktext::htmlLists()
+{
+    QFETCH(QString, text);
+    QFETCH(int, nbLines);
+
+    QQuickView *view = createView(testFile("htmlLists.qml"));
+    QQuickText *textObject = view->rootObject()->findChild<QQuickText*>("myText");
+
+    QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(textObject);
+    QVERIFY(textPrivate != 0);
+    QVERIFY(textPrivate->extra.isAllocated());
+
+    QVERIFY(textObject != 0);
+    textObject->setText(text);
+
+    view->show();
+    view->requestActivate();
+    QVERIFY(QTest::qWaitForWindowActive(view));
+
+    QCOMPARE(textPrivate->extra->doc->lineCount(), nbLines);
+
+    delete view;
+}
+
+void tst_qquicktext::htmlLists_data()
+{
+    QTest::addColumn<QString>("text");
+    QTest::addColumn<int>("nbLines");
+
+    QTest::newRow("ordered list") << "<ol><li>one<li>two<li>three" << 3;
+    QTest::newRow("ordered list closed") << "<ol><li>one</li></ol>" << 1;
+    QTest::newRow("ordered list alpha") << "<ol type=\"a\"><li>one</li><li>two</li></ol>" << 2;
+    QTest::newRow("ordered list upper alpha") << "<ol type=\"A\"><li>one</li><li>two</li></ol>" << 2;
+    QTest::newRow("ordered list roman") << "<ol type=\"i\"><li>one</li><li>two</li></ol>" << 2;
+    QTest::newRow("ordered list upper roman") << "<ol type=\"I\"><li>one</li><li>two</li></ol>" << 2;
+    QTest::newRow("ordered list bad") << "<ol type=\"z\"><li>one</li><li>two</li></ol>" << 2;
+    QTest::newRow("unordered list") << "<ul><li>one<li>two" << 2;
+    QTest::newRow("unordered list closed") << "<ul><li>one</li><li>two</li></ul>" << 2;
+    QTest::newRow("unordered list disc") << "<ul type=\"disc\"><li>one</li><li>two</li></ul>" << 2;
+    QTest::newRow("unordered list square") << "<ul type=\"square\"><li>one</li><li>two</li></ul>" << 2;
+    QTest::newRow("unordered list bad") << "<ul type=\"bad\"><li>one</li><li>two</li></ul>" << 2;
+}
+
 QTEST_MAIN(tst_qquicktext)
 
 #include "tst_qquicktext.moc"